BTicino/Legrand/Netatmo Zigbee devices refuse to connect to third-party gateways
Legrand and its child companies (such as BTicino with their Living Now series) manufacture Zigbee-based smart devices. While these devices use the Zigbee protocol, they are intentionally restricted to work only with Legrand’s own gateways.
[Incident]
During the pairing process, Legrand devices send a non-standard Zigbee command and expect a specific reply from the coordinator. If the expected response is not received, the device will refuse to complete the pairing process.This effectively blocks the use of Legrand devices with third-party Zigbee gateways such as Zigbee2MQTT, Home Assistant’s ZHA, or other open-source coordinators.
When a Legrand device is powered on and attempts to join a network, it sends a read frame to every device on the network. The payload includes the number of seconds since the device was powered.
- If the coordinator responds with a valid value (e.g., 23 seconds), the device continues pairing.
- If the response is missing or the value is too high (e.g., 200), the device leaves the network and refuses to pair.
This mechanism acts as a vendor lock-in, ensuring that only Legrand’s own gateways can provide the expected response.
Zigbee2MQTT Workaround
The Zigbee2MQTT project implevemented a workaround to support Legrand dices by simulating the expected response during pairing. Below is their current implementation:
// support Legrand security protocol // when pairing, a powered device will send a read frame to every device on the network // it expects at least one answer. The payload contains the number of seconds // since when the device is powered. If the value is too high, it will leave & not pair // 23 works, 200 doesn't if (device.manufacturerID === Zcl.ManufacturerCode.LEGRAND_GROUP && !device.customReadResponse) { device.customReadResponse = (frame, endpoint) => { if (frame.isCluster("genBasic") && frame.payload.find((i: {attrId: number}) => i.attrId === 61440)) { const options = {manufacturerCode: Zcl.ManufacturerCode.LEGRAND_GROUP, disableDefaultResponse: true}; const payload = {61440: {value: 23, type: 35}}; endpoint.readResponse("genBasic", frame.header.transactionSequenceNumber, payload, options).catch((e) => { logger.warning(`Legrand security read response failed: ${e}`, NS); }); return true; } return false; }; }
https://github.com/Koenkk/zigbee-herdsman-converters/blob/master/src/index.ts#L638-L658