Koenkk / Koenkk/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

Open
#32,608 6 comments 2 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

problem
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)
  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

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.