zigbee-herdsman-converters 14.0.313 → 14.0.317
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 +204 -28
- package/converters/toZigbee.js +108 -7
- package/devices/aurora_lighting.js +29 -22
- package/devices/bitron.js +1 -1
- package/devices/candeo.js +6 -1
- package/devices/centralite.js +4 -2
- package/devices/develco.js +16 -3
- package/devices/ecozy.js +2 -1
- package/devices/elko.js +1 -1
- package/devices/eurotronic.js +2 -1
- package/devices/hgkg.js +1 -1
- package/devices/hive.js +82 -0
- package/devices/ikea.js +10 -4
- package/devices/javis.js +21 -1
- package/devices/leedarson.js +1 -1
- package/devices/leviton.js +2 -1
- package/devices/lidl.js +30 -1
- package/devices/livolo.js +29 -0
- package/devices/miboxer.js +7 -0
- package/devices/moes.js +4 -3
- package/devices/philips.js +73 -1
- package/devices/saswell.js +2 -1
- package/devices/schneider_electric.js +1 -1
- package/devices/tuya.js +98 -8
- package/devices/xiaomi.js +1 -1
- package/devices/zen.js +2 -2
- package/lib/constants.js +12 -0
- package/lib/exposes.js +3 -2
- package/lib/utils.js +1 -1
- package/npm-shrinkwrap.json +1 -1
- package/package.json +1 -1
package/converters/fromZigbee.js
CHANGED
|
@@ -648,6 +648,26 @@ const converters = {
|
|
|
648
648
|
return payload;
|
|
649
649
|
},
|
|
650
650
|
},
|
|
651
|
+
develco_metering: {
|
|
652
|
+
cluster: 'seMetering',
|
|
653
|
+
type: ['attributeReport', 'readResponse'],
|
|
654
|
+
convert: (model, msg, publish, options, meta) => {
|
|
655
|
+
const result = {};
|
|
656
|
+
if (msg.data.hasOwnProperty('develcoPulseConfiguration')) {
|
|
657
|
+
result[postfixWithEndpointName('pulse_configuration', msg, model)] =
|
|
658
|
+
msg.data['develcoPulseConfiguration'];
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
if (msg.data.hasOwnProperty('develcoInterfaceMode')) {
|
|
662
|
+
result[postfixWithEndpointName('interface_mode', msg, model)] =
|
|
663
|
+
constants.develcoInterfaceMode.hasOwnProperty(msg.data['develcoInterfaceMode']) ?
|
|
664
|
+
constants.develcoInterfaceMode[msg.data['develcoInterfaceMode']] :
|
|
665
|
+
msg.data['develcoInterfaceMode'];
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
return result;
|
|
669
|
+
},
|
|
670
|
+
},
|
|
651
671
|
electrical_measurement: {
|
|
652
672
|
/**
|
|
653
673
|
* When using this converter also add the following to the configure method of the device:
|
|
@@ -2485,6 +2505,22 @@ const converters = {
|
|
|
2485
2505
|
}
|
|
2486
2506
|
},
|
|
2487
2507
|
},
|
|
2508
|
+
livolo_curtain_switch_state: {
|
|
2509
|
+
cluster: 'genPowerCfg',
|
|
2510
|
+
type: ['raw'],
|
|
2511
|
+
convert: (model, msg, publish, options, meta) => {
|
|
2512
|
+
const stateHeader = Buffer.from([122, 209]);
|
|
2513
|
+
if (msg.data.indexOf(stateHeader) === 0) {
|
|
2514
|
+
if (msg.data[10] === 5) {
|
|
2515
|
+
const status = msg.data[14];
|
|
2516
|
+
return {
|
|
2517
|
+
state_left: status === 1 ? 'ON' : 'OFF',
|
|
2518
|
+
state_right: status === 0 ? 'ON' : 'OFF',
|
|
2519
|
+
};
|
|
2520
|
+
}
|
|
2521
|
+
}
|
|
2522
|
+
},
|
|
2523
|
+
},
|
|
2488
2524
|
livolo_dimmer_state: {
|
|
2489
2525
|
cluster: 'genPowerCfg',
|
|
2490
2526
|
type: ['raw'],
|
|
@@ -2592,16 +2628,24 @@ const converters = {
|
|
|
2592
2628
|
[124,210,21,216,128, 199,147,3,24,0,75,18,0, 19,7,0] after interview
|
|
2593
2629
|
[122,209, 199,147,3,24,0,75,18,0, 7,1,6,1,0,11] off
|
|
2594
2630
|
[122,209, 199,147,3,24,0,75,18,0, 7,1,6,1,1,11] on
|
|
2631
|
+
|
|
2595
2632
|
new switch
|
|
2596
2633
|
[124,210,21,216,128, 228,41,3,24,0,75,18,0, 19,1,0] after interview
|
|
2597
2634
|
[122,209, 228,41,3,24,0,75,18,0, 7,1,0,1,0,11] off
|
|
2598
2635
|
[122,209, 228,41,3,24,0,75,18,0, 7,1,0,1,1,11] on
|
|
2636
|
+
|
|
2599
2637
|
old switch
|
|
2600
2638
|
[124,210,21,216,128, 170, 10,2,24,0,75,18,0, 17,0,1] after interview
|
|
2601
2639
|
[124,210,21,216,0, 18, 15,5,24,0,75,18,0, 34,0,0] left: 0, right: 0
|
|
2602
2640
|
[124,210,21,216,0, 18, 15,5,24,0,75,18,0, 34,0,1] left: 1, right: 0
|
|
2603
2641
|
[124,210,21,216,0, 18, 15,5,24,0,75,18,0, 34,0,2] left: 0, right: 1
|
|
2604
2642
|
[124,210,21,216,0, 18, 15,5,24,0,75,18,0, 34,0,3] left: 1, right: 1
|
|
2643
|
+
|
|
2644
|
+
curtain switch
|
|
2645
|
+
[124,210,21,216,128, 110,74,116,33,0,75,18,0, 19,5,0] after interview
|
|
2646
|
+
[122,209, 110,74,116,33,0,75,18,0, 5,1,5,0,2,11] left: 0, right: 0 (off)
|
|
2647
|
+
[122,209, 110,74,116,33,0,75,18,0, 5,1,5,0,1,11] left: 1, right: 0 (left on)
|
|
2648
|
+
[122,209, 110,74,116,33,0,75,18,0, 5,1,5,0,0,11] left: 0, right: 1 (right on)
|
|
2605
2649
|
*/
|
|
2606
2650
|
const malformedHeader = Buffer.from([0x7c, 0xd2, 0x15, 0xd8, 0x00]);
|
|
2607
2651
|
const infoHeader = Buffer.from([0x7c, 0xd2, 0x15, 0xd8, 0x80]);
|
|
@@ -2630,6 +2674,12 @@ const converters = {
|
|
|
2630
2674
|
meta.device.modelID = 'TI0001-switch-2gang';
|
|
2631
2675
|
meta.device.save();
|
|
2632
2676
|
}
|
|
2677
|
+
if (msg.data.includes(Buffer.from([19, 5, 0]), 13)) {
|
|
2678
|
+
if (meta.logger) meta.logger.debug('Detected Livolo Curtain Switch');
|
|
2679
|
+
// curtain switch, hack
|
|
2680
|
+
meta.device.modelID = 'TI0001-curtain-switch';
|
|
2681
|
+
meta.device.save();
|
|
2682
|
+
}
|
|
2633
2683
|
if (msg.data.includes(Buffer.from([19, 20, 0]), 13)) {
|
|
2634
2684
|
// new dimmer, hack
|
|
2635
2685
|
meta.device.modelID = 'TI0001-dimmer';
|
|
@@ -3705,7 +3755,7 @@ const converters = {
|
|
|
3705
3755
|
case tuya.dataPoints.haozeeLocalTemp:
|
|
3706
3756
|
return {local_temperature: (value / 10).toFixed(1)};
|
|
3707
3757
|
case tuya.dataPoints.haozeeBoostHeatingCountdown:
|
|
3708
|
-
// quick heating countdown
|
|
3758
|
+
// quick heating countdown - not supported by this device
|
|
3709
3759
|
return {boost_heating_countdown: value};
|
|
3710
3760
|
case tuya.dataPoints.haozeeWindowDetection:
|
|
3711
3761
|
// window check
|
|
@@ -3782,11 +3832,11 @@ const converters = {
|
|
|
3782
3832
|
// working status 0 - pause 1 -working
|
|
3783
3833
|
return {'heating': value ? 'ON' : 'OFF'};
|
|
3784
3834
|
case tuya.dataPoints.haozeeBoostHeating:
|
|
3785
|
-
// rapid heating -> boolean
|
|
3786
|
-
|
|
3835
|
+
// rapid heating -> boolean - not supported by this device
|
|
3836
|
+
return {'boost_heating': value ? 'ON' : 'OFF'};
|
|
3787
3837
|
case tuya.dataPoints.haozeeTempCalibration:
|
|
3788
3838
|
// temperature calibration
|
|
3789
|
-
|
|
3839
|
+
return {'local_temperature_calibration': ( value/10 ).toFixed(1)};
|
|
3790
3840
|
case tuya.dataPoints.haozeeValvePosition:
|
|
3791
3841
|
// valve position
|
|
3792
3842
|
return {'position': value};
|
|
@@ -4020,32 +4070,33 @@ const converters = {
|
|
|
4020
4070
|
minutes: value[2],
|
|
4021
4071
|
},
|
|
4022
4072
|
};
|
|
4023
|
-
case tuya.dataPoints.scheduleWorkday: // set schedule for workdays [6,0,20,8,0,15,11,30,15,12,30,15,17,30,20,22,0,15]
|
|
4024
|
-
|
|
4025
|
-
// Top bits in hours have special meaning
|
|
4026
|
-
// 8: ??
|
|
4027
|
-
// 7: Current schedule indicator
|
|
4028
|
-
return {workdays: [
|
|
4029
|
-
{hour: value[0] & 0x3F, minute: value[1], temperature: value[2]},
|
|
4030
|
-
{hour: value[3] & 0x3F, minute: value[4], temperature: value[5]},
|
|
4031
|
-
{hour: value[6] & 0x3F, minute: value[7], temperature: value[8]},
|
|
4032
|
-
{hour: value[9] & 0x3F, minute: value[10], temperature: value[11]},
|
|
4033
|
-
{hour: value[12] & 0x3F, minute: value[13], temperature: value[14]},
|
|
4034
|
-
{hour: value[15] & 0x3F, minute: value[16], temperature: value[17]},
|
|
4035
|
-
]};
|
|
4036
|
-
case tuya.dataPoints.scheduleHoliday: // set schedule for holidays [6,0,20,8,0,15,11,30,15,12,30,15,17,30,20,22,0,15]
|
|
4073
|
+
case tuya.dataPoints.scheduleWorkday: // set schedule for workdays/holidays [6,0,20,8,0,15,11,30,15,12,30,15,17,30,20,22,0,15]
|
|
4074
|
+
case tuya.dataPoints.scheduleHoliday: {
|
|
4037
4075
|
// 6:00 - 20*, 8:00 - 15*, 11:30 - 15*, 12:30 - 15*, 17:30 - 20*, 22:00 - 15*
|
|
4038
4076
|
// Top bits in hours have special meaning
|
|
4039
|
-
//
|
|
4040
|
-
|
|
4041
|
-
|
|
4042
|
-
|
|
4043
|
-
|
|
4044
|
-
{hour: value[
|
|
4045
|
-
|
|
4046
|
-
|
|
4047
|
-
|
|
4048
|
-
|
|
4077
|
+
// 6: Current schedule indicator
|
|
4078
|
+
const items = [];
|
|
4079
|
+
const programmingMode = [];
|
|
4080
|
+
|
|
4081
|
+
for (let i = 0; i < 6; i++) {
|
|
4082
|
+
const item = {hour: value[i*3] & 0x3F, minute: value[i*3+1], temperature: value[i*3+2]};
|
|
4083
|
+
if (value[i*3] & 0x40) {
|
|
4084
|
+
item['current'] = true;
|
|
4085
|
+
}
|
|
4086
|
+
|
|
4087
|
+
items[i] = item;
|
|
4088
|
+
programmingMode[i] =
|
|
4089
|
+
item['hour'].toString().padStart(2, '0') + ':' +
|
|
4090
|
+
item['minute'].toString().padStart(2, '0') + '/' +
|
|
4091
|
+
item['temperature'] + '°C';
|
|
4092
|
+
}
|
|
4093
|
+
|
|
4094
|
+
if (dp == tuya.dataPoints.scheduleWorkday) {
|
|
4095
|
+
return {workdays: items, workdays_schedule: programmingMode.join(' ')};
|
|
4096
|
+
} else {
|
|
4097
|
+
return {holidays: items, holidays_schedule: programmingMode.join(' ')};
|
|
4098
|
+
}
|
|
4099
|
+
}
|
|
4049
4100
|
case tuya.dataPoints.childLock:
|
|
4050
4101
|
return {child_lock: value ? 'LOCK' : 'UNLOCK'};
|
|
4051
4102
|
case tuya.dataPoints.siterwellWindowDetection:
|
|
@@ -4244,6 +4295,33 @@ const converters = {
|
|
|
4244
4295
|
}
|
|
4245
4296
|
},
|
|
4246
4297
|
},
|
|
4298
|
+
ikea_pm25: {
|
|
4299
|
+
cluster: 'manuSpecificIkeaPM25Measurement',
|
|
4300
|
+
type: ['attributeReport', 'readResponse'],
|
|
4301
|
+
options: [exposes.options.precision('pm25'), exposes.options.calibration('pm25')],
|
|
4302
|
+
convert: (model, msg, publish, options, meta) => {
|
|
4303
|
+
if (msg.data['measuredValue']) {
|
|
4304
|
+
const pm25 = parseFloat(msg.data['measuredValue']) / 100.0;
|
|
4305
|
+
const pm25Property = postfixWithEndpointName('pm25', msg, model);
|
|
4306
|
+
|
|
4307
|
+
// Air Quality Scale (ikea app):
|
|
4308
|
+
// 0-35=Good, 35-80=OK, 80+=Not Good
|
|
4309
|
+
let airQuality;
|
|
4310
|
+
const airQualityProperty = postfixWithEndpointName('air_quality', msg, model);
|
|
4311
|
+
if (pm25 <= 35) {
|
|
4312
|
+
airQuality = 'good';
|
|
4313
|
+
} else if (pm25 <= 80) {
|
|
4314
|
+
airQuality = 'ok';
|
|
4315
|
+
} else if (pm25 < 65535) {
|
|
4316
|
+
airQuality = 'not_good';
|
|
4317
|
+
} else {
|
|
4318
|
+
airQuality = 'unknown';
|
|
4319
|
+
}
|
|
4320
|
+
|
|
4321
|
+
return {[pm25Property]: calibrateAndPrecisionRoundOptions(pm25, options, 'pm25'), [airQualityProperty]: airQuality};
|
|
4322
|
+
}
|
|
4323
|
+
},
|
|
4324
|
+
},
|
|
4247
4325
|
E1524_E1810_levelctrl: {
|
|
4248
4326
|
cluster: 'genLevelCtrl',
|
|
4249
4327
|
type: [
|
|
@@ -5961,6 +6039,11 @@ const converters = {
|
|
|
5961
6039
|
};
|
|
5962
6040
|
|
|
5963
6041
|
const data = utf8FromStr(msg['data']['16896']);
|
|
6042
|
+
|
|
6043
|
+
clearTimeout(globalStore.getValue(msg.endpoint, 'timer'));
|
|
6044
|
+
const timer = setTimeout(() => publish({action: 'lock', state: 'LOCK'}), 2 * 1000);
|
|
6045
|
+
globalStore.putValue(msg.endpoint, 'timer', timer);
|
|
6046
|
+
|
|
5964
6047
|
return {
|
|
5965
6048
|
action: 'unlock',
|
|
5966
6049
|
action_user: data[3],
|
|
@@ -5969,6 +6052,77 @@ const converters = {
|
|
|
5969
6052
|
};
|
|
5970
6053
|
},
|
|
5971
6054
|
},
|
|
6055
|
+
javis_microwave_sensor: {
|
|
6056
|
+
cluster: 'manuSpecificTuya',
|
|
6057
|
+
type: ['commandSetDataResponse', 'commandGetData'],
|
|
6058
|
+
convert: (model, msg, publish, options, meta) => {
|
|
6059
|
+
const dp = msg.data.dp;
|
|
6060
|
+
const value = tuya.getDataValue(msg.data.datatype, msg.data.data);
|
|
6061
|
+
const lookup = {
|
|
6062
|
+
0: 'no_motion',
|
|
6063
|
+
1: 'big_motion',
|
|
6064
|
+
2: 'minor_motion',
|
|
6065
|
+
3: 'breathing',
|
|
6066
|
+
4: 'abnormal_state',
|
|
6067
|
+
5: 'initializing',
|
|
6068
|
+
6: 'initialization_completed',
|
|
6069
|
+
};
|
|
6070
|
+
switch (dp) {
|
|
6071
|
+
case 1:
|
|
6072
|
+
return {
|
|
6073
|
+
states: lookup[value],
|
|
6074
|
+
occupancy: (0 < value && value < 5) ? true: false,
|
|
6075
|
+
};
|
|
6076
|
+
case 2:
|
|
6077
|
+
return {
|
|
6078
|
+
sensitivity: value,
|
|
6079
|
+
};
|
|
6080
|
+
case 101:
|
|
6081
|
+
return {
|
|
6082
|
+
illuminance_lux: value,
|
|
6083
|
+
};
|
|
6084
|
+
case 102:
|
|
6085
|
+
if (meta.device.manufacturerName === '_TZE200_kagkgk0i') {
|
|
6086
|
+
return {
|
|
6087
|
+
illuminance_calibration: value,
|
|
6088
|
+
};
|
|
6089
|
+
} else {
|
|
6090
|
+
return {
|
|
6091
|
+
keep_time: value,
|
|
6092
|
+
};
|
|
6093
|
+
}
|
|
6094
|
+
case 103:
|
|
6095
|
+
return {
|
|
6096
|
+
led_enable: value == 1 ? true : false,
|
|
6097
|
+
};
|
|
6098
|
+
case 104:
|
|
6099
|
+
return {illuminance_lux: value};
|
|
6100
|
+
case 105:
|
|
6101
|
+
return {
|
|
6102
|
+
illuminance_calibration: value,
|
|
6103
|
+
};
|
|
6104
|
+
case 106:
|
|
6105
|
+
if (meta.device.manufacturerName === '_TZE200_kagkgk0i') {
|
|
6106
|
+
return {
|
|
6107
|
+
keep_time: value,
|
|
6108
|
+
};
|
|
6109
|
+
} else {
|
|
6110
|
+
break;
|
|
6111
|
+
}
|
|
6112
|
+
case 107:
|
|
6113
|
+
if (meta.device.manufacturerName === '_TZE200_kagkgk0i') {
|
|
6114
|
+
return {
|
|
6115
|
+
led_enable: value == 1 ? true : false,
|
|
6116
|
+
};
|
|
6117
|
+
} else {
|
|
6118
|
+
break;
|
|
6119
|
+
}
|
|
6120
|
+
default:
|
|
6121
|
+
meta.logger.warn(`zigbee-herdsman-converters:javis_microwave_sensor: NOT RECOGNIZED ` +
|
|
6122
|
+
`DP #${dp} with data ${JSON.stringify(msg.data)}`);
|
|
6123
|
+
}
|
|
6124
|
+
},
|
|
6125
|
+
},
|
|
5972
6126
|
diyruz_freepad_config: {
|
|
5973
6127
|
cluster: 'genOnOffSwitchCfg',
|
|
5974
6128
|
type: ['readResponse'],
|
|
@@ -6198,12 +6352,34 @@ const converters = {
|
|
|
6198
6352
|
DJT11LM_vibration: {
|
|
6199
6353
|
cluster: 'closuresDoorLock',
|
|
6200
6354
|
type: ['attributeReport', 'readResponse'],
|
|
6355
|
+
options: [exposes.options.vibration_timeout()],
|
|
6201
6356
|
convert: (model, msg, publish, options, meta) => {
|
|
6202
6357
|
const result = {};
|
|
6203
6358
|
|
|
6204
6359
|
if (msg.data['85']) {
|
|
6205
6360
|
const vibrationLookup = {1: 'vibration', 2: 'tilt', 3: 'drop'};
|
|
6206
6361
|
result.action = vibrationLookup[msg.data['85']];
|
|
6362
|
+
|
|
6363
|
+
// Device only sends a message when vibration is detected.
|
|
6364
|
+
// Therefore we need to publish a no_vibration message on our own.
|
|
6365
|
+
if (result.action === 'vibration') {
|
|
6366
|
+
result.vibration = true;
|
|
6367
|
+
|
|
6368
|
+
const timeout = options && options.hasOwnProperty('vibration_timeout') ? options.vibration_timeout : 90;
|
|
6369
|
+
|
|
6370
|
+
// Stop any existing timer cause vibration detected
|
|
6371
|
+
clearTimeout(globalStore.getValue(msg.endpoint, 'vibration_timer', null));
|
|
6372
|
+
globalStore.putValue(msg.endpoint, 'vibration_timer', null);
|
|
6373
|
+
|
|
6374
|
+
// Set new timer to publish no_vibration message
|
|
6375
|
+
if (timeout !== 0) {
|
|
6376
|
+
const timer = setTimeout(() => {
|
|
6377
|
+
publish({vibration: false});
|
|
6378
|
+
}, timeout * 1000);
|
|
6379
|
+
|
|
6380
|
+
globalStore.putValue(msg.endpoint, 'vibration_timer', timer);
|
|
6381
|
+
}
|
|
6382
|
+
}
|
|
6207
6383
|
}
|
|
6208
6384
|
|
|
6209
6385
|
if (msg.data['1283']) {
|
package/converters/toZigbee.js
CHANGED
|
@@ -15,6 +15,7 @@ const manufacturerOptions = {
|
|
|
15
15
|
osram: {manufacturerCode: herdsman.Zcl.ManufacturerCode.OSRAM},
|
|
16
16
|
eurotronic: {manufacturerCode: herdsman.Zcl.ManufacturerCode.JENNIC},
|
|
17
17
|
danfoss: {manufacturerCode: herdsman.Zcl.ManufacturerCode.DANFOSS},
|
|
18
|
+
develco: {manufacturerCode: herdsman.Zcl.ManufacturerCode.DEVELCO},
|
|
18
19
|
hue: {manufacturerCode: herdsman.Zcl.ManufacturerCode.PHILIPS},
|
|
19
20
|
sinope: {manufacturerCode: herdsman.Zcl.ManufacturerCode.SINOPE_TECH},
|
|
20
21
|
/*
|
|
@@ -1910,6 +1911,12 @@ const converters = {
|
|
|
1910
1911
|
await entity.read('genBasic', [0x0033], manufacturerOptions.hue);
|
|
1911
1912
|
},
|
|
1912
1913
|
},
|
|
1914
|
+
ikea_pm25: {
|
|
1915
|
+
key: ['pm25', 'air_quality'],
|
|
1916
|
+
convertGet: async (entity, key, meta) => {
|
|
1917
|
+
await entity.read('manuSpecificIkeaPM25Measurement', ['measuredValue']);
|
|
1918
|
+
},
|
|
1919
|
+
},
|
|
1913
1920
|
RTCGQ13LM_motion_sensitivity: {
|
|
1914
1921
|
key: ['motion_sensitivity'],
|
|
1915
1922
|
convertSet: async (entity, key, value, meta) => {
|
|
@@ -2569,6 +2576,34 @@ const converters = {
|
|
|
2569
2576
|
await entity.read('haDiagnostic', ['danfossMultimasterRole'], manufacturerOptions.danfoss);
|
|
2570
2577
|
},
|
|
2571
2578
|
},
|
|
2579
|
+
develco_pulse_configuration: {
|
|
2580
|
+
key: ['pulse_configuration'],
|
|
2581
|
+
convertSet: async (entity, key, value, meta) => {
|
|
2582
|
+
await entity.write('seMetering', {'develcoPulseConfiguration': value}, manufacturerOptions.develco);
|
|
2583
|
+
return {readAfterWriteTime: 200, state: {'pulse_configuration': value}};
|
|
2584
|
+
},
|
|
2585
|
+
convertGet: async (entity, key, meta) => {
|
|
2586
|
+
await entity.read('seMetering', ['develcoPulseConfiguration'], manufacturerOptions.develco);
|
|
2587
|
+
},
|
|
2588
|
+
},
|
|
2589
|
+
develco_interface_mode: {
|
|
2590
|
+
key: ['interface_mode'],
|
|
2591
|
+
convertSet: async (entity, key, value, meta) => {
|
|
2592
|
+
const payload = {'develcoInterfaceMode': utils.getKey(constants.develcoInterfaceMode, value, undefined, Number)};
|
|
2593
|
+
await entity.write('seMetering', payload, manufacturerOptions.develco);
|
|
2594
|
+
return {readAfterWriteTime: 200, state: {'interface_mode': value}};
|
|
2595
|
+
},
|
|
2596
|
+
convertGet: async (entity, key, meta) => {
|
|
2597
|
+
await entity.read('seMetering', ['develcoInterfaceMode'], manufacturerOptions.develco);
|
|
2598
|
+
},
|
|
2599
|
+
},
|
|
2600
|
+
develco_current_summation: {
|
|
2601
|
+
key: ['current_summation'],
|
|
2602
|
+
convertSet: async (entity, key, value, meta) => {
|
|
2603
|
+
await entity.write('seMetering', {'develcoCurrentSummation': value}, manufacturerOptions.develco);
|
|
2604
|
+
return {state: {'current_summation': value}};
|
|
2605
|
+
},
|
|
2606
|
+
},
|
|
2572
2607
|
ZMCSW032D_cover_position: {
|
|
2573
2608
|
key: ['position', 'tilt'],
|
|
2574
2609
|
convertSet: async (entity, key, value, meta) => {
|
|
@@ -2762,9 +2797,9 @@ const converters = {
|
|
|
2762
2797
|
await tuya.sendDataPointBool(entity, tuya.dataPoints.tvChildLock, value === 'LOCK');
|
|
2763
2798
|
break;
|
|
2764
2799
|
case 'local_temperature_calibration':
|
|
2765
|
-
|
|
2766
|
-
|
|
2767
|
-
await tuya.sendDataPointValue(entity, tuya.dataPoints.tvTempCalibration, value
|
|
2800
|
+
if (value > 0) value = value*10;
|
|
2801
|
+
if (value < 0) value = value*10 + 0x100000000;
|
|
2802
|
+
await tuya.sendDataPointValue(entity, tuya.dataPoints.tvTempCalibration, value);
|
|
2768
2803
|
break;
|
|
2769
2804
|
case 'current_heating_setpoint':
|
|
2770
2805
|
await tuya.sendDataPointValue(entity, tuya.dataPoints.tvHeatingSetpoint, value * 10);
|
|
@@ -2807,7 +2842,7 @@ const converters = {
|
|
|
2807
2842
|
haozee_thermostat_system_mode: {
|
|
2808
2843
|
key: ['preset'],
|
|
2809
2844
|
convertSet: async (entity, key, value, meta) => {
|
|
2810
|
-
const lookup = {
|
|
2845
|
+
const lookup = {'auto': 0, 'manual': 1, 'off': 2, 'on': 3};
|
|
2811
2846
|
await tuya.sendDataPointEnum(entity, tuya.dataPoints.haozeeSystemMode, lookup[value]);
|
|
2812
2847
|
},
|
|
2813
2848
|
},
|
|
@@ -2845,7 +2880,7 @@ const converters = {
|
|
|
2845
2880
|
haozee_thermostat_temperature_calibration: {
|
|
2846
2881
|
key: ['local_temperature_calibration'],
|
|
2847
2882
|
convertSet: async (entity, key, value, meta) => {
|
|
2848
|
-
let temp = Math.round(value *
|
|
2883
|
+
let temp = Math.round(value * 10);
|
|
2849
2884
|
if (temp < 0) {
|
|
2850
2885
|
temp = 0xFFFFFFFF + temp + 1;
|
|
2851
2886
|
}
|
|
@@ -4557,6 +4592,34 @@ const converters = {
|
|
|
4557
4592
|
}
|
|
4558
4593
|
},
|
|
4559
4594
|
},
|
|
4595
|
+
tuya_thermostat_schedule_programming_mode: { // payload example "00:20/5°C 01:20/5°C 6:59/15°C 18:00/5°C 20:00/5°C 23:30/5°C"
|
|
4596
|
+
key: ['workdays_schedule', 'holidays_schedule'],
|
|
4597
|
+
convertSet: async (entity, key, value, meta) => {
|
|
4598
|
+
const dpId =
|
|
4599
|
+
(key === 'workdays_schedule') ?
|
|
4600
|
+
tuya.dataPoints.scheduleWorkday :
|
|
4601
|
+
tuya.dataPoints.scheduleHoliday;
|
|
4602
|
+
const payload = [];
|
|
4603
|
+
const items = value.split(' ');
|
|
4604
|
+
|
|
4605
|
+
for (let i = 0; i < 6; i++) {
|
|
4606
|
+
const hourTemperature = items[i].split('/');
|
|
4607
|
+
const hourMinute = hourTemperature[0].split(':', 2);
|
|
4608
|
+
const hour = parseInt(hourMinute[0]);
|
|
4609
|
+
const minute = parseInt(hourMinute[1]);
|
|
4610
|
+
const temperature = parseInt(hourTemperature[1]);
|
|
4611
|
+
|
|
4612
|
+
if (hour < 0 || hour >= 24 || minute < 0 || minute >= 60 || temperature < 5 || temperature >= 35) {
|
|
4613
|
+
throw new Error('Invalid hour, minute or temperature of:' + items[i]);
|
|
4614
|
+
}
|
|
4615
|
+
|
|
4616
|
+
payload[i*3] = hour;
|
|
4617
|
+
payload[i*3+1] = minute;
|
|
4618
|
+
payload[i*3+2] = temperature;
|
|
4619
|
+
}
|
|
4620
|
+
tuya.sendDataPointRaw(entity, dpId, payload);
|
|
4621
|
+
},
|
|
4622
|
+
},
|
|
4560
4623
|
tuya_thermostat_week: {
|
|
4561
4624
|
key: ['week'],
|
|
4562
4625
|
convertSet: async (entity, key, value, meta) => {
|
|
@@ -5236,8 +5299,6 @@ const converters = {
|
|
|
5236
5299
|
saswell_thermostat_calibration: {
|
|
5237
5300
|
key: ['local_temperature_calibration'],
|
|
5238
5301
|
convertSet: async (entity, key, value, meta) => {
|
|
5239
|
-
if (value > 6) value = 6;
|
|
5240
|
-
if (value < -6) value = -6;
|
|
5241
5302
|
if (value < 0) value = 0xFFFFFFFF + value + 1;
|
|
5242
5303
|
await tuya.sendDataPointValue(entity, tuya.dataPoints.saswellTempCalibration, value);
|
|
5243
5304
|
},
|
|
@@ -6080,6 +6141,46 @@ const converters = {
|
|
|
6080
6141
|
}
|
|
6081
6142
|
},
|
|
6082
6143
|
},
|
|
6144
|
+
javis_microwave_sensor: {
|
|
6145
|
+
key: [
|
|
6146
|
+
'illuminance_calibration', 'led_enable',
|
|
6147
|
+
'sensitivity', 'keep_time',
|
|
6148
|
+
],
|
|
6149
|
+
convertSet: async (entity, key, value, meta) => {
|
|
6150
|
+
switch (key) {
|
|
6151
|
+
case 'illuminance_calibration':// (10--100) sensor illuminance sensitivity
|
|
6152
|
+
if (meta.device.manufacturerName === '_TZE200_kagkgk0i') {
|
|
6153
|
+
await tuya.sendDataPointRaw(entity, 102, [value]);
|
|
6154
|
+
break;
|
|
6155
|
+
} else {
|
|
6156
|
+
tuya.sendDataPointRaw(entity, 105, [value]);
|
|
6157
|
+
break;
|
|
6158
|
+
}
|
|
6159
|
+
case 'led_enable':// OK (value true/false or 1/0)
|
|
6160
|
+
if (meta.device.manufacturerName === '_TZE200_kagkgk0i') {
|
|
6161
|
+
await tuya.sendDataPointRaw(entity, 107, [value ? 1 : 0]);
|
|
6162
|
+
break;
|
|
6163
|
+
} else {
|
|
6164
|
+
await tuya.sendDataPointRaw(entity, 103, [value ? 1 : 0]);
|
|
6165
|
+
break;
|
|
6166
|
+
}
|
|
6167
|
+
|
|
6168
|
+
case 'sensitivity':// value: 25, 50, 75, 100
|
|
6169
|
+
await tuya.sendDataPointRaw(entity, 2, [value]);
|
|
6170
|
+
break;
|
|
6171
|
+
case 'keep_time': // value 0 --> 7 corresponding 5s, 30s, 1, 3, 5, 10, 20, 30 min
|
|
6172
|
+
if (meta.device.manufacturerName === '_TZE200_kagkgk0i') {
|
|
6173
|
+
await tuya.sendDataPointRaw(entity, 106, [value]);
|
|
6174
|
+
break;
|
|
6175
|
+
} else {
|
|
6176
|
+
await tuya.sendDataPointRaw(entity, 102, [value]);
|
|
6177
|
+
break;
|
|
6178
|
+
}
|
|
6179
|
+
default: // Unknown key
|
|
6180
|
+
throw new Error(`Unhandled key ${key}`);
|
|
6181
|
+
}
|
|
6182
|
+
},
|
|
6183
|
+
},
|
|
6083
6184
|
moes_thermostat_tv: {
|
|
6084
6185
|
key: [
|
|
6085
6186
|
'system_mode', 'window_detection', 'frost_detection', 'child_lock',
|
|
@@ -5,6 +5,31 @@ const reporting = require('../lib/reporting');
|
|
|
5
5
|
const extend = require('../lib/extend');
|
|
6
6
|
const e = exposes.presets;
|
|
7
7
|
|
|
8
|
+
const batteryRotaryDimmer = (...endpointsIds) => ({
|
|
9
|
+
fromZigbee: [fz.battery, fz.command_on, fz.command_off, fz.command_step, fz.command_step_color_temperature],
|
|
10
|
+
toZigbee: [],
|
|
11
|
+
exposes: [e.battery(), e.action([
|
|
12
|
+
'on', 'off', 'brightness_step_up', 'brightness_step_down', 'color_temperature_step_up', 'color_temperature_step_down'])],
|
|
13
|
+
configure: async (device, coordinatorEndpoint, logger) => {
|
|
14
|
+
const endpoints = endpointsIds.map((endpoint) => device.getEndpoint(endpoint));
|
|
15
|
+
|
|
16
|
+
// Battery level is only reported on first endpoint
|
|
17
|
+
await reporting.batteryVoltage(endpoints[0]);
|
|
18
|
+
|
|
19
|
+
for await (const endpoint of endpoints) {
|
|
20
|
+
logger.debug(`processing endpoint ${endpoint.ID}`);
|
|
21
|
+
await reporting.bind(endpoint, coordinatorEndpoint,
|
|
22
|
+
['genIdentify', 'genOnOff', 'genLevelCtrl', 'lightingColorCtrl']);
|
|
23
|
+
|
|
24
|
+
// The default is for the device to also report the on/off and
|
|
25
|
+
// brightness at the same time as sending on/off and step commands.
|
|
26
|
+
// Disable the reporting by setting the max interval to 0xFFFF.
|
|
27
|
+
await reporting.brightness(endpoint, {max: 0xFFFF});
|
|
28
|
+
await reporting.onOff(endpoint, {max: 0xFFFF});
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
|
|
8
33
|
module.exports = [
|
|
9
34
|
{
|
|
10
35
|
zigbeeModel: ['TWGU10Bulb50AU'],
|
|
@@ -167,38 +192,20 @@ module.exports = [
|
|
|
167
192
|
model: 'AU-A1ZBR1GW',
|
|
168
193
|
vendor: 'Aurora Lighting',
|
|
169
194
|
description: 'AOne one gang wireless battery rotary dimmer',
|
|
170
|
-
fromZigbee: [fz.battery, fz.command_on, fz.command_off, fz.command_step, fz.command_step_color_temperature],
|
|
171
|
-
toZigbee: [],
|
|
172
|
-
exposes: [e.battery(), e.action([
|
|
173
|
-
'on', 'off', 'brightness_step_up', 'brightness_step_down', 'color_temperature_step_up', 'color_temperature_step_down'])],
|
|
174
195
|
meta: {battery: {voltageToPercentage: '3V_2100'}},
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
await reporting.bind(endpoint1, coordinatorEndpoint,
|
|
178
|
-
['genIdentify', 'genOnOff', 'genLevelCtrl', 'lightingColorCtrl', 'genPowerCfg']);
|
|
179
|
-
await reporting.batteryVoltage(endpoint1);
|
|
180
|
-
},
|
|
196
|
+
// One gang battery rotary dimmer with endpoint ID 1
|
|
197
|
+
...batteryRotaryDimmer(1),
|
|
181
198
|
},
|
|
182
199
|
{
|
|
183
200
|
zigbeeModel: ['2GBatteryDimmer50AU'],
|
|
184
201
|
model: 'AU-A1ZBR2GW',
|
|
185
202
|
vendor: 'Aurora Lighting',
|
|
186
203
|
description: 'AOne two gang wireless battery rotary dimmer',
|
|
187
|
-
fromZigbee: [fz.battery, fz.command_on, fz.command_off, fz.command_step, fz.command_step_color_temperature],
|
|
188
|
-
toZigbee: [],
|
|
189
|
-
exposes: [e.battery(), e.action([
|
|
190
|
-
'on', 'off', 'brightness_step_up', 'brightness_step_down', 'color_temperature_step_up', 'color_temperature_step_down'])],
|
|
191
204
|
meta: {multiEndpoint: true, battery: {voltageToPercentage: '3V_2100'}},
|
|
192
205
|
endpoint: (device) => {
|
|
193
206
|
return {'right': 1, 'left': 2};
|
|
194
207
|
},
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
await reporting.bind(endpoint1, coordinatorEndpoint,
|
|
198
|
-
['genIdentify', 'genOnOff', 'genLevelCtrl', 'lightingColorCtrl', 'genPowerCfg']);
|
|
199
|
-
await reporting.batteryVoltage(endpoint1);
|
|
200
|
-
const endpoint2 = device.getEndpoint(2);
|
|
201
|
-
await reporting.bind(endpoint2, coordinatorEndpoint, ['genIdentify', 'genOnOff', 'genLevelCtrl', 'lightingColorCtrl']);
|
|
202
|
-
},
|
|
208
|
+
// Two gang battery rotary dimmer with endpoint IDs 1 and 2
|
|
209
|
+
...batteryRotaryDimmer(1, 2),
|
|
203
210
|
},
|
|
204
211
|
];
|
package/devices/bitron.js
CHANGED
|
@@ -190,7 +190,7 @@ module.exports = [
|
|
|
190
190
|
tz.thermostat_running_state, tz.thermostat_temperature_display_mode, tz.thermostat_keypad_lockout, tz.thermostat_system_mode],
|
|
191
191
|
exposes: [e.battery(), exposes.climate().withSetpoint('occupied_heating_setpoint', 7, 30, 0.5).withLocalTemperature()
|
|
192
192
|
.withSystemMode(['off', 'auto', 'heat']).withRunningState(['idle', 'heat', 'cool'])
|
|
193
|
-
.withLocalTemperatureCalibration(), e.keypad_lockout()],
|
|
193
|
+
.withLocalTemperatureCalibration(-20, 20, 1), e.keypad_lockout()],
|
|
194
194
|
meta: {battery: {voltageToPercentage: '3V_2500_3200'}},
|
|
195
195
|
configure: async (device, coordinatorEndpoint, logger) => {
|
|
196
196
|
const endpoint = device.getEndpoint(1);
|
package/devices/candeo.js
CHANGED
|
@@ -13,7 +13,12 @@ module.exports = [
|
|
|
13
13
|
const endpoint = device.getEndpoint(1);
|
|
14
14
|
await reporting.bind(endpoint, coordinatorEndpoint, ['genOnOff', 'genLevelCtrl']);
|
|
15
15
|
await reporting.onOff(endpoint);
|
|
16
|
-
|
|
16
|
+
|
|
17
|
+
// The default reporting from the Candeo dimmer can result in a lot of Zigbee traffic
|
|
18
|
+
// to the extent that reported brighness values can arrive at the endpoint out of order
|
|
19
|
+
// giving the appearance that the values are jumping around.
|
|
20
|
+
// Limit the reporting to once a second to give a smoother reporting of brightness.
|
|
21
|
+
await reporting.brightness(endpoint, {min: 1});
|
|
17
22
|
},
|
|
18
23
|
},
|
|
19
24
|
];
|
package/devices/centralite.js
CHANGED
|
@@ -154,7 +154,8 @@ module.exports = [
|
|
|
154
154
|
exposes: [e.battery(), exposes.climate().withSetpoint('occupied_heating_setpoint', 10, 30, 1).withLocalTemperature()
|
|
155
155
|
.withSystemMode(['off', 'heat', 'cool', 'emergency_heating'])
|
|
156
156
|
.withRunningState(['idle', 'heat', 'cool', 'fan_only']).withFanMode(['auto', 'on'])
|
|
157
|
-
.withSetpoint('occupied_cooling_setpoint', 10, 30, 1)
|
|
157
|
+
.withSetpoint('occupied_cooling_setpoint', 10, 30, 1)
|
|
158
|
+
.withLocalTemperatureCalibration(-20, 20, 1)],
|
|
158
159
|
meta: {battery: {voltageToPercentage: '3V_1500_2800'}},
|
|
159
160
|
configure: async (device, coordinatorEndpoint, logger) => {
|
|
160
161
|
const endpoint = device.getEndpoint(1);
|
|
@@ -179,7 +180,8 @@ module.exports = [
|
|
|
179
180
|
exposes: [e.battery(), exposes.climate().withSetpoint('occupied_heating_setpoint', 10, 30, 1).withLocalTemperature()
|
|
180
181
|
.withSystemMode(['off', 'heat', 'cool', 'emergency_heating'])
|
|
181
182
|
.withRunningState(['idle', 'heat', 'cool', 'fan_only']).withFanMode(['auto', 'on'])
|
|
182
|
-
.withSetpoint('occupied_cooling_setpoint', 10, 30, 1)
|
|
183
|
+
.withSetpoint('occupied_cooling_setpoint', 10, 30, 1)
|
|
184
|
+
.withLocalTemperatureCalibration(-20, 20, 1)],
|
|
183
185
|
meta: {battery: {voltageToPercentage: '3V_1500_2800'}},
|
|
184
186
|
configure: async (device, coordinatorEndpoint, logger) => {
|
|
185
187
|
const endpoint = device.getEndpoint(1);
|
package/devices/develco.js
CHANGED
|
@@ -297,15 +297,28 @@ module.exports = [
|
|
|
297
297
|
model: 'ZHEMI101',
|
|
298
298
|
vendor: 'Develco',
|
|
299
299
|
description: 'Energy meter',
|
|
300
|
-
fromZigbee: [fz.metering],
|
|
301
|
-
toZigbee: [],
|
|
300
|
+
fromZigbee: [fz.metering, fz.develco_metering],
|
|
301
|
+
toZigbee: [tz.develco_pulse_configuration, tz.develco_interface_mode, tz.develco_current_summation],
|
|
302
|
+
endpoint: (device) => {
|
|
303
|
+
return {'default': 2};
|
|
304
|
+
},
|
|
302
305
|
configure: async (device, coordinatorEndpoint, logger) => {
|
|
303
306
|
const endpoint = device.getEndpoint(2);
|
|
304
307
|
await reporting.bind(endpoint, coordinatorEndpoint, ['seMetering']);
|
|
305
308
|
await reporting.instantaneousDemand(endpoint);
|
|
306
309
|
await reporting.readMeteringMultiplierDivisor(endpoint);
|
|
307
310
|
},
|
|
308
|
-
exposes: [
|
|
311
|
+
exposes: [
|
|
312
|
+
e.power(),
|
|
313
|
+
e.energy(),
|
|
314
|
+
exposes.numeric('pulse_configuration', ea.ALL).withValueMin(0).withValueMax(65535)
|
|
315
|
+
.withDescription('Pulses per kwh. Default 1000 imp/kWh. Range 0 to 65535'),
|
|
316
|
+
exposes.enum('interface_mode', ea.ALL,
|
|
317
|
+
['electricity', 'gas', 'water', 'kamstrup-kmp', 'linky', 'IEC62056-21', 'DSMR-2.3', 'DSMR-4.0'])
|
|
318
|
+
.withDescription('Operating mode/probe'),
|
|
319
|
+
exposes.numeric('current_summation', ea.SET)
|
|
320
|
+
.withDescription('Current summation value sent to the display. e.g. 570 = 0,570 kWh'),
|
|
321
|
+
],
|
|
309
322
|
},
|
|
310
323
|
{
|
|
311
324
|
zigbeeModel: ['SMRZB-332'],
|
package/devices/ecozy.js
CHANGED
|
@@ -18,7 +18,8 @@ module.exports = [
|
|
|
18
18
|
tz.thermostat_weekly_schedule, tz.thermostat_clear_weekly_schedule, tz.thermostat_relay_status_log,
|
|
19
19
|
tz.thermostat_pi_heating_demand, tz.thermostat_running_state],
|
|
20
20
|
exposes: [e.battery(), exposes.climate().withSetpoint('occupied_heating_setpoint', 7, 30, 1).withLocalTemperature()
|
|
21
|
-
.withSystemMode(['off', 'auto', 'heat']).withRunningState(['idle', 'heat'])
|
|
21
|
+
.withSystemMode(['off', 'auto', 'heat']).withRunningState(['idle', 'heat'])
|
|
22
|
+
.withLocalTemperatureCalibration(-20, 20, 1)
|
|
22
23
|
.withPiHeatingDemand(ea.STATE_GET)],
|
|
23
24
|
configure: async (device, coordinatorEndpoint, logger) => {
|
|
24
25
|
const endpoint = device.getEndpoint(3);
|