Skip to content

Commit 54b8239

Browse files
committed
Replace deviceAddCustomCluster with direct attribute IDs
Using deviceAddCustomCluster for the cover switch and cover clusters caused the Zigbee2MQTT UI to crash when opening the binding and reporting tabs, and made the reconfigure process fail. The issue was reproducible on the main branch as well, so it was not a regression introduced here. Replacing the custom cluster registrations with direct numeric cluster IDs and inline {ID, type} attribute definitions resolves the crashes and restores reliable reconfiguration. The trade-off is reduced visibility of custom attributes in the Z2M reporting UI.
1 parent 6c18f89 commit 54b8239

1 file changed

Lines changed: 19 additions & 49 deletions

File tree

helper_scripts/templates/switch_custom.js.jinja

Lines changed: 19 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ const {
66
text,
77
binary,
88
windowCovering,
9-
deviceAddCustomCluster,
109
} = require("zigbee-herdsman-converters/lib/modernExtend");
1110
const {assertString} = require("zigbee-herdsman-converters/lib/utils");
1211
const reporting = require("zigbee-herdsman-converters/lib/reporting");
@@ -271,8 +270,8 @@ const romasku = {
271270
name,
272271
endpointName,
273272
lookup: { toggle: 0, momentary: 1 },
274-
cluster: "manuSpecificTuyaCoverSwitchConfig",
275-
attribute: "switchType",
273+
cluster: 0xFC01,
274+
attribute: {ID: 0x0000, type: Zcl.DataType.ENUM8},
276275
description: "Type of cover switch: toggle (rocker) or momentary (push button)",
277276
entityCategory: "config",
278277
}),
@@ -284,8 +283,8 @@ const romasku = {
284283
['detached', 0],
285284
...Array.from({ length: output_cnt || 2 }, (_, i) => [`cover_${i + 1}`, i + 1])
286285
]),
287-
cluster: "manuSpecificTuyaCoverSwitchConfig",
288-
attribute: "coverIndex",
286+
cluster: 0xFC01,
287+
attribute: {ID: 0x0001, type: Zcl.DataType.UINT8},
289288
description: "Which cover to control locally (detached = no local control)",
290289
entityCategory: "config",
291290
}),
@@ -295,8 +294,8 @@ const romasku = {
295294
endpointName,
296295
valueOn: ["ON", 1],
297296
valueOff: ["OFF", 0],
298-
cluster: "manuSpecificTuyaCoverSwitchConfig",
299-
attribute: "reversal",
297+
cluster: 0xFC01,
298+
attribute: {ID: 0x0002, type: Zcl.DataType.BOOLEAN},
300299
description: "Inverts UP/DOWN direction for inputs",
301300
access: "ALL",
302301
entityCategory: "config",
@@ -306,8 +305,8 @@ const romasku = {
306305
name,
307306
endpointName,
308307
lookup: { immediate: 0, short_press: 1, long_press: 2, hybrid: 3 },
309-
cluster: "manuSpecificTuyaCoverSwitchConfig",
310-
attribute: "localMode",
308+
cluster: 0xFC01,
309+
attribute: {ID: 0x0003, type: Zcl.DataType.ENUM8},
311310
description: "When to trigger local cover: immediate (start/stop on press), short_press (trigger on release), long_press (trigger after long press duration), hybrid (trigger on release or continuous movement while held). Only affects momentary switches",
312311
entityCategory: "config",
313312
}),
@@ -316,17 +315,17 @@ const romasku = {
316315
name,
317316
endpointName,
318317
lookup: { immediate: 0, short_press: 1, long_press: 2, hybrid: 3 },
319-
cluster: "manuSpecificTuyaCoverSwitchConfig",
320-
attribute: "bindedMode",
318+
cluster: 0xFC01,
319+
attribute: {ID: 0x0004, type: Zcl.DataType.ENUM8},
321320
description: "When to send commands to bound devices: immediate (start/stop on press), short_press (trigger on release), long_press (trigger after long press duration), hybrid (trigger on release or continuous movement while held). Only affects momentary switches",
322321
entityCategory: "config",
323322
}),
324323
coverSwitchLongPressDuration: (name, endpointName) =>
325324
numeric({
326325
name,
327326
endpointNames: [endpointName],
328-
cluster: "manuSpecificTuyaCoverSwitchConfig",
329-
attribute: "longPressDuration",
327+
cluster: 0xFC01,
328+
attribute: {ID: 0x0005, type: Zcl.DataType.UINT16},
330329
description: "Threshold in milliseconds to distinguish short press from long press",
331330
valueMin: 0,
332331
valueMax: 5000,
@@ -343,7 +342,7 @@ const romasku = {
343342
closing: 2
344343
},
345344
cluster: "closuresWindowCovering",
346-
attribute: "moving",
345+
attribute: {ID: 0xff00, type: Zcl.DataType.ENUM8},
347346
description: "Cover movement status",
348347
entityCategory: "diagnostic",
349348
}),
@@ -354,7 +353,7 @@ const romasku = {
354353
valueOn: [true, 1],
355354
valueOff: [false, 0],
356355
cluster: "closuresWindowCovering",
357-
attribute: "motorReversal",
356+
attribute: {ID: 0xff01, type: Zcl.DataType.BOOLEAN},
358357
description: "Reverse motor direction (swap OPEN/CLOSE relays)",
359358
entityCategory: "config",
360359
}),
@@ -363,7 +362,7 @@ const romasku = {
363362
name,
364363
endpointNames: [endpointName],
365364
cluster: "closuresWindowCovering",
366-
attribute: "openTime",
365+
attribute: {ID: 0xff02, type: Zcl.DataType.UINT16},
367366
description: "Travel time for the OPENING direction (0.1 s precision). " +
368367
"For symmetric covers you only need to set this value. " +
369368
"close_time will fall back to open_time automatically.",
@@ -379,7 +378,7 @@ const romasku = {
379378
name,
380379
endpointNames: [endpointName],
381380
cluster: "closuresWindowCovering",
382-
attribute: "closeTime",
381+
attribute: {ID: 0xff03, type: Zcl.DataType.UINT16},
383382
description: "Travel time for the CLOSING direction (0.1 s precision). " +
384383
"Set to 0 to use the same value as open_time. " +
385384
"Set explicitly only if closing speed differs from opening speed.",
@@ -395,7 +394,7 @@ const romasku = {
395394
name,
396395
endpointNames: [endpointName],
397396
cluster: "closuresWindowCovering",
398-
attribute: "closedDeadzone",
397+
attribute: {ID: 0xff04, type: Zcl.DataType.UINT16},
399398
description: "Mechanical deadzone at the CLOSED end (0%) as a percentage of " +
400399
"total travel. Motor movement within this zone does not change the " +
401400
"reported position.",
@@ -409,7 +408,7 @@ const romasku = {
409408
name,
410409
endpointNames: [endpointName],
411410
cluster: "closuresWindowCovering",
412-
attribute: "openDeadzone",
411+
attribute: {ID: 0xff05, type: Zcl.DataType.UINT16},
413412
description: "Mechanical deadzone at the OPEN end (100%) as a percentage of " +
414413
"total travel. Motor movement within this zone does not change the " +
415414
"reported position.",
@@ -436,35 +435,6 @@ const definitions = [
436435
{% if device.has_battery_cluster %}
437436
romasku.batteryPercentage(),
438437
{% endif %}
439-
{% if device.coverSwitchNames %}
440-
deviceAddCustomCluster("manuSpecificTuyaCoverSwitchConfig", {
441-
ID: 0xFC01,
442-
manufacturerCode: 0x125D,
443-
attributes: {
444-
switchType: {ID: 0x0000, type: Zcl.DataType.ENUM8, write: true},
445-
coverIndex: {ID: 0x0001, type: Zcl.DataType.UINT8, write: true},
446-
reversal: {ID: 0x0002, type: Zcl.DataType.BOOLEAN, write: true},
447-
localMode: {ID: 0x0003, type: Zcl.DataType.ENUM8, write: true},
448-
bindedMode: {ID: 0x0004, type: Zcl.DataType.ENUM8, write: true},
449-
longPressDuration: {ID: 0x0005, type: Zcl.DataType.UINT16, write: true},
450-
},
451-
commands: {},
452-
commandsResponse: {},
453-
}),
454-
{% endif %}
455-
{% if device.coverNames %}
456-
deviceAddCustomCluster("closuresWindowCovering", {
457-
ID: 0x0102,
458-
attributes: {
459-
moving: {ID: 0xff00, type: Zcl.DataType.ENUM8},
460-
motorReversal: {ID: 0xff01, type: Zcl.DataType.BOOLEAN, write: true},
461-
openTime: {ID: 0xff02, type: Zcl.DataType.UINT16, write: true},
462-
closeTime: {ID: 0xff03, type: Zcl.DataType.UINT16, write: true},
463-
closedDeadzone: {ID: 0xff04, type: Zcl.DataType.UINT16, write: true},
464-
openDeadzone: {ID: 0xff05, type: Zcl.DataType.UINT16, write: true},
465-
},
466-
}),
467-
{% endif %}
468438
deviceEndpoints({ endpoints: {
469439
{%- for switchName in device.switchNames -%}
470440
"{{switchName}}": {{loop.index}},{{" "}}
@@ -597,7 +567,7 @@ const definitions = [
597567
await reporting.bind(cover{{loop.index}}, coordinatorEndpoint, ["closuresWindowCovering"]);
598568
await cover{{loop.index}}.configureReporting("closuresWindowCovering", [
599569
{
600-
attribute: "moving",
570+
attribute: {ID: 0xff00, type: Zcl.DataType.ENUM8},
601571
minimumReportInterval: 0,
602572
maximumReportInterval: constants.repInterval.MAX,
603573
reportableChange: 1,

0 commit comments

Comments
 (0)