#32608·zigbee2mqtt

BTicino K4003CWI / Legrand 067773 wireless remote only sends "toggle" — herdsman answers the device's manufacturer-specific genBasic 0xF000 read with a value, so the device stays in toggle mode instead of on/off

Author: itrankolovCreated Jul 19, 2026Updated Sep 18, 2026
Labelsproblemstale

What happened?

The Legrand " Remote switch" (067773; sold as BTicino K4003CWI) publishes only action: toggle for both rockers, while the same physical unit paired to ZHA publishes distinct on/off plus brightness_move/brightness_stop.

Root cause, confirmed by packet capture on the same unit: after joining, the device issues a manufacturer-specific read (manufacturerCode 0x1021 / 4129) of genBasic attribute 0xF000. zigbee-herdsman's auto-responder answers this read with a concrete value:

genBasic.readRsp {"61440":{"value":23,"type":35}}   (manufacturerSpecific, status SUCCESS)

With that answer the device runs in standalone toggle mode. When the coordinator instead answers UNSUPPORTED_ATTRIBUTE (0x86) — as zigpy/ZHA effectively does — the device operates as a standalone controller and sends per-button on/off.

Isolation testing on the same unit, varying only the 0xF000 response:

Response to genBasic 0xF000 read Buttons
Native herdsman (value: 23) toggle
Intercepted → UNSUPPORTED_ATTRIBUTE on/off ✓
0xFC01 hello ack only, 0xF000 native toggle
0xFC01 hello ack off, 0xF000 → 0x86 on/off ✓

The 0xF000 response is necessary and sufficient. The device also sends unparseable manufacturer commands on cluster 0xFC01 (cmd 0x09 = EUI64+0x01, cmd 0x05 = EUI64 → "UNSUP_COMMAND 64513:9/5"), but answering or ignoring those makes no difference to the mode — not the cause.

Base genBasic has no 0xF000 attribute, so the value: 23 appears to come from the read-response defaulting logic rather than a cluster definition — worth checking where it's injected. Fix: when the device reads manufacturer-specific genBasic 0xF000 and herdsman has no genuine value, answer UNSUPPORTED_ATTRIBUTE rather than a value, matching zigpy. Likely affects the whole Legrand/BTicino "Wireless remote" family (067773/067774/K4003C…).

What did you expect to happen?

Per-button on/off actions (on, off, brightness_move_up/down, brightness_stop), matching ZHA behavior for the identical hardware — rather than only toggle.

How to reproduce it (minimal and precise)

  1. Pair a BTicino K4003CWI (reports as Legrand 067773, " Remote switch", manufacturerID 4129).
  2. With debug logging, observe after join:
    • device → coordinator: genBasic read, manufacturerSpecific:true, manufacturerCode:4129, attrId:61440 (0xF000)
    • coordinator → device: genBasic.readRsp {"61440":{"value":23,"type":35}}
  3. Press either rocker → both publish action: toggle.
  4. Intercept that read and reply [{attrId:61440, status:0x86}] → both rockers now publish distinct on/off.

Zigbee2MQTT version

2.12.1

Adapter firmware version

Revision: 7.5.1 [GA]

Adapter

HA Yellow

Setup

Home Assistant add-on (Zigbee2MQTT 2.12.1) on Home Assistant OS. Running on Home Assistant Yellow.

Device database.db entry

{"id":2,"type":"EndDevice","ieeeAddr":"0x0004740000cc2c90","nwkAddr":15546,"manufId":4129,"manufName":" Legrand\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000","powerSource":"Battery","modelId":" Remote switch\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000","epList":[1],"endpoints":{"1":{"profId":260,"epId":1,"devId":260,"inClusterList":[0,3,15,32,1,64513],"outClusterList":[3,6,8,0,64513,25],"clusters":{"genBasic":{"attributes":{"modelId":" Remote switch\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000","manufacturerName":" Legrand\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000","powerSource":3,"zclVersion":8,"appVersion":0,"stackVersion":69,"hwVersion":2,"dateCode":" \u000020240628\u0000\u0000\u0000\u0000\u0000","swBuildId":"0052"}},"genPollCtrl":{"attributes":{"checkinInterval":144000}}},"binds":[{"cluster":32,"type":"endpoint","deviceIeeeAddress":"0xf082c0fffe5a9fb2","endpointID":1},{"cluster":6,"type":"endpoint","deviceIeeeAddress":"0xf082c0fffe5a9fb2","endpointID":1},{"cluster":8,"type":"endpoint","deviceIeeeAddress":"0xf082c0fffe5a9fb2","endpointID":1},{"cluster":1,"type":"endpoint","deviceIeeeAddress":"0xf082c0fffe5a9fb2","endpointID":1}],"configuredReportings":[],"meta":{}}},"appVersion":0,"stackVersion":69,"hwVersion":2,"dateCode":" \u000020240628\u0000\u0000\u0000\u0000\u0000","swBuildId":"0052","zclVersion":8,"interviewCompleted":true,"interviewState":"SUCCESSFUL","meta":{"configured":"0.0.0"},"lastSeen":1784497478030,"checkinInterval":36000}#

Debug log

boza9.log

Notes

Before/after logs and the working converter:

  • boza9.log (above): native converter, device sends toggle
  • boza8.log (attached): external converter answering 0xF000 with 0x86, device sends on/off
  • legrand_k4003cwi_v5.js (attached): the working external converter (workaround)

The "device treats a valued response as gateway-present" is my interpretation; the packet-level cause (value→toggle, 0x86→on/off) is what's proven — see the isolation table. Happy to provide sniffer captures or test a patch.

boza8.log

legrand_k4003cwi_v5.js