zigbee-herdsman-converters 14.0.410 → 14.0.414
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/converters/fromZigbee.js +169 -84
- package/converters/toZigbee.js +100 -25
- package/devices/bticino.js +10 -10
- package/devices/custom_devices_diy.js +40 -0
- package/devices/gledopto.js +11 -7
- package/devices/jasco.js +18 -0
- package/devices/legrand.js +33 -12
- package/devices/lixee.js +574 -0
- package/devices/moes.js +22 -9
- package/devices/philips.js +11 -2
- package/devices/sonoff.js +11 -0
- package/devices/tuya.js +19 -10
- package/devices/xiaomi.js +35 -35
- package/devices/zemismart.js +13 -8
- package/lib/exposes.js +1 -1
- package/lib/ota/index.js +1 -0
- package/lib/ota/lixee.js +57 -0
- package/lib/tuya.js +22 -4
- package/lib/utils.js +2 -0
- package/npm-shrinkwrap.json +426 -401
- package/package.json +2 -2
package/converters/fromZigbee.js
CHANGED
|
@@ -616,6 +616,25 @@ const converters = {
|
|
|
616
616
|
return result;
|
|
617
617
|
},
|
|
618
618
|
},
|
|
619
|
+
meter_identification: {
|
|
620
|
+
cluster: 'haMeterIdentification',
|
|
621
|
+
type: ['readResponse'],
|
|
622
|
+
convert: (model, msg, publish, options, meta) => {
|
|
623
|
+
const result = {};
|
|
624
|
+
const elements = [
|
|
625
|
+
/* 0x000A*/ 'softwareRevision',
|
|
626
|
+
/* 0x000D*/ 'availablePower',
|
|
627
|
+
/* 0x000E*/ 'powerThreshold',
|
|
628
|
+
];
|
|
629
|
+
for (const at of elements) {
|
|
630
|
+
const atSnake = at.split(/(?=[A-Z])/).join('_').toLowerCase();
|
|
631
|
+
if (msg.data[at]) {
|
|
632
|
+
result[atSnake] = msg.data[at];
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
return result;
|
|
636
|
+
},
|
|
637
|
+
},
|
|
619
638
|
metering: {
|
|
620
639
|
/**
|
|
621
640
|
* When using this converter also add the following to the configure method of the device:
|
|
@@ -2395,6 +2414,32 @@ const converters = {
|
|
|
2395
2414
|
}
|
|
2396
2415
|
},
|
|
2397
2416
|
},
|
|
2417
|
+
moes_switch: {
|
|
2418
|
+
cluster: 'manuSpecificTuya',
|
|
2419
|
+
type: ['commandDataResponse', 'commandDataReport'],
|
|
2420
|
+
convert: (model, msg, publish, options, meta) => {
|
|
2421
|
+
const dpValue = tuya.firstDpValue(msg, meta, 'moes_switch');
|
|
2422
|
+
const dp = dpValue.dp;
|
|
2423
|
+
|
|
2424
|
+
// tuya_switch datapoints
|
|
2425
|
+
if (dp >= 1 && dp <= 4) {
|
|
2426
|
+
return null;
|
|
2427
|
+
}
|
|
2428
|
+
|
|
2429
|
+
const value = tuya.getDataValue(dpValue);
|
|
2430
|
+
|
|
2431
|
+
switch (dp) {
|
|
2432
|
+
case tuya.dataPoints.moesSwitchPowerOnBehavior:
|
|
2433
|
+
return {power_on_behavior: tuya.moesSwitch.powerOnBehavior[value]};
|
|
2434
|
+
case tuya.dataPoints.moesSwitchIndicateLight:
|
|
2435
|
+
return {indicate_light: tuya.moesSwitch.indicateLight[value]};
|
|
2436
|
+
default:
|
|
2437
|
+
meta.logger.warn(`fromZigbee:moes_switch: NOT RECOGNIZED DP #${
|
|
2438
|
+
dp} with data ${JSON.stringify(dpValue)}`);
|
|
2439
|
+
break;
|
|
2440
|
+
}
|
|
2441
|
+
},
|
|
2442
|
+
},
|
|
2398
2443
|
eurotronic_thermostat: {
|
|
2399
2444
|
cluster: 'hvacThermostat',
|
|
2400
2445
|
type: ['attributeReport', 'readResponse'],
|
|
@@ -2598,6 +2643,31 @@ const converters = {
|
|
|
2598
2643
|
}
|
|
2599
2644
|
},
|
|
2600
2645
|
},
|
|
2646
|
+
ts011f_plug_indicator_mode: {
|
|
2647
|
+
cluster: 'genOnOff',
|
|
2648
|
+
type: ['attributeReport', 'readResponse'],
|
|
2649
|
+
convert: (model, msg, publish, options, meta) => {
|
|
2650
|
+
const property = 'tuyaBacklightMode'; // 0x8001 or 32769
|
|
2651
|
+
if (msg.data.hasOwnProperty(property)) {
|
|
2652
|
+
const value = msg.data[property];
|
|
2653
|
+
const lookup = {0: 'off', 1: 'off/on', 2: 'on/off', 3: 'on'};
|
|
2654
|
+
if (lookup.hasOwnProperty(value)) {
|
|
2655
|
+
return {indicator_mode: lookup[value]};
|
|
2656
|
+
}
|
|
2657
|
+
}
|
|
2658
|
+
},
|
|
2659
|
+
},
|
|
2660
|
+
ts011f_plug_child_mode: {
|
|
2661
|
+
cluster: 'genOnOff',
|
|
2662
|
+
type: ['attributeReport', 'readResponse'],
|
|
2663
|
+
convert: (model, msg, publish, options, meta) => {
|
|
2664
|
+
const property = (0x8000).toString(); // 32768
|
|
2665
|
+
if (msg.data.hasOwnProperty(property)) {
|
|
2666
|
+
const value = msg.data[property];
|
|
2667
|
+
return {child_lock: value ? 'LOCK' : 'UNLOCK'};
|
|
2668
|
+
}
|
|
2669
|
+
},
|
|
2670
|
+
},
|
|
2601
2671
|
WSZ01_on_off_action: {
|
|
2602
2672
|
cluster: 65029,
|
|
2603
2673
|
type: 'raw',
|
|
@@ -5020,27 +5090,28 @@ const converters = {
|
|
|
5020
5090
|
}
|
|
5021
5091
|
},
|
|
5022
5092
|
},
|
|
5023
|
-
|
|
5093
|
+
legrand_cluster_fc01: {
|
|
5024
5094
|
cluster: 'manuSpecificLegrandDevices',
|
|
5025
5095
|
type: ['readResponse'],
|
|
5026
5096
|
convert: (model, msg, publish, options, meta) => {
|
|
5027
5097
|
const payload = {};
|
|
5028
|
-
|
|
5029
|
-
|
|
5030
|
-
|
|
5031
|
-
|
|
5032
|
-
|
|
5033
|
-
|
|
5034
|
-
|
|
5035
|
-
|
|
5036
|
-
|
|
5037
|
-
|
|
5038
|
-
|
|
5039
|
-
|
|
5040
|
-
|
|
5041
|
-
|
|
5042
|
-
payload.device_mode = 'unknown';
|
|
5098
|
+
|
|
5099
|
+
if (msg.data.hasOwnProperty('0')) {
|
|
5100
|
+
const option0 = msg.data['0'];
|
|
5101
|
+
|
|
5102
|
+
if (option0 === 0x0001) payload.device_mode = 'pilot_off';
|
|
5103
|
+
else if (option0 === 0x0002) payload.device_mode = 'pilot_on';
|
|
5104
|
+
else if (option0 === 0x0003) payload.device_mode = 'switch';
|
|
5105
|
+
else if (option0 === 0x0004) payload.device_mode = 'auto';
|
|
5106
|
+
else if (option0 === 0x0100) payload.device_mode = 'dimmer_off';
|
|
5107
|
+
else if (option0 === 0x0101) payload.device_mode = 'dimmer_on';
|
|
5108
|
+
else {
|
|
5109
|
+
meta.logger.warn(`device_mode ${option0} not recognized, please fix me`);
|
|
5110
|
+
payload.device_mode = 'unknown';
|
|
5111
|
+
}
|
|
5043
5112
|
}
|
|
5113
|
+
if (msg.data.hasOwnProperty('1')) payload.permanent_led = msg.data['1'] === 0x00 ? 'OFF' : 'ON';
|
|
5114
|
+
if (msg.data.hasOwnProperty('2')) payload.led_when_on = msg.data['2'] === 0x00 ? 'OFF' : 'ON';
|
|
5044
5115
|
return payload;
|
|
5045
5116
|
},
|
|
5046
5117
|
},
|
|
@@ -5290,8 +5361,11 @@ const converters = {
|
|
|
5290
5361
|
if (index == 1) {
|
|
5291
5362
|
payload.voltage = value;
|
|
5292
5363
|
payload.battery = batteryVoltageToPercentage(value, '3V_2100');
|
|
5293
|
-
} else if (index === 3)
|
|
5294
|
-
|
|
5364
|
+
} else if (index === 3) {
|
|
5365
|
+
if (!['WXCJKG11LM ', 'WXCJKG12LM', 'WXCJKG13LM'].includes(model.model)) {
|
|
5366
|
+
payload.temperature = calibrateAndPrecisionRoundOptions(value, options, 'temperature'); // 0x03
|
|
5367
|
+
}
|
|
5368
|
+
} else if (index === 5) {
|
|
5295
5369
|
if (['JT-BZ-01AQ/A', 'RTCZCGQ11LM'].includes(model.model)) payload.power_outage_count = value;
|
|
5296
5370
|
} else if (index === 100) {
|
|
5297
5371
|
if (['QBKG20LM', 'QBKG31LM', 'QBKG39LM', 'QBKG41LM', 'QBCZ15LM'].includes(model.model)) {
|
|
@@ -5323,8 +5397,9 @@ const converters = {
|
|
|
5323
5397
|
payload.presence_event = {0: 'enter', 1: 'leave', 2: 'left_enter', 3: 'right_leave', 4: 'right_enter',
|
|
5324
5398
|
5: 'left_leave', 6: 'approach', 7: 'away', 255: null}[value];
|
|
5325
5399
|
}
|
|
5326
|
-
} else if (index ===103)
|
|
5327
|
-
|
|
5400
|
+
} else if (index === 103) {
|
|
5401
|
+
if (['RTCZCGQ11LM'].includes(model.model)) payload.monitoring_mode = value === 1 ? 'left_right' : 'undirected';
|
|
5402
|
+
} else if (index === 105) {
|
|
5328
5403
|
if (['RTCGQ13LM'].includes(model.model)) {
|
|
5329
5404
|
payload.motion_sensitivity = {1: 'low', 2: 'medium', 3: 'high'}[value];
|
|
5330
5405
|
} else if (['RTCZCGQ11LM'].includes(model.model)) {
|
|
@@ -8260,79 +8335,89 @@ const converters = {
|
|
|
8260
8335
|
return {action};
|
|
8261
8336
|
},
|
|
8262
8337
|
},
|
|
8263
|
-
|
|
8338
|
+
ZMAM02_cover: {
|
|
8264
8339
|
cluster: 'manuSpecificTuya',
|
|
8265
|
-
type: ['
|
|
8340
|
+
type: ['commandDataReport', 'commandDataResponse'],
|
|
8341
|
+
options: [exposes.options.invert_cover()],
|
|
8266
8342
|
convert: (model, msg, publish, options, meta) => {
|
|
8267
|
-
const
|
|
8268
|
-
|
|
8269
|
-
|
|
8270
|
-
|
|
8271
|
-
|
|
8272
|
-
|
|
8273
|
-
|
|
8274
|
-
|
|
8275
|
-
|
|
8276
|
-
|
|
8277
|
-
|
|
8343
|
+
const dpValue = tuya.firstDpValue(msg, meta, 'ZMAM02_cover');
|
|
8344
|
+
const dp = dpValue.dp;
|
|
8345
|
+
const value = tuya.getDataValue(dpValue);
|
|
8346
|
+
switch (dp) {
|
|
8347
|
+
case tuya.dataPoints.coverPosition: // Started moving to position (triggered from Zigbee)
|
|
8348
|
+
case tuya.dataPoints.coverArrived: { // Arrived at position
|
|
8349
|
+
const running = dp === tuya.dataPoints.coverArrived ? false : true;
|
|
8350
|
+
const invert = tuya.isCoverInverted(meta.device.manufacturerName) ? !options.invert_cover : options.invert_cover;
|
|
8351
|
+
const position = invert ? 100 - (value & 0xFF) : (value & 0xFF);
|
|
8352
|
+
if (position > 0 && position <= 100) {
|
|
8353
|
+
return {running, position, state: 'OPEN'};
|
|
8354
|
+
} else if (position == 0) { // Report fully closed
|
|
8355
|
+
return {running, position, state: 'CLOSE'};
|
|
8356
|
+
} else {
|
|
8357
|
+
return {running}; // Not calibrated yet, no position is available
|
|
8358
|
+
}
|
|
8359
|
+
}
|
|
8360
|
+
case tuya.dataPoints.coverSpeed: // Cover is reporting its current speed setting
|
|
8361
|
+
return {motor_speed: value};
|
|
8362
|
+
case tuya.dataPoints.state: // Ignore the cover state, it's not reliable between different covers!
|
|
8363
|
+
case tuya.dataPoints.coverChange: // Ignore manual cover change, it's not reliable between different covers!
|
|
8364
|
+
break;
|
|
8365
|
+
case tuya.dataPoints.config: // Returned by configuration set; ignore
|
|
8366
|
+
break;
|
|
8367
|
+
case tuya.dataPoints.AM02MotorWorkingMode:
|
|
8368
|
+
switch (value) {
|
|
8369
|
+
case 0: // continuous 1
|
|
8370
|
+
return {motor_working_mode: 'continuous'};
|
|
8371
|
+
case 1: // intermittently
|
|
8372
|
+
return {motor_working_mode: 'intermittently'};
|
|
8373
|
+
default:
|
|
8374
|
+
meta.logger.warn('ZMAM02: ' +
|
|
8375
|
+
`Mode ${value} is not recognized.`);
|
|
8278
8376
|
break;
|
|
8279
|
-
|
|
8280
|
-
|
|
8377
|
+
}
|
|
8378
|
+
break;
|
|
8379
|
+
case tuya.dataPoints.AM02Border:
|
|
8380
|
+
switch (value) {
|
|
8381
|
+
case 0: // up
|
|
8382
|
+
return {border: 'up'};
|
|
8383
|
+
case 1: // down
|
|
8384
|
+
return {border: 'down'};
|
|
8385
|
+
case 2: // down_delete
|
|
8386
|
+
return {border: 'down_delete'};
|
|
8387
|
+
default:
|
|
8388
|
+
meta.logger.warn('zigbee-herdsman-converters:ZM_AM_02: ' +
|
|
8389
|
+
`Mode ${value} is not recognized.`);
|
|
8281
8390
|
break;
|
|
8282
|
-
|
|
8283
|
-
|
|
8284
|
-
|
|
8285
|
-
|
|
8286
|
-
|
|
8287
|
-
|
|
8288
|
-
|
|
8289
|
-
|
|
8290
|
-
|
|
8291
|
-
|
|
8292
|
-
|
|
8293
|
-
break;
|
|
8294
|
-
}
|
|
8391
|
+
}
|
|
8392
|
+
break;
|
|
8393
|
+
case tuya.dataPoints.AM02Direction:
|
|
8394
|
+
switch (value) {
|
|
8395
|
+
case 0:
|
|
8396
|
+
return {motor_direction: 'forward'};
|
|
8397
|
+
case 1:
|
|
8398
|
+
return {motor_direction: 'back'};
|
|
8399
|
+
default:
|
|
8400
|
+
meta.logger.warn('ZMAM02: ' +
|
|
8401
|
+
`Mode ${value} is not recognized.`);
|
|
8295
8402
|
break;
|
|
8296
|
-
|
|
8297
|
-
|
|
8298
|
-
|
|
8299
|
-
|
|
8300
|
-
|
|
8301
|
-
|
|
8302
|
-
|
|
8303
|
-
|
|
8304
|
-
|
|
8305
|
-
|
|
8306
|
-
break;
|
|
8307
|
-
default:
|
|
8308
|
-
meta.logger.warn('zigbee-herdsman-converters:ZM_AM_02: ' +
|
|
8403
|
+
}
|
|
8404
|
+
break;
|
|
8405
|
+
case tuya.dataPoints.AM02Mode:
|
|
8406
|
+
switch (value) {
|
|
8407
|
+
case 0: // morning
|
|
8408
|
+
return {mode: 'morning'};
|
|
8409
|
+
case 1: // night
|
|
8410
|
+
return {mode: 'night'};
|
|
8411
|
+
default:
|
|
8412
|
+
meta.logger.warn('zigbee-herdsman-converters:ZM_AM_02: ' +
|
|
8309
8413
|
`Mode ${value} is not recognized.`);
|
|
8310
|
-
break;
|
|
8311
|
-
}
|
|
8312
|
-
break;
|
|
8313
|
-
case tuya.dataPoints.AM02PercentState:
|
|
8314
|
-
result.percent_state = value;
|
|
8315
8414
|
break;
|
|
8316
|
-
case tuya.dataPoints.AM02Mode:
|
|
8317
|
-
switch (value) {
|
|
8318
|
-
case 0: // morning
|
|
8319
|
-
result.mode = 'morning';
|
|
8320
|
-
break;
|
|
8321
|
-
case 1: // night
|
|
8322
|
-
result.mode = 'night';
|
|
8323
|
-
break;
|
|
8324
|
-
default:
|
|
8325
|
-
meta.logger.warn('zigbee-herdsman-converters:ZM_AM_02: ' +
|
|
8326
|
-
`Mode ${value} is not recognized.`);
|
|
8327
|
-
break;
|
|
8328
|
-
}
|
|
8329
|
-
break;
|
|
8330
|
-
default:
|
|
8331
|
-
meta.logger.warn(`fromZigbee.Zemismart Shader Konverter (Zm_AM02): NOT RECOGNIZED ` +
|
|
8332
|
-
`DP #${dpValue.dp} with data ${JSON.stringify(dpValue)}`);
|
|
8333
8415
|
}
|
|
8416
|
+
break;
|
|
8417
|
+
default: // Unknown code
|
|
8418
|
+
meta.logger.warn(`ZMAM02_cover: Unhandled DP #${dp} for ${meta.device.manufacturerName}:
|
|
8419
|
+
${JSON.stringify(dpValue)}`);
|
|
8334
8420
|
}
|
|
8335
|
-
return result;
|
|
8336
8421
|
},
|
|
8337
8422
|
},
|
|
8338
8423
|
// #endregion
|
package/converters/toZigbee.js
CHANGED
|
@@ -2211,7 +2211,7 @@ const converters = {
|
|
|
2211
2211
|
key: ['led_disabled_night'],
|
|
2212
2212
|
convertSet: async (entity, key, value, meta) => {
|
|
2213
2213
|
if (['ZNCZ04LM', 'ZNCZ15LM', 'QBCZ14LM', 'QBCZ15LM', 'QBKG19LM', 'QBKG20LM', 'QBKG25LM', 'QBKG26LM',
|
|
2214
|
-
'QBKG31LM', 'QBKG34LM', 'DLKZMK11LM'].includes(meta.mapped.model)) {
|
|
2214
|
+
'QBKG31LM', 'QBKG34LM', 'DLKZMK11LM', 'SSM-U01'].includes(meta.mapped.model)) {
|
|
2215
2215
|
await entity.write('aqaraOpple', {0x0203: {value: value ? 1 : 0, type: 0x10}}, manufacturerOptions.xiaomi);
|
|
2216
2216
|
} else if (['ZNCZ11LM'].includes(meta.mapped.model)) {
|
|
2217
2217
|
const payload = value ?
|
|
@@ -2226,7 +2226,7 @@ const converters = {
|
|
|
2226
2226
|
},
|
|
2227
2227
|
convertGet: async (entity, key, meta) => {
|
|
2228
2228
|
if (['ZNCZ04LM', 'ZNCZ15LM', 'QBCZ15LM', 'QBCZ14LM', 'QBKG19LM', 'QBKG20LM', 'QBKG25LM', 'QBKG26LM',
|
|
2229
|
-
'QBKG31LM', 'QBKG34LM', 'DLKZMK11LM'].includes(meta.mapped.model)) {
|
|
2229
|
+
'QBKG31LM', 'QBKG34LM', 'DLKZMK11LM', 'SSM-U01'].includes(meta.mapped.model)) {
|
|
2230
2230
|
await entity.read('aqaraOpple', [0x0203], manufacturerOptions.xiaomi);
|
|
2231
2231
|
} else {
|
|
2232
2232
|
throw new Error('Not supported');
|
|
@@ -3206,6 +3206,30 @@ const converters = {
|
|
|
3206
3206
|
await entity.read('genOnOff', ['moesStartUpOnOff']);
|
|
3207
3207
|
},
|
|
3208
3208
|
},
|
|
3209
|
+
moes_switch: {
|
|
3210
|
+
key: ['power_on_behavior', 'indicate_light'],
|
|
3211
|
+
convertSet: async (entity, key, value, meta) => {
|
|
3212
|
+
switch (key) {
|
|
3213
|
+
case 'power_on_behavior':
|
|
3214
|
+
await tuya.sendDataPointEnum(
|
|
3215
|
+
entity,
|
|
3216
|
+
tuya.dataPoints.moesSwitchPowerOnBehavior,
|
|
3217
|
+
utils.getKey(tuya.moesSwitch.powerOnBehavior, value),
|
|
3218
|
+
);
|
|
3219
|
+
break;
|
|
3220
|
+
case 'indicate_light':
|
|
3221
|
+
await tuya.sendDataPointEnum(
|
|
3222
|
+
entity,
|
|
3223
|
+
tuya.dataPoints.moesSwitchIndicateLight,
|
|
3224
|
+
utils.getKey(tuya.moesSwitch.indicateLight, value),
|
|
3225
|
+
);
|
|
3226
|
+
break;
|
|
3227
|
+
default:
|
|
3228
|
+
meta.logger.warn(`toZigbee.moes_switch: Unhandled Key ${key}`);
|
|
3229
|
+
break;
|
|
3230
|
+
}
|
|
3231
|
+
},
|
|
3232
|
+
},
|
|
3209
3233
|
moes_thermostat_sensor: {
|
|
3210
3234
|
key: ['sensor'],
|
|
3211
3235
|
convertSet: async (entity, key, value, meta) => {
|
|
@@ -4499,6 +4523,10 @@ const converters = {
|
|
|
4499
4523
|
const enableLedIfOn = value === 'ON' || (value === 'OFF' ? false : !!value);
|
|
4500
4524
|
const payload = {1: {value: enableLedIfOn, type: 16}};
|
|
4501
4525
|
await entity.write('manuSpecificLegrandDevices', payload, manufacturerOptions.legrand);
|
|
4526
|
+
return {state: {'permanent_led': value}};
|
|
4527
|
+
},
|
|
4528
|
+
convertGet: async (entity, key, meta) => {
|
|
4529
|
+
await entity.read('manuSpecificLegrandDevices', [0x0001], manufacturerOptions.legrand);
|
|
4502
4530
|
},
|
|
4503
4531
|
},
|
|
4504
4532
|
legrand_settingEnableLedIfOn: {
|
|
@@ -4510,15 +4538,10 @@ const converters = {
|
|
|
4510
4538
|
const enableLedIfOn = value === 'ON' || (value === 'OFF' ? false : !!value);
|
|
4511
4539
|
const payload = {2: {value: enableLedIfOn, type: 16}};
|
|
4512
4540
|
await entity.write('manuSpecificLegrandDevices', payload, manufacturerOptions.legrand);
|
|
4541
|
+
return {state: {'led_when_on': value}};
|
|
4513
4542
|
},
|
|
4514
|
-
|
|
4515
|
-
|
|
4516
|
-
key: ['dimmer_enabled'],
|
|
4517
|
-
convertSet: async (entity, key, value, meta) => {
|
|
4518
|
-
// enable the dimmer, requires a recent firmware on the device
|
|
4519
|
-
const enableDimmer = value === 'ON' || (value === 'OFF' ? false : !!value);
|
|
4520
|
-
const payload = {0: {value: enableDimmer ? 0x0101 : 0x0100, type: 9}};
|
|
4521
|
-
await entity.write('manuSpecificLegrandDevices', payload, manufacturerOptions.legrand);
|
|
4543
|
+
convertGet: async (entity, key, meta) => {
|
|
4544
|
+
await entity.read('manuSpecificLegrandDevices', [0x0002], manufacturerOptions.legrand);
|
|
4522
4545
|
},
|
|
4523
4546
|
},
|
|
4524
4547
|
legrand_deviceMode: {
|
|
@@ -5971,6 +5994,32 @@ const converters = {
|
|
|
5971
5994
|
await entity.read('genOnOff', ['tuyaBacklightMode']);
|
|
5972
5995
|
},
|
|
5973
5996
|
},
|
|
5997
|
+
ts011f_plug_indicator_mode: {
|
|
5998
|
+
key: ['indicator_mode'],
|
|
5999
|
+
convertSet: async (entity, key, value, meta) => {
|
|
6000
|
+
if (typeof value === 'string') {
|
|
6001
|
+
value = value.toLowerCase();
|
|
6002
|
+
const lookup = {'off': 0, 'off/on': 1, 'on/off': 2, 'on': 3};
|
|
6003
|
+
utils.validateValue(value, Object.keys(lookup));
|
|
6004
|
+
value = lookup[value];
|
|
6005
|
+
}
|
|
6006
|
+
|
|
6007
|
+
if (typeof value === 'number' && value >= 0 && value <= 3) {
|
|
6008
|
+
await entity.write('genOnOff', {tuyaBacklightMode: value});
|
|
6009
|
+
} else {
|
|
6010
|
+
meta.logger.warn(`toZigbee.ts011f_plug_indicator_mode: Unsupported value ${value}`);
|
|
6011
|
+
}
|
|
6012
|
+
},
|
|
6013
|
+
convertGet: async (entity, key, meta) => {
|
|
6014
|
+
await entity.read('genOnOff', ['tuyaBacklightMode']);
|
|
6015
|
+
},
|
|
6016
|
+
},
|
|
6017
|
+
ts011f_plug_child_mode: {
|
|
6018
|
+
key: ['child_lock'],
|
|
6019
|
+
convertSet: async (entity, key, value, meta) => {
|
|
6020
|
+
await entity.write('genOnOff', {0x8000: {value: value === 'LOCK', type: 0x10}});
|
|
6021
|
+
},
|
|
6022
|
+
},
|
|
5974
6023
|
hy_thermostat: {
|
|
5975
6024
|
key: [
|
|
5976
6025
|
'child_lock', 'current_heating_setpoint', 'local_temperature_calibration',
|
|
@@ -6875,30 +6924,56 @@ const converters = {
|
|
|
6875
6924
|
}
|
|
6876
6925
|
},
|
|
6877
6926
|
},
|
|
6878
|
-
|
|
6879
|
-
key: ['
|
|
6927
|
+
ZMAM02_cover: {
|
|
6928
|
+
key: ['state', 'position', 'mode', 'motor_direction', 'border', 'motor_working_mode'],
|
|
6929
|
+
options: [exposes.options.invert_cover()],
|
|
6880
6930
|
convertSet: async (entity, key, value, meta) => {
|
|
6931
|
+
if (key === 'position') {
|
|
6932
|
+
if (value >= 0 && value <= 100) {
|
|
6933
|
+
const invert = tuya.isCoverInverted(meta.device.manufacturerName) ?
|
|
6934
|
+
!meta.options.invert_cover : meta.options.invert_cover;
|
|
6935
|
+
|
|
6936
|
+
value = invert ? 100 - value : value;
|
|
6937
|
+
await tuya.sendDataPointValue(entity, tuya.dataPoints.coverPosition, value);
|
|
6938
|
+
} else {
|
|
6939
|
+
throw new Error('TuYa_cover_control: Curtain motor position is out of range');
|
|
6940
|
+
}
|
|
6941
|
+
} else if (key === 'state') {
|
|
6942
|
+
const stateEnums = tuya.getCoverStateEnums(meta.device.manufacturerName);
|
|
6943
|
+
meta.logger.debug(`ZMAM02: Using state enums for ${meta.device.manufacturerName}:
|
|
6944
|
+
${JSON.stringify(stateEnums)}`);
|
|
6945
|
+
value = value.toLowerCase();
|
|
6946
|
+
switch (value) {
|
|
6947
|
+
case 'close':
|
|
6948
|
+
await tuya.sendDataPointEnum(entity, tuya.dataPoints.AM02Control, stateEnums.close);
|
|
6949
|
+
break;
|
|
6950
|
+
case 'open':
|
|
6951
|
+
await tuya.sendDataPointEnum(entity, tuya.dataPoints.AM02Control, stateEnums.open);
|
|
6952
|
+
break;
|
|
6953
|
+
case 'stop':
|
|
6954
|
+
await tuya.sendDataPointEnum(entity, tuya.dataPoints.AM02Control, stateEnums.stop);
|
|
6955
|
+
break;
|
|
6956
|
+
default:
|
|
6957
|
+
throw new Error('ZMAM02: Invalid command received');
|
|
6958
|
+
}
|
|
6959
|
+
}
|
|
6881
6960
|
switch (key) {
|
|
6882
|
-
case 'control':
|
|
6883
|
-
await tuya.sendDataPointEnum(entity, tuya.dataPoints.AM02Control, utils.getKey(tuya.ZMAM02.AM02Control, value));
|
|
6884
|
-
break;
|
|
6885
|
-
case 'percent_control':
|
|
6886
|
-
await tuya.sendDataPointValue(entity, tuya.dataPoints.AM02PercentState, value);
|
|
6887
|
-
break;
|
|
6888
6961
|
case 'mode':
|
|
6889
|
-
await tuya.sendDataPointEnum(entity, tuya.dataPoints.AM02Mode, utils.getKey(tuya.
|
|
6962
|
+
await tuya.sendDataPointEnum(entity, tuya.dataPoints.AM02Mode, utils.getKey(tuya.ZMLookups.AM02Mode, value));
|
|
6890
6963
|
break;
|
|
6891
|
-
case '
|
|
6892
|
-
await tuya.sendDataPointEnum(entity, tuya.dataPoints.
|
|
6964
|
+
case 'motor_direction':
|
|
6965
|
+
await tuya.sendDataPointEnum(entity, tuya.dataPoints.AM02Direction, utils.getKey(tuya.ZMLookups.AM02Direction, value));
|
|
6893
6966
|
break;
|
|
6894
6967
|
case 'border':
|
|
6895
|
-
await tuya.sendDataPointEnum(entity, tuya.dataPoints.AM02Border, utils.getKey(tuya.
|
|
6968
|
+
await tuya.sendDataPointEnum(entity, tuya.dataPoints.AM02Border, utils.getKey(tuya.ZMLookups.AM02Border, value));
|
|
6896
6969
|
break;
|
|
6897
6970
|
case 'motor_working_mode':
|
|
6898
|
-
await tuya.sendDataPointEnum(
|
|
6971
|
+
await tuya.sendDataPointEnum(
|
|
6972
|
+
entity,
|
|
6973
|
+
tuya.dataPoints.AM02MotorWorkingMode,
|
|
6974
|
+
utils.getKey(tuya.ZMLookups.AM02MotorWorkingMode,
|
|
6975
|
+
value));
|
|
6899
6976
|
break;
|
|
6900
|
-
default: // Unknown Key
|
|
6901
|
-
meta.logger.warn(`toZigbee.ZMAM02: Unhandled Key ${key}`);
|
|
6902
6977
|
}
|
|
6903
6978
|
},
|
|
6904
6979
|
},
|
package/devices/bticino.js
CHANGED
|
@@ -12,12 +12,12 @@ module.exports = [
|
|
|
12
12
|
model: 'K4003C/L4003C/N4003C/NT4003C',
|
|
13
13
|
vendor: 'BTicino',
|
|
14
14
|
description: 'Light switch with neutral',
|
|
15
|
-
fromZigbee: [fz.identify, fz.on_off, fz.K4003C_binary_input],
|
|
15
|
+
fromZigbee: [fz.identify, fz.on_off, fz.K4003C_binary_input, fz.legrand_cluster_fc01],
|
|
16
16
|
toZigbee: [tz.on_off, tz.legrand_settingAlwaysEnableLed, tz.legrand_settingEnableLedIfOn, tz.legrand_identify],
|
|
17
17
|
exposes: [
|
|
18
18
|
e.switch(), e.action(['identify', 'on', 'off']),
|
|
19
|
-
exposes.binary('permanent_led', ea.
|
|
20
|
-
exposes.binary('led_when_on', ea.
|
|
19
|
+
exposes.binary('permanent_led', ea.ALL, 'ON', 'OFF').withDescription('Enable or disable the permanent blue LED'),
|
|
20
|
+
exposes.binary('led_when_on', ea.ALL, 'ON', 'OFF').withDescription('Enables the LED when the light is on'),
|
|
21
21
|
],
|
|
22
22
|
configure: async (device, coordinatorEndpoint, logger) => {
|
|
23
23
|
const endpoint = device.getEndpoint(1);
|
|
@@ -30,17 +30,17 @@ module.exports = [
|
|
|
30
30
|
vendor: 'BTicino',
|
|
31
31
|
description: 'Dimmer switch with neutral',
|
|
32
32
|
extend: extend.light_onoff_brightness({noConfigure: true}),
|
|
33
|
-
fromZigbee: [fz.brightness, fz.identify, fz.on_off, fz.lighting_ballast_configuration],
|
|
33
|
+
fromZigbee: [fz.brightness, fz.identify, fz.on_off, fz.lighting_ballast_configuration, fz.legrand_cluster_fc01],
|
|
34
34
|
toZigbee: [tz.light_onoff_brightness, tz.legrand_settingAlwaysEnableLed, tz.legrand_settingEnableLedIfOn,
|
|
35
|
-
tz.
|
|
35
|
+
tz.legrand_deviceMode, tz.legrand_identify, tz.ballast_config],
|
|
36
36
|
exposes: [e.light_brightness(),
|
|
37
37
|
exposes.numeric('ballast_minimum_level', ea.ALL).withValueMin(1).withValueMax(254)
|
|
38
38
|
.withDescription('Specifies the minimum brightness value'),
|
|
39
39
|
exposes.numeric('ballast_maximum_level', ea.ALL).withValueMin(1).withValueMax(254)
|
|
40
40
|
.withDescription('Specifies the maximum brightness value'),
|
|
41
|
-
exposes.binary('
|
|
42
|
-
exposes.binary('permanent_led', ea.
|
|
43
|
-
exposes.binary('led_when_on', ea.
|
|
41
|
+
exposes.binary('device_mode', ea.ALL, 'dimmer_on', 'dimmer_off').withDescription('Allow the device to change brightness'),
|
|
42
|
+
exposes.binary('permanent_led', ea.ALL, 'ON', 'OFF').withDescription('Enable or disable the permanent blue LED'),
|
|
43
|
+
exposes.binary('led_when_on', ea.ALL, 'ON', 'OFF').withDescription('Enables the LED when the light is on')],
|
|
44
44
|
configure: async (device, coordinatorEndpoint, logger) => {
|
|
45
45
|
await extend.light_onoff_brightness().configure(device, coordinatorEndpoint, logger);
|
|
46
46
|
const endpoint = device.getEndpoint(1);
|
|
@@ -76,10 +76,10 @@ module.exports = [
|
|
|
76
76
|
description: 'DIN power consumption module (same as Legrand 412015)',
|
|
77
77
|
vendor: 'BTicino',
|
|
78
78
|
extend: extend.switch(),
|
|
79
|
-
fromZigbee: [fz.identify, fz.on_off, fz.electrical_measurement, fz.
|
|
79
|
+
fromZigbee: [fz.identify, fz.on_off, fz.electrical_measurement, fz.legrand_cluster_fc01, fz.ignore_basic_report, fz.ignore_genOta],
|
|
80
80
|
toZigbee: [tz.legrand_deviceMode, tz.on_off, tz.legrand_identify, tz.electrical_measurement_power],
|
|
81
81
|
exposes: [exposes.switch().withState('state', true, 'On/off (works only if device is in "switch" mode)'),
|
|
82
|
-
e.power().withAccess(ea.STATE_GET), exposes.enum(
|
|
82
|
+
e.power().withAccess(ea.STATE_GET), exposes.enum('device_mode', ea.ALL, ['switch', 'auto'])
|
|
83
83
|
.withDescription('switch: allow on/off, auto will use wired action via C1/C2 on contactor for example with HC/HP')],
|
|
84
84
|
configure: async (device, coordinatorEndpoint, logger) => {
|
|
85
85
|
const endpoint = device.getEndpoint(1);
|
|
@@ -375,4 +375,44 @@ module.exports = [
|
|
|
375
375
|
},
|
|
376
376
|
exposes: [e.battery(), e.illuminance(), e.temperature(), e.humidity(), e.pressure()],
|
|
377
377
|
},
|
|
378
|
+
{
|
|
379
|
+
zigbeeModel: ['EFEKTA_eFlower_Pro'],
|
|
380
|
+
model: 'EFEKTA_eFlower_Pro',
|
|
381
|
+
vendor: 'Custom devices (DiY)',
|
|
382
|
+
description: '[Plant Wattering Sensor with e-ink display 2.13](https://efektalab.com/eFlowerPro)',
|
|
383
|
+
fromZigbee: [fz.temperature, fz.humidity, fz.illuminance, fz.soil_moisture, fz.battery],
|
|
384
|
+
toZigbee: [tz.factory_reset],
|
|
385
|
+
configure: async (device, coordinatorEndpoint, logger) => {
|
|
386
|
+
const firstEndpoint = device.getEndpoint(1);
|
|
387
|
+
await reporting.bind(firstEndpoint, coordinatorEndpoint, [
|
|
388
|
+
'genPowerCfg', 'msTemperatureMeasurement', 'msRelativeHumidity', 'msIlluminanceMeasurement', 'msSoilMoisture']);
|
|
389
|
+
const overides = {min: 0, max: 21600, change: 0};
|
|
390
|
+
await reporting.batteryVoltage(firstEndpoint, overides);
|
|
391
|
+
await reporting.batteryPercentageRemaining(firstEndpoint, overides);
|
|
392
|
+
await reporting.temperature(firstEndpoint, overides);
|
|
393
|
+
await reporting.humidity(firstEndpoint, overides);
|
|
394
|
+
await reporting.illuminance(firstEndpoint, overides);
|
|
395
|
+
await reporting.soil_moisture(firstEndpoint, overides);
|
|
396
|
+
},
|
|
397
|
+
exposes: [e.soil_moisture(), e.battery(), e.illuminance(), e.temperature(), e.humidity()],
|
|
398
|
+
},
|
|
399
|
+
{
|
|
400
|
+
zigbeeModel: ['EFEKTA_eTH102'],
|
|
401
|
+
model: 'EFEKTA_eTH102',
|
|
402
|
+
vendor: 'Custom devices (DiY)',
|
|
403
|
+
description: '[Mini digital thermometer & hygrometer with e-ink1.02](http://efektalab.com/eTH102)',
|
|
404
|
+
fromZigbee: [fz.temperature, fz.humidity, fz.battery],
|
|
405
|
+
toZigbee: [tz.factory_reset],
|
|
406
|
+
configure: async (device, coordinatorEndpoint, logger) => {
|
|
407
|
+
const endpoint = device.getEndpoint(1);
|
|
408
|
+
await reporting.bind(endpoint, coordinatorEndpoint, [
|
|
409
|
+
'genPowerCfg', 'msTemperatureMeasurement', 'msRelativeHumidity']);
|
|
410
|
+
const overides = {min: 0, max: 21600, change: 0};
|
|
411
|
+
await reporting.batteryVoltage(endpoint, overides);
|
|
412
|
+
await reporting.batteryPercentageRemaining(endpoint, overides);
|
|
413
|
+
await reporting.temperature(endpoint, overides);
|
|
414
|
+
await reporting.humidity(endpoint, overides);
|
|
415
|
+
},
|
|
416
|
+
exposes: [e.battery(), e.temperature(), e.humidity()],
|
|
417
|
+
},
|
|
378
418
|
];
|
package/devices/gledopto.js
CHANGED
|
@@ -62,10 +62,14 @@ const gledoptoExtend = {
|
|
|
62
62
|
}),
|
|
63
63
|
};
|
|
64
64
|
|
|
65
|
-
const configureReadModelID = async (device) => {
|
|
65
|
+
const configureReadModelID = async (device, coordinatorEndpoint, logger) => {
|
|
66
66
|
// https://github.com/Koenkk/zigbee-herdsman-converters/issues/3016#issuecomment-1027726604
|
|
67
67
|
const endpoint = device.endpoints[0];
|
|
68
|
-
|
|
68
|
+
const oldModel = device.modelID;
|
|
69
|
+
const newModel = (await endpoint.read('genBasic', ['modelId'])).modelId;
|
|
70
|
+
if (oldModel != newModel) {
|
|
71
|
+
logger.info(`Detected Gledopto device mode change, from '${oldModel}' to '${newModel}'`);
|
|
72
|
+
}
|
|
69
73
|
};
|
|
70
74
|
|
|
71
75
|
module.exports = [
|
|
@@ -125,7 +129,7 @@ module.exports = [
|
|
|
125
129
|
extend: gledoptoExtend.light_onoff_brightness_colortemp({noConfigure: true}),
|
|
126
130
|
configure: async (device, coordinatorEndpoint, logger) => {
|
|
127
131
|
await extend.light_onoff_brightness_colortemp().configure(device, coordinatorEndpoint, logger);
|
|
128
|
-
await configureReadModelID(device);
|
|
132
|
+
await configureReadModelID(device, coordinatorEndpoint, logger);
|
|
129
133
|
},
|
|
130
134
|
},
|
|
131
135
|
{
|
|
@@ -192,7 +196,7 @@ module.exports = [
|
|
|
192
196
|
extend: gledoptoExtend.light_onoff_brightness_colortemp_color({noConfigure: true}),
|
|
193
197
|
configure: async (device, coordinatorEndpoint, logger) => {
|
|
194
198
|
await extend.light_onoff_brightness_colortemp_color().configure(device, coordinatorEndpoint, logger);
|
|
195
|
-
await configureReadModelID(device);
|
|
199
|
+
await configureReadModelID(device, coordinatorEndpoint, logger);
|
|
196
200
|
},
|
|
197
201
|
},
|
|
198
202
|
{
|
|
@@ -254,7 +258,7 @@ module.exports = [
|
|
|
254
258
|
extend: gledoptoExtend.light_onoff_brightness_color({noConfigure: true}),
|
|
255
259
|
configure: async (device, coordinatorEndpoint, logger) => {
|
|
256
260
|
await extend.light_onoff_brightness_color().configure(device, coordinatorEndpoint, logger);
|
|
257
|
-
await configureReadModelID(device);
|
|
261
|
+
await configureReadModelID(device, coordinatorEndpoint, logger);
|
|
258
262
|
},
|
|
259
263
|
},
|
|
260
264
|
{
|
|
@@ -267,7 +271,7 @@ module.exports = [
|
|
|
267
271
|
meta: {disableDefaultResponse: true},
|
|
268
272
|
configure: async (device, coordinatorEndpoint, logger) => {
|
|
269
273
|
await extend.light_onoff_brightness_colortemp_color().configure(device, coordinatorEndpoint, logger);
|
|
270
|
-
await configureReadModelID(device);
|
|
274
|
+
await configureReadModelID(device, coordinatorEndpoint, logger);
|
|
271
275
|
},
|
|
272
276
|
},
|
|
273
277
|
{
|
|
@@ -292,7 +296,7 @@ module.exports = [
|
|
|
292
296
|
extend: gledoptoExtend.light_onoff_brightness({noConfigure: true}),
|
|
293
297
|
configure: async (device, coordinatorEndpoint, logger) => {
|
|
294
298
|
await extend.light_onoff_brightness().configure(device, coordinatorEndpoint, logger);
|
|
295
|
-
await configureReadModelID(device);
|
|
299
|
+
await configureReadModelID(device, coordinatorEndpoint, logger);
|
|
296
300
|
},
|
|
297
301
|
},
|
|
298
302
|
{
|