zigbee-herdsman-converters 15.0.9 → 15.0.10
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 +11 -0
- package/converters/toZigbee.js +11 -121
- package/devices/datek.js +38 -4
- package/devices/legrand.js +2 -2
- package/devices/moes.js +2 -1
- package/devices/netvox.js +9 -0
- package/devices/philips.js +291 -108
- package/devices/tuya.js +66 -0
- package/devices/ubisys.js +0 -1
- package/lib/ota/common.js +0 -5
- package/lib/tuya.js +14 -0
- package/package.json +1 -1
package/converters/fromZigbee.js
CHANGED
|
@@ -8320,6 +8320,17 @@ const converters = {
|
|
|
8320
8320
|
return result;
|
|
8321
8321
|
},
|
|
8322
8322
|
},
|
|
8323
|
+
led_on_motion: {
|
|
8324
|
+
cluster: 'ssIasZone',
|
|
8325
|
+
type: ['attributeReport', 'readResponse'],
|
|
8326
|
+
convert: (model, msg, publish, options, meta) => {
|
|
8327
|
+
const result = {};
|
|
8328
|
+
if (0x4000 in msg.data) {
|
|
8329
|
+
result.led_on_motion = msg.data[0x4000] == 1 ? true : false;
|
|
8330
|
+
}
|
|
8331
|
+
return result;
|
|
8332
|
+
},
|
|
8333
|
+
},
|
|
8323
8334
|
// #endregion
|
|
8324
8335
|
|
|
8325
8336
|
// #region Ignore converters (these message dont need parsing).
|
package/converters/toZigbee.js
CHANGED
|
@@ -2047,127 +2047,6 @@ const converters = {
|
|
|
2047
2047
|
return await converters.light_color_colortemp.convertGet(entity, key, meta);
|
|
2048
2048
|
},
|
|
2049
2049
|
},
|
|
2050
|
-
hue_power_on_behavior: {
|
|
2051
|
-
key: ['hue_power_on_behavior'],
|
|
2052
|
-
convertSet: async (entity, key, value, meta) => {
|
|
2053
|
-
if (value === 'default') {
|
|
2054
|
-
value = 'on';
|
|
2055
|
-
}
|
|
2056
|
-
|
|
2057
|
-
let supports = {colorTemperature: false, colorXY: false};
|
|
2058
|
-
if (entity.constructor.name === 'Endpoint' && entity.supportsInputCluster('lightingColorCtrl')) {
|
|
2059
|
-
const readResult = await entity.read('lightingColorCtrl', ['colorCapabilities']);
|
|
2060
|
-
supports = {
|
|
2061
|
-
colorTemperature: (readResult.colorCapabilities & 1 << 4) > 0,
|
|
2062
|
-
colorXY: (readResult.colorCapabilities & 1 << 3) > 0,
|
|
2063
|
-
};
|
|
2064
|
-
} else if (entity.constructor.name === 'Group') {
|
|
2065
|
-
supports = {colorTemperature: true, colorXY: true};
|
|
2066
|
-
}
|
|
2067
|
-
|
|
2068
|
-
if (value === 'off') {
|
|
2069
|
-
await entity.write('genOnOff', {0x4003: {value: 0x00, type: 0x30}});
|
|
2070
|
-
} else if (value === 'recover') {
|
|
2071
|
-
await entity.write('genOnOff', {0x4003: {value: 0xff, type: 0x30}});
|
|
2072
|
-
await entity.write('genLevelCtrl', {0x4000: {value: 0xff, type: 0x20}});
|
|
2073
|
-
|
|
2074
|
-
if (supports.colorTemperature) {
|
|
2075
|
-
await entity.write('lightingColorCtrl', {0x4010: {value: 0xffff, type: 0x21}});
|
|
2076
|
-
}
|
|
2077
|
-
|
|
2078
|
-
if (supports.colorXY) {
|
|
2079
|
-
await entity.write('lightingColorCtrl', {0x0003: {value: 0xffff, type: 0x21}}, manufacturerOptions.hue);
|
|
2080
|
-
await entity.write('lightingColorCtrl', {0x0004: {value: 0xffff, type: 0x21}}, manufacturerOptions.hue);
|
|
2081
|
-
}
|
|
2082
|
-
} else if (value === 'on') {
|
|
2083
|
-
await entity.write('genOnOff', {0x4003: {value: 0x01, type: 0x30}});
|
|
2084
|
-
|
|
2085
|
-
let brightness = meta.message.hasOwnProperty('hue_power_on_brightness') ?
|
|
2086
|
-
meta.message.hue_power_on_brightness : 0xfe;
|
|
2087
|
-
if (brightness === 255) {
|
|
2088
|
-
// 255 (0xFF) is the value for recover, therefore set it to 254 (0xFE)
|
|
2089
|
-
brightness = 254;
|
|
2090
|
-
}
|
|
2091
|
-
await entity.write('genLevelCtrl', {0x4000: {value: brightness, type: 0x20}});
|
|
2092
|
-
|
|
2093
|
-
if (entity.supportsInputCluster('lightingColorCtrl')) {
|
|
2094
|
-
if (
|
|
2095
|
-
meta.message.hasOwnProperty('hue_power_on_color_temperature') &&
|
|
2096
|
-
meta.message.hasOwnProperty('hue_power_on_color')
|
|
2097
|
-
) {
|
|
2098
|
-
meta.logger.error(`Provide either color temperature or color, not both`);
|
|
2099
|
-
} else if (meta.message.hasOwnProperty('hue_power_on_color_temperature')) {
|
|
2100
|
-
const colortemp = meta.message.hue_power_on_color_temperature;
|
|
2101
|
-
await entity.write('lightingColorCtrl', {0x4010: {value: colortemp, type: 0x21}});
|
|
2102
|
-
// Set color to default
|
|
2103
|
-
if (supports.colorXY) {
|
|
2104
|
-
await entity.write('lightingColorCtrl', {0x0003: {value: 0xFFFF, type: 0x21}}, manufacturerOptions.hue);
|
|
2105
|
-
await entity.write('lightingColorCtrl', {0x0004: {value: 0xFFFF, type: 0x21}}, manufacturerOptions.hue);
|
|
2106
|
-
}
|
|
2107
|
-
} else if (meta.message.hasOwnProperty('hue_power_on_color')) {
|
|
2108
|
-
const colorXY = libColor.ColorRGB.fromHex(meta.message.hue_power_on_color).toXY();
|
|
2109
|
-
value = {x: utils.mapNumberRange(colorXY.x, 0, 1, 0, 65535), y: utils.mapNumberRange(colorXY.y, 0, 1, 0, 65535)};
|
|
2110
|
-
|
|
2111
|
-
// Set colortemp to default
|
|
2112
|
-
if (supports.colorTemperature) {
|
|
2113
|
-
await entity.write('lightingColorCtrl', {0x4010: {value: 366, type: 0x21}});
|
|
2114
|
-
}
|
|
2115
|
-
|
|
2116
|
-
await entity.write('lightingColorCtrl', {0x0003: {value: value.x, type: 0x21}}, manufacturerOptions.hue);
|
|
2117
|
-
await entity.write('lightingColorCtrl', {0x0004: {value: value.y, type: 0x21}}, manufacturerOptions.hue);
|
|
2118
|
-
} else {
|
|
2119
|
-
// Set defaults for colortemp and color
|
|
2120
|
-
if (supports.colorTemperature) {
|
|
2121
|
-
await entity.write('lightingColorCtrl', {0x4010: {value: 366, type: 0x21}});
|
|
2122
|
-
}
|
|
2123
|
-
|
|
2124
|
-
if (supports.colorXY) {
|
|
2125
|
-
await entity.write('lightingColorCtrl', {0x0003: {value: 0xFFFF, type: 0x21}}, manufacturerOptions.hue);
|
|
2126
|
-
await entity.write('lightingColorCtrl', {0x0004: {value: 0xFFFF, type: 0x21}}, manufacturerOptions.hue);
|
|
2127
|
-
}
|
|
2128
|
-
}
|
|
2129
|
-
}
|
|
2130
|
-
}
|
|
2131
|
-
|
|
2132
|
-
return {state: {hue_power_on_behavior: value}};
|
|
2133
|
-
},
|
|
2134
|
-
},
|
|
2135
|
-
hue_power_on_error: {
|
|
2136
|
-
key: ['hue_power_on_brightness', 'hue_power_on_color_temperature', 'hue_power_on_color'],
|
|
2137
|
-
convertSet: async (entity, key, value, meta) => {
|
|
2138
|
-
if (!meta.message.hasOwnProperty('hue_power_on_behavior')) {
|
|
2139
|
-
throw new Error(`Provide a value for 'hue_power_on_behavior'`);
|
|
2140
|
-
}
|
|
2141
|
-
},
|
|
2142
|
-
},
|
|
2143
|
-
hue_motion_sensitivity: {
|
|
2144
|
-
// motion detect sensitivity, philips specific
|
|
2145
|
-
key: ['motion_sensitivity'],
|
|
2146
|
-
convertSet: async (entity, key, value, meta) => {
|
|
2147
|
-
// make sure you write to second endpoint!
|
|
2148
|
-
const lookup = {'low': 0, 'medium': 1, 'high': 2, 'very_high': 3, 'max': 4};
|
|
2149
|
-
value = value.toLowerCase();
|
|
2150
|
-
utils.validateValue(value, Object.keys(lookup));
|
|
2151
|
-
|
|
2152
|
-
const payload = {48: {value: lookup[value], type: 32}};
|
|
2153
|
-
await entity.write('msOccupancySensing', payload, manufacturerOptions.hue);
|
|
2154
|
-
return {state: {motion_sensitivity: value}};
|
|
2155
|
-
},
|
|
2156
|
-
convertGet: async (entity, key, meta) => {
|
|
2157
|
-
await entity.read('msOccupancySensing', [48], manufacturerOptions.hue);
|
|
2158
|
-
},
|
|
2159
|
-
},
|
|
2160
|
-
hue_motion_led_indication: {
|
|
2161
|
-
key: ['led_indication'],
|
|
2162
|
-
convertSet: async (entity, key, value, meta) => {
|
|
2163
|
-
const payload = {0x0033: {value, type: 0x10}};
|
|
2164
|
-
await entity.write('genBasic', payload, manufacturerOptions.hue);
|
|
2165
|
-
return {state: {led_indication: value}};
|
|
2166
|
-
},
|
|
2167
|
-
convertGet: async (entity, key, meta) => {
|
|
2168
|
-
await entity.read('genBasic', [0x0033], manufacturerOptions.hue);
|
|
2169
|
-
},
|
|
2170
|
-
},
|
|
2171
2050
|
aqara_motion_sensitivity: {
|
|
2172
2051
|
key: ['motion_sensitivity'],
|
|
2173
2052
|
convertSet: async (entity, key, value, meta) => {
|
|
@@ -7166,6 +7045,17 @@ const converters = {
|
|
|
7166
7045
|
}
|
|
7167
7046
|
},
|
|
7168
7047
|
},
|
|
7048
|
+
led_on_motion: {
|
|
7049
|
+
key: ['led_on_motion'],
|
|
7050
|
+
convertSet: async (entity, key, value, meta) => {
|
|
7051
|
+
await entity.write('ssIasZone', {0x4000: {value: value === true ? 1 : 0, type: 0x10}},
|
|
7052
|
+
{manufacturerCode: 4919});
|
|
7053
|
+
return {state: {led_on_motion: value}};
|
|
7054
|
+
},
|
|
7055
|
+
convertGet: async (entity, key, meta) => {
|
|
7056
|
+
await entity.read('ssIasZone', [0x4000], {manufacturerCode: 4919});
|
|
7057
|
+
},
|
|
7058
|
+
},
|
|
7169
7059
|
// #endregion
|
|
7170
7060
|
|
|
7171
7061
|
// #region Ignore converters
|
package/devices/datek.js
CHANGED
|
@@ -88,10 +88,29 @@ module.exports = [
|
|
|
88
88
|
fingerprint: [{modelID: 'Motion Sensor', manufacturerName: 'Eva'}],
|
|
89
89
|
model: 'HSE2927E',
|
|
90
90
|
vendor: 'Datek',
|
|
91
|
-
description: 'Eva
|
|
92
|
-
fromZigbee: [fz.
|
|
93
|
-
|
|
94
|
-
|
|
91
|
+
description: 'Eva motion sensor',
|
|
92
|
+
fromZigbee: [fz.battery, fz.occupancy, fz.occupancy_timeout, fz.illuminance, fz.temperature,
|
|
93
|
+
fz.ias_enroll, fz.ias_occupancy_alarm_1, fz.ias_occupancy_alarm_1_report, fz.led_on_motion],
|
|
94
|
+
toZigbee: [tz.occupancy_timeout, tz.led_on_motion],
|
|
95
|
+
configure: async (device, coordinatorEndpoint, logger) => {
|
|
96
|
+
const options = {manufacturerCode: 4919};
|
|
97
|
+
const endpoint = device.getEndpoint(1);
|
|
98
|
+
const binds = ['msIlluminanceMeasurement', 'msTemperatureMeasurement', 'msOccupancySensing', 'ssIasZone'];
|
|
99
|
+
await reporting.bind(endpoint, coordinatorEndpoint, binds);
|
|
100
|
+
await reporting.occupancy(endpoint);
|
|
101
|
+
await reporting.temperature(endpoint);
|
|
102
|
+
await reporting.illuminance(endpoint);
|
|
103
|
+
const payload = [{
|
|
104
|
+
attribute: {ID: 0x4000, type: 0x10},
|
|
105
|
+
}];
|
|
106
|
+
await endpoint.configureReporting('ssIasZone', payload, options);
|
|
107
|
+
await endpoint.read('ssIasZone', ['iasCieAddr', 'zoneState', 'zoneId']);
|
|
108
|
+
await endpoint.read('msOccupancySensing', ['pirOToUDelay']);
|
|
109
|
+
await endpoint.read('ssIasZone', [0x4000], options);
|
|
110
|
+
},
|
|
111
|
+
exposes: [e.temperature(), e.occupancy(), e.battery_low(), e.illuminance_lux(), e.illuminance(),
|
|
112
|
+
exposes.binary('led_on_motion', ea.ALL, true, false).withDescription('Enable/disable LED on motion'),
|
|
113
|
+
exposes.numeric('occupancy_timeout', ea.ALL).withUnit('seconds').withValueMin(0).withValueMax(65535)],
|
|
95
114
|
},
|
|
96
115
|
{
|
|
97
116
|
zigbeeModel: ['ID Lock 150'],
|
|
@@ -208,4 +227,19 @@ module.exports = [
|
|
|
208
227
|
e.action(['recall_1', 'recall_2', 'recall_3', 'recall_4', 'on', 'off',
|
|
209
228
|
'brightness_move_down', 'brightness_move_up', 'brightness_stop'])],
|
|
210
229
|
},
|
|
230
|
+
{
|
|
231
|
+
zigbeeModel: ['Door/Window Sensor'],
|
|
232
|
+
model: 'HSE2920E',
|
|
233
|
+
vendor: 'Datek',
|
|
234
|
+
description: 'Door/window sensor',
|
|
235
|
+
fromZigbee: [fz.ias_contact_alarm_1, fz.ias_contact_alarm_1_report, fz.temperature, fz.ias_enroll],
|
|
236
|
+
toZigbee: [],
|
|
237
|
+
configure: async (device, coordinatorEndpoint, logger) => {
|
|
238
|
+
const endpoint = device.getEndpoint(1);
|
|
239
|
+
await reporting.bind(endpoint, coordinatorEndpoint, ['ssIasZone', 'msTemperatureMeasurement']);
|
|
240
|
+
await reporting.temperature(endpoint);
|
|
241
|
+
await endpoint.read('ssIasZone', ['iasCieAddr', 'zoneState', 'zoneId']);
|
|
242
|
+
},
|
|
243
|
+
exposes: [e.contact(), e.battery_low(), e.tamper(), e.temperature()],
|
|
244
|
+
},
|
|
211
245
|
];
|
package/devices/legrand.js
CHANGED
|
@@ -54,7 +54,7 @@ module.exports = [
|
|
|
54
54
|
zigbeeModel: [' Contactor\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000'+
|
|
55
55
|
'\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000'],
|
|
56
56
|
model: '412171',
|
|
57
|
-
description: 'DIN contactor module (
|
|
57
|
+
description: 'DIN contactor module (BTicino FC80CC )',
|
|
58
58
|
vendor: 'Legrand',
|
|
59
59
|
extend: extend.switch(),
|
|
60
60
|
fromZigbee: [fz.identify, fz.on_off, fz.electrical_measurement, fz.legrand_cluster_fc01, fz.ignore_basic_report, fz.ignore_genOta],
|
|
@@ -78,7 +78,7 @@ module.exports = [
|
|
|
78
78
|
zigbeeModel: [' Teleruptor\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000'+
|
|
79
79
|
'\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000'],
|
|
80
80
|
model: '412170',
|
|
81
|
-
description: 'DIN smart relay for light control (
|
|
81
|
+
description: 'DIN smart relay for light control (BTicino FC80RC ) ',
|
|
82
82
|
vendor: 'Legrand',
|
|
83
83
|
extend: extend.switch(),
|
|
84
84
|
fromZigbee: [fz.identify, fz.on_off, fz.electrical_measurement, fz.legrand_cluster_fc01, fz.ignore_basic_report, fz.ignore_genOta],
|
package/devices/moes.js
CHANGED
|
@@ -310,7 +310,8 @@ module.exports = [
|
|
|
310
310
|
exposes.numeric('boost_heating_countdown', ea.STATE).withUnit('Min').withDescription('Countdown in minutes')
|
|
311
311
|
.withValueMin(0).withValueMax(15),
|
|
312
312
|
exposes.numeric('boost_heating_countdown_time_set', ea.STATE_SET).withUnit('second')
|
|
313
|
-
.withDescription('Boost Time Setting 100 sec - 900 sec, (default = 300 sec)').withValueMin(100)
|
|
313
|
+
.withDescription('Boost Time Setting 100 sec - 900 sec, (default = 300 sec)').withValueMin(100)
|
|
314
|
+
.withValueMax(900).withValueStep(100)],
|
|
314
315
|
},
|
|
315
316
|
{
|
|
316
317
|
fingerprint: [{modelID: 'TS0601', manufacturerName: '_TZE200_e3oitdyu'}],
|
package/devices/netvox.js
CHANGED
|
@@ -23,4 +23,13 @@ module.exports = [
|
|
|
23
23
|
},
|
|
24
24
|
exposes: [e.switch(), e.power(), e.current(), e.voltage()],
|
|
25
25
|
},
|
|
26
|
+
{
|
|
27
|
+
zigbeeModel: ['ZB02AE0ED'],
|
|
28
|
+
model: 'ZB02A',
|
|
29
|
+
vendor: 'Netvox',
|
|
30
|
+
description: 'Wireless wall switch',
|
|
31
|
+
fromZigbee: [fz.command_toggle],
|
|
32
|
+
toZigbee: [],
|
|
33
|
+
exposes: [e.action(['toggle'])],
|
|
34
|
+
},
|
|
26
35
|
];
|
package/devices/philips.js
CHANGED
|
@@ -6,6 +6,8 @@ const reporting = require('../lib/reporting');
|
|
|
6
6
|
const globalStore = require('../lib/store');
|
|
7
7
|
const philips = require('../lib/philips');
|
|
8
8
|
const utils = require('../lib/utils');
|
|
9
|
+
const libColor = require('../lib/color');
|
|
10
|
+
const herdsman = require('zigbee-herdsman');
|
|
9
11
|
const e = exposes.presets;
|
|
10
12
|
const ea = exposes.access;
|
|
11
13
|
|
|
@@ -13,115 +15,96 @@ const ea = exposes.access;
|
|
|
13
15
|
const extendDontUse = require('../lib/extend');
|
|
14
16
|
const extend = {switch: extendDontUse.switch};
|
|
15
17
|
|
|
18
|
+
const manufacturerOptions = {manufacturerCode: herdsman.Zcl.ManufacturerCode.PHILIPS};
|
|
19
|
+
|
|
16
20
|
const hueExtend = {
|
|
17
21
|
light_onoff_brightness: (options={}) => ({
|
|
18
22
|
...extendDontUse.light_onoff_brightness(options),
|
|
19
23
|
ota: ota.zigbeeOTA,
|
|
20
24
|
meta: {turnsOffAtBrightness1: true},
|
|
21
|
-
toZigbee: extendDontUse.light_onoff_brightness(options).toZigbee.concat([
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
...extendDontUse.light_onoff_brightness_colortemp(options),
|
|
25
|
-
ota: ota.zigbeeOTA,
|
|
26
|
-
meta: {turnsOffAtBrightness1: true},
|
|
27
|
-
toZigbee: extendDontUse.light_onoff_brightness_colortemp(options).toZigbee
|
|
28
|
-
.concat([tz.hue_power_on_behavior, tz.hue_power_on_error]),
|
|
29
|
-
}),
|
|
30
|
-
light_onoff_brightness_color: (options={}) => ({
|
|
31
|
-
...extendDontUse.light_onoff_brightness_color({supportsHS: true, ...options}),
|
|
32
|
-
ota: ota.zigbeeOTA,
|
|
33
|
-
meta: {turnsOffAtBrightness1: true},
|
|
34
|
-
toZigbee: extendDontUse.light_onoff_brightness_color({supportsHS: true, ...options}).toZigbee
|
|
35
|
-
.concat([tz.hue_power_on_behavior, tz.hue_power_on_error]),
|
|
36
|
-
}),
|
|
37
|
-
light_onoff_brightness_colortemp_color: (options={}) => ({
|
|
38
|
-
...extendDontUse.light_onoff_brightness_colortemp_color({supportsHS: true, ...options}),
|
|
39
|
-
ota: ota.zigbeeOTA,
|
|
40
|
-
meta: {turnsOffAtBrightness1: true},
|
|
41
|
-
toZigbee: extendDontUse.light_onoff_brightness_colortemp_color({supportsHS: true, ...options})
|
|
42
|
-
.toZigbee.concat([tz.hue_power_on_behavior, tz.hue_power_on_error]),
|
|
25
|
+
toZigbee: extendDontUse.light_onoff_brightness(options).toZigbee.concat([
|
|
26
|
+
tzLocal.hue_power_on_behavior, tzLocal.hue_power_on_error,
|
|
27
|
+
]),
|
|
43
28
|
}),
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
29
|
+
light_onoff_brightness_colortemp: (options={}) => {
|
|
30
|
+
options = {disableHueEffects: true, ...options};
|
|
31
|
+
if (!options.disableHueEffects) options.disableEffect = true;
|
|
32
|
+
const result = extendDontUse.light_onoff_brightness_colortemp(options);
|
|
33
|
+
result['ota'] = ota.zigbeeOTA;
|
|
34
|
+
result['meta'] = {turnsOffAtBrightness1: true};
|
|
35
|
+
result['toZigbee'] = result['toZigbee'].concat([tzLocal.hue_power_on_behavior, tzLocal.hue_power_on_error]);
|
|
36
|
+
if (!options.disableHueEffects) {
|
|
37
|
+
result['toZigbee'] = result['toZigbee'].concat([tzLocal.effect]);
|
|
38
|
+
result['exposes'] = result['exposes'].concat([exposes.enum('effect', ea.SET,
|
|
39
|
+
['blink', 'breathe', 'okay', 'channel_change', 'candle', 'finish_effect', 'stop_effect', 'stop_hue_effect'])]);
|
|
40
|
+
}
|
|
41
|
+
return result;
|
|
42
|
+
},
|
|
43
|
+
light_onoff_brightness_color: (options={}) => {
|
|
44
|
+
options = {disableHueEffects: true, ...options};
|
|
45
|
+
if (!options.disableHueEffects) options.disableEffect = true;
|
|
46
|
+
const result = extendDontUse.light_onoff_brightness_color({supportsHS: true, ...options});
|
|
47
|
+
result['ota'] = ota.zigbeeOTA;
|
|
48
|
+
result['meta'] = {turnsOffAtBrightness1: true};
|
|
49
|
+
result['toZigbee'] = result['toZigbee'].concat([tzLocal.hue_power_on_behavior, tzLocal.hue_power_on_error]);
|
|
50
|
+
if (!options.disableHueEffects) {
|
|
51
|
+
result['toZigbee'] = result['toZigbee'].concat([tzLocal.effect]);
|
|
52
|
+
result['exposes'] = result['exposes'].concat([exposes.enum('effect', ea.SET, [
|
|
53
|
+
'blink', 'breathe', 'okay', 'channel_change',
|
|
54
|
+
'candle', 'fireplace', 'colorloop',
|
|
55
|
+
'finish_effect', 'stop_effect', 'stop_hue_effect',
|
|
56
|
+
])]);
|
|
57
|
+
}
|
|
58
|
+
return result;
|
|
59
|
+
},
|
|
60
|
+
light_onoff_brightness_colortemp_color: (options={}) => {
|
|
61
|
+
options = {disableHueEffects: true, ...options};
|
|
62
|
+
if (!options.disableHueEffects) options.disableEffect = true;
|
|
63
|
+
const result = extendDontUse.light_onoff_brightness_colortemp_color({supportsHS: true, ...options});
|
|
64
|
+
result['ota'] = ota.zigbeeOTA;
|
|
65
|
+
result['meta'] = {turnsOffAtBrightness1: true};
|
|
66
|
+
result['toZigbee'] = result['toZigbee'].concat([tzLocal.hue_power_on_behavior, tzLocal.hue_power_on_error]);
|
|
67
|
+
if (!options.disableHueEffects) {
|
|
68
|
+
result['toZigbee'] = result['toZigbee'].concat([tzLocal.effect]);
|
|
69
|
+
result['exposes'] = result['exposes'].concat([exposes.enum('effect', ea.SET, [
|
|
70
|
+
'blink', 'breathe', 'okay', 'channel_change',
|
|
71
|
+
'candle', 'fireplace', 'colorloop',
|
|
72
|
+
'finish_effect', 'stop_effect', 'stop_hue_effect',
|
|
73
|
+
])]);
|
|
74
|
+
}
|
|
75
|
+
return result;
|
|
76
|
+
},
|
|
77
|
+
light_onoff_brightness_colortemp_color_gradient: (options={}) => {
|
|
78
|
+
options = {supportsHS: true, disableEffect: true, noConfigure: true, ...options};
|
|
79
|
+
const result = extendDontUse.light_onoff_brightness_colortemp_color({supportsHS: true, ...options});
|
|
80
|
+
result['ota'] = ota.zigbeeOTA;
|
|
81
|
+
result['meta'] = {turnsOffAtBrightness1: true};
|
|
82
|
+
result['toZigbee'] = result['toZigbee'].concat([
|
|
83
|
+
tzLocal.hue_power_on_behavior, tzLocal.hue_power_on_error, tzLocal.effect,
|
|
84
|
+
tzLocal.gradient_scene, tzLocal.gradient({reverse: true}),
|
|
85
|
+
]);
|
|
86
|
+
result['fromZigbee'] = result['fromZigbee'].concat([fzLocal.gradient({reverse: true})]);
|
|
87
|
+
result['configure'] = async (device, coordinatorEndpoint, logger) => {
|
|
88
|
+
await extendDontUse.light_onoff_brightness_colortemp_color(options)
|
|
50
89
|
.configure(device, coordinatorEndpoint, logger);
|
|
51
90
|
for (const ep of device.endpoints) {
|
|
52
91
|
await ep.bind('manuSpecificPhilips2', coordinatorEndpoint);
|
|
53
92
|
}
|
|
54
|
-
}
|
|
55
|
-
exposes
|
|
93
|
+
};
|
|
94
|
+
result['exposes'] = result['exposes'].concat([
|
|
56
95
|
// gradient_scene is deprecated, use gradient instead
|
|
57
96
|
exposes.enum('gradient_scene', ea.SET, Object.keys(gradientScenes)),
|
|
58
97
|
exposes.list('gradient', ea.ALL, exposes.text('hex', 'Color in RGB HEX format (eg #663399)'))
|
|
59
98
|
.withLengthMin(1)
|
|
60
99
|
.withLengthMax(9)
|
|
61
100
|
.withDescription('List of RGB HEX colors'),
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
const fzLocal = {
|
|
71
|
-
hue_tap_dial: {
|
|
72
|
-
cluster: 'manuSpecificPhilips',
|
|
73
|
-
type: 'commandHueNotification',
|
|
74
|
-
options: [exposes.options.simulated_brightness()],
|
|
75
|
-
convert: (model, msg, publish, options, meta) => {
|
|
76
|
-
const buttonLookup = {1: 'button_1', 2: 'button_2', 3: 'button_3', 4: 'button_4', 20: 'dial'};
|
|
77
|
-
const button = buttonLookup[msg.data['button']];
|
|
78
|
-
const typeLookup = {0: 'press', 1: 'hold', 2: 'press_release', 3: 'hold_release'};
|
|
79
|
-
const type = typeLookup[msg.data['type']];
|
|
80
|
-
const direction = msg.data['unknown2'] <127 ? 'right' : 'left';
|
|
81
|
-
const time = msg.data['time'];
|
|
82
|
-
const payload = {};
|
|
83
|
-
|
|
84
|
-
if (button === 'dial') {
|
|
85
|
-
const adjustedTime = direction === 'right' ? time : 256 - time;
|
|
86
|
-
const dialType = 'rotate';
|
|
87
|
-
const speed = adjustedTime <= 25 ? 'step' : adjustedTime <= 75 ? 'slow' : 'fast';
|
|
88
|
-
payload.action = `${button}_${dialType}_${direction}_${speed}`;
|
|
89
|
-
|
|
90
|
-
// simulated brightness
|
|
91
|
-
if (options.simulated_brightness) {
|
|
92
|
-
const opts = options.simulated_brightness;
|
|
93
|
-
const deltaOpts = typeof opts === 'object' && opts.hasOwnProperty('delta') ? opts.delta : 35;
|
|
94
|
-
const delta = direction === 'right' ? deltaOpts : deltaOpts * -1;
|
|
95
|
-
const brightness = globalStore.getValue(msg.endpoint, 'brightness', 255) + delta;
|
|
96
|
-
payload.brightness = utils.numberWithinRange(brightness, 0, 255);
|
|
97
|
-
globalStore.putValue(msg.endpoint, 'brightness', payload.brightness);
|
|
98
|
-
}
|
|
99
|
-
} else {
|
|
100
|
-
payload.action = `${button}_${type}`;
|
|
101
|
-
// duration
|
|
102
|
-
if (type === 'press') globalStore.putValue(msg.endpoint, 'press_start', Date.now());
|
|
103
|
-
else if (type === 'hold' || type === 'hold_release') {
|
|
104
|
-
payload.action_duration = (Date.now() - globalStore.getValue(msg.endpoint, 'press_start')) / 1000;
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
return payload;
|
|
108
|
-
},
|
|
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 && msg.data.hasOwnProperty('state')) {
|
|
116
|
-
const input = msg.data['state'].toString('hex');
|
|
117
|
-
const decoded = philips.decodeGradientColors(input, opts);
|
|
118
|
-
if (decoded.color_mode === 'gradient') {
|
|
119
|
-
return {gradient: decoded.colors};
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
return {};
|
|
123
|
-
},
|
|
124
|
-
};
|
|
101
|
+
exposes.enum('effect', ea.SET, [
|
|
102
|
+
'blink', 'breathe', 'okay', 'channel_change',
|
|
103
|
+
'candle', 'fireplace', 'colorloop',
|
|
104
|
+
'finish_effect', 'stop_effect', 'stop_hue_effect',
|
|
105
|
+
]),
|
|
106
|
+
]);
|
|
107
|
+
return result;
|
|
125
108
|
},
|
|
126
109
|
};
|
|
127
110
|
|
|
@@ -203,6 +186,71 @@ const gradientScenes = {
|
|
|
203
186
|
'crystalline': '5001040013500000006ea96a92a85e58074e18543d9cf3332800',
|
|
204
187
|
};
|
|
205
188
|
|
|
189
|
+
const hueEffects = {
|
|
190
|
+
'candle': '21000101',
|
|
191
|
+
'fireplace': '21000102',
|
|
192
|
+
'colorloop': '21000103',
|
|
193
|
+
'stop_hue_effect': '200000',
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
const fzLocal = {
|
|
197
|
+
hue_tap_dial: {
|
|
198
|
+
cluster: 'manuSpecificPhilips',
|
|
199
|
+
type: 'commandHueNotification',
|
|
200
|
+
options: [exposes.options.simulated_brightness()],
|
|
201
|
+
convert: (model, msg, publish, options, meta) => {
|
|
202
|
+
const buttonLookup = {1: 'button_1', 2: 'button_2', 3: 'button_3', 4: 'button_4', 20: 'dial'};
|
|
203
|
+
const button = buttonLookup[msg.data['button']];
|
|
204
|
+
const typeLookup = {0: 'press', 1: 'hold', 2: 'press_release', 3: 'hold_release'};
|
|
205
|
+
const type = typeLookup[msg.data['type']];
|
|
206
|
+
const direction = msg.data['unknown2'] <127 ? 'right' : 'left';
|
|
207
|
+
const time = msg.data['time'];
|
|
208
|
+
const payload = {};
|
|
209
|
+
|
|
210
|
+
if (button === 'dial') {
|
|
211
|
+
const adjustedTime = direction === 'right' ? time : 256 - time;
|
|
212
|
+
const dialType = 'rotate';
|
|
213
|
+
const speed = adjustedTime <= 25 ? 'step' : adjustedTime <= 75 ? 'slow' : 'fast';
|
|
214
|
+
payload.action = `${button}_${dialType}_${direction}_${speed}`;
|
|
215
|
+
|
|
216
|
+
// simulated brightness
|
|
217
|
+
if (options.simulated_brightness) {
|
|
218
|
+
const opts = options.simulated_brightness;
|
|
219
|
+
const deltaOpts = typeof opts === 'object' && opts.hasOwnProperty('delta') ? opts.delta : 35;
|
|
220
|
+
const delta = direction === 'right' ? deltaOpts : deltaOpts * -1;
|
|
221
|
+
const brightness = globalStore.getValue(msg.endpoint, 'brightness', 255) + delta;
|
|
222
|
+
payload.brightness = utils.numberWithinRange(brightness, 0, 255);
|
|
223
|
+
globalStore.putValue(msg.endpoint, 'brightness', payload.brightness);
|
|
224
|
+
}
|
|
225
|
+
} else {
|
|
226
|
+
payload.action = `${button}_${type}`;
|
|
227
|
+
// duration
|
|
228
|
+
if (type === 'press') globalStore.putValue(msg.endpoint, 'press_start', Date.now());
|
|
229
|
+
else if (type === 'hold' || type === 'hold_release') {
|
|
230
|
+
payload.action_duration = (Date.now() - globalStore.getValue(msg.endpoint, 'press_start')) / 1000;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
return payload;
|
|
234
|
+
},
|
|
235
|
+
},
|
|
236
|
+
gradient: (opts = {reverse: false}) => {
|
|
237
|
+
return {
|
|
238
|
+
cluster: 'manuSpecificPhilips2',
|
|
239
|
+
type: ['attributeReport', 'readResponse'],
|
|
240
|
+
convert: (model, msg, publish, options, meta) => {
|
|
241
|
+
if (msg.data && msg.data.hasOwnProperty('state')) {
|
|
242
|
+
const input = msg.data['state'].toString('hex');
|
|
243
|
+
const decoded = philips.decodeGradientColors(input, opts);
|
|
244
|
+
if (decoded.color_mode === 'gradient') {
|
|
245
|
+
return {gradient: decoded.colors};
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
return {};
|
|
249
|
+
},
|
|
250
|
+
};
|
|
251
|
+
},
|
|
252
|
+
};
|
|
253
|
+
|
|
206
254
|
const tzLocal = {
|
|
207
255
|
gradient_scene: {
|
|
208
256
|
key: ['gradient_scene'],
|
|
@@ -226,6 +274,137 @@ const tzLocal = {
|
|
|
226
274
|
},
|
|
227
275
|
};
|
|
228
276
|
},
|
|
277
|
+
effect: {
|
|
278
|
+
key: ['effect'],
|
|
279
|
+
convertSet: async (entity, key, value, meta) => {
|
|
280
|
+
if (Object.keys(hueEffects).includes(value.toLowerCase())) {
|
|
281
|
+
await entity.command('manuSpecificPhilips2', 'multiColor', {data: Buffer.from(hueEffects[value.toLowerCase()], 'hex')});
|
|
282
|
+
} else {
|
|
283
|
+
return await tz.effect.convertSet(entity, key, value, meta);
|
|
284
|
+
}
|
|
285
|
+
},
|
|
286
|
+
},
|
|
287
|
+
hue_power_on_behavior: {
|
|
288
|
+
key: ['hue_power_on_behavior'],
|
|
289
|
+
convertSet: async (entity, key, value, meta) => {
|
|
290
|
+
if (value === 'default') {
|
|
291
|
+
value = 'on';
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
let supports = {colorTemperature: false, colorXY: false};
|
|
295
|
+
if (entity.constructor.name === 'Endpoint' && entity.supportsInputCluster('lightingColorCtrl')) {
|
|
296
|
+
const readResult = await entity.read('lightingColorCtrl', ['colorCapabilities']);
|
|
297
|
+
supports = {
|
|
298
|
+
colorTemperature: (readResult.colorCapabilities & 1 << 4) > 0,
|
|
299
|
+
colorXY: (readResult.colorCapabilities & 1 << 3) > 0,
|
|
300
|
+
};
|
|
301
|
+
} else if (entity.constructor.name === 'Group') {
|
|
302
|
+
supports = {colorTemperature: true, colorXY: true};
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
if (value === 'off') {
|
|
306
|
+
await entity.write('genOnOff', {0x4003: {value: 0x00, type: 0x30}});
|
|
307
|
+
} else if (value === 'recover') {
|
|
308
|
+
await entity.write('genOnOff', {0x4003: {value: 0xff, type: 0x30}});
|
|
309
|
+
await entity.write('genLevelCtrl', {0x4000: {value: 0xff, type: 0x20}});
|
|
310
|
+
|
|
311
|
+
if (supports.colorTemperature) {
|
|
312
|
+
await entity.write('lightingColorCtrl', {0x4010: {value: 0xffff, type: 0x21}});
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
if (supports.colorXY) {
|
|
316
|
+
await entity.write('lightingColorCtrl', {0x0003: {value: 0xffff, type: 0x21}}, manufacturerOptions);
|
|
317
|
+
await entity.write('lightingColorCtrl', {0x0004: {value: 0xffff, type: 0x21}}, manufacturerOptions);
|
|
318
|
+
}
|
|
319
|
+
} else if (value === 'on') {
|
|
320
|
+
await entity.write('genOnOff', {0x4003: {value: 0x01, type: 0x30}});
|
|
321
|
+
|
|
322
|
+
let brightness = meta.message.hasOwnProperty('hue_power_on_brightness') ?
|
|
323
|
+
meta.message.hue_power_on_brightness : 0xfe;
|
|
324
|
+
if (brightness === 255) {
|
|
325
|
+
// 255 (0xFF) is the value for recover, therefore set it to 254 (0xFE)
|
|
326
|
+
brightness = 254;
|
|
327
|
+
}
|
|
328
|
+
await entity.write('genLevelCtrl', {0x4000: {value: brightness, type: 0x20}});
|
|
329
|
+
|
|
330
|
+
if (entity.supportsInputCluster('lightingColorCtrl')) {
|
|
331
|
+
if (
|
|
332
|
+
meta.message.hasOwnProperty('hue_power_on_color_temperature') &&
|
|
333
|
+
meta.message.hasOwnProperty('hue_power_on_color')
|
|
334
|
+
) {
|
|
335
|
+
meta.logger.error(`Provide either color temperature or color, not both`);
|
|
336
|
+
} else if (meta.message.hasOwnProperty('hue_power_on_color_temperature')) {
|
|
337
|
+
const colortemp = meta.message.hue_power_on_color_temperature;
|
|
338
|
+
await entity.write('lightingColorCtrl', {0x4010: {value: colortemp, type: 0x21}});
|
|
339
|
+
// Set color to default
|
|
340
|
+
if (supports.colorXY) {
|
|
341
|
+
await entity.write('lightingColorCtrl', {0x0003: {value: 0xFFFF, type: 0x21}}, manufacturerOptions);
|
|
342
|
+
await entity.write('lightingColorCtrl', {0x0004: {value: 0xFFFF, type: 0x21}}, manufacturerOptions);
|
|
343
|
+
}
|
|
344
|
+
} else if (meta.message.hasOwnProperty('hue_power_on_color')) {
|
|
345
|
+
const colorXY = libColor.ColorRGB.fromHex(meta.message.hue_power_on_color).toXY();
|
|
346
|
+
value = {x: utils.mapNumberRange(colorXY.x, 0, 1, 0, 65535), y: utils.mapNumberRange(colorXY.y, 0, 1, 0, 65535)};
|
|
347
|
+
|
|
348
|
+
// Set colortemp to default
|
|
349
|
+
if (supports.colorTemperature) {
|
|
350
|
+
await entity.write('lightingColorCtrl', {0x4010: {value: 366, type: 0x21}});
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
await entity.write('lightingColorCtrl', {0x0003: {value: value.x, type: 0x21}}, manufacturerOptions);
|
|
354
|
+
await entity.write('lightingColorCtrl', {0x0004: {value: value.y, type: 0x21}}, manufacturerOptions);
|
|
355
|
+
} else {
|
|
356
|
+
// Set defaults for colortemp and color
|
|
357
|
+
if (supports.colorTemperature) {
|
|
358
|
+
await entity.write('lightingColorCtrl', {0x4010: {value: 366, type: 0x21}});
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
if (supports.colorXY) {
|
|
362
|
+
await entity.write('lightingColorCtrl', {0x0003: {value: 0xFFFF, type: 0x21}}, manufacturerOptions);
|
|
363
|
+
await entity.write('lightingColorCtrl', {0x0004: {value: 0xFFFF, type: 0x21}}, manufacturerOptions);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
return {state: {hue_power_on_behavior: value}};
|
|
370
|
+
},
|
|
371
|
+
},
|
|
372
|
+
hue_power_on_error: {
|
|
373
|
+
key: ['hue_power_on_brightness', 'hue_power_on_color_temperature', 'hue_power_on_color'],
|
|
374
|
+
convertSet: async (entity, key, value, meta) => {
|
|
375
|
+
if (!meta.message.hasOwnProperty('hue_power_on_behavior')) {
|
|
376
|
+
throw new Error(`Provide a value for 'hue_power_on_behavior'`);
|
|
377
|
+
}
|
|
378
|
+
},
|
|
379
|
+
},
|
|
380
|
+
hue_motion_sensitivity: {
|
|
381
|
+
// motion detect sensitivity, philips specific
|
|
382
|
+
key: ['motion_sensitivity'],
|
|
383
|
+
convertSet: async (entity, key, value, meta) => {
|
|
384
|
+
// make sure you write to second endpoint!
|
|
385
|
+
const lookup = {'low': 0, 'medium': 1, 'high': 2, 'very_high': 3, 'max': 4};
|
|
386
|
+
value = value.toLowerCase();
|
|
387
|
+
utils.validateValue(value, Object.keys(lookup));
|
|
388
|
+
|
|
389
|
+
const payload = {48: {value: lookup[value], type: 32}};
|
|
390
|
+
await entity.write('msOccupancySensing', payload, manufacturerOptions);
|
|
391
|
+
return {state: {motion_sensitivity: value}};
|
|
392
|
+
},
|
|
393
|
+
convertGet: async (entity, key, meta) => {
|
|
394
|
+
await entity.read('msOccupancySensing', [48], manufacturerOptions);
|
|
395
|
+
},
|
|
396
|
+
},
|
|
397
|
+
hue_motion_led_indication: {
|
|
398
|
+
key: ['led_indication'],
|
|
399
|
+
convertSet: async (entity, key, value, meta) => {
|
|
400
|
+
const payload = {0x0033: {value, type: 0x10}};
|
|
401
|
+
await entity.write('genBasic', payload, manufacturerOptions);
|
|
402
|
+
return {state: {led_indication: value}};
|
|
403
|
+
},
|
|
404
|
+
convertGet: async (entity, key, meta) => {
|
|
405
|
+
await entity.read('genBasic', [0x0033], manufacturerOptions);
|
|
406
|
+
},
|
|
407
|
+
},
|
|
229
408
|
};
|
|
230
409
|
|
|
231
410
|
module.exports = [
|
|
@@ -787,7 +966,9 @@ module.exports = [
|
|
|
787
966
|
model: '929001953101',
|
|
788
967
|
vendor: 'Philips',
|
|
789
968
|
description: 'Hue White and Color Ambiance GU10',
|
|
790
|
-
extend: hueExtend.light_onoff_brightness_colortemp_color(
|
|
969
|
+
extend: hueExtend.light_onoff_brightness_colortemp_color({
|
|
970
|
+
colorTempRange: [153, 500], disableHueEffects: false,
|
|
971
|
+
}),
|
|
791
972
|
},
|
|
792
973
|
{
|
|
793
974
|
zigbeeModel: ['LWA003', 'LWW002'],
|
|
@@ -1060,7 +1241,9 @@ module.exports = [
|
|
|
1060
1241
|
model: '929002471601',
|
|
1061
1242
|
vendor: 'Philips',
|
|
1062
1243
|
description: 'Hue white and color ambiance E26/E27 1600lm',
|
|
1063
|
-
extend: hueExtend.light_onoff_brightness_colortemp_color({
|
|
1244
|
+
extend: hueExtend.light_onoff_brightness_colortemp_color({
|
|
1245
|
+
colorTempRange: [153, 500], disableHueEffects: false,
|
|
1246
|
+
}),
|
|
1064
1247
|
},
|
|
1065
1248
|
{
|
|
1066
1249
|
zigbeeModel: ['LCA009'],
|
|
@@ -1179,7 +1362,7 @@ module.exports = [
|
|
|
1179
1362
|
model: '929001953301',
|
|
1180
1363
|
vendor: 'Philips',
|
|
1181
1364
|
description: 'Hue white ambiance GU10 with Bluetooth',
|
|
1182
|
-
extend: hueExtend.light_onoff_brightness_colortemp({colorTempRange: [153,
|
|
1365
|
+
extend: hueExtend.light_onoff_brightness_colortemp({colorTempRange: [153, 500], disableHueEffects: false}),
|
|
1183
1366
|
},
|
|
1184
1367
|
{
|
|
1185
1368
|
zigbeeModel: ['LTD005'],
|
|
@@ -2039,7 +2222,7 @@ module.exports = [
|
|
|
2039
2222
|
exposes.enum('motion_sensitivity', ea.ALL, ['low', 'medium', 'high']),
|
|
2040
2223
|
exposes.binary('led_indication', ea.ALL, true, false).withDescription('Blink green LED on motion detection'),
|
|
2041
2224
|
exposes.numeric('occupancy_timeout', ea.ALL).withUnit('second').withValueMin(0).withValueMax(65535)],
|
|
2042
|
-
toZigbee: [tz.occupancy_timeout,
|
|
2225
|
+
toZigbee: [tz.occupancy_timeout, tzLocal.hue_motion_sensitivity, tzLocal.hue_motion_led_indication],
|
|
2043
2226
|
endpoint: (device) => {
|
|
2044
2227
|
return {'default': 2, 'ep1': 1, 'ep2': 2};
|
|
2045
2228
|
},
|
|
@@ -2068,7 +2251,7 @@ module.exports = [
|
|
|
2068
2251
|
exposes.enum('motion_sensitivity', ea.ALL, ['low', 'medium', 'high']),
|
|
2069
2252
|
exposes.binary('led_indication', ea.ALL, true, false).withDescription('Blink green LED on motion detection'),
|
|
2070
2253
|
exposes.numeric('occupancy_timeout', ea.ALL).withUnit('second').withValueMin(0).withValueMax(65535)],
|
|
2071
|
-
toZigbee: [tz.occupancy_timeout,
|
|
2254
|
+
toZigbee: [tz.occupancy_timeout, tzLocal.hue_motion_sensitivity, tzLocal.hue_motion_led_indication],
|
|
2072
2255
|
endpoint: (device) => {
|
|
2073
2256
|
return {'default': 2, 'ep1': 1, 'ep2': 2};
|
|
2074
2257
|
},
|
|
@@ -2097,7 +2280,7 @@ module.exports = [
|
|
|
2097
2280
|
exposes.enum('motion_sensitivity', ea.ALL, ['low', 'medium', 'high', 'very_high', 'max']),
|
|
2098
2281
|
exposes.binary('led_indication', ea.ALL, true, false).withDescription('Blink green LED on motion detection'),
|
|
2099
2282
|
exposes.numeric('occupancy_timeout', ea.ALL).withUnit('second').withValueMin(0).withValueMax(65535)],
|
|
2100
|
-
toZigbee: [tz.occupancy_timeout,
|
|
2283
|
+
toZigbee: [tz.occupancy_timeout, tzLocal.hue_motion_sensitivity, tzLocal.hue_motion_led_indication],
|
|
2101
2284
|
configure: async (device, coordinatorEndpoint, logger) => {
|
|
2102
2285
|
const endpoint = device.getEndpoint(2);
|
|
2103
2286
|
const binds = ['genPowerCfg', 'msIlluminanceMeasurement', 'msTemperatureMeasurement', 'msOccupancySensing'];
|
|
@@ -2123,7 +2306,7 @@ module.exports = [
|
|
|
2123
2306
|
exposes.enum('motion_sensitivity', ea.ALL, ['low', 'medium', 'high', 'very_high', 'max']),
|
|
2124
2307
|
exposes.binary('led_indication', ea.ALL, true, false).withDescription('Blink green LED on motion detection'),
|
|
2125
2308
|
exposes.numeric('occupancy_timeout', ea.ALL).withUnit('second').withValueMin(0).withValueMax(65535)],
|
|
2126
|
-
toZigbee: [tz.occupancy_timeout,
|
|
2309
|
+
toZigbee: [tz.occupancy_timeout, tzLocal.hue_motion_sensitivity, tzLocal.hue_motion_led_indication],
|
|
2127
2310
|
configure: async (device, coordinatorEndpoint, logger) => {
|
|
2128
2311
|
const endpoint = device.getEndpoint(2);
|
|
2129
2312
|
const binds = ['genPowerCfg', 'msIlluminanceMeasurement', 'msTemperatureMeasurement', 'msOccupancySensing'];
|
|
@@ -2144,7 +2327,7 @@ module.exports = [
|
|
|
2144
2327
|
vendor: 'Philips',
|
|
2145
2328
|
description: 'Hue smart plug - EU',
|
|
2146
2329
|
extend: extend.switch(),
|
|
2147
|
-
toZigbee: [tz.on_off].concat([
|
|
2330
|
+
toZigbee: [tz.on_off].concat([tzLocal.hue_power_on_behavior, tzLocal.hue_power_on_error]),
|
|
2148
2331
|
configure: async (device, coordinatorEndpoint, logger) => {
|
|
2149
2332
|
const endpoint = device.getEndpoint(11);
|
|
2150
2333
|
await reporting.bind(endpoint, coordinatorEndpoint, ['genOnOff']);
|
|
@@ -2158,7 +2341,7 @@ module.exports = [
|
|
|
2158
2341
|
vendor: 'Philips',
|
|
2159
2342
|
description: 'Hue smart plug bluetooth',
|
|
2160
2343
|
extend: extend.switch(),
|
|
2161
|
-
toZigbee: [tz.on_off].concat([
|
|
2344
|
+
toZigbee: [tz.on_off].concat([tzLocal.hue_power_on_behavior, tzLocal.hue_power_on_error]),
|
|
2162
2345
|
configure: async (device, coordinatorEndpoint, logger) => {
|
|
2163
2346
|
const endpoint = device.getEndpoint(11);
|
|
2164
2347
|
await reporting.bind(endpoint, coordinatorEndpoint, ['genOnOff']);
|
|
@@ -2172,7 +2355,7 @@ module.exports = [
|
|
|
2172
2355
|
vendor: 'Philips',
|
|
2173
2356
|
description: 'Hue smart plug - UK',
|
|
2174
2357
|
extend: extend.switch(),
|
|
2175
|
-
toZigbee: [tz.on_off].concat([
|
|
2358
|
+
toZigbee: [tz.on_off].concat([tzLocal.hue_power_on_behavior, tzLocal.hue_power_on_error]),
|
|
2176
2359
|
configure: async (device, coordinatorEndpoint, logger) => {
|
|
2177
2360
|
const endpoint = device.getEndpoint(11);
|
|
2178
2361
|
await reporting.bind(endpoint, coordinatorEndpoint, ['genOnOff']);
|
|
@@ -2186,7 +2369,7 @@ module.exports = [
|
|
|
2186
2369
|
vendor: 'Philips',
|
|
2187
2370
|
description: 'Hue smart plug - AU',
|
|
2188
2371
|
extend: extend.switch(),
|
|
2189
|
-
toZigbee: [tz.on_off].concat([
|
|
2372
|
+
toZigbee: [tz.on_off].concat([tzLocal.hue_power_on_behavior, tzLocal.hue_power_on_error]),
|
|
2190
2373
|
configure: async (device, coordinatorEndpoint, logger) => {
|
|
2191
2374
|
const endpoint = device.getEndpoint(11);
|
|
2192
2375
|
await reporting.bind(endpoint, coordinatorEndpoint, ['genOnOff']);
|
|
@@ -2200,7 +2383,7 @@ module.exports = [
|
|
|
2200
2383
|
vendor: 'Philips',
|
|
2201
2384
|
description: 'Hue smart plug - AU',
|
|
2202
2385
|
extend: extend.switch(),
|
|
2203
|
-
toZigbee: [tz.on_off].concat([
|
|
2386
|
+
toZigbee: [tz.on_off].concat([tzLocal.hue_power_on_behavior, tzLocal.hue_power_on_error]),
|
|
2204
2387
|
configure: async (device, coordinatorEndpoint, logger) => {
|
|
2205
2388
|
const endpoint = device.getEndpoint(11);
|
|
2206
2389
|
await reporting.bind(endpoint, coordinatorEndpoint, ['genOnOff']);
|
|
@@ -2214,7 +2397,7 @@ module.exports = [
|
|
|
2214
2397
|
vendor: 'Philips',
|
|
2215
2398
|
description: 'Hue smart plug - CH',
|
|
2216
2399
|
extend: extend.switch(),
|
|
2217
|
-
toZigbee: [tz.on_off].concat([
|
|
2400
|
+
toZigbee: [tz.on_off].concat([tzLocal.hue_power_on_behavior, tzLocal.hue_power_on_error]),
|
|
2218
2401
|
configure: async (device, coordinatorEndpoint, logger) => {
|
|
2219
2402
|
const endpoint = device.getEndpoint(11);
|
|
2220
2403
|
await reporting.bind(endpoint, coordinatorEndpoint, ['genOnOff']);
|
|
@@ -2228,7 +2411,7 @@ module.exports = [
|
|
|
2228
2411
|
vendor: 'Philips',
|
|
2229
2412
|
description: 'Hue smart plug',
|
|
2230
2413
|
extend: extend.switch(),
|
|
2231
|
-
toZigbee: [tz.on_off].concat([
|
|
2414
|
+
toZigbee: [tz.on_off].concat([tzLocal.hue_power_on_behavior, tzLocal.hue_power_on_error]),
|
|
2232
2415
|
configure: async (device, coordinatorEndpoint, logger) => {
|
|
2233
2416
|
const endpoint = device.getEndpoint(11);
|
|
2234
2417
|
await reporting.bind(endpoint, coordinatorEndpoint, ['genOnOff']);
|
|
@@ -2242,7 +2425,7 @@ module.exports = [
|
|
|
2242
2425
|
vendor: 'Philips',
|
|
2243
2426
|
description: 'Hue smart plug - EU',
|
|
2244
2427
|
extend: extend.switch(),
|
|
2245
|
-
toZigbee: [tz.on_off].concat([
|
|
2428
|
+
toZigbee: [tz.on_off].concat([tzLocal.hue_power_on_behavior, tzLocal.hue_power_on_error]),
|
|
2246
2429
|
configure: async (device, coordinatorEndpoint, logger) => {
|
|
2247
2430
|
const endpoint = device.getEndpoint(11);
|
|
2248
2431
|
await reporting.bind(endpoint, coordinatorEndpoint, ['genOnOff']);
|
package/devices/tuya.js
CHANGED
|
@@ -1345,6 +1345,30 @@ module.exports = [
|
|
|
1345
1345
|
{vendor: 'Moes', model: 'ZS-EUD_3gang'},
|
|
1346
1346
|
],
|
|
1347
1347
|
},
|
|
1348
|
+
{
|
|
1349
|
+
fingerprint: tuya.fingerprint('TS0601', ['_TZE200_p0gzbqct']),
|
|
1350
|
+
model: 'TS0601_dimmer_knob',
|
|
1351
|
+
vendor: 'TuYa',
|
|
1352
|
+
description: 'Zigbee smart knob dimmer',
|
|
1353
|
+
fromZigbee: [tuya.fz.datapoints],
|
|
1354
|
+
toZigbee: [tuya.tz.datapoints],
|
|
1355
|
+
configure: tuya.configureMagicPacket,
|
|
1356
|
+
exposes: [tuya.exposes.lightBrightness().withMinBrightness().setAccess('min_brightness', ea.STATE_SET), tuya.exposes.lightType(),
|
|
1357
|
+
tuya.exposes.indicatorModeNoneRelayPos()],
|
|
1358
|
+
meta: {
|
|
1359
|
+
tuyaDatapoints: [
|
|
1360
|
+
[1, 'state', tuya.valueConverter.onOff, {skip: tuya.skip.stateOnAndBrightnessPresent}],
|
|
1361
|
+
[2, 'brightness', tuya.valueConverter.scale0_254to0_1000],
|
|
1362
|
+
[3, 'min_brightness', tuya.valueConverter.scale0_254to0_1000],
|
|
1363
|
+
[4, 'light_type', tuya.valueConverter.lightType],
|
|
1364
|
+
[21, 'indicator_mode', tuya.valueConverterBasic.lookup({0: 'none', 1: 'relay', 2: 'pos'})],
|
|
1365
|
+
],
|
|
1366
|
+
},
|
|
1367
|
+
whiteLabel: [
|
|
1368
|
+
{vendor: 'Moes', model: 'WS-SY-EURD'},
|
|
1369
|
+
{vendor: 'Moes', model: 'WS-SY-EURD-WH-MS'},
|
|
1370
|
+
],
|
|
1371
|
+
},
|
|
1348
1372
|
{
|
|
1349
1373
|
fingerprint: [{modelID: 'TS011F', manufacturerName: '_TZ3000_oiymh3qu'}],
|
|
1350
1374
|
model: 'TS011F_socket_module',
|
|
@@ -2515,6 +2539,30 @@ module.exports = [
|
|
|
2515
2539
|
],
|
|
2516
2540
|
},
|
|
2517
2541
|
},
|
|
2542
|
+
{
|
|
2543
|
+
fingerprint: tuya.fingerprint('TS0601', ['_TZE200_m9skfctm']),
|
|
2544
|
+
model: 'TS0601_smoke_2',
|
|
2545
|
+
vendor: 'TuYa',
|
|
2546
|
+
description: 'Photoelectric smoke detector',
|
|
2547
|
+
fromZigbee: [tuya.fz.datapoints],
|
|
2548
|
+
toZigbee: [tuya.tz.datapoints],
|
|
2549
|
+
onEvent: tuya.onEventSetTime,
|
|
2550
|
+
configure: tuya.configureMagicPacket,
|
|
2551
|
+
exposes: [
|
|
2552
|
+
e.smoke(), e.battery(), e.test(),
|
|
2553
|
+
exposes.numeric('smoke_concentration', ea.STATE).withUnit('ppm').withDescription('Parts per million of smoke detected'),
|
|
2554
|
+
exposes.binary('device_fault', ea.STATE, true, false).withDescription('Indicates a fault with the device'),
|
|
2555
|
+
],
|
|
2556
|
+
meta: {
|
|
2557
|
+
tuyaDatapoints: [
|
|
2558
|
+
[1, 'smoke', tuya.valueConverter.true0ElseFalse],
|
|
2559
|
+
[2, 'smoke_concentration', tuya.valueConverter.divideBy10],
|
|
2560
|
+
[11, 'device_fault', tuya.valueConverter.raw],
|
|
2561
|
+
[15, 'battery', tuya.valueConverter.raw],
|
|
2562
|
+
[101, 'test', tuya.valueConverter.raw],
|
|
2563
|
+
],
|
|
2564
|
+
},
|
|
2565
|
+
},
|
|
2518
2566
|
{
|
|
2519
2567
|
zigbeeModel: ['5p1vj8r'],
|
|
2520
2568
|
fingerprint: tuya.fingerprint('TS0601', ['_TZE200_t5p1vj8r', '_TZE200_uebojraa', '_TZE200_vzekyi4c', '_TZE200_yh7aoahi',
|
|
@@ -2548,6 +2596,24 @@ module.exports = [
|
|
|
2548
2596
|
exposes.binary('silence_siren', ea.STATE_SET, true, false).withDescription('Silence the siren')],
|
|
2549
2597
|
onEvent: tuya.onEventsetTime,
|
|
2550
2598
|
},
|
|
2599
|
+
{
|
|
2600
|
+
fingerprint: tuya.fingerprint('TS0601', ['_TZE204_cjbofhxw']),
|
|
2601
|
+
model: 'TS0601_clamp_meter',
|
|
2602
|
+
vendor: 'TuYa',
|
|
2603
|
+
description: 'Clamp meter',
|
|
2604
|
+
fromZigbee: [tuya.fz.datapoints, tuya.fz.gateway_connection_status],
|
|
2605
|
+
toZigbee: [tuya.tz.datapoints],
|
|
2606
|
+
configure: tuya.configureMagicPacket,
|
|
2607
|
+
exposes: [e.current(), e.power(), e.voltage(), e.energy()],
|
|
2608
|
+
meta: {
|
|
2609
|
+
tuyaDatapoints: [
|
|
2610
|
+
[18, 'current', tuya.valueConverter.divideBy1000],
|
|
2611
|
+
[19, 'power', tuya.valueConverter.divideBy10],
|
|
2612
|
+
[20, 'voltage', tuya.valueConverter.divideBy10],
|
|
2613
|
+
[101, 'energy', tuya.valueConverter.divideBy1000],
|
|
2614
|
+
],
|
|
2615
|
+
},
|
|
2616
|
+
},
|
|
2551
2617
|
{
|
|
2552
2618
|
fingerprint: tuya.fingerprint('TS0601', ['_TZE200_bkkmqmyo', '_TZE200_eaac7dkw']),
|
|
2553
2619
|
model: 'TS0601_din_1',
|
package/devices/ubisys.js
CHANGED
|
@@ -17,7 +17,6 @@ const manufacturerOptions = {
|
|
|
17
17
|
*/
|
|
18
18
|
ubisys: {manufacturerCode: herdsman.Zcl.ManufacturerCode.UBISYS},
|
|
19
19
|
ubisysNull: {manufacturerCode: null},
|
|
20
|
-
tint: {manufacturerCode: herdsman.Zcl.ManufacturerCode.MUELLER_LICHT_INT},
|
|
21
20
|
};
|
|
22
21
|
|
|
23
22
|
const ubisysOnEventReadCurrentSummDelivered = async function(type, data, devic) {
|
package/lib/ota/common.js
CHANGED
|
@@ -27,7 +27,6 @@ const eblImageSignature = 0xe350;
|
|
|
27
27
|
|
|
28
28
|
const gblTagHeader = 0xeb17a603;
|
|
29
29
|
const gblTagEnd = 0xfc0404fc;
|
|
30
|
-
const gblPadding = 0x0;
|
|
31
30
|
|
|
32
31
|
function getOTAEndpoint(device) {
|
|
33
32
|
return device.endpoints.find((e) => e.supportsOutputCluster('genOta'));
|
|
@@ -145,10 +144,6 @@ function validateSilabsGbl(data) {
|
|
|
145
144
|
continue;
|
|
146
145
|
}
|
|
147
146
|
|
|
148
|
-
for (let position2 = position; position2 < dataLength; position2++) {
|
|
149
|
-
assert(data.readUInt8(position2) === gblPadding, `Image padding contains invalid bytes`);
|
|
150
|
-
}
|
|
151
|
-
|
|
152
147
|
const calculatedCrc32 = crc32.unsigned(data.slice(0, position));
|
|
153
148
|
|
|
154
149
|
assert(calculatedCrc32 === validSilabsCrc, `Image CRC-32 is invalid`);
|
package/lib/tuya.js
CHANGED
|
@@ -1163,6 +1163,8 @@ const tuyaExposes = {
|
|
|
1163
1163
|
backlightModeOffNormalInverted: () => exposes.enum('backlight_mode', ea.ALL, ['off', 'normal', 'inverted'])
|
|
1164
1164
|
.withDescription('Mode of the backlight'),
|
|
1165
1165
|
indicatorMode: () => exposes.enum('indicator_mode', ea.ALL, ['off', 'off/on', 'on/off', 'on']).withDescription('LED indicator mode'),
|
|
1166
|
+
indicatorModeNoneRelayPos: () => exposes.enum('indicator_mode', ea.ALL, ['none', 'relay', 'pos'])
|
|
1167
|
+
.withDescription('Mode of the indicator light'),
|
|
1166
1168
|
powerOutageMemory: () => exposes.enum('power_outage_memory', ea.ALL, ['on', 'off', 'restore'])
|
|
1167
1169
|
.withDescription('Recover state after power outage'),
|
|
1168
1170
|
batteryState: () => exposes.enum('battery_state', ea.STATE, ['low', 'medium', 'high']).withDescription('State of the battery'),
|
|
@@ -1616,6 +1618,18 @@ const tuyaTz = {
|
|
|
1616
1618
|
};
|
|
1617
1619
|
|
|
1618
1620
|
const tuyaFz = {
|
|
1621
|
+
gateway_connection_status: {
|
|
1622
|
+
cluster: 'manuSpecificTuya',
|
|
1623
|
+
type: ['commandMcuGatewayConnectionStatus'],
|
|
1624
|
+
convert: async (model, msg, publish, options, meta) => {
|
|
1625
|
+
// "payload" can have the following values:
|
|
1626
|
+
// 0x00: The gateway is not connected to the internet.
|
|
1627
|
+
// 0x01: The gateway is connected to the internet.
|
|
1628
|
+
// 0x02: The request timed out after three seconds.
|
|
1629
|
+
const payload = {payloadSize: 1, payload: 1};
|
|
1630
|
+
await msg.endpoint.command('manuSpecificTuya', 'mcuGatewayConnectionStatus', payload, {});
|
|
1631
|
+
},
|
|
1632
|
+
},
|
|
1619
1633
|
power_on_behavior: {
|
|
1620
1634
|
cluster: 'genOnOff',
|
|
1621
1635
|
type: ['attributeReport', 'readResponse'],
|