zigbee-herdsman-converters 14.0.444 → 14.0.445

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.
@@ -428,7 +428,7 @@ const converters = {
428
428
  convert: (model, msg, publish, options, meta) => {
429
429
  if (msg.data.hasOwnProperty('occupancy')) {
430
430
  const payload = {occupancy: (msg.data.occupancy % 2) > 0};
431
- utils.noOccupancySince(msg.endpoint, payload, options, publish, payload.occupancy ? 'stop' : 'start');
431
+ utils.noOccupancySince(msg.endpoint, options, publish, payload.occupancy ? 'stop' : 'start');
432
432
  return payload;
433
433
  }
434
434
  },
@@ -464,7 +464,7 @@ const converters = {
464
464
  }
465
465
 
466
466
  const payload = {occupancy: true};
467
- utils.noOccupancySince(msg.endpoint, payload, options, publish, 'start');
467
+ utils.noOccupancySince(msg.endpoint, options, publish, 'start');
468
468
  return payload;
469
469
  },
470
470
  },
@@ -2790,6 +2790,33 @@ const converters = {
2790
2790
  }
2791
2791
  },
2792
2792
  },
2793
+ livolo_new_switch_state_4gang: {
2794
+ cluster: 'genPowerCfg',
2795
+ type: ['raw'],
2796
+ convert: (model, msg, publish, options, meta) => {
2797
+ const stateHeader = Buffer.from([122, 209]);
2798
+ if (msg.data.indexOf(stateHeader) === 0) {
2799
+ if (msg.data[10] === 7) {
2800
+ const status = msg.data[14];
2801
+ return {
2802
+ state_left: status & 1 ? 'ON' : 'OFF',
2803
+ state_right: status & 2 ? 'ON' : 'OFF',
2804
+ state_bottom_left: status & 4 ? 'ON' : 'OFF',
2805
+ state_bottom_right: status & 8 ? 'ON' : 'OFF',
2806
+ };
2807
+ }
2808
+ if (msg.data[10] === 13) {
2809
+ const status = msg.data[13];
2810
+ return {
2811
+ state_left: status & 1 ? 'ON' : 'OFF',
2812
+ state_right: status & 2 ? 'ON' : 'OFF',
2813
+ state_bottom_left: status & 4 ? 'ON' : 'OFF',
2814
+ state_bottom_right: status & 8 ? 'ON' : 'OFF',
2815
+ };
2816
+ }
2817
+ }
2818
+ },
2819
+ },
2793
2820
  livolo_curtain_switch_state: {
2794
2821
  cluster: 'genPowerCfg',
2795
2822
  type: ['raw'],
@@ -2949,7 +2976,8 @@ const converters = {
2949
2976
  meta.device.modelID = 'TI0001-socket';
2950
2977
  meta.device.save();
2951
2978
  }
2952
- if (msg.data.includes(Buffer.from([19, 1, 0]), 13)) {
2979
+ // No need to detect this switches, will be done by universal procedure
2980
+ /* if (msg.data.includes(Buffer.from([19, 1, 0]), 13)) {
2953
2981
  // new switch, hack
2954
2982
  meta.device.modelID = 'TI0001-switch';
2955
2983
  meta.device.save();
@@ -2958,7 +2986,7 @@ const converters = {
2958
2986
  // new switch, hack
2959
2987
  meta.device.modelID = 'TI0001-switch-2gang';
2960
2988
  meta.device.save();
2961
- }
2989
+ }*/
2962
2990
  if (msg.data.includes(Buffer.from([19, 5, 0]), 13)) {
2963
2991
  if (meta.logger) meta.logger.debug('Detected Livolo Curtain Switch');
2964
2992
  // curtain switch, hack
@@ -5231,40 +5259,8 @@ const converters = {
5231
5259
  type: ['attributeReport', 'readResponse'],
5232
5260
  options: [exposes.options.precision('temperature'), exposes.options.calibration('temperature')],
5233
5261
  convert: (model, msg, publish, options, meta) => {
5234
- if (msg.data['65281']) {
5235
- const data = msg.data['65281'];
5236
- const payload = {};
5237
-
5238
- if (data.hasOwnProperty('100')) {
5239
- if (['QBKG03LM', 'QBKG12LM', 'LLKZMK11LM'].includes(model.model)) {
5240
- const mapping = model.model === 'LLKZMK11LM' ? ['l1', 'l2'] : ['left', 'right'];
5241
- payload[`state_${mapping[0]}`] = data['100'] === 1 ? 'ON' : 'OFF';
5242
- payload[`state_${mapping[1]}`] = data['101'] === 1 ? 'ON' : 'OFF';
5243
- } else {
5244
- payload.state = data['100'] === 1 ? 'ON' : 'OFF';
5245
- }
5246
- }
5247
-
5248
- if (data.hasOwnProperty('152')) {
5249
- payload.power = precisionRound(data['152'], 2);
5250
- }
5251
-
5252
- if (data.hasOwnProperty('149')) {
5253
- // Consumption is deprecated
5254
- payload.consumption = precisionRound(data['149'], 2);
5255
- payload.energy = precisionRound(data['149'], 2);
5256
- }
5257
-
5258
- if (data.hasOwnProperty('3')) {
5259
- payload.temperature = calibrateAndPrecisionRoundOptions(data['3'], options, 'temperature');
5260
- }
5261
-
5262
- if (data.hasOwnProperty('150')) {
5263
- payload.voltage = precisionRound(data['150'] * 0.1, 1);
5264
- }
5265
-
5266
- return payload;
5267
- }
5262
+ const payload = xiaomi.numericAttributes2Payload(msg, meta, model, options, msg.data);
5263
+ return payload;
5268
5264
  },
5269
5265
  },
5270
5266
  xiaomi_basic_raw: {
@@ -1599,13 +1599,23 @@ const converters = {
1599
1599
  }
1600
1600
 
1601
1601
  const state = value.toLowerCase();
1602
+ let oldstate = 1;
1603
+ if (state === 'on') {
1604
+ oldstate = 108;
1605
+ }
1606
+ let channel = 1.0;
1602
1607
  const postfix = meta.endpoint_name || 'left';
1603
1608
  await entity.command('genOnOff', 'toggle', {}, {transactionSequenceNumber: 0});
1604
1609
  const payloadOn = {0x0001: {value: Buffer.from([1, 0, 0, 0, 0, 0, 0, 0]), type: 1}};
1605
1610
  const payloadOff = {0x0001: {value: Buffer.from([0, 0, 0, 0, 0, 0, 0, 0]), type: 1}};
1606
1611
  const payloadOnRight = {0x0001: {value: Buffer.from([2, 0, 0, 0, 0, 0, 0, 0]), type: 2}};
1607
1612
  const payloadOffRight = {0x0001: {value: Buffer.from([0, 0, 0, 0, 0, 0, 0, 0]), type: 2}};
1613
+ const payloadOnBottomLeft = {0x0001: {value: Buffer.from([4, 0, 0, 0, 0, 0, 0, 0]), type: 4}};
1614
+ const payloadOffBottomLeft = {0x0001: {value: Buffer.from([0, 0, 0, 0, 0, 0, 0, 0]), type: 4}};
1615
+ const payloadOnBottomRight = {0x0001: {value: Buffer.from([8, 0, 0, 0, 0, 0, 0, 0]), type: 136}};
1616
+ const payloadOffBottomRight = {0x0001: {value: Buffer.from([0, 0, 0, 0, 0, 0, 0, 0]), type: 136}};
1608
1617
  if (postfix === 'left') {
1618
+ await entity.command('genLevelCtrl', 'moveToLevelWithOnOff', {level: oldstate, transtime: channel});
1609
1619
  await entity.write('genPowerCfg', (state === 'on') ? payloadOn : payloadOff,
1610
1620
  {
1611
1621
  manufacturerCode: 0x1ad2, disableDefaultResponse: true, disableResponse: true,
@@ -1613,12 +1623,28 @@ const converters = {
1613
1623
  });
1614
1624
  return {state: {state_left: value.toUpperCase()}, readAfterWriteTime: 250};
1615
1625
  } else if (postfix === 'right') {
1626
+ channel = 2.0;
1627
+ await entity.command('genLevelCtrl', 'moveToLevelWithOnOff', {level: oldstate, transtime: channel});
1616
1628
  await entity.write('genPowerCfg', (state === 'on') ? payloadOnRight : payloadOffRight,
1617
1629
  {
1618
1630
  manufacturerCode: 0x1ad2, disableDefaultResponse: true, disableResponse: true,
1619
1631
  reservedBits: 3, direction: 1, transactionSequenceNumber: 0xe9,
1620
1632
  });
1621
1633
  return {state: {state_right: value.toUpperCase()}, readAfterWriteTime: 250};
1634
+ } else if (postfix === 'bottom_right') {
1635
+ await entity.write('genPowerCfg', (state === 'on') ? payloadOnBottomRight : payloadOffBottomRight,
1636
+ {
1637
+ manufacturerCode: 0x1ad2, disableDefaultResponse: true, disableResponse: true,
1638
+ reservedBits: 3, direction: 1, transactionSequenceNumber: 0xe9,
1639
+ });
1640
+ return {state: {state_bottom_right: value.toUpperCase()}, readAfterWriteTime: 250};
1641
+ } else if (postfix === 'bottom_left') {
1642
+ await entity.write('genPowerCfg', (state === 'on') ? payloadOnBottomLeft : payloadOffBottomLeft,
1643
+ {
1644
+ manufacturerCode: 0x1ad2, disableDefaultResponse: true, disableResponse: true,
1645
+ reservedBits: 3, direction: 1, transactionSequenceNumber: 0xe9,
1646
+ });
1647
+ return {state: {state_bottom_left: value.toUpperCase()}, readAfterWriteTime: 250};
1622
1648
  }
1623
1649
  return {state: {state: value.toUpperCase()}, readAfterWriteTime: 250};
1624
1650
  },
package/devices/livolo.js CHANGED
@@ -20,13 +20,18 @@ module.exports = [
20
20
  {
21
21
  zigbeeModel: ['TI0001 '],
22
22
  model: 'TI0001',
23
- description: 'Zigbee switch (1 and 2 gang)',
23
+ description: 'Zigbee switch (1, 2, 3, 4 gang)',
24
24
  vendor: 'Livolo',
25
- exposes: [e.switch().withEndpoint('left'), e.switch().withEndpoint('right')],
26
- fromZigbee: [fz.livolo_switch_state, fz.livolo_switch_state_raw],
27
- toZigbee: [tz.livolo_switch_on_off],
25
+ exposes: [
26
+ e.switch().withEndpoint('left'),
27
+ e.switch().withEndpoint('right'),
28
+ e.switch().withEndpoint('bottom_left'),
29
+ e.switch().withEndpoint('bottom_right'),
30
+ ],
31
+ fromZigbee: [fz.livolo_switch_state, fz.livolo_switch_state_raw, fz.livolo_new_switch_state_4gang],
32
+ toZigbee: [tz.livolo_socket_switch_on_off],
28
33
  endpoint: (device) => {
29
- return {'left': 6, 'right': 6};
34
+ return {'left': 6, 'right': 6, 'bottom_left': 6, 'bottom_right': 6};
30
35
  },
31
36
  configure: poll,
32
37
  onEvent: async (type, data, device) => {
@@ -41,6 +46,18 @@ module.exports = [
41
46
  globalStore.putValue(device, 'interval', interval);
42
47
  }
43
48
  }
49
+ if (data.cluster === 'genPowerCfg' && data.type === 'raw') {
50
+ const dp = data.data[10];
51
+ if (data.data[0] === 0x7a && data.data[1] === 0xd1) {
52
+ const endpoint = device.getEndpoint(6);
53
+ if (dp === 0x01) {
54
+ const options = {manufacturerCode: 0x1ad2, disableDefaultResponse: true, disableResponse: true,
55
+ reservedBits: 3, direction: 1, writeUndiv: true};
56
+ const payload = {0x2002: {value: [0, 0, 0, 0, 0, 0, 0], type: 0x0e}};
57
+ await endpoint.readResponse('genPowerCfg', 0xe9, payload, options);
58
+ }
59
+ }
60
+ }
44
61
  },
45
62
  },
46
63
  {
package/devices/woox.js CHANGED
@@ -13,10 +13,10 @@ module.exports = [
13
13
  model: 'R7060',
14
14
  vendor: 'Woox',
15
15
  description: 'Smart garden irrigation control',
16
- fromZigbee: [fz.on_off, fz.ignore_tuya_set_time, fz.ignore_basic_report, fz.woox_R7060],
16
+ fromZigbee: [fz.on_off, fz.ignore_tuya_set_time, fz.ignore_basic_report, fz.woox_R7060, fz.battery],
17
17
  toZigbee: [tz.on_off],
18
18
  onEvent: tuya.onEventSetTime,
19
- exposes: [e.switch()],
19
+ exposes: [e.switch(), e.battery(), e.battery_voltage()],
20
20
  meta: {disableDefaultResponse: true},
21
21
  },
22
22
  {
package/devices/xiaomi.js CHANGED
@@ -1298,7 +1298,7 @@ module.exports = [
1298
1298
  endpoint: (device) => {
1299
1299
  return {'l1': 1, 'l2': 2};
1300
1300
  },
1301
- exposes: [e.power().withAccess(ea.STATE_GET), e.energy(), e.temperature(), e.voltage().withAccess(ea.STATE),
1301
+ exposes: [e.power().withAccess(ea.STATE_GET), e.energy(), e.temperature(), e.voltage().withAccess(ea.STATE), e.current(),
1302
1302
  e.switch().withEndpoint('l1'), e.switch().withEndpoint('l2'),
1303
1303
  exposes.binary('interlock', ea.STATE_SET, true, false)
1304
1304
  .withDescription('Enabling prevents both relais being on at the same time')],
@@ -1,5 +1,4 @@
1
1
  const url = 'https://raw.githubusercontent.com/Koenkk/zigbee-OTA/master/index.json';
2
- const assert = require('assert');
3
2
  const common = require('./common');
4
3
  const axios = common.getAxios();
5
4
  const fs = require('fs');
@@ -110,7 +109,10 @@ async function getImageMeta(current, logger, device) {
110
109
  (!i.minFileVersion || current.fileVersion >= i.minFileVersion) && (!i.maxFileVersion || current.fileVersion <= i.maxFileVersion) &&
111
110
  (!i.modelId || i.modelId === modelId) && (!i.manufacturerName || i.manufacturerName.includes(manufacturerName)));
112
111
 
113
- assert(image !== undefined, `No image available for imageType '${imageType}'`);
112
+ if (!image) {
113
+ throw new Error(`No image available for imageType '${imageType}'`);
114
+ }
115
+
114
116
  return {
115
117
  fileVersion: image.fileVersion,
116
118
  fileSize: image.fileSize,
package/lib/utils.js CHANGED
@@ -397,13 +397,12 @@ function extendDevice(deviceConfigs, modelName, adjustments) {
397
397
  return {...baseDevice, ...adjustments};
398
398
  }
399
399
 
400
- function noOccupancySince(endpoint, newPayload, options, publish, action) {
400
+ function noOccupancySince(endpoint, options, publish, action) {
401
401
  if (options && options.no_occupancy_since) {
402
402
  if (action == 'start') {
403
403
  globalStore.getValue(endpoint, 'no_occupancy_since_timers', []).forEach((t) => clearTimeout(t));
404
404
  globalStore.putValue(endpoint, 'no_occupancy_since_timers', []);
405
405
 
406
- newPayload.no_occupancy_since = 0;
407
406
  options.no_occupancy_since.forEach((since) => {
408
407
  const timer = setTimeout(() => {
409
408
  publish({no_occupancy_since: since});
package/lib/xiaomi.js CHANGED
@@ -168,7 +168,7 @@ const numericAttributes2Payload = (msg, meta, model, options, dataObject) => {
168
168
  payload.switch_type = {1: 'toggle', 2: 'momentary'}[value];
169
169
  break;
170
170
  case '100':
171
- if (['QBKG20LM', 'QBKG31LM', 'QBKG39LM', 'QBKG41LM', 'QBCZ15LM', 'LLKZMK11LM'].includes(model.model)) {
171
+ if (['QBKG20LM', 'QBKG31LM', 'QBKG39LM', 'QBKG41LM', 'QBCZ15LM', 'LLKZMK11LM', 'QBKG12LM', 'QBKG03LM'].includes(model.model)) {
172
172
  let mapping;
173
173
  switch (model.model) {
174
174
  case 'QBCZ15LM':
@@ -191,7 +191,7 @@ const numericAttributes2Payload = (msg, meta, model, options, dataObject) => {
191
191
  }
192
192
  break;
193
193
  case '101':
194
- if (['QBKG20LM', 'QBKG31LM', 'QBKG39LM', 'QBKG41LM', 'QBCZ15LM', 'QBKG25LM', 'QBKG34LM', 'LLKZMK11LM']
194
+ if (['QBKG20LM', 'QBKG31LM', 'QBKG39LM', 'QBKG41LM', 'QBCZ15LM', 'QBKG25LM', 'QBKG34LM', 'LLKZMK11LM', 'QBKG12LM', 'QBKG03LM']
195
195
  .includes(model.model)) {
196
196
  let mapping;
197
197
  switch (model.model) {
@@ -244,7 +244,11 @@ const numericAttributes2Payload = (msg, meta, model, options, dataObject) => {
244
244
  payload.voltage = precisionRound(value * 0.1, 1); // 0x96
245
245
  break;
246
246
  case '151':
247
- payload.current = precisionRound(value * 0.001, 4); // 0x97
247
+ if (['LLKZMK11LM'].includes(model.model)) {
248
+ payload.current = precisionRound(value, 4);
249
+ } else {
250
+ payload.current = precisionRound(value * 0.001, 4);
251
+ }
248
252
  break;
249
253
  case '152':
250
254
  payload.power = precisionRound(value, 2); // 0x98
@@ -355,6 +359,12 @@ const numericAttributes2Payload = (msg, meta, model, options, dataObject) => {
355
359
  case '1289':
356
360
  payload.dimmer_mode = {3: 'rgbw', 1: 'dual_ct'}[value];
357
361
  break;
362
+ case '65281':
363
+ {
364
+ const payload65281 = numericAttributes2Payload(msg, meta, model, options, value);
365
+ payload = {...payload, ...payload65281};
366
+ }
367
+ break;
358
368
  case 'mode':
359
369
  payload.operation_mode = ['command', 'event'][value];
360
370
  break;
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zigbee-herdsman-converters",
3
- "version": "14.0.444",
3
+ "version": "14.0.445",
4
4
  "lockfileVersion": 1,
5
5
  "requires": true,
6
6
  "dependencies": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zigbee-herdsman-converters",
3
- "version": "14.0.444",
3
+ "version": "14.0.445",
4
4
  "description": "Collection of device converters to be used with zigbee-herdsman",
5
5
  "main": "index.js",
6
6
  "files": [