zigbee-herdsman-converters 14.0.684 → 14.0.686
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 +4 -0
- package/devices/philips.js +65 -18
- package/devices/quotra.js +18 -0
- package/devices/sinope.js +73 -59
- package/devices/tuya.js +4 -5
- package/devices/wyze.js +23 -0
- package/lib/color.js +11 -0
- package/lib/exposes.js +10 -0
- package/lib/philips.js +84 -0
- package/package.json +1 -1
package/converters/fromZigbee.js
CHANGED
|
@@ -6278,6 +6278,10 @@ const converters = {
|
|
|
6278
6278
|
result.position = options.invert_cover ? 100 - result.position : result.position;
|
|
6279
6279
|
}
|
|
6280
6280
|
}
|
|
6281
|
+
// Add the state
|
|
6282
|
+
if ('position' in result) {
|
|
6283
|
+
result.state = result.position === 0 ? 'CLOSE' : 'OPEN';
|
|
6284
|
+
}
|
|
6281
6285
|
return result;
|
|
6282
6286
|
},
|
|
6283
6287
|
},
|
package/devices/philips.js
CHANGED
|
@@ -4,6 +4,7 @@ const tz = require('../converters/toZigbee');
|
|
|
4
4
|
const ota = require('../lib/ota');
|
|
5
5
|
const reporting = require('../lib/reporting');
|
|
6
6
|
const globalStore = require('../lib/store');
|
|
7
|
+
const philips = require('../lib/philips');
|
|
7
8
|
const utils = require('../lib/utils');
|
|
8
9
|
const e = exposes.presets;
|
|
9
10
|
const ea = exposes.access;
|
|
@@ -40,6 +41,30 @@ const hueExtend = {
|
|
|
40
41
|
toZigbee: extendDontUse.light_onoff_brightness_colortemp_color({supportsHS: true, ...options})
|
|
41
42
|
.toZigbee.concat([tz.hue_power_on_behavior, tz.hue_power_on_error]),
|
|
42
43
|
}),
|
|
44
|
+
light_onoff_brightness_colortemp_color_gradient: (options={}) => ({
|
|
45
|
+
...extendDontUse.light_onoff_brightness_colortemp_color({supportsHS: true, noConfigure: true, ...options}),
|
|
46
|
+
ota: ota.zigbeeOTA,
|
|
47
|
+
meta: {turnsOffAtBrightness1: true},
|
|
48
|
+
configure: async (device, coordinatorEndpoint, logger) => {
|
|
49
|
+
await extendDontUse.light_onoff_brightness_colortemp_color({supportsHS: true, ...options})
|
|
50
|
+
.configure(device, coordinatorEndpoint, logger);
|
|
51
|
+
for (const ep of device.endpoints) {
|
|
52
|
+
await ep.bind('manuSpecificPhilips2', coordinatorEndpoint);
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
exposes: extendDontUse.light_onoff_brightness_colortemp_color({supportsHS: true, ...options}).exposes.concat([
|
|
56
|
+
// gradient_scene is deprecated, use gradient instead
|
|
57
|
+
exposes.enum('gradient_scene', ea.SET, Object.keys(gradientScenes)),
|
|
58
|
+
exposes.list('gradient', ea.ALL, exposes.text('hex', 'Color in RGB HEX format (eg #663399)'))
|
|
59
|
+
.withLengthMin(1)
|
|
60
|
+
.withLengthMax(9)
|
|
61
|
+
.withDescription('List of RGB HEX colors'),
|
|
62
|
+
]),
|
|
63
|
+
fromZigbee: extendDontUse.light_onoff_brightness_colortemp_color({supportsHS: true, ...options}).fromZigbee.concat(
|
|
64
|
+
[fzLocal.gradient({reverse: true})]),
|
|
65
|
+
toZigbee: extendDontUse.light_onoff_brightness_colortemp_color({supportsHS: true, ...options}).toZigbee.concat(
|
|
66
|
+
[tz.hue_power_on_behavior, tz.hue_power_on_error, tzLocal.gradient_scene, tzLocal.gradient({reverse: true})]),
|
|
67
|
+
}),
|
|
43
68
|
};
|
|
44
69
|
|
|
45
70
|
const fzLocal = {
|
|
@@ -82,6 +107,20 @@ const fzLocal = {
|
|
|
82
107
|
return payload;
|
|
83
108
|
},
|
|
84
109
|
},
|
|
110
|
+
gradient: (opts = {reverse: false}) => {
|
|
111
|
+
return {
|
|
112
|
+
cluster: 'manuSpecificPhilips2',
|
|
113
|
+
type: ['attributeReport', 'readResponse'],
|
|
114
|
+
convert: (model, msg, publish, options, meta) => {
|
|
115
|
+
if (msg.data.hasOwnProperty('state')) {
|
|
116
|
+
const input = msg.data['state'].toString('hex');
|
|
117
|
+
const gradient = philips.decodeGradientColors(input, opts);
|
|
118
|
+
return {gradient};
|
|
119
|
+
}
|
|
120
|
+
return {};
|
|
121
|
+
},
|
|
122
|
+
};
|
|
123
|
+
},
|
|
85
124
|
};
|
|
86
125
|
|
|
87
126
|
const gradientScenes = {
|
|
@@ -172,6 +211,19 @@ const tzLocal = {
|
|
|
172
211
|
await entity.command('manuSpecificPhilips2', 'multiColor', payload);
|
|
173
212
|
},
|
|
174
213
|
},
|
|
214
|
+
gradient: (opts = {reverse: false}) => {
|
|
215
|
+
return {
|
|
216
|
+
key: ['gradient'],
|
|
217
|
+
convertSet: async (entity, key, value, meta) => {
|
|
218
|
+
const scene = philips.encodeGradientColors(value, opts);
|
|
219
|
+
const payload = {data: Buffer.from(scene, 'hex')};
|
|
220
|
+
await entity.command('manuSpecificPhilips2', 'multiColor', payload);
|
|
221
|
+
},
|
|
222
|
+
convertGet: async (entity, key, meta) => {
|
|
223
|
+
await entity.read('manuSpecificPhilips2', ['state']);
|
|
224
|
+
},
|
|
225
|
+
};
|
|
226
|
+
},
|
|
175
227
|
};
|
|
176
228
|
|
|
177
229
|
module.exports = [
|
|
@@ -1278,8 +1330,15 @@ module.exports = [
|
|
|
1278
1330
|
zigbeeModel: ['LCW002', '4090230P9', '929003053101'],
|
|
1279
1331
|
model: '4090230P9',
|
|
1280
1332
|
vendor: 'Philips',
|
|
1281
|
-
description: 'Hue Liane',
|
|
1282
|
-
extend: hueExtend.light_onoff_brightness_colortemp_color(),
|
|
1333
|
+
description: 'Hue Liane (black)',
|
|
1334
|
+
extend: hueExtend.light_onoff_brightness_colortemp_color({colorTempRange: [153, 500]}),
|
|
1335
|
+
},
|
|
1336
|
+
{
|
|
1337
|
+
zigbeeModel: ['929003053201'],
|
|
1338
|
+
model: '929003053201',
|
|
1339
|
+
vendor: 'Philips',
|
|
1340
|
+
description: 'Hue Liane (white)',
|
|
1341
|
+
extend: hueExtend.light_onoff_brightness_colortemp_color({colorTempRange: [153, 500]}),
|
|
1283
1342
|
},
|
|
1284
1343
|
{
|
|
1285
1344
|
zigbeeModel: ['4090231P9'],
|
|
@@ -1643,20 +1702,14 @@ module.exports = [
|
|
|
1643
1702
|
model: '4080248U9',
|
|
1644
1703
|
vendor: 'Philips',
|
|
1645
1704
|
description: 'Hue White and color ambiance Signe floor light',
|
|
1646
|
-
|
|
1647
|
-
exposes: [exposes.enum('gradient_scene', ea.SET, Object.keys(gradientScenes)),
|
|
1648
|
-
...hueExtend.light_onoff_brightness_colortemp_color({colorTempRange: [153, 500]}).exposes],
|
|
1649
|
-
extend: hueExtend.light_onoff_brightness_colortemp_color({colorTempRange: [153, 500]}),
|
|
1705
|
+
extend: hueExtend.light_onoff_brightness_colortemp_color_gradient({colorTempRange: [153, 500]}),
|
|
1650
1706
|
},
|
|
1651
1707
|
{
|
|
1652
1708
|
zigbeeModel: ['915005987601'],
|
|
1653
1709
|
model: '915005987601',
|
|
1654
1710
|
vendor: 'Philips',
|
|
1655
1711
|
description: 'Hue Gradient Signe floor lamp (black)',
|
|
1656
|
-
|
|
1657
|
-
exposes: [exposes.enum('gradient_scene', ea.SET, Object.keys(gradientScenes)),
|
|
1658
|
-
...hueExtend.light_onoff_brightness_colortemp_color({colorTempRange: [153, 500]}).exposes],
|
|
1659
|
-
extend: hueExtend.light_onoff_brightness_colortemp_color({colorTempRange: [153, 500]}),
|
|
1712
|
+
extend: hueExtend.light_onoff_brightness_colortemp_color_gradient({colorTempRange: [153, 500]}),
|
|
1660
1713
|
},
|
|
1661
1714
|
{
|
|
1662
1715
|
zigbeeModel: ['LCT020'],
|
|
@@ -2947,20 +3000,14 @@ module.exports = [
|
|
|
2947
3000
|
model: '929003535301',
|
|
2948
3001
|
vendor: 'Philips',
|
|
2949
3002
|
description: 'Hue Festavia gradient light string 250',
|
|
2950
|
-
|
|
2951
|
-
exposes: [exposes.enum('gradient_scene', ea.SET, Object.keys(gradientScenes)),
|
|
2952
|
-
...hueExtend.light_onoff_brightness_colortemp_color({colorTempRange: [153, 500]}).exposes],
|
|
2953
|
-
extend: hueExtend.light_onoff_brightness_colortemp_color({colorTempRange: [153, 500]}),
|
|
3003
|
+
extend: hueExtend.light_onoff_brightness_colortemp_color_gradient({colorTempRange: [153, 500]}),
|
|
2954
3004
|
},
|
|
2955
3005
|
{
|
|
2956
3006
|
zigbeeModel: ['915005987101'],
|
|
2957
3007
|
model: '915005987101',
|
|
2958
3008
|
vendor: 'Philips',
|
|
2959
3009
|
description: 'Hue Gradient Signe floor lamp (white)',
|
|
2960
|
-
|
|
2961
|
-
exposes: [exposes.enum('gradient_scene', ea.SET, Object.keys(gradientScenes)),
|
|
2962
|
-
...hueExtend.light_onoff_brightness_colortemp_color({colorTempRange: [153, 500]}).exposes],
|
|
2963
|
-
extend: hueExtend.light_onoff_brightness_colortemp_color({colorTempRange: [153, 500]}),
|
|
3010
|
+
extend: hueExtend.light_onoff_brightness_colortemp_color_gradient({colorTempRange: [153, 500]}),
|
|
2964
3011
|
},
|
|
2965
3012
|
{
|
|
2966
3013
|
zigbeeModel: ['929003526301'],
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
const extend = require('../lib/extend');
|
|
2
|
+
|
|
3
|
+
module.exports = [
|
|
4
|
+
{
|
|
5
|
+
zigbeeModel: ['Dimmer_us'],
|
|
6
|
+
model: 'B07CVL9SZF',
|
|
7
|
+
vendor: 'Quotra',
|
|
8
|
+
description: 'Dimmer',
|
|
9
|
+
extend: extend.light_onoff_brightness(),
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
zigbeeModel: ['QV-RGBCCT'],
|
|
13
|
+
model: 'B07JHL6DRV',
|
|
14
|
+
vendor: 'Quotra',
|
|
15
|
+
description: 'RGB WW LED strip',
|
|
16
|
+
extend: extend.light_onoff_brightness_colortemp_color({colorTempRange: [150, 500]}),
|
|
17
|
+
},
|
|
18
|
+
];
|
package/devices/sinope.js
CHANGED
|
@@ -20,13 +20,24 @@ const fzLocal = {
|
|
|
20
20
|
const lookup = {0: 'unoccupied', 1: 'occupied'};
|
|
21
21
|
result.thermostat_occupancy = lookup[msg.data['1024']];
|
|
22
22
|
}
|
|
23
|
+
if (msg.data.hasOwnProperty('SinopeOccupancy')) {
|
|
24
|
+
const lookup = {0: 'unoccupied', 1: 'occupied'};
|
|
25
|
+
result.thermostat_occupancy = lookup[msg.data['SinopeOccupancy']];
|
|
26
|
+
}
|
|
23
27
|
if (msg.data.hasOwnProperty('1025')) {
|
|
24
28
|
result.main_cycle_output = cycleOutputLookup[msg.data['1025']];
|
|
25
29
|
}
|
|
30
|
+
if (msg.data.hasOwnProperty('SinopeMainCycleOutput')) {
|
|
31
|
+
result.main_cycle_output = cycleOutputLookup[msg.data['SinopeMainCycleOutput']];
|
|
32
|
+
}
|
|
26
33
|
if (msg.data.hasOwnProperty('1026')) {
|
|
27
34
|
const lookup = {0: 'on_demand', 1: 'sensing'};
|
|
28
35
|
result.backlight_auto_dim = lookup[msg.data['1026']];
|
|
29
36
|
}
|
|
37
|
+
if (msg.data.hasOwnProperty('SinopeBacklight')) {
|
|
38
|
+
const lookup = {0: 'on_demand', 1: 'sensing'};
|
|
39
|
+
result.backlight_auto_dim = lookup[msg.data['SinopeBacklight']];
|
|
40
|
+
}
|
|
30
41
|
if (msg.data.hasOwnProperty('1028')) {
|
|
31
42
|
result.aux_cycle_output = cycleOutputLookup[msg.data['1028']];
|
|
32
43
|
}
|
|
@@ -112,11 +123,11 @@ const tzLocal = {
|
|
|
112
123
|
convertSet: async (entity, key, value, meta) => {
|
|
113
124
|
const sinopeOccupancy = {0: 'unoccupied', 1: 'occupied'};
|
|
114
125
|
const SinopeOccupancy = utils.getKey(sinopeOccupancy, value, value, Number);
|
|
115
|
-
await entity.write('hvacThermostat', {SinopeOccupancy});
|
|
126
|
+
await entity.write('hvacThermostat', {SinopeOccupancy}, {manufacturerCode: 0x119C});
|
|
116
127
|
return {state: {'thermostat_occupancy': value}};
|
|
117
128
|
},
|
|
118
129
|
convertGet: async (entity, key, meta) => {
|
|
119
|
-
await entity.read('hvacThermostat', ['SinopeOccupancy']);
|
|
130
|
+
await entity.read('hvacThermostat', ['SinopeOccupancy'], {manufacturerCode: 0x119C});
|
|
120
131
|
},
|
|
121
132
|
},
|
|
122
133
|
sinope_thermostat_backlight_autodim_param: {
|
|
@@ -124,11 +135,11 @@ const tzLocal = {
|
|
|
124
135
|
convertSet: async (entity, key, value, meta) => {
|
|
125
136
|
const sinopeBacklightParam = {0: 'on_demand', 1: 'sensing'};
|
|
126
137
|
const SinopeBacklight = utils.getKey(sinopeBacklightParam, value, value, Number);
|
|
127
|
-
await entity.write('hvacThermostat', {SinopeBacklight});
|
|
138
|
+
await entity.write('hvacThermostat', {SinopeBacklight}, {manufacturerCode: 0x119C});
|
|
128
139
|
return {state: {'backlight_auto_dim': value}};
|
|
129
140
|
},
|
|
130
141
|
convertGet: async (entity, key, meta) => {
|
|
131
|
-
await entity.read('hvacThermostat', ['SinopeBacklight']);
|
|
142
|
+
await entity.read('hvacThermostat', ['SinopeBacklight'], {manufacturerCode: 0x119C});
|
|
132
143
|
},
|
|
133
144
|
},
|
|
134
145
|
sinope_thermostat_main_cycle_output: {
|
|
@@ -136,11 +147,11 @@ const tzLocal = {
|
|
|
136
147
|
key: ['main_cycle_output'],
|
|
137
148
|
convertSet: async (entity, key, value, meta) => {
|
|
138
149
|
const lookup = {'15_sec': 15, '5_min': 300, '10_min': 600, '15_min': 900, '20_min': 1200, '30_min': 1800};
|
|
139
|
-
await entity.write('hvacThermostat', {SinopeMainCycleOutput: lookup[value]});
|
|
150
|
+
await entity.write('hvacThermostat', {SinopeMainCycleOutput: lookup[value]}, {manufacturerCode: 0x119C});
|
|
140
151
|
return {state: {'main_cycle_output': value}};
|
|
141
152
|
},
|
|
142
153
|
convertGet: async (entity, key, meta) => {
|
|
143
|
-
await entity.read('hvacThermostat', ['SinopeMainCycleOutput']);
|
|
154
|
+
await entity.read('hvacThermostat', ['SinopeMainCycleOutput'], {manufacturerCode: 0x119C});
|
|
144
155
|
},
|
|
145
156
|
},
|
|
146
157
|
sinope_thermostat_aux_cycle_output: {
|
|
@@ -385,21 +396,20 @@ module.exports = [
|
|
|
385
396
|
fz.electrical_measurement, fz.metering, fz.ignore_temperature_report, fzLocal.sinope_thermostat],
|
|
386
397
|
toZigbee: [tz.thermostat_local_temperature, tz.thermostat_occupied_heating_setpoint, tz.thermostat_unoccupied_heating_setpoint,
|
|
387
398
|
tz.thermostat_temperature_display_mode, tz.thermostat_keypad_lockout, tz.thermostat_system_mode,
|
|
388
|
-
|
|
389
|
-
tzLocal.
|
|
390
|
-
tzLocal.
|
|
391
|
-
tz.electrical_measurement_power],
|
|
399
|
+
tzLocal.sinope_thermostat_backlight_autodim_param, tzLocal.sinope_thermostat_time, tzLocal.sinope_time_format,
|
|
400
|
+
tzLocal.sinope_thermostat_enable_outdoor_temperature, tzLocal.sinope_thermostat_outdoor_temperature,
|
|
401
|
+
tzLocal.sinope_thermostat_occupancy, tzLocal.sinope_thermostat_main_cycle_output, tz.electrical_measurement_power],
|
|
392
402
|
exposes: [
|
|
393
403
|
exposes.climate()
|
|
394
404
|
.withSetpoint('occupied_heating_setpoint', 5, 30, 0.5)
|
|
395
405
|
.withSetpoint('unoccupied_heating_setpoint', 5, 30, 0.5)
|
|
396
406
|
.withLocalTemperature()
|
|
397
407
|
.withSystemMode(['off', 'heat'], ea.ALL, 'Mode of the thermostat')
|
|
398
|
-
.withPiHeatingDemand(
|
|
399
|
-
.withRunningState(['idle', 'heat']),
|
|
408
|
+
.withPiHeatingDemand()
|
|
409
|
+
.withRunningState(['idle', 'heat'], ea.STATE),
|
|
400
410
|
exposes.enum('thermostat_occupancy', ea.ALL, ['unoccupied', 'occupied'])
|
|
401
411
|
.withDescription('Occupancy state of the thermostat'),
|
|
402
|
-
exposes.enum('backlight_auto_dim', ea.ALL, ['
|
|
412
|
+
exposes.enum('backlight_auto_dim', ea.ALL, ['on_demand', 'sensing'])
|
|
403
413
|
.withDescription('Control backlight dimming behavior'),
|
|
404
414
|
exposes.enum('keypad_lockout', ea.ALL, ['unlock', 'lock1'])
|
|
405
415
|
.withDescription('Enables or disables the device’s buttons'),
|
|
@@ -450,21 +460,20 @@ module.exports = [
|
|
|
450
460
|
fz.electrical_measurement, fz.metering, fz.ignore_temperature_report, fzLocal.sinope_thermostat],
|
|
451
461
|
toZigbee: [tz.thermostat_local_temperature, tz.thermostat_occupied_heating_setpoint, tz.thermostat_unoccupied_heating_setpoint,
|
|
452
462
|
tz.thermostat_temperature_display_mode, tz.thermostat_keypad_lockout, tz.thermostat_system_mode,
|
|
453
|
-
|
|
454
|
-
tzLocal.
|
|
455
|
-
tzLocal.
|
|
456
|
-
tz.electrical_measurement_power],
|
|
463
|
+
tzLocal.sinope_thermostat_backlight_autodim_param, tzLocal.sinope_thermostat_time, tzLocal.sinope_time_format,
|
|
464
|
+
tzLocal.sinope_thermostat_enable_outdoor_temperature, tzLocal.sinope_thermostat_outdoor_temperature,
|
|
465
|
+
tzLocal.sinope_thermostat_occupancy, tzLocal.sinope_thermostat_main_cycle_output, tz.electrical_measurement_power],
|
|
457
466
|
exposes: [
|
|
458
467
|
exposes.climate()
|
|
459
468
|
.withSetpoint('occupied_heating_setpoint', 5, 30, 0.5)
|
|
460
469
|
.withSetpoint('unoccupied_heating_setpoint', 5, 30, 0.5)
|
|
461
470
|
.withLocalTemperature()
|
|
462
471
|
.withSystemMode(['off', 'heat'], ea.ALL, 'Mode of the thermostat')
|
|
463
|
-
.withPiHeatingDemand(
|
|
464
|
-
.withRunningState(['idle', 'heat']),
|
|
472
|
+
.withPiHeatingDemand()
|
|
473
|
+
.withRunningState(['idle', 'heat'], ea.STATE),
|
|
465
474
|
exposes.enum('thermostat_occupancy', ea.ALL, ['unoccupied', 'occupied'])
|
|
466
475
|
.withDescription('Occupancy state of the thermostat'),
|
|
467
|
-
exposes.enum('backlight_auto_dim', ea.ALL, ['
|
|
476
|
+
exposes.enum('backlight_auto_dim', ea.ALL, ['on_demand', 'sensing'])
|
|
468
477
|
.withDescription('Control backlight dimming behavior'),
|
|
469
478
|
exposes.enum('keypad_lockout', ea.ALL, ['unlock', 'lock1'])
|
|
470
479
|
.withDescription('Enables or disables the device’s buttons'),
|
|
@@ -512,55 +521,61 @@ module.exports = [
|
|
|
512
521
|
description: 'Zigbee line volt thermostat',
|
|
513
522
|
meta: {thermostat: {dontMapPIHeatingDemand: true}},
|
|
514
523
|
fromZigbee: [fz.legacy.sinope_thermostat_att_report, fz.legacy.hvac_user_interface, fz.legacy.sinope_thermostat_state,
|
|
515
|
-
fz.electrical_measurement, fz.metering, fz.ignore_temperature_report],
|
|
516
|
-
toZigbee: [tz.thermostat_local_temperature, tz.thermostat_occupied_heating_setpoint, tz.
|
|
517
|
-
tz.
|
|
518
|
-
tzLocal.
|
|
519
|
-
tzLocal.
|
|
524
|
+
fz.electrical_measurement, fz.metering, fz.ignore_temperature_report, fzLocal.sinope_thermostat],
|
|
525
|
+
toZigbee: [tz.thermostat_local_temperature, tz.thermostat_occupied_heating_setpoint, tz.thermostat_unoccupied_heating_setpoint,
|
|
526
|
+
tz.thermostat_temperature_display_mode, tz.thermostat_keypad_lockout, tz.thermostat_system_mode,
|
|
527
|
+
tzLocal.sinope_thermostat_backlight_autodim_param, tzLocal.sinope_thermostat_time, tzLocal.sinope_time_format,
|
|
528
|
+
tzLocal.sinope_thermostat_enable_outdoor_temperature, tzLocal.sinope_thermostat_outdoor_temperature,
|
|
529
|
+
tzLocal.sinope_thermostat_occupancy, tzLocal.sinope_thermostat_main_cycle_output, tz.electrical_measurement_power],
|
|
520
530
|
exposes: [
|
|
521
531
|
exposes.climate()
|
|
522
532
|
.withSetpoint('occupied_heating_setpoint', 5, 30, 0.5)
|
|
533
|
+
.withSetpoint('unoccupied_heating_setpoint', 5, 30, 0.5)
|
|
523
534
|
.withLocalTemperature()
|
|
524
535
|
.withSystemMode(['off', 'heat'], ea.ALL, 'Mode of the thermostat')
|
|
525
|
-
.withPiHeatingDemand(
|
|
526
|
-
.withRunningState(['idle', 'heat']),
|
|
536
|
+
.withPiHeatingDemand()
|
|
537
|
+
.withRunningState(['idle', 'heat'], ea.STATE),
|
|
538
|
+
exposes.enum('thermostat_occupancy', ea.ALL, ['unoccupied', 'occupied'])
|
|
539
|
+
.withDescription('Occupancy state of the thermostat'),
|
|
540
|
+
exposes.enum('backlight_auto_dim', ea.ALL, ['on_demand', 'sensing'])
|
|
541
|
+
.withDescription('Control backlight dimming behavior'),
|
|
527
542
|
exposes.enum('keypad_lockout', ea.ALL, ['unlock', 'lock1'])
|
|
528
543
|
.withDescription('Enables or disables the device’s buttons'),
|
|
529
|
-
|
|
544
|
+
exposes.enum('main_cycle_output', ea.ALL, ['15_sec', '15_min'])
|
|
545
|
+
.withDescription('The length of the control cycle: 15_sec=normal 15_min=fan'),
|
|
546
|
+
e.power().withAccess(ea.STATE_GET), e.current(), e.voltage(), e.energy(),
|
|
530
547
|
],
|
|
531
548
|
|
|
532
549
|
configure: async (device, coordinatorEndpoint, logger) => {
|
|
533
550
|
const endpoint = device.getEndpoint(1);
|
|
534
551
|
const binds = [
|
|
535
|
-
'genBasic', 'genIdentify', '
|
|
552
|
+
'genBasic', 'genIdentify', 'hvacThermostat', 'hvacUserInterfaceCfg',
|
|
536
553
|
'msTemperatureMeasurement', 'haElectricalMeasurement', 'seMetering',
|
|
537
554
|
'manuSpecificSinope'];
|
|
538
555
|
await reporting.bind(endpoint, coordinatorEndpoint, binds);
|
|
539
556
|
await reporting.thermostatTemperature(endpoint);
|
|
540
557
|
await reporting.thermostatPIHeatingDemand(endpoint);
|
|
541
558
|
await reporting.thermostatOccupiedHeatingSetpoint(endpoint);
|
|
559
|
+
await reporting.thermostatUnoccupiedHeatingSetpoint(endpoint);
|
|
560
|
+
await reporting.thermostatSystemMode(endpoint);
|
|
542
561
|
|
|
543
|
-
try {
|
|
544
|
-
await reporting.thermostatSystemMode(endpoint);
|
|
545
|
-
} catch (error) {/* Not all support this */}
|
|
546
|
-
try {
|
|
547
|
-
await reporting.thermostatRunningState(endpoint);
|
|
548
|
-
} catch (error) {/* Not all support this */}
|
|
549
562
|
await reporting.readMeteringMultiplierDivisor(endpoint);
|
|
550
563
|
await reporting.currentSummDelivered(endpoint, {min: 10, max: 303, change: [1, 1]});
|
|
551
|
-
|
|
552
564
|
await reporting.readEletricalMeasurementMultiplierDivisors(endpoint);
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
565
|
+
await reporting.activePower(endpoint, {min: 10, max: 305, change: 1}); // divider 1: 1W
|
|
566
|
+
await reporting.rmsCurrent(endpoint, {min: 10, max: 306, change: 100}); // divider 1000: 0.1Arms
|
|
567
|
+
await reporting.rmsVoltage(endpoint, {min: 10, max: 307, change: 5}); // divider 10: 0.5Vrms
|
|
568
|
+
|
|
569
|
+
const thermostatDate = new Date();
|
|
570
|
+
const thermostatTimeSec = thermostatDate.getTime() / 1000;
|
|
571
|
+
const thermostatTimezoneOffsetSec = thermostatDate.getTimezoneOffset() * 60;
|
|
572
|
+
const currentTimeToDisplay = Math.round(thermostatTimeSec - thermostatTimezoneOffsetSec - 946684800);
|
|
573
|
+
await endpoint.write('manuSpecificSinope', {currentTimeToDisplay}, {manufacturerCode: 0x119C});
|
|
562
574
|
|
|
563
575
|
await reporting.temperature(endpoint, {min: 1, max: 0xFFFF}); // Disable default reporting
|
|
576
|
+
try {
|
|
577
|
+
await reporting.thermostatRunningState(endpoint);
|
|
578
|
+
} catch (error) {/* Do nothing */} // Not enought space
|
|
564
579
|
},
|
|
565
580
|
},
|
|
566
581
|
{
|
|
@@ -573,22 +588,21 @@ module.exports = [
|
|
|
573
588
|
fz.electrical_measurement, fz.metering, fz.ignore_temperature_report, fzLocal.sinope_TH1300ZB_specific],
|
|
574
589
|
toZigbee: [tz.thermostat_local_temperature, tz.thermostat_occupied_heating_setpoint, tz.thermostat_unoccupied_heating_setpoint,
|
|
575
590
|
tz.thermostat_temperature_display_mode, tz.thermostat_keypad_lockout, tz.thermostat_system_mode,
|
|
576
|
-
|
|
577
|
-
tzLocal.
|
|
578
|
-
tzLocal.
|
|
579
|
-
tzLocal.
|
|
580
|
-
tzLocal.sinope_temperature_sensor],
|
|
591
|
+
tzLocal.sinope_thermostat_backlight_autodim_param, tzLocal.sinope_thermostat_time, tzLocal.sinope_time_format,
|
|
592
|
+
tzLocal.sinope_thermostat_enable_outdoor_temperature, tzLocal.sinope_thermostat_outdoor_temperature,
|
|
593
|
+
tzLocal.sinope_thermostat_occupancy, tzLocal.sinope_floor_control_mode, tzLocal.sinope_ambiant_max_heat_setpoint,
|
|
594
|
+
tzLocal.sinope_floor_min_heat_setpoint, tzLocal.sinope_floor_max_heat_setpoint, tzLocal.sinope_temperature_sensor],
|
|
581
595
|
exposes: [
|
|
582
596
|
exposes.climate()
|
|
583
597
|
.withSetpoint('occupied_heating_setpoint', 5, 36, 0.5)
|
|
584
598
|
.withSetpoint('unoccupied_heating_setpoint', 5, 36, 0.5)
|
|
585
599
|
.withLocalTemperature()
|
|
586
600
|
.withSystemMode(['off', 'heat'], ea.ALL, 'Mode of the thermostat')
|
|
587
|
-
.withPiHeatingDemand(
|
|
588
|
-
.withRunningState(['idle', 'heat']),
|
|
601
|
+
.withPiHeatingDemand()
|
|
602
|
+
.withRunningState(['idle', 'heat'], ea.STATE),
|
|
589
603
|
exposes.enum('thermostat_occupancy', ea.ALL, ['unoccupied', 'occupied'])
|
|
590
604
|
.withDescription('Occupancy state of the thermostat'),
|
|
591
|
-
exposes.enum('backlight_auto_dim', ea.ALL, ['
|
|
605
|
+
exposes.enum('backlight_auto_dim', ea.ALL, ['on_demand', 'sensing'])
|
|
592
606
|
.withDescription('Control backlight dimming behavior'),
|
|
593
607
|
exposes.enum('keypad_lockout', ea.ALL, ['unlock', 'lock1'])
|
|
594
608
|
.withDescription('Enables or disables the device’s buttons'),
|
|
@@ -742,20 +756,20 @@ module.exports = [
|
|
|
742
756
|
fromZigbee: [fz.legacy.thermostat_att_report],
|
|
743
757
|
toZigbee: [tz.thermostat_local_temperature, tz.thermostat_occupied_heating_setpoint, tz.thermostat_unoccupied_heating_setpoint,
|
|
744
758
|
tz.thermostat_temperature_display_mode, tz.thermostat_keypad_lockout, tz.thermostat_system_mode,
|
|
745
|
-
|
|
746
|
-
tzLocal.
|
|
747
|
-
tzLocal.
|
|
759
|
+
tzLocal.sinope_thermostat_backlight_autodim_param, tzLocal.sinope_thermostat_time, tzLocal.sinope_time_format,
|
|
760
|
+
tzLocal.sinope_thermostat_enable_outdoor_temperature, tzLocal.sinope_thermostat_outdoor_temperature,
|
|
761
|
+
tzLocal.sinope_thermostat_occupancy],
|
|
748
762
|
exposes: [
|
|
749
763
|
exposes.climate()
|
|
750
764
|
.withSetpoint('occupied_heating_setpoint', 5, 30, 0.5)
|
|
751
765
|
.withSetpoint('unoccupied_heating_setpoint', 5, 30, 0.5)
|
|
752
766
|
.withLocalTemperature()
|
|
753
767
|
.withSystemMode(['off', 'heat'], ea.ALL, 'Mode of the thermostat')
|
|
754
|
-
.withPiHeatingDemand(
|
|
755
|
-
.withRunningState(['idle', 'heat']),
|
|
768
|
+
.withPiHeatingDemand()
|
|
769
|
+
.withRunningState(['idle', 'heat'], ea.STATE),
|
|
756
770
|
exposes.enum('thermostat_occupancy', ea.ALL, ['unoccupied', 'occupied'])
|
|
757
771
|
.withDescription('Occupancy state of the thermostat'),
|
|
758
|
-
exposes.enum('backlight_auto_dim', ea.ALL, ['
|
|
772
|
+
exposes.enum('backlight_auto_dim', ea.ALL, ['on_demand', 'sensing'])
|
|
759
773
|
.withDescription('Control backlight dimming behavior'),
|
|
760
774
|
exposes.enum('keypad_lockout', ea.ALL, ['unlock', 'lock1'])
|
|
761
775
|
.withDescription('Enables or disables the device’s buttons'),
|
package/devices/tuya.js
CHANGED
|
@@ -2824,10 +2824,8 @@ module.exports = [
|
|
|
2824
2824
|
},
|
|
2825
2825
|
},
|
|
2826
2826
|
{
|
|
2827
|
-
fingerprint:
|
|
2828
|
-
|
|
2829
|
-
{modelID: 'TS0014', manufacturerName: '_TZ3000_r0pmi2p3'}, {modelID: 'TS0014', manufacturerName: '_TZ3000_fxjdcikv'},
|
|
2830
|
-
{modelID: 'TS0014', manufacturerName: '_TZ3000_q6vxaod1'}],
|
|
2827
|
+
fingerprint: tuya.fingerprint('TS0014', ['_TZ3000_jr2atpww', '_TYZB01_dvakyzhd', '_TZ3000_mrduubod',
|
|
2828
|
+
'_TZ3210_w3hl6rao', '_TYZB01_bagt1e4o', '_TZ3000_r0pmi2p3', '_TZ3000_fxjdcikv', '_TZ3000_q6vxaod1']),
|
|
2831
2829
|
model: 'TS0014',
|
|
2832
2830
|
vendor: 'TuYa',
|
|
2833
2831
|
description: 'Smart light switch - 4 gang without neutral wire',
|
|
@@ -3077,7 +3075,8 @@ module.exports = [
|
|
|
3077
3075
|
fingerprint: [{modelID: 'TS0210', manufacturerName: '_TYZB01_3zv6oleo'},
|
|
3078
3076
|
{modelID: 'TS0210', manufacturerName: '_TYZB01_j9xxahcl'},
|
|
3079
3077
|
{modelID: 'TS0210', manufacturerName: '_TYZB01_kulduhbj'},
|
|
3080
|
-
{modelID: 'TS0210', manufacturerName: '_TZ3000_bmfw9ykl'}
|
|
3078
|
+
{modelID: 'TS0210', manufacturerName: '_TZ3000_bmfw9ykl'},
|
|
3079
|
+
{modelID: 'TS0210', manufacturerName: '_TZ3000_fkxmyics'}],
|
|
3081
3080
|
model: 'TS0210',
|
|
3082
3081
|
vendor: 'TuYa',
|
|
3083
3082
|
description: 'Vibration sensor',
|
package/devices/wyze.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
const exposes = require('../lib/exposes');
|
|
2
|
+
const fz = require('../converters/fromZigbee');
|
|
3
|
+
const tz = require('../converters/toZigbee');
|
|
4
|
+
const reporting = require('../lib/reporting');
|
|
5
|
+
const e = exposes.presets;
|
|
6
|
+
|
|
7
|
+
module.exports = [
|
|
8
|
+
{
|
|
9
|
+
zigbeeModel: ['Ford'],
|
|
10
|
+
model: 'WLCKG1',
|
|
11
|
+
vendor: 'Wyze',
|
|
12
|
+
description: 'Lock',
|
|
13
|
+
fromZigbee: [fz.lock, fz.lock_operation_event, fz.battery],
|
|
14
|
+
toZigbee: [tz.lock],
|
|
15
|
+
configure: async (device, coordinatorEndpoint, logger) => {
|
|
16
|
+
const endpoint = device.endpoints[0];
|
|
17
|
+
await reporting.bind(endpoint, coordinatorEndpoint, ['closuresDoorLock', 'genPowerCfg']);
|
|
18
|
+
await reporting.lockState(endpoint);
|
|
19
|
+
await reporting.batteryPercentageRemaining(endpoint);
|
|
20
|
+
},
|
|
21
|
+
exposes: [e.lock(), e.battery()],
|
|
22
|
+
},
|
|
23
|
+
];
|
package/lib/color.js
CHANGED
|
@@ -180,6 +180,17 @@ class ColorRGB {
|
|
|
180
180
|
}
|
|
181
181
|
return new ColorRGB(transform(this.red), transform(this.green), transform(this.blue));
|
|
182
182
|
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Create hex string from RGB color
|
|
186
|
+
* @return {ColorRGB} hex hex encoded RGB color
|
|
187
|
+
*/
|
|
188
|
+
toHEX() {
|
|
189
|
+
return '#' +
|
|
190
|
+
parseInt((this.red * 255).toFixed(0)).toString(16).padStart(2, '0')+
|
|
191
|
+
parseInt((this.green * 255).toFixed(0)).toString(16).padStart(2, '0')+
|
|
192
|
+
parseInt((this.blue * 255).toFixed(0)).toString(16).padStart(2, '0');
|
|
193
|
+
}
|
|
183
194
|
}
|
|
184
195
|
|
|
185
196
|
/**
|
package/lib/exposes.js
CHANGED
|
@@ -124,6 +124,16 @@ class List extends Base {
|
|
|
124
124
|
this.item_type = itemType;
|
|
125
125
|
delete this.item_type.property;
|
|
126
126
|
}
|
|
127
|
+
|
|
128
|
+
withLengthMin(value) {
|
|
129
|
+
this.length_min = value;
|
|
130
|
+
return this;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
withLengthMax(value) {
|
|
134
|
+
this.length_max = value;
|
|
135
|
+
return this;
|
|
136
|
+
}
|
|
127
137
|
}
|
|
128
138
|
|
|
129
139
|
class Numeric extends Base {
|
package/lib/philips.js
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const ColorXY = require('./color').ColorXY;
|
|
4
|
+
const ColorRGB = require('./color').ColorRGB;
|
|
5
|
+
|
|
6
|
+
const encodeRGBToScaledGradient = (hex) => {
|
|
7
|
+
const xy = ColorRGB.fromHex(hex).toXY();
|
|
8
|
+
const x = xy.x * 4095 / 0.7347;
|
|
9
|
+
const y = xy.y * 4095 / 0.8413;
|
|
10
|
+
const xx = Math.round(x).toString(16);
|
|
11
|
+
const yy = Math.round(y).toString(16);
|
|
12
|
+
|
|
13
|
+
return [
|
|
14
|
+
xx[1], xx[2],
|
|
15
|
+
yy[2], xx[0],
|
|
16
|
+
yy[0], yy[1],
|
|
17
|
+
].join('');
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const decodeScaledGradientToRGB = (p) => {
|
|
21
|
+
const x = p[3] + p[0] + p[1];
|
|
22
|
+
const y = p[4] + p[5] + p[2];
|
|
23
|
+
|
|
24
|
+
const xx = (parseInt(x, 16) * 0.7347 / 4095).toFixed(4);
|
|
25
|
+
const yy = (parseInt(y, 16) * 0.8413 / 4095).toFixed(4);
|
|
26
|
+
|
|
27
|
+
return new ColorXY(xx, yy).toRGB().toHEX();
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
function decodeGradientColors(input, opts) {
|
|
31
|
+
// Example set: 500104001350000000f3297ff3bd52f3bd52f3297ff3bd522800
|
|
32
|
+
// Example get 4b010164fb74346b1350000000f3297fda7d55da7d55f3297fda7d552800
|
|
33
|
+
const offset = input.indexOf('0000000') + 7;
|
|
34
|
+
const points = input.slice(offset, - 4);
|
|
35
|
+
const pairs = points.match(/.{6}/g);
|
|
36
|
+
const colors = pairs.map(decodeScaledGradientToRGB);
|
|
37
|
+
|
|
38
|
+
if (opts.reverse) {
|
|
39
|
+
colors.reverse();
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return colors;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Value is a list of RGB HEX colors
|
|
46
|
+
function encodeGradientColors(value, opts) {
|
|
47
|
+
if (value.length > 9) {
|
|
48
|
+
throw new Error(`Expected up to 9 colors, got ${value.length}`);
|
|
49
|
+
}
|
|
50
|
+
if (value.length < 1) {
|
|
51
|
+
throw new Error(`Expected at least 1 color, got 0`);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// For devices where it makes more sense to specify the colors in reverse
|
|
55
|
+
// For example Hue Signe, where the last color is the top color.
|
|
56
|
+
if (opts.reverse) {
|
|
57
|
+
value.reverse();
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// The number of colors and segments can technically differ. Here they are always the same, but we could
|
|
61
|
+
// support it by extending the API.
|
|
62
|
+
// If number of colors is less than the number of segments, the colors will repeat.
|
|
63
|
+
// It seems like the maximum number of colors is 9, and the maximum number of segments is 31.
|
|
64
|
+
const nColors = (value.length << 4).toString(16);
|
|
65
|
+
const segments = (value.length << 3).toString(16);
|
|
66
|
+
|
|
67
|
+
// Encode the colors
|
|
68
|
+
const colorsPayload = value.map(encodeRGBToScaledGradient).join('');
|
|
69
|
+
|
|
70
|
+
// Offset of the first color, left shifted 3 bits. 0 means the first segment uses the first color.
|
|
71
|
+
const offset = '00';
|
|
72
|
+
|
|
73
|
+
// Payload length
|
|
74
|
+
const length = (1 + 3 * (value.length + 1)).toString(16);
|
|
75
|
+
|
|
76
|
+
const scene = `50010400${length}${nColors}000000${colorsPayload}${segments}${offset}`;
|
|
77
|
+
|
|
78
|
+
return scene;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
module.exports = {
|
|
82
|
+
decodeGradientColors,
|
|
83
|
+
encodeGradientColors,
|
|
84
|
+
};
|