zigbee-herdsman-converters 15.0.68 → 15.0.70

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.
@@ -2271,7 +2271,7 @@ const converters = {
2271
2271
  xiaomi_auto_off: {
2272
2272
  key: ['auto_off'],
2273
2273
  convertSet: async (entity, key, value, meta) => {
2274
- if (['ZNCZ04LM', 'ZNCZ12LM'].includes(meta.mapped.model)) {
2274
+ if (['ZNCZ04LM', 'ZNCZ12LM', 'SP-EUC01'].includes(meta.mapped.model)) {
2275
2275
  await entity.write('aqaraOpple', {0x0202: {value: value ? 1 : 0, type: 0x10}}, manufacturerOptions.xiaomi);
2276
2276
  } else if (['ZNCZ11LM'].includes(meta.mapped.model)) {
2277
2277
  const payload = value ?
@@ -2284,6 +2284,13 @@ const converters = {
2284
2284
  }
2285
2285
  return {state: {auto_off: value}};
2286
2286
  },
2287
+ convertGet: async (entity, key, meta) => {
2288
+ if (['ZNCZ04LM', 'ZNCZ12LM', 'SP-EUC01'].includes(meta.mapped.model)) {
2289
+ await entity.read('aqaraOpple', [0x0202], manufacturerOptions.xiaomi);
2290
+ } else {
2291
+ throw new Error('Not supported');
2292
+ }
2293
+ },
2287
2294
  },
2288
2295
  GZCGQ11LM_detection_period: {
2289
2296
  key: ['detection_period'],
@@ -2356,7 +2363,7 @@ const converters = {
2356
2363
  convertSet: async (entity, key, value, meta) => {
2357
2364
  if (['ZNCZ04LM', 'ZNCZ12LM', 'ZNCZ15LM', 'QBCZ14LM', 'QBCZ15LM', 'QBKG19LM', 'QBKG20LM', 'QBKG25LM', 'QBKG26LM',
2358
2365
  'QBKG28LM', 'QBKG31LM', 'QBKG34LM', 'DLKZMK11LM', 'SSM-U01', 'WS-EUK01', 'WS-EUK02',
2359
- 'WS-EUK03', 'WS-EUK04'].includes(meta.mapped.model)) {
2366
+ 'WS-EUK03', 'WS-EUK04', 'SP-EUC01'].includes(meta.mapped.model)) {
2360
2367
  await entity.write('aqaraOpple', {0x0203: {value: value ? 1 : 0, type: 0x10}}, manufacturerOptions.xiaomi);
2361
2368
  } else if (['ZNCZ11LM'].includes(meta.mapped.model)) {
2362
2369
  const payload = value ?
@@ -2372,7 +2379,7 @@ const converters = {
2372
2379
  convertGet: async (entity, key, meta) => {
2373
2380
  if (['ZNCZ04LM', 'ZNCZ12LM', 'ZNCZ15LM', 'QBCZ15LM', 'QBCZ14LM', 'QBKG19LM', 'QBKG20LM', 'QBKG25LM', 'QBKG26LM',
2374
2381
  'QBKG28LM', 'QBKG31LM', 'QBKG34LM', 'DLKZMK11LM', 'SSM-U01', 'WS-EUK01', 'WS-EUK02',
2375
- 'WS-EUK03', 'WS-EUK04'].includes(meta.mapped.model)) {
2382
+ 'WS-EUK03', 'WS-EUK04', 'SP-EUC01'].includes(meta.mapped.model)) {
2376
2383
  await entity.read('aqaraOpple', [0x0203], manufacturerOptions.xiaomi);
2377
2384
  } else {
2378
2385
  throw new Error('Not supported');
@@ -0,0 +1,44 @@
1
+ const fz = require('../converters/fromZigbee');
2
+ const tz = require('../converters/toZigbee');
3
+ const exposes = require('../lib/exposes');
4
+ const reporting = require('../lib/reporting');
5
+ const tuya = require('../lib/tuya');
6
+ const e = exposes.presets;
7
+
8
+ module.exports = [
9
+ {
10
+ fingerprint: tuya.fingerprint('TS011F', ['_TZ3000_j0ktmul1']),
11
+ model: 'AUT000069',
12
+ vendor: 'AutomatOn',
13
+ description: 'Underfloor heating controller - 5 zones',
14
+ fromZigbee: [fz.on_off, fz.ignore_basic_report, tuya.fz.power_on_behavior_2, tuya.fz.child_lock],
15
+ toZigbee: [tz.on_off, tuya.tz.power_on_behavior_2, tuya.tz.child_lock],
16
+ exposes: [
17
+ e.child_lock(),
18
+ e.switch().withEndpoint('l1'),
19
+ e.switch().withEndpoint('l2'),
20
+ e.switch().withEndpoint('l3'),
21
+ e.switch().withEndpoint('l4'),
22
+ e.switch().withEndpoint('l5'),
23
+ e.power_on_behavior().withEndpoint('l1'),
24
+ e.power_on_behavior().withEndpoint('l2'),
25
+ e.power_on_behavior().withEndpoint('l3'),
26
+ e.power_on_behavior().withEndpoint('l4'),
27
+ e.power_on_behavior().withEndpoint('l5'),
28
+ ],
29
+ endpoint: (device) => {
30
+ return {'l1': 1, 'l2': 2, 'l3': 3, 'l4': 4, 'l5': 5};
31
+ },
32
+ meta: {multiEndpoint: true},
33
+ configure: async (device, coordinatorEndpoint, logger) => {
34
+ await tuya.configureMagicPacket(device, coordinatorEndpoint, logger);
35
+ await reporting.bind(device.getEndpoint(1), coordinatorEndpoint, ['genOnOff']);
36
+ await reporting.bind(device.getEndpoint(2), coordinatorEndpoint, ['genOnOff']);
37
+ await reporting.bind(device.getEndpoint(3), coordinatorEndpoint, ['genOnOff']);
38
+ await reporting.bind(device.getEndpoint(4), coordinatorEndpoint, ['genOnOff']);
39
+ await reporting.bind(device.getEndpoint(5), coordinatorEndpoint, ['genOnOff']);
40
+ device.powerSource = 'Mains (single phase)';
41
+ device.save();
42
+ },
43
+ },
44
+ ];
package/devices/bosch.js CHANGED
@@ -49,7 +49,7 @@ const displayOrientation = {
49
49
  // Radiator Thermostat II
50
50
  const tzLocal = {
51
51
  bosch_thermostat: {
52
- key: ['window_open', 'boost', 'system_mode'],
52
+ key: ['window_open', 'boost', 'system_mode', 'pi_heating_demand'],
53
53
  convertSet: async (entity, key, value, meta) => {
54
54
  if (key === 'window_open') {
55
55
  value = value.toUpperCase();
@@ -66,7 +66,7 @@ const tzLocal = {
66
66
  return {state: {boost: value}};
67
67
  }
68
68
  if (key === 'system_mode') {
69
- // Map system_mode (Off/Auto/Heat) to Boschg operating mode
69
+ // Map system_mode (Off/Auto/Heat) to Bosch operating mode
70
70
  value = value.toLowerCase();
71
71
 
72
72
  let opMode = operatingModes.manual; // OperatingMode 1 = Manual (Default)
@@ -79,6 +79,12 @@ const tzLocal = {
79
79
  await entity.write('hvacThermostat', {0x4007: {value: opMode, type: herdsman.Zcl.DataType.enum8}}, boschManufacturer);
80
80
  return {state: {system_mode: value}};
81
81
  }
82
+ if (key === 'pi_heating_demand') {
83
+ await entity.write('hvacThermostat',
84
+ {0x4020: {value: value, type: herdsman.Zcl.DataType.enum8}},
85
+ boschManufacturer);
86
+ return {state: {pi_heating_demand: value}};
87
+ }
82
88
  },
83
89
  convertGet: async (entity, key, meta) => {
84
90
  switch (key) {
@@ -91,6 +97,9 @@ const tzLocal = {
91
97
  case 'system_mode':
92
98
  await entity.read('hvacThermostat', [0x4007], boschManufacturer);
93
99
  break;
100
+ case 'pi_heating_demand':
101
+ await entity.read('hvacThermostat', [0x4020], boschManufacturer);
102
+ break;
94
103
 
95
104
  default: // Unknown key
96
105
  throw new Error(`Unhandled key toZigbee.bosch_thermostat.convertGet ${key}`);
@@ -426,7 +435,7 @@ const definition = [
426
435
  .withSetpoint('occupied_heating_setpoint', 5, 30, 0.5)
427
436
  .withLocalTemperatureCalibration(-5, 5, 0.1)
428
437
  .withSystemMode(['off', 'heat', 'auto'])
429
- .withPiHeatingDemand(ea.STATE),
438
+ .withPiHeatingDemand(ea.ALL),
430
439
  exposes.binary('boost', ea.ALL, 'ON', 'OFF')
431
440
  .withDescription('Activate Boost heating'),
432
441
  exposes.binary('window_open', ea.ALL, 'ON', 'OFF')
@@ -473,6 +482,13 @@ const definition = [
473
482
  maximumReportInterval: constants.repInterval.HOUR,
474
483
  reportableChange: 1,
475
484
  }], boschManufacturer);
485
+ // report boost as it's disabled by thermostat after some time
486
+ await endpoint.configureReporting('hvacThermostat', [{
487
+ attribute: {ID: 0x4043, type: herdsman.Zcl.DataType.enum8},
488
+ minimumReportInterval: 0,
489
+ maximumReportInterval: constants.repInterval.HOUR,
490
+ reportableChange: 1,
491
+ }], boschManufacturer);
476
492
 
477
493
  await endpoint.read('hvacThermostat', ['localTemperatureCalibration']);
478
494
  await endpoint.read('hvacThermostat', [0x4007, 0x4020, 0x4042, 0x4043], boschManufacturer);
package/devices/casaia.js CHANGED
@@ -39,6 +39,7 @@ module.exports = [
39
39
  fromZigbee: [fz.electrical_measurement, fz.metering, fz.on_off],
40
40
  toZigbee: [tz.on_off],
41
41
  exposes: [e.switch(), e.power(), e.energy()],
42
+ meta: {publishDuplicateTransaction: true},
42
43
  configure: async (device, coordinatorEndpoint, logger) => {
43
44
  const endpoint = device.getEndpoint(1);
44
45
  await reporting.bind(endpoint, coordinatorEndpoint, ['genOnOff', 'haElectricalMeasurement', 'seMetering']);
package/devices/giex.js CHANGED
@@ -1,135 +1,222 @@
1
1
  const exposes = require('../lib/exposes');
2
2
  const tuya = require('../lib/tuya');
3
- const e = exposes.presets;
4
- const ea = exposes.access;
3
+
4
+ const {presets: ep, access: ea} = exposes;
5
+
6
+ const CAPACITY = 'capacity';
7
+ const DURATION = 'duration';
8
+ const MINUTES_IN_A_DAY = 1440;
9
+ const OFF = 'OFF';
10
+ const ON = 'ON';
11
+ const SAFETY_MIN_SECS = 10;
12
+ const SECONDS_IN_12_HOURS = 43200;
13
+
14
+ const toLocalTime = (time, timezone) => {
15
+ if (time === '--:--:--') {
16
+ return time;
17
+ }
18
+
19
+ const local = new Date(`2000-01-01T${time}.000${timezone}`); // Using 1970 instead produces edge cases
20
+ return local.toTimeString().split(' ').shift();
21
+ };
22
+
23
+ const keys = {
24
+ giexWaterValve: {
25
+ battery: 'battery',
26
+ currentTemperature: 'current_temperature',
27
+ cycleIrrigationInterval: 'cycle_irrigation_interval',
28
+ cycleIrrigationNumTimes: 'cycle_irrigation_num_times',
29
+ irrigationEndTime: 'irrigation_end_time',
30
+ irrigationStartTime: 'irrigation_start_time',
31
+ irrigationTarget: 'irrigation_target',
32
+ lastIrrigationDuration: 'last_irrigation_duration',
33
+ mode: 'mode',
34
+ state: 'state',
35
+ waterConsumed: 'water_consumed',
36
+ },
37
+ };
5
38
 
6
39
  const dataPoints = {
7
- // GIEX Water Valve
8
- giexWaterValveState: 2,
9
- giexWaterValveMode: 1,
10
- giexWaterValveIrrigationTarget: 104,
11
- giexWaterValveWaterConsumed: 111,
12
- giexWaterValveIrrigationStartTime: 101,
13
- giexWaterValveIrrigationEndTime: 102,
14
- giexWaterValveLastIrrigationDuration: 114,
15
- giexWaterValveBattery: 108,
16
- giexWaterValveCycleIrrigationNumTimes: 103,
17
- giexWaterValveCycleIrrigationInterval: 105,
18
- giexWaterValveCurrentTempurature: 106,
40
+ giexWaterValve: {
41
+ battery: 108,
42
+ currentTemperature: 106,
43
+ cycleIrrigationInterval: 105,
44
+ cycleIrrigationNumTimes: 103,
45
+ irrigationEndTime: 102,
46
+ irrigationStartTime: 101,
47
+ irrigationTarget: 104,
48
+ lastIrrigationDuration: 114,
49
+ mode: 1,
50
+ state: 2,
51
+ waterConsumed: 111,
52
+ },
53
+ };
54
+
55
+ const fzModelConverters = {
56
+ QT06_1: {
57
+ // _TZE200_sh1btabb timezone is GMT+8
58
+ time: (value) => toLocalTime(value, '+08:00'),
59
+ },
19
60
  };
20
61
 
21
62
  const fzLocal = {
22
- giex_water_valve:
23
- {
63
+ giexWaterValve: {
24
64
  cluster: 'manuSpecificTuya',
25
65
  type: ['commandDataResponse', 'commandDataReport'],
26
66
  convert: (model, msg, publish, options, meta) => {
67
+ const modelConverters = fzModelConverters[model.model] || {};
27
68
  for (const dpValue of msg.data.dpValues) {
28
69
  const value = tuya.getDataValue(dpValue);
29
- const dp = dpValue.dp;
70
+ const {dp} = dpValue;
30
71
  switch (dp) {
31
- case dataPoints.giexWaterValveState: {
32
- return {state: value ? 'ON': 'OFF'};
33
- }
34
- case dataPoints.giexWaterValveMode: {
35
- return {mode: value ? 'capacity': 'duration'};
36
- }
37
- case dataPoints.giexWaterValveIrrigationTarget: {
38
- return {irrigation_target: value};
39
- }
40
- case dataPoints.giexWaterValveCycleIrrigationNumTimes: {
41
- return {cycle_irrigation_num_times: value};
42
- }
43
- case dataPoints.giexWaterValveCycleIrrigationInterval: {
44
- return {cycle_irrigation_interval: value};
45
- }
46
- case dataPoints.giexWaterValveWaterConsumed: {
47
- return {water_consumed: value};
48
- }
49
- case dataPoints.giexWaterValveIrrigationStartTime: {
50
- return {irrigation_start_time: value};
51
- }
52
- case dataPoints.giexWaterValveIrrigationEndTime: {
53
- return {irrigation_end_time: value};
54
- }
55
- case dataPoints.giexWaterValveLastIrrigationDuration: {
56
- return {last_irrigation_duration: value};
57
- }
58
- case dataPoints.giexWaterValveBattery: {
59
- return {battery: value};
60
- }
61
- case dataPoints.giexWaterValveCurrentTempurature: {
62
- return; // Do Nothing - value ignored because isn't a valid tempurature reading. Misdocumented and usage unclear
63
- }
64
- default: {
65
- meta.logger.warn(`fz:giex_water_valve: NOT RECOGNIZED DP #${dp} with VALUE = ${value}`);
66
- }
72
+ case dataPoints.giexWaterValve.state:
73
+ return {[keys.giexWaterValve.state]: value ? ON: OFF};
74
+ case dataPoints.giexWaterValve.mode:
75
+ return {[keys.giexWaterValve.mode]: value ? CAPACITY: DURATION};
76
+ case dataPoints.giexWaterValve.irrigationTarget:
77
+ return {[keys.giexWaterValve.irrigationTarget]: value};
78
+ case dataPoints.giexWaterValve.cycleIrrigationNumTimes:
79
+ return {[keys.giexWaterValve.cycleIrrigationNumTimes]: value};
80
+ case dataPoints.giexWaterValve.cycleIrrigationInterval:
81
+ return {[keys.giexWaterValve.cycleIrrigationInterval]: value};
82
+ case dataPoints.giexWaterValve.waterConsumed:
83
+ return {[keys.giexWaterValve.waterConsumed]: value};
84
+ case dataPoints.giexWaterValve.irrigationStartTime:
85
+ return {[keys.giexWaterValve.irrigationStartTime]: modelConverters.time?.(value) || value};
86
+ case dataPoints.giexWaterValve.irrigationEndTime:
87
+ return {[keys.giexWaterValve.irrigationEndTime]: modelConverters.time?.(value) || value};
88
+ case dataPoints.giexWaterValve.lastIrrigationDuration:
89
+ return {[keys.giexWaterValve.lastIrrigationDuration]: value.split(',').shift()}; // Remove meaningless ,0 suffix
90
+ case dataPoints.giexWaterValve.battery:
91
+ return {[keys.giexWaterValve.battery]: value};
92
+ case dataPoints.giexWaterValve.currentTemperature:
93
+ return; // Do Nothing - value ignored because it isn't a valid temperature reading (misdocumented and usage unclear)
94
+ default: // Unknown data point warning
95
+ meta.logger.warn(`fzLocal.giexWaterValve: Unrecognized DP #${dp} with VALUE = ${value}`);
67
96
  }
68
97
  }
69
98
  },
70
99
  },
71
100
  };
72
101
 
102
+ const tzModelConverters = {
103
+ QT06_2: {
104
+ // _TZE200_a7sghmms irrigation time should not be less than 10 secs as per GiEX advice
105
+ irrigationTarget: (value, mode) => value > 0 && value < SAFETY_MIN_SECS && mode === DURATION ? SAFETY_MIN_SECS : value,
106
+ },
107
+ };
108
+
73
109
  const tzLocal = {
74
- giex_water_valve:
110
+ giexWaterValve:
75
111
  {
76
- key: ['mode', 'irrigation_target', 'state', 'cycle_irrigation_num_times', 'cycle_irrigation_interval'],
112
+ key: [
113
+ keys.giexWaterValve.mode,
114
+ keys.giexWaterValve.irrigationTarget,
115
+ keys.giexWaterValve.state,
116
+ keys.giexWaterValve.cycleIrrigationNumTimes,
117
+ keys.giexWaterValve.cycleIrrigationInterval,
118
+ ],
77
119
  convertSet: async (entity, key, value, meta) => {
120
+ const modelConverters = tzModelConverters[meta.mapped?.model] || {};
78
121
  switch (key) {
79
- case 'mode': {
80
- let mode = 0;
81
- if (value === 'duration') mode = 0;
82
- else if (value === 'capacity') mode = 1;
83
- await tuya.sendDataPointBool(entity, dataPoints.giexWaterValveMode, mode);
84
- return {state: {mode: value}};
85
- }
86
- case 'irrigation_target':
87
- await tuya.sendDataPointValue(entity, dataPoints.giexWaterValveIrrigationTarget, value);
88
- return {state: {irrigation_target: value}};
89
- case 'state':
90
- await tuya.sendDataPointBool(entity, dataPoints.giexWaterValveState, value === 'ON');
122
+ case keys.giexWaterValve.state:
123
+ await tuya.sendDataPointBool(entity, dataPoints.giexWaterValve.state, value === ON);
91
124
  break;
92
- case 'cycle_irrigation_num_times':
93
- await tuya.sendDataPointValue(entity, dataPoints.giexWaterValveCycleIrrigationNumTimes, value);
94
- return {state: {cycle_irrigation_num_times: value}};
95
- case 'cycle_irrigation_interval':
96
- await tuya.sendDataPointValue(entity, dataPoints.giexWaterValveCycleIrrigationInterval, value);
97
- return {state: {cycle_irrigation_interval: value}};
125
+ case keys.giexWaterValve.mode:
126
+ await tuya.sendDataPointBool(entity, dataPoints.giexWaterValve.mode, value === CAPACITY);
127
+ return {state: {[keys.giexWaterValve.mode]: value}};
128
+ case keys.giexWaterValve.irrigationTarget: {
129
+ const mode = meta.state?.[keys.giexWaterValve.mode];
130
+ const sanitizedValue = modelConverters.irrigationTarget?.(value, mode) || value;
131
+ await tuya.sendDataPointValue(entity, dataPoints.giexWaterValve.irrigationTarget, sanitizedValue);
132
+ return {state: {[keys.giexWaterValve.irrigationTarget]: sanitizedValue}};
133
+ }
134
+ case keys.giexWaterValve.cycleIrrigationNumTimes:
135
+ await tuya.sendDataPointValue(entity, dataPoints.giexWaterValve.cycleIrrigationNumTimes, value);
136
+ return {state: {[keys.giexWaterValve.cycleIrrigationNumTimes]: value}};
137
+ case keys.giexWaterValve.cycleIrrigationInterval:
138
+ await tuya.sendDataPointValue(entity, dataPoints.giexWaterValve.cycleIrrigationInterval, value);
139
+ return {state: {[keys.giexWaterValve.cycleIrrigationInterval]: value}};
98
140
  default: // Unknown key warning
99
- meta.logger.warn(`tz.GIEX_water_valve: Unhandled key ${key}`);
141
+ meta.logger.warn(`tzLocal.giexWaterValve: Unhandled KEY ${key}`);
100
142
  }
101
143
  },
102
144
  },
103
145
  };
104
146
 
147
+ const exportTemplates = {
148
+ giexWaterValve: {
149
+ vendor: 'GiEX',
150
+ description: 'Water irrigation valve',
151
+ onEvent: tuya.onEventSetLocalTime,
152
+ fromZigbee: [fzLocal.giexWaterValve],
153
+ toZigbee: [tzLocal.giexWaterValve],
154
+ exposes: [
155
+ ep.battery(),
156
+ exposes.binary(keys.giexWaterValve.state, ea.STATE_SET, ON, OFF)
157
+ .withDescription('State'),
158
+ exposes.enum(keys.giexWaterValve.mode, ea.STATE_SET, [DURATION, CAPACITY])
159
+ .withDescription('Irrigation mode'),
160
+ exposes.numeric(keys.giexWaterValve.cycleIrrigationNumTimes, ea.STATE_SET)
161
+ .withValueMin(0)
162
+ .withValueMax(100)
163
+ .withDescription('Number of cycle irrigation times, set to 0 for single cycle'),
164
+ exposes.numeric(keys.giexWaterValve.irrigationStartTime, ea.STATE)
165
+ .withDescription('Last irrigation start time'),
166
+ exposes.numeric(keys.giexWaterValve.irrigationEndTime, ea.STATE)
167
+ .withDescription('Last irrigation end time'),
168
+ exposes.numeric(keys.giexWaterValve.lastIrrigationDuration, ea.STATE)
169
+ .withDescription('Last irrigation duration'),
170
+ exposes.numeric(keys.giexWaterValve.waterConsumed, ea.STATE)
171
+ .withUnit('L')
172
+ .withDescription('Last irrigation water consumption'),
173
+ ],
174
+ },
175
+ };
176
+
105
177
  module.exports = [
178
+ // _TZE200_sh1btabb uses minutes, timezone is GMT+8
106
179
  {
180
+ ...exportTemplates.giexWaterValve,
181
+ model: 'QT06_1',
107
182
  fingerprint: [
108
183
  {modelID: 'TS0601', manufacturerName: '_TZE200_sh1btabb'},
184
+ ],
185
+ exposes: [
186
+ ...exportTemplates.giexWaterValve.exposes,
187
+ exposes.numeric(keys.giexWaterValve.irrigationTarget, ea.STATE_SET)
188
+ .withValueMin(0)
189
+ .withValueMax(MINUTES_IN_A_DAY)
190
+ .withUnit('minutes or litres')
191
+ .withDescription('Irrigation target, duration in minutes or capacity in litres (depending on mode)'),
192
+ exposes.numeric(keys.giexWaterValve.cycleIrrigationInterval, ea.STATE_SET)
193
+ .withValueMin(0)
194
+ .withValueMax(MINUTES_IN_A_DAY)
195
+ .withUnit('min')
196
+ .withDescription('Cycle irrigation interval'),
197
+ ],
198
+ },
199
+ // _TZE200_a7sghmms uses seconds, timezone is local
200
+ {
201
+ ...exportTemplates.giexWaterValve,
202
+ model: 'QT06_2',
203
+ fingerprint: [
109
204
  {modelID: 'TS0601', manufacturerName: '_TZE200_a7sghmms'},
110
205
  ],
111
- model: 'QT06',
112
- vendor: 'GiEX',
113
- description: 'Water irrigation valve',
114
- onEvent: tuya.onEventSetLocalTime,
115
- fromZigbee: [fzLocal.giex_water_valve],
116
- toZigbee: [tzLocal.giex_water_valve],
117
206
  exposes: [
118
- e.battery(),
119
- exposes.binary('state', ea.STATE_SET, 'ON', 'OFF').withDescription('State'),
120
- exposes.enum('mode', ea.STATE_SET, ['duration', 'capacity']).withDescription('Irrigation mode'),
121
- exposes.numeric('irrigation_target', exposes.access.STATE_SET).withValueMin(0).withValueMax(3600).withUnit('seconds or litres')
122
- .withDescription('Irrigation target, duration in seconds or capacity in litres (depending on mode)'),
123
- exposes.numeric('cycle_irrigation_num_times', exposes.access.STATE_SET).withValueMin(0).withValueMax(100)
124
- .withDescription('Number of cycle irrigation times, set to 0 for single cycle'),
125
- exposes.numeric('cycle_irrigation_interval', exposes.access.STATE_SET).withValueMin(0).withValueMax(3600).withUnit('sec')
207
+ ...exportTemplates.giexWaterValve.exposes,
208
+ exposes.numeric(keys.giexWaterValve.irrigationTarget, ea.STATE_SET)
209
+ .withValueMin(0)
210
+ .withValueMax(SECONDS_IN_12_HOURS)
211
+ .withUnit('seconds or litres')
212
+ .withDescription('Irrigation target, duration in seconds or capacity in litres (depending on mode), ' +
213
+ 'set to 0 to leave the valve on indefinitely, ' +
214
+ 'for safety reasons the target will be forced to a minimum of 10 seconds in duration mode'),
215
+ exposes.numeric(keys.giexWaterValve.cycleIrrigationInterval, ea.STATE_SET)
216
+ .withValueMin(0)
217
+ .withValueMax(SECONDS_IN_12_HOURS)
218
+ .withUnit('sec')
126
219
  .withDescription('Cycle irrigation interval'),
127
- exposes.numeric('irrigation_start_time', ea.STATE).withDescription('Last irrigation start time (GMT)'),
128
- exposes.numeric('irrigation_end_time', ea.STATE).withDescription('Last irrigation end time (GMT)'),
129
- exposes.numeric('last_irrigation_duration', exposes.access.STATE)
130
- .withDescription('Last irrigation duration'),
131
- exposes.numeric('water_consumed', exposes.access.STATE).withUnit('L')
132
- .withDescription('Last irrigation water consumption'),
133
220
  ],
134
221
  },
135
222
  ];
package/devices/hzc.js ADDED
@@ -0,0 +1,21 @@
1
+ const fz = require('../converters/fromZigbee');
2
+ const exposes = require('../lib/exposes');
3
+ const reporting = require('../lib/reporting');
4
+ const e = exposes.presets;
5
+
6
+ module.exports = [
7
+ {
8
+ zigbeeModel: ['WaterLeakageSensor-ZB3.0'],
9
+ model: 'S900W-ZG',
10
+ vendor: 'HZC',
11
+ description: 'Water leak sensor',
12
+ fromZigbee: [fz.ias_water_leak_alarm_1, fz.battery],
13
+ toZigbee: [],
14
+ exposes: [e.water_leak(), e.battery_low(), e.battery()],
15
+ configure: async (device, coordinatorEndpoint, logger) => {
16
+ const endpoint = device.getEndpoint(1);
17
+ await reporting.bind(endpoint, coordinatorEndpoint, ['genPowerCfg']);
18
+ await reporting.batteryPercentageRemaining(endpoint);
19
+ },
20
+ },
21
+ ];
@@ -8,7 +8,7 @@ module.exports = [
8
8
  model: '4058075208384',
9
9
  vendor: 'LEDVANCE',
10
10
  description: 'SMART+ Classic A60 E27 Tunable white',
11
- extend: extend.light_onoff_brightness_colortemp({colorTempRange: [153, 370], disablePowerOnBehavior: true}),
11
+ extend: extend.light_onoff_brightness_colortemp({colorTempRange: [153, 370]}),
12
12
  ota: ota.ledvance,
13
13
  },
14
14
  {
@@ -55,7 +55,7 @@ module.exports = [
55
55
  model: 'AC25697',
56
56
  vendor: 'LEDVANCE',
57
57
  description: 'SMART+ CLASSIC MULTICOLOUR 60 10W E27',
58
- extend: extend.ledvance.light_onoff_brightness_colortemp_color({colorTempRange: [153, 500], disablePowerOnBehavior: true}),
58
+ extend: extend.ledvance.light_onoff_brightness_colortemp_color({colorTempRange: [153, 500]}),
59
59
  ota: ota.ledvance,
60
60
  },
61
61
  {
package/devices/lellki.js CHANGED
@@ -121,7 +121,7 @@ module.exports = [
121
121
  extend: extend.switch(),
122
122
  fromZigbee: [fz.on_off_force_multiendpoint, fz.electrical_measurement, fz.metering, fz.ignore_basic_report,
123
123
  tuya.fz.power_outage_memory],
124
- toZigbee: [tz.on_off, tuya.tz.power_on_behavior],
124
+ toZigbee: [tz.on_off, tuya.tz.power_on_behavior_1],
125
125
  configure: async (device, coordinatorEndpoint, logger) => {
126
126
  const endpoint = device.getEndpoint(1);
127
127
  await tuya.configureMagicPacket(device, coordinatorEndpoint, logger);
package/devices/osram.js CHANGED
@@ -335,7 +335,7 @@ module.exports = [
335
335
  model: 'AC03648',
336
336
  vendor: 'OSRAM',
337
337
  description: 'SMART+ spot GU5.3 tunable white',
338
- extend: extend.ledvance.light_onoff_brightness_colortemp({colorTempRange: [153, 370], disablePowerOnBehavior: true}),
338
+ extend: extend.ledvance.light_onoff_brightness_colortemp({colorTempRange: [153, 370]}),
339
339
  ota: ota.ledvance,
340
340
  },
341
341
  {
@@ -412,7 +412,7 @@ module.exports = [
412
412
  model: '4062172044776_1',
413
413
  vendor: 'OSRAM',
414
414
  description: 'Zigbee 3.0 DALI CONV LI dimmer for DALI-based luminaires (only one device)',
415
- extend: extend.ledvance.light_onoff_brightness({disablePowerOnBehavior: true}),
415
+ extend: extend.ledvance.light_onoff_brightness(),
416
416
  ota: ota.zigbeeOTA,
417
417
  },
418
418
  {
@@ -425,7 +425,7 @@ module.exports = [
425
425
  fz.command_toggle, fz.command_move, fz.command_stop],
426
426
  extend: extend.ledvance.light_onoff_brightness({noConfigure: true}),
427
427
  exposes: [e.action(['toggle', 'brightness_move_up', 'brightness_move_down', 'brightness_stop']),
428
- ...extend.ledvance.light_onoff_brightness({noConfigure: true, disablePowerOnBehavior: true}).exposes],
428
+ ...extend.ledvance.light_onoff_brightness({noConfigure: true}).exposes],
429
429
  ota: ota.zigbeeOTA,
430
430
  configure: async (device, coordinatorEndpoint, logger) => {
431
431
  await reporting.bind(device.getEndpoint(10), coordinatorEndpoint, ['genLevelCtrl', 'genOnOff']);
package/devices/owon.js CHANGED
@@ -241,6 +241,7 @@ module.exports = [
241
241
  device.save();
242
242
  }
243
243
  },
244
+ meta: {publishDuplicateTransaction: true},
244
245
  exposes: [e.energy(),
245
246
  exposes.numeric('voltage_l1', ea.STATE).withUnit('V').withDescription('Phase 1 voltage'),
246
247
  exposes.numeric('voltage_l2', ea.STATE).withUnit('V').withDescription('Phase 2 voltage'),
@@ -2813,7 +2813,7 @@ module.exports = [
2813
2813
  extend: philips.extend.light_onoff_brightness_colortemp_color({colorTempRange: [153, 500]}),
2814
2814
  },
2815
2815
  {
2816
- zigbeeModel: ['915005997501', '915005997401'],
2816
+ zigbeeModel: ['915005997501', '915005997401', '929003542401'],
2817
2817
  model: '915005997501',
2818
2818
  vendor: 'Philips',
2819
2819
  description: 'Hue Bluetooth white & color ambiance ceiling lamp Infuse large',
@@ -16,8 +16,8 @@ module.exports = [
16
16
  configure: async (device, coordinatorEndpoint, logger) => {
17
17
  const endpoint = device.getEndpoint(1);
18
18
  await reporting.bind(endpoint, coordinatorEndpoint, ['genPowerCfg', 'closuresWindowCovering']);
19
- await reporting.batteryPercentageRemaining(endpoint);
20
- await reporting.currentPositionLiftPercentage(endpoint);
19
+ device.powerSource = 'Mains (single phase)';
20
+ device.save();
21
21
  },
22
22
  exposes: [e.cover_position(), e.battery()],
23
23
  },
package/devices/tuya.js CHANGED
@@ -133,20 +133,6 @@ const tzLocal = {
133
133
  return {state: {[key]: value}};
134
134
  },
135
135
  },
136
- power_on_behavior: {
137
- key: ['power_on_behavior'],
138
- convertSet: async (entity, key, value, meta) => {
139
- value = value.toLowerCase();
140
- const lookup = {'off': 0, 'on': 1, 'previous': 2};
141
- utils.validateValue(value, Object.keys(lookup));
142
- const pState = lookup[value];
143
- await entity.write('manuSpecificTuya_3', {'powerOnBehavior': pState}, {disableDefaultResponse: true});
144
- return {state: {power_on_behavior: value}};
145
- },
146
- convertGet: async (entity, key, meta) => {
147
- await entity.read('manuSpecificTuya_3', ['powerOnBehavior']);
148
- },
149
- },
150
136
  zb_sm_cover: {
151
137
  key: ['state', 'position', 'reverse_direction', 'top_limit', 'bottom_limit', 'favorite_position', 'goto_positon', 'report'],
152
138
  convertSet: async (entity, key, value, meta) => {
@@ -540,19 +526,6 @@ const fzLocal = {
540
526
  }
541
527
  },
542
528
  },
543
- power_on_behavior: {
544
- cluster: 'manuSpecificTuya_3',
545
- type: ['attributeReport', 'readResponse'],
546
- convert: (model, msg, publish, options, meta) => {
547
- const attribute = 'powerOnBehavior';
548
- const lookup = {0: 'off', 1: 'on', 2: 'previous'};
549
-
550
- if (msg.data.hasOwnProperty(attribute)) {
551
- const property = utils.postfixWithEndpointName('power_on_behavior', msg, model, meta);
552
- return {[property]: lookup[msg.data[attribute]]};
553
- }
554
- },
555
- },
556
529
  zb_sm_cover: {
557
530
  cluster: 'manuSpecificTuya',
558
531
  type: ['commandDataReport', 'commandDataResponse'],
@@ -1918,7 +1891,7 @@ module.exports = [
1918
1891
  vendor: 'TuYa',
1919
1892
  fromZigbee: [fz.on_off, fz.electrical_measurement, fz.metering, fz.ignore_basic_report,
1920
1893
  tuya.fz.power_outage_memory, tuya.fz.switch_type],
1921
- toZigbee: [tz.on_off, tuya.tz.power_on_behavior, tuya.tz.switch_type],
1894
+ toZigbee: [tz.on_off, tuya.tz.power_on_behavior_1, tuya.tz.switch_type],
1922
1895
  configure: async (device, coordinatorEndpoint, logger) => {
1923
1896
  await tuya.configureMagicPacket(device, coordinatorEndpoint, logger);
1924
1897
  const endpoint = device.getEndpoint(1);
@@ -1963,9 +1936,9 @@ module.exports = [
1963
1936
  model: 'TS000F_power',
1964
1937
  description: 'Switch with power monitoring',
1965
1938
  vendor: 'TuYa',
1966
- fromZigbee: [fz.on_off, fz.electrical_measurement, fz.metering, fz.ignore_basic_report, tuya.fz.power_on_behavior,
1939
+ fromZigbee: [fz.on_off, fz.electrical_measurement, fz.metering, fz.ignore_basic_report, tuya.fz.power_on_behavior_1,
1967
1940
  tuya.fz.switch_type],
1968
- toZigbee: [tz.on_off, tuya.tz.power_on_behavior, tuya.tz.switch_type],
1941
+ toZigbee: [tz.on_off, tuya.tz.power_on_behavior_1, tuya.tz.switch_type],
1969
1942
  configure: async (device, coordinatorEndpoint, logger) => {
1970
1943
  const endpoint = device.getEndpoint(1);
1971
1944
  await tuya.configureMagicPacket(device, coordinatorEndpoint, logger);
@@ -2593,7 +2566,7 @@ module.exports = [
2593
2566
  vendor: 'TuYa',
2594
2567
  fromZigbee: [fz.on_off, fz.electrical_measurement, fz.metering, fz.ignore_basic_report, tuya.fz.power_outage_memory,
2595
2568
  tuya.fz.indicator_mode],
2596
- toZigbee: [tz.on_off, tuya.tz.power_on_behavior, tuya.tz.backlight_indicator_mode_1],
2569
+ toZigbee: [tz.on_off, tuya.tz.power_on_behavior_1, tuya.tz.backlight_indicator_mode_1],
2597
2570
  configure: async (device, coordinatorEndpoint, logger) => {
2598
2571
  const endpoint = device.getEndpoint(1);
2599
2572
  await reporting.bind(endpoint, coordinatorEndpoint, ['genOnOff', 'haElectricalMeasurement', 'seMetering']);
@@ -3208,8 +3181,8 @@ module.exports = [
3208
3181
  model: 'TS0004',
3209
3182
  vendor: 'TuYa',
3210
3183
  description: 'Smart light switch - 4 gang with neutral wire',
3211
- fromZigbee: [fz.on_off, fzLocal.power_on_behavior, fz.ignore_basic_report],
3212
- toZigbee: [tz.on_off, tzLocal.power_on_behavior],
3184
+ fromZigbee: [fz.on_off, tuya.fz.power_on_behavior_2, fz.ignore_basic_report],
3185
+ toZigbee: [tz.on_off, tuya.tz.power_on_behavior_2],
3213
3186
  exposes: [e.switch().withEndpoint('l1'), e.power_on_behavior().withEndpoint('l1'), e.switch().withEndpoint('l2'),
3214
3187
  e.power_on_behavior().withEndpoint('l2'), e.switch().withEndpoint('l3'), e.power_on_behavior().withEndpoint('l3'),
3215
3188
  e.switch().withEndpoint('l4'), e.power_on_behavior().withEndpoint('l4')],
@@ -3414,7 +3387,7 @@ module.exports = [
3414
3387
  vendor: 'TuYa',
3415
3388
  fromZigbee: [fz.on_off, fz.electrical_measurement, fz.metering, fz.ignore_basic_report, tuya.fz.power_outage_memory,
3416
3389
  fz.tuya_relay_din_led_indicator],
3417
- toZigbee: [tz.on_off, tuya.tz.power_on_behavior, tz.tuya_relay_din_led_indicator],
3390
+ toZigbee: [tz.on_off, tuya.tz.power_on_behavior_1, tz.tuya_relay_din_led_indicator],
3418
3391
  whiteLabel: [{vendor: 'MatSee Plus', model: 'ATMS1602Z'}, {vendor: 'Tongou', model: 'TO-Q-SY1-JZT'}],
3419
3392
  ota: ota.zigbeeOTA,
3420
3393
  configure: async (device, coordinatorEndpoint, logger) => {
@@ -3440,7 +3413,7 @@ module.exports = [
3440
3413
  description: 'Din smart relay (without power monitoring)',
3441
3414
  vendor: 'TuYa',
3442
3415
  fromZigbee: [fz.on_off, fz.ignore_basic_report, tuya.fz.power_outage_memory, fz.tuya_relay_din_led_indicator],
3443
- toZigbee: [tz.on_off, tuya.tz.power_on_behavior, tz.tuya_relay_din_led_indicator],
3416
+ toZigbee: [tz.on_off, tuya.tz.power_on_behavior_1, tz.tuya_relay_din_led_indicator],
3444
3417
  configure: async (device, coordinatorEndpoint, logger) => {
3445
3418
  const endpoint = device.getEndpoint(1);
3446
3419
  await reporting.bind(endpoint, coordinatorEndpoint, ['genOnOff']);
@@ -3989,10 +3962,10 @@ module.exports = [
3989
3962
  vendor: 'TuYa',
3990
3963
  description: '1 channel dimmer',
3991
3964
  fromZigbee: extend.light_onoff_brightness({disablePowerOnBehavior: true, disableMoveStep: true, disableTransition: true})
3992
- .fromZigbee.concat([tuya.fz.power_on_behavior, fzLocal.TS110E_switch_type, fzLocal.TS110E]),
3965
+ .fromZigbee.concat([tuya.fz.power_on_behavior_1, fzLocal.TS110E_switch_type, fzLocal.TS110E]),
3993
3966
  toZigbee: utils.replaceInArray(
3994
3967
  extend.light_onoff_brightness({disablePowerOnBehavior: true, disableMoveStep: true, disableTransition: true})
3995
- .toZigbee.concat([tuya.tz.power_on_behavior, tzLocal.TS110E_options]),
3968
+ .toZigbee.concat([tuya.tz.power_on_behavior_1, tzLocal.TS110E_options]),
3996
3969
  [tz.light_onoff_brightness],
3997
3970
  [tzLocal.TS110E_light_onoff_brightness],
3998
3971
  ),
@@ -4009,8 +3982,8 @@ module.exports = [
4009
3982
  vendor: 'TuYa',
4010
3983
  description: '1 channel dimmer',
4011
3984
  whiteLabel: [{vendor: 'RTX', model: 'QS-Zigbee-D02-TRIAC-LN'}],
4012
- fromZigbee: [fzLocal.TS110E, fzLocal.TS110E_light_type, tuya.fz.power_on_behavior, fz.on_off],
4013
- toZigbee: [tzLocal.TS110E_onoff_brightness, tzLocal.TS110E_options, tuya.tz.power_on_behavior, tz.light_brightness_move],
3985
+ fromZigbee: [fzLocal.TS110E, fzLocal.TS110E_light_type, tuya.fz.power_on_behavior_1, fz.on_off],
3986
+ toZigbee: [tzLocal.TS110E_onoff_brightness, tzLocal.TS110E_options, tuya.tz.power_on_behavior_1, tz.light_brightness_move],
4014
3987
  exposes: [
4015
3988
  e.light_brightness().withMinBrightness().withMaxBrightness(),
4016
3989
  tuya.exposes.lightType().withAccess(ea.ALL), e.power_on_behavior().withAccess(ea.ALL)],
@@ -4027,10 +4000,10 @@ module.exports = [
4027
4000
  vendor: 'TuYa',
4028
4001
  description: '2 channel dimmer',
4029
4002
  fromZigbee: extend.light_onoff_brightness({disablePowerOnBehavior: true, disableMoveStep: true, disableTransition: true})
4030
- .fromZigbee.concat([tuya.fz.power_on_behavior, fzLocal.TS110E_switch_type, fzLocal.TS110E]),
4003
+ .fromZigbee.concat([tuya.fz.power_on_behavior_1, fzLocal.TS110E_switch_type, fzLocal.TS110E]),
4031
4004
  toZigbee: utils.replaceInArray(
4032
4005
  extend.light_onoff_brightness({disablePowerOnBehavior: true, disableMoveStep: true, disableTransition: true})
4033
- .toZigbee.concat([tuya.tz.power_on_behavior, tzLocal.TS110E_options]),
4006
+ .toZigbee.concat([tuya.tz.power_on_behavior_1, tzLocal.TS110E_options]),
4034
4007
  [tz.light_onoff_brightness],
4035
4008
  [tzLocal.TS110E_light_onoff_brightness],
4036
4009
  ),
@@ -4057,8 +4030,8 @@ module.exports = [
4057
4030
  model: 'TS110E_2gang_2',
4058
4031
  vendor: 'TuYa',
4059
4032
  description: '2 channel dimmer',
4060
- fromZigbee: [fzLocal.TS110E, fzLocal.TS110E_light_type, tuya.fz.power_on_behavior, fz.on_off],
4061
- toZigbee: [tzLocal.TS110E_onoff_brightness, tzLocal.TS110E_options, tuya.tz.power_on_behavior, tz.light_brightness_move],
4033
+ fromZigbee: [fzLocal.TS110E, fzLocal.TS110E_light_type, tuya.fz.power_on_behavior_1, fz.on_off],
4034
+ toZigbee: [tzLocal.TS110E_onoff_brightness, tzLocal.TS110E_options, tuya.tz.power_on_behavior_1, tz.light_brightness_move],
4062
4035
  meta: {multiEndpoint: true},
4063
4036
  exposes: [
4064
4037
  e.light_brightness().withMinBrightness().withMaxBrightness().withEndpoint('l1'),
package/devices/xiaomi.js CHANGED
@@ -1927,10 +1927,8 @@ module.exports = [
1927
1927
  exposes: [
1928
1928
  e.switch(), e.power().withAccess(ea.STATE_GET), e.energy(), e.device_temperature().withAccess(ea.STATE),
1929
1929
  e.voltage(), e.current(), e.consumer_connected(), e.led_disabled_night(),
1930
- e.power_outage_memory(), exposes.binary('auto_off', ea.STATE_SET, true, false)
1931
- .withDescription('Turn the device automatically off when attached device consumes less than 2W for 20 minutes'),
1932
- exposes.numeric('overload_protection', exposes.access.ALL).withValueMin(100).withValueMax(2300).withUnit('W')
1933
- .withDescription('Maximum allowed load, turns off if exceeded')],
1930
+ e.power_outage_memory(), e.auto_off(20),
1931
+ e.overload_protection(100, 2300)],
1934
1932
  ota: ota.zigbeeOTA,
1935
1933
  },
1936
1934
  {
@@ -1944,10 +1942,8 @@ module.exports = [
1944
1942
  exposes: [
1945
1943
  e.switch(), e.power().withAccess(ea.STATE_GET), e.energy(), e.device_temperature().withAccess(ea.STATE),
1946
1944
  e.voltage(), e.current(), e.consumer_connected(), e.led_disabled_night(),
1947
- e.power_outage_memory(), exposes.binary('auto_off', ea.STATE_SET, true, false)
1948
- .withDescription('Turn the device automatically off when attached device consumes less than 2W for 20 minutes'),
1949
- exposes.numeric('overload_protection', exposes.access.ALL).withValueMin(100).withValueMax(2300).withUnit('W')
1950
- .withDescription('Maximum allowed load, turns off if exceeded')],
1945
+ e.power_outage_memory(), e.auto_off(20),
1946
+ e.overload_protection(100, 2300)],
1951
1947
  ota: ota.zigbeeOTA,
1952
1948
  },
1953
1949
  {
@@ -1957,7 +1953,8 @@ module.exports = [
1957
1953
  vendor: 'Xiaomi',
1958
1954
  fromZigbee: [fz.on_off, fz.xiaomi_basic, fz.electrical_measurement, fz.metering,
1959
1955
  fz.aqara_opple, fz.xiaomi_power, fz.device_temperature],
1960
- toZigbee: [tz.on_off, tz.xiaomi_switch_power_outage_memory],
1956
+ toZigbee: [tz.on_off, tz.xiaomi_switch_power_outage_memory, tz.xiaomi_led_disabled_night,
1957
+ tz.xiaomi_overload_protection, tz.xiaomi_auto_off, tz.xiaomi_socket_button_lock],
1961
1958
  configure: async (device, coordinatorEndpoint, logger) => {
1962
1959
  const endpoint = device.getEndpoint(1);
1963
1960
  await reporting.bind(endpoint, coordinatorEndpoint, ['genOnOff']);
@@ -2006,9 +2003,10 @@ module.exports = [
2006
2003
  globalStore.putValue(device, 'interval', interval);
2007
2004
  }
2008
2005
  },
2009
- exposes: [e.switch(), e.power(), e.energy(), e.power_outage_memory(),
2010
- e.voltage(), e.current(),
2011
- e.device_temperature().withDescription('Device temperature (polled every 30 min)')],
2006
+ exposes: [e.switch(), e.power(), e.energy(), e.power_outage_memory(), e.voltage(), e.current(),
2007
+ e.device_temperature().withDescription('Device temperature (polled every 30 min)'),
2008
+ e.consumer_connected(), e.led_disabled_night(), e.overload_protection(100, 2300),
2009
+ e.auto_off(20), e.button_lock()],
2012
2010
  ota: ota.zigbeeOTA,
2013
2011
  },
2014
2012
  {
@@ -2021,9 +2019,7 @@ module.exports = [
2021
2019
  tz.xiaomi_switch_power_outage_memory, tz.xiaomi_auto_off],
2022
2020
  exposes: [e.switch(), e.power().withAccess(ea.STATE_GET), e.energy(), e.device_temperature(), e.voltage(),
2023
2021
  e.power_outage_memory(), e.led_disabled_night(),
2024
- exposes.binary('auto_off', ea.STATE_SET, true, false)
2025
- .withDescription('If the power is constantly lower than 2W within half an hour, ' +
2026
- 'the plug will be automatically turned off')],
2022
+ e.auto_off(30)],
2027
2023
  onEvent: async (type, data, device) => {
2028
2024
  device.skipTimeResponse = true;
2029
2025
  // According to the Zigbee the genTime.time should be the seconds since 1 January 2020 UTC
@@ -2829,8 +2825,7 @@ module.exports = [
2829
2825
  exposes: [e.switch(), e.power().withAccess(ea.STATE), e.energy(), e.device_temperature().withAccess(ea.STATE),
2830
2826
  e.voltage(), e.current(), e.consumer_connected().withAccess(ea.STATE),
2831
2827
  e.power_outage_memory(), e.led_disabled_night(), e.button_lock(),
2832
- exposes.numeric('overload_protection', exposes.access.ALL).withValueMin(100).withValueMax(2500).withUnit('W')
2833
- .withDescription('Maximum allowed load, turns off if exceeded')],
2828
+ e.overload_protection(100, 2500)],
2834
2829
  ota: ota.zigbeeOTA,
2835
2830
  },
2836
2831
  {
@@ -2900,8 +2895,7 @@ module.exports = [
2900
2895
  e.current(), e.power_outage_memory(), e.led_disabled_night(), e.button_lock(),
2901
2896
  exposes.enum('button_switch_mode', exposes.access.ALL, ['relay', 'relay_and_usb'])
2902
2897
  .withDescription('Control both relay and usb or only the relay with the physical switch button'),
2903
- exposes.numeric('overload_protection', exposes.access.ALL).withValueMin(100).withValueMax(2500).withUnit('W')
2904
- .withDescription('Maximum allowed load, turns off if exceeded')],
2898
+ e.overload_protection(100, 2500)],
2905
2899
  ota: ota.zigbeeOTA,
2906
2900
  },
2907
2901
  {
@@ -2927,8 +2921,7 @@ module.exports = [
2927
2921
  e.switch(), e.power().withAccess(ea.STATE), e.energy(),
2928
2922
  e.device_temperature().withAccess(ea.STATE), e.voltage(),
2929
2923
  e.current(), e.power_outage_memory(), e.led_disabled_night(), e.button_lock(),
2930
- exposes.numeric('overload_protection', exposes.access.ALL).withValueMin(100).withValueMax(2500).withUnit('W')
2931
- .withDescription('Maximum allowed load, turns off if exceeded')],
2924
+ e.overload_protection(100, 2500)],
2932
2925
  ota: ota.zigbeeOTA,
2933
2926
  },
2934
2927
  {
package/lib/exposes.js CHANGED
@@ -541,6 +541,7 @@ module.exports = {
541
541
  angle_axis: (name) => new Numeric(name, access.STATE).withValueMin(-90).withValueMax(90).withUnit('°'),
542
542
  aqi: () => new Numeric('aqi', access.STATE).withDescription('Air quality index'),
543
543
  auto_lock: () => new Switch().withState('auto_lock', false, 'Enable/disable auto lock', access.STATE_SET, 'AUTO', 'MANUAL'),
544
+ auto_off: (offTime) => new Binary('auto_off', access.ALL, true, false).withDescription(`Turn the device automatically off when attached device consumes less than 2W for ${offTime} minutes`),
544
545
  auto_relock_time: () => new Numeric('auto_relock_time', access.ALL).withValueMin(0).withUnit('s').withDescription('The number of seconds to wait after unlocking a lock before it automatically locks again. 0=disabled'),
545
546
  away_mode: () => new Switch().withState('away_mode', false, 'Enable/disable away mode', access.STATE_SET),
546
547
  away_preset_days: () => new Numeric('away_preset_days', access.STATE_SET).withDescription('Away preset days').withValueMin(0).withValueMax(100),
@@ -615,6 +616,7 @@ module.exports = {
615
616
  occupancy_level: () => new Numeric('occupancy_level', access.STATE).withDescription('The measured occupancy value'),
616
617
  open_window: () => new Binary('open_window', access.STATE_SET, 'ON', 'OFF').withDescription('Enables/disables the status on the device'),
617
618
  open_window_temperature: () => new Numeric('open_window_temperature', access.STATE_SET).withUnit('°C').withDescription('Open window temperature').withValueMin(0).withValueMax(35),
619
+ overload_protection: (min, max) => new Numeric('overload_protection', access.ALL).withUnit('W').withValueMin(min).withValueMax(max).withDescription('Maximum allowed load, turns off if exceeded'),
618
620
  pm10: () => new Numeric('pm10', access.STATE).withUnit('µg/m³').withDescription('Measured PM10 (particulate matter) concentration'),
619
621
  pm25: () => new Numeric('pm25', access.STATE).withUnit('µg/m³').withDescription('Measured PM2.5 (particulate matter) concentration'),
620
622
  position: () => new Numeric('position', access.STATE).withUnit('%').withDescription('Position'),
package/lib/extend.js CHANGED
@@ -144,24 +144,36 @@ const extend = {
144
144
  };
145
145
  {
146
146
  extend.ledvance = {
147
- light_onoff_brightness: (options={}) => ({
148
- ...extend.light_onoff_brightness(options),
149
- toZigbee: extend.light_onoff_brightness(options).toZigbee.concat([tz.ledvance_commands]),
150
- }),
151
- light_onoff_brightness_colortemp: (options={}) => ({
152
- ...extend.light_onoff_brightness_colortemp({disableColorTempStartup: true, ...options}),
153
- toZigbee: extend.light_onoff_brightness_colortemp({disableColorTempStartup: true, ...options})
154
- .toZigbee.concat([tz.ledvance_commands]),
155
- }),
156
- light_onoff_brightness_color: (options={}) => ({
157
- ...extend.light_onoff_brightness_color({supportsHS: true, ...options}),
158
- toZigbee: extend.light_onoff_brightness_color({supportsHS: true, ...options}).toZigbee.concat([tz.ledvance_commands]),
159
- }),
160
- light_onoff_brightness_colortemp_color: (options={}) => ({
161
- ...extend.light_onoff_brightness_colortemp_color({supportsHS: true, disableColorTempStartup: true, ...options}),
162
- toZigbee: extend.light_onoff_brightness_colortemp_color({supportsHS: true, disableColorTempStartup: true, ...options})
163
- .toZigbee.concat([tz.ledvance_commands]),
164
- }),
147
+ light_onoff_brightness: (options={}) => {
148
+ options = {disablePowerOnBehavior: true, ...options};
149
+ return {
150
+ ...extend.light_onoff_brightness(options),
151
+ toZigbee: extend.light_onoff_brightness(options).toZigbee.concat([tz.ledvance_commands]),
152
+ };
153
+ },
154
+ light_onoff_brightness_colortemp: (options={}) => {
155
+ options = {disablePowerOnBehavior: true, ...options};
156
+ return {
157
+ ...extend.light_onoff_brightness_colortemp({disableColorTempStartup: true, ...options}),
158
+ toZigbee: extend.light_onoff_brightness_colortemp({disableColorTempStartup: true, ...options})
159
+ .toZigbee.concat([tz.ledvance_commands]),
160
+ };
161
+ },
162
+ light_onoff_brightness_color: (options={}) => {
163
+ options = {disablePowerOnBehavior: true, ...options};
164
+ return {
165
+ ...extend.light_onoff_brightness_color({supportsHS: true, ...options}),
166
+ toZigbee: extend.light_onoff_brightness_color({supportsHS: true, ...options}).toZigbee.concat([tz.ledvance_commands]),
167
+ };
168
+ },
169
+ light_onoff_brightness_colortemp_color: (options={}) => {
170
+ options = {disablePowerOnBehavior: true, ...options};
171
+ return {
172
+ ...extend.light_onoff_brightness_colortemp_color({supportsHS: true, disableColorTempStartup: true, ...options}),
173
+ toZigbee: extend.light_onoff_brightness_colortemp_color({supportsHS: true, disableColorTempStartup: true, ...options})
174
+ .toZigbee.concat([tz.ledvance_commands]),
175
+ };
176
+ },
165
177
  };
166
178
  }
167
179
 
package/lib/tuya.js CHANGED
@@ -1519,7 +1519,7 @@ const valueConverter = {
1519
1519
  };
1520
1520
 
1521
1521
  const tuyaTz = {
1522
- power_on_behavior: {
1522
+ power_on_behavior_1: {
1523
1523
  key: ['power_on_behavior', 'power_outage_memory'],
1524
1524
  convertSet: async (entity, key, value, meta) => {
1525
1525
  // Legacy: remove power_outage_memory
@@ -1534,6 +1534,20 @@ const tuyaTz = {
1534
1534
  await entity.read('genOnOff', ['moesStartUpOnOff']);
1535
1535
  },
1536
1536
  },
1537
+ power_on_behavior_2: {
1538
+ key: ['power_on_behavior'],
1539
+ convertSet: async (entity, key, value, meta) => {
1540
+ value = value.toLowerCase();
1541
+ const lookup = {'off': 0, 'on': 1, 'previous': 2};
1542
+ utils.validateValue(value, Object.keys(lookup));
1543
+ const pState = lookup[value];
1544
+ await entity.write('manuSpecificTuya_3', {'powerOnBehavior': pState});
1545
+ return {state: {power_on_behavior: value}};
1546
+ },
1547
+ convertGet: async (entity, key, meta) => {
1548
+ await entity.read('manuSpecificTuya_3', ['powerOnBehavior']);
1549
+ },
1550
+ },
1537
1551
  switch_type: {
1538
1552
  key: ['switch_type'],
1539
1553
  convertSet: async (entity, key, value, meta) => {
@@ -1675,7 +1689,7 @@ const tuyaFz = {
1675
1689
  await msg.endpoint.command('manuSpecificTuya', 'mcuGatewayConnectionStatus', payload, {});
1676
1690
  },
1677
1691
  },
1678
- power_on_behavior: {
1692
+ power_on_behavior_1: {
1679
1693
  cluster: 'genOnOff',
1680
1694
  type: ['attributeReport', 'readResponse'],
1681
1695
  convert: (model, msg, publish, options, meta) => {
@@ -1686,6 +1700,18 @@ const tuyaFz = {
1686
1700
  }
1687
1701
  },
1688
1702
  },
1703
+ power_on_behavior_2: {
1704
+ cluster: 'manuSpecificTuya_3',
1705
+ type: ['attributeReport', 'readResponse'],
1706
+ convert: (model, msg, publish, options, meta) => {
1707
+ const attribute = 'powerOnBehavior';
1708
+ const lookup = {0: 'off', 1: 'on', 2: 'previous'};
1709
+ if (msg.data.hasOwnProperty(attribute)) {
1710
+ const property = utils.postfixWithEndpointName('power_on_behavior', msg, model, meta);
1711
+ return {[property]: lookup[msg.data[attribute]]};
1712
+ }
1713
+ },
1714
+ },
1689
1715
  power_outage_memory: {
1690
1716
  cluster: 'genOnOff',
1691
1717
  type: ['attributeReport', 'readResponse'],
@@ -1830,11 +1856,11 @@ const tuyaExtend = {
1830
1856
  if (options.powerOutageMemory) {
1831
1857
  // Legacy, powerOnBehavior is preferred
1832
1858
  fromZigbee.push(tuyaFz.power_outage_memory);
1833
- toZigbee.push(tuyaTz.power_on_behavior);
1859
+ toZigbee.push(tuyaTz.power_on_behavior_1);
1834
1860
  exposes.push(tuyaExposes.powerOutageMemory());
1835
1861
  } else {
1836
- fromZigbee.push(tuyaFz.power_on_behavior);
1837
- toZigbee.push(tuyaTz.power_on_behavior);
1862
+ fromZigbee.push(tuyaFz.power_on_behavior_1);
1863
+ toZigbee.push(tuyaTz.power_on_behavior_1);
1838
1864
  exposes.push(e.power_on_behavior());
1839
1865
  }
1840
1866
 
package/lib/xiaomi.js CHANGED
@@ -552,7 +552,7 @@ const numericAttributes2Payload = async (msg, meta, model, options, dataObject)
552
552
  }
553
553
  break;
554
554
  case '512':
555
- if (['ZNCZ15LM', 'QBCZ14LM', 'QBCZ15LM'].includes(model.model)) {
555
+ if (['ZNCZ15LM', 'QBCZ14LM', 'QBCZ15LM', 'SP-EUC01'].includes(model.model)) {
556
556
  payload.button_lock = value === 1 ? 'OFF' : 'ON';
557
557
  } else {
558
558
  const mode = {0x01: 'control_relay', 0x00: 'decoupled'}[value];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zigbee-herdsman-converters",
3
- "version": "15.0.68",
3
+ "version": "15.0.70",
4
4
  "description": "Collection of device converters to be used with zigbee-herdsman",
5
5
  "main": "index.js",
6
6
  "files": [