zigbee-herdsman-converters 25.92.0 → 25.94.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/CHANGELOG.md +53 -0
  2. package/dist/converters/fromZigbee.d.ts +1 -1
  3. package/dist/converters/fromZigbee.d.ts.map +1 -1
  4. package/dist/converters/fromZigbee.js +4 -4
  5. package/dist/converters/fromZigbee.js.map +1 -1
  6. package/dist/devices/ctm.d.ts.map +1 -1
  7. package/dist/devices/ctm.js +51 -9
  8. package/dist/devices/ctm.js.map +1 -1
  9. package/dist/devices/dresden_elektronik.d.ts.map +1 -1
  10. package/dist/devices/dresden_elektronik.js +16 -6
  11. package/dist/devices/dresden_elektronik.js.map +1 -1
  12. package/dist/devices/ezviz.d.ts +3 -0
  13. package/dist/devices/ezviz.d.ts.map +1 -0
  14. package/dist/devices/ezviz.js +47 -0
  15. package/dist/devices/ezviz.js.map +1 -0
  16. package/dist/devices/gewiss.d.ts.map +1 -1
  17. package/dist/devices/gewiss.js +2 -1
  18. package/dist/devices/gewiss.js.map +1 -1
  19. package/dist/devices/heiman.d.ts.map +1 -1
  20. package/dist/devices/heiman.js +948 -256
  21. package/dist/devices/heiman.js.map +1 -1
  22. package/dist/devices/index.d.ts.map +1 -1
  23. package/dist/devices/index.js +2 -0
  24. package/dist/devices/index.js.map +1 -1
  25. package/dist/devices/jxuan.js +1 -1
  26. package/dist/devices/jxuan.js.map +1 -1
  27. package/dist/devices/ledvance.d.ts.map +1 -1
  28. package/dist/devices/ledvance.js +14 -20
  29. package/dist/devices/ledvance.js.map +1 -1
  30. package/dist/devices/legrand.d.ts.map +1 -1
  31. package/dist/devices/legrand.js +8 -1
  32. package/dist/devices/legrand.js.map +1 -1
  33. package/dist/devices/moes.d.ts.map +1 -1
  34. package/dist/devices/moes.js +98 -0
  35. package/dist/devices/moes.js.map +1 -1
  36. package/dist/devices/muller_licht.d.ts.map +1 -1
  37. package/dist/devices/muller_licht.js +7 -0
  38. package/dist/devices/muller_licht.js.map +1 -1
  39. package/dist/devices/paulmann.d.ts.map +1 -1
  40. package/dist/devices/paulmann.js +7 -0
  41. package/dist/devices/paulmann.js.map +1 -1
  42. package/dist/devices/philips.d.ts.map +1 -1
  43. package/dist/devices/philips.js +37 -13
  44. package/dist/devices/philips.js.map +1 -1
  45. package/dist/devices/sonoff.d.ts.map +1 -1
  46. package/dist/devices/sonoff.js +5 -0
  47. package/dist/devices/sonoff.js.map +1 -1
  48. package/dist/devices/tuya.d.ts.map +1 -1
  49. package/dist/devices/tuya.js +440 -16
  50. package/dist/devices/tuya.js.map +1 -1
  51. package/dist/lib/modernExtend.d.ts.map +1 -1
  52. package/dist/lib/modernExtend.js +3 -3
  53. package/dist/lib/modernExtend.js.map +1 -1
  54. package/dist/models-index.json +1 -1
  55. package/package.json +3 -3
@@ -43,218 +43,748 @@ const heiman_1 = require("../lib/heiman");
43
43
  const m = __importStar(require("../lib/modernExtend"));
44
44
  const reporting = __importStar(require("../lib/reporting"));
45
45
  const tuya = __importStar(require("../lib/tuya"));
46
+ const utils = __importStar(require("../lib/utils"));
46
47
  const e = exposes.presets;
47
48
  const ea = exposes.access;
48
- const fzLocal = {
49
- occupancyRadarHeiman: {
50
- cluster: "msOccupancySensing",
51
- type: ["attributeReport", "readResponse"],
52
- convert: (model, msg, publish, options, meta) => {
53
- const result = {};
54
- if (Object.hasOwn(msg.data, "occupancy")) {
55
- const occupancy = msg.data.occupancy;
56
- const bit0 = occupancy & 0x01; // Bit 0: Occupancy (0: no one, 1: someone)
57
- const bit1to3 = (occupancy >> 1) & 0x07; // Bits 1-3: Sensor status
58
- const bit4to5 = (occupancy >> 4) & 0x03; // Bits 4-5: Fall status
59
- // Interpretación de los estados
60
- result.occupancy = bit0 === 1;
61
- result.sensor_status = ["none", "activity"][bit1to3] || "unknown";
62
- result.fall_status = ["normal", "fall_warning", "fall_alarm"][bit4to5] || "unknown";
63
- }
64
- return result;
49
+ const defaultResponseOptions = { disableDefaultResponse: false };
50
+ const iasWarningMode = { stop: 0, burglar: 1, fire: 2, emergency: 3, police_panic: 4, fire_panic: 5, emergency_panic: 6 };
51
+ const heimanExtend = {
52
+ heimanClusterRadar: () => m.deviceAddCustomCluster("heimanClusterRadar", {
53
+ ID: 0xfc8b,
54
+ manufacturerCode: zigbee_herdsman_1.Zcl.ManufacturerCode.HEIMAN_TECHNOLOGY_CO_LTD,
55
+ attributes: {
56
+ enableIndicator: { ID: 0xf001, type: zigbee_herdsman_1.Zcl.DataType.UINT8, write: true }, // 0: off, 1: enable
57
+ sensitivity: { ID: 0xf002, type: zigbee_herdsman_1.Zcl.DataType.UINT8, write: true },
58
+ enableSubRegionIsolation: { ID: 0xf006, type: zigbee_herdsman_1.Zcl.DataType.UINT8, write: true },
59
+ installationMethod: { ID: 0xf007, type: zigbee_herdsman_1.Zcl.DataType.UINT8, write: true },
60
+ cellMountedTable: { ID: 0xf008, type: zigbee_herdsman_1.Zcl.DataType.OCTET_STR, write: true }, // string
61
+ wallMountedTable: { ID: 0xf009, type: zigbee_herdsman_1.Zcl.DataType.OCTET_STR, write: true }, // string
62
+ subRegionIsolationTable: { ID: 0xf00a, type: zigbee_herdsman_1.Zcl.DataType.OCTET_STR, write: true }, // string
65
63
  },
64
+ commands: {},
65
+ commandsResponse: {},
66
+ }),
67
+ heimanClusterSpecial: () => m.deviceAddCustomCluster("heimanClusterSpecial", {
68
+ ID: 0xfc90,
69
+ manufacturerCode: zigbee_herdsman_1.Zcl.ManufacturerCode.HEIMAN_TECHNOLOGY_CO_LTD,
70
+ attributes: {
71
+ // Sensor 0x0000~0x0FFF
72
+ sensorPreheatingState: { ID: 0x0000, type: zigbee_herdsman_1.Zcl.DataType.ENUM8, write: true },
73
+ sensorSelfCheckState: { ID: 0x0001, type: zigbee_herdsman_1.Zcl.DataType.ENUM8, write: true },
74
+ sensorFaultState: { ID: 0x0002, type: zigbee_herdsman_1.Zcl.DataType.BITMAP16, write: true },
75
+ sensorPollutionLevel: { ID: 0x0003, type: zigbee_herdsman_1.Zcl.DataType.UINT8, write: true },
76
+ sensorSensitivityLevel: { ID: 0x0004, type: zigbee_herdsman_1.Zcl.DataType.ENUM8, write: true },
77
+ sensorPrealarmThreshold: { ID: 0x0005, type: zigbee_herdsman_1.Zcl.DataType.ENUM8, write: true },
78
+ sensorLifeState: { ID: 0x0006, type: zigbee_herdsman_1.Zcl.DataType.ENUM8, write: true },
79
+ sensorLifeTime: { ID: 0x0007, type: zigbee_herdsman_1.Zcl.DataType.UINT16, write: true },
80
+ deviceMuteControl: { ID: 0x0008, type: zigbee_herdsman_1.Zcl.DataType.BITMAP32, write: true },
81
+ deviceMuteState: { ID: 0x0009, type: zigbee_herdsman_1.Zcl.DataType.BITMAP16, write: true },
82
+ deviceCascadeControlEnable: { ID: 0x000a, type: zigbee_herdsman_1.Zcl.DataType.BITMAP8, write: true },
83
+ deviceSoundToneType: { ID: 0x000b, type: zigbee_herdsman_1.Zcl.DataType.ENUM8, write: true },
84
+ deviceSoundControl: { ID: 0x000c, type: zigbee_herdsman_1.Zcl.DataType.ARRAY, write: true },
85
+ deviceBlinkControl: { ID: 0x000d, type: zigbee_herdsman_1.Zcl.DataType.ARRAY, write: true },
86
+ smokeAdValue: { ID: 0x000e, type: zigbee_herdsman_1.Zcl.DataType.UINT16, write: true },
87
+ smokeAlarmType: { ID: 0x000f, type: zigbee_herdsman_1.Zcl.DataType.ENUM8, write: true },
88
+ smokeWaterMistState: { ID: 0x0010, type: zigbee_herdsman_1.Zcl.DataType.ENUM8, write: true },
89
+ smokeSensorData: { ID: 0x0011, type: zigbee_herdsman_1.Zcl.DataType.ARRAY, write: true },
90
+ deviceCascadeState: { ID: 0x0012, type: zigbee_herdsman_1.Zcl.DataType.ENUM8, write: true },
91
+ sensorPrealarmState: { ID: 0x0013, type: zigbee_herdsman_1.Zcl.DataType.ENUM8, write: true },
92
+ smokeConcentrationLevel: { ID: 0x0016, type: zigbee_herdsman_1.Zcl.DataType.UINT8 },
93
+ smokeChamberContaminationLevel: { ID: 0x0017, type: zigbee_herdsman_1.Zcl.DataType.UINT8 },
94
+ smokeConcentationUnit: { ID: 0x0018, type: zigbee_herdsman_1.Zcl.DataType.UINT8 },
95
+ // Light/Switch 0x1000~0x1FFF
96
+ indicatorLightControl: { ID: 0x1000, type: zigbee_herdsman_1.Zcl.DataType.BITMAP8, write: true },
97
+ indicatorLightNotDisturbStartTime: { ID: 0x1001, type: zigbee_herdsman_1.Zcl.DataType.UINT16, write: true },
98
+ indicatorLightNotDisturbEndTime: { ID: 0x1002, type: zigbee_herdsman_1.Zcl.DataType.UINT16, write: true },
99
+ indicatorLightNotDisturbEnable: { ID: 0x1003, type: zigbee_herdsman_1.Zcl.DataType.UINT8, write: true },
100
+ indicatorLightLevelControlOf1: { ID: 0x1004, type: zigbee_herdsman_1.Zcl.DataType.UINT8, write: true },
101
+ indicatorLightLevelControlOf2: { ID: 0x1005, type: zigbee_herdsman_1.Zcl.DataType.UINT8, write: true },
102
+ indicatorLightLevelControlOf3: { ID: 0x1006, type: zigbee_herdsman_1.Zcl.DataType.UINT8, write: true },
103
+ interconnectable: { ID: 0x1007, type: zigbee_herdsman_1.Zcl.DataType.UINT8, write: true },
104
+ smokeUnit: { ID: 0x1008, type: zigbee_herdsman_1.Zcl.DataType.UINT8 },
105
+ rebootedCount: { ID: 0x1009, type: zigbee_herdsman_1.Zcl.DataType.UINT8 },
106
+ rejoinedCount: { ID: 0x100a, type: zigbee_herdsman_1.Zcl.DataType.UINT8 },
107
+ reportedPackages: { ID: 0x100b, type: zigbee_herdsman_1.Zcl.DataType.UINT8 },
108
+ },
109
+ commands: {},
110
+ commandsResponse: {},
111
+ }),
112
+ heimanClusterIasZone: () => m.deviceAddCustomCluster("ssIasZone", {
113
+ ID: zigbee_herdsman_1.Zcl.Clusters.ssIasZone.ID,
114
+ attributes: {},
115
+ commands: {
116
+ initiateTestMode: {
117
+ ID: 0x02,
118
+ parameters: [
119
+ { name: "testModeDuration", type: zigbee_herdsman_1.Zcl.DataType.UINT8 },
120
+ { name: "sensitivityLevel", type: zigbee_herdsman_1.Zcl.DataType.UINT8 },
121
+ ],
122
+ },
123
+ },
124
+ commandsResponse: {},
125
+ }),
126
+ // ModernExtend define
127
+ heimanClusterRadarActiveIndicatorExtend: () => {
128
+ const clusterName = "heimanClusterRadar";
129
+ const exposes = utils.exposeEndpoints(e.binary("enable_indicator", ea.ALL, true, false).withDescription("active green indicator"));
130
+ const fromZigbee = [
131
+ {
132
+ cluster: clusterName,
133
+ type: ["attributeReport", "readResponse"],
134
+ convert: (model, msg, publish, options, meta) => {
135
+ if (msg.data.enableIndicator === undefined) {
136
+ return;
137
+ }
138
+ const state = !!msg.data["enableIndicator"];
139
+ return { enable_indicator: state };
140
+ },
141
+ },
142
+ ];
143
+ const toZigbee = [
144
+ {
145
+ key: ["enable_indicator"],
146
+ convertGet: async (entity, key, meta) => {
147
+ await entity.read(clusterName, ["enableIndicator"], defaultResponseOptions);
148
+ },
149
+ convertSet: async (entity, key, value, meta) => {
150
+ // const state = (value as Record<string, unknown>) || {};
151
+ const state = value ? 1 : 0;
152
+ await entity.write(clusterName, { enableIndicator: state }, defaultResponseOptions);
153
+ },
154
+ },
155
+ ];
156
+ return { exposes: exposes, fromZigbee, toZigbee, isModernExtend: true };
66
157
  },
67
- radarSensorHeiman: {
68
- cluster: "RadarSensorHeiman",
69
- type: ["attributeReport", "readResponse"],
70
- convert: (model, msg, publish, options, meta) => {
71
- const result = {};
72
- const mapAttributes = {
73
- enable_indicator: "enable_indicator",
74
- sensitivity: "sensitivity",
75
- enable_sub_region_isolation: "enable_sub_region_isolation",
76
- installation_method: "installation_method",
77
- cell_mounted_table: "cell_mounted_table",
78
- wall_mounted_table: "wall_mounted_table",
79
- sub_region_isolation_table: "sub_region_isolation_table",
80
- };
81
- for (const key of Object.keys(msg.data)) {
82
- if (mapAttributes[key]) {
83
- const value = msg.data[key];
158
+ heimanClusterRadarSubRegionEnableExtend: () => {
159
+ const clusterName = "heimanClusterRadar";
160
+ const exposes = utils.exposeEndpoints(e.binary("enable_sub_region_isolation", ea.ALL, true, false).withDescription("active green indicator"));
161
+ const fromZigbee = [
162
+ {
163
+ cluster: clusterName,
164
+ type: ["attributeReport", "readResponse"],
165
+ convert: (model, msg, publish, options, meta) => {
166
+ if (msg.data.enableSubRegionIsolation === undefined) {
167
+ return;
168
+ }
169
+ const state = !!msg.data["enableSubRegionIsolation"];
170
+ return { enable_sub_region_isolation: state };
171
+ },
172
+ },
173
+ ];
174
+ const toZigbee = [
175
+ {
176
+ key: ["enable_sub_region_isolation"],
177
+ convertGet: async (entity, key, meta) => {
178
+ await entity.read(clusterName, ["enableSubRegionIsolation"], defaultResponseOptions);
179
+ },
180
+ convertSet: async (entity, key, value, meta) => {
181
+ const state = value ? 1 : 0;
182
+ await entity.write(clusterName, { enableSubRegionIsolation: state }, defaultResponseOptions);
183
+ },
184
+ },
185
+ ];
186
+ return {
187
+ exposes: exposes,
188
+ fromZigbee,
189
+ toZigbee,
190
+ isModernExtend: true,
191
+ };
192
+ },
193
+ heimanClusterRadarSensitivityExtend: () => {
194
+ const clusterName = "heimanClusterRadar";
195
+ const exposes = utils.exposeEndpoints(e
196
+ .numeric("sensitivity", ea.ALL)
197
+ .withUnit("%")
198
+ .withValueMin(0)
199
+ .withValueMax(100)
200
+ .withDescription("Sensitivity of the radar sensor in range of 0 ~ 100%"));
201
+ const fromZigbee = [
202
+ {
203
+ cluster: clusterName,
204
+ type: ["attributeReport", "readResponse"],
205
+ convert: (model, msg, publish, options, meta) => {
206
+ let attrData = null;
207
+ if (msg.data.sensitivity === undefined) {
208
+ return;
209
+ }
210
+ attrData = msg.data["sensitivity"];
211
+ return { sensitivity: attrData };
212
+ },
213
+ },
214
+ ];
215
+ const toZigbee = [
216
+ {
217
+ key: ["sensitivity"],
218
+ convertGet: async (entity, key, meta) => {
219
+ await entity.read(clusterName, ["sensitivity"], defaultResponseOptions);
220
+ },
221
+ convertSet: async (entity, key, value, meta) => {
222
+ const state = Number(value);
223
+ await entity.write(clusterName, { sensitivity: state }, defaultResponseOptions);
224
+ },
225
+ },
226
+ ];
227
+ return {
228
+ exposes: exposes,
229
+ fromZigbee,
230
+ toZigbee,
231
+ isModernExtend: true,
232
+ };
233
+ },
234
+ heimanClusterRadarCellMountedTableExtend: () => {
235
+ const clusterName = "heimanClusterRadar";
236
+ const exposes = utils.exposeEndpoints(e
237
+ .text("cell_mounted_table", ea.ALL)
238
+ .withDescription("Ceiling installation area coordinate table. Format: 'X1,X2,Y1,Y2,height'. Value range: -2000≤X1≤0, 0≤X2≤2000 -2500≤Y1≤0, 0≤Y2≤2500 2300≤height≤3000 Unit:mm"));
239
+ const fromZigbee = [
240
+ {
241
+ cluster: clusterName,
242
+ type: ["attributeReport", "readResponse"],
243
+ convert: (model, msg, publish, options, meta) => {
244
+ const result = {};
245
+ const value = msg.data["cellMountedTable"];
84
246
  if (Buffer.isBuffer(value) && value.length >= 5) {
85
- try {
86
- if (key === "cell_mounted_table") {
87
- if (value.length !== 10) {
88
- throw new Error(`Invalid cell_mounted_table data length: expected 10 bytes, got ${value.length}.`);
89
- }
90
- const coordinates = [
91
- value.readInt16LE(0), // x1
92
- value.readInt16LE(2), // y1
93
- value.readInt16LE(4), // x2
94
- value.readInt16LE(6), // y2
95
- value.readInt16LE(8), // height
96
- ];
97
- result.cell_mounted_table = coordinates.join(",");
98
- }
99
- else if (key === "wall_mounted_table") {
100
- if (value.length !== 8) {
101
- throw new Error(`Invalid wall_mounted_table data length: expected 8 bytes, got ${value.length}.`);
102
- }
103
- const coordinates = [
104
- value.readInt16LE(0), // x1
105
- value.readInt16LE(2), // y1
106
- value.readInt16LE(4), // x2
107
- value.readInt16LE(6), // height
108
- ];
109
- result.wall_mounted_table = coordinates.join(",");
110
- }
111
- else if (key === "sub_region_isolation_table") {
112
- if (value.length !== 12) {
113
- throw new Error(`Invalid sub_region_isolation_table data length: expected 12 bytes, got ${value.length}.`);
114
- }
115
- const coordinates = [
116
- value.readInt16LE(0), // x1
117
- value.readInt16LE(2), // y1
118
- value.readInt16LE(4), // x2
119
- value.readInt16LE(6), // y2
120
- value.readInt16LE(8), // z1
121
- value.readInt16LE(10), // z2
122
- ];
123
- result.sub_region_isolation_table = coordinates.join(",");
124
- }
247
+ console.log(value);
248
+ if (value.length !== 10) {
249
+ throw new Error(`Invalid cell_mounted_table data length: expected 10 bytes, got ${value.length}.`);
125
250
  }
126
- catch (error) {
127
- console.error(`Error decoding attribute ${key}: ${error.message}`);
251
+ const coordinates = [
252
+ value.readInt16LE(0), // x1
253
+ value.readInt16LE(2), // y1
254
+ value.readInt16LE(4), // x2
255
+ value.readInt16LE(6), // y2
256
+ value.readInt16LE(8), // height
257
+ ];
258
+ result.cell_mounted_table = coordinates.join(",");
259
+ console.log(result);
260
+ }
261
+ return result;
262
+ },
263
+ },
264
+ ];
265
+ const toZigbee = [
266
+ {
267
+ key: ["cell_mounted_table"],
268
+ convertGet: async (entity, key, meta) => {
269
+ await entity.read(clusterName, ["cellMountedTable"], defaultResponseOptions);
270
+ },
271
+ convertSet: async (entity, key, value, meta) => {
272
+ if (key === "cell_mounted_table" && value !== "") {
273
+ const coordinates = value.split(",").map((v) => Number.parseInt(v, 10));
274
+ if (coordinates.length !== 5) {
275
+ throw new Error("cell_mounted_table must be a string with 5 comma-separated values (e.g., '-2000,2000,-2500,2500,2300')");
276
+ }
277
+ // Range check
278
+ if (coordinates[0] < -2000 ||
279
+ coordinates[0] > 0 || // X1
280
+ coordinates[1] < 0 ||
281
+ coordinates[1] > 2000 || // X2
282
+ coordinates[2] < -2500 ||
283
+ coordinates[2] > 0 || // Y1
284
+ coordinates[3] < 0 ||
285
+ coordinates[3] > 2500 || // Y2
286
+ coordinates[4] < 2300 ||
287
+ coordinates[4] > 3000 // height
288
+ ) {
289
+ throw new Error("Values out of range for Cell Mounted Table.");
290
+ }
291
+ const buffer = Buffer.alloc(10); // 10 bytes + 1 byte
292
+ buffer.writeInt16LE(coordinates[0], 0); // x1
293
+ buffer.writeInt16LE(coordinates[1], 2); // x2
294
+ buffer.writeInt16LE(coordinates[2], 4); // y1
295
+ buffer.writeInt16LE(coordinates[3], 6); // y2
296
+ buffer.writeInt16LE(coordinates[4], 8); // height
297
+ await entity.write(clusterName, { cellMountedTable: buffer }, defaultResponseOptions);
298
+ }
299
+ },
300
+ },
301
+ ];
302
+ return {
303
+ exposes: exposes,
304
+ fromZigbee,
305
+ toZigbee,
306
+ isModernExtend: true,
307
+ };
308
+ },
309
+ heimanClusterRadarWallMountedTableExtend: () => {
310
+ const clusterName = "heimanClusterRadar";
311
+ const exposes = utils.exposeEndpoints(e
312
+ .text("wall_mounted_table", ea.ALL)
313
+ .withDescription("Wall-mounted installation area coordinate table. Format: 'X1,X2,Y2,height' Value range: -2000≤X1≤0, 0≤X2≤2000 200≤Y2≤4000 1500≤height≤1600 Unit:mm."));
314
+ const fromZigbee = [
315
+ {
316
+ cluster: clusterName,
317
+ type: ["attributeReport", "readResponse"],
318
+ convert: (model, msg, publish, options, meta) => {
319
+ const result = {};
320
+ const value = msg.data["wallMountedTable"];
321
+ if (Buffer.isBuffer(value) && value.length >= 5) {
322
+ if (value.length !== 8) {
323
+ throw new Error(`Invalid wall_mounted_table data length: expected 8 bytes, got ${value.length}.`);
128
324
  }
325
+ const coordinates = [
326
+ value.readInt16LE(0), // x1
327
+ value.readInt16LE(2), // y1
328
+ value.readInt16LE(4), // x2
329
+ value.readInt16LE(6), // height
330
+ ];
331
+ result.wall_mounted_table = coordinates.join(",");
129
332
  }
130
- else {
131
- result[mapAttributes[key]] = value;
333
+ return result;
334
+ },
335
+ },
336
+ ];
337
+ const toZigbee = [
338
+ {
339
+ key: ["wall_mounted_table"],
340
+ convertGet: async (entity, key, meta) => {
341
+ await entity.read(clusterName, ["wallMountedTable"], defaultResponseOptions);
342
+ },
343
+ convertSet: async (entity, key, value, meta) => {
344
+ if (key === "wall_mounted_table" && value !== "") {
345
+ const coordinates = value.split(",").map((v) => Number.parseInt(v, 10));
346
+ if (coordinates.length !== 4) {
347
+ throw new Error("wall_mounted_table must be a string with 4 comma-separated values (e.g., '-2000,2000,4000,1600')");
348
+ }
349
+ if (coordinates[0] < -2000 ||
350
+ coordinates[0] > 0 || // X1
351
+ coordinates[1] < 0 ||
352
+ coordinates[1] > 2000 || // X2
353
+ coordinates[2] < 200 ||
354
+ coordinates[2] > 4000 || // Y2
355
+ coordinates[3] < 1500 ||
356
+ coordinates[3] > 1600 // height
357
+ ) {
358
+ throw new Error("Values out of range for Wall Mounted Table.");
359
+ }
360
+ const buffer = Buffer.alloc(8); // 8 bytes + 1 byte
361
+ buffer.writeInt16LE(coordinates[0], 0); // x1
362
+ buffer.writeInt16LE(coordinates[1], 2); // x2
363
+ buffer.writeInt16LE(coordinates[2], 4); // y2
364
+ buffer.writeInt16LE(coordinates[3], 6); // height
365
+ await entity.write(clusterName, { wallMountedTable: buffer }, defaultResponseOptions);
132
366
  }
133
- }
134
- }
135
- return result;
136
- },
367
+ },
368
+ },
369
+ ];
370
+ return {
371
+ exposes: exposes,
372
+ fromZigbee,
373
+ toZigbee,
374
+ isModernExtend: true,
375
+ };
137
376
  },
138
- };
139
- const tzLocal = {
140
- radarSensorHeiman: {
141
- key: [
142
- "enable_indicator",
143
- "sensitivity",
144
- "enable_sub_region_isolation",
145
- "installation_method",
146
- "cell_mounted_table",
147
- "wall_mounted_table",
148
- "sub_region_isolation_table",
149
- ],
150
- convertSet: async (entity, key, value, meta) => {
151
- const cluster = "RadarSensorHeiman";
152
- const mapAttributes = {
153
- enable_indicator: { id: 0xf001, type: 0x20 },
154
- sensitivity: { id: 0xf002, type: 0x20 },
155
- enable_sub_region_isolation: { id: 0xf006, type: 0x20 },
156
- installation_method: { id: 0xf007, type: 0x20 },
157
- cell_mounted_table: { id: 0xf008, type: 0x41 }, // string
158
- wall_mounted_table: { id: 0xf009, type: 0x41 }, // string
159
- sub_region_isolation_table: { id: 0xf00a, type: 0x41 }, // string
160
- };
161
- const attributeInfo = mapAttributes[key];
162
- if (!attributeInfo) {
163
- throw new Error(`Unsupported attribute: ${key}`);
164
- }
165
- const { id, type } = attributeInfo;
166
- let payloadValue = value;
167
- if (key === "cell_mounted_table" && value !== "") {
168
- const coordinates = value.split(",").map((v) => Number.parseInt(v, 10));
169
- if (coordinates.length !== 5) {
170
- throw new Error("cell_mounted_table must be a string with 5 comma-separated values (e.g., '-2000,2000,-2500,2500,2300')");
171
- }
172
- // Rango de valores
173
- if (coordinates[0] < -2000 ||
174
- coordinates[0] > 0 || // X1
175
- coordinates[1] < 0 ||
176
- coordinates[1] > 2000 || // X2
177
- coordinates[2] < -2500 ||
178
- coordinates[2] > 0 || // Y1
179
- coordinates[3] < 0 ||
180
- coordinates[3] > 2500 || // Y2
181
- coordinates[4] < 2300 ||
182
- coordinates[4] > 3000 // height
183
- ) {
184
- throw new Error("Values out of range for Cell Mounted Table.");
185
- }
186
- const buffer = Buffer.alloc(10); // 10 bytes + 1 byte
187
- buffer.writeInt16LE(coordinates[0], 0); // x1
188
- buffer.writeInt16LE(coordinates[1], 2); // x2
189
- buffer.writeInt16LE(coordinates[2], 4); // y1
190
- buffer.writeInt16LE(coordinates[3], 6); // y2
191
- buffer.writeInt16LE(coordinates[4], 8); // height
192
- payloadValue = buffer;
193
- }
194
- else if (key === "wall_mounted_table" && value !== "") {
195
- const coordinates = value.split(",").map((v) => Number.parseInt(v, 10));
196
- if (coordinates.length !== 4) {
197
- throw new Error("wall_mounted_table must be a string with 4 comma-separated values (e.g., '-2000,2000,4000,1600')");
198
- }
199
- if (coordinates[0] < -2000 ||
200
- coordinates[0] > 0 || // X1
201
- coordinates[1] < 0 ||
202
- coordinates[1] > 2000 || // X2
203
- coordinates[2] < 200 ||
204
- coordinates[2] > 4000 || // Y2
205
- coordinates[3] < 1500 ||
206
- coordinates[3] > 1600 // height
207
- ) {
208
- throw new Error("Values out of range for Wall Mounted Table.");
209
- }
210
- const buffer = Buffer.alloc(8); // 8 bytes + 1 byte
211
- buffer.writeInt16LE(coordinates[0], 0); // x1
212
- buffer.writeInt16LE(coordinates[1], 2); // x2
213
- buffer.writeInt16LE(coordinates[2], 4); // y2
214
- buffer.writeInt16LE(coordinates[3], 6); // height
215
- payloadValue = buffer;
216
- }
217
- else if (key === "sub_region_isolation_table" && value !== "") {
218
- const coordinates = value.split(",").map((v) => Number.parseInt(v, 10));
219
- if (coordinates.length !== 6) {
220
- throw new Error("sub_region_isolation_table must be a string with 6 comma-separated values (e.g., '-2000,2000,-2500,2500,2300,3000')");
221
- }
222
- if (coordinates[0] < -2000 ||
223
- coordinates[0] > 2000 || // X1
224
- coordinates[1] < -2000 ||
225
- coordinates[1] > 2000 // X2
226
- ) {
227
- throw new Error("Values out of range for Sub-Region Isolation Table.");
228
- }
229
- const buffer = Buffer.alloc(12); // 12 bytes + 1 byte
230
- buffer.writeInt16LE(coordinates[0], 0); // x1
231
- buffer.writeInt16LE(coordinates[1], 2); // x2
232
- buffer.writeInt16LE(coordinates[2], 4); // y1
233
- buffer.writeInt16LE(coordinates[3], 6); // y2
234
- buffer.writeInt16LE(coordinates[4], 8); // z1
235
- buffer.writeInt16LE(coordinates[5], 10); // z2
236
- payloadValue = buffer;
237
- }
238
- await entity.write(cluster, { [id]: { value: payloadValue, type } }, { manufacturerCode: 0x120b });
239
- return { state: { [key]: value } };
240
- },
241
- convertGet: async (entity, key, meta) => {
242
- const cluster = "RadarSensorHeiman";
243
- const mapAttributes = {
244
- enable_indicator: 0xf001,
245
- sensitivity: 0xf002,
246
- enable_sub_region_isolation: 0xf006,
247
- installation_method: 0xf007,
248
- cell_mounted_table: 0xf008,
249
- wall_mounted_table: 0xf009,
250
- sub_region_isolation_table: 0xf00a,
251
- };
252
- const attributeId = mapAttributes[key];
253
- if (!attributeId) {
254
- throw new Error(`Unsupported attribute for get: ${key}`);
255
- }
256
- await entity.read(cluster, [attributeId], { manufacturerCode: 0x120b });
257
- },
377
+ heimanClusterRadarSubRegionIsolationTableExtend: () => {
378
+ const clusterName = "heimanClusterRadar";
379
+ const exposes = utils.exposeEndpoints(e
380
+ .text("sub_region_isolation_table", ea.ALL)
381
+ .withDescription("Undetectable area coordinate table. Format: 'x1,x2,y1,y2,z1,z2'. Ranges: X1≤x1≤x2≤X2 When wall-mounted: 200≤y1≤y2≤Y2 0≤z1≤z2≤2300 Ceiling installation: Y1≤y1≤y2≤Y2 0≤z1≤z2≤height Unit:mm"));
382
+ const fromZigbee = [
383
+ {
384
+ cluster: clusterName,
385
+ type: ["attributeReport", "readResponse"],
386
+ convert: (model, msg, publish, options, meta) => {
387
+ const result = {};
388
+ const value = msg.data["subRegionIsolationTable"];
389
+ if (Buffer.isBuffer(value) && value.length >= 5) {
390
+ if (value.length !== 12) {
391
+ throw new Error(`Invalid sub_region_isolation_table data length: expected 12 bytes, got ${value.length}.`);
392
+ }
393
+ const coordinates = [
394
+ value.readInt16LE(0), // x1
395
+ value.readInt16LE(2), // y1
396
+ value.readInt16LE(4), // x2
397
+ value.readInt16LE(6), // y2
398
+ value.readInt16LE(8), // z1
399
+ value.readInt16LE(10), // z2
400
+ ];
401
+ result.sub_region_isolation_table = coordinates.join(",");
402
+ }
403
+ return result;
404
+ },
405
+ },
406
+ ];
407
+ const toZigbee = [
408
+ {
409
+ key: ["sub_region_isolation_table"],
410
+ convertGet: async (entity, key, meta) => {
411
+ await entity.read(clusterName, ["subRegionIsolationTable"], defaultResponseOptions);
412
+ },
413
+ convertSet: async (entity, key, value, meta) => {
414
+ if (key === "sub_region_isolation_table" && value !== "") {
415
+ const coordinates = value.split(",").map((v) => Number.parseInt(v, 10));
416
+ if (coordinates.length !== 6) {
417
+ throw new Error("sub_region_isolation_table must be a string with 6 comma-separated values (e.g., '-2000,2000,-2500,2500,2300,3000')");
418
+ }
419
+ if (coordinates[0] < -2000 ||
420
+ coordinates[0] > 2000 || // X1
421
+ coordinates[1] < -2000 ||
422
+ coordinates[1] > 2000 // X2
423
+ ) {
424
+ throw new Error("Values out of range for Sub-Region Isolation Table.");
425
+ }
426
+ const buffer = Buffer.alloc(12); // 12 bytes + 1 byte
427
+ buffer.writeInt16LE(coordinates[0], 0); // x1
428
+ buffer.writeInt16LE(coordinates[1], 2); // x2
429
+ buffer.writeInt16LE(coordinates[2], 4); // y1
430
+ buffer.writeInt16LE(coordinates[3], 6); // y2
431
+ buffer.writeInt16LE(coordinates[4], 8); // z1
432
+ buffer.writeInt16LE(coordinates[5], 10); // z2
433
+ await entity.write(clusterName, { subRegionIsolationTable: buffer }, defaultResponseOptions);
434
+ }
435
+ },
436
+ },
437
+ ];
438
+ return {
439
+ exposes: exposes,
440
+ fromZigbee,
441
+ toZigbee,
442
+ isModernExtend: true,
443
+ };
444
+ },
445
+ heimanClusterRadarSenseExtend: () => {
446
+ const clusterName = "msOccupancySensing";
447
+ const exposes = [
448
+ e.binary("occupancy", ea.STATE, true, false).withDescription("Indicates if someone is present"),
449
+ e.enum("sensor_status", ea.STATE, ["none", "activity", "unknown"]).withDescription("Sensor activity status"),
450
+ e.enum("fall_status", ea.STATE, ["normal", "fall_warning", "fall_alarm", "unknown"]).withDescription("Fall detection status"),
451
+ ];
452
+ const fromZigbee = [
453
+ {
454
+ cluster: clusterName,
455
+ type: ["attributeReport", "readResponse"],
456
+ convert: (model, msg, publish, options, meta) => {
457
+ const result = {};
458
+ if (Object.hasOwn(msg.data, "occupancy")) {
459
+ const occupancy = msg.data.occupancy;
460
+ const bit0 = occupancy & 0x01; // Bit 0: Occupancy (0: no one, 1: someone)
461
+ const bit1to3 = (occupancy >> 1) & 0x07; // Bits 1-3: Sensor status
462
+ const bit4to5 = (occupancy >> 4) & 0x03; // Bits 4-5: Fall status
463
+ // Interprete bitmap
464
+ result.occupancy = bit0 === 1;
465
+ result.sensor_status = ["none", "activity"][bit1to3] || "unknown";
466
+ result.fall_status = ["normal", "fall_warning", "fall_alarm"][bit4to5] || "unknown";
467
+ }
468
+ return result;
469
+ },
470
+ },
471
+ ];
472
+ return {
473
+ exposes: exposes,
474
+ fromZigbee,
475
+ isModernExtend: true,
476
+ };
477
+ },
478
+ heimanClusterLegacyIlluminanceExtend: () => {
479
+ const clusterName = "msIlluminanceMeasurement";
480
+ const exposes = utils.exposeEndpoints(e.numeric("ambient_light", ea.STATE_GET).withUnit("Lx").withDescription("ambient illuminance in lux"));
481
+ const fromZigbee = [
482
+ {
483
+ cluster: clusterName,
484
+ type: ["attributeReport", "readResponse"],
485
+ convert: (model, msg, publish, options, meta) => {
486
+ let attrData = null;
487
+ if (msg.data.measuredValue === undefined) {
488
+ return;
489
+ }
490
+ attrData = msg.data["measuredValue"];
491
+ return { ambient_light: attrData };
492
+ },
493
+ },
494
+ ];
495
+ const toZigbee = [
496
+ {
497
+ key: ["ambient_light"],
498
+ convertGet: async (entity, key, meta) => {
499
+ await entity.read(clusterName, ["measuredValue"], defaultResponseOptions);
500
+ },
501
+ },
502
+ ];
503
+ return {
504
+ exposes: exposes,
505
+ fromZigbee,
506
+ toZigbee,
507
+ isModernExtend: true,
508
+ };
509
+ },
510
+ heimanClusterSensorFaultState: () => {
511
+ const clusterName = "heimanClusterSpecial";
512
+ const faultStateBitMap = {
513
+ 0: "fault", // bit0
514
+ 1: "sensor_open_circuit_fault", // bit1
515
+ 2: "sensor_short_circuit_fault", // bit2
516
+ 3: "sensor_pollution_fault", // bit3
517
+ };
518
+ const exposes = utils.exposeEndpoints(e.text("fault_state", ea.STATE_GET).withDescription("Device fault status (normal or fault types)."));
519
+ const fromZigbee = [
520
+ {
521
+ cluster: clusterName,
522
+ type: ["attributeReport", "readResponse"],
523
+ convert: (model, msg, publish, options, meta) => {
524
+ let attrData = null;
525
+ let attrValue = 0;
526
+ if (msg.data.sensorFaultState === undefined) {
527
+ return;
528
+ }
529
+ attrData = msg.data["sensorFaultState"];
530
+ attrValue = Number(attrData);
531
+ const activeFaults = [];
532
+ for (const [bit, faultDesc] of Object.entries(faultStateBitMap)) {
533
+ const bitNum = Number(bit);
534
+ const isFault = (attrValue & (1 << bitNum)) !== 0;
535
+ if (isFault) {
536
+ activeFaults.push(faultDesc);
537
+ }
538
+ }
539
+ const faultResult = activeFaults.length > 0 ? activeFaults.join(" | ") : "normal";
540
+ return { fault_state: faultResult };
541
+ },
542
+ },
543
+ ];
544
+ const toZigbee = [
545
+ {
546
+ key: ["fault_state"],
547
+ convertGet: async (entity, key, meta) => {
548
+ await entity.read(clusterName, ["sensorFaultState"], defaultResponseOptions);
549
+ },
550
+ },
551
+ ];
552
+ return {
553
+ exposes: exposes,
554
+ fromZigbee,
555
+ toZigbee,
556
+ isModernExtend: true,
557
+ };
558
+ },
559
+ heimanClusterDeviceMuteState: () => {
560
+ const clusterName = "heimanClusterSpecial";
561
+ const muteStateBitMap = {
562
+ 0: "mute", // bit0
563
+ 1: "alarm_mute", // bit1
564
+ 2: "fault_mute", // bit2
565
+ 3: "low_battery_mute", // bit3
566
+ };
567
+ const exposes = utils.exposeEndpoints(e.text("muted", ea.STATE_GET).withDescription("Device mute status (normal or mute types)."));
568
+ const fromZigbee = [
569
+ {
570
+ cluster: clusterName,
571
+ type: ["attributeReport", "readResponse"],
572
+ convert: (model, msg, publish, options, meta) => {
573
+ let attrData = null;
574
+ let attrValue = 0;
575
+ if (msg.data.deviceMuteState === undefined) {
576
+ return;
577
+ }
578
+ attrData = msg.data["deviceMuteState"];
579
+ attrValue = Number(attrData);
580
+ const activeMutes = [];
581
+ for (const [bit, muteDesc] of Object.entries(muteStateBitMap)) {
582
+ const bitNum = Number(bit);
583
+ const isFault = (attrValue & (1 << bitNum)) !== 0;
584
+ if (isFault) {
585
+ activeMutes.push(muteDesc);
586
+ }
587
+ }
588
+ const muteResult = activeMutes.length > 0 ? activeMutes.join(" | ") : "normal";
589
+ return { Muted: muteResult };
590
+ },
591
+ },
592
+ ];
593
+ const toZigbee = [
594
+ {
595
+ key: ["muted"],
596
+ convertGet: async (entity, key, meta) => {
597
+ await entity.read(clusterName, ["deviceMuteState"], defaultResponseOptions);
598
+ },
599
+ },
600
+ ];
601
+ return {
602
+ exposes: exposes,
603
+ fromZigbee,
604
+ toZigbee,
605
+ isModernExtend: true,
606
+ };
607
+ },
608
+ heimanClusterIndicatorLight: () => {
609
+ const clusterName = "heimanClusterSpecial";
610
+ const exposes = utils.exposeEndpoints(e.binary("heartbeat_indicator", ea.ALL, true, false).withDescription("active green indicator"));
611
+ const fromZigbee = [
612
+ {
613
+ cluster: clusterName,
614
+ type: ["attributeReport", "readResponse"],
615
+ convert: (model, msg, publish, options, meta) => {
616
+ if (msg.data.indicatorLightLevelControlOf1 === undefined) {
617
+ return;
618
+ }
619
+ const state = !!msg.data["indicatorLightLevelControlOf1"];
620
+ return { heartbeat_indicator: state };
621
+ },
622
+ },
623
+ ];
624
+ const toZigbee = [
625
+ {
626
+ key: ["heartbeat_indicator"],
627
+ convertGet: async (entity, key, meta) => {
628
+ await entity.read(clusterName, ["indicatorLightLevelControlOf1"], defaultResponseOptions);
629
+ },
630
+ convertSet: async (entity, key, value, meta) => {
631
+ // const state = (value as Record<string, unknown>) || {};
632
+ const state = value ? 1 : 0;
633
+ await entity.write(clusterName, { indicatorLightLevelControlOf1: state }, defaultResponseOptions);
634
+ },
635
+ },
636
+ ];
637
+ return {
638
+ exposes: exposes,
639
+ fromZigbee,
640
+ toZigbee,
641
+ isModernExtend: true,
642
+ };
643
+ },
644
+ heimanClusterSensorInterconnectable: () => {
645
+ const clusterName = "heimanClusterSpecial";
646
+ const exposes = utils.exposeEndpoints(e.binary("interconnectable", ea.STATE_GET, true, false).withDescription("used for interconnection automation."));
647
+ const fromZigbee = [
648
+ {
649
+ cluster: clusterName,
650
+ type: ["attributeReport", "readResponse"],
651
+ convert: (model, msg, publish, options, meta) => {
652
+ if (msg.data.interconnectable === undefined) {
653
+ return;
654
+ }
655
+ const state = !!msg.data["interconnectable"];
656
+ return { interconnectable: state };
657
+ },
658
+ },
659
+ ];
660
+ const toZigbee = [
661
+ {
662
+ key: ["interconnectable"],
663
+ convertGet: async (entity, key, meta) => {
664
+ await entity.read(clusterName, ["interconnectable"], defaultResponseOptions);
665
+ },
666
+ },
667
+ ];
668
+ return {
669
+ exposes: exposes,
670
+ fromZigbee,
671
+ toZigbee,
672
+ isModernExtend: true,
673
+ };
674
+ },
675
+ iasZoneInitiateTestMode: () => {
676
+ const exposes = utils.exposeEndpoints(e.enum("trigger_selftest", ea.SET, ["test"]).withDescription("Trigger smoke alarm self-check test."));
677
+ const toZigbee = [
678
+ {
679
+ key: ["trigger_selftest"],
680
+ convertSet: async (entity, key, value, meta) => {
681
+ const testMode = {
682
+ testModeDuration: 0x01,
683
+ currentZoneSensitivityLevel: 0x01,
684
+ };
685
+ await entity.command("ssIasZone", "initTestMode", testMode);
686
+ return { state: { [key]: value } };
687
+ },
688
+ },
689
+ ];
690
+ return {
691
+ exposes: exposes,
692
+ fromZigbee: [],
693
+ toZigbee,
694
+ isModernExtend: true,
695
+ };
696
+ },
697
+ iasWarningDeviceControl: (args) => {
698
+ const defaultModes = Object.keys(iasWarningMode);
699
+ const displayModes = args?.warningMode ?? defaultModes;
700
+ const invalidModes = displayModes.filter((m) => !defaultModes.includes(m));
701
+ if (invalidModes.length > 0) {
702
+ throw new Error(`Invalid alarm mode: ${invalidModes.join(", ")},
703
+ Legal values: ${defaultModes.join(", ")}`);
704
+ }
705
+ // biome-ignore lint/correctness/noUnusedVariables: In the future use.
706
+ const level = { low: 0, medium: 1, high: 2, very_high: 3 };
707
+ // biome-ignore lint/correctness/noUnusedVariables: In the future use.
708
+ const strobeLevel = { low: 0, medium: 1, high: 2, very_high: 3 };
709
+ const exposes = utils.exposeEndpoints(e
710
+ .composite("warning_control", "warning_control", ea.SET)
711
+ .withDescription("Make the device trigger an alarm.")
712
+ .withFeature(e.enum("mode", ea.SET, displayModes).withDescription("Mode of the warning (sound effect)"))
713
+ .withFeature(e.numeric("duration", ea.SET).withUnit("s").withDescription("Duration in seconds of the alarm")));
714
+ const toZigbee = [
715
+ {
716
+ key: ["warning_control"],
717
+ convertSet: async (entity, key, value, meta) => {
718
+ const warningModeKey = "mode";
719
+ const warningDurationKey = "duration";
720
+ const warningModeStr = value[warningModeKey];
721
+ const warningDurationValue = Number(value[warningDurationKey]);
722
+ const warningModeValue = iasWarningMode[warningModeStr];
723
+ if (warningModeValue === undefined) {
724
+ throw new Error(`Invalid warning mode: ${invalidModes.join(", ")}, Valid modes:${defaultModes.join(", ")}`);
725
+ }
726
+ if (Number.isNaN(warningDurationValue) || warningDurationValue < 0) {
727
+ throw new Error(`Invalid duration: ${warningDurationValue}. Must be a non-negative number.`);
728
+ }
729
+ const values = {
730
+ mode: warningModeValue,
731
+ level: warningModeValue ? 1 : 0,
732
+ strobe: false,
733
+ duration: warningDurationValue,
734
+ strobeDutyCycle: 0,
735
+ strobeLevel: 0,
736
+ };
737
+ if (Array.isArray(meta.mapped))
738
+ throw new Error("Not supported for groups");
739
+ const info = (values.mode << 4) + ((values.strobe ? 1 : 0) << 2) + values.level;
740
+ await entity.command("ssIasWd", "startWarning", {
741
+ startwarninginfo: info,
742
+ warningduration: values.duration,
743
+ strobedutycycle: values.strobeDutyCycle,
744
+ strobelevel: values.strobeLevel,
745
+ }, utils.getOptions(meta.mapped, entity));
746
+ },
747
+ },
748
+ ];
749
+ return {
750
+ exposes: exposes,
751
+ fromZigbee: [],
752
+ toZigbee,
753
+ isModernExtend: true,
754
+ };
755
+ },
756
+ iasWarningDeviceMute: () => {
757
+ const exposes = utils.exposeEndpoints(e.enum("temporary_mute", ea.SET, ["mute"]).withDescription("temporarily mute smoke alarm but please ensure there is no real fire."));
758
+ const toZigbee = [
759
+ {
760
+ key: ["temporary_mute"],
761
+ convertSet: async (entity, key, value, meta) => {
762
+ const values = {
763
+ mode: 0,
764
+ level: 0,
765
+ strobe: false,
766
+ duration: 0,
767
+ strobeDutyCycle: 0,
768
+ strobeLevel: 0,
769
+ };
770
+ if (Array.isArray(meta.mapped))
771
+ throw new Error("Not supported for groups");
772
+ const info = (values.mode << 4) + ((values.strobe ? 1 : 0) << 2) + values.level;
773
+ await entity.command("ssIasWd", "startWarning", {
774
+ startwarninginfo: info,
775
+ warningduration: values.duration,
776
+ strobedutycycle: values.strobeDutyCycle,
777
+ strobelevel: values.strobeLevel,
778
+ }, utils.getOptions(meta.mapped, entity));
779
+ },
780
+ },
781
+ ];
782
+ return {
783
+ exposes: exposes,
784
+ fromZigbee: [],
785
+ toZigbee,
786
+ isModernExtend: true,
787
+ };
258
788
  },
259
789
  };
260
790
  exports.definitions = [
@@ -480,7 +1010,7 @@ exports.definitions = [
480
1010
  },
481
1011
  {
482
1012
  zigbeeModel: ["DOOR_TPV13", "DOOR_TPV12"],
483
- model: "HEIMAN-M1",
1013
+ model: "Heiman-M1",
484
1014
  vendor: "Heiman",
485
1015
  description: "Door sensor",
486
1016
  fromZigbee: [fz.ias_contact_alarm_1],
@@ -488,7 +1018,7 @@ exports.definitions = [
488
1018
  exposes: [e.contact(), e.battery_low(), e.tamper()],
489
1019
  },
490
1020
  {
491
- zigbeeModel: ["WaterSensor-N", "WaterSensor-EM", "WaterSensor-N-3.0", "WaterSensor-EF-3.0", "WaterSensor2-EF-3.0", "WATER_TPV13"],
1021
+ zigbeeModel: ["WaterSensor-N", "WaterSensor-EM", "WaterSensor-N-3.0", "WaterSensor-EF-3.0", "WATER_TPV13"],
492
1022
  model: "HS1WL/HS3WL",
493
1023
  vendor: "Heiman",
494
1024
  description: "Water leakage sensor",
@@ -502,6 +1032,22 @@ exports.definitions = [
502
1032
  },
503
1033
  exposes: [e.water_leak(), e.battery_low(), e.tamper(), e.battery()],
504
1034
  },
1035
+ {
1036
+ zigbeeModel: ["WaterSensor2-EF-3.0"],
1037
+ model: "HS2WL",
1038
+ vendor: "Heiman",
1039
+ description: "Water leakage sensor",
1040
+ fromZigbee: [],
1041
+ toZigbee: [],
1042
+ extend: [m.iasZoneAlarm({ zoneType: "water_leak", zoneAttributes: ["alarm_1"] }), m.temperature(), m.battery({ lowStatus: true })],
1043
+ configure: async (device, coordinatorEndpoint) => {
1044
+ const endpoint = device.getEndpoint(1);
1045
+ await reporting.bind(endpoint, coordinatorEndpoint, ["genPowerCfg", "msTemperatureMeasurement"]);
1046
+ await reporting.batteryPercentageRemaining(endpoint);
1047
+ await endpoint.read("genPowerCfg", ["batteryPercentageRemaining"]);
1048
+ await endpoint.read("msTemperatureMeasurement", ["measuredValue"]);
1049
+ },
1050
+ },
505
1051
  {
506
1052
  fingerprint: [{ modelID: "RC-N", manufacturerName: "HEIMAN" }],
507
1053
  model: "HS1RC-N",
@@ -849,7 +1395,7 @@ exports.definitions = [
849
1395
  await heiman.configureReporting.tvocMeasuredValue(endpoint);
850
1396
  await heiman.configureReporting.aqiMeasuredValue(endpoint);
851
1397
  await endpoint.read("genPowerCfg", ["batteryPercentageRemaining"]);
852
- // Seems that it is bug in HEIMAN, device does not asks for the time with binding
1398
+ // Seems that it is bug in Heiman, device does not asks for the time with binding
853
1399
  // So, we need to write time during configure
854
1400
  const time = Math.round((Date.now() - constants.OneJanuary2000) / 1000);
855
1401
  // Time-master + synchronised
@@ -1077,67 +1623,86 @@ exports.definitions = [
1077
1623
  vendor: "Heiman",
1078
1624
  description: "Fall Detection Sensor",
1079
1625
  extend: [
1080
- m.deviceAddCustomCluster("RadarSensorHeiman", {
1081
- ID: 0xfc8b,
1082
- manufacturerCode: zigbee_herdsman_1.Zcl.ManufacturerCode.HEIMAN_TECHNOLOGY_CO_LTD,
1083
- attributes: {
1084
- enable_indicator: { ID: 0xf001, type: zigbee_herdsman_1.Zcl.DataType.UINT8, write: true, max: 0xff }, // 0: off, 1: enable
1085
- sensitivity: { ID: 0xf002, type: zigbee_herdsman_1.Zcl.DataType.UINT8, write: true, max: 0xff }, // 0: Off, 1: Low sensitivity, 2: High sensitivity
1086
- enable_sub_region_isolation: { ID: 0xf006, type: zigbee_herdsman_1.Zcl.DataType.UINT8, write: true, max: 0xff }, // 0: Disable, 1: Enable
1087
- installation_method: { ID: 0xf007, type: zigbee_herdsman_1.Zcl.DataType.UINT8, write: true, max: 0xff }, // 0: Wall-mounted, 1: Ceiling, 2: Rotate ceiling 45°
1088
- cell_mounted_table: {
1089
- ID: 0xf008,
1090
- type: zigbee_herdsman_1.Zcl.DataType.OCTET_STR,
1091
- write: true,
1092
- },
1093
- wall_mounted_table: {
1094
- ID: 0xf009,
1095
- type: zigbee_herdsman_1.Zcl.DataType.OCTET_STR,
1096
- write: true,
1097
- },
1098
- sub_region_isolation_table: {
1099
- ID: 0xf00a,
1100
- type: zigbee_herdsman_1.Zcl.DataType.OCTET_STR,
1101
- write: true,
1102
- },
1103
- },
1104
- commands: {},
1105
- commandsResponse: {},
1626
+ // m.occupancy(),
1627
+ heimanExtend.heimanClusterRadar(),
1628
+ heimanExtend.heimanClusterRadarActiveIndicatorExtend(),
1629
+ heimanExtend.heimanClusterRadarSubRegionEnableExtend(),
1630
+ heimanExtend.heimanClusterRadarSenseExtend(),
1631
+ heimanExtend.heimanClusterRadarCellMountedTableExtend(),
1632
+ heimanExtend.heimanClusterRadarWallMountedTableExtend(),
1633
+ heimanExtend.heimanClusterRadarSubRegionIsolationTableExtend(),
1634
+ m.enumLookup({
1635
+ name: "sensitivity",
1636
+ lookup: { Off: 0, LowSensitivity: 1, HighSensitivity: 2 },
1637
+ cluster: "heimanClusterRadar",
1638
+ attribute: { ID: 0xf002, type: zigbee_herdsman_1.Zcl.DataType.UINT8 },
1639
+ description: "0: Off, 1: Low sensitivity, 2: High sensitivity",
1640
+ access: "ALL",
1641
+ }),
1642
+ m.enumLookup({
1643
+ name: "installation_method",
1644
+ lookup: { WallMounted: 0, Ceiling: 1, RotateCeiling45: 2 },
1645
+ cluster: "heimanClusterRadar",
1646
+ attribute: { ID: 0xf007, type: zigbee_herdsman_1.Zcl.DataType.UINT8 },
1647
+ description: "0: Wall-mounted, 1: Ceiling, 2: Rotate ceiling 45°",
1648
+ access: "ALL",
1106
1649
  }),
1107
1650
  ],
1108
- fromZigbee: [fz.identify, fzLocal.occupancyRadarHeiman, fzLocal.radarSensorHeiman],
1109
- toZigbee: [tzLocal.radarSensorHeiman],
1651
+ fromZigbee: [fz.identify],
1652
+ toZigbee: [],
1110
1653
  ota: true,
1111
- exposes: [
1112
- e.binary("occupancy", ea.STATE, true, false).withDescription("Indicates if someone is present"),
1113
- e.enum("sensor_status", ea.STATE, ["none", "activity", "unknown"]).withDescription("Sensor activity status"),
1114
- e.enum("fall_status", ea.STATE, ["normal", "fall_warning", "fall_alarm", "unknown"]).withDescription("Fall detection status"),
1115
- e.enum("enable_indicator", ea.ALL, [0, 1]).withDescription("0: Off, 1: Enable"),
1116
- e.enum("sensitivity", ea.ALL, [0, 1, 2]).withDescription("0: Off, 1: Low sensitivity, 2: High sensitivity"),
1117
- e.enum("enable_sub_region_isolation", ea.ALL, [0, 1]).withDescription("0: Disable, 1: Enable"),
1118
- e.enum("installation_method", ea.ALL, [0, 1, 2]).withDescription("0: Wall-mounted, 1: Ceiling, 2: Rotate ceiling 45°"),
1119
- exposes
1120
- .text("cell_mounted_table", ea.ALL)
1121
- .withDescription("Ceiling installation area coordinate table. Format: 'X1,X2,Y1,Y2,height'. Value range: -2000≤X1≤0, 0≤X2≤2000 -2500≤Y1≤0, 0≤Y2≤2500 2300≤height≤3000 Unit:mm"),
1122
- exposes
1123
- .text("wall_mounted_table", ea.ALL)
1124
- .withDescription("Wall-mounted installation area coordinate table. Format: 'X1,X2,Y2,height' Value range: -2000≤X1≤0, 0≤X2≤2000 200≤Y2≤4000 1500≤height≤1600 Unit:mm."),
1125
- exposes
1126
- .text("sub_region_isolation_table", ea.ALL)
1127
- .withDescription("Undetectable area coordinate table. Format: 'x1,x2,y1,y2,z1,z2'. Ranges: X1≤x1≤x2≤X2 When wall-mounted: 200≤y1≤y2≤Y2 0≤z1≤z2≤2300 Ceiling installation: Y1≤y1≤y2≤Y2 0≤z1≤z2≤height Unit:mm"),
1128
- ],
1654
+ exposes: [],
1129
1655
  configure: async (device, coordinatorEndpoint, logger) => {
1130
1656
  const endpoint = device.getEndpoint(1);
1131
- await reporting.bind(endpoint, coordinatorEndpoint, ["msOccupancySensing", "RadarSensorHeiman"]);
1657
+ await reporting.bind(endpoint, coordinatorEndpoint, ["msOccupancySensing", "heimanClusterRadar"]);
1132
1658
  await reporting.occupancy(endpoint);
1133
- await endpoint.read("RadarSensorHeiman", [
1134
- "cell_mounted_table",
1135
- "wall_mounted_table",
1136
- "sub_region_isolation_table",
1659
+ await endpoint.read("heimanClusterRadar", [
1660
+ "cellMountedTable",
1661
+ "wallMountedTable",
1662
+ "subRegionIsolationTable",
1137
1663
  ]);
1138
1664
  },
1139
1665
  endpoint: (device) => ({ default: 1 }),
1140
1666
  },
1667
+ {
1668
+ zigbeeModel: ["HS8OS-EF1-3.0"],
1669
+ model: "HS8OS-EF1-3.0",
1670
+ vendor: "Heiman",
1671
+ description: "Human presence sensor",
1672
+ extend: [
1673
+ m.occupancy(),
1674
+ heimanExtend.heimanClusterRadar(),
1675
+ heimanExtend.heimanClusterRadarActiveIndicatorExtend(),
1676
+ heimanExtend.heimanClusterRadarSensitivityExtend(),
1677
+ heimanExtend.heimanClusterLegacyIlluminanceExtend(),
1678
+ m.numeric({
1679
+ name: "radar_delay_time",
1680
+ cluster: 0x0406,
1681
+ attribute: { ID: 0x0020, type: 0x21 },
1682
+ description: "Occupied to unoccupied delay",
1683
+ valueMin: 60,
1684
+ valueMax: 3600,
1685
+ access: "ALL",
1686
+ }),
1687
+ ],
1688
+ fromZigbee: [],
1689
+ toZigbee: [],
1690
+ ota: true,
1691
+ exposes: [],
1692
+ configure: async (device, coordinatorEndpoint, logger) => {
1693
+ const endpoint = device.getEndpoint(1);
1694
+ await reporting.bind(endpoint, coordinatorEndpoint, [
1695
+ "msOccupancySensing",
1696
+ "msIlluminanceMeasurement",
1697
+ "heimanClusterRadar",
1698
+ "haDiagnostic",
1699
+ ]);
1700
+ await endpoint.read("msIlluminanceMeasurement", ["measuredValue"]);
1701
+ await endpoint.read("msOccupancySensing", ["ultrasonicOToUDelay"]);
1702
+ await endpoint.read("heimanClusterRadar", [0xf001, 0xf002], { manufacturerCode: zigbee_herdsman_1.Zcl.ManufacturerCode.HEIMAN_TECHNOLOGY_CO_LTD });
1703
+ },
1704
+ endpoint: (device) => ({ default: 1 }),
1705
+ },
1141
1706
  {
1142
1707
  fingerprint: [{ modelID: "HS2AQ-EF-3.0", manufacturerName: "HEIMAN" }],
1143
1708
  model: "HS2AQ-EF-3.0",
@@ -1215,5 +1780,132 @@ exports.definitions = [
1215
1780
  },
1216
1781
  exposes: [e.temperature(), e.pm25(), e.hcho(), e.aqi(), e.pm10()],
1217
1782
  },
1783
+ {
1784
+ zigbeeModel: ["HS1SA-EF-3.0"],
1785
+ model: "HS1SA-E",
1786
+ vendor: "Heiman",
1787
+ description: "Smoke detector",
1788
+ fromZigbee: [fz.ias_smoke_alarm_1, fz.battery],
1789
+ toZigbee: [tz.warning],
1790
+ configure: async (device, coordinatorEndpoint) => {
1791
+ const endpoint = device.getEndpoint(1);
1792
+ await reporting.bind(endpoint, coordinatorEndpoint, ["genPowerCfg", 0xfc90]);
1793
+ await reporting.batteryPercentageRemaining(endpoint);
1794
+ await endpoint.read("ssIasZone", ["zoneStatus", "zoneState", "iasCieAddr", "zoneId"]);
1795
+ await endpoint.read("heimanClusterSpecial", [0x0002, 0x009, 0x1004, 0x1007, 0x0016, 0x0017, 0x0018, 0x0019, 0x001a, 0x001b], {
1796
+ manufacturerCode: zigbee_herdsman_1.Zcl.ManufacturerCode.HEIMAN_TECHNOLOGY_CO_LTD,
1797
+ });
1798
+ },
1799
+ exposes: [],
1800
+ extend: [
1801
+ m.battery(),
1802
+ m.temperature(),
1803
+ m.iasZoneAlarm({ zoneType: "smoke", zoneAttributes: ["alarm_1", "battery_low", "test"] }),
1804
+ heimanExtend.heimanClusterSpecial(),
1805
+ heimanExtend.heimanClusterSensorFaultState(),
1806
+ heimanExtend.heimanClusterDeviceMuteState(),
1807
+ heimanExtend.iasZoneInitiateTestMode(),
1808
+ heimanExtend.iasWarningDeviceMute(),
1809
+ heimanExtend.heimanClusterIndicatorLight(),
1810
+ heimanExtend.heimanClusterSensorInterconnectable(),
1811
+ m.numeric({
1812
+ name: "smoke_level",
1813
+ unit: "",
1814
+ scale: 0.1,
1815
+ valueMin: 0,
1816
+ valueMax: 20,
1817
+ cluster: "heimanClusterSpecial",
1818
+ attribute: { ID: 0x0016, type: zigbee_herdsman_1.Zcl.DataType.UINT8 },
1819
+ description: "smoke level",
1820
+ access: "STATE_GET",
1821
+ }),
1822
+ m.enumLookup({
1823
+ name: "smoke_unit",
1824
+ lookup: { "dB/m": 0, "%ft OBS": 1 },
1825
+ cluster: "heimanClusterSpecial",
1826
+ attribute: { ID: 0x0018, type: zigbee_herdsman_1.Zcl.DataType.UINT8 },
1827
+ description: "smoke level unit",
1828
+ access: "STATE_GET",
1829
+ }),
1830
+ m.enumLookup({
1831
+ name: "chamber_contamination",
1832
+ lookup: { normal: 0, light_contamination: 1, medium_contamication: 2, critical_contamication: 3 },
1833
+ cluster: "heimanClusterSpecial",
1834
+ attribute: { ID: 0x0017, type: zigbee_herdsman_1.Zcl.DataType.UINT8 },
1835
+ description: "it indicates that how serious the smoke chamber get contaminated.",
1836
+ access: "STATE_GET",
1837
+ }),
1838
+ m.enumLookup({
1839
+ name: "siren_for_automation_only",
1840
+ lookup: { stop: 0, smoke_siren: 1, co_siren: 2 },
1841
+ cluster: "heimanClusterSpecial",
1842
+ attribute: { ID: 0x0012, type: zigbee_herdsman_1.Zcl.DataType.UINT8 },
1843
+ description: "siren effect",
1844
+ access: "ALL",
1845
+ }),
1846
+ m.numeric({
1847
+ name: "reported_packages",
1848
+ unit: "",
1849
+ valueMin: 0,
1850
+ valueMax: 60000,
1851
+ cluster: "heimanClusterSpecial",
1852
+ attribute: { ID: 0x001b, type: zigbee_herdsman_1.Zcl.DataType.UINT8 },
1853
+ description: "for diagnostic purpose, how many zigbee packages has the reported in a day.",
1854
+ access: "STATE_GET",
1855
+ }),
1856
+ m.numeric({
1857
+ name: "rejoin_count",
1858
+ unit: "",
1859
+ valueMin: 0,
1860
+ valueMax: 60000,
1861
+ cluster: "heimanClusterSpecial",
1862
+ attribute: { ID: 0x001a, type: zigbee_herdsman_1.Zcl.DataType.UINT8 },
1863
+ description: "for diagnostic purpose, how many times has the product rejoined to zigbee network.",
1864
+ access: "STATE_GET",
1865
+ }),
1866
+ m.numeric({
1867
+ name: "reboot_count",
1868
+ unit: "",
1869
+ valueMin: 0,
1870
+ valueMax: 60000,
1871
+ cluster: "heimanClusterSpecial",
1872
+ attribute: { ID: 0x0019, type: zigbee_herdsman_1.Zcl.DataType.UINT8 },
1873
+ description: "for diagnostic purpose, how many times has the product rebooted.",
1874
+ access: "STATE_GET",
1875
+ }),
1876
+ ],
1877
+ ota: true,
1878
+ },
1879
+ {
1880
+ zigbeeModel: ["WarningDevice-EFA1-3.0"],
1881
+ model: "HS2WD-EF",
1882
+ vendor: "Heiman",
1883
+ description: "Smart siren",
1884
+ fromZigbee: [fz.battery, fz.ias_wd],
1885
+ toZigbee: [tz.warning, tz.ias_max_duration],
1886
+ meta: { disableDefaultResponse: true },
1887
+ configure: async (device, coordinatorEndpoint) => {
1888
+ const endpoint = device.getEndpoint(1);
1889
+ await reporting.bind(endpoint, coordinatorEndpoint, ["genPowerCfg"]);
1890
+ await reporting.batteryPercentageRemaining(endpoint);
1891
+ await endpoint.read("ssIasWd", ["maxDuration"]);
1892
+ },
1893
+ exposes: [
1894
+ e.battery(),
1895
+ e
1896
+ .numeric("max_duration", ea.ALL)
1897
+ .withUnit("s")
1898
+ .withValueMin(0)
1899
+ .withValueMax(1800)
1900
+ .withDescription("Max duration of Siren")
1901
+ .withCategory("config"),
1902
+ e
1903
+ .warning()
1904
+ .removeFeature("strobe_level")
1905
+ .removeFeature("mode")
1906
+ .withFeature(e.enum("mode", ea.SET, ["stop", "burglar", "fire", "emergency"]).withDescription("Mode of the warning(sound effect)")),
1907
+ ],
1908
+ ota: true,
1909
+ },
1218
1910
  ];
1219
1911
  //# sourceMappingURL=heiman.js.map