zigbee-herdsman-converters 15.0.26 → 15.0.28

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.
@@ -3271,7 +3271,12 @@ const converters = {
3271
3271
  moes_thermostat_current_heating_setpoint: {
3272
3272
  key: ['current_heating_setpoint'],
3273
3273
  convertSet: async (entity, key, value, meta) => {
3274
- await tuya.sendDataPointValue(entity, tuya.dataPoints.moesHeatingSetpoint, value);
3274
+ if (meta.device.manufacturerName === '_TZE200_5toc8efa') {
3275
+ const temp = Math.round(value*10);
3276
+ await tuya.sendDataPointValue(entity, tuya.dataPoints.moesHeatingSetpoint, temp);
3277
+ } else {
3278
+ await tuya.sendDataPointValue(entity, tuya.dataPoints.moesHeatingSetpoint, value);
3279
+ }
3275
3280
  },
3276
3281
  },
3277
3282
  moes_thermostat_deadzone_temperature: {
@@ -0,0 +1,11 @@
1
+ const extend = require('../lib/extend');
2
+
3
+ module.exports = [
4
+ {
5
+ zigbeeModel: ['Bankamp Dimm-Leuchte'],
6
+ model: '2189/1-xx',
7
+ vendor: 'Bankamp',
8
+ description: 'Grazia ceiling light',
9
+ extend: extend.light_onoff_brightness(),
10
+ },
11
+ ];
package/devices/datek.js CHANGED
@@ -28,6 +28,7 @@ module.exports = [
28
28
  await reporting.activePower(endpoint);
29
29
  await reporting.temperature(endpoint);
30
30
  },
31
+ ota: ota.zigbeeOTA,
31
32
  exposes: [e.power(), e.current(), e.voltage(), e.switch(), e.temperature(), e.power_on_behavior()],
32
33
  },
33
34
  {
package/devices/ikea.js CHANGED
@@ -556,7 +556,9 @@ module.exports = [
556
556
  model: 'ICPSHC24-30-IL44-1',
557
557
  vendor: 'IKEA',
558
558
  description: 'SILVERGLANS IP44 LED driver for wireless control (30 watt)',
559
+ whiteLabel: [{vendor: 'IKEA', model: 'T2030', description: 'PILSKOTT LED pendant lamp'}],
559
560
  extend: tradfriExtend.light_onoff_brightness(),
561
+ meta: {turnsOffAtBrightness1: true},
560
562
  },
561
563
  {
562
564
  zigbeeModel: ['FLOALT panel WS 30x30'],
@@ -446,6 +446,52 @@ module.exports = [
446
446
  await reporting.activePower(endpoint);
447
447
  },
448
448
  },
449
+ {
450
+ zigbeeModel: ['Hospitality on off switch'],
451
+ model: 'WNAL10/WNRL10',
452
+ vendor: 'Legrand',
453
+ description: 'Smart switch with Netatmo',
454
+ fromZigbee: [fz.on_off, fz.legrand_binary_input_on_off, fz.legrand_cluster_fc01],
455
+ toZigbee: [tz.on_off, tz.legrand_settingEnableLedInDark, tz.legrand_settingEnableLedIfOn],
456
+ exposes: [e.switch(),
457
+ exposes.binary('led_in_dark', ea.ALL, 'ON', 'OFF').withDescription(`Enables the LED when the light is turned off, allowing to` +
458
+ ` see the switch in the dark`),
459
+ exposes.binary('led_if_on', ea.ALL, 'ON', 'OFF').withDescription('Enables the LED when the light is turned on')],
460
+ configure: async (device, coordinatorEndpoint, logger) => {
461
+ const endpoint = device.getEndpoint(1);
462
+ await reporting.bind(endpoint, coordinatorEndpoint, ['genOnOff']);
463
+ await reporting.onOff(endpoint);
464
+ },
465
+ },
466
+ {
467
+ zigbeeModel: ['Hospitality dimmer switch'],
468
+ model: 'WNAL50/WNRL50',
469
+ vendor: 'Legrand',
470
+ description: 'Smart dimmer switch with Netatmo',
471
+ extend: extend.light_onoff_brightness({noConfigure: true}),
472
+ fromZigbee: [fz.brightness, fz.identify, fz.on_off, fz.lighting_ballast_configuration, fz.legrand_cluster_fc01,
473
+ fz.power_on_behavior],
474
+ toZigbee: [tz.light_onoff_brightness, tz.legrand_settingEnableLedInDark, tz.legrand_settingEnableLedIfOn,
475
+ tz.legrand_deviceMode, tz.legrand_identify, tz.ballast_config, tz.power_on_behavior],
476
+ exposes: [e.light_brightness(),
477
+ exposes.numeric('ballast_minimum_level', ea.ALL).withValueMin(1).withValueMax(254)
478
+ .withDescription('Specifies the minimum brightness value'),
479
+ exposes.numeric('ballast_maximum_level', ea.ALL).withValueMin(1).withValueMax(254)
480
+ .withDescription('Specifies the maximum brightness value'),
481
+ exposes.binary('device_mode', ea.ALL, 'dimmer_on', 'dimmer_off').withDescription('Allow the device to change brightness'),
482
+ exposes.binary('led_in_dark', ea.ALL, 'ON', 'OFF').withDescription(`Enables the LED when the light is turned off, allowing to` +
483
+ ` see the switch in the dark`),
484
+ exposes.binary('led_if_on', ea.ALL, 'ON', 'OFF').withDescription('Enables the LED when the light is turned on'),
485
+ e.power_on_behavior()],
486
+ configure: async (device, coordinatorEndpoint, logger) => {
487
+ await extend.light_onoff_brightness().configure(device, coordinatorEndpoint, logger);
488
+ const endpoint = device.getEndpoint(1);
489
+ await reporting.bind(endpoint, coordinatorEndpoint, ['genIdentify', 'genOnOff', 'genLevelCtrl',
490
+ 'genBinaryInput', 'lightingBallastCfg']);
491
+ await reporting.onOff(endpoint);
492
+ await reporting.brightness(endpoint);
493
+ },
494
+ },
449
495
  {
450
496
  zigbeeModel: ['Remote dimmer switch'],
451
497
  model: 'WNAL63',
package/devices/moes.js CHANGED
@@ -291,9 +291,9 @@ module.exports = [
291
291
  e.valve_state(), e.position(), e.window_detection(),
292
292
  exposes.binary('window', ea.STATE, 'OPEN', 'CLOSED').withDescription('Window status closed or open '),
293
293
  exposes.climate()
294
- .withLocalTemperature(ea.STATE).withSetpoint('current_heating_setpoint', 5, 45, 0.5, ea.STATE_SET)
294
+ .withLocalTemperature(ea.STATE).withSetpoint('current_heating_setpoint', 5, 35, 1, ea.STATE_SET)
295
295
  .withLocalTemperatureCalibration(-9, 9, 1, ea.STATE_SET)
296
- .withSystemMode(['off', 'heat'], ea.STATE_SET)
296
+ .withSystemMode(['heat'], ea.STATE_SET)
297
297
  .withRunningState(['idle', 'heat'], ea.STATE)
298
298
  .withPreset(['programming', 'manual', 'temporary_manual', 'holiday'],
299
299
  'MANUAL MODE ☝ - In this mode, the device executes manual temperature setting. '+
@@ -73,6 +73,13 @@ module.exports = [
73
73
  description: 'Hue Centura recessed spotlight white and color ambiance GU10 (black)',
74
74
  extend: philips.extend.light_onoff_brightness_colortemp_color({colorTempRange: [153, 500]}),
75
75
  },
76
+ {
77
+ zigbeeModel: ['929003047601'],
78
+ model: '929003047601',
79
+ vendor: 'Philips',
80
+ description: 'Hue White and Color Ambiance GU10 (Centura)',
81
+ extend: philips.extend.light_onoff_brightness_colortemp_color({colorTempRange: [153, 500]}),
82
+ },
76
83
  {
77
84
  zigbeeModel: ['929003047501'],
78
85
  model: '929003047501',
@@ -2264,7 +2271,7 @@ module.exports = [
2264
2271
  model: '046677551780',
2265
2272
  vendor: 'Philips',
2266
2273
  description: 'Hue white filament Edison ST19 LED',
2267
- extend: philips.extend.light_onoff_brightness(),
2274
+ extend: philips.extend.light_onoff_brightness({disableHueEffects: false}),
2268
2275
  },
2269
2276
  {
2270
2277
  zigbeeModel: ['LWV003'],
@@ -3020,4 +3027,18 @@ module.exports = [
3020
3027
  description: 'Hue Perifo Wall Light, 3 spotlights (white)',
3021
3028
  extend: philips.extend.light_onoff_brightness_colortemp_color({colorTempRange: [153, 500]}),
3022
3029
  },
3030
+ {
3031
+ zigbeeModel: ['915005988101'],
3032
+ model: '915005988101',
3033
+ vendor: 'Philips',
3034
+ description: 'Hue Gradient light tube large',
3035
+ extend: philips.extend.light_onoff_brightness_colortemp_color_gradient({disableHueEffects: false, colorTempRange: [153, 500]}),
3036
+ },
3037
+ {
3038
+ zigbeeModel: ['915005987901'],
3039
+ model: '915005987901',
3040
+ vendor: 'Philips',
3041
+ description: 'Hue Gradient light tube compact',
3042
+ extend: philips.extend.light_onoff_brightness_colortemp_color_gradient({disableHueEffects: false, colorTempRange: [153, 500]}),
3043
+ },
3023
3044
  ];
@@ -735,7 +735,7 @@ module.exports = [
735
735
  fromZigbee: [fz.ignore_basic_report, fz.ignore_genOta, fz.ignore_zclversion_read, fz.battery, fz.hvac_user_interface,
736
736
  fz.wiser_smart_thermostat_client, fz.wiser_smart_setpoint_command_client, fz.schneider_temperature],
737
737
  toZigbee: [tz.wiser_sed_zone_mode, tz.wiser_sed_occupied_heating_setpoint],
738
- exposes: [e.battery(), e.temperature(),
738
+ exposes: [e.battery(),
739
739
  exposes.climate().withSetpoint('occupied_heating_setpoint', 7, 30, 0.5, ea.STATE_SET)
740
740
  .withLocalTemperature(ea.STATE),
741
741
  exposes.enum('zone_mode',
@@ -0,0 +1,11 @@
1
+ const extend = require('../lib/extend');
2
+
3
+ module.exports = [
4
+ {
5
+ zigbeeModel: ['SE-SW'],
6
+ model: 'SEHAZB-DR-SWITCH-2',
7
+ vendor: 'SolarEdge',
8
+ description: 'Smart energy switch',
9
+ extend: extend.switch(),
10
+ },
11
+ ];
package/devices/tuya.js CHANGED
@@ -1104,6 +1104,7 @@ module.exports = [
1104
1104
  {modelID: 'TS0202', manufacturerName: '_TYZB01_zwvaj5wy'},
1105
1105
  {modelID: 'TS0202', manufacturerName: '_TZ3000_bsvqrxru'},
1106
1106
  {modelID: 'TS0202', manufacturerName: '_TYZB01_tv3wxhcz'},
1107
+ {modelID: 'TS0202', manufacturerName: '_TYZB01_rwb0hxtf'},
1107
1108
  {modelID: 'TS0202', manufacturerName: '_TYZB01_hqbdru35'},
1108
1109
  {modelID: 'TS0202', manufacturerName: '_TZ3000_otvn3lne'},
1109
1110
  {modelID: 'TS0202', manufacturerName: '_TZ3000_tiwq83wk'},
@@ -2114,6 +2115,7 @@ module.exports = [
2114
2115
  {modelID: 'TS0601', manufacturerName: '_TZE200_zuz7f94z'},
2115
2116
  {modelID: 'TS0601', manufacturerName: '_TZE200_68nvbio9'},
2116
2117
  {modelID: 'TS0601', manufacturerName: '_TZE200_3ylew7b4'},
2118
+ {modelID: 'TS0601', manufacturerName: '_TZE200_llm0epxg'},
2117
2119
  ],
2118
2120
  model: 'TS0601_cover',
2119
2121
  vendor: 'TuYa',
package/devices/xiaomi.js CHANGED
@@ -11,7 +11,7 @@ const globalStore = require('../lib/store');
11
11
  const xiaomi = require('../lib/xiaomi');
12
12
  const utils = require('../lib/utils');
13
13
  const {printNumberAsHex, printNumbersAsHexSequence} = utils;
14
- const {fp1, manufacturerCode} = xiaomi;
14
+ const {fp1, manufacturerCode, trv} = xiaomi;
15
15
 
16
16
  const xiaomiExtend = {
17
17
  light_onoff_brightness_colortemp: (options={disableColorTempStartup: true}) => ({
@@ -87,7 +87,7 @@ const fzLocal = {
87
87
  result['system_mode'] = {1: 'heat', 0: 'off'}[value];
88
88
  break;
89
89
  case 0x0272:
90
- result['preset'] = {2: 'away', 1: 'auto', 0: 'manual'}[value];
90
+ Object.assign(result, trv.decodePreset(value));
91
91
  break;
92
92
  case 0x0273:
93
93
  result['window_detection'] = {1: 'ON', 0: 'OFF'}[value];
@@ -116,9 +116,26 @@ const fzLocal = {
116
116
  case 0x0275:
117
117
  result['valve_alarm'] = {1: true, 0: false}[value];
118
118
  break;
119
+ case 247: {
120
+ const heartbeat = trv.decodeHeartbeat(meta, model, value);
121
+
122
+ meta.logger.debug(`${model.zigbeeModel}: Processed heartbeat message into payload ${JSON.stringify(heartbeat)}`);
123
+
124
+ if (heartbeat.firmware_version) {
125
+ // Overwrite the "placeholder" version `0.0.0_0025` advertised by `genBasic`
126
+ // with the correct version from the heartbeat.
127
+ // This is not reflected in the frontend unless the device is reconfigured
128
+ // or the whole service restarted.
129
+ // See https://github.com/Koenkk/zigbee-herdsman-converters/pull/5363#discussion_r1081477047
130
+ meta.device.softwareBuildID = heartbeat.firmware_version;
131
+ delete heartbeat.firmware_version;
132
+ }
133
+
134
+ Object.assign(result, heartbeat);
135
+ break;
136
+ }
119
137
  case 0xfff2:
120
138
  case 0x00ff: // 4e:27:49:bb:24:b6:30:dd:74:de:53:76:89:44:c4:81
121
- case 0x00f7: // 03:28:1f:05:21:01:00:0a:21:00:00:0d:23:19:08:00:00:11:23...
122
139
  case 0x0276: // 04:3e:01:e0:00:00:09:60:04:38:00:00:06:a4:05:64:00:00:08:98:81:e0:00:00:08:98
123
140
  case 0x027c: // 0x00
124
141
  case 0x027d: // 0x00
@@ -2889,9 +2906,11 @@ module.exports = [
2889
2906
  fromZigbee: [fzLocal.aqara_trv, fz.thermostat, fz.battery],
2890
2907
  toZigbee: [tzLocal.aqara_trv, tz.thermostat_occupied_heating_setpoint],
2891
2908
  exposes: [
2909
+ exposes.binary('setup', ea.STATE, true, false)
2910
+ .withDescription('Indicates if the device is in setup mode (E11)'),
2892
2911
  exposes.climate()
2893
2912
  .withSetpoint('occupied_heating_setpoint', 5, 30, 0.5)
2894
- .withLocalTemperature(ea.STATE)
2913
+ .withLocalTemperature(ea.STATE, 'Current temperature measured by the internal or external sensor')
2895
2914
  .withSystemMode(['off', 'heat'], ea.ALL)
2896
2915
  .withPreset(['manual', 'away', 'auto']).setAccess('preset', ea.ALL),
2897
2916
  e.temperature_sensor_select(['internal', 'external']).withAccess(ea.ALL),
@@ -2900,15 +2919,24 @@ module.exports = [
2900
2919
  e.child_lock().setAccess('state', ea.ALL),
2901
2920
  e.window_detection().setAccess('state', ea.ALL),
2902
2921
  exposes.binary('window_open', ea.STATE, true, false),
2903
- e.valve_detection().setAccess('state', ea.ALL),
2922
+ e.valve_detection().setAccess('state', ea.ALL)
2923
+ .withDescription('Determines if temperature control abnormalities should be detected'),
2924
+ exposes.binary('valve_alarm', ea.STATE, true, false)
2925
+ .withDescription('Notifies of a temperature control abnormality if valve detection is enabled ' +
2926
+ '(e.g., thermostat not installed correctly, valve failure or incorrect calibration, ' +
2927
+ 'incorrect link to external temperature sensor)'),
2904
2928
  e.away_preset_temperature().withAccess(ea.ALL),
2905
2929
  e.battery_voltage(),
2906
2930
  e.battery(),
2931
+ e.power_outage_count(),
2932
+ e.device_temperature(),
2907
2933
  ],
2908
- meta: {battery: {voltageToPercentage: '3V_2850_3000'}},
2909
2934
  configure: async (device, coordinatorEndpoint, logger) => {
2910
2935
  const endpoint = device.getEndpoint(1);
2936
+
2937
+ // Initialize battery percentage and voltage
2911
2938
  await endpoint.read('aqaraOpple', [0x040a], {manufacturerCode: 0x115f});
2939
+ await endpoint.read('genPowerCfg', ['batteryVoltage']);
2912
2940
 
2913
2941
  // This cluster is not discovered automatically and needs to be explicitly attached to enable OTA
2914
2942
  utils.attachOutputCluster(device, 'genOta');
package/lib/exposes.js CHANGED
@@ -385,7 +385,7 @@ class Climate extends Base {
385
385
  return this;
386
386
  }
387
387
 
388
- withLocalTemperature(access=a.STATE_GET) {
388
+ withLocalTemperature(access=a.STATE_GET, description='Current temperature measured on the device') {
389
389
  assert(!this.endpoint, 'Cannot add feature after adding endpoint');
390
390
  this.features.push(new Numeric('local_temperature', access).withUnit('°C').withDescription('Current temperature measured on the device'));
391
391
  return this;
package/lib/xiaomi.js CHANGED
@@ -861,6 +861,81 @@ const fp1 = {
861
861
  },
862
862
  };
863
863
 
864
+ const trv = {
865
+ decodeFirmwareVersionString(value) {
866
+ // Add prefix to follow Aqara's versioning schema: https://www.aqara.com/en/version/radiator-thermostat-e1
867
+ const firmwareVersionPrefix = '0.0.0_';
868
+
869
+ // Reinterpret from LE integer to byte sequence(e.g., `[25,8,0,0]` corresponds to 0.0.0_0825)
870
+ const buffer = Buffer.alloc(4);
871
+ buffer.writeUInt32LE(value);
872
+ const firmwareVersionNumber = buffer.reverse().subarray(1).join('');
873
+
874
+ return firmwareVersionPrefix + firmwareVersionNumber;
875
+ },
876
+
877
+ decodePreset(value) {
878
+ // Setup mode is the initial device state after powering it ("F11" on display) and not a real preset that can be deliberately
879
+ // set by users, therefore it is exposed as a separate flag.
880
+ return {
881
+ setup: value === 3,
882
+ preset: {2: 'away', 1: 'auto', 0: 'manual'}[value],
883
+ };
884
+ },
885
+
886
+ decodeHeartbeat(meta, model, messageBuffer) {
887
+ const data = buffer2DataObject(meta, model, messageBuffer);
888
+ const payload = {};
889
+
890
+ Object.entries(data).forEach(([key, value]) => {
891
+ switch (parseInt(key)) {
892
+ case 3:
893
+ payload.device_temperature = value;
894
+ break;
895
+ case 5:
896
+ payload.power_outage_count = value - 1;
897
+ break;
898
+ case 10:
899
+ // unidentified number, e.g. 32274, 3847
900
+ break;
901
+ case 13:
902
+ payload.firmware_version = trv.decodeFirmwareVersionString(value);
903
+ break;
904
+ case 17:
905
+ // unidentified flag/enum, e.g. 1
906
+ break;
907
+ case 101:
908
+ Object.assign(payload, trv.decodePreset(value));
909
+ break;
910
+ case 102:
911
+ payload.local_temperature = value / 100;
912
+ break;
913
+ case 103:
914
+ // This takes the following values:
915
+ // - `occupied_heating_setpoint` if `system_mode` is `heat` and `preset` is `manual`
916
+ // - `away_preset_temperature` if `system_mode` is `heat` and `preset` is `away`
917
+ // - `5` if `system_mode` is `off`
918
+ // It thus behaves similar to `occupied_heating_setpoint` except in `off` mode. Due to this difference,
919
+ // this value is written to another property to avoid an inconsistency of the `occupied_heating_setpoint`.
920
+ // TODO How to handle this value? Find better name?
921
+ payload.internal_heating_setpoint = value / 100;
922
+ break;
923
+ case 104:
924
+ payload.valve_alarm = value === 1;
925
+ break;
926
+ case 105:
927
+ payload.battery = value;
928
+ break;
929
+ case 106:
930
+ // unidentified flag/enum, e.g. 0
931
+ break;
932
+ }
933
+ });
934
+
935
+ return payload;
936
+ },
937
+ };
938
+
864
939
  module.exports = {
865
940
  buffer2DataObject,
866
941
  numericAttributes2Payload,
@@ -868,4 +943,5 @@ module.exports = {
868
943
  VOCKQJK11LMDisplayUnit,
869
944
  fp1,
870
945
  manufacturerCode: 0x115f,
946
+ trv,
871
947
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zigbee-herdsman-converters",
3
- "version": "15.0.26",
3
+ "version": "15.0.28",
4
4
  "description": "Collection of device converters to be used with zigbee-herdsman",
5
5
  "main": "index.js",
6
6
  "files": [