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
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 15.7k
- Forks
- 2k
- Avg merge
- 18h 55m
- Merged PRs (30d)
- 35
Description
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)
- Pair a BTicino K4003CWI (reports as Legrand 067773, " Remote switch", manufacturerID 4129).
- 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}}
- Press either rocker → both publish action: toggle.
- 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
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.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by tracing zigbee-herdsman's auto-responder for manufacturer-specific genBasic reads, focusing on attribute 0xF000 and the default value 23 shown in the debug log. Compare boza9.log with boza8.log and legrand_k4003cwi_v5.js to confirm the response difference. Done means a missing genuine value produces UNSUPPORTED_ATTRIBUTE (0x86), allowing the device to publish per-button on/off actions.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- backend, embedded-iot
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 55/100