TS0002 _TZ3218_sfjr4fnz (ENGO EREL-16ZB) is a 1-gang relay but matches the 2-gang TS0002 default definition - configure always fails
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 ENGO EREL-16ZB is a single-channel Zigbee relay (1x16A, NO-COM output). Its fingerprint is modelID: TS0002 / manufacturerName: _TZ3218_sfjr4fnz and it exposes one endpoint only (epList: [1]).
There is no definition for this manufacturerName in zigbee-herdsman-converters, so it falls through to the catch-all entry in src/devices/tuya.ts (model: "TS0002", zigbeeModel: ["TS0002"], commented "This is the default for TS0002 devices"). That definition is 2-gang:
endpoint: (device) => { return {l1: 1, l2: 2}; },
configure: async (device, coordinatorEndpoint) => {
await tuya.configureMagicPacket(device, coordinatorEndpoint);
await reporting.bind(device.getEndpoint(1), coordinatorEndpoint, ["genOnOff"]);
await reporting.bind(device.getEndpoint(2), coordinatorEndpoint, ["genOnOff"]);
},
Endpoint 2 does not exist, so device.getEndpoint(2) returns undefined and configure throws on every attempt, after every restart:
error: z2m: Failed to configure 'ENGO Gas boiler Relay', attempt 3 (TypeError: Cannot read properties of undefined (reading 'bind')
at Object.bind (zigbee-herdsman-converters/src/lib/reporting.ts:48:24)
at configure (zigbee-herdsman-converters/src/devices/tuya.ts:8502:29)
at async Object.configure (zigbee-herdsman-converters/src/index.ts:371:21)
at async Configure.configure (/app/lib/extension/configure.ts:135:13))
Consequences:
- The device never reaches the configured state; 3 failed attempts are logged after every restart.
state_l2,countdown_l2andpower_on_behavior_l2are exposed but can never work (permanentlyunknownin Home Assistant).- Endpoint 1 itself is fine: its
genOnOffbind runs before the throw, so on/off commands and state updates work normally. This is a definition bug, not a functional outage.
A tuya.whitelabel(...) entry on the existing definition would fix only the displayed vendor/model - configure would still fail, because the generic definition binds endpoint 2 unconditionally.
What did you expect to happen?
The device recognised as a 1-gang relay: configure succeeds, and no l2 exposes are created.
How to reproduce it (minimal and precise)
Pair an ENGO EREL-16ZB ("ENGO Smart Relay EREL-16ZB, 1x16A, NO-COM"). configure fails 3 times on every Zigbee2MQTT start.
Zigbee2MQTT version
2.14.1 (Home Assistant add-on 2.14.1-1), zigbee-herdsman-converters 26.105.0
Adapter firmware version
20260311
Adapter
SMLIGHT SLZB-06 (zstack), connected over TCP
Setup
Home Assistant add-on on Home Assistant OS, x86-64.
Linux 6.18.39-haos #1 SMP PREEMPT_DYNAMIC x86_64, Home Assistant Core 2026.9.1
Device database.db entry
{"type":"Router","manufId":4098,"manufName":"_TZ3218_sfjr4fnz","powerSource":"Mains (single phase)","modelId":"TS0002","epList":[1],"endpoints":{"1":{"profId":260,"epId":1,"devId":256,"inClusterList":[0,3,4,5,6,57345,57344],"outClusterList":[10,25],"clusters":{"genBasic":{"attributes":{"65507":"sdk:1.0.16;cfg:0.0.0;ker:0.0.14-beta.1;","65508":0,"65534":0,"modelId":"TS0002","manufacturerName":"_TZ3218_sfjr4fnz","powerSource":1,"zclVersion":3,"appVersion":73,"stackVersion":1,"hwVersion":1,"dateCode":""}},"genOnOff":{"attributes":{"onOff":0,"onTime":0,"offWaitTime":0,"tuyaBacklightMode":1,"moesStartUpOnOff":2}},"manuSpecificTuya3":{"attributes":{"powerOnBehavior":2,"switchType":0}}},"binds":[{"cluster":6,"type":"endpoint","endpointID":1}],"configuredReportings":[],"meta":{}}},"appVersion":73,"stackVersion":1,"hwVersion":1,"zclVersion":3,"interviewCompleted":true,"interviewState":"SUCCESSFUL"}
(IEEE address and coordinator address removed.)
Notes
Product: ENGO Controls EREL-16ZB, "Smart Relay ZigBee 1x16A, NO-COM" (vendor site: https://engocontrols.com, example retailer listing: https://qualityheating.co.uk/products/engo-erel-16zb-zigbee-smart-relay-1-16a-wireless-boiler-pump-lighting-control).
Suggested fix - a fingerprint-matched 1-gang definition placed before the generic TS0002 entry, following the pattern of the existing TS0002_basic / TS0002_limited variants (all 2-gang today):
{
fingerprint: tuya.fingerprint("TS0002", ["_TZ3218_sfjr4fnz"]),
model: "EREL-16ZB",
vendor: "ENGO",
description: "ZigBee smart relay 1x16A",
extend: [
tuya.modernExtend.tuyaBase(),
tuya.modernExtend.tuyaOnOff({
switchType: true,
powerOnBehavior2: true,
backlightModeOffOn: true,
indicatorMode: true,
onOffCountdown: true,
inchingSwitch: true,
}),
tuya.clusters.addTuyaCommonPrivateCluster(),
],
configure: async (device, coordinatorEndpoint) => {
await tuya.configureMagicPacket(device, coordinatorEndpoint);
await reporting.bind(device.getEndpoint(1), coordinatorEndpoint, ["genOnOff"]);
},
}
The feature set matches what the hardware actually has on endpoint 1: tuyaBacklightMode and moesStartUpOnOff in genOnOff, powerOnBehavior / switchType in manuSpecificTuya3 (0xE001), with both 0xE000 and 0xE001 in the input cluster list. Not tested yet - happy to run it as an external converter and report back.
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 in src/devices/tuya.ts, comparing the existing TS0002, TS0002_basic, and TS0002_limited definitions. Check the reported fingerprint and endpoint list, then validate the candidate definition with an external converter or the ENGO device. Done means configure succeeds for endpoint 1 and no l2 exposes are produced.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- embedded-iot
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 76/100