zigbee-herdsman-converters 15.0.44 → 15.0.46
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/fromZigbee.js +0 -117
- package/converters/toZigbee.js +12 -74
- package/devices/bitron.js +39 -8
- package/devices/candeo.js +1 -1
- package/devices/centralite.js +1 -1
- package/devices/custom_devices_diy.js +2 -2
- package/devices/danalock.js +2 -0
- package/devices/frankever.js +1 -1
- package/devices/ikea.js +89 -57
- package/devices/legrand.js +16 -0
- package/devices/mercator.js +4 -0
- package/devices/moes.js +2 -2
- package/devices/neo.js +2 -2
- package/devices/philips.js +27 -31
- package/devices/salus_controls.js +18 -0
- package/devices/sengled.js +5 -3
- package/devices/tuya.js +117 -30
- package/lib/exposes.js +8 -0
- package/lib/philips.js +3 -3
- package/lib/tuya.js +12 -24
- package/package.json +2 -2
package/converters/fromZigbee.js
CHANGED
|
@@ -4150,123 +4150,6 @@ const converters = {
|
|
|
4150
4150
|
}
|
|
4151
4151
|
},
|
|
4152
4152
|
},
|
|
4153
|
-
haozee_thermostat: {
|
|
4154
|
-
cluster: 'manuSpecificTuya',
|
|
4155
|
-
type: ['commandDataResponse', 'commandDataReport'],
|
|
4156
|
-
convert: (model, msg, publish, options, meta) => {
|
|
4157
|
-
const dpValue = tuya.firstDpValue(msg, meta, 'haozee_thermostat');
|
|
4158
|
-
const dp = dpValue.dp; // First we get the data point ID
|
|
4159
|
-
const value = tuya.getDataValue(dpValue);
|
|
4160
|
-
const presetLookup = {0: 'auto', 1: 'manual', 2: 'off', 3: 'on'};
|
|
4161
|
-
const systemModeLookup = {0: 'auto', 1: 'auto', 2: 'off', 3: 'heat'};
|
|
4162
|
-
switch (dp) {
|
|
4163
|
-
case tuya.dataPoints.haozeeSystemMode:
|
|
4164
|
-
return {preset: presetLookup[value], system_mode: systemModeLookup[value]};
|
|
4165
|
-
case tuya.dataPoints.haozeeHeatingSetpoint:
|
|
4166
|
-
return {current_heating_setpoint: (value / 10).toFixed(1)};
|
|
4167
|
-
case tuya.dataPoints.haozeeLocalTemp:
|
|
4168
|
-
return {local_temperature: (value / 10).toFixed(1)};
|
|
4169
|
-
case tuya.dataPoints.haozeeBoostHeatingCountdown:
|
|
4170
|
-
// quick heating countdown - not supported by this device
|
|
4171
|
-
return {boost_heating_countdown: value};
|
|
4172
|
-
case tuya.dataPoints.haozeeWindowDetection:
|
|
4173
|
-
// window check
|
|
4174
|
-
return {window_detection: value ? 'ON' : 'OFF'};
|
|
4175
|
-
case tuya.dataPoints.haozeeWindowState:
|
|
4176
|
-
// window state
|
|
4177
|
-
return {window: value ? 'OPEN' : 'CLOSED'};
|
|
4178
|
-
case tuya.dataPoints.haozeeChildLock:
|
|
4179
|
-
return {child_lock: value ? 'LOCK' : 'UNLOCK'};
|
|
4180
|
-
case tuya.dataPoints.haozeeBattery:
|
|
4181
|
-
// battery
|
|
4182
|
-
return {battery: value};
|
|
4183
|
-
case tuya.dataPoints.haozeeFaultAlarm:
|
|
4184
|
-
return {error: value ? 'ON': 'OFF'};
|
|
4185
|
-
case tuya.dataPoints.haozeeScheduleMonday:
|
|
4186
|
-
// Monday
|
|
4187
|
-
return {
|
|
4188
|
-
'monday_schedule': ' ' + value[1] + 'h:' + value[2] + 'm ' + value[4] / 10 + '°C' +
|
|
4189
|
-
', ' + value[5] + 'h:' + value[6] + 'm ' + value[8] / 10 + '°C' +
|
|
4190
|
-
', ' + value[9] + 'h:' + value[10] + 'm ' + value[12] / 10 + '°C' +
|
|
4191
|
-
', ' + value[13] + 'h:' + value[14] + 'm ' + value[16] / 10 + '°C ',
|
|
4192
|
-
};
|
|
4193
|
-
case tuya.dataPoints.haozeeScheduleTuesday:
|
|
4194
|
-
// Tuesday
|
|
4195
|
-
return {
|
|
4196
|
-
'tuesday_schedule': ' ' + value[1] + 'h:' + value[2] + 'm ' + value[4] / 10 + '°C' +
|
|
4197
|
-
', ' + value[5] + 'h:' + value[6] + 'm ' + value[8] / 10 + '°C' +
|
|
4198
|
-
', ' + value[9] + 'h:' + value[10] + 'm ' + value[12] / 10 + '°C' +
|
|
4199
|
-
', ' + value[13] + 'h:' + value[14] + 'm ' + value[16] / 10 + '°C ',
|
|
4200
|
-
};
|
|
4201
|
-
case tuya.dataPoints.haozeeScheduleWednesday:
|
|
4202
|
-
// wednesday
|
|
4203
|
-
return {
|
|
4204
|
-
'wednesday_schedule': ' ' + value[1] + 'h:' + value[2] + 'm ' + value[4] / 10 + '°C' +
|
|
4205
|
-
', ' + value[5] + 'h:' + value[6] + 'm ' + value[8] / 10 + '°C' +
|
|
4206
|
-
', ' + value[9] + 'h:' + value[10] + 'm ' + value[12] / 10 + '°C' +
|
|
4207
|
-
', ' + value[13] + 'h:' + value[14] + 'm ' + value[16] / 10 + '°C ',
|
|
4208
|
-
};
|
|
4209
|
-
case tuya.dataPoints.haozeeScheduleThursday:
|
|
4210
|
-
// Thursday
|
|
4211
|
-
return {
|
|
4212
|
-
'thursday_schedule': ' ' + value[1] + 'h:' + value[2] + 'm ' + value[4] / 10 + '°C' +
|
|
4213
|
-
', ' + value[5] + 'h:' + value[6] + 'm ' + value[8] / 10 + '°C' +
|
|
4214
|
-
', ' + value[9] + 'h:' + value[10] + 'm ' + value[12] / 10 + '°C' +
|
|
4215
|
-
', ' + value[13] + 'h:' + value[14] + 'm ' + value[16] / 10 + '°C ',
|
|
4216
|
-
};
|
|
4217
|
-
case tuya.dataPoints.haozeeScheduleFriday:
|
|
4218
|
-
// Friday
|
|
4219
|
-
return {
|
|
4220
|
-
'friday_schedule': ' ' + value[1] + 'h:' + value[2] + 'm ' + value[4] / 10 + '°C' +
|
|
4221
|
-
', ' + value[5] + 'h:' + value[6] + 'm ' + value[8] / 10 + '°C' +
|
|
4222
|
-
', ' + value[9] + 'h:' + value[10] + 'm ' + value[12] / 10 + '°C' +
|
|
4223
|
-
', ' + value[13] + 'h:' + value[14] + 'm ' + value[16] / 10 + '°C ',
|
|
4224
|
-
};
|
|
4225
|
-
case tuya.dataPoints.haozeeScheduleSaturday:
|
|
4226
|
-
// Saturday
|
|
4227
|
-
return {
|
|
4228
|
-
'saturday_schedule': ' ' + value[1] + 'h:' + value[2] + 'm ' + value[4] / 10 + '°C' +
|
|
4229
|
-
', ' + value[5] + 'h:' + value[6] + 'm ' + value[8] / 10 + '°C' +
|
|
4230
|
-
', ' + value[9] + 'h:' + value[10] + 'm ' + value[12] / 10 + '°C' +
|
|
4231
|
-
', ' + value[13] + 'h:' + value[14] + 'm ' + value[16] / 10 + '°C ',
|
|
4232
|
-
};
|
|
4233
|
-
case tuya.dataPoints.haozeeScheduleSunday:
|
|
4234
|
-
|
|
4235
|
-
// Sunday
|
|
4236
|
-
return {
|
|
4237
|
-
'sunday_schedule': ' ' + value[1] + 'h:' + value[2] + 'm ' + value[4] / 10 + '°C' +
|
|
4238
|
-
', ' + value[5] + 'h:' + value[6] + 'm ' + value[8] / 10 + '°C' +
|
|
4239
|
-
', ' + value[9] + 'h:' + value[10] + 'm ' + value[12] / 10 + '°C' +
|
|
4240
|
-
', ' + value[13] + 'h:' + value[14] + 'm ' + value[16] / 10 + '°C ',
|
|
4241
|
-
};
|
|
4242
|
-
|
|
4243
|
-
case tuya.dataPoints.haozeeRunningState:
|
|
4244
|
-
// working status 0 - pause 1 -working
|
|
4245
|
-
return {'heating': value ? 'ON' : 'OFF'};
|
|
4246
|
-
case tuya.dataPoints.haozeeBoostHeating:
|
|
4247
|
-
// rapid heating -> boolean - not supported by this device
|
|
4248
|
-
return {'boost_heating': value ? 'ON' : 'OFF'};
|
|
4249
|
-
case tuya.dataPoints.haozeeTempCalibration:
|
|
4250
|
-
// temperature calibration
|
|
4251
|
-
return {'local_temperature_calibration': ( value/10 ).toFixed(1)};
|
|
4252
|
-
case tuya.dataPoints.haozeeValvePosition:
|
|
4253
|
-
// valve position
|
|
4254
|
-
return {'position': ( value/10 ).toFixed(1)};
|
|
4255
|
-
case tuya.dataPoints.haozeeMinTemp:
|
|
4256
|
-
// lower limit temperature
|
|
4257
|
-
return {'min_temperature': ( value/10 ).toFixed(1)};
|
|
4258
|
-
case tuya.dataPoints.haozeeMaxTemp:
|
|
4259
|
-
// max limit temperature
|
|
4260
|
-
return {'max_temperature': ( value/10 ).toFixed(1)};
|
|
4261
|
-
case tuya.dataPoints.haozeeSoftVersion:
|
|
4262
|
-
// software
|
|
4263
|
-
break;
|
|
4264
|
-
default:
|
|
4265
|
-
meta.logger.warn(`zigbee-herdsman-converters:haozee: NOT RECOGNIZED DP #${
|
|
4266
|
-
dp} with data ${JSON.stringify(dpValue)}`);
|
|
4267
|
-
}
|
|
4268
|
-
},
|
|
4269
|
-
},
|
|
4270
4153
|
tuya_air_quality: {
|
|
4271
4154
|
cluster: 'manuSpecificTuya',
|
|
4272
4155
|
type: ['commandDataReport', 'commandDataResponse'],
|
package/converters/toZigbee.js
CHANGED
|
@@ -102,6 +102,12 @@ const converters = {
|
|
|
102
102
|
await entity.read('genPowerCfg', ['batteryPercentageRemaining']);
|
|
103
103
|
},
|
|
104
104
|
},
|
|
105
|
+
battery_voltage: {
|
|
106
|
+
key: ['battery', 'voltage'],
|
|
107
|
+
convertGet: async (entity, key, meta) => {
|
|
108
|
+
await entity.read('genPowerCfg', ['batteryVoltage']);
|
|
109
|
+
},
|
|
110
|
+
},
|
|
105
111
|
power_on_behavior: {
|
|
106
112
|
key: ['power_on_behavior'],
|
|
107
113
|
convertSet: async (entity, key, value, meta) => {
|
|
@@ -1260,7 +1266,12 @@ const converters = {
|
|
|
1260
1266
|
if (val === undefined) {
|
|
1261
1267
|
val = utils.getKey(constants.thermostatControlSequenceOfOperations, value, value, Number);
|
|
1262
1268
|
}
|
|
1263
|
-
|
|
1269
|
+
const attributes = {ctrlSeqeOfOper: val};
|
|
1270
|
+
await entity.write('hvacThermostat', attributes);
|
|
1271
|
+
// NOTE: update the cluster attribute we store as this is used by
|
|
1272
|
+
// SMaBiT AV2010/32's dynamic expose function.
|
|
1273
|
+
entity.saveClusterAttributeKeyValue('hvacThermostat', attributes);
|
|
1274
|
+
return {readAfterWriteTime: 250, state: {control_sequence_of_operation: value}};
|
|
1264
1275
|
},
|
|
1265
1276
|
convertGet: async (entity, key, meta) => {
|
|
1266
1277
|
await entity.read('hvacThermostat', ['ctrlSeqeOfOper']);
|
|
@@ -3474,79 +3485,6 @@ const converters = {
|
|
|
3474
3485
|
return tuya.sendDataPointRaw(entity, tuya.dataPoints.moesSschedule, payload);
|
|
3475
3486
|
},
|
|
3476
3487
|
},
|
|
3477
|
-
haozee_thermostat_preset: {
|
|
3478
|
-
key: ['preset'],
|
|
3479
|
-
convertSet: async (entity, key, value, meta) => {
|
|
3480
|
-
const lookup = {'auto': 0, 'manual': 1, 'off': 2, 'on': 3};
|
|
3481
|
-
await tuya.sendDataPointEnum(entity, tuya.dataPoints.haozeeSystemMode, lookup[value]);
|
|
3482
|
-
},
|
|
3483
|
-
},
|
|
3484
|
-
haozee_thermostat_system_mode: {
|
|
3485
|
-
key: ['system_mode'],
|
|
3486
|
-
convertSet: async (entity, key, value, meta) => {
|
|
3487
|
-
// mapping 'heat' system mode to 100% heating (same as preset 'ON'),
|
|
3488
|
-
// mapping 'auto' system mode to heating up to the set point temperature (same as preset 'MANUAL')
|
|
3489
|
-
// mapping 'off' system mode to idle (same as preset 'OFF')
|
|
3490
|
-
// programmed schedule can be enabled by using preset mode 'AUTO' instead of 'MANUAL'
|
|
3491
|
-
const lookup = {'auto': 1, 'off': 2, 'heat': 3};
|
|
3492
|
-
await tuya.sendDataPointEnum(entity, tuya.dataPoints.haozeeSystemMode, lookup[value]);
|
|
3493
|
-
},
|
|
3494
|
-
},
|
|
3495
|
-
haozee_thermostat_current_heating_setpoint: {
|
|
3496
|
-
key: ['current_heating_setpoint'],
|
|
3497
|
-
convertSet: async (entity, key, value, meta) => {
|
|
3498
|
-
const temp = Math.round(value*10);
|
|
3499
|
-
await tuya.sendDataPointValue(entity, tuya.dataPoints.haozeeHeatingSetpoint, temp);
|
|
3500
|
-
},
|
|
3501
|
-
},
|
|
3502
|
-
haozee_thermostat_boost_heating: {
|
|
3503
|
-
key: ['boost_heating'],
|
|
3504
|
-
convertSet: async (entity, key, value, meta) => {
|
|
3505
|
-
await tuya.sendDataPointBool(entity, tuya.dataPoints.haozeeBoostHeating, value === 'ON');
|
|
3506
|
-
},
|
|
3507
|
-
},
|
|
3508
|
-
haozee_thermostat_boost_heating_countdown: {
|
|
3509
|
-
key: ['boost_heating_countdown'],
|
|
3510
|
-
convertSet: async (entity, key, value, meta) => {
|
|
3511
|
-
await tuya.sendDataPointValue(entity, tuya.dataPoints.haozeeBoostHeatingCountdown, value);
|
|
3512
|
-
},
|
|
3513
|
-
},
|
|
3514
|
-
haozee_thermostat_window_detection: {
|
|
3515
|
-
key: ['window_detection'],
|
|
3516
|
-
convertSet: async (entity, key, value, meta) => {
|
|
3517
|
-
await tuya.sendDataPointBool(entity, tuya.dataPoints.haozeeWindowDetection, value === 'ON');
|
|
3518
|
-
},
|
|
3519
|
-
},
|
|
3520
|
-
haozee_thermostat_child_lock: {
|
|
3521
|
-
key: ['child_lock'],
|
|
3522
|
-
convertSet: async (entity, key, value, meta) => {
|
|
3523
|
-
await tuya.sendDataPointBool(entity, tuya.dataPoints.haozeeChildLock, value === 'LOCK');
|
|
3524
|
-
},
|
|
3525
|
-
},
|
|
3526
|
-
haozee_thermostat_temperature_calibration: {
|
|
3527
|
-
key: ['local_temperature_calibration'],
|
|
3528
|
-
convertSet: async (entity, key, value, meta) => {
|
|
3529
|
-
let temp = Math.round(value * 10);
|
|
3530
|
-
if (temp < 0) {
|
|
3531
|
-
temp = 0xFFFFFFFF + temp + 1;
|
|
3532
|
-
}
|
|
3533
|
-
await tuya.sendDataPointValue(entity, tuya.dataPoints.haozeeTempCalibration, temp);
|
|
3534
|
-
},
|
|
3535
|
-
},
|
|
3536
|
-
haozee_thermostat_max_temperature: {
|
|
3537
|
-
key: ['max_temperature'],
|
|
3538
|
-
convertSet: async (entity, key, value, meta) => {
|
|
3539
|
-
const temp = Math.round(value*10);
|
|
3540
|
-
await tuya.sendDataPointValue(entity, tuya.dataPoints.haozeeMaxTemp, temp);
|
|
3541
|
-
},
|
|
3542
|
-
},
|
|
3543
|
-
haozee_thermostat_min_temperature: {
|
|
3544
|
-
key: ['min_temperature'],
|
|
3545
|
-
convertSet: async (entity, key, value, meta) => {
|
|
3546
|
-
const temp = Math.round(value*10);
|
|
3547
|
-
await tuya.sendDataPointValue(entity, tuya.dataPoints.haozeeMinTemp, temp);
|
|
3548
|
-
},
|
|
3549
|
-
},
|
|
3550
3488
|
hgkg_thermostat_standby: {
|
|
3551
3489
|
key: ['system_mode'],
|
|
3552
3490
|
convertSet: async (entity, key, value, meta) => {
|
package/devices/bitron.js
CHANGED
|
@@ -4,6 +4,7 @@ const tz = require('../converters/toZigbee');
|
|
|
4
4
|
const reporting = require('../lib/reporting');
|
|
5
5
|
const extend = require('../lib/extend');
|
|
6
6
|
const e = exposes.presets;
|
|
7
|
+
const ea = exposes.access;
|
|
7
8
|
|
|
8
9
|
module.exports = [
|
|
9
10
|
{
|
|
@@ -190,13 +191,43 @@ module.exports = [
|
|
|
190
191
|
vendor: 'SMaBiT (Bitron Video)',
|
|
191
192
|
description: 'Wireless wall thermostat with relay',
|
|
192
193
|
fromZigbee: [fz.legacy.thermostat_att_report, fz.battery, fz.hvac_user_interface],
|
|
193
|
-
toZigbee: [
|
|
194
|
-
tz.
|
|
195
|
-
tz.
|
|
196
|
-
|
|
197
|
-
.
|
|
198
|
-
|
|
199
|
-
|
|
194
|
+
toZigbee: [
|
|
195
|
+
tz.thermostat_control_sequence_of_operation, tz.thermostat_occupied_heating_setpoint,
|
|
196
|
+
tz.thermostat_occupied_cooling_setpoint, tz.thermostat_local_temperature_calibration,
|
|
197
|
+
tz.thermostat_local_temperature, tz.thermostat_running_state, tz.thermostat_temperature_display_mode,
|
|
198
|
+
tz.thermostat_keypad_lockout, tz.thermostat_system_mode, tz.battery_voltage,
|
|
199
|
+
],
|
|
200
|
+
exposes: (device, options) => {
|
|
201
|
+
const dynExposes = [];
|
|
202
|
+
let ctrlSeqeOfOper = device ? device.getEndpoint(1).getClusterAttributeValue('hvacThermostat', 'ctrlSeqeOfOper') : null;
|
|
203
|
+
const modes = [];
|
|
204
|
+
|
|
205
|
+
// NOTE: ctrlSeqeOfOper defaults to 2 for this device (according to the manual)
|
|
206
|
+
if (ctrlSeqeOfOper == null) ctrlSeqeOfOper = 2;
|
|
207
|
+
|
|
208
|
+
// NOTE: set cool and/or heat support based on ctrlSeqeOfOper (see lib/constants -> thermostatControlSequenceOfOperations)
|
|
209
|
+
// WARN: a restart of zigbee2mqtt is required after changing ctrlSeqeOfOper for expose data to be re-calculated
|
|
210
|
+
if (ctrlSeqeOfOper >= 2) {
|
|
211
|
+
modes.push('heat');
|
|
212
|
+
}
|
|
213
|
+
if (ctrlSeqeOfOper < 2 || ctrlSeqeOfOper > 3) {
|
|
214
|
+
modes.push('cool');
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
dynExposes.push(exposes.climate()
|
|
218
|
+
.withSetpoint('occupied_heating_setpoint', 7, 30, 0.5)
|
|
219
|
+
.withLocalTemperature()
|
|
220
|
+
.withSystemMode(['off'].concat(modes))
|
|
221
|
+
.withRunningState(['idle'].concat(modes))
|
|
222
|
+
.withLocalTemperatureCalibration()
|
|
223
|
+
.withControlSequenceOfOperation(['heating_only', 'cooling_only'], ea.ALL));
|
|
224
|
+
dynExposes.push(e.keypad_lockout());
|
|
225
|
+
dynExposes.push(e.battery().withAccess(ea.STATE_GET));
|
|
226
|
+
dynExposes.push(e.battery_low());
|
|
227
|
+
dynExposes.push(e.linkquality());
|
|
228
|
+
return dynExposes;
|
|
229
|
+
},
|
|
230
|
+
meta: {battery: {voltageToPercentage: '3V_2500'}},
|
|
200
231
|
configure: async (device, coordinatorEndpoint, logger) => {
|
|
201
232
|
const endpoint = device.getEndpoint(1);
|
|
202
233
|
const binds = [
|
|
@@ -204,12 +235,12 @@ module.exports = [
|
|
|
204
235
|
];
|
|
205
236
|
await reporting.bind(endpoint, coordinatorEndpoint, binds);
|
|
206
237
|
await reporting.thermostatTemperature(endpoint);
|
|
207
|
-
await reporting.thermostatTemperatureCalibration(endpoint);
|
|
208
238
|
await reporting.thermostatOccupiedHeatingSetpoint(endpoint);
|
|
209
239
|
await reporting.thermostatOccupiedCoolingSetpoint(endpoint);
|
|
210
240
|
await reporting.thermostatRunningState(endpoint);
|
|
211
241
|
await reporting.batteryAlarmState(endpoint);
|
|
212
242
|
await reporting.batteryVoltage(endpoint);
|
|
243
|
+
await endpoint.read('hvacThermostat', ['ctrlSeqeOfOper', 'localTemperatureCalibration']);
|
|
213
244
|
},
|
|
214
245
|
},
|
|
215
246
|
{
|
package/devices/candeo.js
CHANGED
package/devices/centralite.js
CHANGED
|
@@ -129,7 +129,7 @@ module.exports = [
|
|
|
129
129
|
// For some this fails so set manually
|
|
130
130
|
// https://github.com/Koenkk/zigbee2mqtt/issues/3575
|
|
131
131
|
endpoint.saveClusterAttributeKeyValue('haElectricalMeasurement', {
|
|
132
|
-
acCurrentDivisor: 1000, acCurrentMultiplier: 1,
|
|
132
|
+
acCurrentDivisor: 1000, acCurrentMultiplier: 1, acPowerMultiplier: 1, acPowerDivisor: 10});
|
|
133
133
|
}
|
|
134
134
|
await reporting.rmsVoltage(endpoint, {change: 2}); // Voltage reports in V
|
|
135
135
|
await reporting.rmsCurrent(endpoint, {change: 10}); // Current reports in mA
|
|
@@ -473,7 +473,7 @@ module.exports = [
|
|
|
473
473
|
await firstEndpoint.configureReporting('genPowerCfg', payload1);
|
|
474
474
|
},
|
|
475
475
|
exposes: [e.soil_moisture(), e.battery(), e.temperature(),
|
|
476
|
-
exposes.numeric('report_delay', ea.STATE_SET).withUnit('
|
|
476
|
+
exposes.numeric('report_delay', ea.STATE_SET).withUnit('min').withValueMin(1).withValueMax(240)
|
|
477
477
|
.withDescription('Adjust Report Delay. Setting the time in minutes, by default 15 minutes')],
|
|
478
478
|
},
|
|
479
479
|
{
|
|
@@ -823,7 +823,7 @@ module.exports = [
|
|
|
823
823
|
await reporting.batteryPercentageRemaining(endpoint, overides);
|
|
824
824
|
},
|
|
825
825
|
exposes: [e.battery(), e.temperature(), e.humidity(),
|
|
826
|
-
exposes.numeric('report_delay', ea.STATE_SET).withUnit('
|
|
826
|
+
exposes.numeric('report_delay', ea.STATE_SET).withUnit('min')
|
|
827
827
|
.withDescription('Adjust Report Delay. Setting the time in minutes, by default 5 minutes')
|
|
828
828
|
.withValueMin(1).withValueMax(60),
|
|
829
829
|
exposes.binary('enable_temperature', ea.STATE_SET, 'ON', 'OFF').withDescription('Enable Temperature Control'),
|
package/devices/danalock.js
CHANGED
|
@@ -2,6 +2,7 @@ const exposes = require('../lib/exposes');
|
|
|
2
2
|
const fz = {...require('../converters/fromZigbee'), legacy: require('../lib/legacy').fromZigbee};
|
|
3
3
|
const tz = require('../converters/toZigbee');
|
|
4
4
|
const reporting = require('../lib/reporting');
|
|
5
|
+
const ota = require('../lib/ota');
|
|
5
6
|
const e = exposes.presets;
|
|
6
7
|
|
|
7
8
|
module.exports = [
|
|
@@ -13,6 +14,7 @@ module.exports = [
|
|
|
13
14
|
fromZigbee: [fz.lock, fz.lock_operation_event, fz.battery, fz.lock_programming_event, fz.lock_pin_code_response],
|
|
14
15
|
toZigbee: [tz.lock, tz.pincode_lock, tz.lock_userstatus],
|
|
15
16
|
meta: {pinCodeCount: 20},
|
|
17
|
+
ota: ota.zigbeeOTA,
|
|
16
18
|
configure: async (device, coordinatorEndpoint, logger) => {
|
|
17
19
|
const endpoint = device.getEndpoint(1);
|
|
18
20
|
await reporting.bind(endpoint, coordinatorEndpoint, ['closuresDoorLock', 'genPowerCfg']);
|
package/devices/frankever.js
CHANGED
|
@@ -17,7 +17,7 @@ module.exports = [
|
|
|
17
17
|
exposes: [e.switch().setAccess('state', ea.STATE_SET),
|
|
18
18
|
exposes.numeric('threshold', exposes.access.STATE_SET).withValueMin(0).withValueMax(100).withUnit('%')
|
|
19
19
|
.withDescription('Valve open percentage (multiple of 10)'),
|
|
20
|
-
exposes.numeric('timer', exposes.access.STATE_SET).withValueMin(0).withValueMax(600).withUnit('
|
|
20
|
+
exposes.numeric('timer', exposes.access.STATE_SET).withValueMin(0).withValueMax(600).withUnit('min')
|
|
21
21
|
.withDescription('Countdown timer in minutes')],
|
|
22
22
|
},
|
|
23
23
|
];
|
package/devices/ikea.js
CHANGED
|
@@ -12,7 +12,7 @@ const e = exposes.presets;
|
|
|
12
12
|
const ea = exposes.access;
|
|
13
13
|
const herdsman = require('zigbee-herdsman');
|
|
14
14
|
const {
|
|
15
|
-
calibrateAndPrecisionRoundOptions, postfixWithEndpointName, getMetaValue,
|
|
15
|
+
calibrateAndPrecisionRoundOptions, postfixWithEndpointName, getMetaValue, precisionRound,
|
|
16
16
|
} = require('../lib/utils');
|
|
17
17
|
|
|
18
18
|
const bulbOnEvent = async (type, data, device, options, state) => {
|
|
@@ -72,39 +72,6 @@ const configureRemote = async (device, coordinatorEndpoint, logger) => {
|
|
|
72
72
|
await reporting.batteryPercentageRemaining(endpoint);
|
|
73
73
|
};
|
|
74
74
|
|
|
75
|
-
const fzLocal = {
|
|
76
|
-
// The STYRBAR sends an on +- 500ms after the arrow release. We don't want to send the ON action in this case.
|
|
77
|
-
// https://github.com/Koenkk/zigbee2mqtt/issues/13335
|
|
78
|
-
STYRBAR_on: {
|
|
79
|
-
cluster: 'genOnOff',
|
|
80
|
-
type: 'commandOn',
|
|
81
|
-
convert: (model, msg, publish, options, meta) => {
|
|
82
|
-
if (utils.hasAlreadyProcessedMessage(msg, model)) return;
|
|
83
|
-
const arrowReleaseAgo = Date.now() - globalStore.getValue(msg.endpoint, 'arrow_release', 0);
|
|
84
|
-
if (arrowReleaseAgo > 700) {
|
|
85
|
-
return {action: 'on'};
|
|
86
|
-
}
|
|
87
|
-
},
|
|
88
|
-
},
|
|
89
|
-
STYRBAR_arrow_release: {
|
|
90
|
-
cluster: 'genScenes',
|
|
91
|
-
type: 'commandTradfriArrowRelease',
|
|
92
|
-
options: [exposes.options.legacy()],
|
|
93
|
-
convert: (model, msg, publish, options, meta) => {
|
|
94
|
-
if (utils.hasAlreadyProcessedMessage(msg, model)) return;
|
|
95
|
-
globalStore.putValue(msg.endpoint, 'arrow_release', Date.now());
|
|
96
|
-
const direction = globalStore.getValue(msg.endpoint, 'direction');
|
|
97
|
-
if (direction) {
|
|
98
|
-
globalStore.clearValue(msg.endpoint, 'direction');
|
|
99
|
-
const duration = msg.data.value / 1000;
|
|
100
|
-
const result = {action: `arrow_${direction}_release`, duration, action_duration: duration};
|
|
101
|
-
if (!utils.isLegacyEnabled(options)) delete result.duration;
|
|
102
|
-
return result;
|
|
103
|
-
}
|
|
104
|
-
},
|
|
105
|
-
},
|
|
106
|
-
};
|
|
107
|
-
|
|
108
75
|
const tradfriExtend = {
|
|
109
76
|
light_onoff_brightness: (options = {}) => ({
|
|
110
77
|
...extend.light_onoff_brightness(options),
|
|
@@ -214,6 +181,61 @@ const ikea = {
|
|
|
214
181
|
return state;
|
|
215
182
|
},
|
|
216
183
|
},
|
|
184
|
+
remote_battery: {
|
|
185
|
+
cluster: 'genPowerCfg',
|
|
186
|
+
type: ['attributeReport', 'readResponse'],
|
|
187
|
+
convert: (model, msg, publish, options, meta) => {
|
|
188
|
+
const payload = {};
|
|
189
|
+
if (msg.data.hasOwnProperty('batteryPercentageRemaining')) {
|
|
190
|
+
// Some devices do not comply to the ZCL and report a
|
|
191
|
+
// batteryPercentageRemaining of 100 when the battery is full (should be 200).
|
|
192
|
+
//
|
|
193
|
+
// IKEA corrected this on newer remote fw version, but many people are still
|
|
194
|
+
// 2.2.010 which is the last version supporting group bindings. We try to be
|
|
195
|
+
// smart and pick the correct one for IKEA remotes.
|
|
196
|
+
let dontDividePercentage = false;
|
|
197
|
+
let percentage = msg.data['batteryPercentageRemaining'];
|
|
198
|
+
const fwVer = meta.device.softwareBuildID.split('.');
|
|
199
|
+
if ((fwVer[0] < 2) || (fwVer[0] == 2 && fwVer[1] <= 3)) {
|
|
200
|
+
dontDividePercentage = true;
|
|
201
|
+
}
|
|
202
|
+
percentage = dontDividePercentage ? percentage : percentage / 2;
|
|
203
|
+
payload.battery = precisionRound(percentage, 2);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
return payload;
|
|
207
|
+
},
|
|
208
|
+
},
|
|
209
|
+
// The STYRBAR sends an on +- 500ms after the arrow release. We don't want to send the ON action in this case.
|
|
210
|
+
// https://github.com/Koenkk/zigbee2mqtt/issues/13335
|
|
211
|
+
styrbar_on: {
|
|
212
|
+
cluster: 'genOnOff',
|
|
213
|
+
type: 'commandOn',
|
|
214
|
+
convert: (model, msg, publish, options, meta) => {
|
|
215
|
+
if (utils.hasAlreadyProcessedMessage(msg, model)) return;
|
|
216
|
+
const arrowReleaseAgo = Date.now() - globalStore.getValue(msg.endpoint, 'arrow_release', 0);
|
|
217
|
+
if (arrowReleaseAgo > 700) {
|
|
218
|
+
return {action: 'on'};
|
|
219
|
+
}
|
|
220
|
+
},
|
|
221
|
+
},
|
|
222
|
+
styrbar_arrow_release: {
|
|
223
|
+
cluster: 'genScenes',
|
|
224
|
+
type: 'commandTradfriArrowRelease',
|
|
225
|
+
options: [exposes.options.legacy()],
|
|
226
|
+
convert: (model, msg, publish, options, meta) => {
|
|
227
|
+
if (utils.hasAlreadyProcessedMessage(msg, model)) return;
|
|
228
|
+
globalStore.putValue(msg.endpoint, 'arrow_release', Date.now());
|
|
229
|
+
const direction = globalStore.getValue(msg.endpoint, 'direction');
|
|
230
|
+
if (direction) {
|
|
231
|
+
globalStore.clearValue(msg.endpoint, 'direction');
|
|
232
|
+
const duration = msg.data.value / 1000;
|
|
233
|
+
const result = {action: `arrow_${direction}_release`, duration, action_duration: duration};
|
|
234
|
+
if (!utils.isLegacyEnabled(options)) delete result.duration;
|
|
235
|
+
return result;
|
|
236
|
+
}
|
|
237
|
+
},
|
|
238
|
+
},
|
|
217
239
|
},
|
|
218
240
|
tz: {
|
|
219
241
|
air_purifier_fan_mode: {
|
|
@@ -623,12 +645,12 @@ module.exports = [
|
|
|
623
645
|
model: 'E1524/E1810',
|
|
624
646
|
description: 'TRADFRI remote control',
|
|
625
647
|
vendor: 'IKEA',
|
|
626
|
-
fromZigbee: [fz.
|
|
648
|
+
fromZigbee: [ikea.fz.remote_battery, fz.E1524_E1810_toggle, fz.E1524_E1810_levelctrl, fz.ikea_arrow_click, fz.ikea_arrow_hold,
|
|
627
649
|
fz.ikea_arrow_release],
|
|
628
|
-
exposes: [e.battery(), e.action(['arrow_left_click', 'arrow_left_hold', 'arrow_left_release',
|
|
629
|
-
'arrow_right_hold', 'arrow_right_release', 'brightness_down_click', 'brightness_down_hold',
|
|
630
|
-
'brightness_up_click', 'brightness_up_hold', 'brightness_up_release', 'toggle'])],
|
|
631
|
-
toZigbee: [],
|
|
650
|
+
exposes: [e.battery().withAccess(ea.STATE_GET), e.action(['arrow_left_click', 'arrow_left_hold', 'arrow_left_release',
|
|
651
|
+
'arrow_right_click', 'arrow_right_hold', 'arrow_right_release', 'brightness_down_click', 'brightness_down_hold',
|
|
652
|
+
'brightness_down_release', 'brightness_up_click', 'brightness_up_hold', 'brightness_up_release', 'toggle'])],
|
|
653
|
+
toZigbee: [tz.battery_percentage_remaining],
|
|
632
654
|
ota: ota.tradfri,
|
|
633
655
|
// dontDividePercentage: true not needed with latest firmware
|
|
634
656
|
// https://github.com/Koenkk/zigbee2mqtt/issues/16412
|
|
@@ -639,12 +661,12 @@ module.exports = [
|
|
|
639
661
|
model: 'E2001/E2002',
|
|
640
662
|
vendor: 'IKEA',
|
|
641
663
|
description: 'STYRBAR remote control',
|
|
642
|
-
fromZigbee: [fz.
|
|
643
|
-
fz.ikea_arrow_hold,
|
|
644
|
-
exposes: [e.battery(), e.action(['on', 'off', 'brightness_move_up', 'brightness_move_down',
|
|
664
|
+
fromZigbee: [ikea.fz.remote_battery, ikea.fz.styrbar_on, fz.command_off, fz.command_move, fz.command_stop, fz.ikea_arrow_click,
|
|
665
|
+
fz.ikea_arrow_hold, ikea.fz.styrbar_arrow_release],
|
|
666
|
+
exposes: [e.battery().withAccess(ea.STATE_GET), e.action(['on', 'off', 'brightness_move_up', 'brightness_move_down',
|
|
645
667
|
'brightness_stop', 'arrow_left_click', 'arrow_right_click', 'arrow_left_hold',
|
|
646
668
|
'arrow_right_hold', 'arrow_left_release', 'arrow_right_release'])],
|
|
647
|
-
toZigbee: [],
|
|
669
|
+
toZigbee: [tz.battery_percentage_remaining],
|
|
648
670
|
ota: ota.tradfri,
|
|
649
671
|
meta: {battery: {dontDividePercentage: true}},
|
|
650
672
|
configure: async (device, coordinatorEndpoint, logger) => {
|
|
@@ -659,10 +681,14 @@ module.exports = [
|
|
|
659
681
|
model: 'E1743',
|
|
660
682
|
vendor: 'IKEA',
|
|
661
683
|
description: 'TRADFRI ON/OFF switch',
|
|
662
|
-
fromZigbee: [fz.command_on, fz.legacy.genOnOff_cmdOn, fz.command_off, fz.legacy.genOnOff_cmdOff, fz.command_move,
|
|
663
|
-
fz.legacy.E1743_brightness_up, fz.legacy.E1743_brightness_down, fz.command_stop,
|
|
664
|
-
|
|
665
|
-
|
|
684
|
+
fromZigbee: [fz.command_on, fz.legacy.genOnOff_cmdOn, fz.command_off, fz.legacy.genOnOff_cmdOff, fz.command_move,
|
|
685
|
+
ikea.fz.remote_battery, fz.legacy.E1743_brightness_up, fz.legacy.E1743_brightness_down, fz.command_stop,
|
|
686
|
+
fz.legacy.E1743_brightness_stop],
|
|
687
|
+
exposes: [
|
|
688
|
+
e.battery().withAccess(ea.STATE_GET),
|
|
689
|
+
e.action(['on', 'off', 'brightness_move_down', 'brightness_move_up', 'brightness_stop']),
|
|
690
|
+
],
|
|
691
|
+
toZigbee: [tz.battery_percentage_remaining],
|
|
666
692
|
ota: ota.tradfri,
|
|
667
693
|
meta: {disableActionGroup: true, battery: {dontDividePercentage: true}},
|
|
668
694
|
configure: configureRemote,
|
|
@@ -672,9 +698,9 @@ module.exports = [
|
|
|
672
698
|
model: 'E1841',
|
|
673
699
|
vendor: 'IKEA',
|
|
674
700
|
description: 'KNYCKLAN open/close remote water valve',
|
|
675
|
-
fromZigbee: [fz.command_on, fz.command_off, fz.
|
|
676
|
-
exposes: [e.battery(), e.action(['on', 'off'])],
|
|
677
|
-
toZigbee: [],
|
|
701
|
+
fromZigbee: [fz.command_on, fz.command_off, ikea.fz.remote_battery],
|
|
702
|
+
exposes: [e.battery().withAccess(ea.STATE_GET), e.action(['on', 'off'])],
|
|
703
|
+
toZigbee: [tz.battery_percentage_remaining],
|
|
678
704
|
ota: ota.tradfri,
|
|
679
705
|
meta: {disableActionGroup: true, battery: {dontDividePercentage: true}},
|
|
680
706
|
configure: configureRemote,
|
|
@@ -699,9 +725,9 @@ module.exports = [
|
|
|
699
725
|
model: 'E1812',
|
|
700
726
|
vendor: 'IKEA',
|
|
701
727
|
description: 'TRADFRI shortcut button',
|
|
702
|
-
fromZigbee: [fz.command_on, fz.command_off, fz.command_move, fz.command_stop, fz.
|
|
703
|
-
exposes: [e.battery(), e.action(['on', 'off', 'brightness_move_up', 'brightness_stop'])],
|
|
704
|
-
toZigbee: [],
|
|
728
|
+
fromZigbee: [fz.command_on, fz.command_off, fz.command_move, fz.command_stop, ikea.fz.remote_battery],
|
|
729
|
+
exposes: [e.battery().withAccess(ea.STATE_GET), e.action(['on', 'off', 'brightness_move_up', 'brightness_stop'])],
|
|
730
|
+
toZigbee: [tz.battery_percentage_remaining],
|
|
705
731
|
ota: ota.tradfri,
|
|
706
732
|
meta: {disableActionGroup: true, battery: {dontDividePercentage: true}},
|
|
707
733
|
configure: async (device, coordinatorEndpoint, logger) => {
|
|
@@ -741,7 +767,6 @@ module.exports = [
|
|
|
741
767
|
exposes.binary('illuminance_above_threshold', ea.STATE, true, false)
|
|
742
768
|
.withDescription('Indicates whether the device detected bright light (works only in night mode)')],
|
|
743
769
|
ota: ota.tradfri,
|
|
744
|
-
meta: {battery: {dontDividePercentage: true}},
|
|
745
770
|
configure: async (device, coordinatorEndpoint, logger) => {
|
|
746
771
|
const endpoint = device.getEndpoint(1);
|
|
747
772
|
await reporting.bind(endpoint, coordinatorEndpoint, ['genPowerCfg']);
|
|
@@ -839,10 +864,10 @@ module.exports = [
|
|
|
839
864
|
model: 'E1766',
|
|
840
865
|
vendor: 'IKEA',
|
|
841
866
|
description: 'TRADFRI open/close remote',
|
|
842
|
-
fromZigbee: [fz.
|
|
867
|
+
fromZigbee: [ikea.fz.remote_battery, fz.command_cover_close, fz.legacy.cover_close, fz.command_cover_open, fz.legacy.cover_open,
|
|
843
868
|
fz.command_cover_stop, fz.legacy.cover_stop],
|
|
844
|
-
exposes: [e.battery(), e.action(['close', 'open', 'stop'])],
|
|
845
|
-
toZigbee: [],
|
|
869
|
+
exposes: [e.battery().withAccess(ea.STATE_GET), e.action(['close', 'open', 'stop'])],
|
|
870
|
+
toZigbee: [tz.battery_percentage_remaining],
|
|
846
871
|
meta: {battery: {dontDividePercentage: true}},
|
|
847
872
|
ota: ota.tradfri,
|
|
848
873
|
configure: configureRemote,
|
|
@@ -980,6 +1005,13 @@ module.exports = [
|
|
|
980
1005
|
description: 'STOFTMOLN ceiling/wall lamp 24 warm light dimmable',
|
|
981
1006
|
extend: tradfriExtend.light_onoff_brightness(),
|
|
982
1007
|
},
|
|
1008
|
+
{
|
|
1009
|
+
zigbeeModel: ['STOFTMOLN ceiling/wall lamp WW10'],
|
|
1010
|
+
model: 'T2105',
|
|
1011
|
+
vendor: 'IKEA',
|
|
1012
|
+
description: 'STOFTMOLN ceiling/wall lamp 10 warm light dimmable',
|
|
1013
|
+
extend: tradfriExtend.light_onoff_brightness(),
|
|
1014
|
+
},
|
|
983
1015
|
{
|
|
984
1016
|
zigbeeModel: ['TRADFRIbulbPAR38WS900lm'],
|
|
985
1017
|
model: 'LED2006R9',
|
package/devices/legrand.js
CHANGED
|
@@ -28,6 +28,22 @@ const tzLocal = {
|
|
|
28
28
|
};
|
|
29
29
|
|
|
30
30
|
module.exports = [
|
|
31
|
+
{
|
|
32
|
+
zigbeeModel: [' Pocket remote\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000'+
|
|
33
|
+
'\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000'],
|
|
34
|
+
model: '067755',
|
|
35
|
+
vendor: 'Legrand',
|
|
36
|
+
description: 'Wireless and batteryless 4 scenes control',
|
|
37
|
+
fromZigbee: [fz.identify, fz.battery, fz.command_recall],
|
|
38
|
+
exposes: [e.battery(), e.action(['identify', 'recall_1_1'])],
|
|
39
|
+
toZigbee: [],
|
|
40
|
+
meta: {multiEndpoint: true, battery: {voltageToPercentage: '3V_2500'}, publishDuplicateTransaction: true},
|
|
41
|
+
onEvent: readInitialBatteryState,
|
|
42
|
+
configure: async (device, coordinatorEndpoint, logger) => {
|
|
43
|
+
const endpoint = device.getEndpoint(1);
|
|
44
|
+
await reporting.bind(endpoint, coordinatorEndpoint, ['genPowerCfg', 'genOnOff', 'genLevelCtrl']);
|
|
45
|
+
},
|
|
46
|
+
},
|
|
31
47
|
{
|
|
32
48
|
zigbeeModel: [' Dry contact\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000'+
|
|
33
49
|
'\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000'],
|
package/devices/mercator.js
CHANGED
|
@@ -183,6 +183,10 @@ module.exports = [
|
|
|
183
183
|
await reporting.bind(endpoint, coordinatorEndpoint, ['genOnOff', 'genIdentify', 'manuSpecificTuya', 'hvacFanCtrl']);
|
|
184
184
|
await reporting.onOff(endpoint);
|
|
185
185
|
await reporting.fanMode(endpoint);
|
|
186
|
+
|
|
187
|
+
// Device ships with {fanModeSequence: 1} which restricts physical speed
|
|
188
|
+
// button to low/high. Set to 0 to allow low/med/high from physical press.
|
|
189
|
+
await endpoint.write('hvacFanCtrl', {fanModeSequence: 0});
|
|
186
190
|
},
|
|
187
191
|
},
|
|
188
192
|
{
|
package/devices/moes.js
CHANGED
|
@@ -308,9 +308,9 @@ module.exports = [
|
|
|
308
308
|
'You can set up to 4 stages of temperature every for WEEKDAY ➀➁➂➃➄, SATURDAY ➅ and SUNDAY ➆.'),
|
|
309
309
|
exposes.binary('boost_heating', ea.STATE_SET, 'ON', 'OFF').withDescription('Boost Heating: press and hold "+" for 3 seconds, ' +
|
|
310
310
|
'the device will enter the boost heating mode, and the ▷╵◁ will flash. The countdown will be displayed in the APP'),
|
|
311
|
-
exposes.numeric('boost_heating_countdown', ea.STATE).withUnit('
|
|
311
|
+
exposes.numeric('boost_heating_countdown', ea.STATE).withUnit('min').withDescription('Countdown in minutes')
|
|
312
312
|
.withValueMin(0).withValueMax(15),
|
|
313
|
-
exposes.numeric('boost_heating_countdown_time_set', ea.STATE_SET).withUnit('
|
|
313
|
+
exposes.numeric('boost_heating_countdown_time_set', ea.STATE_SET).withUnit('s')
|
|
314
314
|
.withDescription('Boost Time Setting 100 sec - 900 sec, (default = 300 sec)').withValueMin(100)
|
|
315
315
|
.withValueMax(900).withValueStep(100)],
|
|
316
316
|
},
|