zigbee-herdsman-converters 15.0.52 → 15.0.54

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.
@@ -327,7 +327,7 @@ const converters = {
327
327
  type: ['attributeReport', 'readResponse'],
328
328
  convert: (model, msg, publish, options, meta) => {
329
329
  const payload = {};
330
- if (msg.data.hasOwnProperty('batteryPercentageRemaining')) {
330
+ if (msg.data.hasOwnProperty('batteryPercentageRemaining') && (msg.data['batteryPercentageRemaining'] < 255)) {
331
331
  // Some devices do not comply to the ZCL and report a
332
332
  // batteryPercentageRemaining of 100 when the battery is full (should be 200).
333
333
  const dontDividePercentage = model.meta && model.meta.battery && model.meta.battery.dontDividePercentage;
@@ -336,7 +336,7 @@ const converters = {
336
336
  payload.battery = precisionRound(percentage, 2);
337
337
  }
338
338
 
339
- if (msg.data.hasOwnProperty('batteryVoltage')) {
339
+ if (msg.data.hasOwnProperty('batteryVoltage') && (msg.data['batteryVoltage'] < 255)) {
340
340
  // Deprecated: voltage is = mV now but should be V
341
341
  payload.voltage = msg.data['batteryVoltage'] * 100;
342
342
 
@@ -601,8 +601,8 @@ const converters = {
601
601
  }
602
602
 
603
603
  if (msg.data.hasOwnProperty('colorMode')) {
604
- result.color_mode = constants.colorMode.hasOwnProperty(msg.data['colorMode']) ?
605
- constants.colorMode[msg.data['colorMode']] : msg.data['colorMode'];
604
+ result.color_mode = constants.colorModeLookup.hasOwnProperty(msg.data['colorMode']) ?
605
+ constants.colorModeLookup[msg.data['colorMode']] : msg.data['colorMode'];
606
606
  }
607
607
 
608
608
  if (
@@ -2299,9 +2299,9 @@ const converters = {
2299
2299
 
2300
2300
  if (msg.data.hasOwnProperty('tuyaRgbMode')) {
2301
2301
  if (msg.data['tuyaRgbMode'] === 1) {
2302
- result.color_mode = constants.colorMode[0];
2302
+ result.color_mode = constants.colorModeLookup[0];
2303
2303
  } else {
2304
- result.color_mode = constants.colorMode[2];
2304
+ result.color_mode = constants.colorModeLookup[2];
2305
2305
  }
2306
2306
  }
2307
2307
 
@@ -796,7 +796,7 @@ const converters = {
796
796
  };
797
797
  await entity.command('lightingColorCtrl', 'moveToColor', payload, utils.getOptions(meta.mapped, entity));
798
798
  return {
799
- state: libColor.syncColorState({'color_mode': constants.colorMode[2], 'color_temp': value}, meta.state,
799
+ state: libColor.syncColorState({'color_mode': constants.colorModeLookup[2], 'color_temp': value}, meta.state,
800
800
  entity, meta.options, meta.logger), readAfterWriteTime: payload.transtime * 100,
801
801
  };
802
802
  }
@@ -1031,7 +1031,7 @@ const converters = {
1031
1031
  const payload = {colortemp: value, transtime: utils.getTransition(entity, key, meta).time};
1032
1032
  await entity.command('lightingColorCtrl', 'moveToColorTemp', payload, utils.getOptions(meta.mapped, entity));
1033
1033
  return {
1034
- state: libColor.syncColorState({'color_mode': constants.colorMode[2], 'color_temp': value}, meta.state,
1034
+ state: libColor.syncColorState({'color_mode': constants.colorModeLookup[2], 'color_temp': value}, meta.state,
1035
1035
  entity, meta.options, meta.logger), readAfterWriteTime: payload.transtime * 100,
1036
1036
  };
1037
1037
  },
@@ -1091,7 +1091,7 @@ const converters = {
1091
1091
  xy.y = 0.2993;
1092
1092
  }
1093
1093
 
1094
- newState.color_mode = constants.colorMode[1];
1094
+ newState.color_mode = constants.colorModeLookup[1];
1095
1095
  newState.color = xy.toObject();
1096
1096
  zclData.colorx = utils.mapNumberRange(xy.x, 0, 1, 0, 65535);
1097
1097
  zclData.colory = utils.mapNumberRange(xy.y, 0, 1, 0, 65535);
@@ -1100,7 +1100,7 @@ const converters = {
1100
1100
  const enhancedHue = utils.getMetaValue(entity, meta.mapped, 'enhancedHue', 'allEqual', true);
1101
1101
  const hsv = newColor.hsv;
1102
1102
  const hsvCorrected = hsv.colorCorrected(meta);
1103
- newState.color_mode = constants.colorMode[0];
1103
+ newState.color_mode = constants.colorModeLookup[0];
1104
1104
  newState.color = hsv.toObject(false);
1105
1105
 
1106
1106
  if (hsv.hue !== null) {
@@ -3542,7 +3542,7 @@ const converters = {
3542
3542
  key: ['brightness', 'color', 'color_temp'],
3543
3543
  options: [exposes.options.color_sync()],
3544
3544
  convertSet: async (entity, key, value, meta) => {
3545
- if (key === 'brightness' && meta.state.color_mode == constants.colorMode[2] &&
3545
+ if (key === 'brightness' && meta.state.color_mode == constants.colorModeLookup[2] &&
3546
3546
  !meta.message.hasOwnProperty('color') && !meta.message.hasOwnProperty('color_temp')) {
3547
3547
  const zclData = {level: Number(value), transtime: 0};
3548
3548
 
@@ -3565,7 +3565,7 @@ const converters = {
3565
3565
 
3566
3566
  const newState = {
3567
3567
  brightness: zclDataBrightness.level,
3568
- color_mode: constants.colorMode[2],
3568
+ color_mode: constants.colorModeLookup[2],
3569
3569
  color_temp: meta.message.color_temp,
3570
3570
  };
3571
3571
 
@@ -3583,7 +3583,7 @@ const converters = {
3583
3583
 
3584
3584
  const newState = {
3585
3585
  brightness: zclDataBrightness.level,
3586
- color_mode: constants.colorMode[2],
3586
+ color_mode: constants.colorModeLookup[2],
3587
3587
  color_temp: value,
3588
3588
  };
3589
3589
 
@@ -3649,7 +3649,7 @@ const converters = {
3649
3649
  s: utils.mapNumberRange(zclData.saturation, 0, 254, 0, 100),
3650
3650
  saturation: utils.mapNumberRange(zclData.saturation, 0, 254, 0, 100),
3651
3651
  },
3652
- color_mode: constants.colorMode[0],
3652
+ color_mode: constants.colorModeLookup[0],
3653
3653
  };
3654
3654
 
3655
3655
  return {state: libColor.syncColorState(newState, meta.state, entity, meta.options, meta.logger),
@@ -5285,12 +5285,12 @@ const converters = {
5285
5285
 
5286
5286
  const addColorMode = (newState) => {
5287
5287
  if (newState.hasOwnProperty('color_temp')) {
5288
- newState.color_mode = constants.colorMode[2];
5288
+ newState.color_mode = constants.colorModeLookup[2];
5289
5289
  } else if (newState.hasOwnProperty('color')) {
5290
5290
  if (newState.color.hasOwnProperty('x')) {
5291
- newState.color_mode = constants.colorMode[1];
5291
+ newState.color_mode = constants.colorModeLookup[1];
5292
5292
  } else {
5293
- newState.color_mode = constants.colorMode[0];
5293
+ newState.color_mode = constants.colorModeLookup[0];
5294
5294
  }
5295
5295
  }
5296
5296
 
@@ -5391,7 +5391,7 @@ const converters = {
5391
5391
  const xScaled = utils.mapNumberRange(xy.x, 0, 1, 0, 65535);
5392
5392
  const yScaled = utils.mapNumberRange(xy.y, 0, 1, 0, 65535);
5393
5393
  extensionfieldsets.push({'clstId': 768, 'len': 4, 'extField': [xScaled, yScaled]});
5394
- state['color_mode'] = constants.colorMode[2];
5394
+ state['color_mode'] = constants.colorModeLookup[2];
5395
5395
  state['color_temp'] = val;
5396
5396
  } else if (attribute === 'color') {
5397
5397
  try {
@@ -5411,7 +5411,7 @@ const converters = {
5411
5411
  'extField': [xScaled, yScaled],
5412
5412
  },
5413
5413
  );
5414
- state['color_mode'] = constants.colorMode[1];
5414
+ state['color_mode'] = constants.colorModeLookup[1];
5415
5415
  state['color'] = newColor.xy.toObject();
5416
5416
  } else if (newColor.isHSV()) {
5417
5417
  const hsvCorrected = newColor.hsv.colorCorrected(meta);
@@ -5439,7 +5439,7 @@ const converters = {
5439
5439
  },
5440
5440
  );
5441
5441
  }
5442
- state['color_mode'] = constants.colorMode[0];
5442
+ state['color_mode'] = constants.colorModeLookup[0];
5443
5443
  state['color'] = newColor.hsv.toObject(false, false);
5444
5444
  }
5445
5445
  }
package/devices/ikea.js CHANGED
@@ -186,7 +186,7 @@ const ikea = {
186
186
  type: ['attributeReport', 'readResponse'],
187
187
  convert: (model, msg, publish, options, meta) => {
188
188
  const payload = {};
189
- if (msg.data.hasOwnProperty('batteryPercentageRemaining')) {
189
+ if (msg.data.hasOwnProperty('batteryPercentageRemaining') && (msg.data['batteryPercentageRemaining'] < 255)) {
190
190
  // Some devices do not comply to the ZCL and report a
191
191
  // batteryPercentageRemaining of 100 when the battery is full (should be 200).
192
192
  //
package/devices/innr.js CHANGED
@@ -4,7 +4,6 @@ const tz = require('../converters/toZigbee');
4
4
  const reporting = require('../lib/reporting');
5
5
  const extend = require('../lib/extend');
6
6
  const e = exposes.presets;
7
- const ea = exposes.access;
8
7
  const ota = require('../lib/ota');
9
8
 
10
9
  module.exports = [
@@ -543,7 +542,7 @@ module.exports = [
543
542
  endpoint.saveClusterAttributeKeyValue('seMetering', {multiplier: 1, divisor: 100});
544
543
  await reporting.currentSummDelivered(endpoint);
545
544
  },
546
- exposes: [e.power(), e.current(), e.voltage().withAccess(ea.STATE), e.switch(), e.energy()],
545
+ exposes: [e.power(), e.current(), e.voltage(), e.switch(), e.energy()],
547
546
  },
548
547
  {
549
548
  zigbeeModel: ['SP 110'],
@@ -618,7 +617,7 @@ module.exports = [
618
617
  await reporting.currentSummDelivered(endpoint);
619
618
  },
620
619
  ota: ota.zigbeeOTA,
621
- exposes: [e.power(), e.current(), e.voltage().withAccess(ea.STATE), e.switch(), e.energy()],
620
+ exposes: [e.power(), e.current(), e.voltage(), e.switch(), e.energy()],
622
621
  },
623
622
  {
624
623
  zigbeeModel: ['OFL 120 C'],
package/devices/lellki.js CHANGED
@@ -135,7 +135,7 @@ module.exports = [
135
135
  },
136
136
  options: [exposes.options.measurement_poll_interval()],
137
137
  exposes: [e.switch().withEndpoint('l1'), e.switch().withEndpoint('l2'),
138
- e.switch().withEndpoint('l3'), e.power(), e.current(), e.voltage().withAccess(ea.STATE),
138
+ e.switch().withEndpoint('l3'), e.power(), e.current(), e.voltage(),
139
139
  e.energy(), exposes.enum('power_outage_memory', ea.ALL, ['on', 'off', 'restore'])
140
140
  .withDescription('Recover state after power outage')],
141
141
  endpoint: (device) => {
package/devices/lidl.js CHANGED
@@ -7,6 +7,9 @@ const ea = exposes.access;
7
7
  const tuya = require('../lib/tuya');
8
8
  const globalStore = require('../lib/store');
9
9
  const ota = require('../lib/ota');
10
+ const utils = require('../lib/utils');
11
+ const {ColorMode} = require('../lib/constants');
12
+ const libColor = require('../lib/color');
10
13
 
11
14
  const tuyaLocal = {
12
15
  dataPoints: {
@@ -142,6 +145,104 @@ const fzLocal = {
142
145
  },
143
146
  };
144
147
  const tzLocal = {
148
+ led_control: {
149
+ key: ['brightness', 'color', 'color_temp', 'transition'],
150
+ options: [exposes.options.color_sync()],
151
+ convertSet: async (entity, _key, _value, meta) => {
152
+ const newState = {};
153
+
154
+ // The color mode encodes whether the light is using its white LEDs or its color LEDs
155
+ let colorMode = meta.state.color_mode || ColorMode.ColorTemp;
156
+
157
+ // Color mode switching is done by setting color temperature (switch to white LEDs) or setting color (switch
158
+ // to color LEDs)
159
+ if ('color_temp' in meta.message) colorMode = ColorMode.ColorTemp;
160
+ if ('color' in meta.message) colorMode = ColorMode.HS;
161
+
162
+ if (colorMode != meta.state.color_mode) {
163
+ newState.color_mode = colorMode;
164
+
165
+ // We need to send a command to switch from white LEDs to color LEDs. We don't need to send one to
166
+ // switch back because the color LEDs are automatically turned off by the moveToColorTemp and
167
+ // moveToLevel commands.
168
+ if (colorMode == ColorMode.HS) {
169
+ await entity.command('lightingColorCtrl', 'tuyaRgbMode', {enable: 1}, {}, {disableDefaultResponse: true});
170
+ }
171
+ }
172
+
173
+ // A transition time of 0 would be treated as about 1 second, probably some kind of fallback/default
174
+ // transition time, so for "no transition" we use 1 (tenth of a second).
175
+ const transtime = 'transition' in meta.message ? meta.message.transition * 10 : 1;
176
+
177
+ if (colorMode == ColorMode.ColorTemp) {
178
+ if ('brightness' in meta.message) {
179
+ const zclData = {level: Number(meta.message.brightness), transtime};
180
+ await entity.command('genLevelCtrl', 'moveToLevel', zclData, utils.getOptions(meta.mapped, entity));
181
+ newState.brightness = meta.message.brightness;
182
+ }
183
+
184
+ if ('color_temp' in meta.message) {
185
+ const zclData = {colortemp: meta.message.color_temp, transtime: transtime};
186
+ await entity.command('lightingColorCtrl', 'moveToColorTemp', zclData, utils.getOptions(meta.mapped, entity));
187
+ newState.color_temp = meta.message.color_temp;
188
+ }
189
+ } else if (colorMode == ColorMode.HS) {
190
+ if ('brightness' in meta.message || 'color' in meta.message) {
191
+ // We ignore the brightness of the color and instead use the overall brightness setting of the lamp
192
+ // for the brightness because I think that's the expected behavior and also because the color
193
+ // conversion below always returns 100 as brightness ("value") even for very dark colors, except
194
+ // when the color is completely black/zero.
195
+
196
+ // Load current state or defaults
197
+ const newSettings = {
198
+ brightness: meta.state.brightness || 254, // full brightness
199
+ hue: (meta.state.color || {}).h || 0, // red
200
+ saturation: (meta.state.color || {}).s || 100, // full saturation
201
+ };
202
+
203
+ // Apply changes
204
+ if ('brightness' in meta.message) {
205
+ newSettings.brightness = meta.message.brightness;
206
+ newState.brightness = meta.message.brightness;
207
+ }
208
+ if ('color' in meta.message) {
209
+ // The Z2M UI sends `{ hex:'#xxxxxx' }`.
210
+ // Home Assistant sends `{ h: xxx, s: xxx }`.
211
+ // We convert the former into the latter.
212
+ const c = libColor.Color.fromConverterArg(meta.message.color);
213
+ if (c.isRGB()) {
214
+ // https://github.com/Koenkk/zigbee2mqtt/issues/13421#issuecomment-1426044963
215
+ c.hsv = c.rgb.gammaCorrected().toXY().toHSV();
216
+ }
217
+ const color = c.hsv;
218
+
219
+ newSettings.hue = color.hue;
220
+ newSettings.saturation = color.saturation;
221
+
222
+ newState.color = {
223
+ h: color.hue,
224
+ s: color.saturation,
225
+ };
226
+ }
227
+
228
+ // Convert to device specific format and send
229
+ const zclData = {
230
+ brightness: utils.mapNumberRange(newSettings.brightness, 0, 254, 0, 1000),
231
+ hue: newSettings.hue,
232
+ saturation: utils.mapNumberRange(newSettings.saturation, 0, 100, 0, 1000),
233
+ };
234
+ // This command doesn't support a transition time
235
+ await entity.command('lightingColorCtrl', 'tuyaMoveToHueAndSaturationBrightness2', zclData,
236
+ utils.getOptions(meta.mapped, entity));
237
+ }
238
+ }
239
+
240
+ return {state: newState};
241
+ },
242
+ convertGet: async (entity, key, meta) => {
243
+ await entity.read('lightingColorCtrl', ['currentHue', 'currentSaturation', 'currentLevel', 'tuyaRgbMode', 'colorTemperature']);
244
+ },
245
+ },
145
246
  zs_thermostat_child_lock: {
146
247
  key: ['child_lock'],
147
248
  convertSet: async (entity, key, value, meta) => {
@@ -484,6 +585,7 @@ module.exports = [
484
585
  {
485
586
  fingerprint: [
486
587
  {modelID: 'TS011F', manufacturerName: '_TZ3000_wzauvbcs'}, // EU
588
+ {modelID: 'TS011F', manufacturerName: '_TZ3000_oznonj5q'},
487
589
  {modelID: 'TS011F', manufacturerName: '_TZ3000_1obwwnmq'},
488
590
  {modelID: 'TS011F', manufacturerName: '_TZ3000_4uf3d0ax'}, // FR
489
591
  {modelID: 'TS011F', manufacturerName: '_TZ3000_vzopcetz'}, // CZ
@@ -738,7 +840,9 @@ module.exports = [
738
840
  model: '14149505L/14149506L',
739
841
  vendor: 'Lidl',
740
842
  description: 'Livarno Lux light bar RGB+CCT (black/white)',
741
- extend: tuya.extend.light_onoff_brightness_colortemp_color({noConfigure: true}),
843
+ toZigbee: [tz.on_off, tzLocal.led_control],
844
+ fromZigbee: [fz.on_off, fz.tuya_led_controller, fz.brightness, fz.ignore_basic_report],
845
+ exposes: [e.light_brightness_colortemp_colorhs([153, 500]).removeFeature('color_temp_startup')],
742
846
  configure: async (device, coordinatorEndpoint, logger) => {
743
847
  device.getEndpoint(1).saveClusterAttributeKeyValue('lightingColorCtrl', {colorCapabilities: 29});
744
848
  },
@@ -5,7 +5,6 @@ const reporting = require('../lib/reporting');
5
5
  const tuya = require('../lib/tuya');
6
6
  const utils = require('../lib/utils');
7
7
  const e = exposes.presets;
8
- const ea = exposes.access;
9
8
 
10
9
  const fzLocal = {
11
10
  // MG-AUZG01 requires multiEndpoint only for on_off
@@ -30,7 +29,7 @@ module.exports = [
30
29
  description: 'Double Zigbee power point',
31
30
  fromZigbee: [fzLocal.MGAUZG01_on_off, fz.electrical_measurement, fz.metering, fz.ignore_basic_report],
32
31
  toZigbee: [tz.on_off],
33
- exposes: [e.switch().withEndpoint('l1'), e.switch().withEndpoint('l2'), e.power(), e.current(), e.voltage().withAccess(ea.STATE),
32
+ exposes: [e.switch().withEndpoint('l1'), e.switch().withEndpoint('l2'), e.power(), e.current(), e.voltage(),
34
33
  e.energy()],
35
34
  endpoint: (device) => {
36
35
  return {'l1': 1, 'l2': 2};
@@ -3,8 +3,6 @@ const exposes = require('../lib/exposes');
3
3
  const reporting = require('../lib/reporting');
4
4
  const extend = require('../lib/extend');
5
5
  const e = exposes.presets;
6
- const ea = exposes.access;
7
-
8
6
 
9
7
  module.exports = [
10
8
  {
@@ -26,6 +24,6 @@ module.exports = [
26
24
  await reporting.activePower(endpoint, {min: 10, change: 15}); // W - Min change of 1,5W
27
25
  await reporting.currentSummDelivered(endpoint, {min: 300}); // Report KWH every 5min
28
26
  },
29
- exposes: [e.light_brightness(), e.power(), e.current(), e.voltage().withAccess(ea.STATE), e.energy()],
27
+ exposes: [e.light_brightness(), e.power(), e.current(), e.voltage(), e.energy()],
30
28
  },
31
29
  ];
@@ -591,6 +591,13 @@ module.exports = [
591
591
  description: 'Hue Flourish white and color ambiance table light with Bluetooth',
592
592
  extend: philips.extend.light_onoff_brightness_colortemp_color({colorTempRange: [153, 500]}),
593
593
  },
594
+ {
595
+ zigbeeModel: ['915005988401'],
596
+ model: '915005988401',
597
+ vendor: 'Philips',
598
+ description: 'Hue Gradient light tube compact black',
599
+ extend: philips.extend.light_onoff_brightness_colortemp_color_gradient({colorTempRange: [153, 500]}),
600
+ },
594
601
  {
595
602
  zigbeeModel: ['LCG002', '929003047701', '929003047701', '929003526202_01', ' 929003526202_02', '929003526202_03'],
596
603
  model: '929001953101',
package/devices/robb.js CHANGED
@@ -4,7 +4,6 @@ const tz = require('../converters/toZigbee');
4
4
  const reporting = require('../lib/reporting');
5
5
  const extend = require('../lib/extend');
6
6
  const e = exposes.presets;
7
- const ea = exposes.access;
8
7
 
9
8
  module.exports = [
10
9
  {
@@ -242,7 +241,7 @@ module.exports = [
242
241
  await reporting.temperature(endpoint);
243
242
  await reporting.currentSummDelivered(endpoint);
244
243
  },
245
- exposes: [e.power(), e.current(), e.voltage().withAccess(ea.STATE), e.switch(), e.energy(), e.temperature()],
244
+ exposes: [e.power(), e.current(), e.voltage(), e.switch(), e.energy(), e.temperature()],
246
245
  },
247
246
  {
248
247
  zigbeeModel: ['ROB_200-017-1'],
@@ -264,7 +263,7 @@ module.exports = [
264
263
  await reporting.temperature(endpoint);
265
264
  await reporting.currentSummDelivered(endpoint);
266
265
  },
267
- exposes: [e.power(), e.current(), e.voltage().withAccess(ea.STATE), e.switch(), e.energy(), e.temperature()],
266
+ exposes: [e.power(), e.current(), e.voltage(), e.switch(), e.energy(), e.temperature()],
268
267
  },
269
268
  {
270
269
  zigbeeModel: ['ROB_200-016-0'],
package/devices/sinope.js CHANGED
@@ -797,7 +797,9 @@ module.exports = [
797
797
  await reporting.thermostatTemperature(endpoint);
798
798
  await reporting.thermostatPIHeatingDemand(endpoint);
799
799
  await reporting.thermostatOccupiedHeatingSetpoint(endpoint);
800
-
800
+ try {
801
+ await reporting.readMeteringMultiplierDivisor(endpoint);
802
+ } catch (error) {/* Do nothing*/}
801
803
  try {
802
804
  await reporting.currentSummDelivered(endpoint, {min: 10, max: 303, change: [1, 1]});
803
805
  } catch (error) {/* Do nothing*/}
package/devices/sonoff.js CHANGED
@@ -209,7 +209,7 @@ module.exports = [
209
209
  model: 'S26R2ZB',
210
210
  vendor: 'SONOFF',
211
211
  description: 'Zigbee smart plug',
212
- extend: extend.switch(),
212
+ extend: extend.switch({disablePowerOnBehavior: true}),
213
213
  },
214
214
  {
215
215
  zigbeeModel: ['S40LITE'],
@@ -15,9 +15,10 @@ module.exports = [
15
15
  ota: ota.zigbeeOTA,
16
16
  fromZigbee: [fz.on_off, fz.battery],
17
17
  toZigbee: [tz.on_off, tz.ignore_transition],
18
- meta: {battery: {dontDividePercentage: true}},
19
18
  exposes: [e.switch(), e.battery(), e.battery_voltage()],
20
19
  configure: async (device, coordinatorEndpoint, logger) => {
20
+ const endpoint = device.getEndpoint(1);
21
+ await endpoint.read('genPowerCfg', ['batteryPercentageRemaining']);
21
22
  device.powerSource = 'Battery';
22
23
  device.save();
23
24
  },
@@ -69,6 +70,8 @@ module.exports = [
69
70
  ota: ota.zigbeeOTA,
70
71
  exposes: [e.water_leak(), e.battery_low(), e.battery(), e.battery_voltage()],
71
72
  configure: async (device, coordinatorEndpoint, logger) => {
73
+ const endpoint = device.getEndpoint(1);
74
+ await endpoint.read('genPowerCfg', ['batteryPercentageRemaining']);
72
75
  device.powerSource = 'Battery';
73
76
  device.save();
74
77
  },
@@ -83,6 +86,8 @@ module.exports = [
83
86
  ota: ota.zigbeeOTA,
84
87
  exposes: [e.occupancy(), e.battery_low(), e.battery(), e.battery_voltage()],
85
88
  configure: async (device, coordinatorEndpoint, logger) => {
89
+ const endpoint = device.getEndpoint(1);
90
+ await endpoint.read('genPowerCfg', ['batteryPercentageRemaining']);
86
91
  device.powerSource = 'Battery';
87
92
  device.save();
88
93
  },
@@ -95,9 +100,10 @@ module.exports = [
95
100
  fromZigbee: [fz.ias_contact_alarm_1, fz.battery],
96
101
  toZigbee: [],
97
102
  ota: ota.zigbeeOTA,
98
- meta: {battery: {dontDividePercentage: true}},
99
103
  exposes: [e.contact(), e.battery_low(), e.battery(), e.battery_voltage()],
100
104
  configure: async (device, coordinatorEndpoint, logger) => {
105
+ const endpoint = device.getEndpoint(1);
106
+ await endpoint.read('genPowerCfg', ['batteryPercentageRemaining']);
101
107
  device.powerSource = 'Battery';
102
108
  device.save();
103
109
  },
@@ -144,6 +150,8 @@ module.exports = [
144
150
  ota: ota.zigbeeOTA,
145
151
  exposes: [e.battery(), e.battery_low(), e.battery_voltage(), e.action(['single', 'double', 'long'])],
146
152
  configure: async (device, coordinatorEndpoint, logger) => {
153
+ const endpoint = device.getEndpoint(1);
154
+ await endpoint.read('genPowerCfg', ['batteryPercentageRemaining']);
147
155
  device.powerSource = 'Battery';
148
156
  device.save();
149
157
  },
@@ -163,17 +171,25 @@ module.exports = [
163
171
  model: '3RSP02028BZ',
164
172
  vendor: 'Third Reality',
165
173
  description: 'Zigbee / BLE smart plug with power',
166
- fromZigbee: [fz.on_off, fz.electrical_measurement],
174
+ fromZigbee: [fz.on_off, fz.electrical_measurement, fz.metering],
167
175
  toZigbee: [tz.on_off],
168
176
  ota: ota.zigbeeOTA,
169
- exposes: [e.switch(), e.power(), e.current(), e.voltage()],
177
+ exposes: [e.switch(), e.ac_frequency(), e.power(), e.power_factor(), e.energy(), e.current(), e.voltage()],
170
178
  configure: async (device, coordinatorEndpoint, logger) => {
171
179
  const endpoint = device.getEndpoint(1);
172
- await reporting.bind(endpoint, coordinatorEndpoint, ['genOnOff', 'haElectricalMeasurement']);
180
+ await reporting.bind(endpoint, coordinatorEndpoint, ['genOnOff', 'haElectricalMeasurement', 'seMetering']);
181
+ await endpoint.read('haElectricalMeasurement', ['acPowerMultiplier', 'acPowerDivisor']);
173
182
  await reporting.onOff(endpoint);
174
- await reporting.activePower(endpoint);
175
- await reporting.rmsCurrent(endpoint);
176
- await reporting.rmsVoltage(endpoint);
183
+ await reporting.activePower(endpoint, {change: 10});
184
+ await reporting.rmsCurrent(endpoint, {change: 50});
185
+ await reporting.rmsVoltage(endpoint, {change: 5});
186
+ await reporting.readMeteringMultiplierDivisor(endpoint);
187
+ endpoint.saveClusterAttributeKeyValue('seMetering', {divisor: 100, multiplier: 1});
188
+ endpoint.saveClusterAttributeKeyValue('haElectricalMeasurement', {
189
+ acVoltageMultiplier: 1, acVoltageDivisor: 10, acCurrentMultiplier: 1, acCurrentDivisor: 1000, acPowerMultiplier: 1,
190
+ acPowerDivisor: 10,
191
+ });
192
+ device.save();
177
193
  },
178
194
  },
179
195
  ];
package/devices/tuya.js CHANGED
@@ -1914,7 +1914,7 @@ module.exports = [
1914
1914
  endpoint.saveClusterAttributeKeyValue('seMetering', {divisor: 100, multiplier: 1});
1915
1915
  device.save();
1916
1916
  },
1917
- exposes: [e.switch(), e.power(), e.current(), e.voltage().withAccess(ea.STATE), e.energy(), tuya.exposes.switchType(),
1917
+ exposes: [e.switch(), e.power(), e.current(), e.voltage(), e.energy(), tuya.exposes.switchType(),
1918
1918
  exposes.enum('power_outage_memory', ea.ALL, ['on', 'off', 'restore']).withDescription('Recover state after power outage')],
1919
1919
  },
1920
1920
  {
@@ -1960,7 +1960,7 @@ module.exports = [
1960
1960
  device.save();
1961
1961
  },
1962
1962
  whiteLabel: [{vendor: 'Aubess', model: 'WHD02'}],
1963
- exposes: [e.switch(), e.power(), e.current(), e.voltage().withAccess(ea.STATE), e.energy(), e.power_on_behavior(),
1963
+ exposes: [e.switch(), e.power(), e.current(), e.voltage(), e.energy(), e.power_on_behavior(),
1964
1964
  tuya.exposes.switchType()],
1965
1965
  },
1966
1966
  {
@@ -2042,8 +2042,25 @@ module.exports = [
2042
2042
  },
2043
2043
  },
2044
2044
  {
2045
- fingerprint: tuya.fingerprint('TS0003', ['_TZ3000_vsasbzkf', '_TZ3000_odzoiovu', '_TZ3000_4o16jdca']),
2046
- model: 'TS0003_switch_module',
2045
+ fingerprint: [{modelID: 'TS0003', manufacturerName: '_TZ3000_4o16jdca'}],
2046
+ model: 'TS0003_switch_module_2',
2047
+ vendor: 'TuYa',
2048
+ description: '3 gang switch module',
2049
+ extend: tuya.extend.switch({endpoints: ['l1', 'l2', 'l3']}),
2050
+ endpoint: (device) => {
2051
+ return {'l1': 1, 'l2': 2, 'l3': 3};
2052
+ },
2053
+ meta: {multiEndpoint: true},
2054
+ configure: async (device, coordinatorEndpoint, logger) => {
2055
+ await tuya.configureMagicPacket(device, coordinatorEndpoint, logger);
2056
+ await reporting.bind(device.getEndpoint(1), coordinatorEndpoint, ['genOnOff']);
2057
+ await reporting.bind(device.getEndpoint(2), coordinatorEndpoint, ['genOnOff']);
2058
+ await reporting.bind(device.getEndpoint(3), coordinatorEndpoint, ['genOnOff']);
2059
+ },
2060
+ },
2061
+ {
2062
+ fingerprint: tuya.fingerprint('TS0003', ['_TZ3000_vsasbzkf', '_TZ3000_odzoiovu']),
2063
+ model: 'TS0003_switch_module_1',
2047
2064
  vendor: 'TuYa',
2048
2065
  description: '3 gang switch module',
2049
2066
  whiteLabel: [{vendor: 'OXT', model: 'SWTZ23'}],
@@ -2577,7 +2594,7 @@ module.exports = [
2577
2594
  options: [exposes.options.measurement_poll_interval()],
2578
2595
  // This device doesn't support reporting correctly.
2579
2596
  // https://github.com/Koenkk/zigbee-herdsman-converters/pull/1270
2580
- exposes: [e.switch(), e.power(), e.current(), e.voltage().withAccess(ea.STATE),
2597
+ exposes: [e.switch(), e.power(), e.current(), e.voltage(),
2581
2598
  e.energy(), exposes.enum('power_outage_memory', ea.ALL, ['on', 'off', 'restore'])
2582
2599
  .withDescription('Recover state after power outage'),
2583
2600
  exposes.enum('indicator_mode', ea.ALL, ['off', 'off/on', 'on/off']).withDescription('LED indicator mode')],
@@ -3391,7 +3408,7 @@ module.exports = [
3391
3408
  endpoint.saveClusterAttributeKeyValue('seMetering', {divisor: 100, multiplier: 1});
3392
3409
  device.save();
3393
3410
  },
3394
- exposes: [e.switch(), e.power(), e.current(), e.voltage().withAccess(ea.STATE),
3411
+ exposes: [e.switch(), e.power(), e.current(), e.voltage(),
3395
3412
  e.energy(), exposes.enum('power_outage_memory', ea.ALL, ['on', 'off', 'restore'])
3396
3413
  .withDescription('Recover state after power outage'),
3397
3414
  exposes.enum('indicator_mode', ea.STATE_SET, ['off', 'on_off', 'off_on'])
package/devices/ubisys.js CHANGED
@@ -874,10 +874,10 @@ module.exports = [
874
874
  tz.thermostat_local_temperature, tz.thermostat_system_mode,
875
875
  tz.thermostat_weekly_schedule, tz.thermostat_clear_weekly_schedule,
876
876
  tz.thermostat_running_mode, ubisys.tz.thermostat_vacation_mode,
877
- tz.thermostat_pi_heating_demand,
877
+ tz.thermostat_pi_heating_demand, tz.battery_percentage_remaining,
878
878
  ],
879
879
  exposes: [
880
- e.battery(),
880
+ e.battery().withAccess(ea.STATE_GET),
881
881
  exposes.climate()
882
882
  .withSystemMode(['off', 'heat'], ea.ALL)
883
883
  .withRunningMode(['off', 'heat'])
@@ -903,7 +903,8 @@ module.exports = [
903
903
  {min: 0, max: constants.repInterval.HOUR, change: 50});
904
904
  await reporting.thermostatOccupiedHeatingSetpoint(endpoint,
905
905
  {min: 0, max: constants.repInterval.HOUR, change: 50});
906
- await reporting.thermostatPIHeatingDemand(endpoint);
906
+ await reporting.thermostatPIHeatingDemand(endpoint,
907
+ {min: 15, max: constants.repInterval.HOUR, change: 1});
907
908
  await reporting.thermostatOccupancy(endpoint);
908
909
  await reporting.batteryPercentageRemaining(endpoint,
909
910
  {min: constants.repInterval.HOUR, max: 43200, change: 1});
@@ -0,0 +1,188 @@
1
+ const exposes = require('../lib/exposes');
2
+ const fz = require('../converters/fromZigbee');
3
+ const tz = require('../converters/toZigbee');
4
+ const reporting = require('../lib/reporting');
5
+ const extend = require('../lib/extend');
6
+ const e = exposes.presets;
7
+
8
+ module.exports = [
9
+ {
10
+ fingerprint: [{modelID: 'HK-SL-DIM-A', softwareBuildID: '2.5.3_r52'}],
11
+ model: 'VES-ZB-DIM-004',
12
+ vendor: 'Vesternet',
13
+ description: 'Zigbee dimmer',
14
+ fromZigbee: extend.light_onoff_brightness().fromZigbee
15
+ .concat([fz.electrical_measurement, fz.metering, fz.ignore_genOta]),
16
+ toZigbee: extend.light_onoff_brightness().toZigbee.concat([tz.power_on_behavior]),
17
+ exposes: [e.light_brightness(), e.power(), e.voltage(), e.current(), e.energy(), e.power_on_behavior(['off', 'on', 'previous'])],
18
+ whiteLabel: [{vendor: 'Sunricher', model: 'SR-ZG9040A'}],
19
+ configure: async (device, coordinatorEndpoint, logger) => {
20
+ const endpoint = device.getEndpoint(1);
21
+ await reporting.bind(endpoint, coordinatorEndpoint, ['genOnOff', 'genLevelCtrl', 'haElectricalMeasurement', 'seMetering']);
22
+ await reporting.onOff(endpoint);
23
+ await reporting.brightness(endpoint);
24
+ await reporting.readEletricalMeasurementMultiplierDivisors(endpoint);
25
+ await reporting.activePower(endpoint);
26
+ await reporting.rmsCurrent(endpoint, {min: 10, change: 10});
27
+ await reporting.rmsVoltage(endpoint, {min: 10});
28
+ await reporting.readMeteringMultiplierDivisor(endpoint);
29
+ await reporting.currentSummDelivered(endpoint);
30
+ },
31
+ },
32
+ {
33
+ fingerprint: [{modelID: 'ON/OFF -M', softwareBuildID: '2.9.2_r54'}],
34
+ model: 'VES-ZB-HLD-017',
35
+ vendor: 'Vesternet',
36
+ description: 'Zigbee high load switch',
37
+ fromZigbee: [fz.on_off, fz.electrical_measurement, fz.metering, fz.power_on_behavior, fz.ignore_genOta],
38
+ toZigbee: [tz.on_off, tz.power_on_behavior],
39
+ exposes: [e.switch(), e.power(), e.current(), e.voltage(), e.energy(), e.power_on_behavior(['off', 'on', 'previous'])],
40
+ whiteLabel: [{vendor: 'Sunricher', model: 'SR-ZG9101SAC-HP-SWITCH-B'}],
41
+ configure: async (device, coordinatorEndpoint, logger) => {
42
+ const endpoint = device.getEndpoint(1);
43
+ await reporting.bind(endpoint, coordinatorEndpoint, ['genOnOff', 'haElectricalMeasurement', 'seMetering']);
44
+ await reporting.onOff(endpoint);
45
+ await reporting.readEletricalMeasurementMultiplierDivisors(endpoint);
46
+ await reporting.activePower(endpoint);
47
+ await reporting.rmsCurrent(endpoint, {min: 10, change: 10});
48
+ await reporting.rmsVoltage(endpoint, {min: 10});
49
+ await reporting.readMeteringMultiplierDivisor(endpoint);
50
+ await reporting.currentSummDelivered(endpoint);
51
+ },
52
+ },
53
+ {
54
+ fingerprint: [{modelID: 'HK-ZCC-A', softwareBuildID: '2.5.3_r48'}],
55
+ model: 'VES-ZB-MOT-019',
56
+ vendor: 'Vesternet',
57
+ description: 'Zigbee motor controller',
58
+ fromZigbee: [fz.cover_position_tilt, fz.ignore_genOta],
59
+ toZigbee: [tz.cover_state, tz.cover_position_tilt],
60
+ exposes: [e.cover_position()],
61
+ whiteLabel: [{vendor: 'Sunricher', model: 'SR-ZG9080A'}],
62
+ meta: {coverInverted: true},
63
+ configure: async (device, coordinatorEndpoint, logger) => {
64
+ const endpoint = device.getEndpoint(1);
65
+ await reporting.bind(endpoint, coordinatorEndpoint, ['closuresWindowCovering']);
66
+ await reporting.currentPositionLiftPercentage(endpoint);
67
+ },
68
+ },
69
+ {
70
+ fingerprint: [{modelID: 'ZGRC-KEY-013', softwareBuildID: '2.5.3_r20'}],
71
+ model: 'VES-ZB-REM-013',
72
+ vendor: 'Vesternet',
73
+ description: 'Zigbee remote control - 12 button',
74
+ fromZigbee: [fz.command_on, fz.command_off, fz.command_move, fz.command_stop, fz.command_recall, fz.battery, fz.ignore_genOta],
75
+ exposes: [e.battery(),
76
+ e.action(['on_*', 'off_*', 'stop_*', 'brightness_move_up_*', 'brightness_move_down_*', 'brightness_stop_*', 'recall_*'])],
77
+ toZigbee: [],
78
+ meta: {multiEndpoint: true, battery: {dontDividePercentage: true}, publishDuplicateTransaction: true},
79
+ whiteLabel: [{vendor: 'Sunricher', model: 'SR-ZG9001K12-DIM-Z4'}],
80
+ configure: async (device, coordinatorEndpoint, logger) => {
81
+ await reporting.bind(device.getEndpoint(1), coordinatorEndpoint, ['genOnOff', 'genLevelCtrl', 'genScenes', 'genPowerCfg']);
82
+ await reporting.bind(device.getEndpoint(2), coordinatorEndpoint, ['genOnOff', 'genLevelCtrl', 'genScenes']);
83
+ await reporting.bind(device.getEndpoint(3), coordinatorEndpoint, ['genOnOff', 'genLevelCtrl', 'genScenes']);
84
+ await reporting.bind(device.getEndpoint(4), coordinatorEndpoint, ['genOnOff', 'genLevelCtrl', 'genScenes']);
85
+ await reporting.batteryPercentageRemaining(device.getEndpoint(1));
86
+ },
87
+ },
88
+ {
89
+ fingerprint: [{modelID: 'HK-SL-RELAY-A', softwareBuildID: '2.5.3_r47'}],
90
+ model: 'VES-ZB-SWI-005',
91
+ vendor: 'Vesternet',
92
+ description: 'Zigbee switch',
93
+ fromZigbee: [fz.on_off, fz.power_on_behavior, fz.ignore_genOta],
94
+ toZigbee: [tz.on_off, tz.power_on_behavior],
95
+ exposes: [e.switch(), e.power_on_behavior(['off', 'on', 'previous'])],
96
+ whiteLabel: [{vendor: 'Sunricher', model: 'SR-ZG9100A-S'}],
97
+ configure: async (device, coordinatorEndpoint, logger) => {
98
+ const endpoint = device.getEndpoint(1);
99
+ await reporting.bind(endpoint, coordinatorEndpoint, ['genOnOff']);
100
+ await reporting.onOff(endpoint);
101
+ },
102
+ },
103
+ {
104
+ fingerprint: [{modelID: 'ON/OFF(2CH)', softwareBuildID: '2.5.3_r2'}],
105
+ model: 'VES-ZB-SWI-015',
106
+ vendor: 'Vesternet',
107
+ description: 'Zigbee 2 channel switch',
108
+ fromZigbee: [fz.on_off, fz.electrical_measurement, fz.metering, fz.power_on_behavior, fz.ignore_genOta],
109
+ toZigbee: [tz.on_off, tz.power_on_behavior],
110
+ exposes: [e.switch().withEndpoint('l1'), e.switch().withEndpoint('l2'), e.power(), e.current(),
111
+ e.voltage(), e.energy(), e.power_on_behavior(['off', 'on', 'previous'])],
112
+ whiteLabel: [{vendor: 'Sunricher', model: 'SR-ZG9101SAC-HP-SWITCH-2CH'}],
113
+ endpoint: (device) => {
114
+ return {'l1': 1, 'l2': 2};
115
+ },
116
+ meta: {multiEndpoint: true},
117
+ configure: async (device, coordinatorEndpoint, logger) => {
118
+ const endpoint1 = device.getEndpoint(1);
119
+ const endpoint2 = device.getEndpoint(2);
120
+ await reporting.bind(endpoint1, coordinatorEndpoint, ['genOnOff', 'haElectricalMeasurement', 'seMetering']);
121
+ await reporting.bind(endpoint2, coordinatorEndpoint, ['genOnOff']);
122
+ await reporting.onOff(endpoint1);
123
+ await reporting.onOff(endpoint2);
124
+ await reporting.readEletricalMeasurementMultiplierDivisors(endpoint1);
125
+ await reporting.activePower(endpoint1);
126
+ await reporting.rmsCurrent(endpoint1, {min: 10, change: 10});
127
+ await reporting.rmsVoltage(endpoint1, {min: 10});
128
+ await reporting.readMeteringMultiplierDivisor(endpoint1);
129
+ await reporting.currentSummDelivered(endpoint1);
130
+ },
131
+ },
132
+ {
133
+ fingerprint: [{modelID: 'ZG2833K2_EU07', softwareBuildID: '2.5.3_r20'}],
134
+ model: 'VES-ZB-WAL-006',
135
+ vendor: 'Vesternet',
136
+ description: 'Zigbee wall controller - 2 button',
137
+ fromZigbee: [fz.command_on, fz.command_off, fz.command_move, fz.command_stop, fz.battery, fz.ignore_genOta],
138
+ exposes: [e.battery(), e.action([
139
+ 'on_1', 'off_1', 'stop_1', 'brightness_move_up_1', 'brightness_move_down_1', 'brightness_stop_1'])],
140
+ toZigbee: [],
141
+ meta: {multiEndpoint: true, battery: {dontDividePercentage: true}},
142
+ whiteLabel: [{vendor: 'Sunricher', model: 'SR-ZG9001K2-DIM2'}],
143
+ configure: async (device, coordinatorEndpoint, logger) => {
144
+ await reporting.bind(device.getEndpoint(1), coordinatorEndpoint, ['genOnOff', 'genLevelCtrl', 'genPowerCfg']);
145
+ await reporting.batteryPercentageRemaining(device.getEndpoint(1));
146
+ },
147
+ },
148
+ {
149
+ fingerprint: [{modelID: 'ZG2833K4_EU06', softwareBuildID: '2.5.3_r20'}],
150
+ model: 'VES-ZB-WAL-011',
151
+ vendor: 'Vesternet',
152
+ description: 'Zigbee wall controller - 4 button',
153
+ fromZigbee: [fz.command_on, fz.command_off, fz.command_move, fz.command_stop, fz.battery, fz.ignore_genOta],
154
+ exposes: [e.battery(), e.action([
155
+ 'on_1', 'off_1', 'stop_1', 'brightness_move_up_1', 'brightness_move_down_1', 'brightness_stop_1',
156
+ 'on_2', 'off_2', 'stop_2', 'brightness_move_up_2', 'brightness_move_down_2', 'brightness_stop_2'])],
157
+ toZigbee: [],
158
+ meta: {multiEndpoint: true, battery: {dontDividePercentage: true}},
159
+ whiteLabel: [{vendor: 'Sunricher', model: 'SR-ZG9001K4-DIM2'}],
160
+ configure: async (device, coordinatorEndpoint, logger) => {
161
+ await reporting.bind(device.getEndpoint(1), coordinatorEndpoint, ['genOnOff', 'genLevelCtrl', 'genPowerCfg']);
162
+ await reporting.bind(device.getEndpoint(2), coordinatorEndpoint, ['genOnOff', 'genLevelCtrl']);
163
+ await reporting.batteryPercentageRemaining(device.getEndpoint(1));
164
+ },
165
+ },
166
+ {
167
+ fingerprint: [{modelID: 'ZG2833K8_EU05', softwareBuildID: '2.5.3_r20'}],
168
+ model: 'VES-ZB-WAL-012',
169
+ vendor: 'Vesternet',
170
+ description: 'Zigbee wall controller - 8 button',
171
+ fromZigbee: [fz.command_on, fz.command_off, fz.command_move, fz.command_stop, fz.battery, fz.ignore_genOta],
172
+ exposes: [e.battery(), e.action([
173
+ 'on_1', 'off_1', 'stop_1', 'brightness_move_up_1', 'brightness_move_down_1', 'brightness_stop_1',
174
+ 'on_2', 'off_2', 'stop_2', 'brightness_move_up_2', 'brightness_move_down_2', 'brightness_stop_2',
175
+ 'on_3', 'off_3', 'stop_3', 'brightness_move_up_3', 'brightness_move_down_3', 'brightness_stop_3',
176
+ 'on_4', 'off_4', 'stop_4', 'brightness_move_up_4', 'brightness_move_down_4', 'brightness_stop_4'])],
177
+ toZigbee: [],
178
+ meta: {multiEndpoint: true, battery: {dontDividePercentage: true}},
179
+ whiteLabel: [{vendor: 'Sunricher', model: 'SR-ZG9001K8-DIM'}],
180
+ configure: async (device, coordinatorEndpoint, logger) => {
181
+ await reporting.bind(device.getEndpoint(1), coordinatorEndpoint, ['genOnOff', 'genLevelCtrl', 'genPowerCfg']);
182
+ await reporting.bind(device.getEndpoint(2), coordinatorEndpoint, ['genOnOff', 'genLevelCtrl']);
183
+ await reporting.bind(device.getEndpoint(3), coordinatorEndpoint, ['genOnOff', 'genLevelCtrl']);
184
+ await reporting.bind(device.getEndpoint(4), coordinatorEndpoint, ['genOnOff', 'genLevelCtrl']);
185
+ await reporting.batteryPercentageRemaining(device.getEndpoint(1));
186
+ },
187
+ },
188
+ ];
package/devices/xiaomi.js CHANGED
@@ -1490,7 +1490,7 @@ module.exports = [
1490
1490
  exposes: [
1491
1491
  e.switch(), e.power().withAccess(ea.STATE_GET),
1492
1492
  e.energy(), e.device_temperature().withAccess(ea.STATE),
1493
- e.voltage().withAccess(ea.STATE), e.action(['single', 'release']),
1493
+ e.voltage(), e.action(['single', 'release']),
1494
1494
  exposes.enum('operation_mode', ea.ALL, ['control_relay', 'decoupled'])
1495
1495
  .withDescription('Decoupled mode'),
1496
1496
  ],
@@ -1539,7 +1539,7 @@ module.exports = [
1539
1539
  tz.xiaomi_led_disabled_night, tz.xiaomi_flip_indicator_light],
1540
1540
  exposes: [
1541
1541
  e.switch(), e.action(['single', 'double']), e.power().withAccess(ea.STATE), e.energy(),
1542
- e.voltage().withAccess(ea.STATE), e.device_temperature().withAccess(ea.STATE),
1542
+ e.voltage(), e.device_temperature().withAccess(ea.STATE),
1543
1543
  e.power_outage_memory(), e.led_disabled_night(), e.flip_indicator_light(),
1544
1544
  exposes.enum('operation_mode', ea.ALL, ['control_relay', 'decoupled'])
1545
1545
  .withDescription('Decoupled mode for left button'),
@@ -1561,7 +1561,7 @@ module.exports = [
1561
1561
  },
1562
1562
  exposes: [
1563
1563
  e.switch().withEndpoint('left'), e.switch().withEndpoint('right'),
1564
- e.power().withAccess(ea.STATE), e.energy(), e.voltage().withAccess(ea.STATE), e.flip_indicator_light(),
1564
+ e.power().withAccess(ea.STATE), e.energy(), e.voltage(), e.flip_indicator_light(),
1565
1565
  e.power_outage_memory(), e.led_disabled_night(), e.device_temperature().withAccess(ea.STATE),
1566
1566
  e.action([
1567
1567
  'single_left', 'double_left', 'single_right', 'double_right', 'single_both', 'double_both']),
@@ -1589,7 +1589,7 @@ module.exports = [
1589
1589
  },
1590
1590
  exposes: [
1591
1591
  e.switch().withEndpoint('left'), e.switch().withEndpoint('center'), e.switch().withEndpoint('right'),
1592
- e.power().withAccess(ea.STATE), e.energy(), e.voltage().withAccess(ea.STATE), e.flip_indicator_light(),
1592
+ e.power().withAccess(ea.STATE), e.energy(), e.voltage(), e.flip_indicator_light(),
1593
1593
  e.power_outage_memory(), e.led_disabled_night(), e.device_temperature().withAccess(ea.STATE),
1594
1594
  e.action([
1595
1595
  'single_left', 'double_left', 'single_center', 'double_center',
@@ -1908,7 +1908,7 @@ module.exports = [
1908
1908
  fromZigbee: [fz.on_off, fz.xiaomi_power, fz.xiaomi_basic, fz.ignore_occupancy_report, fz.ignore_illuminance_report],
1909
1909
  toZigbee: [tz.on_off, tz.xiaomi_power],
1910
1910
  exposes: [e.switch(), e.power().withAccess(ea.STATE_GET), e.energy(), e.device_temperature().withAccess(ea.STATE),
1911
- e.voltage().withAccess(ea.STATE)],
1911
+ e.voltage()],
1912
1912
  },
1913
1913
  {
1914
1914
  zigbeeModel: ['lumi.plug.mmeu01'],
@@ -1921,7 +1921,7 @@ module.exports = [
1921
1921
  tz.xiaomi_overload_protection],
1922
1922
  exposes: [
1923
1923
  e.switch(), e.power().withAccess(ea.STATE_GET), e.energy(), e.device_temperature().withAccess(ea.STATE),
1924
- e.voltage().withAccess(ea.STATE), e.current(), e.consumer_connected(), e.led_disabled_night(),
1924
+ e.voltage(), e.current(), e.consumer_connected(), e.led_disabled_night(),
1925
1925
  e.power_outage_memory(), exposes.binary('auto_off', ea.STATE_SET, true, false)
1926
1926
  .withDescription('Turn the device automatically off when attached device consumes less than 2W for 20 minutes'),
1927
1927
  exposes.numeric('overload_protection', exposes.access.ALL).withValueMin(100).withValueMax(2300).withUnit('W')
@@ -1938,7 +1938,7 @@ module.exports = [
1938
1938
  tz.xiaomi_overload_protection],
1939
1939
  exposes: [
1940
1940
  e.switch(), e.power().withAccess(ea.STATE_GET), e.energy(), e.device_temperature().withAccess(ea.STATE),
1941
- e.voltage().withAccess(ea.STATE), e.current(), e.consumer_connected(), e.led_disabled_night(),
1941
+ e.voltage(), e.current(), e.consumer_connected(), e.led_disabled_night(),
1942
1942
  e.power_outage_memory(), exposes.binary('auto_off', ea.STATE_SET, true, false)
1943
1943
  .withDescription('Turn the device automatically off when attached device consumes less than 2W for 20 minutes'),
1944
1944
  exposes.numeric('overload_protection', exposes.access.ALL).withValueMin(100).withValueMax(2300).withUnit('W')
@@ -2002,7 +2002,7 @@ module.exports = [
2002
2002
  }
2003
2003
  },
2004
2004
  exposes: [e.switch(), e.power(), e.energy(), e.power_outage_memory(),
2005
- e.voltage().withAccess(ea.STATE), e.current(),
2005
+ e.voltage(), e.current(),
2006
2006
  e.device_temperature().withDescription('Device temperature (polled every 30 min)')],
2007
2007
  ota: ota.zigbeeOTA,
2008
2008
  },
@@ -2014,7 +2014,7 @@ module.exports = [
2014
2014
  fromZigbee: [fz.on_off, fz.xiaomi_power, fz.ignore_occupancy_report, fz.xiaomi_basic],
2015
2015
  toZigbee: [tz.on_off, tz.xiaomi_power, tz.xiaomi_led_disabled_night,
2016
2016
  tz.xiaomi_switch_power_outage_memory, tz.xiaomi_auto_off],
2017
- exposes: [e.switch(), e.power().withAccess(ea.STATE_GET), e.energy(), e.device_temperature(), e.voltage().withAccess(ea.STATE),
2017
+ exposes: [e.switch(), e.power().withAccess(ea.STATE_GET), e.energy(), e.device_temperature(), e.voltage(),
2018
2018
  e.power_outage_memory(), e.led_disabled_night(),
2019
2019
  exposes.binary('auto_off', ea.STATE_SET, true, false)
2020
2020
  .withDescription('If the power is constantly lower than 2W within half an hour, ' +
@@ -2041,7 +2041,7 @@ module.exports = [
2041
2041
  fromZigbee: [fz.on_off, fz.xiaomi_power, fz.xiaomi_basic],
2042
2042
  toZigbee: [tz.on_off, tz.xiaomi_switch_power_outage_memory, tz.xiaomi_power],
2043
2043
  exposes: [e.switch(), e.power().withAccess(ea.STATE_GET), e.energy(), e.device_temperature().withAccess(ea.STATE),
2044
- e.voltage().withAccess(ea.STATE), e.power_outage_memory()],
2044
+ e.voltage(), e.power_outage_memory()],
2045
2045
  ota: ota.zigbeeOTA,
2046
2046
  },
2047
2047
  {
@@ -2576,7 +2576,7 @@ module.exports = [
2576
2576
  fromZigbee: [fz.on_off, fz.xiaomi_power, fz.aqara_opple],
2577
2577
  toZigbee: [tz.on_off, tz.xiaomi_power, tz.xiaomi_switch_type, tz.xiaomi_switch_power_outage_memory, tz.xiaomi_led_disabled_night],
2578
2578
  exposes: [e.switch(), e.power().withAccess(ea.STATE_GET), e.energy(), e.device_temperature().withAccess(ea.STATE),
2579
- e.voltage().withAccess(ea.STATE), e.power_outage_memory(), e.led_disabled_night(), e.switch_type()],
2579
+ e.voltage(), e.power_outage_memory(), e.led_disabled_night(), e.switch_type()],
2580
2580
  configure: async (device, coordinatorEndpoint, logger) => {
2581
2581
  await device.getEndpoint(1).write('aqaraOpple', {'mode': 1}, {manufacturerCode: 0x115f, disableResponse: true});
2582
2582
  device.powerSource = 'Mains (single phase)';
@@ -2822,7 +2822,7 @@ module.exports = [
2822
2822
  toZigbee: [tz.on_off, tz.xiaomi_switch_power_outage_memory, tz.xiaomi_led_disabled_night,
2823
2823
  tz.xiaomi_overload_protection, tz.xiaomi_socket_button_lock],
2824
2824
  exposes: [e.switch(), e.power().withAccess(ea.STATE), e.energy(), e.device_temperature().withAccess(ea.STATE),
2825
- e.voltage().withAccess(ea.STATE), e.current(), e.consumer_connected().withAccess(ea.STATE),
2825
+ e.voltage(), e.current(), e.consumer_connected().withAccess(ea.STATE),
2826
2826
  e.power_outage_memory(), e.led_disabled_night(), e.button_lock(),
2827
2827
  exposes.numeric('overload_protection', exposes.access.ALL).withValueMin(100).withValueMax(2500).withUnit('W')
2828
2828
  .withDescription('Maximum allowed load, turns off if exceeded')],
@@ -2891,7 +2891,7 @@ module.exports = [
2891
2891
  },
2892
2892
  exposes: [
2893
2893
  e.switch().withEndpoint('relay'), e.switch().withEndpoint('usb'),
2894
- e.power().withAccess(ea.STATE), e.energy(), e.device_temperature().withAccess(ea.STATE), e.voltage().withAccess(ea.STATE),
2894
+ e.power().withAccess(ea.STATE), e.energy(), e.device_temperature().withAccess(ea.STATE), e.voltage(),
2895
2895
  e.current(), e.power_outage_memory(), e.led_disabled_night(), e.button_lock(),
2896
2896
  exposes.enum('button_switch_mode', exposes.access.ALL, ['relay', 'relay_and_usb'])
2897
2897
  .withDescription('Control both relay and usb or only the relay with the physical switch button'),
@@ -2919,7 +2919,7 @@ module.exports = [
2919
2919
  tz.xiaomi_overload_protection, tz.xiaomi_socket_button_lock],
2920
2920
  exposes: [
2921
2921
  e.switch(), e.power().withAccess(ea.STATE), e.energy(),
2922
- e.device_temperature().withAccess(ea.STATE), e.voltage().withAccess(ea.STATE),
2922
+ e.device_temperature().withAccess(ea.STATE), e.voltage(),
2923
2923
  e.current(), e.power_outage_memory(), e.led_disabled_night(), e.button_lock(),
2924
2924
  exposes.numeric('overload_protection', exposes.access.ALL).withValueMin(100).withValueMax(2500).withUnit('W')
2925
2925
  .withDescription('Maximum allowed load, turns off if exceeded')],
package/lib/constants.js CHANGED
@@ -210,10 +210,16 @@ const armNotification = {
210
210
 
211
211
  // ID's from ZCL mapped to ha names where appropriate
212
212
  // https://github.com/home-assistant/core/pull/47720
213
- const colorMode = {
214
- 0: 'hs',
215
- 1: 'xy',
216
- 2: 'color_temp',
213
+ const ColorMode = {
214
+ HS: 0,
215
+ XY: 1,
216
+ ColorTemp: 2,
217
+ };
218
+
219
+ const colorModeLookup = {
220
+ [ColorMode.HS]: 'hs',
221
+ [ColorMode.XY]: 'xy',
222
+ [ColorMode.ColorTemp]: 'color_temp',
217
223
  };
218
224
 
219
225
  const lockSoundVolume = ['silent_mode', 'low_volume', 'high_volume'];
@@ -294,7 +300,8 @@ module.exports = {
294
300
  lockSourceName,
295
301
  armMode,
296
302
  armNotification,
297
- colorMode,
303
+ colorModeLookup,
304
+ ColorMode,
298
305
  lockSoundVolume,
299
306
  lockUserStatus,
300
307
  easyCodeTouchActions,
package/lib/tuya.js CHANGED
@@ -1868,7 +1868,7 @@ const tuyaExtend = {
1868
1868
 
1869
1869
  if (options.electricalMeasurements) {
1870
1870
  fromZigbee.push(fz.electrical_measurement, fz.metering);
1871
- exposes.push(e.power(), e.current(), e.voltage().withAccess(ea.STATE), e.energy());
1871
+ exposes.push(e.power(), e.current(), e.voltage(), e.energy());
1872
1872
  }
1873
1873
  if (options.childLock) {
1874
1874
  fromZigbee.push(tuyaFz.child_lock);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zigbee-herdsman-converters",
3
- "version": "15.0.52",
3
+ "version": "15.0.54",
4
4
  "description": "Collection of device converters to be used with zigbee-herdsman",
5
5
  "main": "index.js",
6
6
  "files": [