zigbee-herdsman-converters 15.0.9 → 15.0.11

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.
@@ -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).
@@ -369,8 +369,16 @@ const converters = {
369
369
  warning_simple: {
370
370
  key: ['alarm'],
371
371
  convertSet: async (entity, key, value, meta) => {
372
- const alarmState = (value === 'OFF' ? 0 : 1);
373
- const info = (3 << 6) + ((alarmState) << 2);
372
+ const alarmState = (value === 'alarm' || value === 'OFF' ? 0 : 1);
373
+
374
+ let info;
375
+ // For Develco SMSZB-120, introduced change in fw 4.0.5, tested backward with 4.0.4
376
+ if (['SMSZB-120'].includes(meta.mapped.model)) {
377
+ info = ((alarmState) << 7) + ((alarmState) << 6);
378
+ } else {
379
+ info = (3 << 6) + ((alarmState) << 2);
380
+ }
381
+
374
382
  await entity.command(
375
383
  'ssIasWd',
376
384
  'startWarning',
@@ -2047,127 +2055,6 @@ const converters = {
2047
2055
  return await converters.light_color_colortemp.convertGet(entity, key, meta);
2048
2056
  },
2049
2057
  },
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
2058
  aqara_motion_sensitivity: {
2172
2059
  key: ['motion_sensitivity'],
2173
2060
  convertSet: async (entity, key, value, meta) => {
@@ -7166,6 +7053,17 @@ const converters = {
7166
7053
  }
7167
7054
  },
7168
7055
  },
7056
+ led_on_motion: {
7057
+ key: ['led_on_motion'],
7058
+ convertSet: async (entity, key, value, meta) => {
7059
+ await entity.write('ssIasZone', {0x4000: {value: value === true ? 1 : 0, type: 0x10}},
7060
+ {manufacturerCode: 4919});
7061
+ return {state: {led_on_motion: value}};
7062
+ },
7063
+ convertGet: async (entity, key, meta) => {
7064
+ await entity.read('ssIasZone', [0x4000], {manufacturerCode: 4919});
7065
+ },
7066
+ },
7169
7067
  // #endregion
7170
7068
 
7171
7069
  // #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 Zigbee motion sensor',
92
- fromZigbee: [fz.ias_occupancy_alarm_1, fz.ias_occupancy_alarm_1_with_timeout, fz.illuminance, fz.temperature],
93
- toZigbee: [],
94
- exposes: [e.occupancy(), e.battery_low(), e.tamper(), e.illuminance(), e.illuminance_lux(), e.temperature()],
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/fantem.js CHANGED
@@ -49,7 +49,6 @@ module.exports = [
49
49
  {
50
50
  fingerprint: [{modelID: 'TS0202', manufacturerName: '_TZ3210_rxqls8v0'},
51
51
  {modelID: 'TS0202', manufacturerName: '_TZ3210_zmy9hjay'},
52
- {modelID: 'TS0202', manufacturerName: '_TZ3000_6ygjfyll'},
53
52
  {modelID: 'TS0202', manufacturerName: '_TZ3210_wuhzzfqg'}],
54
53
  model: 'ZB003-X',
55
54
  vendor: 'Fantem',
package/devices/innr.js CHANGED
@@ -8,6 +8,21 @@ const ea = exposes.access;
8
8
  const ota = require('../lib/ota');
9
9
 
10
10
  module.exports = [
11
+ {
12
+ zigbeeModel: ['RC 210'],
13
+ model: 'RC 210',
14
+ vendor: 'Innr',
15
+ description: 'Remote control',
16
+ fromZigbee: [fz.command_on, fz.command_off, fz.command_move, fz.command_stop, fz.command_move_to_level,
17
+ fz.command_move_to_color_temp],
18
+ toZigbee: [],
19
+ exposes: [e.action(['on', 'off', 'brightness_move_up', 'brightness_move_down', 'brightness_stop', 'brightness_move_to_level',
20
+ 'color_temperature_move'])],
21
+ configure: async (device, coordinatorEndpoint, logger) => {
22
+ const ep = device.getEndpoint(1);
23
+ await reporting.bind(ep, coordinatorEndpoint, ['genBasic', 'genOnOff', 'genLevelCtrl', 'lightingColorCtrl']);
24
+ },
25
+ },
11
26
  {
12
27
  zigbeeModel: ['RC 250'],
13
28
  model: 'RC 250',
@@ -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 ( Bticino FC80CC )',
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 ( Bticino FC80RC ) ',
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).withValueMax(900)],
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/namron.js CHANGED
@@ -725,4 +725,12 @@ module.exports = [
725
725
  await reporting.bind(endpoint, coordinatorEndpoint, binds);
726
726
  },
727
727
  },
728
+ {
729
+ zigbeeModel: ['3802968'],
730
+ model: '3802968',
731
+ vendor: 'Namron',
732
+ description: 'LED Filament Flex 5W CCT E27 Clear',
733
+ extend: extend.light_onoff_brightness_colortemp({colorTempRange: [153, 555]}),
734
+ meta: {turnsOffAtBrightness1: true},
735
+ },
728
736
  ];
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
  ];
@@ -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,97 @@ 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([tz.hue_power_on_behavior, tz.hue_power_on_error]),
22
- }),
23
- light_onoff_brightness_colortemp: (options={}) => ({
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
- 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})
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, extraEffects: [], ...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: extendDontUse.light_onoff_brightness_colortemp_color({supportsHS: true, ...options}).exposes.concat([
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
- 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
- }),
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', 'sunrise',
104
+ 'finish_effect', 'stop_effect', 'stop_hue_effect',
105
+ ...options.extraEffects,
106
+ ]),
107
+ ]);
108
+ return result;
125
109
  },
126
110
  };
127
111
 
@@ -203,6 +187,73 @@ const gradientScenes = {
203
187
  'crystalline': '5001040013500000006ea96a92a85e58074e18543d9cf3332800',
204
188
  };
205
189
 
190
+ const hueEffects = {
191
+ 'candle': '21000101',
192
+ 'fireplace': '21000102',
193
+ 'colorloop': '21000103',
194
+ 'sunrise': '21000109',
195
+ 'sparkle': '2100010a',
196
+ 'stop_hue_effect': '200000',
197
+ };
198
+
199
+ const fzLocal = {
200
+ hue_tap_dial: {
201
+ cluster: 'manuSpecificPhilips',
202
+ type: 'commandHueNotification',
203
+ options: [exposes.options.simulated_brightness()],
204
+ convert: (model, msg, publish, options, meta) => {
205
+ const buttonLookup = {1: 'button_1', 2: 'button_2', 3: 'button_3', 4: 'button_4', 20: 'dial'};
206
+ const button = buttonLookup[msg.data['button']];
207
+ const typeLookup = {0: 'press', 1: 'hold', 2: 'press_release', 3: 'hold_release'};
208
+ const type = typeLookup[msg.data['type']];
209
+ const direction = msg.data['unknown2'] <127 ? 'right' : 'left';
210
+ const time = msg.data['time'];
211
+ const payload = {};
212
+
213
+ if (button === 'dial') {
214
+ const adjustedTime = direction === 'right' ? time : 256 - time;
215
+ const dialType = 'rotate';
216
+ const speed = adjustedTime <= 25 ? 'step' : adjustedTime <= 75 ? 'slow' : 'fast';
217
+ payload.action = `${button}_${dialType}_${direction}_${speed}`;
218
+
219
+ // simulated brightness
220
+ if (options.simulated_brightness) {
221
+ const opts = options.simulated_brightness;
222
+ const deltaOpts = typeof opts === 'object' && opts.hasOwnProperty('delta') ? opts.delta : 35;
223
+ const delta = direction === 'right' ? deltaOpts : deltaOpts * -1;
224
+ const brightness = globalStore.getValue(msg.endpoint, 'brightness', 255) + delta;
225
+ payload.brightness = utils.numberWithinRange(brightness, 0, 255);
226
+ globalStore.putValue(msg.endpoint, 'brightness', payload.brightness);
227
+ }
228
+ } else {
229
+ payload.action = `${button}_${type}`;
230
+ // duration
231
+ if (type === 'press') globalStore.putValue(msg.endpoint, 'press_start', Date.now());
232
+ else if (type === 'hold' || type === 'hold_release') {
233
+ payload.action_duration = (Date.now() - globalStore.getValue(msg.endpoint, 'press_start')) / 1000;
234
+ }
235
+ }
236
+ return payload;
237
+ },
238
+ },
239
+ gradient: (opts = {reverse: false}) => {
240
+ return {
241
+ cluster: 'manuSpecificPhilips2',
242
+ type: ['attributeReport', 'readResponse'],
243
+ convert: (model, msg, publish, options, meta) => {
244
+ if (msg.data && msg.data.hasOwnProperty('state')) {
245
+ const input = msg.data['state'].toString('hex');
246
+ const decoded = philips.decodeGradientColors(input, opts);
247
+ if (decoded.color_mode === 'gradient') {
248
+ return {gradient: decoded.colors};
249
+ }
250
+ }
251
+ return {};
252
+ },
253
+ };
254
+ },
255
+ };
256
+
206
257
  const tzLocal = {
207
258
  gradient_scene: {
208
259
  key: ['gradient_scene'],
@@ -226,6 +277,137 @@ const tzLocal = {
226
277
  },
227
278
  };
228
279
  },
280
+ effect: {
281
+ key: ['effect'],
282
+ convertSet: async (entity, key, value, meta) => {
283
+ if (Object.keys(hueEffects).includes(value.toLowerCase())) {
284
+ await entity.command('manuSpecificPhilips2', 'multiColor', {data: Buffer.from(hueEffects[value.toLowerCase()], 'hex')});
285
+ } else {
286
+ return await tz.effect.convertSet(entity, key, value, meta);
287
+ }
288
+ },
289
+ },
290
+ hue_power_on_behavior: {
291
+ key: ['hue_power_on_behavior'],
292
+ convertSet: async (entity, key, value, meta) => {
293
+ if (value === 'default') {
294
+ value = 'on';
295
+ }
296
+
297
+ let supports = {colorTemperature: false, colorXY: false};
298
+ if (entity.constructor.name === 'Endpoint' && entity.supportsInputCluster('lightingColorCtrl')) {
299
+ const readResult = await entity.read('lightingColorCtrl', ['colorCapabilities']);
300
+ supports = {
301
+ colorTemperature: (readResult.colorCapabilities & 1 << 4) > 0,
302
+ colorXY: (readResult.colorCapabilities & 1 << 3) > 0,
303
+ };
304
+ } else if (entity.constructor.name === 'Group') {
305
+ supports = {colorTemperature: true, colorXY: true};
306
+ }
307
+
308
+ if (value === 'off') {
309
+ await entity.write('genOnOff', {0x4003: {value: 0x00, type: 0x30}});
310
+ } else if (value === 'recover') {
311
+ await entity.write('genOnOff', {0x4003: {value: 0xff, type: 0x30}});
312
+ await entity.write('genLevelCtrl', {0x4000: {value: 0xff, type: 0x20}});
313
+
314
+ if (supports.colorTemperature) {
315
+ await entity.write('lightingColorCtrl', {0x4010: {value: 0xffff, type: 0x21}});
316
+ }
317
+
318
+ if (supports.colorXY) {
319
+ await entity.write('lightingColorCtrl', {0x0003: {value: 0xffff, type: 0x21}}, manufacturerOptions);
320
+ await entity.write('lightingColorCtrl', {0x0004: {value: 0xffff, type: 0x21}}, manufacturerOptions);
321
+ }
322
+ } else if (value === 'on') {
323
+ await entity.write('genOnOff', {0x4003: {value: 0x01, type: 0x30}});
324
+
325
+ let brightness = meta.message.hasOwnProperty('hue_power_on_brightness') ?
326
+ meta.message.hue_power_on_brightness : 0xfe;
327
+ if (brightness === 255) {
328
+ // 255 (0xFF) is the value for recover, therefore set it to 254 (0xFE)
329
+ brightness = 254;
330
+ }
331
+ await entity.write('genLevelCtrl', {0x4000: {value: brightness, type: 0x20}});
332
+
333
+ if (entity.supportsInputCluster('lightingColorCtrl')) {
334
+ if (
335
+ meta.message.hasOwnProperty('hue_power_on_color_temperature') &&
336
+ meta.message.hasOwnProperty('hue_power_on_color')
337
+ ) {
338
+ meta.logger.error(`Provide either color temperature or color, not both`);
339
+ } else if (meta.message.hasOwnProperty('hue_power_on_color_temperature')) {
340
+ const colortemp = meta.message.hue_power_on_color_temperature;
341
+ await entity.write('lightingColorCtrl', {0x4010: {value: colortemp, type: 0x21}});
342
+ // Set color to default
343
+ if (supports.colorXY) {
344
+ await entity.write('lightingColorCtrl', {0x0003: {value: 0xFFFF, type: 0x21}}, manufacturerOptions);
345
+ await entity.write('lightingColorCtrl', {0x0004: {value: 0xFFFF, type: 0x21}}, manufacturerOptions);
346
+ }
347
+ } else if (meta.message.hasOwnProperty('hue_power_on_color')) {
348
+ const colorXY = libColor.ColorRGB.fromHex(meta.message.hue_power_on_color).toXY();
349
+ value = {x: utils.mapNumberRange(colorXY.x, 0, 1, 0, 65535), y: utils.mapNumberRange(colorXY.y, 0, 1, 0, 65535)};
350
+
351
+ // Set colortemp to default
352
+ if (supports.colorTemperature) {
353
+ await entity.write('lightingColorCtrl', {0x4010: {value: 366, type: 0x21}});
354
+ }
355
+
356
+ await entity.write('lightingColorCtrl', {0x0003: {value: value.x, type: 0x21}}, manufacturerOptions);
357
+ await entity.write('lightingColorCtrl', {0x0004: {value: value.y, type: 0x21}}, manufacturerOptions);
358
+ } else {
359
+ // Set defaults for colortemp and color
360
+ if (supports.colorTemperature) {
361
+ await entity.write('lightingColorCtrl', {0x4010: {value: 366, type: 0x21}});
362
+ }
363
+
364
+ if (supports.colorXY) {
365
+ await entity.write('lightingColorCtrl', {0x0003: {value: 0xFFFF, type: 0x21}}, manufacturerOptions);
366
+ await entity.write('lightingColorCtrl', {0x0004: {value: 0xFFFF, type: 0x21}}, manufacturerOptions);
367
+ }
368
+ }
369
+ }
370
+ }
371
+
372
+ return {state: {hue_power_on_behavior: value}};
373
+ },
374
+ },
375
+ hue_power_on_error: {
376
+ key: ['hue_power_on_brightness', 'hue_power_on_color_temperature', 'hue_power_on_color'],
377
+ convertSet: async (entity, key, value, meta) => {
378
+ if (!meta.message.hasOwnProperty('hue_power_on_behavior')) {
379
+ throw new Error(`Provide a value for 'hue_power_on_behavior'`);
380
+ }
381
+ },
382
+ },
383
+ hue_motion_sensitivity: {
384
+ // motion detect sensitivity, philips specific
385
+ key: ['motion_sensitivity'],
386
+ convertSet: async (entity, key, value, meta) => {
387
+ // make sure you write to second endpoint!
388
+ const lookup = {'low': 0, 'medium': 1, 'high': 2, 'very_high': 3, 'max': 4};
389
+ value = value.toLowerCase();
390
+ utils.validateValue(value, Object.keys(lookup));
391
+
392
+ const payload = {48: {value: lookup[value], type: 32}};
393
+ await entity.write('msOccupancySensing', payload, manufacturerOptions);
394
+ return {state: {motion_sensitivity: value}};
395
+ },
396
+ convertGet: async (entity, key, meta) => {
397
+ await entity.read('msOccupancySensing', [48], manufacturerOptions);
398
+ },
399
+ },
400
+ hue_motion_led_indication: {
401
+ key: ['led_indication'],
402
+ convertSet: async (entity, key, value, meta) => {
403
+ const payload = {0x0033: {value, type: 0x10}};
404
+ await entity.write('genBasic', payload, manufacturerOptions);
405
+ return {state: {led_indication: value}};
406
+ },
407
+ convertGet: async (entity, key, meta) => {
408
+ await entity.read('genBasic', [0x0033], manufacturerOptions);
409
+ },
410
+ },
229
411
  };
230
412
 
231
413
  module.exports = [
@@ -787,7 +969,9 @@ module.exports = [
787
969
  model: '929001953101',
788
970
  vendor: 'Philips',
789
971
  description: 'Hue White and Color Ambiance GU10',
790
- extend: hueExtend.light_onoff_brightness_colortemp_color(),
972
+ extend: hueExtend.light_onoff_brightness_colortemp_color({
973
+ colorTempRange: [153, 500], disableHueEffects: false,
974
+ }),
791
975
  },
792
976
  {
793
977
  zigbeeModel: ['LWA003', 'LWW002'],
@@ -1039,35 +1223,37 @@ module.exports = [
1039
1223
  model: '9290022166',
1040
1224
  vendor: 'Philips',
1041
1225
  description: 'Hue white and color ambiance E26/E27',
1042
- extend: hueExtend.light_onoff_brightness_colortemp_color(),
1226
+ extend: hueExtend.light_onoff_brightness_colortemp_color({disableHueEffects: false, colorTempRange: [153, 500]}),
1043
1227
  },
1044
1228
  {
1045
1229
  zigbeeModel: ['LCA004'],
1046
1230
  model: '9290024896',
1047
1231
  vendor: 'Philips',
1048
1232
  description: 'Hue white and color ambiance E27',
1049
- extend: hueExtend.light_onoff_brightness_colortemp_color({colorTempRange: [153, 500]}),
1233
+ extend: hueExtend.light_onoff_brightness_colortemp_color({disableHueEffects: false, colorTempRange: [153, 500]}),
1050
1234
  },
1051
1235
  {
1052
1236
  zigbeeModel: ['LCA006'],
1053
1237
  model: '9290024689',
1054
1238
  vendor: 'Philips',
1055
1239
  description: 'Hue white and color ambiance B22 1100lm',
1056
- extend: hueExtend.light_onoff_brightness_colortemp_color({colorTempRange: [153, 500]}),
1240
+ extend: hueExtend.light_onoff_brightness_colortemp_color({disableHueEffects: false, colorTempRange: [153, 500]}),
1057
1241
  },
1058
1242
  {
1059
1243
  zigbeeModel: ['LCA008'],
1060
1244
  model: '929002471601',
1061
1245
  vendor: 'Philips',
1062
1246
  description: 'Hue white and color ambiance E26/E27 1600lm',
1063
- extend: hueExtend.light_onoff_brightness_colortemp_color({colorTempRange: [153, 500]}),
1247
+ extend: hueExtend.light_onoff_brightness_colortemp_color({
1248
+ colorTempRange: [153, 500], disableHueEffects: false,
1249
+ }),
1064
1250
  },
1065
1251
  {
1066
1252
  zigbeeModel: ['LCA009'],
1067
1253
  model: '9290024717',
1068
1254
  vendor: 'Philips',
1069
1255
  description: 'Hue white and color ambiance E26/A19 1600lm',
1070
- extend: hueExtend.light_onoff_brightness_colortemp_color({colorTempRange: [153, 500]}),
1256
+ extend: hueExtend.light_onoff_brightness_colortemp_color({disableHueEffects: false, colorTempRange: [153, 500]}),
1071
1257
  },
1072
1258
  {
1073
1259
  zigbeeModel: ['LCT001', 'LCT007', 'LCT010', 'LCT012', 'LCT014', 'LCT015', 'LCT016', 'LCT021'],
@@ -1179,7 +1365,7 @@ module.exports = [
1179
1365
  model: '929001953301',
1180
1366
  vendor: 'Philips',
1181
1367
  description: 'Hue white ambiance GU10 with Bluetooth',
1182
- extend: hueExtend.light_onoff_brightness_colortemp({colorTempRange: [153, 454]}),
1368
+ extend: hueExtend.light_onoff_brightness_colortemp({colorTempRange: [153, 500], disableHueEffects: false}),
1183
1369
  },
1184
1370
  {
1185
1371
  zigbeeModel: ['LTD005'],
@@ -1741,6 +1927,13 @@ module.exports = [
1741
1927
  description: 'Hue Gradient Signe floor lamp (black)',
1742
1928
  extend: hueExtend.light_onoff_brightness_colortemp_color_gradient({colorTempRange: [153, 500]}),
1743
1929
  },
1930
+ {
1931
+ zigbeeModel: ['915005987501'],
1932
+ model: '915005987501',
1933
+ vendor: 'Philips',
1934
+ description: 'Hue Gradient Signe floor lamp (white)',
1935
+ extend: hueExtend.light_onoff_brightness_colortemp_color_gradient({colorTempRange: [153, 500]}),
1936
+ },
1744
1937
  {
1745
1938
  zigbeeModel: ['LCT020'],
1746
1939
  model: '4080148P7',
@@ -2039,7 +2232,7 @@ module.exports = [
2039
2232
  exposes.enum('motion_sensitivity', ea.ALL, ['low', 'medium', 'high']),
2040
2233
  exposes.binary('led_indication', ea.ALL, true, false).withDescription('Blink green LED on motion detection'),
2041
2234
  exposes.numeric('occupancy_timeout', ea.ALL).withUnit('second').withValueMin(0).withValueMax(65535)],
2042
- toZigbee: [tz.occupancy_timeout, tz.hue_motion_sensitivity, tz.hue_motion_led_indication],
2235
+ toZigbee: [tz.occupancy_timeout, tzLocal.hue_motion_sensitivity, tzLocal.hue_motion_led_indication],
2043
2236
  endpoint: (device) => {
2044
2237
  return {'default': 2, 'ep1': 1, 'ep2': 2};
2045
2238
  },
@@ -2068,7 +2261,7 @@ module.exports = [
2068
2261
  exposes.enum('motion_sensitivity', ea.ALL, ['low', 'medium', 'high']),
2069
2262
  exposes.binary('led_indication', ea.ALL, true, false).withDescription('Blink green LED on motion detection'),
2070
2263
  exposes.numeric('occupancy_timeout', ea.ALL).withUnit('second').withValueMin(0).withValueMax(65535)],
2071
- toZigbee: [tz.occupancy_timeout, tz.hue_motion_sensitivity, tz.hue_motion_led_indication],
2264
+ toZigbee: [tz.occupancy_timeout, tzLocal.hue_motion_sensitivity, tzLocal.hue_motion_led_indication],
2072
2265
  endpoint: (device) => {
2073
2266
  return {'default': 2, 'ep1': 1, 'ep2': 2};
2074
2267
  },
@@ -2097,7 +2290,7 @@ module.exports = [
2097
2290
  exposes.enum('motion_sensitivity', ea.ALL, ['low', 'medium', 'high', 'very_high', 'max']),
2098
2291
  exposes.binary('led_indication', ea.ALL, true, false).withDescription('Blink green LED on motion detection'),
2099
2292
  exposes.numeric('occupancy_timeout', ea.ALL).withUnit('second').withValueMin(0).withValueMax(65535)],
2100
- toZigbee: [tz.occupancy_timeout, tz.hue_motion_sensitivity, tz.hue_motion_led_indication],
2293
+ toZigbee: [tz.occupancy_timeout, tzLocal.hue_motion_sensitivity, tzLocal.hue_motion_led_indication],
2101
2294
  configure: async (device, coordinatorEndpoint, logger) => {
2102
2295
  const endpoint = device.getEndpoint(2);
2103
2296
  const binds = ['genPowerCfg', 'msIlluminanceMeasurement', 'msTemperatureMeasurement', 'msOccupancySensing'];
@@ -2123,7 +2316,7 @@ module.exports = [
2123
2316
  exposes.enum('motion_sensitivity', ea.ALL, ['low', 'medium', 'high', 'very_high', 'max']),
2124
2317
  exposes.binary('led_indication', ea.ALL, true, false).withDescription('Blink green LED on motion detection'),
2125
2318
  exposes.numeric('occupancy_timeout', ea.ALL).withUnit('second').withValueMin(0).withValueMax(65535)],
2126
- toZigbee: [tz.occupancy_timeout, tz.hue_motion_sensitivity, tz.hue_motion_led_indication],
2319
+ toZigbee: [tz.occupancy_timeout, tzLocal.hue_motion_sensitivity, tzLocal.hue_motion_led_indication],
2127
2320
  configure: async (device, coordinatorEndpoint, logger) => {
2128
2321
  const endpoint = device.getEndpoint(2);
2129
2322
  const binds = ['genPowerCfg', 'msIlluminanceMeasurement', 'msTemperatureMeasurement', 'msOccupancySensing'];
@@ -2144,7 +2337,7 @@ module.exports = [
2144
2337
  vendor: 'Philips',
2145
2338
  description: 'Hue smart plug - EU',
2146
2339
  extend: extend.switch(),
2147
- toZigbee: [tz.on_off].concat([tz.hue_power_on_behavior, tz.hue_power_on_error]),
2340
+ toZigbee: [tz.on_off].concat([tzLocal.hue_power_on_behavior, tzLocal.hue_power_on_error]),
2148
2341
  configure: async (device, coordinatorEndpoint, logger) => {
2149
2342
  const endpoint = device.getEndpoint(11);
2150
2343
  await reporting.bind(endpoint, coordinatorEndpoint, ['genOnOff']);
@@ -2158,7 +2351,7 @@ module.exports = [
2158
2351
  vendor: 'Philips',
2159
2352
  description: 'Hue smart plug bluetooth',
2160
2353
  extend: extend.switch(),
2161
- toZigbee: [tz.on_off].concat([tz.hue_power_on_behavior, tz.hue_power_on_error]),
2354
+ toZigbee: [tz.on_off].concat([tzLocal.hue_power_on_behavior, tzLocal.hue_power_on_error]),
2162
2355
  configure: async (device, coordinatorEndpoint, logger) => {
2163
2356
  const endpoint = device.getEndpoint(11);
2164
2357
  await reporting.bind(endpoint, coordinatorEndpoint, ['genOnOff']);
@@ -2172,7 +2365,7 @@ module.exports = [
2172
2365
  vendor: 'Philips',
2173
2366
  description: 'Hue smart plug - UK',
2174
2367
  extend: extend.switch(),
2175
- toZigbee: [tz.on_off].concat([tz.hue_power_on_behavior, tz.hue_power_on_error]),
2368
+ toZigbee: [tz.on_off].concat([tzLocal.hue_power_on_behavior, tzLocal.hue_power_on_error]),
2176
2369
  configure: async (device, coordinatorEndpoint, logger) => {
2177
2370
  const endpoint = device.getEndpoint(11);
2178
2371
  await reporting.bind(endpoint, coordinatorEndpoint, ['genOnOff']);
@@ -2186,7 +2379,7 @@ module.exports = [
2186
2379
  vendor: 'Philips',
2187
2380
  description: 'Hue smart plug - AU',
2188
2381
  extend: extend.switch(),
2189
- toZigbee: [tz.on_off].concat([tz.hue_power_on_behavior, tz.hue_power_on_error]),
2382
+ toZigbee: [tz.on_off].concat([tzLocal.hue_power_on_behavior, tzLocal.hue_power_on_error]),
2190
2383
  configure: async (device, coordinatorEndpoint, logger) => {
2191
2384
  const endpoint = device.getEndpoint(11);
2192
2385
  await reporting.bind(endpoint, coordinatorEndpoint, ['genOnOff']);
@@ -2200,7 +2393,7 @@ module.exports = [
2200
2393
  vendor: 'Philips',
2201
2394
  description: 'Hue smart plug - AU',
2202
2395
  extend: extend.switch(),
2203
- toZigbee: [tz.on_off].concat([tz.hue_power_on_behavior, tz.hue_power_on_error]),
2396
+ toZigbee: [tz.on_off].concat([tzLocal.hue_power_on_behavior, tzLocal.hue_power_on_error]),
2204
2397
  configure: async (device, coordinatorEndpoint, logger) => {
2205
2398
  const endpoint = device.getEndpoint(11);
2206
2399
  await reporting.bind(endpoint, coordinatorEndpoint, ['genOnOff']);
@@ -2214,7 +2407,7 @@ module.exports = [
2214
2407
  vendor: 'Philips',
2215
2408
  description: 'Hue smart plug - CH',
2216
2409
  extend: extend.switch(),
2217
- toZigbee: [tz.on_off].concat([tz.hue_power_on_behavior, tz.hue_power_on_error]),
2410
+ toZigbee: [tz.on_off].concat([tzLocal.hue_power_on_behavior, tzLocal.hue_power_on_error]),
2218
2411
  configure: async (device, coordinatorEndpoint, logger) => {
2219
2412
  const endpoint = device.getEndpoint(11);
2220
2413
  await reporting.bind(endpoint, coordinatorEndpoint, ['genOnOff']);
@@ -2228,7 +2421,7 @@ module.exports = [
2228
2421
  vendor: 'Philips',
2229
2422
  description: 'Hue smart plug',
2230
2423
  extend: extend.switch(),
2231
- toZigbee: [tz.on_off].concat([tz.hue_power_on_behavior, tz.hue_power_on_error]),
2424
+ toZigbee: [tz.on_off].concat([tzLocal.hue_power_on_behavior, tzLocal.hue_power_on_error]),
2232
2425
  configure: async (device, coordinatorEndpoint, logger) => {
2233
2426
  const endpoint = device.getEndpoint(11);
2234
2427
  await reporting.bind(endpoint, coordinatorEndpoint, ['genOnOff']);
@@ -2242,7 +2435,7 @@ module.exports = [
2242
2435
  vendor: 'Philips',
2243
2436
  description: 'Hue smart plug - EU',
2244
2437
  extend: extend.switch(),
2245
- toZigbee: [tz.on_off].concat([tz.hue_power_on_behavior, tz.hue_power_on_error]),
2438
+ toZigbee: [tz.on_off].concat([tzLocal.hue_power_on_behavior, tzLocal.hue_power_on_error]),
2246
2439
  configure: async (device, coordinatorEndpoint, logger) => {
2247
2440
  const endpoint = device.getEndpoint(11);
2248
2441
  await reporting.bind(endpoint, coordinatorEndpoint, ['genOnOff']);
@@ -2626,7 +2819,7 @@ module.exports = [
2626
2819
  model: '9290024687',
2627
2820
  vendor: 'Philips',
2628
2821
  description: 'Hue White and Color Ambiance A19 1100 lumen',
2629
- extend: hueExtend.light_onoff_brightness_colortemp_color({colorTempRange: [153, 500]}),
2822
+ extend: hueExtend.light_onoff_brightness_colortemp_color({disableHueEffects: false, colorTempRange: [153, 500]}),
2630
2823
  },
2631
2824
  {
2632
2825
  zigbeeModel: ['RDM002'],
@@ -2665,7 +2858,7 @@ module.exports = [
2665
2858
  model: '9290022266A',
2666
2859
  vendor: 'Philips',
2667
2860
  description: 'Hue White and color ambiance A19 800 lumen',
2668
- extend: hueExtend.light_onoff_brightness_colortemp_color({colorTempRange: [153, 500]}),
2861
+ extend: hueExtend.light_onoff_brightness_colortemp_color({disableHueEffects: false, colorTempRange: [153, 500]}),
2669
2862
  },
2670
2863
  {
2671
2864
  zigbeeModel: ['LWE003'],
@@ -3022,7 +3215,7 @@ module.exports = [
3022
3215
  model: '929003535301',
3023
3216
  vendor: 'Philips',
3024
3217
  description: 'Hue Festavia gradient light string 250',
3025
- extend: hueExtend.light_onoff_brightness_colortemp_color_gradient({colorTempRange: [153, 500]}),
3218
+ extend: hueExtend.light_onoff_brightness_colortemp_color_gradient({colorTempRange: [153, 500], extraEffects: ['sparkle']}),
3026
3219
  },
3027
3220
  {
3028
3221
  zigbeeModel: ['915005987101'],
package/devices/sinope.js CHANGED
@@ -584,7 +584,7 @@ module.exports = [
584
584
  'genBasic', 'genIdentify', 'genGroups', 'hvacThermostat', 'hvacUserInterfaceCfg',
585
585
  'msTemperatureMeasurement', 'haElectricalMeasurement', 'seMetering',
586
586
  'manuSpecificSinope'];
587
- await reporting.bind(endpoint, coordinatorEndpoint, binds);
587
+ await reporting.bind(endpoint, coordinatorEndpoint, binds); // This G2 version has limited memory space
588
588
  const thermostatDate = new Date();
589
589
  const thermostatTimeSec = thermostatDate.getTime() / 1000;
590
590
  const thermostatTimezoneOffsetSec = thermostatDate.getTimezoneOffset() * 60;
@@ -607,6 +607,9 @@ module.exports = [
607
607
  await reporting.activePower(endpoint, {min: 10, max: 305, change: 1}); // divider 1: 1W
608
608
  await reporting.rmsCurrent(endpoint, {min: 10, max: 306, change: 100}); // divider 1000: 0.1Arms
609
609
  await reporting.rmsVoltage(endpoint, {min: 10, max: 307, change: 5}); // divider 10: 0.5Vrms
610
+
611
+ // Disable default reporting (not used by Sinope)
612
+ await reporting.thermostatRunningState(endpoint, {min: 1, max: 0xFFFF});
610
613
  try {
611
614
  await reporting.thermostatUnoccupiedHeatingSetpoint(endpoint);
612
615
  } catch (error) {/* Do nothing */}
package/devices/tuya.js CHANGED
@@ -1144,7 +1144,7 @@ module.exports = [
1144
1144
  configure: tuya.configureMagicPacket,
1145
1145
  },
1146
1146
  {
1147
- fingerprint: tuya.fingerprint('TS0202', ['_TZ3000_mcxw5ehu', '_TZ3040_6ygjfyll']),
1147
+ fingerprint: tuya.fingerprint('TS0202', ['_TZ3000_mcxw5ehu', '_TZ3000_6ygjfyll', '_TZ3040_6ygjfyll']),
1148
1148
  model: 'IH012-RT01',
1149
1149
  vendor: 'TuYa',
1150
1150
  description: 'Motion sensor',
@@ -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',
@@ -1644,7 +1668,6 @@ module.exports = [
1644
1668
  {modelID: 'TS0201', manufacturerName: '_TZ3000_6uzkisv2'},
1645
1669
  {modelID: 'TS0201', manufacturerName: '_TZ3000_xr3htd96'},
1646
1670
  {modelID: 'TS0601', manufacturerName: '_TZE200_9yapgbuv'},
1647
- {modelID: 'TS0601', manufacturerName: '_TZE200_yjjdcqsq'},
1648
1671
  ],
1649
1672
  model: 'WSD500A',
1650
1673
  vendor: 'TuYa',
@@ -2515,6 +2538,30 @@ module.exports = [
2515
2538
  ],
2516
2539
  },
2517
2540
  },
2541
+ {
2542
+ fingerprint: tuya.fingerprint('TS0601', ['_TZE200_m9skfctm']),
2543
+ model: 'TS0601_smoke_2',
2544
+ vendor: 'TuYa',
2545
+ description: 'Photoelectric smoke detector',
2546
+ fromZigbee: [tuya.fz.datapoints],
2547
+ toZigbee: [tuya.tz.datapoints],
2548
+ onEvent: tuya.onEventSetTime,
2549
+ configure: tuya.configureMagicPacket,
2550
+ exposes: [
2551
+ e.smoke(), e.battery(), e.test(),
2552
+ exposes.numeric('smoke_concentration', ea.STATE).withUnit('ppm').withDescription('Parts per million of smoke detected'),
2553
+ exposes.binary('device_fault', ea.STATE, true, false).withDescription('Indicates a fault with the device'),
2554
+ ],
2555
+ meta: {
2556
+ tuyaDatapoints: [
2557
+ [1, 'smoke', tuya.valueConverter.true0ElseFalse],
2558
+ [2, 'smoke_concentration', tuya.valueConverter.divideBy10],
2559
+ [11, 'device_fault', tuya.valueConverter.raw],
2560
+ [15, 'battery', tuya.valueConverter.raw],
2561
+ [101, 'test', tuya.valueConverter.raw],
2562
+ ],
2563
+ },
2564
+ },
2518
2565
  {
2519
2566
  zigbeeModel: ['5p1vj8r'],
2520
2567
  fingerprint: tuya.fingerprint('TS0601', ['_TZE200_t5p1vj8r', '_TZE200_uebojraa', '_TZE200_vzekyi4c', '_TZE200_yh7aoahi',
@@ -2548,6 +2595,24 @@ module.exports = [
2548
2595
  exposes.binary('silence_siren', ea.STATE_SET, true, false).withDescription('Silence the siren')],
2549
2596
  onEvent: tuya.onEventsetTime,
2550
2597
  },
2598
+ {
2599
+ fingerprint: tuya.fingerprint('TS0601', ['_TZE204_cjbofhxw']),
2600
+ model: 'TS0601_clamp_meter',
2601
+ vendor: 'TuYa',
2602
+ description: 'Clamp meter',
2603
+ fromZigbee: [tuya.fz.datapoints, tuya.fz.gateway_connection_status],
2604
+ toZigbee: [tuya.tz.datapoints],
2605
+ configure: tuya.configureMagicPacket,
2606
+ exposes: [e.current(), e.power(), e.voltage(), e.energy()],
2607
+ meta: {
2608
+ tuyaDatapoints: [
2609
+ [18, 'current', tuya.valueConverter.divideBy1000],
2610
+ [19, 'power', tuya.valueConverter.divideBy10],
2611
+ [20, 'voltage', tuya.valueConverter.divideBy10],
2612
+ [101, 'energy', tuya.valueConverter.divideBy1000],
2613
+ ],
2614
+ },
2615
+ },
2551
2616
  {
2552
2617
  fingerprint: tuya.fingerprint('TS0601', ['_TZE200_bkkmqmyo', '_TZE200_eaac7dkw']),
2553
2618
  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'],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zigbee-herdsman-converters",
3
- "version": "15.0.9",
3
+ "version": "15.0.11",
4
4
  "description": "Collection of device converters to be used with zigbee-herdsman",
5
5
  "main": "index.js",
6
6
  "files": [