zigbee-herdsman-converters 14.0.685 → 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 +56 -16
- package/devices/tuya.js +3 -2
- 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 = [
|
|
@@ -1650,20 +1702,14 @@ module.exports = [
|
|
|
1650
1702
|
model: '4080248U9',
|
|
1651
1703
|
vendor: 'Philips',
|
|
1652
1704
|
description: 'Hue White and color ambiance Signe floor light',
|
|
1653
|
-
|
|
1654
|
-
exposes: [exposes.enum('gradient_scene', ea.SET, Object.keys(gradientScenes)),
|
|
1655
|
-
...hueExtend.light_onoff_brightness_colortemp_color({colorTempRange: [153, 500]}).exposes],
|
|
1656
|
-
extend: hueExtend.light_onoff_brightness_colortemp_color({colorTempRange: [153, 500]}),
|
|
1705
|
+
extend: hueExtend.light_onoff_brightness_colortemp_color_gradient({colorTempRange: [153, 500]}),
|
|
1657
1706
|
},
|
|
1658
1707
|
{
|
|
1659
1708
|
zigbeeModel: ['915005987601'],
|
|
1660
1709
|
model: '915005987601',
|
|
1661
1710
|
vendor: 'Philips',
|
|
1662
1711
|
description: 'Hue Gradient Signe floor lamp (black)',
|
|
1663
|
-
|
|
1664
|
-
exposes: [exposes.enum('gradient_scene', ea.SET, Object.keys(gradientScenes)),
|
|
1665
|
-
...hueExtend.light_onoff_brightness_colortemp_color({colorTempRange: [153, 500]}).exposes],
|
|
1666
|
-
extend: hueExtend.light_onoff_brightness_colortemp_color({colorTempRange: [153, 500]}),
|
|
1712
|
+
extend: hueExtend.light_onoff_brightness_colortemp_color_gradient({colorTempRange: [153, 500]}),
|
|
1667
1713
|
},
|
|
1668
1714
|
{
|
|
1669
1715
|
zigbeeModel: ['LCT020'],
|
|
@@ -2954,20 +3000,14 @@ module.exports = [
|
|
|
2954
3000
|
model: '929003535301',
|
|
2955
3001
|
vendor: 'Philips',
|
|
2956
3002
|
description: 'Hue Festavia gradient light string 250',
|
|
2957
|
-
|
|
2958
|
-
exposes: [exposes.enum('gradient_scene', ea.SET, Object.keys(gradientScenes)),
|
|
2959
|
-
...hueExtend.light_onoff_brightness_colortemp_color({colorTempRange: [153, 500]}).exposes],
|
|
2960
|
-
extend: hueExtend.light_onoff_brightness_colortemp_color({colorTempRange: [153, 500]}),
|
|
3003
|
+
extend: hueExtend.light_onoff_brightness_colortemp_color_gradient({colorTempRange: [153, 500]}),
|
|
2961
3004
|
},
|
|
2962
3005
|
{
|
|
2963
3006
|
zigbeeModel: ['915005987101'],
|
|
2964
3007
|
model: '915005987101',
|
|
2965
3008
|
vendor: 'Philips',
|
|
2966
3009
|
description: 'Hue Gradient Signe floor lamp (white)',
|
|
2967
|
-
|
|
2968
|
-
exposes: [exposes.enum('gradient_scene', ea.SET, Object.keys(gradientScenes)),
|
|
2969
|
-
...hueExtend.light_onoff_brightness_colortemp_color({colorTempRange: [153, 500]}).exposes],
|
|
2970
|
-
extend: hueExtend.light_onoff_brightness_colortemp_color({colorTempRange: [153, 500]}),
|
|
3010
|
+
extend: hueExtend.light_onoff_brightness_colortemp_color_gradient({colorTempRange: [153, 500]}),
|
|
2971
3011
|
},
|
|
2972
3012
|
{
|
|
2973
3013
|
zigbeeModel: ['929003526301'],
|
package/devices/tuya.js
CHANGED
|
@@ -2824,7 +2824,7 @@ module.exports = [
|
|
|
2824
2824
|
},
|
|
2825
2825
|
},
|
|
2826
2826
|
{
|
|
2827
|
-
fingerprint: tuya.fingerprint('TS0014', ['_TZ3000_jr2atpww', '_TYZB01_dvakyzhd',
|
|
2827
|
+
fingerprint: tuya.fingerprint('TS0014', ['_TZ3000_jr2atpww', '_TYZB01_dvakyzhd', '_TZ3000_mrduubod',
|
|
2828
2828
|
'_TZ3210_w3hl6rao', '_TYZB01_bagt1e4o', '_TZ3000_r0pmi2p3', '_TZ3000_fxjdcikv', '_TZ3000_q6vxaod1']),
|
|
2829
2829
|
model: 'TS0014',
|
|
2830
2830
|
vendor: 'TuYa',
|
|
@@ -3075,7 +3075,8 @@ module.exports = [
|
|
|
3075
3075
|
fingerprint: [{modelID: 'TS0210', manufacturerName: '_TYZB01_3zv6oleo'},
|
|
3076
3076
|
{modelID: 'TS0210', manufacturerName: '_TYZB01_j9xxahcl'},
|
|
3077
3077
|
{modelID: 'TS0210', manufacturerName: '_TYZB01_kulduhbj'},
|
|
3078
|
-
{modelID: 'TS0210', manufacturerName: '_TZ3000_bmfw9ykl'}
|
|
3078
|
+
{modelID: 'TS0210', manufacturerName: '_TZ3000_bmfw9ykl'},
|
|
3079
|
+
{modelID: 'TS0210', manufacturerName: '_TZ3000_fkxmyics'}],
|
|
3079
3080
|
model: 'TS0210',
|
|
3080
3081
|
vendor: 'TuYa',
|
|
3081
3082
|
description: 'Vibration sensor',
|
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
|
+
};
|