W8004 LoRaWAN Thermostat Controller User Guide

Device Information: Model W8004, devEui ffffff1000047040, gateway IP 192.168.31.205, Modbus Slave ID 2, BACnet Device ID 101

1 Protocol Overview

The W8004 reports power state, setpoint temperature, work mode, fan speed, ambient temperature and humidity, key lock state, valve state, cumulative on-time, and signal quality on Fport 210.

Payload format (Fport 210):

Byte Field Description
0 Reserved Always 0x00 (protocol version)
1 … N TLV pairs [Type(1 B)][Value(N B)] … repeating until end

Each Type byte determines the field name and the number of following value bytes:

Type Value Bytes Field Name Encoding Scale Unit Notes
0x01 1 model uint8 Model code = 0x46
0x94 1 rs485Addr uint8 RS485 slave address of the thermostat
0x95 1+N modbusBlock block 1-byte length N + N-byte Modbus register data; see sub-table below
0x2D 1+N transparentSerial block 1-byte length + raw Modbus RTU response frame (optional)

W8004 0x95 Modbus Register Block

The block starts with a 2-byte Block ID (big-endian), followed by consecutive 16-bit register values (2 bytes each, big-endian):

Normal status block (Block ID = 0x0000):

Register Field Encoding Notes
0x0000 Hardware / Software version uint16 High byte = HW ver, Low byte = SW ver
0x0001 Device status bits bitfield Bit 0: 0=power on, 1=power off; Bit 1: key lock; Bit 2: 0=valve open, 1=closed
0x0002 Current temperature int16 ÷100 → °C
0x0003 Current humidity uint16 ÷100 → %RH
0x0004 Set temperature int16 ÷100 → °C
0x0005 Work mode uint16 0=Auto 1=Cooling 2=Heating 3=Fan-only
0x0006 Fan speed uint16 0=Auto 1=Low 2=Mid 3=High 4=Turbo
0x0007 Cumulative on-time uint16 ×10 → minutes
0x0008 Cumulative valve open-time uint16 ×10 → minutes

Remote control status block (Block ID = 0xF000):

Register Field Notes
0xF000 Remote power state 0=off, 1=on
0xF001 Remote key lock state 0=unlocked, 1=locked
0xF004 Wireless signal strength 0–5

JavaScript Decoder Example:

// W8004 Thermostat — Fport 210 uplink decoder
function decodeUplink(bytes) {
  var i = 1, r = {};
  function u8(b,o)  { return b[o]&0xFF; }
  function u16(b,o) { return (b[o]<<8)|b[o+1]; }
  function i16(b,o) { var v=u16(b,o); return v>32767?v-65536:v; }
  while (i < bytes.length) {
    var t = bytes[i++];
    switch (t) {
      case 0x01: r.model     = bytes[i++]; break;
      case 0x94: r.rs485Addr = bytes[i++]; break;
      case 0x95: {
        // Modbus register block: 1-byte length, then register pairs
        var len = bytes[i++];
        var end = i + len;
        var blockId = (bytes[i]<<8)|bytes[i+1];
        var reg = (blockId === 0xF000) ? 0xF000 : 0x0000;
        var off = i + 2;
        while (off + 1 < end) {
          var val = u16(bytes, off);
          var valS = val > 32767 ? val - 65536 : val;
          if (reg === 0x0000) {
            r.hwVersion = (val>>8)&0xFF; r.swVersion = val&0xFF;
          } else if (reg === 0x0001) {
            r.powerState  = (val&0x01) ? 0 : 1;  // bit0: 0=on
            r.keyLockState = (val&0x02) ? 1 : 0;
            r.valveState   = (val&0x04) ? 1 : 0;  // bit2: 0=open
          } else if (reg === 0x0002) { r.temperature    = valS/100; }
          else if (reg === 0x0003)   { r.humidity        = val/100; }
          else if (reg === 0x0004)   { r.setTemperature  = valS/100; }
          else if (reg === 0x0005)   { r.workMode        = val&0xFF; }
          else if (reg === 0x0006)   { r.fanSpeed        = val&0xFF; }
          else if (reg === 0x0007)   { r.cumulativeOnTime = val*10; }
          else if (reg === 0x0008)   { r.cumulativeValveOpenTime = val*10; }
          else if (reg === 0xF000)   { r.remotePowerState = val&0xFF; }
          else if (reg === 0xF001)   { r.remoteKeyLock   = val&0xFF; }
          else if (reg === 0xF004)   { r.signalStrength   = val&0xFF; }
          off += 2; reg++;
        }
        i = end; break;
      }
      case 0x2D: { var len=bytes[i++]; i+=len; break; } // skip transparent serial
      default:   i++; break;
    }
  }
  return r;
}

Script download: LPP.zip

Compatibility note: LPP.js is developed and tested against ChirpStack v4.17.0. The ChirpStack JavaScript codec API may differ across versions — if you are running a different ChirpStack version, review and adjust the script as needed before deployment.

The W8004 supports power on/off, climate settings (temperature, work mode, fan speed), and touch-panel lock/unlock via Fport 2.

Fport: 2 — No fixed header; two packet formats (single vs. multiple register write)

Format A — single register write: 06 06 [Reg_H Reg_L] [Val_H Val_L] Byte 0=0x06 (instruction type), Byte 1=0x06 (Modbus FC06), then register address (2B BE) and 16-bit value (2B BE).

Format B — multiple consecutive registers write (FC10): 07 [SlaveAddr 0x10 StartReg_H StartReg_L RegCnt_H RegCnt_L ByteCnt Val0_H Val0_L... CRC_L CRC_H] Byte 0=0x07 (instruction type), followed by a complete Modbus RTU FC10 frame including CRC.

Control register address map:

Register Bytes Description
0x0004 06 06 00 04 [T_H T_L] Set temperature (int16, ×100 °C; e.g. 0x09C4=2500 → 25.00 °C)
0x0005 06 06 00 05 00 [Mode] Set work mode (0=Auto 1=Cooling 2=Heating 3=Fan-only)
0x0006 06 06 00 06 00 [Speed] Set fan speed (0=Auto 1=Low 2=Mid 3=High 4=Turbo)
0xF000 06 06 F0 00 00 [State] Remote power ON/OFF (0=OFF 1=ON)
0xF001 06 06 F0 01 00 [State] Remote key lock (0=unlock 1=lock)

JavaScript Encoder Example:

// W8004 Thermostat — Fport 2 downlink encoder
// Returns Modbus-wrapped command bytes.
function encodeDownlink(data) {
  // data.setTemperature : number (°C, e.g. 25.5)
  // data.workMode       : 0=Auto 1=Cooling 2=Heating 3=Fan-only
  // data.fanSpeed       : 0=Auto 1=Low 2=Mid 3=High 4=Turbo
  // data.powerState     : 0=OFF 1=ON
  // data.keyLockState   : 0=unlock 1=lock
  var writes = [];
  if (data.setTemperature !== undefined) {
    var v = Math.round(Number(data.setTemperature) * 100);
    writes.push({reg: 0x0004, val: v & 0xFFFF});
  }
  if (data.workMode !== undefined) writes.push({reg:0x0005, val: Number(data.workMode)&0xFF});
  if (data.fanSpeed  !== undefined) writes.push({reg:0x0006, val: Number(data.fanSpeed) &0xFF});
  if (data.powerState    !== undefined) writes.push({reg:0xF000, val: Number(data.powerState)   ?1:0});
  if (data.keyLockState  !== undefined) writes.push({reg:0xF001, val: Number(data.keyLockState) ?1:0});
  if (writes.length === 0) return [];
  // Single register → Format A: 06 06 Reg_H Reg_L Val_H Val_L
  if (writes.length === 1) {
    var a = writes[0];
    return [0x06, 0x06, (a.reg>>8)&0xFF, a.reg&0xFF, (a.val>>8)&0xFF, a.val&0xFF];
  }
  // Multiple → Format B: 07 [Modbus FC10 frame with CRC16]
  writes.sort(function(a,b){return a.reg-b.reg;});
  var start = writes[0].reg, cnt = writes.length, byteCount = cnt*2;
  var slave = data.rs485Addr || 0x01;
  var frame = [slave, 0x10, (start>>8)&0xFF, start&0xFF, 0x00, cnt, byteCount];
  for (var i=0; i<writes.length; i++) {
    frame.push((writes[i].val>>8)&0xFF, writes[i].val&0xFF);
  }
  // CRC16 (Modbus)
  var crc = 0xFFFF;
  for (var j=0; j<frame.length; j++) {
    crc ^= frame[j];
    for (var b=0; b<8; b++) { crc = (crc&1) ? ((crc>>>1)^0xA001) : (crc>>>1); }
  }
  frame.push(crc&0xFF, (crc>>8)&0xFF);
  return [0x07].concat(frame);
}
// Examples:
//   encodeDownlink({setTemperature:25})    → [0x06,0x06,0x00,0x04,0x09,0xC4]
//   encodeDownlink({powerState:1})         → [0x06,0x06,0xF0,0x00,0x00,0x01]
//   encodeDownlink({workMode:1, fanSpeed:2})→ [0x07, …FC10 frame…]
⚠️ The IP addresses (192.168.31.205 / 192.168.31.193), ChirpStack API token, Slave ID, BACnet Device ID, and devEui in the examples below are for demonstration only. Replace them with your actual gateway IP, ChirpStack API token, and device parameters.

2.1 ChirpStack MQTT Subscription

Subscribe to the MQTT topic to receive real-time uplink frames:

Application ID3ef9e6b9-ec54-4eda-86b8-a5fb46899f39 is the factory-default ChirpStack
application built into the gateway. Replace it with your actual application
ID if you have created a different one.

Gateway IP192.168.31.205 is the gateway WAN port IP shown as an
example. Replace it with your actual gateway IP address.

Device EUIffffff1000047040 is the EUI of the example device.
Replace it with the EUI shown in the gateway device list, or use +
as a wildcard to subscribe to all devices at once.
# Subscribe to one specific device
mosquitto_sub -h 192.168.31.205 -p 1883 \
  -u gateway -P mqtt88888888 \
  -t "application/3ef9e6b9-ec54-4eda-86b8-a5fb46899f39/device/ffffff1000047040/event/up"

# Subscribe to ALL devices on ALL applications (wildcard)
mosquitto_sub -h 192.168.31.205 -p 1883 \
  -u gateway -P mqtt88888888 \
  -t "application/+/device/+/event/up"

Example uplink payload (JSON):

{
  "devEui": "ffffff1000047040",
  "fPort": 210,
  "object": {
    ...  (decoded LPP fields)
  }
}

2.2 IoT Hub HTTP API

Send a GET request to retrieve the latest device state:

curl -s "http://192.168.31.205:8070/api/getStatus?devEui=ffffff1000047040"
{
  "success": true,
  "result": {
    "powerState": true,
    "setTemperature": 24,
    "workMode": 2,
    "fanSpeed": 1,
    "temperature": 29.6,
    "humidity": 0,
    "keyLockState": 0,
    "valveState": 0,
    "hardwareVersion": "0",
    "model": "W8004"
  }
}

2.3 IoT Hub Modbus TCP — Python Script

Script download: modbus_tcp_read.py

Use modbus_tcp_read.py to poll all registers at once:

python3 modbus_tcp_read.py --ip 192.168.31.205 --port 502 \
    --slaveId 2 --sensorType W8004
Target: 192.168.31.205:502 | Slave ID: 2 | Sensor: W8004
================================================================================================================================================================
Attribute                | Addr   | FC  | Format     | Order        | Cnt | Scale    | Raw(Hex)            | Value              | Unit
----------------------------------------------------------------------------------------------------------------------------------------------------------------
online                   | 6      | 03  | Bit/Bool   | Big(ABCD)    | 1   | x1       | 0001                | true               | none
lastOnlineTime           | 7      | 03  | UnixTime   | Big(ABCD)    | 2   | x1       | 69CC CC0B           | 1775029259         | second
powerState               | 9      | 03  | Bit/Bool   | Big(ABCD)    | 1   | x1       | 0001                | true               | none
setTemperature           | 10     | 03  | Int16(S)   | Big(ABCD)    | 1   | /100     | 0960                | 24.0               | celsius
workMode                 | 11     | 03  | Int16      | Big(ABCD)    | 1   | x1       | 0002                | 2.0                | none
fanSpeed                 | 12     | 03  | Int16      | Big(ABCD)    | 1   | x1       | 0001                | 1.0                | none
temperature              | 13     | 03  | Int16(S)   | Big(ABCD)    | 1   | /100     | 0B90                | 29.6               | celsius
humidity                 | 14     | 03  | Int16(S)   | Big(ABCD)    | 1   | /100     | 0000                | 0.0                | percent
keyLockState             | 15     | 03  | Int16      | Big(ABCD)    | 1   | x1       | 0000                | 0.0                | none
valveState               | 16     | 03  | Int16      | Big(ABCD)    | 1   | x1       | 0000                | 0.0                | none
signalStrength           | 17     | 03  | Int16      | Big(ABCD)    | 1   | x1       | 0003                | 3.0                | none
cumulativeOnTime         | 18     | 03  | Int16      | Big(ABCD)    | 1   | /10      | 0000                | 0.0                | minute
... (16 fields total)

2.4 IoT Hub Modbus TCP — Modbus Poll

Tool download: Modbus Poll 9.5.0.1507.zip

  1. Open Modbus Poll, connect to 192.168.31.205:502, Slave ID 2
  2. Menu Setup → Read/Write Definition, set Function Code = FC03, Start Address = 6, Length = 52
  3. Click OK — values update in real-time

2.5 IoT Hub BACnet BIP — Python Script

Script download: bacnet_read.py

Use bacnet_read.py to read all BACnet objects:

python3 bacnet_read.py --ip 192.168.31.205 --port 47808 --id 101
Target: 192.168.31.205:47808 | BACnet ID: 101 | Scan: 10100-10199
------------------------------------------------------------
Type | Instance | Offset | Value                    | Object Name
------------------------------------------------------------
BI   | 10102    | 2      | active                   | ffffff1000047040.online
AI   | 10103    | 3      | 1775029248.00            | ffffff1000047040.lastOnlineTime
BV   | 10104    | 4      | active                   | ffffff1000047040.powerState
AV   | 10105    | 5      | 24.00                    | ffffff1000047040.setTemperature
AV   | 10106    | 6      | 2.00                     | ffffff1000047040.workMode
AV   | 10107    | 7      | 1.00                     | ffffff1000047040.fanSpeed
AI   | 10108    | 8      | 29.60                    | ffffff1000047040.temperature
AI   | 10109    | 9      | 0.00                     | ffffff1000047040.humidity
BV   | 10110    | 10     | inactive                 | ffffff1000047040.keyLockState
BI   | 10111    | 11     | inactive                 | ffffff1000047040.valveState
AI   | 10112    | 12     | 3.00                     | ffffff1000047040.signalStrength
AI   | 10113    | 13     | 0.00                     | ffffff1000047040.cumulativeOnTime
... (16 objects total)

2.6 IoT Hub BACnet BIP — YABE

Tool download: SetupYabe_v2.1.0.exe

  1. Open YABE, connect to 192.168.31.205:47808
  2. Expand Device 101 in the device tree
  3. Browse AI/BI/AV/BV/CV objects to view real-time values

3 Sending Control Commands

The W8004 supports power on/off, climate settings (temperature, work mode, fan speed), and touch-panel lock/unlock via Fport 2.

⚠️ The IP addresses (192.168.31.205 / 192.168.31.193), ChirpStack API token, Slave ID, BACnet Device ID, and devEui in the examples below are for demonstration only. Replace them with your actual gateway IP, ChirpStack API token, and device parameters.

Script downloads: modbus_tcp_write.py · bacnet_write.py

Example 1: Power ON

Modbus TCP:

python3 modbus_tcp_write.py --ip 192.168.31.205 --port 502 --slaveId 2 --sensorType W8004 power --value 1
Target: 192.168.31.205:502 | Slave ID: 2 | Sensor: W8004
Expected values:
  powerState: 1
Attempt 1/1: writing control values (default)...
Modbus Poll write guide:
  Batch 1: FC06 Write Single Register | Start register: 9 (4x40010) | Count: 1
    powerState -> register 9 (4x40010) raw=1 signed=1
Observed values:
  powerState: 1
Link confirmation time: 0.132s
Control completed in: 0.143s
The script automatically writes registers and reads them back for verification. The Modbus Poll write guide section in the output can be used directly as a reference for manual writes in Modbus Poll.

Modbus Poll:

  1. Connect to the gateway (IP 192.168.31.205, Port 502, Slave ID 2)
  2. Menu Functions → 16 Write Multiple Registers
  3. Refer to the Modbus Poll write guide in the script output above for the Register Address and value to enter
  4. Click Send to send the write command

BACnet BIP:

python3 bacnet_write.py --ip 192.168.31.205 --port 47808 --id 101 --sensorType W8004 power --value 1
Target: 192.168.31.205:47808 | BACnet ID: 101 | Sensor: W8004
Expected values:
  powerState: 1
Attempt 1/1: writing control values (default)...
YABE write guide:
  powerState -> object binary-value,10104 | property present-value | type=binary-value | write=active
Observed values:
  powerState: 1
Link confirmation time: 0.010s
Control completed in: 0.033s
The script automatically writes and reads back for verification. The YABE write guide section in the output can be used directly as a YABE manual-write reference.

YABE:

  1. Connect to the gateway (IP 192.168.31.205, Port 47808), expand Device 101
  2. Refer to the YABE write guide in the script output above to locate the corresponding object
  3. Right-click the object → Write Property, set the property to present-value, enter the target value, then click Write

IoT Hub HTTP:

Endpoint: POST http://192.168.31.205:8070/api/sendCommand
curl -s -X POST "http://192.168.31.205:8070/api/sendCommand" \
  -H "Content-Type: application/json" \
  -d '{"devEui":"ffffff1000047040","params":{"powerState":true}}'
{
  "success": true,
  "result": {
    "devEui": "ffffff1000047040",
    "status": "buffered"
  }
}

ChirpStack REST API:

Endpoint: POST http://192.168.31.205:8090/api/devices/ffffff1000047040/queue

curl -s -X POST 'http://192.168.31.205:8090/api/devices/ffffff1000047040/queue' \
  -H 'accept: application/json' \
  -H 'Grpc-Metadata-Authorization: Bearer <token>' \
  -H 'Content-Type: application/json' \
  -d '{"flushQueue":true,"queueItem":{"confirmed":false,"isPending":false,"object":{"model":"W8004","powerState":true}}}'
{
  "id": "4e8b2c17-5f93-4a01-b8d0-2c91ef345678"
}

Formatted JSON payload:

{
  "flushQueue": true,
  "queueItem": {
    "confirmed": false,
    "isPending": false,
    "object": {
      "model": "W8004",
      "powerState": true
    }
  }
}

ChirpStack MQTT:

mosquitto_pub -h 192.168.31.205 -p 1883 -u gateway -P mqtt88888888 \
  -t "application/3ef9e6b9-ec54-4eda-86b8-a5fb46899f39/device/ffffff1000047040/command/down" \
  -m '{"flushQueue":true,"devEui":"ffffff1000047040","confirmed":false,"object":{"model":"W8004","powerState":true}}'

Formatted JSON payload:

{
  "flushQueue": true,
  "devEui": "ffffff1000047040",
  "confirmed": false,
  "object": {
    "model": "W8004",
    "powerState": true
  }
}

Example 2: Power OFF

Modbus TCP:

python3 modbus_tcp_write.py --ip 192.168.31.205 --port 502 --slaveId 2 --sensorType W8004 power --value 0
Target: 192.168.31.205:502 | Slave ID: 2 | Sensor: W8004
Expected values:
  powerState: 0
Attempt 1/1: writing control values (default)...
Modbus Poll write guide:
  Batch 1: FC06 Write Single Register | Start register: 9 (4x40010) | Count: 1
    powerState -> register 9 (4x40010) raw=0 signed=0
Observed values:
  powerState: 0
Link confirmation time: 0.132s
Control completed in: 0.143s
The script automatically writes registers and reads them back for verification. The Modbus Poll write guide section in the output can be used directly as a reference for manual writes in Modbus Poll.

Modbus Poll:

  1. Connect to the gateway (IP 192.168.31.205, Port 502, Slave ID 2)
  2. Menu Functions → 16 Write Multiple Registers
  3. Refer to the Modbus Poll write guide in the script output above for the Register Address and value to enter
  4. Click Send to send the write command

BACnet BIP:

python3 bacnet_write.py --ip 192.168.31.205 --port 47808 --id 101 --sensorType W8004 power --value 0
Target: 192.168.31.205:47808 | BACnet ID: 101 | Sensor: W8004
Expected values:
  powerState: 0
Attempt 1/1: writing control values (default)...
YABE write guide:
  powerState -> object binary-value,10104 | property present-value | type=binary-value | write=inactive
Observed values:
  powerState: 0
Link confirmation time: 0.010s
Control completed in: 0.033s
The script automatically writes and reads back for verification. The YABE write guide section in the output can be used directly as a YABE manual-write reference.

YABE:

  1. Connect to the gateway (IP 192.168.31.205, Port 47808), expand Device 101
  2. Refer to the YABE write guide in the script output above to locate the corresponding object
  3. Right-click the object → Write Property, set the property to present-value, enter the target value, then click Write

IoT Hub HTTP:

Endpoint: POST http://192.168.31.205:8070/api/sendCommand
curl -s -X POST "http://192.168.31.205:8070/api/sendCommand" \
  -H "Content-Type: application/json" \
  -d '{"devEui":"ffffff1000047040","params":{"powerState":false}}'
{
  "success": true,
  "result": {
    "devEui": "ffffff1000047040",
    "status": "buffered"
  }
}

ChirpStack REST API:

Endpoint: POST http://192.168.31.205:8090/api/devices/ffffff1000047040/queue

curl -s -X POST 'http://192.168.31.205:8090/api/devices/ffffff1000047040/queue' \
  -H 'accept: application/json' \
  -H 'Grpc-Metadata-Authorization: Bearer <token>' \
  -H 'Content-Type: application/json' \
  -d '{"flushQueue":true,"queueItem":{"confirmed":false,"isPending":false,"object":{"model":"W8004","powerState":false}}}'
{
  "id": "4e8b2c17-5f93-4a01-b8d0-2c91ef345678"
}

Formatted JSON payload:

{
  "flushQueue": true,
  "queueItem": {
    "confirmed": false,
    "isPending": false,
    "object": {
      "model": "W8004",
      "powerState": false
    }
  }
}

ChirpStack MQTT:

mosquitto_pub -h 192.168.31.205 -p 1883 -u gateway -P mqtt88888888 \
  -t "application/3ef9e6b9-ec54-4eda-86b8-a5fb46899f39/device/ffffff1000047040/command/down" \
  -m '{"flushQueue":true,"devEui":"ffffff1000047040","confirmed":false,"object":{"model":"W8004","powerState":false}}'

Formatted JSON payload:

{
  "flushQueue": true,
  "devEui": "ffffff1000047040",
  "confirmed": false,
  "object": {
    "model": "W8004",
    "powerState": false
  }
}

Example 3: Set Target Temperature Only (26 °C)
The climate command writes setTemperature, workMode, and fanSpeed simultaneously. Unspecified parameters use their defaults (setTemperature=23.0 °C, workMode=1, fanSpeed=1). To change only one attribute without affecting the others, use the IoT Hub HTTP or ChirpStack methods below, which accept individual fields.

Modbus TCP:

python3 modbus_tcp_write.py --ip 192.168.31.205 --port 502 --slaveId 2 --sensorType W8004 climate --setTemperature 26 --workMode 1 --fanSpeed 1
Target: 192.168.31.205:502 | Slave ID: 2 | Sensor: W8004
Expected values:
  setTemperature: 26.0
  workMode: 1
  fanSpeed: 1
Attempt 1/1: writing control values (default)...
Modbus Poll write guide:
  Batch 1: FC16 Write Multiple Registers | Start register: 10 (4x40011) | Count: 3
    setTemperature -> register 10 (4x40011) raw=2600 signed=2600
    workMode -> register 11 (4x40012) raw=1 signed=1
    fanSpeed -> register 12 (4x40013) raw=1 signed=1
Observed values:
  setTemperature: 26.0
  workMode: 1
  fanSpeed: 1
Link confirmation time: 0.154s
Control completed in: 0.165s
The script automatically writes registers and reads them back for verification. The Modbus Poll write guide section in the output can be used directly as a reference for manual writes in Modbus Poll.

Modbus Poll:

  1. Connect to the gateway (IP 192.168.31.205, Port 502, Slave ID 2)
  2. Menu Functions → 16 Write Multiple Registers
  3. Refer to the Modbus Poll write guide in the script output above for the Register Address and value to enter
  4. Click Send to send the write command

BACnet BIP:

python3 bacnet_write.py --ip 192.168.31.205 --port 47808 --id 101 --sensorType W8004 climate --setTemperature 26 --workMode 1 --fanSpeed 1
Target: 192.168.31.205:47808 | BACnet ID: 101 | Sensor: W8004
Expected values:
  setTemperature: 26.0
  workMode: 1
  fanSpeed: 1
Attempt 1/1: writing control values (default)...
YABE write guide:
  setTemperature -> object analog-value,10105 | property present-value | type=analog-value | write=26.0
  workMode -> object analog-value,10106 | property present-value | type=analog-value | write=1
  fanSpeed -> object analog-value,10107 | property present-value | type=analog-value | write=1
Observed values:
  setTemperature: 26.0
  workMode: 1
  fanSpeed: 1
Link confirmation time: 0.031s
Control completed in: 0.075s
The script automatically writes and reads back for verification. The YABE write guide section in the output can be used directly as a YABE manual-write reference.

YABE:

  1. Connect to the gateway (IP 192.168.31.205, Port 47808), expand Device 101
  2. Refer to the YABE write guide in the script output above to locate the corresponding object
  3. Right-click the object → Write Property, set the property to present-value, enter the target value, then click Write

IoT Hub HTTP:

Endpoint: POST http://192.168.31.205:8070/api/sendCommand
curl -s -X POST "http://192.168.31.205:8070/api/sendCommand" \
  -H "Content-Type: application/json" \
  -d '{"devEui":"ffffff1000047040","params":{"setTemperature":26}}'
{
  "success": true,
  "result": {
    "devEui": "ffffff1000047040",
    "status": "buffered"
  }
}

ChirpStack REST API:

Endpoint: POST http://192.168.31.205:8090/api/devices/ffffff1000047040/queue

curl -s -X POST 'http://192.168.31.205:8090/api/devices/ffffff1000047040/queue' \
  -H 'accept: application/json' \
  -H 'Grpc-Metadata-Authorization: Bearer <token>' \
  -H 'Content-Type: application/json' \
  -d '{"flushQueue":true,"queueItem":{"confirmed":false,"isPending":false,"object":{"model":"W8004","setTemperature":26}}}'
{
  "id": "4e8b2c17-5f93-4a01-b8d0-2c91ef345678"
}

Formatted JSON payload:

{
  "flushQueue": true,
  "queueItem": {
    "confirmed": false,
    "isPending": false,
    "object": {
      "model": "W8004",
      "setTemperature": 26
    }
  }
}

ChirpStack MQTT:

mosquitto_pub -h 192.168.31.205 -p 1883 -u gateway -P mqtt88888888 \
  -t "application/3ef9e6b9-ec54-4eda-86b8-a5fb46899f39/device/ffffff1000047040/command/down" \
  -m '{"flushQueue":true,"devEui":"ffffff1000047040","confirmed":false,"object":{"model":"W8004","setTemperature":26}}'

Formatted JSON payload:

{
  "flushQueue": true,
  "devEui": "ffffff1000047040",
  "confirmed": false,
  "object": {
    "model": "W8004",
    "setTemperature": 26
  }
}

Example 4: Set Work Mode Only (Cooling=1)
workMode values: 0=Auto, 1=Cooling, 2=Heating, 3=Ventilation/Fan-only. Via Python script, setTemperature and fanSpeed are also written using defaults.

Modbus TCP:

python3 modbus_tcp_write.py --ip 192.168.31.205 --port 502 --slaveId 2 --sensorType W8004 climate --setTemperature 23 --workMode 1 --fanSpeed 1
Target: 192.168.31.205:502 | Slave ID: 2 | Sensor: W8004
Expected values:
  setTemperature: 23.0
  workMode: 1
  fanSpeed: 1
Attempt 1/1: writing control values (default)...
Modbus Poll write guide:
  Batch 1: FC16 Write Multiple Registers | Start register: 10 (4x40011) | Count: 3
    setTemperature -> register 10 (4x40011) raw=2300 signed=2300
    workMode -> register 11 (4x40012) raw=1 signed=1
    fanSpeed -> register 12 (4x40013) raw=1 signed=1
Observed values:
  setTemperature: 23.0
  workMode: 1
  fanSpeed: 1
Link confirmation time: 0.154s
Control completed in: 0.165s
The script automatically writes registers and reads them back for verification. The Modbus Poll write guide section in the output can be used directly as a reference for manual writes in Modbus Poll.

Modbus Poll:

  1. Connect to the gateway (IP 192.168.31.205, Port 502, Slave ID 2)
  2. Menu Functions → 16 Write Multiple Registers
  3. Refer to the Modbus Poll write guide in the script output above for the Register Address and value to enter
  4. Click Send to send the write command

BACnet BIP:

python3 bacnet_write.py --ip 192.168.31.205 --port 47808 --id 101 --sensorType W8004 climate --setTemperature 23 --workMode 1 --fanSpeed 1
Target: 192.168.31.205:47808 | BACnet ID: 101 | Sensor: W8004
Expected values:
  setTemperature: 23.0
  workMode: 1
  fanSpeed: 1
Attempt 1/1: writing control values (default)...
YABE write guide:
  setTemperature -> object analog-value,10105 | property present-value | type=analog-value | write=23.0
  workMode -> object analog-value,10106 | property present-value | type=analog-value | write=1
  fanSpeed -> object analog-value,10107 | property present-value | type=analog-value | write=1
Observed values:
  setTemperature: 23.0
  workMode: 1
  fanSpeed: 1
Link confirmation time: 0.031s
Control completed in: 0.075s
The script automatically writes and reads back for verification. The YABE write guide section in the output can be used directly as a YABE manual-write reference.

YABE:

  1. Connect to the gateway (IP 192.168.31.205, Port 47808), expand Device 101
  2. Refer to the YABE write guide in the script output above to locate the corresponding object
  3. Right-click the object → Write Property, set the property to present-value, enter the target value, then click Write

IoT Hub HTTP:

Endpoint: POST http://192.168.31.205:8070/api/sendCommand
curl -s -X POST "http://192.168.31.205:8070/api/sendCommand" \
  -H "Content-Type: application/json" \
  -d '{"devEui":"ffffff1000047040","params":{"workMode":1}}'
{
  "success": true,
  "result": {
    "devEui": "ffffff1000047040",
    "status": "buffered"
  }
}

ChirpStack REST API:

Endpoint: POST http://192.168.31.205:8090/api/devices/ffffff1000047040/queue

curl -s -X POST 'http://192.168.31.205:8090/api/devices/ffffff1000047040/queue' \
  -H 'accept: application/json' \
  -H 'Grpc-Metadata-Authorization: Bearer <token>' \
  -H 'Content-Type: application/json' \
  -d '{"flushQueue":true,"queueItem":{"confirmed":false,"isPending":false,"object":{"model":"W8004","workMode":1}}}'
{
  "id": "4e8b2c17-5f93-4a01-b8d0-2c91ef345678"
}

Formatted JSON payload:

{
  "flushQueue": true,
  "queueItem": {
    "confirmed": false,
    "isPending": false,
    "object": {
      "model": "W8004",
      "workMode": 1
    }
  }
}

ChirpStack MQTT:

mosquitto_pub -h 192.168.31.205 -p 1883 -u gateway -P mqtt88888888 \
  -t "application/3ef9e6b9-ec54-4eda-86b8-a5fb46899f39/device/ffffff1000047040/command/down" \
  -m '{"flushQueue":true,"devEui":"ffffff1000047040","confirmed":false,"object":{"model":"W8004","workMode":1}}'

Formatted JSON payload:

{
  "flushQueue": true,
  "devEui": "ffffff1000047040",
  "confirmed": false,
  "object": {
    "model": "W8004",
    "workMode": 1
  }
}

Example 5: Set Fan Speed Only (Medium=2)
fanSpeed values: 0=Auto, 1=Low, 2=Medium, 3=High, 4=Turbo.

Modbus TCP:

python3 modbus_tcp_write.py --ip 192.168.31.205 --port 502 --slaveId 2 --sensorType W8004 climate --setTemperature 23 --workMode 1 --fanSpeed 2
Target: 192.168.31.205:502 | Slave ID: 2 | Sensor: W8004
Expected values:
  setTemperature: 23.0
  workMode: 1
  fanSpeed: 2
Attempt 1/1: writing control values (default)...
Modbus Poll write guide:
  Batch 1: FC16 Write Multiple Registers | Start register: 10 (4x40011) | Count: 3
    setTemperature -> register 10 (4x40011) raw=2300 signed=2300
    workMode -> register 11 (4x40012) raw=1 signed=1
    fanSpeed -> register 12 (4x40013) raw=2 signed=2
Observed values:
  setTemperature: 23.0
  workMode: 1
  fanSpeed: 2
Link confirmation time: 0.154s
Control completed in: 0.165s
The script automatically writes registers and reads them back for verification. The Modbus Poll write guide section in the output can be used directly as a reference for manual writes in Modbus Poll.

Modbus Poll:

  1. Connect to the gateway (IP 192.168.31.205, Port 502, Slave ID 2)
  2. Menu Functions → 16 Write Multiple Registers
  3. Refer to the Modbus Poll write guide in the script output above for the Register Address and value to enter
  4. Click Send to send the write command

BACnet BIP:

python3 bacnet_write.py --ip 192.168.31.205 --port 47808 --id 101 --sensorType W8004 climate --setTemperature 23 --workMode 1 --fanSpeed 2
Target: 192.168.31.205:47808 | BACnet ID: 101 | Sensor: W8004
Expected values:
  setTemperature: 23.0
  workMode: 1
  fanSpeed: 2
Attempt 1/1: writing control values (default)...
YABE write guide:
  setTemperature -> object analog-value,10105 | property present-value | type=analog-value | write=23.0
  workMode -> object analog-value,10106 | property present-value | type=analog-value | write=1
  fanSpeed -> object analog-value,10107 | property present-value | type=analog-value | write=2
Observed values:
  setTemperature: 23.0
  workMode: 1
  fanSpeed: 2
Link confirmation time: 0.031s
Control completed in: 0.075s
The script automatically writes and reads back for verification. The YABE write guide section in the output can be used directly as a YABE manual-write reference.

YABE:

  1. Connect to the gateway (IP 192.168.31.205, Port 47808), expand Device 101
  2. Refer to the YABE write guide in the script output above to locate the corresponding object
  3. Right-click the object → Write Property, set the property to present-value, enter the target value, then click Write

IoT Hub HTTP:

Endpoint: POST http://192.168.31.205:8070/api/sendCommand
curl -s -X POST "http://192.168.31.205:8070/api/sendCommand" \
  -H "Content-Type: application/json" \
  -d '{"devEui":"ffffff1000047040","params":{"fanSpeed":2}}'
{
  "success": true,
  "result": {
    "devEui": "ffffff1000047040",
    "status": "buffered"
  }
}

ChirpStack REST API:

Endpoint: POST http://192.168.31.205:8090/api/devices/ffffff1000047040/queue

curl -s -X POST 'http://192.168.31.205:8090/api/devices/ffffff1000047040/queue' \
  -H 'accept: application/json' \
  -H 'Grpc-Metadata-Authorization: Bearer <token>' \
  -H 'Content-Type: application/json' \
  -d '{"flushQueue":true,"queueItem":{"confirmed":false,"isPending":false,"object":{"model":"W8004","fanSpeed":2}}}'
{
  "id": "4e8b2c17-5f93-4a01-b8d0-2c91ef345678"
}

Formatted JSON payload:

{
  "flushQueue": true,
  "queueItem": {
    "confirmed": false,
    "isPending": false,
    "object": {
      "model": "W8004",
      "fanSpeed": 2
    }
  }
}

ChirpStack MQTT:

mosquitto_pub -h 192.168.31.205 -p 1883 -u gateway -P mqtt88888888 \
  -t "application/3ef9e6b9-ec54-4eda-86b8-a5fb46899f39/device/ffffff1000047040/command/down" \
  -m '{"flushQueue":true,"devEui":"ffffff1000047040","confirmed":false,"object":{"model":"W8004","fanSpeed":2}}'

Formatted JSON payload:

{
  "flushQueue": true,
  "devEui": "ffffff1000047040",
  "confirmed": false,
  "object": {
    "model": "W8004",
    "fanSpeed": 2
  }
}

Example 6: Set Temperature + Work Mode + Fan Speed Together (26 °C, Cooling, Medium)

Modbus TCP:

python3 modbus_tcp_write.py --ip 192.168.31.205 --port 502 --slaveId 2 --sensorType W8004 climate --setTemperature 26 --workMode 1 --fanSpeed 2
Target: 192.168.31.205:502 | Slave ID: 2 | Sensor: W8004
Expected values:
  setTemperature: 26.0
  workMode: 1
  fanSpeed: 2
Attempt 1/1: writing control values (default)...
Modbus Poll write guide:
  Batch 1: FC16 Write Multiple Registers | Start register: 10 (4x40011) | Count: 3
    setTemperature -> register 10 (4x40011) raw=2600 signed=2600
    workMode -> register 11 (4x40012) raw=1 signed=1
    fanSpeed -> register 12 (4x40013) raw=2 signed=2
Observed values:
  setTemperature: 26.0
  workMode: 1
  fanSpeed: 2
Link confirmation time: 0.154s
Control completed in: 0.165s
The script automatically writes registers and reads them back for verification. The Modbus Poll write guide section in the output can be used directly as a reference for manual writes in Modbus Poll.

Modbus Poll:

  1. Connect to the gateway (IP 192.168.31.205, Port 502, Slave ID 2)
  2. Menu Functions → 16 Write Multiple Registers
  3. Refer to the Modbus Poll write guide in the script output above for the Register Address and value to enter
  4. Click Send to send the write command

BACnet BIP:

python3 bacnet_write.py --ip 192.168.31.205 --port 47808 --id 101 --sensorType W8004 climate --setTemperature 26 --workMode 1 --fanSpeed 2
Target: 192.168.31.205:47808 | BACnet ID: 101 | Sensor: W8004
Expected values:
  setTemperature: 26.0
  workMode: 1
  fanSpeed: 2
Attempt 1/1: writing control values (default)...
YABE write guide:
  setTemperature -> object analog-value,10105 | property present-value | type=analog-value | write=26.0
  workMode -> object analog-value,10106 | property present-value | type=analog-value | write=1
  fanSpeed -> object analog-value,10107 | property present-value | type=analog-value | write=2
Observed values:
  setTemperature: 26.0
  workMode: 1
  fanSpeed: 2
Link confirmation time: 0.031s
Control completed in: 0.075s
The script automatically writes and reads back for verification. The YABE write guide section in the output can be used directly as a YABE manual-write reference.

YABE:

  1. Connect to the gateway (IP 192.168.31.205, Port 47808), expand Device 101
  2. Refer to the YABE write guide in the script output above to locate the corresponding object
  3. Right-click the object → Write Property, set the property to present-value, enter the target value, then click Write

IoT Hub HTTP:

Endpoint: POST http://192.168.31.205:8070/api/sendCommand
curl -s -X POST "http://192.168.31.205:8070/api/sendCommand" \
  -H "Content-Type: application/json" \
  -d '{"devEui":"ffffff1000047040","params":{"setTemperature":26,"workMode":1,"fanSpeed":2}}'
{
  "success": true,
  "result": {
    "devEui": "ffffff1000047040",
    "status": "buffered"
  }
}

ChirpStack REST API:

Endpoint: POST http://192.168.31.205:8090/api/devices/ffffff1000047040/queue

curl -s -X POST 'http://192.168.31.205:8090/api/devices/ffffff1000047040/queue' \
  -H 'accept: application/json' \
  -H 'Grpc-Metadata-Authorization: Bearer <token>' \
  -H 'Content-Type: application/json' \
  -d '{"flushQueue":true,"queueItem":{"confirmed":false,"isPending":false,"object":{"model":"W8004","setTemperature":26,"workMode":1,"fanSpeed":2}}}'
{
  "id": "4e8b2c17-5f93-4a01-b8d0-2c91ef345678"
}

Formatted JSON payload:

{
  "flushQueue": true,
  "queueItem": {
    "confirmed": false,
    "isPending": false,
    "object": {
      "model": "W8004",
      "setTemperature": 26,
      "workMode": 1,
      "fanSpeed": 2
    }
  }
}

ChirpStack MQTT:

mosquitto_pub -h 192.168.31.205 -p 1883 -u gateway -P mqtt88888888 \
  -t "application/3ef9e6b9-ec54-4eda-86b8-a5fb46899f39/device/ffffff1000047040/command/down" \
  -m '{"flushQueue":true,"devEui":"ffffff1000047040","confirmed":false,"object":{"model":"W8004","setTemperature":26,"workMode":1,"fanSpeed":2}}'

Formatted JSON payload:

{
  "flushQueue": true,
  "devEui": "ffffff1000047040",
  "confirmed": false,
  "object": {
    "model": "W8004",
    "setTemperature": 26,
    "workMode": 1,
    "fanSpeed": 2
  }
}

Example 7: Lock Touch Panel (keyLockState ON)

Modbus TCP:

python3 modbus_tcp_write.py --ip 192.168.31.205 --port 502 --slaveId 2 --sensorType W8004 lock --value 1
Target: 192.168.31.205:502 | Slave ID: 2 | Sensor: W8004
Expected values:
  keyLockState: 1
Attempt 1/1: writing control values (default)...
Modbus Poll write guide:
  Batch 1: FC06 Write Single Register | Start register: 15 (4x40016) | Count: 1
    keyLockState -> register 15 (4x40016) raw=1 signed=1
Observed values:
  keyLockState: 1
Link confirmation time: 0.125s
Control completed in: 0.146s
The script automatically writes registers and reads them back for verification. The Modbus Poll write guide section in the output can be used directly as a reference for manual writes in Modbus Poll.

Modbus Poll:

  1. Connect to the gateway (IP 192.168.31.205, Port 502, Slave ID 2)
  2. Menu Functions → 16 Write Multiple Registers
  3. Refer to the Modbus Poll write guide in the script output above for the Register Address and value to enter
  4. Click Send to send the write command

BACnet BIP:

python3 bacnet_write.py --ip 192.168.31.205 --port 47808 --id 101 --sensorType W8004 lock --value 1
Target: 192.168.31.205:47808 | BACnet ID: 101 | Sensor: W8004
Expected values:
  keyLockState: 1
Attempt 1/1: writing control values (default)...
YABE write guide:
  keyLockState -> object binary-value,10110 | property present-value | type=binary-value | write=active
Observed values:
  keyLockState: 1
Link confirmation time: 0.010s
Control completed in: 0.033s
The script automatically writes and reads back for verification. The YABE write guide section in the output can be used directly as a YABE manual-write reference.

YABE:

  1. Connect to the gateway (IP 192.168.31.205, Port 47808), expand Device 101
  2. Refer to the YABE write guide in the script output above to locate the corresponding object
  3. Right-click the object → Write Property, set the property to present-value, enter the target value, then click Write

IoT Hub HTTP:

Endpoint: POST http://192.168.31.205:8070/api/sendCommand
curl -s -X POST "http://192.168.31.205:8070/api/sendCommand" \
  -H "Content-Type: application/json" \
  -d '{"devEui":"ffffff1000047040","params":{"keyLockState":true}}'
{
  "success": true,
  "result": {
    "devEui": "ffffff1000047040",
    "status": "buffered"
  }
}

ChirpStack REST API:

Endpoint: POST http://192.168.31.205:8090/api/devices/ffffff1000047040/queue

curl -s -X POST 'http://192.168.31.205:8090/api/devices/ffffff1000047040/queue' \
  -H 'accept: application/json' \
  -H 'Grpc-Metadata-Authorization: Bearer <token>' \
  -H 'Content-Type: application/json' \
  -d '{"flushQueue":true,"queueItem":{"confirmed":false,"isPending":false,"object":{"model":"W8004","keyLockState":true}}}'
{
  "id": "4e8b2c17-5f93-4a01-b8d0-2c91ef345678"
}

Formatted JSON payload:

{
  "flushQueue": true,
  "queueItem": {
    "confirmed": false,
    "isPending": false,
    "object": {
      "model": "W8004",
      "keyLockState": true
    }
  }
}

ChirpStack MQTT:

mosquitto_pub -h 192.168.31.205 -p 1883 -u gateway -P mqtt88888888 \
  -t "application/3ef9e6b9-ec54-4eda-86b8-a5fb46899f39/device/ffffff1000047040/command/down" \
  -m '{"flushQueue":true,"devEui":"ffffff1000047040","confirmed":false,"object":{"model":"W8004","keyLockState":true}}'

Formatted JSON payload:

{
  "flushQueue": true,
  "devEui": "ffffff1000047040",
  "confirmed": false,
  "object": {
    "model": "W8004",
    "keyLockState": true
  }
}

Example 8: Unlock Touch Panel (keyLockState OFF)

Modbus TCP:

python3 modbus_tcp_write.py --ip 192.168.31.205 --port 502 --slaveId 2 --sensorType W8004 lock --value 0
Target: 192.168.31.205:502 | Slave ID: 2 | Sensor: W8004
Expected values:
  keyLockState: 0
Attempt 1/1: writing control values (default)...
Modbus Poll write guide:
  Batch 1: FC06 Write Single Register | Start register: 15 (4x40016) | Count: 1
    keyLockState -> register 15 (4x40016) raw=0 signed=0
Observed values:
  keyLockState: 0
Link confirmation time: 0.125s
Control completed in: 0.146s
The script automatically writes registers and reads them back for verification. The Modbus Poll write guide section in the output can be used directly as a reference for manual writes in Modbus Poll.

Modbus Poll:

  1. Connect to the gateway (IP 192.168.31.205, Port 502, Slave ID 2)
  2. Menu Functions → 16 Write Multiple Registers
  3. Refer to the Modbus Poll write guide in the script output above for the Register Address and value to enter
  4. Click Send to send the write command

BACnet BIP:

python3 bacnet_write.py --ip 192.168.31.205 --port 47808 --id 101 --sensorType W8004 lock --value 0
Target: 192.168.31.205:47808 | BACnet ID: 101 | Sensor: W8004
Expected values:
  keyLockState: 0
Attempt 1/1: writing control values (default)...
YABE write guide:
  keyLockState -> object binary-value,10110 | property present-value | type=binary-value | write=inactive
Observed values:
  keyLockState: 0
Link confirmation time: 0.010s
Control completed in: 0.033s
The script automatically writes and reads back for verification. The YABE write guide section in the output can be used directly as a YABE manual-write reference.

YABE:

  1. Connect to the gateway (IP 192.168.31.205, Port 47808), expand Device 101
  2. Refer to the YABE write guide in the script output above to locate the corresponding object
  3. Right-click the object → Write Property, set the property to present-value, enter the target value, then click Write

IoT Hub HTTP:

Endpoint: POST http://192.168.31.205:8070/api/sendCommand
curl -s -X POST "http://192.168.31.205:8070/api/sendCommand" \
  -H "Content-Type: application/json" \
  -d '{"devEui":"ffffff1000047040","params":{"keyLockState":false}}'
{
  "success": true,
  "result": {
    "devEui": "ffffff1000047040",
    "status": "buffered"
  }
}

ChirpStack REST API:

Endpoint: POST http://192.168.31.205:8090/api/devices/ffffff1000047040/queue

curl -s -X POST 'http://192.168.31.205:8090/api/devices/ffffff1000047040/queue' \
  -H 'accept: application/json' \
  -H 'Grpc-Metadata-Authorization: Bearer <token>' \
  -H 'Content-Type: application/json' \
  -d '{"flushQueue":true,"queueItem":{"confirmed":false,"isPending":false,"object":{"model":"W8004","keyLockState":false}}}'
{
  "id": "4e8b2c17-5f93-4a01-b8d0-2c91ef345678"
}

Formatted JSON payload:

{
  "flushQueue": true,
  "queueItem": {
    "confirmed": false,
    "isPending": false,
    "object": {
      "model": "W8004",
      "keyLockState": false
    }
  }
}

ChirpStack MQTT:

mosquitto_pub -h 192.168.31.205 -p 1883 -u gateway -P mqtt88888888 \
  -t "application/3ef9e6b9-ec54-4eda-86b8-a5fb46899f39/device/ffffff1000047040/command/down" \
  -m '{"flushQueue":true,"devEui":"ffffff1000047040","confirmed":false,"object":{"model":"W8004","keyLockState":false}}'

Formatted JSON payload:

{
  "flushQueue": true,
  "devEui": "ffffff1000047040",
  "confirmed": false,
  "object": {
    "model": "W8004",
    "keyLockState": false
  }
}