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.
- package/converters/toZigbee.js +10 -3
- package/devices/automaton.js +44 -0
- package/devices/bosch.js +19 -3
- package/devices/casaia.js +1 -0
- package/devices/giex.js +181 -94
- package/devices/hzc.js +21 -0
- package/devices/ledvance.js +2 -2
- package/devices/lellki.js +1 -1
- package/devices/osram.js +3 -3
- package/devices/owon.js +1 -0
- package/devices/philips.js +1 -1
- package/devices/smartwings.js +2 -2
- package/devices/tuya.js +16 -43
- package/devices/xiaomi.js +14 -21
- package/lib/exposes.js +2 -0
- package/lib/extend.js +30 -18
- package/lib/tuya.js +31 -5
- package/lib/xiaomi.js +1 -1
- package/package.json +1 -1
package/converters/toZigbee.js
CHANGED
|
@@ -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
|
|
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.
|
|
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
|
-
|
|
4
|
-
const ea = exposes
|
|
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
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
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
|
-
|
|
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
|
|
70
|
+
const {dp} = dpValue;
|
|
30
71
|
switch (dp) {
|
|
31
|
-
case dataPoints.
|
|
32
|
-
return {state: value ?
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
case dataPoints.
|
|
38
|
-
return {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
case dataPoints.
|
|
44
|
-
return {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
case dataPoints.
|
|
50
|
-
return {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
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
|
-
|
|
110
|
+
giexWaterValve:
|
|
75
111
|
{
|
|
76
|
-
key: [
|
|
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
|
|
80
|
-
|
|
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
|
|
93
|
-
await tuya.
|
|
94
|
-
return {state: {
|
|
95
|
-
case
|
|
96
|
-
|
|
97
|
-
|
|
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(`
|
|
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
|
-
|
|
119
|
-
exposes.
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
.
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
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
|
+
];
|
package/devices/ledvance.js
CHANGED
|
@@ -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]
|
|
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]
|
|
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.
|
|
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]
|
|
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(
|
|
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
|
|
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'),
|
package/devices/philips.js
CHANGED
|
@@ -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',
|
package/devices/smartwings.js
CHANGED
|
@@ -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
|
-
|
|
20
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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,
|
|
3212
|
-
toZigbee: [tz.on_off,
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
4013
|
-
toZigbee: [tzLocal.TS110E_onoff_brightness, tzLocal.TS110E_options, tuya.tz.
|
|
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.
|
|
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.
|
|
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.
|
|
4061
|
-
toZigbee: [tzLocal.TS110E_onoff_brightness, tzLocal.TS110E_options, tuya.tz.
|
|
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(),
|
|
1931
|
-
|
|
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(),
|
|
1948
|
-
|
|
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.
|
|
2011
|
-
e.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
...
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
1859
|
+
toZigbee.push(tuyaTz.power_on_behavior_1);
|
|
1834
1860
|
exposes.push(tuyaExposes.powerOutageMemory());
|
|
1835
1861
|
} else {
|
|
1836
|
-
fromZigbee.push(tuyaFz.
|
|
1837
|
-
toZigbee.push(tuyaTz.
|
|
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];
|