zigbee-herdsman-converters 15.0.43 → 15.0.45

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.
@@ -3068,6 +3068,21 @@ const converters = {
3068
3068
  }
3069
3069
  },
3070
3070
  },
3071
+ livolo_pir_state: {
3072
+ cluster: 'genPowerCfg',
3073
+ type: ['raw'],
3074
+ convert: (model, msg, publish, options, meta) => {
3075
+ const stateHeader = Buffer.from([122, 209]);
3076
+ if (msg.data.indexOf(stateHeader) === 0) {
3077
+ if (msg.data[10] === 7) {
3078
+ const status = msg.data[14];
3079
+ return {
3080
+ occupancy: status & 1 ? true : false,
3081
+ };
3082
+ }
3083
+ }
3084
+ },
3085
+ },
3071
3086
  easycode_action: {
3072
3087
  cluster: 'closuresDoorLock',
3073
3088
  type: 'raw',
@@ -3126,6 +3141,11 @@ const converters = {
3126
3141
  [122,209, 110,74,116,33,0,75,18,0, 5,1,5,0,2,11] left: 0, right: 0 (off)
3127
3142
  [122,209, 110,74,116,33,0,75,18,0, 5,1,5,0,1,11] left: 1, right: 0 (left on)
3128
3143
  [122,209, 110,74,116,33,0,75,18,0, 5,1,5,0,0,11] left: 0, right: 1 (right on)
3144
+
3145
+ pir sensor
3146
+ [124,210,21,216,128, 225,52,225,34,0,75,18,0, 19,13,0] after interview
3147
+ [122,209, 245,94,225,34,0,75,18,0, 7,1,7,1,1,11] occupancy: true
3148
+ [122,209, 245,94,225,34,0,75,18,0, 7,1,7,1,0,11] occupancy: false
3129
3149
  */
3130
3150
  const malformedHeader = Buffer.from([0x7c, 0xd2, 0x15, 0xd8, 0x00]);
3131
3151
  const infoHeader = Buffer.from([0x7c, 0xd2, 0x15, 0xd8, 0x80]);
@@ -3170,6 +3190,11 @@ const converters = {
3170
3190
  meta.device.modelID = 'TI0001-cover';
3171
3191
  meta.device.save();
3172
3192
  }
3193
+ if (msg.data.includes(Buffer.from([19, 13, 0]), 13)) {
3194
+ if (meta.logger) meta.logger.debug('Detected Livolo Pir Sensor');
3195
+ meta.device.modelID = 'TI0001-pir';
3196
+ meta.device.save();
3197
+ }
3173
3198
  }
3174
3199
  },
3175
3200
  },
@@ -4125,123 +4150,6 @@ const converters = {
4125
4150
  }
4126
4151
  },
4127
4152
  },
4128
- haozee_thermostat: {
4129
- cluster: 'manuSpecificTuya',
4130
- type: ['commandDataResponse', 'commandDataReport'],
4131
- convert: (model, msg, publish, options, meta) => {
4132
- const dpValue = tuya.firstDpValue(msg, meta, 'haozee_thermostat');
4133
- const dp = dpValue.dp; // First we get the data point ID
4134
- const value = tuya.getDataValue(dpValue);
4135
- const presetLookup = {0: 'auto', 1: 'manual', 2: 'off', 3: 'on'};
4136
- const systemModeLookup = {0: 'auto', 1: 'auto', 2: 'off', 3: 'heat'};
4137
- switch (dp) {
4138
- case tuya.dataPoints.haozeeSystemMode:
4139
- return {preset: presetLookup[value], system_mode: systemModeLookup[value]};
4140
- case tuya.dataPoints.haozeeHeatingSetpoint:
4141
- return {current_heating_setpoint: (value / 10).toFixed(1)};
4142
- case tuya.dataPoints.haozeeLocalTemp:
4143
- return {local_temperature: (value / 10).toFixed(1)};
4144
- case tuya.dataPoints.haozeeBoostHeatingCountdown:
4145
- // quick heating countdown - not supported by this device
4146
- return {boost_heating_countdown: value};
4147
- case tuya.dataPoints.haozeeWindowDetection:
4148
- // window check
4149
- return {window_detection: value ? 'ON' : 'OFF'};
4150
- case tuya.dataPoints.haozeeWindowState:
4151
- // window state
4152
- return {window: value ? 'OPEN' : 'CLOSED'};
4153
- case tuya.dataPoints.haozeeChildLock:
4154
- return {child_lock: value ? 'LOCK' : 'UNLOCK'};
4155
- case tuya.dataPoints.haozeeBattery:
4156
- // battery
4157
- return {battery: value};
4158
- case tuya.dataPoints.haozeeFaultAlarm:
4159
- return {error: value ? 'ON': 'OFF'};
4160
- case tuya.dataPoints.haozeeScheduleMonday:
4161
- // Monday
4162
- return {
4163
- 'monday_schedule': ' ' + value[1] + 'h:' + value[2] + 'm ' + value[4] / 10 + '°C' +
4164
- ', ' + value[5] + 'h:' + value[6] + 'm ' + value[8] / 10 + '°C' +
4165
- ', ' + value[9] + 'h:' + value[10] + 'm ' + value[12] / 10 + '°C' +
4166
- ', ' + value[13] + 'h:' + value[14] + 'm ' + value[16] / 10 + '°C ',
4167
- };
4168
- case tuya.dataPoints.haozeeScheduleTuesday:
4169
- // Tuesday
4170
- return {
4171
- 'tuesday_schedule': ' ' + value[1] + 'h:' + value[2] + 'm ' + value[4] / 10 + '°C' +
4172
- ', ' + value[5] + 'h:' + value[6] + 'm ' + value[8] / 10 + '°C' +
4173
- ', ' + value[9] + 'h:' + value[10] + 'm ' + value[12] / 10 + '°C' +
4174
- ', ' + value[13] + 'h:' + value[14] + 'm ' + value[16] / 10 + '°C ',
4175
- };
4176
- case tuya.dataPoints.haozeeScheduleWednesday:
4177
- // wednesday
4178
- return {
4179
- 'wednesday_schedule': ' ' + value[1] + 'h:' + value[2] + 'm ' + value[4] / 10 + '°C' +
4180
- ', ' + value[5] + 'h:' + value[6] + 'm ' + value[8] / 10 + '°C' +
4181
- ', ' + value[9] + 'h:' + value[10] + 'm ' + value[12] / 10 + '°C' +
4182
- ', ' + value[13] + 'h:' + value[14] + 'm ' + value[16] / 10 + '°C ',
4183
- };
4184
- case tuya.dataPoints.haozeeScheduleThursday:
4185
- // Thursday
4186
- return {
4187
- 'thursday_schedule': ' ' + value[1] + 'h:' + value[2] + 'm ' + value[4] / 10 + '°C' +
4188
- ', ' + value[5] + 'h:' + value[6] + 'm ' + value[8] / 10 + '°C' +
4189
- ', ' + value[9] + 'h:' + value[10] + 'm ' + value[12] / 10 + '°C' +
4190
- ', ' + value[13] + 'h:' + value[14] + 'm ' + value[16] / 10 + '°C ',
4191
- };
4192
- case tuya.dataPoints.haozeeScheduleFriday:
4193
- // Friday
4194
- return {
4195
- 'friday_schedule': ' ' + value[1] + 'h:' + value[2] + 'm ' + value[4] / 10 + '°C' +
4196
- ', ' + value[5] + 'h:' + value[6] + 'm ' + value[8] / 10 + '°C' +
4197
- ', ' + value[9] + 'h:' + value[10] + 'm ' + value[12] / 10 + '°C' +
4198
- ', ' + value[13] + 'h:' + value[14] + 'm ' + value[16] / 10 + '°C ',
4199
- };
4200
- case tuya.dataPoints.haozeeScheduleSaturday:
4201
- // Saturday
4202
- return {
4203
- 'saturday_schedule': ' ' + value[1] + 'h:' + value[2] + 'm ' + value[4] / 10 + '°C' +
4204
- ', ' + value[5] + 'h:' + value[6] + 'm ' + value[8] / 10 + '°C' +
4205
- ', ' + value[9] + 'h:' + value[10] + 'm ' + value[12] / 10 + '°C' +
4206
- ', ' + value[13] + 'h:' + value[14] + 'm ' + value[16] / 10 + '°C ',
4207
- };
4208
- case tuya.dataPoints.haozeeScheduleSunday:
4209
-
4210
- // Sunday
4211
- return {
4212
- 'sunday_schedule': ' ' + value[1] + 'h:' + value[2] + 'm ' + value[4] / 10 + '°C' +
4213
- ', ' + value[5] + 'h:' + value[6] + 'm ' + value[8] / 10 + '°C' +
4214
- ', ' + value[9] + 'h:' + value[10] + 'm ' + value[12] / 10 + '°C' +
4215
- ', ' + value[13] + 'h:' + value[14] + 'm ' + value[16] / 10 + '°C ',
4216
- };
4217
-
4218
- case tuya.dataPoints.haozeeRunningState:
4219
- // working status 0 - pause 1 -working
4220
- return {'heating': value ? 'ON' : 'OFF'};
4221
- case tuya.dataPoints.haozeeBoostHeating:
4222
- // rapid heating -> boolean - not supported by this device
4223
- return {'boost_heating': value ? 'ON' : 'OFF'};
4224
- case tuya.dataPoints.haozeeTempCalibration:
4225
- // temperature calibration
4226
- return {'local_temperature_calibration': ( value/10 ).toFixed(1)};
4227
- case tuya.dataPoints.haozeeValvePosition:
4228
- // valve position
4229
- return {'position': ( value/10 ).toFixed(1)};
4230
- case tuya.dataPoints.haozeeMinTemp:
4231
- // lower limit temperature
4232
- return {'min_temperature': ( value/10 ).toFixed(1)};
4233
- case tuya.dataPoints.haozeeMaxTemp:
4234
- // max limit temperature
4235
- return {'max_temperature': ( value/10 ).toFixed(1)};
4236
- case tuya.dataPoints.haozeeSoftVersion:
4237
- // software
4238
- break;
4239
- default:
4240
- meta.logger.warn(`zigbee-herdsman-converters:haozee: NOT RECOGNIZED DP #${
4241
- dp} with data ${JSON.stringify(dpValue)}`);
4242
- }
4243
- },
4244
- },
4245
4153
  tuya_air_quality: {
4246
4154
  cluster: 'manuSpecificTuya',
4247
4155
  type: ['commandDataReport', 'commandDataResponse'],
@@ -102,6 +102,12 @@ const converters = {
102
102
  await entity.read('genPowerCfg', ['batteryPercentageRemaining']);
103
103
  },
104
104
  },
105
+ battery_voltage: {
106
+ key: ['battery', 'voltage'],
107
+ convertGet: async (entity, key, meta) => {
108
+ await entity.read('genPowerCfg', ['batteryVoltage']);
109
+ },
110
+ },
105
111
  power_on_behavior: {
106
112
  key: ['power_on_behavior'],
107
113
  convertSet: async (entity, key, value, meta) => {
@@ -1260,7 +1266,12 @@ const converters = {
1260
1266
  if (val === undefined) {
1261
1267
  val = utils.getKey(constants.thermostatControlSequenceOfOperations, value, value, Number);
1262
1268
  }
1263
- await entity.write('hvacThermostat', {ctrlSeqeOfOper: val});
1269
+ const attributes = {ctrlSeqeOfOper: val};
1270
+ await entity.write('hvacThermostat', attributes);
1271
+ // NOTE: update the cluster attribute we store as this is used by
1272
+ // SMaBiT AV2010/32's dynamic expose function.
1273
+ entity.saveClusterAttributeKeyValue('hvacThermostat', attributes);
1274
+ return {readAfterWriteTime: 250, state: {control_sequence_of_operation: value}};
1264
1275
  },
1265
1276
  convertGet: async (entity, key, meta) => {
1266
1277
  await entity.read('hvacThermostat', ['ctrlSeqeOfOper']);
@@ -3474,79 +3485,6 @@ const converters = {
3474
3485
  return tuya.sendDataPointRaw(entity, tuya.dataPoints.moesSschedule, payload);
3475
3486
  },
3476
3487
  },
3477
- haozee_thermostat_preset: {
3478
- key: ['preset'],
3479
- convertSet: async (entity, key, value, meta) => {
3480
- const lookup = {'auto': 0, 'manual': 1, 'off': 2, 'on': 3};
3481
- await tuya.sendDataPointEnum(entity, tuya.dataPoints.haozeeSystemMode, lookup[value]);
3482
- },
3483
- },
3484
- haozee_thermostat_system_mode: {
3485
- key: ['system_mode'],
3486
- convertSet: async (entity, key, value, meta) => {
3487
- // mapping 'heat' system mode to 100% heating (same as preset 'ON'),
3488
- // mapping 'auto' system mode to heating up to the set point temperature (same as preset 'MANUAL')
3489
- // mapping 'off' system mode to idle (same as preset 'OFF')
3490
- // programmed schedule can be enabled by using preset mode 'AUTO' instead of 'MANUAL'
3491
- const lookup = {'auto': 1, 'off': 2, 'heat': 3};
3492
- await tuya.sendDataPointEnum(entity, tuya.dataPoints.haozeeSystemMode, lookup[value]);
3493
- },
3494
- },
3495
- haozee_thermostat_current_heating_setpoint: {
3496
- key: ['current_heating_setpoint'],
3497
- convertSet: async (entity, key, value, meta) => {
3498
- const temp = Math.round(value*10);
3499
- await tuya.sendDataPointValue(entity, tuya.dataPoints.haozeeHeatingSetpoint, temp);
3500
- },
3501
- },
3502
- haozee_thermostat_boost_heating: {
3503
- key: ['boost_heating'],
3504
- convertSet: async (entity, key, value, meta) => {
3505
- await tuya.sendDataPointBool(entity, tuya.dataPoints.haozeeBoostHeating, value === 'ON');
3506
- },
3507
- },
3508
- haozee_thermostat_boost_heating_countdown: {
3509
- key: ['boost_heating_countdown'],
3510
- convertSet: async (entity, key, value, meta) => {
3511
- await tuya.sendDataPointValue(entity, tuya.dataPoints.haozeeBoostHeatingCountdown, value);
3512
- },
3513
- },
3514
- haozee_thermostat_window_detection: {
3515
- key: ['window_detection'],
3516
- convertSet: async (entity, key, value, meta) => {
3517
- await tuya.sendDataPointBool(entity, tuya.dataPoints.haozeeWindowDetection, value === 'ON');
3518
- },
3519
- },
3520
- haozee_thermostat_child_lock: {
3521
- key: ['child_lock'],
3522
- convertSet: async (entity, key, value, meta) => {
3523
- await tuya.sendDataPointBool(entity, tuya.dataPoints.haozeeChildLock, value === 'LOCK');
3524
- },
3525
- },
3526
- haozee_thermostat_temperature_calibration: {
3527
- key: ['local_temperature_calibration'],
3528
- convertSet: async (entity, key, value, meta) => {
3529
- let temp = Math.round(value * 10);
3530
- if (temp < 0) {
3531
- temp = 0xFFFFFFFF + temp + 1;
3532
- }
3533
- await tuya.sendDataPointValue(entity, tuya.dataPoints.haozeeTempCalibration, temp);
3534
- },
3535
- },
3536
- haozee_thermostat_max_temperature: {
3537
- key: ['max_temperature'],
3538
- convertSet: async (entity, key, value, meta) => {
3539
- const temp = Math.round(value*10);
3540
- await tuya.sendDataPointValue(entity, tuya.dataPoints.haozeeMaxTemp, temp);
3541
- },
3542
- },
3543
- haozee_thermostat_min_temperature: {
3544
- key: ['min_temperature'],
3545
- convertSet: async (entity, key, value, meta) => {
3546
- const temp = Math.round(value*10);
3547
- await tuya.sendDataPointValue(entity, tuya.dataPoints.haozeeMinTemp, temp);
3548
- },
3549
- },
3550
3488
  hgkg_thermostat_standby: {
3551
3489
  key: ['system_mode'],
3552
3490
  convertSet: async (entity, key, value, meta) => {
package/devices/bitron.js CHANGED
@@ -4,6 +4,7 @@ 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;
7
8
 
8
9
  module.exports = [
9
10
  {
@@ -190,13 +191,43 @@ module.exports = [
190
191
  vendor: 'SMaBiT (Bitron Video)',
191
192
  description: 'Wireless wall thermostat with relay',
192
193
  fromZigbee: [fz.legacy.thermostat_att_report, fz.battery, fz.hvac_user_interface],
193
- toZigbee: [tz.thermostat_control_sequence_of_operation, tz.thermostat_occupied_heating_setpoint,
194
- tz.thermostat_occupied_cooling_setpoint, tz.thermostat_local_temperature_calibration, tz.thermostat_local_temperature,
195
- tz.thermostat_running_state, tz.thermostat_temperature_display_mode, tz.thermostat_keypad_lockout, tz.thermostat_system_mode],
196
- exposes: [e.battery(), exposes.climate().withSetpoint('occupied_heating_setpoint', 7, 30, 0.5).withLocalTemperature()
197
- .withSystemMode(['off', 'heat']).withRunningState(['idle', 'heat'])
198
- .withLocalTemperatureCalibration(), e.keypad_lockout()],
199
- meta: {battery: {voltageToPercentage: '3V_2500_3200'}},
194
+ toZigbee: [
195
+ tz.thermostat_control_sequence_of_operation, tz.thermostat_occupied_heating_setpoint,
196
+ tz.thermostat_occupied_cooling_setpoint, tz.thermostat_local_temperature_calibration,
197
+ tz.thermostat_local_temperature, tz.thermostat_running_state, tz.thermostat_temperature_display_mode,
198
+ tz.thermostat_keypad_lockout, tz.thermostat_system_mode, tz.battery_voltage,
199
+ ],
200
+ exposes: (device, options) => {
201
+ const dynExposes = [];
202
+ let ctrlSeqeOfOper = device ? device.getEndpoint(1).getClusterAttributeValue('hvacThermostat', 'ctrlSeqeOfOper') : null;
203
+ const modes = [];
204
+
205
+ // NOTE: ctrlSeqeOfOper defaults to 2 for this device (according to the manual)
206
+ if (ctrlSeqeOfOper == null) ctrlSeqeOfOper = 2;
207
+
208
+ // NOTE: set cool and/or heat support based on ctrlSeqeOfOper (see lib/constants -> thermostatControlSequenceOfOperations)
209
+ // WARN: a restart of zigbee2mqtt is required after changing ctrlSeqeOfOper for expose data to be re-calculated
210
+ if (ctrlSeqeOfOper >= 2) {
211
+ modes.push('heat');
212
+ }
213
+ if (ctrlSeqeOfOper < 2 || ctrlSeqeOfOper > 3) {
214
+ modes.push('cool');
215
+ }
216
+
217
+ dynExposes.push(exposes.climate()
218
+ .withSetpoint('occupied_heating_setpoint', 7, 30, 0.5)
219
+ .withLocalTemperature()
220
+ .withSystemMode(['off'].concat(modes))
221
+ .withRunningState(['idle'].concat(modes))
222
+ .withLocalTemperatureCalibration()
223
+ .withControlSequenceOfOperation(['heating_only', 'cooling_only'], ea.ALL));
224
+ dynExposes.push(e.keypad_lockout());
225
+ dynExposes.push(e.battery().withAccess(ea.STATE_GET));
226
+ dynExposes.push(e.battery_low());
227
+ dynExposes.push(e.linkquality());
228
+ return dynExposes;
229
+ },
230
+ meta: {battery: {voltageToPercentage: '3V_2500'}},
200
231
  configure: async (device, coordinatorEndpoint, logger) => {
201
232
  const endpoint = device.getEndpoint(1);
202
233
  const binds = [
@@ -204,12 +235,12 @@ module.exports = [
204
235
  ];
205
236
  await reporting.bind(endpoint, coordinatorEndpoint, binds);
206
237
  await reporting.thermostatTemperature(endpoint);
207
- await reporting.thermostatTemperatureCalibration(endpoint);
208
238
  await reporting.thermostatOccupiedHeatingSetpoint(endpoint);
209
239
  await reporting.thermostatOccupiedCoolingSetpoint(endpoint);
210
240
  await reporting.thermostatRunningState(endpoint);
211
241
  await reporting.batteryAlarmState(endpoint);
212
242
  await reporting.batteryVoltage(endpoint);
243
+ await endpoint.read('hvacThermostat', ['ctrlSeqeOfOper', 'localTemperatureCalibration']);
213
244
  },
214
245
  },
215
246
  {
@@ -473,7 +473,7 @@ module.exports = [
473
473
  await firstEndpoint.configureReporting('genPowerCfg', payload1);
474
474
  },
475
475
  exposes: [e.soil_moisture(), e.battery(), e.temperature(),
476
- exposes.numeric('report_delay', ea.STATE_SET).withUnit('Minutes').withValueMin(1).withValueMax(240)
476
+ exposes.numeric('report_delay', ea.STATE_SET).withUnit('min').withValueMin(1).withValueMax(240)
477
477
  .withDescription('Adjust Report Delay. Setting the time in minutes, by default 15 minutes')],
478
478
  },
479
479
  {
@@ -823,7 +823,7 @@ module.exports = [
823
823
  await reporting.batteryPercentageRemaining(endpoint, overides);
824
824
  },
825
825
  exposes: [e.battery(), e.temperature(), e.humidity(),
826
- exposes.numeric('report_delay', ea.STATE_SET).withUnit('Minutes')
826
+ exposes.numeric('report_delay', ea.STATE_SET).withUnit('min')
827
827
  .withDescription('Adjust Report Delay. Setting the time in minutes, by default 5 minutes')
828
828
  .withValueMin(1).withValueMax(60),
829
829
  exposes.binary('enable_temperature', ea.STATE_SET, 'ON', 'OFF').withDescription('Enable Temperature Control'),
@@ -8,11 +8,60 @@ module.exports = [
8
8
  description: 'Smart LED bulb',
9
9
  extend: extend.light_onoff_brightness_colortemp({colorTempRange: [153, 370]}),
10
10
  },
11
+ {
12
+ zigbeeModel: ['EB-E14-FLA-CCT'],
13
+ model: 'EB-E14-FLA-CCT',
14
+ vendor: 'EssentielB',
15
+ description: 'E14 flame CCT light bulb',
16
+ extend: extend.light_onoff_brightness_colortemp({colorTempRange: [153, 454]}),
17
+ },
18
+ {
19
+ zigbeeModel: ['EB-E27-A60-CCT-FC'],
20
+ model: 'EB-E27-A60-CCT-FC',
21
+ vendor: 'EssentielB',
22
+ description: 'E27 A60 CCT filament clear light bulb',
23
+ extend: extend.light_onoff_brightness_colortemp({colorTempRange: [153, 454]}),
24
+ },
25
+ {
26
+ zigbeeModel: ['EB-E27-A60-CCT'],
27
+ model: 'EB-E27-A60-CCT',
28
+ vendor: 'EssentielB',
29
+ description: 'E27 A60 CCT light bulb',
30
+ extend: extend.light_onoff_brightness_colortemp({colorTempRange: [153, 454]}),
31
+ },
32
+ {
33
+ zigbeeModel: ['EB-E27-A60-RGBW'],
34
+ model: 'EB-E27-A60-RGBW',
35
+ vendor: 'EssentielB',
36
+ description: 'E27 A60 RGBW light bulb',
37
+ extend: extend.light_onoff_brightness_colortemp_color({colorTempRange: [153, 370]}),
38
+ },
39
+ {
40
+ zigbeeModel: ['EB-E27-G95-CCT-FV'],
41
+ model: 'EB-E27-G95-CCT-FV',
42
+ vendor: 'EssentielB',
43
+ description: 'Filament vintage globe light bulb',
44
+ extend: extend.light_onoff_brightness_colortemp({colorTempRange: [153, 454]}),
45
+ },
11
46
  {
12
47
  zigbeeModel: ['EB-E27-ST64-CCT-FV'],
13
48
  model: 'EB-E27-ST64-CCT-FV',
14
49
  vendor: 'EssentielB',
15
- description: 'Filament vintage light bulb',
16
- extend: extend.light_onoff_brightness_colortemp_color({colorTempRange: [153, 454]}),
50
+ description: 'Filament vintage edison light bulb',
51
+ extend: extend.light_onoff_brightness_colortemp({colorTempRange: [153, 454]}),
52
+ },
53
+ {
54
+ zigbeeModel: ['EB-GU10-MR16-CCT'],
55
+ model: 'EB-GU10-MR16-CCT',
56
+ vendor: 'EssentielB',
57
+ description: 'GU10 MR16 CCT light bulb',
58
+ extend: extend.light_onoff_brightness_colortemp({colorTempRange: [153, 454]}),
59
+ },
60
+ {
61
+ zigbeeModel: ['EB-GU10-MR16-RGBW'],
62
+ model: 'EB-GU10-MR16-RGBW',
63
+ vendor: 'EssentielB',
64
+ description: 'GU10 MR16 RGBW light bulb',
65
+ extend: extend.light_onoff_brightness_colortemp_color({colorTempRange: [153, 370]}),
17
66
  },
18
67
  ];
@@ -22,7 +22,7 @@ module.exports = [
22
22
  model: 'SA-003-Zigbee',
23
23
  vendor: 'eWeLink',
24
24
  description: 'Zigbee smart plug',
25
- extend: extend.switch(),
25
+ extend: extend.switch({disablePowerOnBehavior: true}),
26
26
  fromZigbee: [fz.on_off_skip_duplicate_transaction],
27
27
  configure: async (device, coordinatorEndpoint, logger) => {
28
28
  const endpoint = device.getEndpoint(1);
@@ -17,7 +17,7 @@ module.exports = [
17
17
  exposes: [e.switch().setAccess('state', ea.STATE_SET),
18
18
  exposes.numeric('threshold', exposes.access.STATE_SET).withValueMin(0).withValueMax(100).withUnit('%')
19
19
  .withDescription('Valve open percentage (multiple of 10)'),
20
- exposes.numeric('timer', exposes.access.STATE_SET).withValueMin(0).withValueMax(600).withUnit('minutes')
20
+ exposes.numeric('timer', exposes.access.STATE_SET).withValueMin(0).withValueMax(600).withUnit('min')
21
21
  .withDescription('Countdown timer in minutes')],
22
22
  },
23
23
  ];
package/devices/ikea.js CHANGED
@@ -741,7 +741,6 @@ module.exports = [
741
741
  exposes.binary('illuminance_above_threshold', ea.STATE, true, false)
742
742
  .withDescription('Indicates whether the device detected bright light (works only in night mode)')],
743
743
  ota: ota.tradfri,
744
- meta: {battery: {dontDividePercentage: true}},
745
744
  configure: async (device, coordinatorEndpoint, logger) => {
746
745
  const endpoint = device.getEndpoint(1);
747
746
  await reporting.bind(endpoint, coordinatorEndpoint, ['genPowerCfg']);
package/devices/livolo.js CHANGED
@@ -246,4 +246,41 @@ module.exports = [
246
246
  }
247
247
  },
248
248
  },
249
+ {
250
+ zigbeeModel: ['TI0001-pir'],
251
+ model: 'TI0001-pir',
252
+ description: 'Zigbee motion Sensor',
253
+ vendor: 'Livolo',
254
+ exposes: [
255
+ e.occupancy(),
256
+ ],
257
+ fromZigbee: [fz.livolo_pir_state],
258
+ toZigbee: [],
259
+ configure: poll,
260
+ onEvent: async (type, data, device) => {
261
+ if (type === 'stop') {
262
+ clearInterval(globalStore.getValue(device, 'interval'));
263
+ globalStore.clearValue(device, 'interval');
264
+ }
265
+ if (['start', 'deviceAnnounce'].includes(type)) {
266
+ await poll(device);
267
+ if (!globalStore.hasValue(device, 'interval')) {
268
+ const interval = setInterval(async () => await poll(device), 10*1000);
269
+ globalStore.putValue(device, 'interval', interval);
270
+ }
271
+ }
272
+ if (data.cluster === 'genPowerCfg' && data.type === 'raw') {
273
+ const dp = data.data[10];
274
+ if (data.data[0] === 0x7a && data.data[1] === 0xd1) {
275
+ const endpoint = device.getEndpoint(6);
276
+ if (dp === 0x01) {
277
+ const options = {manufacturerCode: 0x1ad2, disableDefaultResponse: true, disableResponse: true,
278
+ reservedBits: 3, direction: 1, writeUndiv: true};
279
+ const payload = {0x2002: {value: [0, 0, 0, 0, 0, 0, 0], type: 0x0e}};
280
+ await endpoint.readResponse('genPowerCfg', 0xe9, payload, options);
281
+ }
282
+ }
283
+ }
284
+ },
285
+ },
249
286
  ];
@@ -27,7 +27,7 @@ module.exports = [
27
27
  description: 'Curtain switch',
28
28
  fromZigbee: [fz.cover_position_tilt, tuya.fz.backlight_mode_low_medium_high, fz.tuya_cover_options],
29
29
  toZigbee: [tz.cover_state, tz.cover_position_tilt, tz.tuya_cover_calibration, tz.tuya_cover_reversal,
30
- tuya.tz.backlight_indicator_mode],
30
+ tuya.tz.backlight_indicator_mode_1],
31
31
  meta: {coverInverted: true},
32
32
  exposes: [e.cover_position(), exposes.enum('moving', ea.STATE, ['UP', 'STOP', 'DOWN']),
33
33
  exposes.binary('calibration', ea.ALL, 'ON', 'OFF'), exposes.binary('motor_reversal', ea.ALL, 'ON', 'OFF'),
@@ -183,6 +183,10 @@ module.exports = [
183
183
  await reporting.bind(endpoint, coordinatorEndpoint, ['genOnOff', 'genIdentify', 'manuSpecificTuya', 'hvacFanCtrl']);
184
184
  await reporting.onOff(endpoint);
185
185
  await reporting.fanMode(endpoint);
186
+
187
+ // Device ships with {fanModeSequence: 1} which restricts physical speed
188
+ // button to low/high. Set to 0 to allow low/med/high from physical press.
189
+ await endpoint.write('hvacFanCtrl', {fanModeSequence: 0});
186
190
  },
187
191
  },
188
192
  {
package/devices/moes.js CHANGED
@@ -308,9 +308,9 @@ module.exports = [
308
308
  'You can set up to 4 stages of temperature every for WEEKDAY ➀➁➂➃➄, SATURDAY ➅ and SUNDAY ➆.'),
309
309
  exposes.binary('boost_heating', ea.STATE_SET, 'ON', 'OFF').withDescription('Boost Heating: press and hold "+" for 3 seconds, ' +
310
310
  'the device will enter the boost heating mode, and the ▷╵◁ will flash. The countdown will be displayed in the APP'),
311
- exposes.numeric('boost_heating_countdown', ea.STATE).withUnit('minutes').withDescription('Countdown in minutes')
311
+ exposes.numeric('boost_heating_countdown', ea.STATE).withUnit('min').withDescription('Countdown in minutes')
312
312
  .withValueMin(0).withValueMax(15),
313
- exposes.numeric('boost_heating_countdown_time_set', ea.STATE_SET).withUnit('seconds')
313
+ exposes.numeric('boost_heating_countdown_time_set', ea.STATE_SET).withUnit('s')
314
314
  .withDescription('Boost Time Setting 100 sec - 900 sec, (default = 300 sec)').withValueMin(100)
315
315
  .withValueMax(900).withValueStep(100)],
316
316
  },
package/devices/namron.js CHANGED
@@ -116,6 +116,7 @@ module.exports = [
116
116
  vendor: 'Namron',
117
117
  description: 'ZigBee dimmer 400W',
118
118
  extend: extend.light_onoff_brightness({noConfigure: true}),
119
+ ota: ota.zigbeeOTA,
119
120
  configure: async (device, coordinatorEndpoint, logger) => {
120
121
  await extend.light_onoff_brightness().configure(device, coordinatorEndpoint, logger);
121
122
  const endpoint = device.getEndpoint(1);
package/devices/neo.js CHANGED
@@ -19,7 +19,7 @@ module.exports = [
19
19
  exposes.binary('temperature_alarm', ea.STATE_SET, true, false),
20
20
  exposes.binary('alarm', ea.STATE_SET, true, false),
21
21
  exposes.enum('melody', ea.STATE_SET, Array.from(Array(18).keys()).map((x)=>(x+1).toString())),
22
- exposes.numeric('duration', ea.STATE_SET).withUnit('second').withValueMin(0).withValueMax(1800),
22
+ exposes.numeric('duration', ea.STATE_SET).withUnit('s').withValueMin(0).withValueMax(1800),
23
23
  exposes.numeric('temperature_min', ea.STATE_SET).withUnit('°C').withValueMin(-20).withValueMax(80),
24
24
  exposes.numeric('temperature_max', ea.STATE_SET).withUnit('°C').withValueMin(-20).withValueMax(80),
25
25
  exposes.numeric('humidity_min', ea.STATE_SET).withUnit('%').withValueMin(1).withValueMax(100),
@@ -46,7 +46,7 @@ module.exports = [
46
46
  e.battery_low(),
47
47
  exposes.binary('alarm', ea.STATE_SET, true, false),
48
48
  exposes.enum('melody', ea.STATE_SET, Array.from(Array(18).keys()).map((x)=>(x+1).toString())),
49
- exposes.numeric('duration', ea.STATE_SET).withUnit('second').withValueMin(0).withValueMax(1800),
49
+ exposes.numeric('duration', ea.STATE_SET).withUnit('s').withValueMin(0).withValueMax(1800),
50
50
  exposes.enum('volume', ea.STATE_SET, ['low', 'medium', 'high']),
51
51
  exposes.numeric('battpercentage', ea.STATE).withUnit('%'),
52
52
  ],