zigbee-herdsman-converters 14.0.566 → 14.0.569

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.
package/devices/adeo.js CHANGED
@@ -29,6 +29,13 @@ module.exports = [
29
29
  description: 'ENKI LEXMAN E27 LED white filament 1055 lumen',
30
30
  extend: extend.light_onoff_brightness_colortemp({colorTempRange: [153, 454]}),
31
31
  },
32
+ {
33
+ zigbeeModel: ['ZBEK-9'],
34
+ model: 'IA-CDZFB2AA007NA-MZN-02',
35
+ vendor: 'ADEO',
36
+ description: 'ENKI LEXMAN E27 LED white',
37
+ extend: extend.light_onoff_brightness_colortemp_color({colorTempRange: [153, 454]}),
38
+ },
32
39
  {
33
40
  zigbeeModel: ['ZBEK-4'],
34
41
  model: 'IM-CDZDGAAA0005KA_MAN',
package/devices/ctm.js ADDED
@@ -0,0 +1,961 @@
1
+ const fz = require('../converters/fromZigbee');
2
+ const tz = require('../converters/toZigbee');
3
+ const exposes = require('../lib/exposes');
4
+ const reporting = require('../lib/reporting');
5
+ const constants = require('../lib/constants');
6
+ const utils = require('../lib/utils');
7
+ const e = exposes.presets;
8
+ const ea = exposes.access;
9
+
10
+ const dataType = {
11
+ boolean: 16,
12
+ uint8: 32,
13
+ uint16: 33,
14
+ int8: 40,
15
+ int16: 41,
16
+ enum8: 48,
17
+ charStr: 66,
18
+ ieeeAddr: 240,
19
+ };
20
+
21
+ const fzLocal = {
22
+ ctm_device_mode: {
23
+ cluster: 'genOnOff',
24
+ type: ['attributeReport', 'readResponse'],
25
+ convert: (model, msg, publish, options, meta) => {
26
+ const result = {};
27
+ const data = msg.data;
28
+ if (data.hasOwnProperty(0x2200)) {
29
+ const deviceModeLookup = {0: 'astro_clock', 1: 'timer', 2: 'daily_timer', 3: 'weekly_timer'};
30
+ result.device_mode = deviceModeLookup[data[0x2200]];
31
+ }
32
+
33
+ return result;
34
+ },
35
+ },
36
+ ctm_device_enabled: {
37
+ cluster: 'genOnOff',
38
+ type: ['attributeReport', 'readResponse'],
39
+ convert: (model, msg, publish, options, meta) => {
40
+ const result = {};
41
+ const data = msg.data;
42
+ if (data.hasOwnProperty(0x2201)) {
43
+ result.device_enabled = data[0x2201] ? 'ON' : 'OFF';
44
+ }
45
+
46
+ return result;
47
+ },
48
+ },
49
+ ctm_child_lock: {
50
+ cluster: 'genOnOff',
51
+ type: ['attributeReport', 'readResponse'],
52
+ convert: (model, msg, publish, options, meta) => {
53
+ const result = {};
54
+ const data = msg.data;
55
+ if (data.hasOwnProperty(0x2202)) {
56
+ result.child_lock = data[0x2202] ? 'locked' : 'unlocked';
57
+ }
58
+
59
+ return result;
60
+ },
61
+ },
62
+ ctm_current_flag: {
63
+ cluster: 'genOnOff',
64
+ type: ['attributeReport', 'readResponse'],
65
+ convert: (model, msg, publish, options, meta) => {
66
+ const result = {};
67
+ const data = msg.data;
68
+ if (data.hasOwnProperty(0x5000)) {
69
+ result.current_flag = data[0x5000];
70
+ }
71
+
72
+ return result;
73
+ },
74
+ },
75
+ ctm_temperature_offset: {
76
+ cluster: 'msTemperatureMeasurement',
77
+ type: ['attributeReport', 'readResponse'],
78
+ convert: (model, msg, publish, options, meta) => {
79
+ const result = {};
80
+ const data = msg.data;
81
+ if (data.hasOwnProperty(0x0400)) {
82
+ result.temperature_offset = data[0x0400];
83
+ }
84
+
85
+ return result;
86
+ },
87
+ },
88
+ ctm_thermostat: {
89
+ cluster: 'hvacThermostat',
90
+ type: ['attributeReport', 'readResponse'],
91
+ convert: (model, msg, publish, options, meta) => {
92
+ const result = {};
93
+ const data = msg.data;
94
+ if (data.hasOwnProperty(0x0401)) { // Load
95
+ result.load = data[0x0401];
96
+ }
97
+ if (data.hasOwnProperty(0x0402)) { // Display text
98
+ result.display_text = data[0x0402];
99
+ }
100
+ if (data.hasOwnProperty(0x0403)) { // Sensor
101
+ const sensorModeLookup = {
102
+ 0: 'air', 1: 'floor', 2: 'external', 3: 'regulator', 4: 'mv_air', 5: 'mv_external', 6: 'mv_regulator'};
103
+ result.sensor = sensorModeLookup[data[0x0403]];
104
+ }
105
+ if (data.hasOwnProperty(0x0405)) { // Regulator mode
106
+ result.regulator_mode = data[0x0405] ? 'regulator' : 'thermostat';
107
+ }
108
+ if (data.hasOwnProperty(0x0406)) { // Power status
109
+ result.power_status = data[0x0406] ? 'ON' : 'OFF';
110
+ }
111
+ if (data.hasOwnProperty(0x0408)) { // Mean power
112
+ result.mean_power = data[0x0408];
113
+ }
114
+ if (data.hasOwnProperty(0x0409)) { // Floor temp
115
+ result.floor_temp = utils.precisionRound(data[0x0409], 2) /100;
116
+ }
117
+ if (data.hasOwnProperty(0x0411)) { // Night switching
118
+ result.night_switching = data[0x0411] ? 'ON' : 'OFF';
119
+ }
120
+ if (data.hasOwnProperty(0x0412)) { // Frost guard
121
+ result.frost_guard = data[0x0412] ? 'ON' : 'OFF';
122
+ }
123
+ if (data.hasOwnProperty(0x0413)) { // Child lock
124
+ result.child_lock = data[0x0413] ? 'LOCK' : 'UNLOCK';
125
+ }
126
+ if (data.hasOwnProperty(0x0414)) { // Max floor temp
127
+ result.max_floor_temp = data[0x0414];
128
+ }
129
+ if (data.hasOwnProperty(0x0415)) { // Heating
130
+ result.heating = data[0x0415] ? 'heating' : 'idle';
131
+ }
132
+ if (data.hasOwnProperty(0x0420)) { // Regulator setpoint
133
+ result.regulator_setpoint = data[0x0420];
134
+ }
135
+ if (data.hasOwnProperty(0x0421)) { // Regulation mode
136
+ const regulationModeLookup = {0: 'thermostat', 1: 'regulator', 2: 'zzilent'};
137
+ result.regulation_mode= regulationModeLookup[data[0x0421]];
138
+ }
139
+ if (data.hasOwnProperty(0x0422)) { // Operation mode
140
+ const presetLookup = {0: 'off', 1: 'away', 2: 'sleep', 3: 'home'};
141
+ const systemModeLookup = {0: 'off', 1: 'off', 2: 'off', 3: 'heat'};
142
+ result.preset = presetLookup[data[0x0422]];
143
+ result.system_mode = systemModeLookup[data[0x0422]];
144
+ }
145
+ if (data.hasOwnProperty(0x0423)) { // Maximum floor temp guard
146
+ result.max_floor_guard = data[0x0423] ? 'ON' : 'OFF';
147
+ }
148
+ if (data.hasOwnProperty(0x0424)) { // Weekly timer enabled
149
+ result.weekly_timer = data[0x0424] ? 'ON' : 'OFF';
150
+ }
151
+ if (data.hasOwnProperty(0x0425)) { // Frost guard setpoint
152
+ result.frost_guard_setpoint = data[0x0425];
153
+ }
154
+ if (data.hasOwnProperty(0x0426)) { // External temperature
155
+ result.external_temp = utils.precisionRound(data[0x0426], 2) /100;
156
+ }
157
+ if (data.hasOwnProperty(0x0428)) { // Exteral sensor source
158
+ result.exteral_sensor_source = data[0x0428];
159
+ }
160
+ if (data.hasOwnProperty(0x0429)) { // Current air temperature
161
+ result.air_temp = utils.precisionRound(data[0x0429], 2) /100;
162
+ }
163
+ if (data.hasOwnProperty(0x0424)) { // Floor Sensor Error
164
+ result.floor_sensor_error = data[0x042B] ? 'error' : 'ok';
165
+ }
166
+ if (data.hasOwnProperty(0x0424)) { // External Air Sensor Error
167
+ result.exteral_sensor_error = data[0x042C] ? 'error' : 'ok';
168
+ }
169
+
170
+ return result;
171
+ },
172
+ },
173
+ ctm_group_config: {
174
+ cluster: '65191', // 0xFEA7 ctmGroupConfig
175
+ type: ['attributeReport', 'readResponse'],
176
+ convert: (model, msg, publish, options, meta) => {
177
+ const result = {};
178
+ const data = msg.data;
179
+ if (data.hasOwnProperty(0x0000)) {
180
+ result.group_id = data[0x0000];
181
+ }
182
+
183
+ return result;
184
+ },
185
+ },
186
+ ctm_sove_guard: {
187
+ cluster: '65481', // 0xFFC9 ctmSoveGuard
188
+ type: ['attributeReport', 'readResponse'],
189
+ convert: (model, msg, publish, options, meta) => {
190
+ const result = {};
191
+ const data = msg.data;
192
+ if (data.hasOwnProperty(0x0001)) { // Alarm status
193
+ const alarmStatusLookup = {
194
+ 0: 'ok', 1: 'tamper', 2: 'high_temperatur', 3: 'timer', 4: 'battery_alarm', 5: 'error', 0xFF: 'unknown'};
195
+ result.alarm_status = alarmStatusLookup[data[0x0001]];
196
+ }
197
+ if (data.hasOwnProperty(0x0002)) { // Change battery
198
+ result.battery_low = data[0x0002] ? true : false;
199
+ }
200
+ if (data.hasOwnProperty(0x0003)) { // Stove temperature
201
+ result.stove_temperature = data[0x0003];
202
+ }
203
+ if (data.hasOwnProperty(0x0004)) { // Ambient temperature
204
+ result.ambient_temperature = data[0x0004];
205
+ }
206
+ if (data.hasOwnProperty(0x0005)) { // Active
207
+ result.active = data[0x0005] ? true : false;
208
+ }
209
+ if (data.hasOwnProperty(0x0006)) { // Runtime
210
+ result.runtime = data[0x0006];
211
+ }
212
+ if (data.hasOwnProperty(0x0007)) { // Runtime timeout
213
+ result.runtime_timeout = data[0x0007];
214
+ }
215
+ if (data.hasOwnProperty(0x0008)) { // Reset reason
216
+ const resetReasonLookup = {
217
+ 0: 'unknown', 1: 'power_on', 2: 'external', 3: 'brown_out', 4: 'watchdog', 5: 'program_interface',
218
+ 6: 'software', 0xFF: 'unknown'};
219
+ result.reset_reason = resetReasonLookup[data[0x0008]];
220
+ }
221
+ if (data.hasOwnProperty(0x0009)) { // Dip switch
222
+ result.dip_switch = data[0x0009];
223
+ }
224
+ if (data.hasOwnProperty(0x000A)) { // Software version
225
+ result.sw_version = data[0x000A];
226
+ }
227
+ if (data.hasOwnProperty(0x000B)) { // Hardware version
228
+ result.hw_version = data[0x000B];
229
+ }
230
+ if (data.hasOwnProperty(0x000C)) { // Bootloader version
231
+ result.bootloader_version = data[0x000C];
232
+ }
233
+ if (data.hasOwnProperty(0x000D)) { // Model
234
+ const modelLookup = {0: 'unknown', 1: '1_8', 2: 'infinity', 3: 'hybrid', 4: 'tak', 0xFF: 'unknown'};
235
+ result.model = modelLookup[data[0x000D]];
236
+ }
237
+ if (data.hasOwnProperty(0x0010)) { // Relay address
238
+ result.relay_address = data[0x0010];
239
+ }
240
+ if (data.hasOwnProperty(0x0100)) { // Relay current flag
241
+ const currentFlagLookup = {0: 'false', 1: 'true', 0xFF: 'unknown'};
242
+ result.current_flag = currentFlagLookup[data[0x0100]];
243
+ }
244
+ if (data.hasOwnProperty(0x0101)) { // Relay current
245
+ result.relay_current = data[0x0101];
246
+ }
247
+ if (data.hasOwnProperty(0x0102)) { // Relay status
248
+ const relayStatusLookup = {0: 'off', 1: 'on', 2: 'not_present', 0xFF: 'unknown'};
249
+ result.relay_status = relayStatusLookup[data[0x0102]];
250
+ }
251
+ if (data.hasOwnProperty(0x0103)) { // Relay external button
252
+ const relayStatusLookup = {0: 'not_clicked', 1: 'clicked', 0xFF: 'unknown'};
253
+ result.external_button = relayStatusLookup[data[0x0103]];
254
+ }
255
+ if (data.hasOwnProperty(0x0104)) { // Relay alarm
256
+ const relayAlarmLookup = {0: 'ok', 1: 'no_communication', 2: 'over_current', 3: 'over_temperature', 0xFF: 'unknown'};
257
+ result.relay_alarm = relayAlarmLookup[data[0x0104]];
258
+ }
259
+ if (data.hasOwnProperty(0x0105)) { // Alarm status (from relay)
260
+ const relayAlarmStatusLookup = {
261
+ 0: 'ok', 1: 'tamper', 2: 'high_temperatur', 3: 'timer', 4: 'battery_alarm', 5: 'error', 0xFF: 'unknown'};
262
+ result.relay_alarm_status = relayAlarmStatusLookup[data[0x0105]];
263
+ }
264
+
265
+ return result;
266
+ },
267
+ },
268
+ ctm_water_leak_alarm: {
269
+ cluster: 'ssIasZone',
270
+ type: ['commandStatusChangeNotification', 'attributeReport'],
271
+ convert: (model, msg, publish, options, meta) => {
272
+ const zoneStatus = msg.data.zonestatus;
273
+ return {
274
+ active_water_leak: (zoneStatus & 1) > 0,
275
+ water_leak: (zoneStatus & 1<<1) > 0,
276
+ battery_low: (zoneStatus & 1<<3) > 0,
277
+ };
278
+ },
279
+ },
280
+ };
281
+
282
+
283
+ const tzLocal = {
284
+ ballast_power_on_level: {
285
+ key: ['ballast_power_on_level'],
286
+ convertSet: async (entity, key, value, meta) => {
287
+ await entity.write('lightingBallastCfg', {'powerOnLevel': value});
288
+ },
289
+ convertGet: async (entity, key, meta) => {
290
+ await entity.read('lightingBallastCfg', ['powerOnLevel']);
291
+ },
292
+ },
293
+ ctm_device_mode: {
294
+ key: ['device_mode'],
295
+ convertGet: async (entity, key, meta) => {
296
+ await entity.read('genOnOff', [0x2200]);
297
+ },
298
+ },
299
+ ctm_device_enabled: {
300
+ key: ['device_enabled'],
301
+ convertSet: async (entity, key, value, meta) => {
302
+ await entity.write('genOnOff', {0x2201: {value: {'OFF': 0, 'ON': 1}[value], type: dataType.boolean}});
303
+ },
304
+ convertGet: async (entity, key, meta) => {
305
+ await entity.read('genOnOff', [0x2201]);
306
+ },
307
+ },
308
+ ctm_child_lock: {
309
+ key: ['child_lock'],
310
+ convertGet: async (entity, key, meta) => {
311
+ await entity.read('genOnOff', [0x2202]);
312
+ },
313
+ },
314
+ ctm_current_flag: {
315
+ key: ['current_flag'],
316
+ convertGet: async (entity, key, meta) => {
317
+ await entity.read('genOnOff', [0x5000], {manufacturerCode: 0x1337});
318
+ },
319
+ },
320
+ ctm_temperature_offset: {
321
+ key: ['temperature_offset'],
322
+ convertSet: async (entity, key, value, meta) => {
323
+ await entity.write('msTemperatureMeasurement',
324
+ {0x0400: {value: value, type: dataType.int8}}, {manufacturerCode: 0x1337, sendWhen: 'active'});
325
+ },
326
+ convertGet: async (entity, key, meta) => {
327
+ await entity.read('msTemperatureMeasurement', [0x0400], {manufacturerCode: 0x1337, sendWhen: 'active'});
328
+ await entity.read('msTemperatureMeasurement', ['measuredValue'], {sendWhen: 'active'});
329
+ },
330
+ },
331
+ ctm_thermostat: {
332
+ key: ['load', 'display_text', 'sensor', 'regulator_mode', 'power_status', 'system_mode', 'night_switching', 'frost_guard',
333
+ 'max_floor_temp', 'regulator_setpoint', 'regulation_mode', 'max_floor_guard', 'weekly_timer', 'exteral_sensor_source',
334
+ ],
335
+ convertSet: async (entity, key, value, meta) => {
336
+ switch (key) {
337
+ case 'load':
338
+ await entity.write('hvacThermostat', {0x0401: {value: value, type: dataType.uint16}});
339
+ break;
340
+ case 'display_text':
341
+ await entity.write('hvacThermostat', {0x0402: {value: value, type: dataType.charStr}});
342
+ break;
343
+ case 'sensor':
344
+ await entity.write('hvacThermostat', {0x0403: {
345
+ value: {'air': 0, 'floor': 1, 'external': 2, 'regulator': 3, 'mv_air': 4, 'mv_external': 5, 'mv_regulator': 6}[value],
346
+ type: dataType.enum8}});
347
+ break;
348
+ case 'regulator_mode':
349
+ await entity.write('hvacThermostat', {0x0405: {value: {'thermostat': 0, 'regulator': 1}[value], type: dataType.boolean}});
350
+ break;
351
+ case 'power_status':
352
+ await entity.write('hvacThermostat', {0x0406: {value: {'OFF': 0, 'ON': 1}[value], type: dataType.boolean}});
353
+ break;
354
+ case 'system_mode':
355
+ if (value === 'off') {
356
+ await entity.write('hvacThermostat', {0x0406: {value: 0, type: dataType.boolean}});
357
+ } else if (value === 'heat') {
358
+ await entity.write('hvacThermostat', {0x0422: {value: 3, type: dataType.uint8}});
359
+ }
360
+ break;
361
+ case 'night_switching':
362
+ await entity.write('hvacThermostat', {0x0411: {value: {'OFF': 0, 'ON': 1}[value], type: dataType.boolean}});
363
+ break;
364
+ case 'frost_guard':
365
+ await entity.write('hvacThermostat', {0x0412: {value: {'OFF': 0, 'ON': 1}[value], type: dataType.boolean}});
366
+ break;
367
+ case 'max_floor_temp':
368
+ await entity.write('hvacThermostat', {0x0414: {value: value, type: dataType.uint8}});
369
+ break;
370
+ case 'regulator_setpoint':
371
+ await entity.write('hvacThermostat', {0x0420: {value: value, type: dataType.uint8}});
372
+ break;
373
+ case 'regulation_mode':
374
+ await entity.write('hvacThermostat', {0x0421: {
375
+ value: {'thermostat': 0, 'regulator': 1, 'zzilent': 2}[value],
376
+ type: dataType.uint8}});
377
+ break;
378
+ case 'max_floor_guard':
379
+ await entity.write('hvacThermostat', {0x0423: {value: {'OFF': 0, 'ON': 1}[value], type: dataType.boolean}});
380
+ break;
381
+ case 'weekly_timer':
382
+ await entity.write('hvacThermostat', {0x0424: {value: {'OFF': 0, 'ON': 1}[value], type: dataType.boolean}});
383
+ break;
384
+ case 'exteral_sensor_source':
385
+ await entity.write('hvacThermostat', {0x0428: {value: value, type: dataType.uint16}});
386
+ break;
387
+
388
+ default: // Unknown key
389
+ throw new Error(`Unhandled key tzLocal.ctm_thermostat.convertSet ${key}`);
390
+ }
391
+ },
392
+ convertGet: async (entity, key, meta) => {
393
+ switch (key) {
394
+ case 'load':
395
+ await entity.read('hvacThermostat', [0x0401]);
396
+ break;
397
+ case 'display_text':
398
+ await entity.read('hvacThermostat', [0x0402]);
399
+ break;
400
+ case 'sensor':
401
+ await entity.read('hvacThermostat', [0x0403]);
402
+ break;
403
+ case 'regulator_mode':
404
+ await entity.read('hvacThermostat', [0x0405]);
405
+ break;
406
+ case 'power_status':
407
+ await entity.read('hvacThermostat', [0x0406]);
408
+ break;
409
+ case 'night_switching':
410
+ await entity.read('hvacThermostat', [0x0411]);
411
+ break;
412
+ case 'frost_guard':
413
+ await entity.read('hvacThermostat', [0x0412]);
414
+ break;
415
+ case 'max_floor_temp':
416
+ await entity.read('hvacThermostat', [0x0414]);
417
+ break;
418
+ case 'regulator_setpoint':
419
+ await entity.read('hvacThermostat', [0x0420]);
420
+ break;
421
+ case 'regulation_mode':
422
+ await entity.read('hvacThermostat', [0x0421]);
423
+ break;
424
+ case 'system_mode':
425
+ await entity.read('hvacThermostat', [0x0422]);
426
+ break;
427
+ case 'max_floor_guard':
428
+ await entity.read('hvacThermostat', [0x0423]);
429
+ break;
430
+ case 'weekly_timer':
431
+ await entity.read('hvacThermostat', [0x0424]);
432
+ break;
433
+ case 'exteral_sensor_source':
434
+ await entity.read('hvacThermostat', [0x0428]);
435
+ break;
436
+
437
+ default: // Unknown key
438
+ throw new Error(`Unhandled key tzLocal.ctm_thermostat.convertGet ${key}`);
439
+ }
440
+ },
441
+ },
442
+ ctm_thermostat_preset: {
443
+ key: ['preset'],
444
+ convertSet: async (entity, key, value, meta) => {
445
+ const presetLookup = {'off': 0, 'away': 1, 'sleep': 2, 'home': 3};
446
+ await entity.write('hvacThermostat', {0x0422: {value: presetLookup[value], type: dataType.uint8}});
447
+ },
448
+ },
449
+ ctm_thermostat_child_lock: {
450
+ key: ['child_lock'],
451
+ convertSet: async (entity, key, value, meta) => {
452
+ await entity.write('hvacThermostat', {0x0413: {value: {'UNLOCK': 0, 'LOCK': 1}[value], type: dataType.boolean}});
453
+ },
454
+ },
455
+ ctm_thermostat_gets: {
456
+ key: ['mean_power', 'floor_temp', 'heating', 'frost_guard_setpoint', 'external_temp',
457
+ 'air_temp', 'floor_sensor_error', 'exteral_sensor_error',
458
+ ],
459
+ convertGet: async (entity, key, meta) => {
460
+ switch (key) {
461
+ case 'mean_power':
462
+ await entity.read('hvacThermostat', [0x0408]);
463
+ break;
464
+ case 'floor_temp':
465
+ await entity.read('hvacThermostat', [0x0409]);
466
+ break;
467
+ case 'heating':
468
+ await entity.read('hvacThermostat', [0x0415]);
469
+ break;
470
+ case 'frost_guard_setpoint':
471
+ await entity.read('hvacThermostat', [0x0425]);
472
+ break;
473
+ case 'external_temp':
474
+ await entity.read('hvacThermostat', [0x0426]);
475
+ break;
476
+ case 'air_temp':
477
+ await entity.read('hvacThermostat', [0x0429]);
478
+ break;
479
+ case 'floor_sensor_error':
480
+ await entity.read('hvacThermostat', [0x042B]);
481
+ break;
482
+ case 'exteral_sensor_error':
483
+ await entity.read('hvacThermostat', [0x042C]);
484
+ break;
485
+
486
+ default: // Unknown key
487
+ throw new Error(`Unhandled key tzLocal.ctm_thermostat.convertGet ${key}`);
488
+ }
489
+ },
490
+ },
491
+ ctm_group_config: {
492
+ key: ['group_id'],
493
+ convertGet: async (entity, key, meta) => {
494
+ await entity.read(0xFEA7, [0x0000], {manufacturerCode: 0x1337, sendWhen: 'active'});
495
+ },
496
+ },
497
+ ctm_sove_guard: {
498
+ key: [
499
+ 'alarm_status', 'change_battery', 'stove_temperature', 'ambient_temperature', 'active', 'runtime', 'runtime_timeout',
500
+ 'reset_reason', 'dip_switch', 'sw_version', 'hw_version', 'bootloader_version', 'model', 'relay_address',
501
+ 'current_flag', 'relay_current', 'relay_status', 'external_button', 'relay_alarm', 'relay_alarm_status',
502
+ ],
503
+ convertGet: async (entity, key, meta) => {
504
+ switch (key) {
505
+ case 'alarm_status':
506
+ await entity.read(0xFFC9, [0x0001], {manufacturerCode: 0x1337});
507
+ break;
508
+ case 'battery_low':
509
+ await entity.read(0xFFC9, [0x0002], {manufacturerCode: 0x1337});
510
+ break;
511
+ case 'stove_temperature':
512
+ await entity.read(0xFFC9, [0x0003], {manufacturerCode: 0x1337});
513
+ break;
514
+ case 'ambient_temperature':
515
+ await entity.read(0xFFC9, [0x0004], {manufacturerCode: 0x1337});
516
+ break;
517
+ case 'active':
518
+ await entity.read(0xFFC9, [0x0005], {manufacturerCode: 0x1337});
519
+ break;
520
+ case 'runtime':
521
+ await entity.read(0xFFC9, [0x0006], {manufacturerCode: 0x1337});
522
+ break;
523
+ case 'runtime_timeout':
524
+ await entity.read(0xFFC9, [0x0007], {manufacturerCode: 0x1337});
525
+ break;
526
+ case 'reset_reason':
527
+ await entity.read(0xFFC9, [0x0008], {manufacturerCode: 0x1337});
528
+ break;
529
+ case 'dip_switch':
530
+ await entity.read(0xFFC9, [0x0009], {manufacturerCode: 0x1337});
531
+ break;
532
+ case 'sw_version':
533
+ await entity.read(0xFFC9, [0x000A], {manufacturerCode: 0x1337});
534
+ break;
535
+ case 'hw_version':
536
+ await entity.read(0xFFC9, [0x000B], {manufacturerCode: 0x1337});
537
+ break;
538
+ case 'bootloader_version':
539
+ await entity.read(0xFFC9, [0x000C], {manufacturerCode: 0x1337});
540
+ break;
541
+ case 'model':
542
+ await entity.read(0xFFC9, [0x000D], {manufacturerCode: 0x1337});
543
+ break;
544
+ case 'relay_address':
545
+ await entity.read(0xFFC9, [0x0010], {manufacturerCode: 0x1337});
546
+ break;
547
+ case 'current_flag':
548
+ await entity.read(0xFFC9, [0x0100], {manufacturerCode: 0x1337});
549
+ break;
550
+ case 'relay_current':
551
+ await entity.read(0xFFC9, [0x0101], {manufacturerCode: 0x1337});
552
+ break;
553
+ case 'relay_status':
554
+ await entity.read(0xFFC9, [0x0102], {manufacturerCode: 0x1337});
555
+ break;
556
+ case 'external_button':
557
+ await entity.read(0xFFC9, [0x0103], {manufacturerCode: 0x1337});
558
+ break;
559
+ case 'relay_alarm':
560
+ await entity.read(0xFFC9, [0x0104], {manufacturerCode: 0x1337});
561
+ break;
562
+ case 'relay_alarm_status':
563
+ await entity.read(0xFFC9, [0x0105], {manufacturerCode: 0x1337});
564
+ break;
565
+
566
+ default: // Unknown key
567
+ throw new Error(`Unhandled key tzLocal.ctm_sove_guard.convertGet ${key}`);
568
+ }
569
+ },
570
+ },
571
+ };
572
+
573
+ module.exports = [
574
+ {
575
+ zigbeeModel: ['mTouch Dim'],
576
+ model: 'mTouch_Dim',
577
+ vendor: 'CTM Lyng',
578
+ description: 'mTouch Dim OP, touch dimmer',
579
+ fromZigbee: [fz.on_off, fz.brightness, fz.lighting_ballast_configuration],
580
+ toZigbee: [tz.on_off, tz.light_onoff_brightness, tz.light_brightness_move, tz.ballast_config, tzLocal.ballast_power_on_level],
581
+ meta: {disableDefaultResponse: true},
582
+ configure: async (device, coordinatorEndpoint, logger) => {
583
+ const endpoint = device.getEndpoint(1);
584
+ await reporting.bind(endpoint, coordinatorEndpoint, ['genOnOff', 'genLevelCtrl', 'lightingBallastCfg']);
585
+ await endpoint.read('genOnOff', ['onOff']);
586
+ await reporting.onOff(endpoint);
587
+ await endpoint.read('genLevelCtrl', ['currentLevel']);
588
+ await reporting.brightness(endpoint);
589
+ await endpoint.read('lightingBallastCfg', ['minLevel', 'maxLevel', 'powerOnLevel']);
590
+ await endpoint.configureReporting('lightingBallastCfg', [{
591
+ attribute: 'minLevel',
592
+ minimumReportInterval: 0,
593
+ maximumReportInterval: constants.repInterval.HOUR,
594
+ reportableChange: null}]);
595
+ await endpoint.configureReporting('lightingBallastCfg', [{
596
+ attribute: 'maxLevel',
597
+ minimumReportInterval: 0,
598
+ maximumReportInterval: constants.repInterval.HOUR,
599
+ reportableChange: null}]);
600
+ await endpoint.configureReporting('lightingBallastCfg', [{
601
+ attribute: 'powerOnLevel',
602
+ minimumReportInterval: 0,
603
+ maximumReportInterval: constants.repInterval.HOUR,
604
+ reportableChange: null}]);
605
+ },
606
+ exposes: [e.light_brightness(),
607
+ exposes.numeric('ballast_minimum_level', ea.ALL).withValueMin(1).withValueMax(99)
608
+ .withDescription('Specifies the minimum brightness value'),
609
+ exposes.numeric('ballast_maximum_level', ea.ALL).withValueMin(1).withValueMax(99)
610
+ .withDescription('Specifies the maximum brightness value'),
611
+ exposes.numeric('ballast_power_on_level', ea.ALL).withValueMin(1).withValueMax(99)
612
+ .withDescription('Specifies the initialisation light level. Can not be set lower than "ballast_minimum_level"')],
613
+ },
614
+ {
615
+ zigbeeModel: ['mTouch Bryter'],
616
+ model: 'mTouch_Bryter',
617
+ vendor: 'CTM Lyng',
618
+ description: 'mTouch Bryter OP, 3 channel switch',
619
+ fromZigbee: [fz.temperature, fz.battery, fz.command_recall, fz.command_on, fz.command_off, fz.command_toggle,
620
+ fz.command_move, fz.command_stop, fzLocal.ctm_group_config],
621
+ toZigbee: [],
622
+ meta: {battery: {voltageToPercentage: '3V_2500_3200'}},
623
+ configure: async (device, coordinatorEndpoint, logger) => {
624
+ const endpoint = device.getEndpoint(1);
625
+ await reporting.bind(endpoint, coordinatorEndpoint, ['genPowerCfg', 'msTemperatureMeasurement']);
626
+ await reporting.batteryVoltage(endpoint);
627
+ await endpoint.read('msTemperatureMeasurement', ['measuredValue']);
628
+ await reporting.temperature(endpoint, {min: constants.repInterval.MINUTES_10, max: constants.repInterval.HOUR, change: 100});
629
+ await endpoint.read(0xFEA7, [0x0000], {manufacturerCode: 0x1337});
630
+ },
631
+ exposes: [e.battery(), e.temperature(),
632
+ e.action(['recall_1', 'recall_2', 'recall_3', 'on', 'off', 'toggle',
633
+ 'brightness_move_down', 'brightness_move_up', 'brightness_stop']),
634
+ exposes.numeric('group_id', ea.STATE)
635
+ .withDescription('The device sends commands with this group ID. Put dvices in this group to control them.')],
636
+ },
637
+ {
638
+ zigbeeModel: ['mTouch One'],
639
+ model: 'mTouch_One',
640
+ vendor: 'CTM Lyng',
641
+ description: 'mTouch One OP, touch thermostat',
642
+ fromZigbee: [fz.thermostat, fzLocal.ctm_thermostat],
643
+ toZigbee: [tz.thermostat_occupied_heating_setpoint, tz.thermostat_local_temperature, tzLocal.ctm_thermostat,
644
+ tzLocal.ctm_thermostat_preset, tzLocal.ctm_thermostat_child_lock, tzLocal.ctm_thermostat_gets],
645
+ configure: async (device, coordinatorEndpoint, logger) => {
646
+ const endpoint = device.getEndpoint(1);
647
+ await reporting.bind(endpoint, coordinatorEndpoint, ['hvacThermostat']);
648
+ await endpoint.read('hvacThermostat', ['localTemp', 'occupiedHeatingSetpoint']);
649
+ await reporting.thermostatTemperature(endpoint);
650
+ await reporting.thermostatOccupiedHeatingSetpoint(endpoint);
651
+ await endpoint.read('hvacThermostat', [0x0401]);
652
+ await endpoint.read('hvacThermostat', [0x0402]);
653
+ // Regulator mode
654
+ await endpoint.read('hvacThermostat', [0x0405]);
655
+ await endpoint.configureReporting('hvacThermostat', [{
656
+ attribute: {ID: 0x0405, type: dataType.boolean},
657
+ minimumReportInterval: 1,
658
+ maximumReportInterval: constants.repInterval.MAX,
659
+ reportableChange: null}]);
660
+ // Power consumption
661
+ await endpoint.read('hvacThermostat', [0x0408]);
662
+ await endpoint.configureReporting('hvacThermostat', [{
663
+ attribute: {ID: 0x0408, type: dataType.uint16},
664
+ minimumReportInterval: 0,
665
+ maximumReportInterval: constants.repInterval.HOUR,
666
+ reportableChange: 5}]);
667
+ // Floor temp sensor
668
+ await endpoint.read('hvacThermostat', [0x0409]);
669
+ await endpoint.configureReporting('hvacThermostat', [{
670
+ attribute: {ID: 0x0409, type: dataType.int16},
671
+ minimumReportInterval: 0,
672
+ maximumReportInterval: constants.repInterval.HOUR,
673
+ reportableChange: 10}]);
674
+ // Frost guard
675
+ await endpoint.read('hvacThermostat', [0x0412]);
676
+ await endpoint.configureReporting('hvacThermostat', [{
677
+ attribute: {ID: 0x0412, type: dataType.boolean},
678
+ minimumReportInterval: 0,
679
+ maximumReportInterval: constants.repInterval.MAX,
680
+ reportableChange: null}]);
681
+ // Child lock active/inactive
682
+ await endpoint.read('hvacThermostat', [0x0413]);
683
+ await endpoint.configureReporting('hvacThermostat', [{
684
+ attribute: {ID: 0x0413, type: dataType.boolean},
685
+ minimumReportInterval: 0,
686
+ maximumReportInterval: constants.repInterval.MAX,
687
+ reportableChange: null}]);
688
+ // Regulator setpoint
689
+ await endpoint.read('hvacThermostat', [0x0420]);
690
+ await endpoint.configureReporting('hvacThermostat', [{
691
+ attribute: {ID: 0x0420, type: dataType.uint8},
692
+ minimumReportInterval: 0,
693
+ maximumReportInterval: constants.repInterval.HOUR,
694
+ reportableChange: 1}]);
695
+ // Operation mode
696
+ await endpoint.read('hvacThermostat', [0x0422]);
697
+ await endpoint.configureReporting('hvacThermostat', [{
698
+ attribute: {ID: 0x0422, type: dataType.uint8},
699
+ minimumReportInterval: 0,
700
+ maximumReportInterval: constants.repInterval.HOUR,
701
+ reportableChange: 1}]);
702
+ // Air temp sensor
703
+ await endpoint.read('hvacThermostat', [0x0429]);
704
+ await endpoint.configureReporting('hvacThermostat', [{
705
+ attribute: {ID: 0x0429, type: dataType.int16},
706
+ minimumReportInterval: 0,
707
+ maximumReportInterval: constants.repInterval.HOUR,
708
+ reportableChange: 10}]);
709
+ },
710
+ exposes: [e.child_lock(),
711
+ exposes.climate()
712
+ .withSetpoint('occupied_heating_setpoint', 5, 40, 1)
713
+ .withLocalTemperature()
714
+ .withSystemMode(['off', 'heat'])
715
+ .withPreset(['off', 'away', 'sleep', 'home']),
716
+ exposes.numeric('load', ea.ALL).withUnit('W')
717
+ .withDescription('Load in W when heating is on (between 0-3600 W). The thermostat uses the value as input to the ' +
718
+ 'mean_power calculation.')
719
+ .withValueMin(0).withValueMax(3600),
720
+ exposes.text('display_text', ea.ALL)
721
+ .withDescription('Displayed text on thermostat display (zone). Max 19 characters'),
722
+ exposes.binary('regulator_mode', ea.ALL, 'regulator', 'thermostat')
723
+ .withDescription('Device in regulator or thermostat mode.'),
724
+ exposes.numeric('mean_power', ea.STATE_GET).withUnit('W')
725
+ .withDescription('Reports average power usage last 10 minutes'),
726
+ exposes.numeric('floor_temp', ea.STATE_GET).withUnit('°C')
727
+ .withDescription('Current temperature measured from the floor sensor'),
728
+ exposes.binary('frost_guard', ea.ALL, 'ON', 'OFF')
729
+ .withDescription('When frost guard is ON, it is activated when the thermostat is switched OFF with the ON/OFF button.' +
730
+ 'At the same time, the display will fade and the text "Frostsikring x °C" appears in the display and remains until the ' +
731
+ 'thermostat is switched on again.'),
732
+ exposes.numeric('regulator_setpoint', ea.ALL).withUnit('%')
733
+ .withDescription('Setpoint in %, use only when the thermostat is in regulator mode.')
734
+ .withValueMin(1).withValueMax(99),
735
+ exposes.numeric('air_temp', ea.STATE_GET).withUnit('°C')
736
+ .withDescription('Current temperature measured from the air sensor'),
737
+ ],
738
+ },
739
+ {
740
+ zigbeeModel: ['mStikk Outlet'],
741
+ model: 'mStikk_Outlet',
742
+ vendor: 'CTM Lyng',
743
+ description: 'mStikk OP, wall socket',
744
+ fromZigbee: [fz.on_off, fz.electrical_measurement, fz.metering],
745
+ toZigbee: [tz.on_off],
746
+ configure: async (device, coordinatorEndpoint, logger) => {
747
+ const endpoint = device.getEndpoint(1);
748
+ await reporting.bind(endpoint, coordinatorEndpoint, ['genOnOff', 'haElectricalMeasurement', 'seMetering']);
749
+ await endpoint.read('haElectricalMeasurement', ['acVoltageMultiplier', 'acVoltageDivisor']);
750
+ await endpoint.read('haElectricalMeasurement', ['acCurrentMultiplier', 'acCurrentDivisor']);
751
+ await endpoint.read('haElectricalMeasurement', ['acPowerMultiplier', 'acPowerDivisor']);
752
+ await reporting.readMeteringMultiplierDivisor(endpoint);
753
+ await endpoint.read('genOnOff', ['onOff']);
754
+ await reporting.onOff(endpoint);
755
+ await reporting.rmsVoltage(endpoint, {change: 100});
756
+ await reporting.rmsCurrent(endpoint);
757
+ await reporting.activePower(endpoint);
758
+ await reporting.currentSummDelivered(endpoint);
759
+ },
760
+ exposes: [e.power(), e.current(), e.voltage(), e.switch(), e.energy()],
761
+ },
762
+ {
763
+ zigbeeModel: ['mKomfy'],
764
+ model: 'mKomfy_Sensor',
765
+ vendor: 'CTM Lyng',
766
+ description: 'mKomfy, stove guard',
767
+ fromZigbee: [fz.temperature, fz.battery, fzLocal.ctm_sove_guard],
768
+ toZigbee: [],
769
+ configure: async (device, coordinatorEndpoint, logger) => {
770
+ const endpoint = device.getEndpoint(1);
771
+ await reporting.bind(endpoint, coordinatorEndpoint, ['genPowerCfg', 'msTemperatureMeasurement', 0xFFC9]);
772
+ await reporting.batteryPercentageRemaining(endpoint);
773
+ // await endpoint.read('msTemperatureMeasurement', ['measuredValue']);
774
+ await reporting.temperature(endpoint, {min: constants.repInterval.MINUTES_10, max: constants.repInterval.HOUR, change: 100});
775
+ // Alarm status
776
+ // await endpoint.read(0xFFC9, [0x0001], {manufacturerCode: 0x1337});
777
+ await endpoint.configureReporting(0xFFC9, [{
778
+ attribute: {ID: 0x0001, type: dataType.uint8},
779
+ minimumReportInterval: 0,
780
+ maximumReportInterval: constants.repInterval.HOUR,
781
+ reportableChange: 0}], {manufacturerCode: 0x1337});
782
+ // Change battery
783
+ // await endpoint.read(0xFFC9, [0x0002], {manufacturerCode: 0x1337});
784
+ await endpoint.configureReporting(0xFFC9, [{
785
+ attribute: {ID: 0x0002, type: dataType.uint8},
786
+ minimumReportInterval: 0,
787
+ maximumReportInterval: constants.repInterval.MAX,
788
+ reportableChange: 0}], {manufacturerCode: 0x1337});
789
+ // Active
790
+ // await endpoint.read(0xFFC9, [0x0005], {manufacturerCode: 0x1337});
791
+ await endpoint.configureReporting(0xFFC9, [{
792
+ attribute: {ID: 0x0005, type: dataType.uint8},
793
+ minimumReportInterval: 0,
794
+ maximumReportInterval: constants.repInterval.HOUR,
795
+ reportableChange: 0}], {manufacturerCode: 0x1337});
796
+ },
797
+ exposes: [e.battery(), e.battery_low(), e.temperature(),
798
+ exposes.enum('alarm_status', ea.STATE, ['ok', 'tamper', 'high_temperatur', 'timer', 'battery_alarm', 'error', 'unknown'])
799
+ .withDescription('Alarm status.'),
800
+ exposes.binary('active', ea.STATE, true, false)
801
+ .withDescription('Stove guard active/inactive (Stove in use)')],
802
+ },
803
+ {
804
+ zigbeeModel: ['mTouch Astro'],
805
+ model: 'mTouch_Astro',
806
+ vendor: 'CTM Lyng',
807
+ description: 'mTouch Astro OP, astro clock',
808
+ fromZigbee: [fz.on_off, fz.command_on, fz.command_off, fzLocal.ctm_device_mode, fzLocal.ctm_device_enabled,
809
+ fzLocal.ctm_child_lock, fzLocal.ctm_group_config],
810
+ toZigbee: [tz.on_off, tzLocal.ctm_device_enabled],
811
+ meta: {disableDefaultResponse: true},
812
+ configure: async (device, coordinatorEndpoint, logger) => {
813
+ const endpoint = device.getEndpoint(1);
814
+ await reporting.bind(endpoint, coordinatorEndpoint, ['genOnOff']);
815
+ await endpoint.read('genOnOff', ['onOff']);
816
+ await reporting.onOff(endpoint);
817
+ // Device mode
818
+ await endpoint.read('genOnOff', [0x2200]);
819
+ await endpoint.configureReporting('genOnOff', [{
820
+ attribute: {ID: 0x2200, type: dataType.uint8},
821
+ minimumReportInterval: 0,
822
+ maximumReportInterval: constants.repInterval.HOUR,
823
+ reportableChange: 0}]);
824
+ await endpoint.read('genOnOff', [0x2201]);
825
+ await endpoint.configureReporting('genOnOff', [{
826
+ attribute: {ID: 0x2201, type: dataType.boolean},
827
+ minimumReportInterval: 0,
828
+ maximumReportInterval: constants.repInterval.HOUR,
829
+ reportableChange: null}]);
830
+ await endpoint.read('genOnOff', [0x2202]);
831
+ await endpoint.configureReporting('genOnOff', [{
832
+ attribute: {ID: 0x2202, type: dataType.boolean},
833
+ minimumReportInterval: 0,
834
+ maximumReportInterval: constants.repInterval.HOUR,
835
+ reportableChange: null}]);
836
+ await endpoint.read(0xFEA7, [0x0000], {manufacturerCode: 0x1337});
837
+ },
838
+ exposes: [e.switch(), e.action(['on', 'off']),
839
+ exposes.enum('device_mode', ea.STATE, ['astro_clock', 'timer', 'daily_timer', 'weekly_timer'])
840
+ .withDescription('Device mode.'),
841
+ exposes.binary('device_enabled', ea.ALL, 'ON', 'OFF')
842
+ .withDescription('Turn the device on or off'),
843
+ exposes.binary('child_lock', ea.STATE, 'locked', 'unlocked')
844
+ .withDescription('Physical input on the device enabled/disabled'),
845
+ exposes.numeric('group_id', ea.STATE)
846
+ .withDescription('The device sends commands with this group ID. Put dvices in this group to control them.'),
847
+ ],
848
+ },
849
+ {
850
+ zigbeeModel: ['AX Water Sensor'],
851
+ model: 'AX_Water_Sensor',
852
+ vendor: 'CTM Lyng',
853
+ description: 'AX Water Sensor, water leakage detector',
854
+ fromZigbee: [fz.battery, fz.ias_enroll, fzLocal.ctm_water_leak_alarm],
855
+ toZigbee: [],
856
+ meta: {battery: {voltageToPercentage: '3V_2500_3200'}},
857
+ configure: async (device, coordinatorEndpoint, logger) => {
858
+ const endpoint = device.getEndpoint(1);
859
+ await reporting.bind(endpoint, coordinatorEndpoint, ['genPowerCfg', 'ssIasZone']);
860
+ await reporting.batteryVoltage(endpoint);
861
+ await endpoint.read('ssIasZone', ['iasCieAddr', 'zoneState', 'zoneId']);
862
+ },
863
+ exposes: [e.battery(), e.battery_low(), e.water_leak(),
864
+ exposes.binary('active_water_leak', ea.STATE, true, false)
865
+ .withDescription('Indicates whether there is an active water leak'),
866
+ ],
867
+ },
868
+ {
869
+ zigbeeModel: ['AX Valve Controller'],
870
+ model: 'AX_Valve_Controller',
871
+ vendor: 'CTM Lyng',
872
+ description: 'AX Valve Controller, water shutoff valve controller',
873
+ fromZigbee: [fz.on_off, fz.ias_enroll, fzLocal.ctm_water_leak_alarm],
874
+ toZigbee: [tz.on_off],
875
+ meta: {disableDefaultResponse: true},
876
+ configure: async (device, coordinatorEndpoint, logger) => {
877
+ const endpoint = device.getEndpoint(1);
878
+ await reporting.bind(endpoint, coordinatorEndpoint, ['genOnOff', 'ssIasZone']);
879
+ await endpoint.read('genOnOff', ['onOff']);
880
+ await reporting.onOff(endpoint);
881
+ await endpoint.read('ssIasZone', ['iasCieAddr', 'zoneState', 'zoneId']);
882
+ },
883
+ exposes: [e.switch(), e.water_leak(),
884
+ exposes.binary('active_water_leak', ea.STATE, true, false)
885
+ .withDescription('Indicates whether there is an active water leak'),
886
+ ],
887
+ },
888
+ {
889
+ zigbeeModel: ['Mikrofon'],
890
+ model: 'mSwitch_Mic',
891
+ vendor: 'CTM Lyng',
892
+ description: 'Mikrofon, alarm detection microphone',
893
+ fromZigbee: [fz.temperature, fz.battery, fz.command_on, fz.command_off, fz.ias_enroll, fz.ias_smoke_alarm_1,
894
+ fzLocal.ctm_group_config],
895
+ toZigbee: [],
896
+ meta: {battery: {voltageToPercentage: '3V_2500_3200'}},
897
+ configure: async (device, coordinatorEndpoint, logger) => {
898
+ const endpoint = device.getEndpoint(1);
899
+ await reporting.bind(endpoint, coordinatorEndpoint, ['genPowerCfg', 'ssIasZone', 'msTemperatureMeasurement']);
900
+ await reporting.batteryVoltage(endpoint);
901
+ await endpoint.read('ssIasZone', ['iasCieAddr', 'zoneState', 'zoneId']);
902
+ await endpoint.read('msTemperatureMeasurement', ['measuredValue']);
903
+ await reporting.temperature(endpoint, {min: constants.repInterval.MINUTES_10, max: constants.repInterval.HOUR, change: 100});
904
+ await endpoint.read(0xFEA7, [0x0000], {manufacturerCode: 0x1337});
905
+ },
906
+ exposes: [e.temperature(), e.battery(), e.battery_low(), e.smoke(),
907
+ e.action(['on', 'off']),
908
+ exposes.numeric('group_id', ea.STATE)
909
+ .withDescription('The device sends commands with this group ID. Put dvices in this group to control them.'),
910
+ ],
911
+ },
912
+ {
913
+ zigbeeModel: ['Air Sensor'],
914
+ model: 'mTouch_Air_Sensor',
915
+ vendor: 'CTM Lyng',
916
+ description: 'Air Sensor, temperature & humidity sensor',
917
+ fromZigbee: [fz.battery, fz.temperature, fz.humidity],
918
+ toZigbee: [],
919
+ meta: {battery: {voltageToPercentage: '3V_2500_3200'}},
920
+ configure: async (device, coordinatorEndpoint, logger) => {
921
+ const endpoint = device.getEndpoint(1);
922
+ await reporting.bind(endpoint, coordinatorEndpoint, ['genPowerCfg', 'msTemperatureMeasurement', 'msRelativeHumidity']);
923
+ await reporting.batteryVoltage(endpoint);
924
+ await endpoint.read('msTemperatureMeasurement', ['measuredValue']);
925
+ await reporting.temperature(endpoint);
926
+ await endpoint.read('msRelativeHumidity', ['measuredValue']);
927
+ await reporting.humidity(endpoint);
928
+ },
929
+ exposes: [e.battery(), e.temperature(), e.humidity()],
930
+ },
931
+ {
932
+ zigbeeModel: ['MBD-S'],
933
+ model: 'MBD-S',
934
+ vendor: 'CTM Lyng',
935
+ description: 'MBD-S, motion detector with 16A relay',
936
+ fromZigbee: [fz.on_off, fz.illuminance, fz.occupancy, fzLocal.ctm_device_enabled],
937
+ toZigbee: [tz.on_off, tzLocal.ctm_device_enabled],
938
+ meta: {disableDefaultResponse: true},
939
+ configure: async (device, coordinatorEndpoint, logger) => {
940
+ const endpoint = device.getEndpoint(1);
941
+ await reporting.bind(endpoint, coordinatorEndpoint, ['genOnOff', 'msIlluminanceMeasurement', 'msOccupancySensing']);
942
+ await endpoint.read('genOnOff', ['onOff']);
943
+ await reporting.onOff(endpoint);
944
+ await endpoint.read('msIlluminanceMeasurement', ['measuredValue']);
945
+ await reporting.illuminance(endpoint);
946
+ await endpoint.read('msOccupancySensing', ['occupancy']);
947
+ await reporting.occupancy(endpoint);
948
+ // Device enabled
949
+ await endpoint.read('genOnOff', [0x2201]);
950
+ await endpoint.configureReporting('genOnOff', [{
951
+ attribute: {ID: 0x2201, type: dataType.boolean},
952
+ minimumReportInterval: 0,
953
+ maximumReportInterval: constants.repInterval.HOUR,
954
+ reportableChange: null}]);
955
+ },
956
+ exposes: [e.switch(), e.illuminance(), e.illuminance_lux(), e.occupancy(),
957
+ exposes.binary('device_enabled', ea.ALL, 'ON', 'OFF')
958
+ .withDescription('Turn the device on or off'),
959
+ ],
960
+ },
961
+ ];
@@ -0,0 +1,24 @@
1
+ const fz = require('../converters/fromZigbee');
2
+ const tz = require('../converters/toZigbee');
3
+ const reporting = require('../lib/reporting');
4
+ const exposes = require('../lib/exposes');
5
+ const e = exposes.presets;
6
+
7
+ module.exports = [
8
+ {
9
+ zigbeeModel: ['PSE03-V1.1.0'],
10
+ model: 'PSE03-V1.1.0',
11
+ vendor: 'EVOLOGY',
12
+ description: 'Sound and flash siren',
13
+ fromZigbee: [fz.ignore_basic_report, fz.ias_wd, fz.ias_enroll, fz.ias_siren],
14
+ toZigbee: [tz.warning],
15
+ meta: {disableDefaultResponse: true},
16
+ configure: async (device, coordinatorEndpoint, logger) => {
17
+ const endpoint = device.getEndpoint(1);
18
+ await reporting.bind(endpoint, coordinatorEndpoint, ['genPowerCfg', 'ssIasZone', 'ssIasWd', 'genBasic']);
19
+ await endpoint.read('ssIasZone', ['zoneState', 'iasCieAddr', 'zoneId', 'zoneStatus']);
20
+ await endpoint.read('ssIasWd', ['maxDuration']);
21
+ },
22
+ exposes: [e.warning().removeFeature('strobe_level').removeFeature('strobe').removeFeature('strobe_duty_cycle')],
23
+ },
24
+ ];
package/devices/heiman.js CHANGED
@@ -256,7 +256,8 @@ module.exports = [
256
256
  exposes: [e.carbon_monoxide(), e.battery_low(), e.battery()],
257
257
  },
258
258
  {
259
- fingerprint: [{modelID: 'TS0216', manufacturerName: '_TYZB01_8scntis1'}],
259
+ fingerprint: [{modelID: 'TS0216', manufacturerName: '_TYZB01_8scntis1'},
260
+ {modelID: 'TS0216', manufacturerName: '_TYZB01_4obovpbi'}],
260
261
  zigbeeModel: ['WarningDevice', 'WarningDevice-EF-3.0', 'SRHMP-I1'],
261
262
  model: 'HS2WD-E',
262
263
  vendor: 'HEIMAN',
package/devices/hive.js CHANGED
@@ -91,7 +91,7 @@ module.exports = [
91
91
  model: '1613V',
92
92
  vendor: 'Hive',
93
93
  description: 'Active plug',
94
- fromZigbee: [fz.on_off, fz.metering, fz.temperature],
94
+ fromZigbee: [fz.on_off, fz.metering],
95
95
  toZigbee: [tz.on_off],
96
96
  configure: async (device, coordinatorEndpoint, logger) => {
97
97
  const endpoint = device.getEndpoint(9);
@@ -99,8 +99,9 @@ module.exports = [
99
99
  await reporting.onOff(endpoint);
100
100
  await reporting.readMeteringMultiplierDivisor(endpoint);
101
101
  await reporting.instantaneousDemand(endpoint);
102
+ await reporting.currentSummDelivered(endpoint);
102
103
  },
103
- exposes: [e.switch(), e.power(), e.energy(), e.temperature()],
104
+ exposes: [e.switch(), e.power(), e.energy()],
104
105
  },
105
106
  {
106
107
  zigbeeModel: ['TWBulb01US'],
@@ -159,4 +159,12 @@ module.exports = [
159
159
  extend: extend.ledvance.light_onoff_brightness_colortemp_color({colorTempRange: [153, 526]}),
160
160
  ota: ota.ledvance,
161
161
  },
162
+ {
163
+ zigbeeModel: ['A60S RGBW'],
164
+ model: '4058075208391',
165
+ vendor: 'LEDVANCE',
166
+ description: 'SMART+ lamp E27 RGBW',
167
+ extend: extend.ledvance.light_onoff_brightness_colortemp_color({colorTempRange: [153, 370]}),
168
+ ota: ota.ledvance,
169
+ },
162
170
  ];
package/devices/tuya.js CHANGED
@@ -406,6 +406,7 @@ module.exports = [
406
406
  {modelID: 'TS0001', manufacturerName: '_TZ3000_qsp2pwtf'}],
407
407
  model: 'WHD02',
408
408
  vendor: 'TuYa',
409
+ whiteLabel: [{vendor: 'TuYa', model: 'iHSW02'}],
409
410
  description: 'Wall switch module',
410
411
  toZigbee: extend.switch().toZigbee.concat([tz.moes_power_on_behavior, tz.tuya_switch_type]),
411
412
  fromZigbee: extend.switch().fromZigbee.concat([fz.moes_power_on_behavior, fz.tuya_switch_type]),
package/devices/ubisys.js CHANGED
@@ -74,6 +74,22 @@ const ubisys = {
74
74
  }
75
75
  },
76
76
  },
77
+ configure_device_setup: {
78
+ cluster: 'manuSpecificUbisysDeviceSetup',
79
+ type: ['attributeReport', 'readResponse'],
80
+ convert: (model, msg, publish, options, meta) => {
81
+ const result = {};
82
+ if (msg.data.hasOwnProperty('input_configurations')) {
83
+ result['input_configurations'] = msg.data['input_configurations'];
84
+ }
85
+ if (msg.data.hasOwnProperty('inputActions')) {
86
+ result['input_actions'] = msg.data['inputActions'].map(function(el) {
87
+ return Object.values(el);
88
+ });
89
+ }
90
+ return {configure_device_setup: result};
91
+ },
92
+ },
77
93
  },
78
94
  tz: {
79
95
  configure_j1: {
@@ -482,13 +498,9 @@ const ubisys = {
482
498
  },
483
499
 
484
500
  convertGet: async (entity, key, meta) => {
485
- const log = (dataRead) => {
486
- meta.logger.warn(
487
- `ubisys: Device setup read for '${meta.options.friendly_name}': ${JSON.stringify(utils.toSnakeCase(dataRead))}`);
488
- };
489
501
  const devMgmtEp = meta.device.getEndpoint(232);
490
- log(await devMgmtEp.read('manuSpecificUbisysDeviceSetup', ['inputConfigurations'], manufacturerOptions.ubisysNull));
491
- log(await devMgmtEp.read('manuSpecificUbisysDeviceSetup', ['inputActions'], manufacturerOptions.ubisysNull));
502
+ await devMgmtEp.read('manuSpecificUbisysDeviceSetup', ['inputConfigurations', 'inputActions'],
503
+ manufacturerOptions.ubisysNull);
492
504
  },
493
505
  },
494
506
  },
@@ -507,7 +519,7 @@ module.exports = [
507
519
  ]),
508
520
  e.power_on_behavior()],
509
521
  fromZigbee: [fz.on_off, fz.metering, fz.command_toggle, fz.command_on, fz.command_off, fz.command_recall, fz.command_move,
510
- fz.command_stop, fz.power_on_behavior],
522
+ fz.command_stop, fz.power_on_behavior, ubisys.fz.configure_device_setup],
511
523
  toZigbee: [tz.on_off, tz.metering_power, ubisys.tz.configure_device_setup, tz.power_on_behavior],
512
524
  endpoint: (device) => {
513
525
  return {'l1': 1, 's1': 2, 'meter': 3};
@@ -549,7 +561,7 @@ module.exports = [
549
561
  ]),
550
562
  e.power_on_behavior()],
551
563
  fromZigbee: [fz.on_off, fz.metering, fz.command_toggle, fz.command_on, fz.command_off, fz.command_recall, fz.command_move,
552
- fz.command_stop, fz.power_on_behavior],
564
+ fz.command_stop, fz.power_on_behavior, ubisys.fz.configure_device_setup],
553
565
  toZigbee: [tz.on_off, tz.metering_power, ubisys.tz.configure_device_setup, tz.power_on_behavior],
554
566
  endpoint: (device) => {
555
567
  return {'l1': 1, 's1': 2, 'meter': 4};
@@ -592,7 +604,7 @@ module.exports = [
592
604
  'brightness_stop_s2']),
593
605
  e.power_on_behavior().withEndpoint('l1'), e.power_on_behavior().withEndpoint('l2')],
594
606
  fromZigbee: [fz.on_off, fz.metering, fz.command_toggle, fz.command_on, fz.command_off, fz.command_recall, fz.command_move,
595
- fz.command_stop, fz.power_on_behavior],
607
+ fz.command_stop, fz.power_on_behavior, ubisys.fz.configure_device_setup],
596
608
  toZigbee: [tz.on_off, tz.metering_power, ubisys.tz.configure_device_setup, tz.power_on_behavior],
597
609
  endpoint: (device) => {
598
610
  return {'l1': 1, 'l2': 2, 's1': 3, 's2': 4, 'meter': 5};
@@ -638,7 +650,7 @@ module.exports = [
638
650
  description: 'Universal dimmer D1',
639
651
  fromZigbee: [fz.on_off, fz.brightness, fz.metering, fz.command_toggle, fz.command_on, fz.command_off, fz.command_recall,
640
652
  fz.command_move, fz.command_stop, fz.lighting_ballast_configuration, fz.level_config, ubisys.fz.dimmer_setup,
641
- ubisys.fz.dimmer_setup_genLevelCtrl],
653
+ ubisys.fz.dimmer_setup_genLevelCtrl, ubisys.fz.configure_device_setup],
642
654
  toZigbee: [tz.light_onoff_brightness, tz.ballast_config, tz.level_config, ubisys.tz.dimmer_setup,
643
655
  ubisys.tz.dimmer_setup_genLevelCtrl, ubisys.tz.configure_device_setup, tz.ignore_transition, tz.light_brightness_move,
644
656
  tz.light_brightness_step],
@@ -702,7 +714,7 @@ module.exports = [
702
714
  model: 'J1',
703
715
  vendor: 'Ubisys',
704
716
  description: 'Shutter control J1',
705
- fromZigbee: [fz.cover_position_tilt, fz.metering],
717
+ fromZigbee: [fz.cover_position_tilt, fz.metering, ubisys.fz.configure_device_setup],
706
718
  toZigbee: [tz.cover_state, tz.cover_position_tilt, tz.metering_power,
707
719
  ubisys.tz.configure_j1, ubisys.tz.configure_device_setup],
708
720
  exposes: [e.cover_position_tilt(),
@@ -739,7 +751,8 @@ module.exports = [
739
751
  model: 'C4',
740
752
  vendor: 'Ubisys',
741
753
  description: 'Control unit C4',
742
- fromZigbee: [fz.legacy.ubisys_c4_scenes, fz.legacy.ubisys_c4_onoff, fz.legacy.ubisys_c4_level, fz.legacy.ubisys_c4_cover],
754
+ fromZigbee: [fz.legacy.ubisys_c4_scenes, fz.legacy.ubisys_c4_onoff, fz.legacy.ubisys_c4_level, fz.legacy.ubisys_c4_cover,
755
+ ubisys.fz.configure_device_setup],
743
756
  toZigbee: [ubisys.tz.configure_device_setup],
744
757
  exposes: [e.action([
745
758
  '1_scene_*', '1_on', '1_off', '1_toggle', '1_level_move_down', '1_level_move_up',
package/lib/utils.js CHANGED
@@ -75,12 +75,12 @@ function hasAlreadyProcessedMessage(msg, ID=null, key=null) {
75
75
  }
76
76
 
77
77
  const defaultPrecision = {temperature: 2, humidity: 2, pressure: 1, pm25: 0, power: 2, current: 2, current_phase_b: 2, current_phase_c: 2,
78
- voltage: 2, voltage_phase_b: 2, voltage_phase_c: 2};
78
+ voltage: 2, voltage_phase_b: 2, voltage_phase_c: 2, power_phase_b: 2, power_phase_c: 2};
79
79
  function calibrateAndPrecisionRoundOptions(number, options, type) {
80
80
  // Calibrate
81
81
  const calibrateKey = `${type}_calibration`;
82
82
  let calibrationOffset = options && options.hasOwnProperty(calibrateKey) ? options[calibrateKey] : 0;
83
- if (['illuminance', 'illuminance_lux', 'power', 'current', 'voltage'].includes(type)) {
83
+ if (type.startsWith('current') || type.startsWith('voltage') || type.startsWith('power') || type.startsWith('illuminance')) {
84
84
  // linear calibration because measured value is zero based
85
85
  // +/- percent
86
86
  calibrationOffset = Math.round(number * calibrationOffset / 100);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zigbee-herdsman-converters",
3
- "version": "14.0.566",
3
+ "version": "14.0.569",
4
4
  "description": "Collection of device converters to be used with zigbee-herdsman",
5
5
  "main": "index.js",
6
6
  "files": [