zigbee-herdsman-converters 14.0.568 → 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
+ ];
@@ -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/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.568",
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": [