zigbee-herdsman-converters 14.0.264 → 14.0.268

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.
@@ -2517,6 +2517,18 @@ const converters = {
2517
2517
  }
2518
2518
  },
2519
2519
  },
2520
+ easycodetouch_action: {
2521
+ cluster: 'closuresDoorLock',
2522
+ type: 'raw',
2523
+ convert: (model, msg, publish, options, meta) => {
2524
+ const value = constants.easyCodeTouchActions[(msg.data[3] << 8) | msg.data[4]];
2525
+ if (value) {
2526
+ return {action: value};
2527
+ } else {
2528
+ meta.logger.log('Unknown lock status with source ' + msg.data[3] + ' and event code ' + msg.data[4]);
2529
+ }
2530
+ },
2531
+ },
2520
2532
  livolo_switch_state_raw: {
2521
2533
  cluster: 'genPowerCfg',
2522
2534
  type: ['raw'],
@@ -3757,6 +3769,16 @@ const converters = {
3757
3769
  });
3758
3770
  },
3759
3771
  },
3772
+ tuya_switch_type: {
3773
+ cluster: 'manuSpecificTuya_3',
3774
+ type: ['attributeReport', 'readResponse'],
3775
+ convert: (model, msg, publish, options, meta) => {
3776
+ const lookup = {0: 'toggle', 1: 'state', 2: 'momentary'};
3777
+ if (msg.data.hasOwnProperty('switchType')) {
3778
+ return {switch_type: lookup[msg.data['switchType']]};
3779
+ }
3780
+ },
3781
+ },
3760
3782
  restorable_brightness: {
3761
3783
  cluster: 'genLevelCtrl',
3762
3784
  type: ['attributeReport', 'readResponse'],
@@ -6059,6 +6081,18 @@ const converters = {
6059
6081
  }
6060
6082
  },
6061
6083
  },
6084
+ ias_keypad: {
6085
+ cluster: 'ssIasZone',
6086
+ type: 'commandStatusChangeNotification',
6087
+ convert: (model, msg, publish, options, meta) => {
6088
+ const zoneStatus = msg.data.zonestatus;
6089
+ return {
6090
+ tamper: (zoneStatus & 1<<2) > 0,
6091
+ battery_low: (zoneStatus & 1<<3) > 0,
6092
+ restore_reports: (zoneStatus & 1<<5) > 0,
6093
+ };
6094
+ },
6095
+ },
6062
6096
  ubisys_dimmer_setup: {
6063
6097
  cluster: 'manuSpecificUbisysDimmerSetup',
6064
6098
  type: ['attributeReport', 'readResponse'],
@@ -6509,8 +6543,15 @@ const converters = {
6509
6543
  convert: (model, msg, publish, options, meta) => {
6510
6544
  const result = {};
6511
6545
  if (0x8000 in msg.data) {
6512
- result.current_firmware = msg.data[0x8000].join('.');
6546
+ const firmware = msg.data[0x8000].join('.');
6547
+ result.current_firmware = firmware;
6548
+ msg.device.zhDevice.softwareBuildID = firmware;
6513
6549
  }
6550
+
6551
+ if (0x8020 in msg.data) {
6552
+ msg.device.zhDevice.hardwareVersion = msg.data[0x8020].join('.');
6553
+ }
6554
+
6514
6555
  return result;
6515
6556
  },
6516
6557
  },
@@ -2869,6 +2869,19 @@ const converters = {
2869
2869
  return {state: {state: value.toUpperCase()}};
2870
2870
  },
2871
2871
  },
2872
+ tuya_switch_type: {
2873
+ key: ['switch_type'],
2874
+ convertSet: async (entity, key, value, meta) => {
2875
+ value = value.toLowerCase();
2876
+ const lookup = {'toggle': 0, 'state': 1, 'momentary': 2};
2877
+ utils.validateValue(value, Object.keys(lookup));
2878
+ await entity.write('manuSpecificTuya_3', {'switchType': lookup[value]}, {disableDefaultResponse: true});
2879
+ return {state: {switch_type: value}};
2880
+ },
2881
+ convertGet: async (entity, key, meta) => {
2882
+ await entity.read('manuSpecificTuya_3', ['switchType']);
2883
+ },
2884
+ },
2872
2885
  frankever_threshold: {
2873
2886
  key: ['threshold'],
2874
2887
  convertSet: async (entity, key, value, meta) => {
@@ -4612,17 +4625,23 @@ const converters = {
4612
4625
  convertSet: async (entity, key, value, meta) => {
4613
4626
  const isGroup = entity.constructor.name === 'Group';
4614
4627
  const groupid = isGroup ? entity.groupID : 0;
4615
- const sceneid = value;
4628
+ let sceneid = value;
4629
+ let scenename = null;
4630
+ if (typeof value === 'object') {
4631
+ sceneid = value.ID;
4632
+ scenename = value.name;
4633
+ }
4634
+
4616
4635
  const response = await entity.command('genScenes', 'store', {groupid, sceneid}, utils.getOptions(meta.mapped));
4617
4636
 
4618
4637
  if (isGroup) {
4619
4638
  if (meta.membersState) {
4620
4639
  for (const member of entity.members) {
4621
- utils.saveSceneState(member, sceneid, groupid, meta.membersState[member.getDevice().ieeeAddr]);
4640
+ utils.saveSceneState(member, sceneid, groupid, meta.membersState[member.getDevice().ieeeAddr], scenename);
4622
4641
  }
4623
4642
  }
4624
4643
  } else if (response.status === 0) {
4625
- utils.saveSceneState(entity, sceneid, groupid, meta.state);
4644
+ utils.saveSceneState(entity, sceneid, groupid, meta.state, scenename);
4626
4645
  } else {
4627
4646
  throw new Error(`Scene add not succesfull ('${herdsman.Zcl.Status[response.status]}')`);
4628
4647
  }
@@ -4652,15 +4671,11 @@ const converters = {
4652
4671
  };
4653
4672
 
4654
4673
  const isGroup = entity.constructor.name === 'Group';
4655
- const metaKey = `${sceneid}_${groupid}`;
4656
4674
  if (isGroup) {
4657
4675
  const membersState = {};
4658
4676
  for (const member of entity.members) {
4659
- if (member.meta.hasOwnProperty('scenes') && member.meta.scenes.hasOwnProperty(metaKey)) {
4660
- membersState[member.getDevice().ieeeAddr] = addColorMode(member.meta.scenes[metaKey].state);
4661
-
4662
- let recalledState = member.meta.scenes[metaKey].state;
4663
-
4677
+ let recalledState = utils.getSceneState(member, sceneid, groupid);
4678
+ if (recalledState) {
4664
4679
  // add color_mode if saved state does not contain it
4665
4680
  if (!recalledState.hasOwnProperty('color_mode')) {
4666
4681
  recalledState = addColorMode(recalledState);
@@ -4675,9 +4690,8 @@ const converters = {
4675
4690
  }
4676
4691
  return {membersState};
4677
4692
  } else {
4678
- if (entity.meta.hasOwnProperty('scenes') && entity.meta.scenes.hasOwnProperty(metaKey)) {
4679
- let recalledState = entity.meta.scenes[metaKey].state;
4680
-
4693
+ let recalledState = utils.getSceneState(entity, sceneid, groupid);
4694
+ if (recalledState) {
4681
4695
  // add color_mode if saved state does not contain it
4682
4696
  if (!recalledState.hasOwnProperty('color_mode')) {
4683
4697
  recalledState = addColorMode(recalledState);
@@ -4711,7 +4725,7 @@ const converters = {
4711
4725
  const isGroup = entity.constructor.name === 'Group';
4712
4726
  const groupid = isGroup ? entity.groupID : 0;
4713
4727
  const sceneid = value.ID;
4714
- const scenename = '';
4728
+ const scenename = value.name;
4715
4729
  const transtime = value.hasOwnProperty('transition') ? value.transition : 0;
4716
4730
 
4717
4731
  const state = {};
@@ -4813,17 +4827,17 @@ const converters = {
4813
4827
 
4814
4828
  if (isGroup || (removeresp.status === 0 || removeresp.status == 133 || removeresp.status == 139)) {
4815
4829
  const response = await entity.command(
4816
- 'genScenes', 'add', {groupid, sceneid, scenename, transtime, extensionfieldsets}, utils.getOptions(meta.mapped),
4830
+ 'genScenes', 'add', {groupid, sceneid, scenename: '', transtime, extensionfieldsets}, utils.getOptions(meta.mapped),
4817
4831
  );
4818
4832
 
4819
4833
  if (isGroup) {
4820
4834
  if (meta.membersState) {
4821
4835
  for (const member of entity.members) {
4822
- utils.saveSceneState(member, sceneid, groupid, state);
4836
+ utils.saveSceneState(member, sceneid, groupid, state, scenename);
4823
4837
  }
4824
4838
  }
4825
4839
  } else if (response.status === 0) {
4826
- utils.saveSceneState(entity, sceneid, groupid, state);
4840
+ utils.saveSceneState(entity, sceneid, groupid, state, scenename);
4827
4841
  } else {
4828
4842
  throw new Error(`Scene add not succesfull ('${herdsman.Zcl.Status[response.status]}')`);
4829
4843
  }
@@ -4844,21 +4858,14 @@ const converters = {
4844
4858
  );
4845
4859
 
4846
4860
  const isGroup = entity.constructor.name === 'Group';
4847
- const metaKey = `${sceneid}_${groupid}`;
4848
4861
  if (isGroup) {
4849
4862
  if (meta.membersState) {
4850
4863
  for (const member of entity.members) {
4851
- if (member.meta.scenes && member.meta.scenes.hasOwnProperty(metaKey)) {
4852
- delete member.meta.scenes[metaKey];
4853
- member.save();
4854
- }
4864
+ utils.deleteSceneState(member, sceneid, groupid);
4855
4865
  }
4856
4866
  }
4857
4867
  } else if (response.status === 0) {
4858
- if (entity.meta.scenes && entity.meta.scenes.hasOwnProperty(metaKey)) {
4859
- delete entity.meta.scenes[metaKey];
4860
- entity.save();
4861
- }
4868
+ utils.deleteSceneState(entity, sceneid, groupid);
4862
4869
  } else {
4863
4870
  throw new Error(`Scene remove not succesfull ('${herdsman.Zcl.Status[response.status]}')`);
4864
4871
  }
@@ -4876,17 +4883,11 @@ const converters = {
4876
4883
  if (isGroup) {
4877
4884
  if (meta.membersState) {
4878
4885
  for (const member of entity.members) {
4879
- if (member.meta.scenes) {
4880
- member.meta.scenes = {};
4881
- member.save();
4882
- }
4886
+ utils.deleteSceneState(member);
4883
4887
  }
4884
4888
  }
4885
4889
  } else if (response.status === 0) {
4886
- if (entity.meta.scenes) {
4887
- entity.meta.scenes = {};
4888
- entity.save();
4889
- }
4890
+ utils.deleteSceneState(entity);
4890
4891
  } else {
4891
4892
  throw new Error(`Scene remove all not succesfull ('${herdsman.Zcl.Status[response.status]}')`);
4892
4893
  }
package/devices/climax.js CHANGED
@@ -111,4 +111,14 @@ module.exports = [
111
111
  toZigbee: [],
112
112
  exposes: [e.carbon_monoxide(), e.battery_low(), e.tamper(), e.battery()],
113
113
  },
114
+ {
115
+ zigbeeModel: ['KP-ACE_00.00.03.12TC'],
116
+ model: 'KP-23EL-ZBS-ACE',
117
+ vendor: 'Climax',
118
+ description: 'Remote Keypad',
119
+ fromZigbee: [fz.ias_keypad, fz.battery, fz.command_arm, fz.command_panic, fz.command_emergency],
120
+ toZigbee: [],
121
+ exposes: [e.battery_low(), e.tamper(), e.action(['emergency', 'panic', 'disarm', 'arm_all_zones', 'arm_day_zones']),
122
+ ],
123
+ },
114
124
  ];
@@ -112,11 +112,13 @@ module.exports = [
112
112
  model: 'EMIZB-132',
113
113
  vendor: 'Develco',
114
114
  description: 'Wattle AMS HAN power-meter sensor',
115
- fromZigbee: [fz.metering, fz.electrical_measurement],
115
+ fromZigbee: [fz.metering, fz.electrical_measurement, fz.develco_fw],
116
116
  toZigbee: [tz.EMIZB_132_mode],
117
117
  ota: ota.zigbeeOTA,
118
118
  configure: async (device, coordinatorEndpoint, logger) => {
119
119
  const endpoint = device.getEndpoint(2);
120
+ const options = {manufacturerCode: 4117};
121
+ await endpoint.read('genBasic', [0x8000, 0x8010, 0x8020], options);
120
122
  await reporting.bind(endpoint, coordinatorEndpoint, ['haElectricalMeasurement', 'seMetering']);
121
123
 
122
124
  try {
@@ -162,7 +164,7 @@ module.exports = [
162
164
 
163
165
  await reporting.bind(endpoint, coordinatorEndpoint, ['genPowerCfg', 'ssIasZone', 'ssIasWd', 'genBasic', 'genBinaryInput']);
164
166
  await reporting.batteryVoltage(endpoint);
165
- await endpoint.read('genBasic', [0x8000], options);
167
+ await endpoint.read('genBasic', [0x8000, 0x8010, 0x8020], options);
166
168
  await endpoint.read('ssIasZone', ['iasCieAddr', 'zoneState', 'zoneId']);
167
169
  await endpoint.read('genBinaryInput', ['reliability', 'statusFlags']);
168
170
  await endpoint.read('ssIasWd', ['maxDuration']);
package/devices/neo.js CHANGED
@@ -36,7 +36,7 @@ module.exports = [
36
36
  fromZigbee: [fz.neo_nas_pd07],
37
37
  toZigbee: [],
38
38
  onEvent: tuya.setTime,
39
- exposes: [e.occupancy(), e.humidity(), e.temperature(), e.tamper(),
39
+ exposes: [e.occupancy(), e.humidity(), e.temperature(), e.tamper(), e.battery_low(),
40
40
  exposes.enum('power_type', ea.STATE, ['battery_full', 'battery_high', 'battery_medium', 'battery_low', 'usb'])],
41
41
  },
42
42
  ];
package/devices/onesti.js CHANGED
@@ -12,8 +12,9 @@ module.exports = [
12
12
  model: 'easyCodeTouch_v1',
13
13
  vendor: 'Onesti Products AS',
14
14
  description: 'Zigbee module for EasyAccess code touch series',
15
- fromZigbee: [fz.lock, fz.lock_operation_event, fz.battery, fz.lock_programming_event],
16
- toZigbee: [tz.lock, tz.lock_sound_volume],
15
+ fromZigbee: [fz.lock, fz.lock_operation_event, fz.battery, fz.lock_programming_event, fz.easycodetouch_action],
16
+ toZigbee: [tz.lock, tz.easycode_auto_relock, tz.lock_sound_volume, tz.pincode_lock],
17
+ meta: {pinCodeCount: 50},
17
18
  configure: async (device, coordinatorEndpoint, logger) => {
18
19
  const endpoint = device.getEndpoint(11);
19
20
  await reporting.bind(endpoint, coordinatorEndpoint, ['closuresDoorLock', 'genPowerCfg']);
@@ -21,8 +22,11 @@ module.exports = [
21
22
  await reporting.batteryPercentageRemaining(endpoint);
22
23
  await endpoint.read('closuresDoorLock', ['lockState', 'soundVolume']);
23
24
  },
24
- exposes: [e.lock(), e.battery(),
25
- exposes.enum('sound_volume', ea.ALL, constants.lockSoundVolume).withDescription('Sound volume of the lock')],
25
+ exposes: [e.lock(), e.battery(), e.sound_volume(),
26
+ e.action(Array.from(Object.values(constants.easyCodeTouchActions))),
27
+ exposes.binary('auto_relock', ea.STATE_SET, true, false).withDescription('Auto relock after 7 seconds.'),
28
+ e.pincode(),
29
+ ],
26
30
  },
27
31
  {
28
32
  zigbeeModel: ['S4RX-110'],
@@ -1813,6 +1813,15 @@ module.exports = [
1813
1813
  meta: {turnsOffAtBrightness1: true},
1814
1814
  extend: hueExtend.light_onoff_brightness(),
1815
1815
  },
1816
+ {
1817
+ zigbeeModel: ['LTV002'],
1818
+ model: '929002477901',
1819
+ vendor: 'Philips',
1820
+ description: 'Hue white filament Edison ST72 E27 LED warm-to-cool',
1821
+ ota: ota.zigbeeOTA,
1822
+ meta: {turnsOffAtBrightness1: true},
1823
+ extend: hueExtend.light_onoff_brightness_colortemp({colorTempRange: [222, 454]}),
1824
+ },
1816
1825
  {
1817
1826
  zigbeeModel: ['LWV002'],
1818
1827
  model: '046677551780',
@@ -2038,4 +2047,13 @@ module.exports = [
2038
2047
  extend: hueExtend.light_onoff_brightness_colortemp({colorTempRange: [153, 500]}),
2039
2048
  ota: ota.zigbeeOTA,
2040
2049
  },
2050
+ {
2051
+ zigbeeModel: ['LTV001'],
2052
+ model: '92900244777',
2053
+ vendor: 'Philips',
2054
+ description: 'Hue White Ambiance E27 ST64 filament bulb',
2055
+ extend: hueExtend.light_onoff_brightness_colortemp({colorTempRange: [222, 454]}),
2056
+ meta: {turnsOffAtBrightness1: true},
2057
+ ota: ota.zigbeeOTA,
2058
+ },
2041
2059
  ];
@@ -17,6 +17,7 @@ module.exports = [
17
17
  {modelID: 'TS0601', manufacturerName: '_TZE200_yw7cahqs'},
18
18
  {modelID: 'TS0601', manufacturerName: '_TZE200_azqp6ssj'},
19
19
  {modelID: 'TS0601', manufacturerName: '_TZE200_zuhszj9s'},
20
+ {modelID: 'TS0601', manufacturerName: '_TZE200_9gvruqf5'},
20
21
  ],
21
22
  model: 'SEA801-Zigbee/SEA802-Zigbee',
22
23
  vendor: 'Saswell',
@@ -0,0 +1,24 @@
1
+ const exposes = require('../lib/exposes');
2
+ const fz = {...require('../converters/fromZigbee'), legacy: require('../lib/legacy').fromZigbee};
3
+ const e = exposes.presets;
4
+
5
+ module.exports = [
6
+ {
7
+ zigbeeModel: ['S57003'],
8
+ model: 'S57003',
9
+ vendor: 'The Light Group',
10
+ description: 'SLC SmartOne Zigbee wall remote 4-channels',
11
+ fromZigbee: [fz.command_on, fz.command_off, fz.battery, fz.command_move, fz.command_stop],
12
+ exposes: [e.battery(), e.action([
13
+ 'on_l1', 'off_l1', 'brightness_move_up_l1', 'brightness_move_down_l1', 'brightness_stop_l1',
14
+ 'on_l2', 'off_l2', 'brightness_move_up_l2', 'brightness_move_down_l2', 'brightness_stop_l2',
15
+ 'on_l3', 'off_l3', 'brightness_move_up_l3', 'brightness_move_down_l3', 'brightness_stop_l3',
16
+ 'on_l4', 'off_l4', 'brightness_move_up_l4', 'brightness_move_down_l4', 'brightness_stop_l4',
17
+ ])],
18
+ toZigbee: [],
19
+ meta: {multiEndpoint: true},
20
+ endpoint: (device) => {
21
+ return {l1: 1, l2: 2, l3: 3, l4: 4};
22
+ },
23
+ },
24
+ ];
package/devices/tuya.js CHANGED
@@ -585,6 +585,40 @@ module.exports = [
585
585
  await reporting.bind(device.getEndpoint(2), coordinatorEndpoint, ['genOnOff']);
586
586
  },
587
587
  },
588
+ {
589
+ fingerprint: [{modelID: 'TS0001', manufacturerName: '_TZ3000_tqlv4ug4'}],
590
+ model: 'TS0001_switch_module',
591
+ vendor: 'TuYa',
592
+ description: '1 gang switch module',
593
+ whiteLabel: [{vendor: 'OXT', model: 'SWTZ21'}],
594
+ toZigbee: extend.switch().toZigbee.concat([tz.moes_power_on_behavior, tz.tuya_switch_type]),
595
+ fromZigbee: extend.switch().fromZigbee.concat([fz.moes_power_on_behavior, fz.tuya_switch_type]),
596
+ exposes: extend.switch().exposes.concat([exposes.presets.power_on_behavior(),
597
+ exposes.enum('switch_type', ea.ALL, ['toggle', 'state', 'momentary'])
598
+ .withDescription('Switch type settings')]),
599
+ configure: async (device, coordinatorEndpoint, logger) => {
600
+ await reporting.bind(device.getEndpoint(1), coordinatorEndpoint, ['genOnOff']);
601
+ },
602
+ },
603
+ {
604
+ fingerprint: [{modelID: 'TS0002', manufacturerName: '_TZ3000_01gpyda5'}],
605
+ model: 'TS0002_switch_module',
606
+ vendor: 'TuYa',
607
+ description: '2 gang switch module',
608
+ whiteLabel: [{vendor: 'OXT', model: 'SWTZ22'}],
609
+ toZigbee: extend.switch().toZigbee.concat([tz.moes_power_on_behavior, tz.tuya_switch_type]),
610
+ fromZigbee: extend.switch().fromZigbee.concat([fz.moes_power_on_behavior, fz.tuya_switch_type]),
611
+ exposes: [e.switch().withEndpoint('l1'), e.switch().withEndpoint('l2'), exposes.presets.power_on_behavior(),
612
+ exposes.enum('switch_type', ea.ALL, ['toggle', 'state', 'momentary']).withDescription('Switch type settings')],
613
+ endpoint: (device) => {
614
+ return {'l1': 1, 'l2': 2};
615
+ },
616
+ meta: {multiEndpoint: true},
617
+ configure: async (device, coordinatorEndpoint, logger) => {
618
+ await reporting.bind(device.getEndpoint(1), coordinatorEndpoint, ['genOnOff']);
619
+ await reporting.bind(device.getEndpoint(2), coordinatorEndpoint, ['genOnOff']);
620
+ },
621
+ },
588
622
  {
589
623
  zigbeeModel: [
590
624
  'owvfni3\u0000', 'owvfni3', 'u1rkty3', 'aabybja', // Curtain motors
@@ -827,6 +861,7 @@ module.exports = [
827
861
  const interval = setInterval(async () => {
828
862
  try {
829
863
  await endpoint.read('haElectricalMeasurement', ['rmsVoltage', 'rmsCurrent', 'activePower']);
864
+ await endpoint.read('seMetering', ['currentSummDelivered']);
830
865
  } catch (error) {/* Do nothing*/}
831
866
  }, seconds*1000);
832
867
  globalStore.putValue(device, 'interval', interval);
package/lib/constants.js CHANGED
@@ -147,6 +147,43 @@ const lockUserStatus = {
147
147
  3: 'disabled',
148
148
  };
149
149
 
150
+ const easyCodeTouchActions = {
151
+ // First byte are source / msg.data[3]
152
+ // 0x00 KeyPad: If the user uses the code panel.
153
+ // 0x02 Manual: If the user used a key, button or fingerprint.
154
+ // 0x03 RFID: If the user used an RFID tag.
155
+ // 0xFF Other: If the user used an unknown method
156
+
157
+ // Last byte are eventCode / msg.data[4]
158
+ // 0x00 Lock: The device was locked using either button, code panel or RFID.
159
+ // 0x01 Unlock: The device was unlocked using either button, code panel or RFID.
160
+ // 0x08 Key Lock: If the user locked with a key.
161
+ // 0x09 Key Unlock: If the user unlocked with a key.
162
+ // 0x10 Fingerprint Lock: The device was locked using fingerprint.
163
+ // 0x11 Fingerprint Unlock: The device was unlocked using fingerprint.
164
+
165
+ 0x0000: 'keypad_lock',
166
+ 0x0001: 'keypad_unlock',
167
+
168
+ // Manual says 0x0001 but the lock sends 0x0002 when you unlock it using the keypad
169
+ 0x0002: 'keypad_unlock',
170
+
171
+ 0x0200: 'manual_lock',
172
+ 0x0201: 'manual_unlock',
173
+
174
+ 0x0208: 'key_lock',
175
+ 0x0209: 'key_unlock',
176
+
177
+ 0x0210: 'fingerprint_lock',
178
+ 0x0211: 'fingerprint_unlock',
179
+
180
+ 0x0300: 'rfid_lock',
181
+ 0x0301: 'rfid_unlock',
182
+
183
+ 0xFF0D: 'lock',
184
+ 0xFF0E: 'zigbee_unlock',
185
+ };
186
+
150
187
  module.exports = {
151
188
  OneJanuary2000,
152
189
  repInterval,
@@ -166,4 +203,5 @@ module.exports = {
166
203
  colorMode,
167
204
  lockSoundVolume,
168
205
  lockUserStatus,
206
+ easyCodeTouchActions,
169
207
  };
package/lib/exposes.js CHANGED
@@ -469,7 +469,7 @@ module.exports = {
469
469
  child_lock: () => new Lock().withState('child_lock', 'LOCK', 'UNLOCK', 'Enables/disables physical input on the device', access.STATE_SET),
470
470
  co2: () => new Numeric('co2', access.STATE).withUnit('ppm').withDescription('The measured CO2 (carbon dioxide) value'),
471
471
  comfort_temperature: () => new Numeric('comfort_temperature', access.STATE_SET).withUnit('°C').withDescription('Comfort temperature'),
472
- consumer_connected: () => new Binary('consumer_connected', access.STATE, true, false).withDescription('Indicates whether device is physically attached. Device does not have to pull power or even be connected electrically (switch can be ON even if switch is OFF).'),
472
+ consumer_connected: () => new Binary('consumer_connected', access.STATE, true, false).withDescription('Indicates whether a plug is physically attached. Device does not have to pull power or even be connected electrically (state of this binary switch can be ON even if main power switch is OFF)'),
473
473
  contact: () => new Binary('contact', access.STATE, false, true).withDescription('Indicates if the contact is closed (= true) or open (= false)'),
474
474
  cover_position: () => new Cover().withPosition(),
475
475
  cover_position_tilt: () => new Cover().withPosition().withTilt(),
package/lib/utils.js CHANGED
@@ -271,14 +271,37 @@ function toCamelCase(value) {
271
271
  }
272
272
  }
273
273
 
274
- function saveSceneState(entity, sceneID, groupID, state) {
274
+ function saveSceneState(entity, sceneID, groupID, state, name) {
275
275
  const attributes = ['state', 'brightness', 'color', 'color_temp', 'color_mode'];
276
276
  if (!entity.meta.hasOwnProperty('scenes')) entity.meta.scenes = {};
277
277
  const metaKey = `${sceneID}_${groupID}`;
278
- entity.meta.scenes[metaKey] = {state: filterObject(state, attributes)};
278
+ entity.meta.scenes[metaKey] = {name, state: filterObject(state, attributes)};
279
279
  entity.save();
280
280
  }
281
281
 
282
+ function deleteSceneState(entity, sceneID=null, groupID=null) {
283
+ if (entity.meta.scenes) {
284
+ if (sceneID == null && groupID == null) {
285
+ entity.meta.scenes = {};
286
+ } else {
287
+ const metaKey = `${sceneID}_${groupID}`;
288
+ if (entity.meta.scenes.hasOwnProperty(metaKey)) {
289
+ delete entity.meta.scenes[metaKey];
290
+ }
291
+ }
292
+ entity.save();
293
+ }
294
+ }
295
+
296
+ function getSceneState(entity, sceneID, groupID) {
297
+ const metaKey = `${sceneID}_${groupID}`;
298
+ if (entity.meta.hasOwnProperty('scenes') && entity.meta.scenes.hasOwnProperty(metaKey)) {
299
+ return entity.meta.scenes[metaKey].state;
300
+ }
301
+
302
+ return null;
303
+ }
304
+
282
305
  function getEntityOrFirstGroupMember(entity) {
283
306
  if (entity.constructor.name === 'Group') {
284
307
  return entity.members.length > 0 ? entity.members[0] : null;
@@ -381,4 +404,6 @@ module.exports = {
381
404
  toSnakeCase,
382
405
  toCamelCase,
383
406
  normalizeCelsiusVersionOfFahrenheit,
407
+ deleteSceneState,
408
+ getSceneState,
384
409
  };
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zigbee-herdsman-converters",
3
- "version": "14.0.264",
3
+ "version": "14.0.268",
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.264",
3
+ "version": "14.0.268",
4
4
  "description": "Collection of device converters to be used with zigbee-herdsman",
5
5
  "main": "index.js",
6
6
  "files": [