zigbee-herdsman-converters 14.0.645 → 14.0.647
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 +2 -176
- package/converters/toZigbee.js +0 -85
- package/devices/adeo.js +7 -0
- package/devices/awox.js +1 -1
- package/devices/develco.js +73 -50
- package/devices/innr.js +23 -0
- package/devices/lonsonho.js +4 -1
- package/devices/moes.js +6 -4
- package/devices/osram.js +9 -1
- package/devices/philips.js +2 -2
- package/devices/tuya.js +97 -33
- package/devices/vimar.js +25 -0
- package/lib/tuya.js +173 -5
- package/package.json +1 -1
package/converters/fromZigbee.js
CHANGED
|
@@ -4090,160 +4090,6 @@ const converters = {
|
|
|
4090
4090
|
}
|
|
4091
4091
|
},
|
|
4092
4092
|
},
|
|
4093
|
-
tvtwo_thermostat: {
|
|
4094
|
-
cluster: 'manuSpecificTuya',
|
|
4095
|
-
type: ['commandDataResponse', 'commandDataReport'],
|
|
4096
|
-
convert: (model, msg, publish, options, meta) => {
|
|
4097
|
-
const dpValue = tuya.firstDpValue(msg, meta, 'tvtwo_thermostat');
|
|
4098
|
-
const dp = dpValue.dp;
|
|
4099
|
-
const value = tuya.getDataValue(dpValue);
|
|
4100
|
-
const presetLookup = {0: 'auto', 1: 'manual', 2: 'holiday'};
|
|
4101
|
-
switch (dp) {
|
|
4102
|
-
case tuya.dataPoints.tvMode:
|
|
4103
|
-
return {preset: presetLookup[value]};
|
|
4104
|
-
case tuya.dataPoints.tvChildLock:
|
|
4105
|
-
return {child_lock: value ? 'LOCK' : 'UNLOCK'};
|
|
4106
|
-
case tuya.dataPoints.tvHolidayTemp:
|
|
4107
|
-
return {holiday_temperature: (value / 10).toFixed(1)};
|
|
4108
|
-
case tuya.dataPoints.tvHeatingSetpoint:
|
|
4109
|
-
return {current_heating_setpoint: (value / 10).toFixed(1)};
|
|
4110
|
-
case tuya.dataPoints.tvFrostDetection:
|
|
4111
|
-
return {frost_protection: value ? 'ON' : 'OFF'};
|
|
4112
|
-
case tuya.dataPoints.tvWindowDetection:
|
|
4113
|
-
return {open_window: value ? 'ON' : 'OFF'};
|
|
4114
|
-
case tuya.dataPoints.tvHeatingStop:
|
|
4115
|
-
return {heating_stop: value ? 'ON' : 'OFF'};
|
|
4116
|
-
case tuya.dataPoints.tvLocalTemp:
|
|
4117
|
-
return {local_temperature: (value / 10).toFixed(1)};
|
|
4118
|
-
case tuya.dataPoints.tvBattery:
|
|
4119
|
-
return {battery_low: value === 0 ? true : false};
|
|
4120
|
-
case tuya.dataPoints.tvTempCalibration:
|
|
4121
|
-
return {local_temperature_calibration: value > 55 ?
|
|
4122
|
-
((value - 0x100000000)/10).toFixed(1): (value/ 10).toFixed(1)};
|
|
4123
|
-
case tuya.dataPoints.tvBoostTime:
|
|
4124
|
-
// Setting minimum 0 - maximum 465 seconds
|
|
4125
|
-
return {boost_timeset_countdown: value};
|
|
4126
|
-
case tuya.dataPoints.tvComfortTemp:
|
|
4127
|
-
return {comfort_temperature: (value / 10).toFixed(1)};
|
|
4128
|
-
case tuya.dataPoints.tvEcoTemp:
|
|
4129
|
-
return {eco_temperature: (value / 10).toFixed(1)};
|
|
4130
|
-
case tuya.dataPoints.tvOpenWindowTemp:
|
|
4131
|
-
return {open_window_temperature: (value / 10).toFixed(1)};
|
|
4132
|
-
case tuya.dataPoints.tvErrorStatus:
|
|
4133
|
-
return {fault_alarm: value};
|
|
4134
|
-
case tuya.dataPoints.tvHolidayMode: {
|
|
4135
|
-
const sy = value.slice(0, 4); const sm = value.slice(4, 6); const sd = value.slice(6, 8);
|
|
4136
|
-
const sh = value.slice(8, 10); const smi = value.slice(10, 12); const ey = value.slice(12, 16);
|
|
4137
|
-
const em = value.slice(16, 18); const ed = value.slice(18, 20); const eh = value.slice(20, 22);
|
|
4138
|
-
const emi = value.slice(22, 24);
|
|
4139
|
-
const hMode = 'start --> ' + sy + ' - ' + sm + ' - ' + sd + ' ' + sh + ' : ' + smi +
|
|
4140
|
-
' stop --> ' + ey + ' - ' + em + ' - ' + ed + ' ' + eh + ' : ' + emi;
|
|
4141
|
-
return {holiday_start_stop: hMode};
|
|
4142
|
-
}
|
|
4143
|
-
case tuya.dataPoints.tvBoostMode:
|
|
4144
|
-
// 115 online / Is the device online
|
|
4145
|
-
return {online: value ? 'ON' : 'OFF'};
|
|
4146
|
-
case tuya.dataPoints.tvWorkingDay:
|
|
4147
|
-
// DP-31, Send and Report, ENUM, Week select 0 - 5 days, 1 - 6 days, 2 - 7 days
|
|
4148
|
-
return {working_day: value};
|
|
4149
|
-
case tuya.dataPoints.tvMondaySchedule:
|
|
4150
|
-
return {schedule_monday:
|
|
4151
|
-
' ' + value[0] / 6 + 'h:' + value[1] + 'm ' + value[2] / 10 + '°C' +
|
|
4152
|
-
', ' + value[3] / 6 + 'h:' + value[4] + 'm ' + value[5] / 10 + '°C' +
|
|
4153
|
-
', ' + value[6] / 6 + 'h:' + value[7] + 'm ' + value[8] / 10 + '°C' +
|
|
4154
|
-
', ' + value[9] / 6 + 'h:' + value[10] + 'm ' + value[11] / 10 + '°C' +
|
|
4155
|
-
', ' + value[12] / 6 + 'h:' + value[13] + 'm ' + value[14] / 10 + '°C ' +
|
|
4156
|
-
', ' + value[15] / 6 + 'h:' + value[16] + 'm ' + value[17] / 10 + '°C ' +
|
|
4157
|
-
', ' + value[18] / 6 + 'h:' + value[19] + 'm ' + value[20] / 10 + '°C ' +
|
|
4158
|
-
', ' + value[21] / 6 + 'h:' + value[22] + 'm ' + value[23] / 10 + '°C ' +
|
|
4159
|
-
', ' + value[24] / 6 + 'h:' + value[25] + 'm ' + value[26] / 10 + '°C ' +
|
|
4160
|
-
', ' + value[27] / 6 + 'h:' + value[28] + 'm ' + value[29] / 10 + '°C ',
|
|
4161
|
-
};
|
|
4162
|
-
case tuya.dataPoints.tvTuesdaySchedule:
|
|
4163
|
-
return {schedule_tuesday:
|
|
4164
|
-
' ' + value[0] / 6 + 'h:' + value[1] + 'm ' + value[2] / 10 + '°C' +
|
|
4165
|
-
', ' + value[3] / 6 + 'h:' + value[4] + 'm ' + value[5] / 10 + '°C' +
|
|
4166
|
-
', ' + value[6] / 6 + 'h:' + value[7] + 'm ' + value[8] / 10 + '°C' +
|
|
4167
|
-
', ' + value[9] / 6 + 'h:' + value[10] + 'm ' + value[11] / 10 + '°C' +
|
|
4168
|
-
', ' + value[12] / 6 + 'h:' + value[13] + 'm ' + value[14] / 10 + '°C ' +
|
|
4169
|
-
', ' + value[15] / 6 + 'h:' + value[16] + 'm ' + value[17] / 10 + '°C ' +
|
|
4170
|
-
', ' + value[18] / 6 + 'h:' + value[19] + 'm ' + value[20] / 10 + '°C ' +
|
|
4171
|
-
', ' + value[21] / 6 + 'h:' + value[22] + 'm ' + value[23] / 10 + '°C ' +
|
|
4172
|
-
', ' + value[24] / 6 + 'h:' + value[25] + 'm ' + value[26] / 10 + '°C ' +
|
|
4173
|
-
', ' + value[27] / 6 + 'h:' + value[28] + 'm ' + value[29] / 10 + '°C ',
|
|
4174
|
-
};
|
|
4175
|
-
case tuya.dataPoints.tvWednesdaySchedule:
|
|
4176
|
-
return {schedule_wednesday:
|
|
4177
|
-
' ' + value[0] / 6 + 'h:' + value[1] + 'm ' + value[2] / 10 + '°C' +
|
|
4178
|
-
', ' + value[3] / 6 + 'h:' + value[4] + 'm ' + value[5] / 10 + '°C' +
|
|
4179
|
-
', ' + value[6] / 6 + 'h:' + value[7] + 'm ' + value[8] / 10 + '°C' +
|
|
4180
|
-
', ' + value[9] / 6 + 'h:' + value[10] + 'm ' + value[11] / 10 + '°C' +
|
|
4181
|
-
', ' + value[12] / 6 + 'h:' + value[13] + 'm ' + value[14] / 10 + '°C ' +
|
|
4182
|
-
', ' + value[15] / 6 + 'h:' + value[16] + 'm ' + value[17] / 10 + '°C ' +
|
|
4183
|
-
', ' + value[18] / 6 + 'h:' + value[19] + 'm ' + value[20] / 10 + '°C ' +
|
|
4184
|
-
', ' + value[21] / 6 + 'h:' + value[22] + 'm ' + value[23] / 10 + '°C ' +
|
|
4185
|
-
', ' + value[24] / 6 + 'h:' + value[25] + 'm ' + value[26] / 10 + '°C ' +
|
|
4186
|
-
', ' + value[27] / 6 + 'h:' + value[28] + 'm ' + value[29] / 10 + '°C ',
|
|
4187
|
-
};
|
|
4188
|
-
case tuya.dataPoints.tvThursdaySchedule:
|
|
4189
|
-
return {schedule_thursday:
|
|
4190
|
-
' ' + value[0] / 6 + 'h:' + value[1] + 'm ' + value[2] / 10 + '°C' +
|
|
4191
|
-
', ' + value[3] / 6 + 'h:' + value[4] + 'm ' + value[5] / 10 + '°C' +
|
|
4192
|
-
', ' + value[6] / 6 + 'h:' + value[7] + 'm ' + value[8] / 10 + '°C' +
|
|
4193
|
-
', ' + value[9] / 6 + 'h:' + value[10] + 'm ' + value[11] / 10 + '°C' +
|
|
4194
|
-
', ' + value[12] / 6 + 'h:' + value[13] + 'm ' + value[14] / 10 + '°C ' +
|
|
4195
|
-
', ' + value[15] / 6 + 'h:' + value[16] + 'm ' + value[17] / 10 + '°C ' +
|
|
4196
|
-
', ' + value[18] / 6 + 'h:' + value[19] + 'm ' + value[20] / 10 + '°C ' +
|
|
4197
|
-
', ' + value[21] / 6 + 'h:' + value[22] + 'm ' + value[23] / 10 + '°C ' +
|
|
4198
|
-
', ' + value[24] / 6 + 'h:' + value[25] + 'm ' + value[26] / 10 + '°C ' +
|
|
4199
|
-
', ' + value[27] / 6 + 'h:' + value[28] + 'm ' + value[29] / 10 + '°C ',
|
|
4200
|
-
};
|
|
4201
|
-
case tuya.dataPoints.tvFridaySchedule:
|
|
4202
|
-
return {schedule_friday:
|
|
4203
|
-
' ' + value[0] / 6 + 'h:' + value[1] + 'm ' + value[2] / 10 + '°C' +
|
|
4204
|
-
', ' + value[3] / 6 + 'h:' + value[4] + 'm ' + value[5] / 10 + '°C' +
|
|
4205
|
-
', ' + value[6] / 6 + 'h:' + value[7] + 'm ' + value[8] / 10 + '°C' +
|
|
4206
|
-
', ' + value[9] / 6 + 'h:' + value[10] + 'm ' + value[11] / 10 + '°C' +
|
|
4207
|
-
', ' + value[12] / 6 + 'h:' + value[13] + 'm ' + value[14] / 10 + '°C ' +
|
|
4208
|
-
', ' + value[15] / 6 + 'h:' + value[16] + 'm ' + value[17] / 10 + '°C ' +
|
|
4209
|
-
', ' + value[18] / 6 + 'h:' + value[19] + 'm ' + value[20] / 10 + '°C ' +
|
|
4210
|
-
', ' + value[21] / 6 + 'h:' + value[22] + 'm ' + value[23] / 10 + '°C ' +
|
|
4211
|
-
', ' + value[24] / 6 + 'h:' + value[25] + 'm ' + value[26] / 10 + '°C ' +
|
|
4212
|
-
', ' + value[27] / 6 + 'h:' + value[28] + 'm ' + value[29] / 10 + '°C ',
|
|
4213
|
-
};
|
|
4214
|
-
case tuya.dataPoints.tvSaturdaySchedule:
|
|
4215
|
-
return {schedule_saturday:
|
|
4216
|
-
' ' + value[0] / 6 + 'h:' + value[1] + 'm ' + value[2] / 10 + '°C' +
|
|
4217
|
-
', ' + value[3] / 6 + 'h:' + value[4] + 'm ' + value[5] / 10 + '°C' +
|
|
4218
|
-
', ' + value[6] / 6 + 'h:' + value[7] + 'm ' + value[8] / 10 + '°C' +
|
|
4219
|
-
', ' + value[9] / 6 + 'h:' + value[10] + 'm ' + value[11] / 10 + '°C' +
|
|
4220
|
-
', ' + value[12] / 6 + 'h:' + value[13] + 'm ' + value[14] / 10 + '°C ' +
|
|
4221
|
-
', ' + value[15] / 6 + 'h:' + value[16] + 'm ' + value[17] / 10 + '°C ' +
|
|
4222
|
-
', ' + value[18] / 6 + 'h:' + value[19] + 'm ' + value[20] / 10 + '°C ' +
|
|
4223
|
-
', ' + value[21] / 6 + 'h:' + value[22] + 'm ' + value[23] / 10 + '°C ' +
|
|
4224
|
-
', ' + value[24] / 6 + 'h:' + value[25] + 'm ' + value[26] / 10 + '°C ' +
|
|
4225
|
-
', ' + value[27] / 6 + 'h:' + value[28] + 'm ' + value[29] / 10 + '°C ',
|
|
4226
|
-
};
|
|
4227
|
-
case tuya.dataPoints.tvSundaySchedule:
|
|
4228
|
-
return {schedule_sunday:
|
|
4229
|
-
' ' + value[0] / 6 + 'h:' + value[1] + 'm ' + value[2] / 10 + '°C' +
|
|
4230
|
-
', ' + value[3] / 6 + 'h:' + value[4] + 'm ' + value[5] / 10 + '°C' +
|
|
4231
|
-
', ' + value[6] / 6 + 'h:' + value[7] + 'm ' + value[8] / 10 + '°C' +
|
|
4232
|
-
', ' + value[9] / 6 + 'h:' + value[10] + 'm ' + value[11] / 10 + '°C' +
|
|
4233
|
-
', ' + value[12] / 6 + 'h:' + value[13] + 'm ' + value[14] / 10 + '°C ' +
|
|
4234
|
-
', ' + value[15] / 6 + 'h:' + value[16] + 'm ' + value[17] / 10 + '°C ' +
|
|
4235
|
-
', ' + value[18] / 6 + 'h:' + value[19] + 'm ' + value[20] / 10 + '°C ' +
|
|
4236
|
-
', ' + value[21] / 6 + 'h:' + value[22] + 'm ' + value[23] / 10 + '°C ' +
|
|
4237
|
-
', ' + value[24] / 6 + 'h:' + value[25] + 'm ' + value[26] / 10 + '°C ' +
|
|
4238
|
-
', ' + value[27] / 6 + 'h:' + value[28] + 'm ' + value[29] / 10 + '°C ',
|
|
4239
|
-
};
|
|
4240
|
-
|
|
4241
|
-
default:
|
|
4242
|
-
meta.logger.warn(`zigbee-herdsman-converters:tvtwo_thermostat: NOT RECOGNIZED DP #${
|
|
4243
|
-
dp} with data ${JSON.stringify(dpValue)}`);
|
|
4244
|
-
}
|
|
4245
|
-
},
|
|
4246
|
-
},
|
|
4247
4093
|
haozee_thermostat: {
|
|
4248
4094
|
cluster: 'manuSpecificTuya',
|
|
4249
4095
|
type: ['commandDataResponse', 'commandDataReport'],
|
|
@@ -5047,11 +4893,8 @@ const converters = {
|
|
|
5047
4893
|
0x63: 'release_2_and_4', 0x22: 'press_energy_bar',
|
|
5048
4894
|
};
|
|
5049
4895
|
|
|
5050
|
-
|
|
5051
|
-
|
|
5052
|
-
} else {
|
|
5053
|
-
return {action: lookup[commandID]};
|
|
5054
|
-
}
|
|
4896
|
+
const action = lookup.hasOwnProperty(commandID) ? lookup[commandID] : `unknown_${commandID}`;
|
|
4897
|
+
return {action};
|
|
5055
4898
|
},
|
|
5056
4899
|
},
|
|
5057
4900
|
enocean_ptm215ze: {
|
|
@@ -7491,23 +7334,6 @@ const converters = {
|
|
|
7491
7334
|
return result;
|
|
7492
7335
|
},
|
|
7493
7336
|
},
|
|
7494
|
-
tuya_gas: {
|
|
7495
|
-
cluster: 'manuSpecificTuya',
|
|
7496
|
-
type: ['commandDataResponse'],
|
|
7497
|
-
convert: (model, msg, publish, options, meta) => {
|
|
7498
|
-
const dpValue = tuya.firstDpValue(msg, meta, 'tuya_gas');
|
|
7499
|
-
const dp = dpValue.dp;
|
|
7500
|
-
const value = tuya.getDataValue(dpValue);
|
|
7501
|
-
|
|
7502
|
-
switch (dp) {
|
|
7503
|
-
case tuya.dataPoints.state:
|
|
7504
|
-
return {gas: value === 0 ? true : false};
|
|
7505
|
-
default:
|
|
7506
|
-
meta.logger.warn(`zigbee-herdsman-converters:tuya_gas: Unrecognized DP #${
|
|
7507
|
-
dp} with data ${JSON.stringify(dpValue)}`);
|
|
7508
|
-
}
|
|
7509
|
-
},
|
|
7510
|
-
},
|
|
7511
7337
|
idlock: {
|
|
7512
7338
|
cluster: 'closuresDoorLock',
|
|
7513
7339
|
type: ['attributeReport', 'readResponse'],
|
package/converters/toZigbee.js
CHANGED
|
@@ -3518,91 +3518,6 @@ const converters = {
|
|
|
3518
3518
|
return tuya.sendDataPointRaw(entity, tuya.dataPoints.moesSschedule, payload);
|
|
3519
3519
|
},
|
|
3520
3520
|
},
|
|
3521
|
-
tvtwo_thermostat: {
|
|
3522
|
-
key: [
|
|
3523
|
-
'child_lock', 'open_window', 'open_window_temperature', 'frost_protection', 'heating_stop',
|
|
3524
|
-
'current_heating_setpoint', 'local_temperature_calibration', 'preset', 'boost_timeset_countdown',
|
|
3525
|
-
'holiday_start_stop', 'holiday_temperature', 'comfort_temperature', 'eco_temperature',
|
|
3526
|
-
'working_day', 'week_schedule_programming', 'online', 'holiday_mode_date',
|
|
3527
|
-
],
|
|
3528
|
-
convertSet: async (entity, key, value, meta) => {
|
|
3529
|
-
switch (key) {
|
|
3530
|
-
case 'preset': {
|
|
3531
|
-
const presetLookup = {'auto': 0, 'manual': 1, 'holiday': 3};
|
|
3532
|
-
await tuya.sendDataPointEnum(entity, tuya.dataPoints.tvMode, presetLookup[value]);
|
|
3533
|
-
return {state: {preset: value}};}
|
|
3534
|
-
case 'heating_stop':
|
|
3535
|
-
if (value == 'ON') {
|
|
3536
|
-
await tuya.sendDataPointBool(entity, tuya.dataPoints.tvHeatingStop, 1);
|
|
3537
|
-
} else {
|
|
3538
|
-
await tuya.sendDataPointEnum(entity, tuya.dataPoints.tvMode, 1 /* manual */);
|
|
3539
|
-
}
|
|
3540
|
-
break;
|
|
3541
|
-
case 'frost_protection':
|
|
3542
|
-
if (value == 'ON') {
|
|
3543
|
-
await tuya.sendDataPointBool(entity, tuya.dataPoints.tvFrostDetection, 1);
|
|
3544
|
-
} else {
|
|
3545
|
-
await tuya.sendDataPointEnum(entity, tuya.dataPoints.tvMode, 1 /* manual */);
|
|
3546
|
-
}
|
|
3547
|
-
break;
|
|
3548
|
-
case 'open_window':
|
|
3549
|
-
await tuya.sendDataPointBool(entity, tuya.dataPoints.tvWindowDetection, value === 'ON');
|
|
3550
|
-
break;
|
|
3551
|
-
case 'child_lock':
|
|
3552
|
-
await tuya.sendDataPointBool(entity, tuya.dataPoints.tvChildLock, value === 'LOCK');
|
|
3553
|
-
break;
|
|
3554
|
-
case 'local_temperature_calibration':
|
|
3555
|
-
if (value > 0) value = value*10;
|
|
3556
|
-
if (value < 0) value = value*10 + 0x100000000;
|
|
3557
|
-
await tuya.sendDataPointValue(entity, tuya.dataPoints.tvTempCalibration, value);
|
|
3558
|
-
break;
|
|
3559
|
-
case 'current_heating_setpoint':
|
|
3560
|
-
await tuya.sendDataPointValue(entity, tuya.dataPoints.tvHeatingSetpoint, value * 10);
|
|
3561
|
-
await utils.sleep(500);
|
|
3562
|
-
await tuya.sendDataPointEnum(entity, tuya.dataPoints.tvMode, 1 /* manual */);
|
|
3563
|
-
break;
|
|
3564
|
-
case 'holiday_temperature':
|
|
3565
|
-
await tuya.sendDataPointValue(entity, tuya.dataPoints.tvHolidayTemp, value * 10);
|
|
3566
|
-
break;
|
|
3567
|
-
case 'comfort_temperature':
|
|
3568
|
-
await tuya.sendDataPointValue(entity, tuya.dataPoints.tvComfortTemp, value * 10);
|
|
3569
|
-
break;
|
|
3570
|
-
case 'eco_temperature':
|
|
3571
|
-
await tuya.sendDataPointValue(entity, tuya.dataPoints.tvEcoTemp, value * 10);
|
|
3572
|
-
break;
|
|
3573
|
-
case 'boost_timeset_countdown':
|
|
3574
|
-
// set min 0 - max 465 sec boost time
|
|
3575
|
-
await tuya.sendDataPointValue(entity, tuya.dataPoints.tvBoostTime, value);
|
|
3576
|
-
break;
|
|
3577
|
-
case 'open_window_temperature':
|
|
3578
|
-
await tuya.sendDataPointValue(entity, tuya.dataPoints.tvOpenWindowTemp, value * 10);
|
|
3579
|
-
break;
|
|
3580
|
-
case 'holiday_start_stop': {
|
|
3581
|
-
const numberPattern = /\d+/g;
|
|
3582
|
-
value = value.match(numberPattern).join([]).toString();
|
|
3583
|
-
return tuya.sendDataPointStringBuffer(entity, tuya.dataPoints.tvHolidayMode, value);
|
|
3584
|
-
}
|
|
3585
|
-
case 'online':
|
|
3586
|
-
// 115 online / Is the device online
|
|
3587
|
-
await tuya.sendDataPointBool(entity, tuya.dataPoints.tvBoostMode, value === 'ON');
|
|
3588
|
-
break;
|
|
3589
|
-
case 'working_day': {
|
|
3590
|
-
// DP-31, Send and Report, ENUM, Week select 0 - 5 days, 1 - 6 days, 2 - 7 days
|
|
3591
|
-
const workLookup = {'0': 0, '1': 1, '2': 2, '3': 3};
|
|
3592
|
-
const workDay = workLookup[value];
|
|
3593
|
-
await tuya.sendDataPointEnum(entity, tuya.dataPoints.tvWorkingDay, workDay);
|
|
3594
|
-
return {state: {working_day: value}};
|
|
3595
|
-
}
|
|
3596
|
-
case 'week_schedule_programming':
|
|
3597
|
-
// DP-106, Send Only, raw, week_program3_day
|
|
3598
|
-
await tuya.sendDataPointRaw(entity, tuya.dataPoints.tvWeekSchedule, value);
|
|
3599
|
-
break;
|
|
3600
|
-
|
|
3601
|
-
default: // Unknown key
|
|
3602
|
-
meta.logger.warn(`toZigbee.tvtwo_thermostat: Unhandled key ${key}`);
|
|
3603
|
-
}
|
|
3604
|
-
},
|
|
3605
|
-
},
|
|
3606
3521
|
haozee_thermostat_system_mode: {
|
|
3607
3522
|
key: ['preset'],
|
|
3608
3523
|
convertSet: async (entity, key, value, meta) => {
|
package/devices/adeo.js
CHANGED
|
@@ -87,6 +87,13 @@ module.exports = [
|
|
|
87
87
|
description: 'ENKI LEXMAN E14 LED white',
|
|
88
88
|
extend: extend.light_onoff_brightness_colortemp({colorTempRange: [153, 454]}),
|
|
89
89
|
},
|
|
90
|
+
{
|
|
91
|
+
zigbeeModel: ['ZBEK-11'],
|
|
92
|
+
model: 'IM-CDZDGAAG005KA-MZN',
|
|
93
|
+
vendor: 'ADEO',
|
|
94
|
+
description: 'ENKI LEXMAN GU-10 LED white',
|
|
95
|
+
extend: extend.light_onoff_brightness_colortemp({colorTempRange: [153, 454]}),
|
|
96
|
+
},
|
|
90
97
|
{
|
|
91
98
|
zigbeeModel: ['ZBEK-12'],
|
|
92
99
|
model: 'IA-CDZFB2AA007NA-MZN-01',
|
package/devices/awox.js
CHANGED
|
@@ -155,7 +155,7 @@ module.exports = [
|
|
|
155
155
|
model: '33957',
|
|
156
156
|
vendor: 'AwoX',
|
|
157
157
|
description: 'LED light with color temperature',
|
|
158
|
-
extend: extend.light_onoff_brightness_colortemp({colorTempRange: [153,
|
|
158
|
+
extend: extend.light_onoff_brightness_colortemp({colorTempRange: [153, 454]}),
|
|
159
159
|
whiteLabel: [{vendor: 'EGLO', model: '12239'}],
|
|
160
160
|
},
|
|
161
161
|
{
|
package/devices/develco.js
CHANGED
|
@@ -25,6 +25,33 @@ const develcoLedControlMap = {
|
|
|
25
25
|
0xFF: 'both',
|
|
26
26
|
};
|
|
27
27
|
|
|
28
|
+
|
|
29
|
+
const fwOnEvent = async (type, data, device, options, state) => {
|
|
30
|
+
/**
|
|
31
|
+
* Develco devices have a manufacturer specific field on genBasic
|
|
32
|
+
* for sw and hw versions.
|
|
33
|
+
*
|
|
34
|
+
* We read those during deviceInterview
|
|
35
|
+
* so that this information is usable during configure() calls
|
|
36
|
+
* to skip some features on older devices that might cause
|
|
37
|
+
* timeouts and other issues.
|
|
38
|
+
*/
|
|
39
|
+
if (type === 'deviceInterview') { // WARN: also reading on deviceAnnounce hits a timeout!
|
|
40
|
+
try {
|
|
41
|
+
const data = await device.endpoints[0].read('genBasic', ['develcoPrimarySwVersion', 'develcoPrimaryHwVersion'],
|
|
42
|
+
manufacturerOptions);
|
|
43
|
+
|
|
44
|
+
if (data.hasOwnProperty('develcoPrimarySwVersion')) {
|
|
45
|
+
device.softwareBuildID = data.develcoPrimarySwVersion.join('.');
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (data.hasOwnProperty('develcoPrimaryHwVersion')) {
|
|
49
|
+
device.hardwareVersion = data.develcoPrimaryHwVersion.join('.');
|
|
50
|
+
}
|
|
51
|
+
} catch (error) {/* catch timeouts of sleeping devices */}
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
|
|
28
55
|
// develco specific convertors
|
|
29
56
|
const develco = {
|
|
30
57
|
fz: {
|
|
@@ -86,24 +113,6 @@ const develco = {
|
|
|
86
113
|
return result;
|
|
87
114
|
},
|
|
88
115
|
},
|
|
89
|
-
firmware_version: {
|
|
90
|
-
cluster: 'genBasic',
|
|
91
|
-
type: ['attributeReport', 'readResponse'],
|
|
92
|
-
convert: (model, msg, publish, options, meta) => {
|
|
93
|
-
const result = {};
|
|
94
|
-
if (msg.data.hasOwnProperty('develcoPrimarySwVersion')) {
|
|
95
|
-
const firmware = msg.data.develcoPrimarySwVersion.join('.');
|
|
96
|
-
result.current_firmware = firmware;
|
|
97
|
-
meta.device.softwareBuildID = firmware;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
if (msg.data.hasOwnProperty('develcoPrimaryHwVersion')) {
|
|
101
|
-
meta.device.hardwareVersion = msg.data.develcoPrimaryHwVersion.join('.');
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
return result;
|
|
105
|
-
},
|
|
106
|
-
},
|
|
107
116
|
fault_status: {
|
|
108
117
|
cluster: 'genBinaryInput',
|
|
109
118
|
type: ['attributeReport', 'readResponse'],
|
|
@@ -374,12 +383,11 @@ module.exports = [
|
|
|
374
383
|
model: 'EMIZB-132',
|
|
375
384
|
vendor: 'Develco',
|
|
376
385
|
description: 'Wattle AMS HAN power-meter sensor',
|
|
377
|
-
fromZigbee: [develco.fz.metering, develco.fz.electrical_measurement
|
|
386
|
+
fromZigbee: [develco.fz.metering, develco.fz.electrical_measurement],
|
|
378
387
|
toZigbee: [tz.EMIZB_132_mode],
|
|
379
388
|
ota: ota.zigbeeOTA,
|
|
380
389
|
configure: async (device, coordinatorEndpoint, logger) => {
|
|
381
390
|
const endpoint = device.getEndpoint(2);
|
|
382
|
-
await endpoint.read('genBasic', ['develcoPrimarySwVersion', 'develcoPrimaryHwVersion'], manufacturerOptions);
|
|
383
391
|
await reporting.bind(endpoint, coordinatorEndpoint, ['haElectricalMeasurement', 'seMetering']);
|
|
384
392
|
|
|
385
393
|
try {
|
|
@@ -402,6 +410,7 @@ module.exports = [
|
|
|
402
410
|
exposes: [e.power(), e.energy(), e.current(), e.voltage(), e.current_phase_b(), e.voltage_phase_b(), e.current_phase_c(),
|
|
403
411
|
e.voltage_phase_c()],
|
|
404
412
|
onEvent: async (type, data, device) => {
|
|
413
|
+
fwOnEvent(type, data, device);
|
|
405
414
|
if (type === 'message' && data.type === 'attributeReport' && data.cluster === 'seMetering' && data.data['divisor']) {
|
|
406
415
|
// Device sends wrong divisior (512) while it should be fixed to 1000
|
|
407
416
|
// https://github.com/Koenkk/zigbee-herdsman-converters/issues/3066
|
|
@@ -415,16 +424,16 @@ module.exports = [
|
|
|
415
424
|
vendor: 'Develco',
|
|
416
425
|
description: 'Smoke detector with siren',
|
|
417
426
|
fromZigbee: [fz.temperature, fz.battery, fz.ias_smoke_alarm_1_develco, fz.ignore_basic_report,
|
|
418
|
-
|
|
427
|
+
fz.ias_enroll, fz.ias_wd, develco.fz.fault_status],
|
|
419
428
|
toZigbee: [tz.warning, tz.ias_max_duration, tz.warning_simple],
|
|
420
429
|
ota: ota.zigbeeOTA,
|
|
430
|
+
onEvent: fwOnEvent,
|
|
421
431
|
meta: {battery: {voltageToPercentage: '3V_2500'}},
|
|
422
432
|
configure: async (device, coordinatorEndpoint, logger) => {
|
|
423
433
|
const endpoint = device.getEndpoint(35);
|
|
424
434
|
|
|
425
435
|
await reporting.bind(endpoint, coordinatorEndpoint, ['genPowerCfg', 'ssIasZone', 'ssIasWd', 'genBasic', 'genBinaryInput']);
|
|
426
436
|
await reporting.batteryVoltage(endpoint);
|
|
427
|
-
await endpoint.read('genBasic', ['develcoPrimarySwVersion', 'develcoPrimaryHwVersion'], manufacturerOptions);
|
|
428
437
|
await endpoint.read('ssIasZone', ['iasCieAddr', 'zoneState', 'zoneId']);
|
|
429
438
|
await endpoint.read('genBinaryInput', ['reliability', 'statusFlags']);
|
|
430
439
|
await endpoint.read('ssIasWd', ['maxDuration']);
|
|
@@ -449,15 +458,15 @@ module.exports = [
|
|
|
449
458
|
vendor: 'Develco',
|
|
450
459
|
description: 'Fire detector with siren',
|
|
451
460
|
fromZigbee: [fz.temperature, fz.battery, fz.ias_smoke_alarm_1_develco, fz.ignore_basic_report,
|
|
452
|
-
|
|
461
|
+
fz.ias_enroll, fz.ias_wd, develco.fz.fault_status],
|
|
453
462
|
toZigbee: [tz.warning, tz.ias_max_duration, tz.warning_simple],
|
|
454
463
|
meta: {battery: {voltageToPercentage: '3V_2500'}},
|
|
464
|
+
onEvent: fwOnEvent,
|
|
455
465
|
configure: async (device, coordinatorEndpoint, logger) => {
|
|
456
466
|
const endpoint = device.getEndpoint(35);
|
|
457
467
|
|
|
458
468
|
await reporting.bind(endpoint, coordinatorEndpoint, ['genPowerCfg', 'ssIasZone', 'ssIasWd', 'genBasic', 'genBinaryInput']);
|
|
459
469
|
await reporting.batteryVoltage(endpoint);
|
|
460
|
-
await endpoint.read('genBasic', ['develcoPrimarySwVersion'], manufacturerOptions);
|
|
461
470
|
await endpoint.read('ssIasZone', ['iasCieAddr', 'zoneState', 'zoneId']);
|
|
462
471
|
await endpoint.read('genBinaryInput', ['reliability', 'statusFlags']);
|
|
463
472
|
await endpoint.read('ssIasWd', ['maxDuration']);
|
|
@@ -519,39 +528,55 @@ module.exports = [
|
|
|
519
528
|
description: 'Motion sensor',
|
|
520
529
|
fromZigbee: [
|
|
521
530
|
fz.temperature, fz.illuminance, fz.ias_occupancy_alarm_1, fz.battery,
|
|
522
|
-
develco.fz.led_control, develco.fz.ias_occupancy_timeout,
|
|
531
|
+
develco.fz.led_control, develco.fz.ias_occupancy_timeout,
|
|
523
532
|
],
|
|
524
533
|
toZigbee: [develco.tz.led_control, develco.tz.ias_occupancy_timeout],
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
534
|
+
onEvent: fwOnEvent,
|
|
535
|
+
exposes: (device, options) => {
|
|
536
|
+
const dynExposes = [];
|
|
537
|
+
dynExposes.push(e.occupancy());
|
|
538
|
+
if (device && device.softwareBuildID && device.softwareBuildID.split('.')[0] >= 3) {
|
|
539
|
+
dynExposes.push(exposes.numeric('occupancy_timeout', ea.ALL).withUnit('second').
|
|
540
|
+
withValueMin(20).withValueMax(65535));
|
|
541
|
+
}
|
|
542
|
+
dynExposes.push(e.temperature());
|
|
543
|
+
dynExposes.push(e.illuminance_lux());
|
|
544
|
+
dynExposes.push(e.tamper());
|
|
545
|
+
dynExposes.push(e.battery_low());
|
|
546
|
+
dynExposes.push(e.battery());
|
|
547
|
+
if (device && device.softwareBuildID && device.softwareBuildID.split('.')[0] >= 4) {
|
|
548
|
+
dynExposes.push(exposes.enum('led_control', ea.ALL, ['off', 'fault_only', 'motion_only', 'both']).
|
|
549
|
+
withDescription('Control LED indicator usage.'));
|
|
550
|
+
}
|
|
551
|
+
dynExposes.push(e.linkquality());
|
|
552
|
+
return dynExposes;
|
|
553
|
+
},
|
|
532
554
|
meta: {battery: {voltageToPercentage: '3V_2500'}},
|
|
533
555
|
endpoint: (device) => {
|
|
534
556
|
return {default: 35};
|
|
535
557
|
},
|
|
536
558
|
configure: async (device, coordinatorEndpoint, logger) => {
|
|
537
|
-
const
|
|
538
|
-
await reporting.bind(
|
|
539
|
-
await reporting.
|
|
540
|
-
|
|
541
|
-
await endpoint35.read('genBasic', ['develcoPrimarySwVersion', 'develcoPrimaryHwVersion'], manufacturerOptions);
|
|
542
|
-
await endpoint35.read('ssIasZone', ['develcoAlarmOffDelay'], manufacturerOptions);
|
|
543
|
-
await endpoint35.read('genBasic', ['develcoLedControl'], manufacturerOptions);
|
|
544
|
-
} catch (error) {/* some reports of timeouts on reading these */}
|
|
559
|
+
const endpoint39 = device.getEndpoint(39);
|
|
560
|
+
await reporting.bind(endpoint39, coordinatorEndpoint, ['msIlluminanceMeasurement']);
|
|
561
|
+
await reporting.illuminance(endpoint39,
|
|
562
|
+
{min: constants.repInterval.MINUTE, max: constants.repInterval.MINUTES_10, change: 500});
|
|
545
563
|
|
|
546
564
|
const endpoint38 = device.getEndpoint(38);
|
|
547
565
|
await reporting.bind(endpoint38, coordinatorEndpoint, ['msTemperatureMeasurement']);
|
|
548
566
|
await reporting.temperature(endpoint38,
|
|
549
567
|
{min: constants.repInterval.MINUTE, max: constants.repInterval.MINUTES_10, change: 100});
|
|
550
568
|
|
|
551
|
-
const
|
|
552
|
-
await reporting.bind(
|
|
553
|
-
await reporting.
|
|
554
|
-
|
|
569
|
+
const endpoint35 = device.getEndpoint(35);
|
|
570
|
+
await reporting.bind(endpoint35, coordinatorEndpoint, ['genPowerCfg']);
|
|
571
|
+
await reporting.batteryVoltage(endpoint35, {min: constants.repInterval.HOUR, max: 43200, change: 100});
|
|
572
|
+
|
|
573
|
+
// zigbee2mqtt#14277 some features are not available on older firmwares
|
|
574
|
+
if (device && device.softwareBuildID && device.softwareBuildID.split('.')[0] >= 3) {
|
|
575
|
+
await endpoint35.read('ssIasZone', ['develcoAlarmOffDelay'], manufacturerOptions);
|
|
576
|
+
}
|
|
577
|
+
if (device && device.softwareBuildID && device.softwareBuildID.split('.')[0] >= 4) {
|
|
578
|
+
await endpoint35.read('genBasic', ['develcoLedControl'], manufacturerOptions);
|
|
579
|
+
}
|
|
555
580
|
},
|
|
556
581
|
},
|
|
557
582
|
{
|
|
@@ -676,14 +701,14 @@ module.exports = [
|
|
|
676
701
|
model: 'SIRZB-110',
|
|
677
702
|
vendor: 'Develco',
|
|
678
703
|
description: 'Customizable siren',
|
|
679
|
-
fromZigbee: [fz.temperature, fz.battery, fz.ias_enroll, fz.ias_wd,
|
|
704
|
+
fromZigbee: [fz.temperature, fz.battery, fz.ias_enroll, fz.ias_wd, fz.ias_siren],
|
|
680
705
|
toZigbee: [tz.warning, tz.warning_simple, tz.ias_max_duration, tz.squawk],
|
|
706
|
+
onEvent: fwOnEvent,
|
|
681
707
|
meta: {battery: {voltageToPercentage: '3V_2500'}},
|
|
682
708
|
configure: async (device, coordinatorEndpoint, logger) => {
|
|
683
709
|
const endpoint = device.getEndpoint(43);
|
|
684
710
|
await reporting.bind(endpoint, coordinatorEndpoint, ['genPowerCfg', 'ssIasZone', 'ssIasWd', 'genBasic']);
|
|
685
711
|
await reporting.batteryVoltage(endpoint);
|
|
686
|
-
await endpoint.read('genBasic', ['develcoPrimarySwVersion', 'develcoPrimaryHwVersion'], manufacturerOptions);
|
|
687
712
|
await endpoint.read('ssIasZone', ['iasCieAddr', 'zoneState', 'zoneId']);
|
|
688
713
|
await endpoint.read('ssIasWd', ['maxDuration']);
|
|
689
714
|
|
|
@@ -742,8 +767,9 @@ module.exports = [
|
|
|
742
767
|
model: 'IOMZB-110',
|
|
743
768
|
vendor: 'Develco',
|
|
744
769
|
description: 'IO module',
|
|
745
|
-
fromZigbee: [fz.on_off, develco.fz.input
|
|
770
|
+
fromZigbee: [fz.on_off, develco.fz.input],
|
|
746
771
|
toZigbee: [tz.on_off, develco.tz.input],
|
|
772
|
+
onEvent: fwOnEvent,
|
|
747
773
|
meta: {multiEndpoint: true},
|
|
748
774
|
exposes: [
|
|
749
775
|
exposes.binary('input', ea.STATE_GET, true, false).withEndpoint('l1').withDescription('State of input 1'),
|
|
@@ -754,12 +780,9 @@ module.exports = [
|
|
|
754
780
|
exposes.switch().withState('state', true, 'On/off state of switch 2').withEndpoint('l12'),
|
|
755
781
|
],
|
|
756
782
|
configure: async (device, coordinatorEndpoint, logger) => {
|
|
757
|
-
const options = {manufacturerCode: 4117};
|
|
758
|
-
|
|
759
783
|
const ep2 = device.getEndpoint(112);
|
|
760
784
|
await reporting.bind(ep2, coordinatorEndpoint, ['genBinaryInput', 'genBasic']);
|
|
761
785
|
await reporting.presentValue(ep2, {min: 0});
|
|
762
|
-
await ep2.read('genBasic', ['develcoPrimarySwVersion', 'develcoPrimaryHwVersion'], options);
|
|
763
786
|
|
|
764
787
|
const ep3 = device.getEndpoint(113);
|
|
765
788
|
await reporting.bind(ep3, coordinatorEndpoint, ['genBinaryInput']);
|
package/devices/innr.js
CHANGED
|
@@ -7,6 +7,21 @@ const e = exposes.presets;
|
|
|
7
7
|
const ea = exposes.access;
|
|
8
8
|
|
|
9
9
|
module.exports = [
|
|
10
|
+
{
|
|
11
|
+
zigbeeModel: ['RC 250'],
|
|
12
|
+
model: 'RC 250',
|
|
13
|
+
vendor: 'Innr',
|
|
14
|
+
description: 'Remote control',
|
|
15
|
+
fromZigbee: [fz.command_step, fz.command_on, fz.command_off, fz.command_move_to_level, fz.command_move_to_color_temp],
|
|
16
|
+
toZigbee: [],
|
|
17
|
+
exposes: [e.action(['on', 'off', 'brightness_step_up', 'brightness_step_down',
|
|
18
|
+
'brightness_move_to_level', 'color_temperature_move'])],
|
|
19
|
+
configure: async (device, coordinatorEndpoint, logger) => {
|
|
20
|
+
const ep = device.getEndpoint(1);
|
|
21
|
+
await reporting.bind(ep, coordinatorEndpoint, ['genBasic', 'genGroups', 'genScenes',
|
|
22
|
+
'genOnOff', 'genLevelCtrl', 'lightingColorCtrl']);
|
|
23
|
+
},
|
|
24
|
+
},
|
|
10
25
|
{
|
|
11
26
|
zigbeeModel: ['RCL 240 T'],
|
|
12
27
|
model: 'RCL 240 T',
|
|
@@ -144,6 +159,14 @@ module.exports = [
|
|
|
144
159
|
extend: extend.light_onoff_brightness_colortemp({colorTempRange: [153, 555]}),
|
|
145
160
|
meta: {applyRedFix: true, turnsOffAtBrightness1: true},
|
|
146
161
|
},
|
|
162
|
+
{
|
|
163
|
+
zigbeeModel: ['RB 279 T'],
|
|
164
|
+
model: 'RB 279 T',
|
|
165
|
+
vendor: 'Innr',
|
|
166
|
+
description: 'Smart bulb tunable white E27',
|
|
167
|
+
extend: extend.light_onoff_brightness_colortemp({colorTempRange: [153, 555]}),
|
|
168
|
+
meta: {applyRedFix: true, turnsOffAtBrightness1: true},
|
|
169
|
+
},
|
|
147
170
|
{
|
|
148
171
|
zigbeeModel: ['RB 285 C'],
|
|
149
172
|
model: 'RB 285 C',
|
package/devices/lonsonho.js
CHANGED
|
@@ -84,7 +84,10 @@ module.exports = [
|
|
|
84
84
|
exposes: [
|
|
85
85
|
exposes.enum('moving', ea.STATE, ['UP', 'STOP', 'DOWN']).withEndpoint('left'),
|
|
86
86
|
exposes.enum('moving', ea.STATE, ['UP', 'STOP', 'DOWN']).withEndpoint('right'),
|
|
87
|
-
exposes.numeric('calibration_time', ea.STATE).withUnit('S').withDescription('Calibration time')
|
|
87
|
+
exposes.numeric('calibration_time', ea.STATE).withUnit('S').withDescription('Calibration time')
|
|
88
|
+
.withEndpoint('left'),
|
|
89
|
+
exposes.numeric('calibration_time', ea.STATE).withUnit('S').withDescription('Calibration time')
|
|
90
|
+
.withEndpoint('right'),
|
|
88
91
|
e.cover_position().withEndpoint('left'), exposes.binary('calibration', ea.ALL, 'ON', 'OFF')
|
|
89
92
|
.withEndpoint('left'), exposes.binary('motor_reversal', ea.ALL, 'ON', 'OFF').withEndpoint('left'),
|
|
90
93
|
e.cover_position().withEndpoint('right'), exposes.binary('calibration', ea.ALL, 'ON', 'OFF')
|
package/devices/moes.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const exposes = require('../lib/exposes');
|
|
2
|
-
const fz =
|
|
2
|
+
const fz = require('../converters/fromZigbee');
|
|
3
3
|
const tz = require('../converters/toZigbee');
|
|
4
4
|
const ota = require('../lib/ota');
|
|
5
5
|
const tuya = require('../lib/tuya');
|
|
@@ -26,11 +26,13 @@ module.exports = [
|
|
|
26
26
|
model: 'ZP-LZ-FR2U',
|
|
27
27
|
vendor: 'Moes',
|
|
28
28
|
description: 'Zigbee 3.0 dual USB wireless socket plug',
|
|
29
|
-
fromZigbee: [fz.on_off, fz.tuya_switch_power_outage_memory, fz.ts011f_plug_child_mode],
|
|
30
|
-
toZigbee: [tz.on_off, tz.tuya_switch_power_outage_memory, tz.ts011f_plug_child_mode],
|
|
29
|
+
fromZigbee: [fz.on_off, fz.tuya_switch_power_outage_memory, fz.ts011f_plug_indicator_mode, fz.ts011f_plug_child_mode],
|
|
30
|
+
toZigbee: [tz.on_off, tz.tuya_switch_power_outage_memory, tz.ts011f_plug_indicator_mode, tz.ts011f_plug_child_mode],
|
|
31
31
|
exposes: [e.switch().withEndpoint('l1'), e.switch().withEndpoint('l2'),
|
|
32
32
|
exposes.enum('power_outage_memory', ea.ALL, ['on', 'off', 'restore'])
|
|
33
|
-
.withDescription('Recover state after power outage'),
|
|
33
|
+
.withDescription('Recover state after power outage'),
|
|
34
|
+
exposes.enum('indicator_mode', ea.ALL, ['off', 'off/on', 'on/off', 'on'])
|
|
35
|
+
.withDescription('Plug LED indicator mode'), e.child_lock()],
|
|
34
36
|
endpoint: (device) => {
|
|
35
37
|
return {'l1': 1, 'l2': 2};
|
|
36
38
|
},
|
package/devices/osram.js
CHANGED
|
@@ -227,7 +227,7 @@ module.exports = [
|
|
|
227
227
|
model: '4052899926110',
|
|
228
228
|
vendor: 'OSRAM',
|
|
229
229
|
description: 'Flex RGBW',
|
|
230
|
-
extend: extend.ledvance.light_onoff_brightness_colortemp_color({colorTempRange: [125, 666], supportsHS: true
|
|
230
|
+
extend: extend.ledvance.light_onoff_brightness_colortemp_color({colorTempRange: [125, 666], supportsHS: true}),
|
|
231
231
|
ota: ota.ledvance,
|
|
232
232
|
},
|
|
233
233
|
{
|
|
@@ -458,4 +458,12 @@ module.exports = [
|
|
|
458
458
|
}
|
|
459
459
|
},
|
|
460
460
|
},
|
|
461
|
+
{
|
|
462
|
+
zigbeeModel: ['LIGHTIFY Under Cabinet TW'],
|
|
463
|
+
model: '71150',
|
|
464
|
+
vendor: 'OSRAM',
|
|
465
|
+
description: 'Lightify under cabinet tunable white',
|
|
466
|
+
extend: extend.ledvance.light_onoff_brightness_colortemp({colorTempRange: [153, 370]}),
|
|
467
|
+
ota: ota.ledvance,
|
|
468
|
+
},
|
|
461
469
|
];
|
package/devices/philips.js
CHANGED
|
@@ -813,7 +813,7 @@ module.exports = [
|
|
|
813
813
|
extend: hueExtend.light_onoff_brightness(),
|
|
814
814
|
},
|
|
815
815
|
{
|
|
816
|
-
zigbeeModel: ['LTA001', '4080130P6'],
|
|
816
|
+
zigbeeModel: ['LTA001', '4080130P6', '4300631P6'],
|
|
817
817
|
model: '9290022169',
|
|
818
818
|
vendor: 'Philips',
|
|
819
819
|
description: 'Hue white ambiance E27 with Bluetooth',
|
|
@@ -932,7 +932,7 @@ module.exports = [
|
|
|
932
932
|
extend: hueExtend.light_onoff_brightness_colortemp_color(),
|
|
933
933
|
},
|
|
934
934
|
{
|
|
935
|
-
zigbeeModel: ['LCL001'],
|
|
935
|
+
zigbeeModel: ['LCL001', 'LCL006'],
|
|
936
936
|
model: '8718699703424',
|
|
937
937
|
vendor: 'Philips',
|
|
938
938
|
description: 'Hue white and color ambiance LightStrip plus',
|
package/devices/tuya.js
CHANGED
|
@@ -22,7 +22,7 @@ const TS011Fplugs = ['_TZ3000_5f43h46b', '_TZ3000_cphmq0q7', '_TZ3000_dpo1ysak',
|
|
|
22
22
|
'_TZ3000_zloso4jk', '_TZ3000_r6buo8ba', '_TZ3000_iksasdbv', '_TZ3000_idrffznf', '_TZ3000_okaz9tjs', '_TZ3210_q7oryllx',
|
|
23
23
|
'_TZ3000_ss98ec5d', '_TZ3000_gznh2xla', '_TZ3000_hdopuwv6', '_TZ3000_gvn91tmx', '_TZ3000_dksbtrzs', '_TZ3000_b28wrpvx',
|
|
24
24
|
'_TZ3000_aim0ztek', '_TZ3000_mlswgkc3', '_TZ3000_7dndcnnb', '_TZ3000_waho4jtj', '_TZ3000_nmsciidq', '_TZ3000_jtgxgmks',
|
|
25
|
-
'_TZ3000_rdfh8cfs'];
|
|
25
|
+
'_TZ3000_rdfh8cfs', '_TZ3000_yujkchbz'];
|
|
26
26
|
|
|
27
27
|
const tzLocal = {
|
|
28
28
|
SA12IZL_silence_siren: {
|
|
@@ -781,13 +781,23 @@ module.exports = [
|
|
|
781
781
|
exposes: [e.carbon_monoxide(), e.co()],
|
|
782
782
|
},
|
|
783
783
|
{
|
|
784
|
-
fingerprint:
|
|
784
|
+
fingerprint: tuya.fingerprint('TS0601', ['_TZE200_ggev5fsl', '_TZE200_u319yc66']),
|
|
785
785
|
model: 'TS0601_gas_sensor',
|
|
786
786
|
vendor: 'TuYa',
|
|
787
787
|
description: 'gas sensor',
|
|
788
|
-
fromZigbee: [
|
|
789
|
-
toZigbee: [],
|
|
790
|
-
|
|
788
|
+
fromZigbee: [tuya.fzDataPoints],
|
|
789
|
+
toZigbee: [tuya.tzDataPoints],
|
|
790
|
+
configure: tuya.configureMagicPacket,
|
|
791
|
+
exposes: [e.gas(), tuya.exposes.selfTest(), tuya.exposes.selfTestResult(), tuya.exposes.faultAlarm(), tuya.exposes.silence()],
|
|
792
|
+
meta: {
|
|
793
|
+
tuyaDatapoints: [
|
|
794
|
+
[1, 'gas', tuya.valueConverter.true0ElseFalse],
|
|
795
|
+
[8, 'self_test', tuya.valueConverter.raw],
|
|
796
|
+
[9, 'self_test_result', tuya.valueConverter.selfTestResult],
|
|
797
|
+
[11, 'fault_alarm', tuya.valueConverter.trueFalse],
|
|
798
|
+
[16, 'silence', tuya.valueConverter.raw],
|
|
799
|
+
],
|
|
800
|
+
},
|
|
791
801
|
},
|
|
792
802
|
{
|
|
793
803
|
fingerprint: [{modelID: 'TS0001', manufacturerName: '_TZ3000_hktqahrq'}, {manufacturerName: '_TZ3000_hktqahrq'},
|
|
@@ -1069,7 +1079,7 @@ module.exports = [
|
|
|
1069
1079
|
},
|
|
1070
1080
|
},
|
|
1071
1081
|
{
|
|
1072
|
-
fingerprint:
|
|
1082
|
+
fingerprint: tuya.fingerprint('TS0202', ['_TZ3000_mcxw5ehu', '_TZ3040_6ygjfyll']),
|
|
1073
1083
|
model: 'IH012-RT01',
|
|
1074
1084
|
vendor: 'TuYa',
|
|
1075
1085
|
description: 'Motion sensor',
|
|
@@ -2048,16 +2058,16 @@ module.exports = [
|
|
|
2048
2058
|
.withFeature(exposes.text('holidays_schedule', ea.STATE_SET))],
|
|
2049
2059
|
},
|
|
2050
2060
|
{
|
|
2051
|
-
fingerprint: [
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
],
|
|
2061
|
+
fingerprint: tuya.fingerprint('TS0601', [
|
|
2062
|
+
'_TZE200_hue3yfsn', /* model: 'TV02-Zigbee', vendor: 'TuYa' */
|
|
2063
|
+
'_TZE200_e9ba97vf', /* model: 'TV01-ZB', vendor: 'Moes' */
|
|
2064
|
+
'_TZE200_husqqvux', /* model: 'TSL-TRV-TV01ZG', vendor: 'Tesla Smart' */
|
|
2065
|
+
'_TZE200_lnbfnyxd', /* model: 'TSL-TRV-TV01ZG', vendor: 'Tesla Smart' */
|
|
2066
|
+
'_TZE200_lllliz3p', /* model: 'TV02-Zigbee', vendor: 'TuYa' */
|
|
2067
|
+
'_TZE200_mudxchsu', /* model: 'TV05-ZG curve', vendor: 'TuYa' */
|
|
2068
|
+
'_TZE200_7yoranx2', /* model: 'TV01-ZB', vendor: 'Moes' */
|
|
2069
|
+
'_TZE200_kds0pmmv', /* model: 'TV01-ZB', vendor: 'Moes' */
|
|
2070
|
+
]),
|
|
2061
2071
|
model: 'TV02-Zigbee',
|
|
2062
2072
|
vendor: 'TuYa',
|
|
2063
2073
|
description: 'Thermostat radiator valve',
|
|
@@ -2068,30 +2078,49 @@ module.exports = [
|
|
|
2068
2078
|
{vendor: 'Unknown/id3.pl', model: 'GTZ08'},
|
|
2069
2079
|
],
|
|
2070
2080
|
ota: ota.zigbeeOTA,
|
|
2071
|
-
fromZigbee: [
|
|
2072
|
-
toZigbee: [
|
|
2081
|
+
fromZigbee: [tuya.fzDataPoints],
|
|
2082
|
+
toZigbee: [tuya.tzDataPoints],
|
|
2073
2083
|
onEvent: tuya.onEventSetLocalTime,
|
|
2084
|
+
configure: tuya.configureMagicPacket,
|
|
2074
2085
|
exposes: [
|
|
2075
|
-
e.
|
|
2076
|
-
e.comfort_temperature().withValueMin(
|
|
2077
|
-
exposes.climate().
|
|
2078
|
-
|
|
2086
|
+
e.battery(), e.child_lock(), e.open_window(), e.open_window_temperature().withValueMin(5).withValueMax(30),
|
|
2087
|
+
e.comfort_temperature().withValueMin(5).withValueMax(30), e.eco_temperature().withValueMin(5).withValueMax(30),
|
|
2088
|
+
exposes.climate().withSystemMode(['off', 'heat'], ea.STATE_SET, 'When switched to the "off" mode, the device will display ' +
|
|
2089
|
+
'"HS" and the valve will be fully closed. Press the pair button to cancel or switch back to "heat" mode. Battery life ' +
|
|
2090
|
+
'can be prolonged by switching the heating off. After switching to `heat` mode, `preset` will be reset to `auto` and ' +
|
|
2091
|
+
'after changing `preset` to `manual` temperature setpoint will be 20 degrees.').withPreset(['auto', 'manual', 'holiday'],
|
|
2092
|
+
'`auto` uses schedule properties, check them. `manual` allows you to control the device, `holiday` uses ' +
|
|
2093
|
+
'`holiday_start_stop` and `holiday_temperature` properties.').withLocalTemperatureCalibration(-5, 5, 0.1, ea.STATE_SET)
|
|
2094
|
+
.withLocalTemperature(ea.STATE).withSetpoint('current_heating_setpoint', 5, 30, 0.5, ea.STATE_SET),
|
|
2079
2095
|
exposes.numeric('boost_timeset_countdown', ea.STATE_SET).withUnit('second').withDescription('Setting '+
|
|
2080
2096
|
'minimum 0 - maximum 465 seconds boost time. The boost (♨) function is activated. The remaining '+
|
|
2081
2097
|
'time for the function will be counted down in seconds ( 465 to 0 ).').withValueMin(0).withValueMax(465),
|
|
2082
2098
|
exposes.binary('frost_protection', ea.STATE_SET, 'ON', 'OFF').withDescription('When Anti-Freezing function'+
|
|
2083
2099
|
' is activated, the temperature in the house is kept at 8 °C, the device display "AF".press the '+
|
|
2084
2100
|
'pair button to cancel.'),
|
|
2085
|
-
exposes.binary('heating_stop', ea.STATE_SET, 'ON', 'OFF').withDescription('
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2101
|
+
exposes.binary('heating_stop', ea.STATE_SET, 'ON', 'OFF').withDescription('Same as `system_mode`. Left for compatibility.'),
|
|
2102
|
+
exposes.numeric('holiday_temperature', ea.STATE_SET).withUnit('°C').withDescription('Holiday temperature')
|
|
2103
|
+
.withValueMin(5).withValueMax(30),
|
|
2104
|
+
exposes.text('holiday_start_stop', ea.STATE_SET).withDescription('The holiday mode will automatically start ' +
|
|
2105
|
+
'at the set time starting point and run the holiday temperature. Can be defined in the following format: ' +
|
|
2106
|
+
'`startYear/startMonth/startDay startHours:startMinutes | endYear/endMonth/endDay endHours:endMinutes`. ' +
|
|
2107
|
+
'For example: `2022/10/01 16:30 | 2022/10/21 18:10`. After the end of holiday mode, it switches to "auto" ' +
|
|
2108
|
+
'mode and uses schedule.'),
|
|
2109
|
+
exposes.enum('working_day', ea.STATE_SET, ['mon_sun', 'mon_fri+sat+sun', 'separate']).withDescription('`mon_sun` ' +
|
|
2110
|
+
'- schedule for Monday used for each day (define it only for Monday). `mon_fri+sat+sun` - schedule for ' +
|
|
2111
|
+
'workdays used from Monday (define it only for Monday), Saturday and Sunday are defined separately. `separate` ' +
|
|
2112
|
+
'- schedule for each day is defined separately.'),
|
|
2113
|
+
exposes.composite('schedule', 'schedule').withFeature(exposes.enum('week_day', ea.SET, ['monday', 'tuesday',
|
|
2114
|
+
'wednesday', 'thursday', 'friday', 'saturday', 'sunday'])).withFeature(exposes.text('schedule', ea.SET))
|
|
2115
|
+
.withDescription('Schedule will work with "auto" preset. In this mode, the device executes ' +
|
|
2116
|
+
'a preset week programming temperature time and temperature. Before using these properties, check `working_day` ' +
|
|
2117
|
+
'property. Each day can contain up to 10 segments. At least 1 segment should be defined. Different count of segments ' +
|
|
2118
|
+
'can be defined for each day, e.g., 3 segments for Monday, 5 segments for Thursday, etc. It should be defined in the ' +
|
|
2119
|
+
'following format: `hours:minutes/temperature`. Minutes can be only tens, i.e., 00, 10, 20, 30, 40, 50. Segments should ' +
|
|
2120
|
+
'be divided by space symbol. Each day should end with the last segment of 24:00. Examples: `04:00/20 08:30/22 10:10/18 ' +
|
|
2121
|
+
'18:40/24 22:50/19.5`; `06:00/21.5 17:20/26 24:00/18`. The temperature will be set from the beginning/start of one ' +
|
|
2122
|
+
'period and until the next period, e.g., `04:00/20 24:00/22` means that from 00:00 to 04:00 temperature will be 20 ' +
|
|
2123
|
+
'degrees and from 04:00 to 00:00 temperature will be 22 degrees.'),
|
|
2095
2124
|
exposes.text('schedule_monday', ea.STATE),
|
|
2096
2125
|
exposes.text('schedule_tuesday', ea.STATE),
|
|
2097
2126
|
exposes.text('schedule_wednesday', ea.STATE),
|
|
@@ -2099,9 +2128,44 @@ module.exports = [
|
|
|
2099
2128
|
exposes.text('schedule_friday', ea.STATE),
|
|
2100
2129
|
exposes.text('schedule_saturday', ea.STATE),
|
|
2101
2130
|
exposes.text('schedule_sunday', ea.STATE),
|
|
2102
|
-
exposes.binary('online', ea.STATE_SET, 'ON', 'OFF').withDescription('
|
|
2131
|
+
exposes.binary('online', ea.STATE_SET, 'ON', 'OFF').withDescription('Turn on this property to poll current data from the ' +
|
|
2132
|
+
'device. It can be used to periodically fetch a new local temperature since the device doesn\'t update itself. ' +
|
|
2133
|
+
'Setting this property doesn\'t turn on the display.'),
|
|
2103
2134
|
exposes.numeric('error_status', ea.STATE).withDescription('Error status'),
|
|
2104
2135
|
],
|
|
2136
|
+
meta: {
|
|
2137
|
+
tuyaDatapoints: [
|
|
2138
|
+
[2, 'preset', tuya.valueConverterBasic.lookup({'auto': tuya.enum(0), 'manual': tuya.enum(1), 'holiday': tuya.enum(3)})],
|
|
2139
|
+
[8, 'open_window', tuya.valueConverter.onOff],
|
|
2140
|
+
[10, 'frost_protection', tuya.valueConverter.onOff],
|
|
2141
|
+
[16, 'current_heating_setpoint', tuya.valueConverter.divideBy10],
|
|
2142
|
+
[24, 'local_temperature', tuya.valueConverter.divideBy10],
|
|
2143
|
+
[27, 'local_temperature_calibration', tuya.valueConverter.localTempCalibration],
|
|
2144
|
+
[31, 'working_day', tuya.valueConverterBasic.lookup({'mon_sun': tuya.enum(0), 'mon_fri+sat+sun': tuya.enum(1),
|
|
2145
|
+
'separate': tuya.enum(2)})],
|
|
2146
|
+
[32, 'holiday_temperature', tuya.valueConverter.divideBy10],
|
|
2147
|
+
[35, 'battery_low', tuya.valueConverter.true0ElseFalse],
|
|
2148
|
+
[40, 'child_lock', tuya.valueConverter.lockUnlock],
|
|
2149
|
+
[45, 'error_status', tuya.valueConverter.raw],
|
|
2150
|
+
[46, 'holiday_start_stop', tuya.valueConverter.thermostatHolidayStartStop],
|
|
2151
|
+
[101, 'boost_timeset_countdown', tuya.valueConverter.raw],
|
|
2152
|
+
[102, 'open_window_temperature', tuya.valueConverter.divideBy10],
|
|
2153
|
+
[104, 'comfort_temperature', tuya.valueConverter.divideBy10],
|
|
2154
|
+
[105, 'eco_temperature', tuya.valueConverter.divideBy10],
|
|
2155
|
+
[106, 'schedule', tuya.valueConverter.thermostatScheduleDay],
|
|
2156
|
+
[107, null, tuya.valueConverter.TV02SystemMode],
|
|
2157
|
+
[107, 'system_mode', tuya.valueConverterBasic.lookup({'heat': false, 'off': true})],
|
|
2158
|
+
[107, 'heating_stop', tuya.valueConverter.onOff],
|
|
2159
|
+
[115, 'online', tuya.valueConverter.onOffNotStrict],
|
|
2160
|
+
[108, 'schedule_monday', tuya.valueConverter.thermostatScheduleDay],
|
|
2161
|
+
[112, 'schedule_tuesday', tuya.valueConverter.thermostatScheduleDay],
|
|
2162
|
+
[109, 'schedule_wednesday', tuya.valueConverter.thermostatScheduleDay],
|
|
2163
|
+
[113, 'schedule_thursday', tuya.valueConverter.thermostatScheduleDay],
|
|
2164
|
+
[110, 'schedule_friday', tuya.valueConverter.thermostatScheduleDay],
|
|
2165
|
+
[114, 'schedule_saturday', tuya.valueConverter.thermostatScheduleDay],
|
|
2166
|
+
[111, 'schedule_sunday', tuya.valueConverter.thermostatScheduleDay],
|
|
2167
|
+
],
|
|
2168
|
+
},
|
|
2105
2169
|
},
|
|
2106
2170
|
{
|
|
2107
2171
|
fingerprint: [
|
package/devices/vimar.js
CHANGED
|
@@ -48,4 +48,29 @@ module.exports = [
|
|
|
48
48
|
await reporting.bind(endpoint, coordinatorEndpoint, ['genOnOff']);
|
|
49
49
|
},
|
|
50
50
|
},
|
|
51
|
+
{
|
|
52
|
+
zigbeeModel: ['Thermostat_v0.1'],
|
|
53
|
+
model: '02973.B',
|
|
54
|
+
vendor: 'Vimar',
|
|
55
|
+
description: 'Vimar IoT thermostat',
|
|
56
|
+
fromZigbee: [fz.thermostat],
|
|
57
|
+
toZigbee: [
|
|
58
|
+
tz.thermostat_local_temperature,
|
|
59
|
+
tz.thermostat_occupied_heating_setpoint,
|
|
60
|
+
tz.thermostat_occupied_cooling_setpoint,
|
|
61
|
+
tz.thermostat_system_mode,
|
|
62
|
+
],
|
|
63
|
+
exposes: [
|
|
64
|
+
exposes.climate().withSetpoint('occupied_heating_setpoint', 4, 40, 0.1)
|
|
65
|
+
.withSetpoint('occupied_cooling_setpoint', 4, 40, 0.1)
|
|
66
|
+
.withLocalTemperature()
|
|
67
|
+
.withSystemMode(['heat', 'cool']),
|
|
68
|
+
],
|
|
69
|
+
configure: async (device, coordinatorEndpoint, logger) => {
|
|
70
|
+
const endpoint = device.getEndpoint(10);
|
|
71
|
+
const binds = ['genBasic', 'genIdentify', 'hvacThermostat'];
|
|
72
|
+
await reporting.bind(endpoint, coordinatorEndpoint, binds);
|
|
73
|
+
await reporting.thermostatTemperature(endpoint);
|
|
74
|
+
},
|
|
75
|
+
},
|
|
51
76
|
];
|
package/lib/tuya.js
CHANGED
|
@@ -1152,6 +1152,12 @@ const tuyaExposes = {
|
|
|
1152
1152
|
countdown: () => exposes.numeric('countdown', ea.STATE_SET).withValueMin(0).withValueMax(43200).withValueStep(1).withUnit('s')
|
|
1153
1153
|
.withDescription('Countdown to turn device off after a certain time'),
|
|
1154
1154
|
switch: () => e.switch().setAccess('state', ea.STATE_SET),
|
|
1155
|
+
selfTest: () => exposes.binary('self_test', ea.STATE_SET, true, false)
|
|
1156
|
+
.withDescription('Indicates whether the device is being self-tested'),
|
|
1157
|
+
selfTestResult: () => exposes.enum('self_test_result', ea.STATE, ['checking', 'success', 'failure', 'others'])
|
|
1158
|
+
.withDescription('Result of the self-test'),
|
|
1159
|
+
faultAlarm: () => exposes.binary('fault_alarm', ea.STATE, true, false).withDescription('Indicates whether a fault was detected'),
|
|
1160
|
+
silence: () => exposes.binary('silence', ea.STATE_SET, true, false).withDescription('Silence the alarm'),
|
|
1155
1161
|
};
|
|
1156
1162
|
|
|
1157
1163
|
const skip = {
|
|
@@ -1172,6 +1178,27 @@ const fingerprint = (modelID, manufacturerNames) => {
|
|
|
1172
1178
|
});
|
|
1173
1179
|
};
|
|
1174
1180
|
|
|
1181
|
+
class Base {
|
|
1182
|
+
constructor(value) {
|
|
1183
|
+
this.value = value;
|
|
1184
|
+
}
|
|
1185
|
+
|
|
1186
|
+
valueOf() {
|
|
1187
|
+
return this.value;
|
|
1188
|
+
}
|
|
1189
|
+
}
|
|
1190
|
+
class Enum extends Base {
|
|
1191
|
+
constructor(value) {
|
|
1192
|
+
super(value);
|
|
1193
|
+
}
|
|
1194
|
+
}
|
|
1195
|
+
|
|
1196
|
+
class Bitmap extends Base {
|
|
1197
|
+
constructor(value) {
|
|
1198
|
+
super(value);
|
|
1199
|
+
}
|
|
1200
|
+
}
|
|
1201
|
+
|
|
1175
1202
|
const valueConverterBasic = {
|
|
1176
1203
|
lookup: (map) => {
|
|
1177
1204
|
return {
|
|
@@ -1180,7 +1207,7 @@ const valueConverterBasic = {
|
|
|
1180
1207
|
return map[v];
|
|
1181
1208
|
},
|
|
1182
1209
|
from: (v) => {
|
|
1183
|
-
const value = Object.entries(map).find((i) => i[1] === v);
|
|
1210
|
+
const value = Object.entries(map).find((i) => i[1].valueOf() === v);
|
|
1184
1211
|
if (!value) throw new Error(`Value '${v}' is not allowed, expected one of ${Object.values(map)}`);
|
|
1185
1212
|
return value[0];
|
|
1186
1213
|
},
|
|
@@ -1215,10 +1242,141 @@ const valueConverter = {
|
|
|
1215
1242
|
},
|
|
1216
1243
|
},
|
|
1217
1244
|
true0ElseFalse: {from: (v) => v === 0},
|
|
1245
|
+
selfTestResult: valueConverterBasic.lookup({'checking': 0, 'success': 1, 'failure': 2, 'others': 3}),
|
|
1246
|
+
lockUnlock: valueConverterBasic.lookup({'LOCK': true, 'UNLOCK': false}),
|
|
1247
|
+
localTempCalibration: {
|
|
1248
|
+
from: (v) => {
|
|
1249
|
+
if (v > 55) v -= 0x100000000;
|
|
1250
|
+
return v / 10;
|
|
1251
|
+
},
|
|
1252
|
+
to: (v) => {
|
|
1253
|
+
if (v > 0) return v * 10;
|
|
1254
|
+
if (v < 0) return v * 10 + 0x100000000;
|
|
1255
|
+
return v;
|
|
1256
|
+
},
|
|
1257
|
+
},
|
|
1258
|
+
thermostatHolidayStartStop: {
|
|
1259
|
+
from: (v) => {
|
|
1260
|
+
const start = {
|
|
1261
|
+
year: v.slice(0, 4), month: v.slice(4, 6), day: v.slice(6, 8),
|
|
1262
|
+
hours: v.slice(8, 10), minutes: v.slice(10, 12),
|
|
1263
|
+
};
|
|
1264
|
+
const end = {
|
|
1265
|
+
year: v.slice(12, 16), month: v.slice(16, 18), day: v.slice(18, 20),
|
|
1266
|
+
hours: v.slice(20, 22), minutes: v.slice(22, 24),
|
|
1267
|
+
};
|
|
1268
|
+
const startStr = `${start.year}/${start.month}/${start.day} ${start.hours}:${start.minutes}`;
|
|
1269
|
+
const endStr = `${end.year}/${end.month}/${end.day} ${end.hours}:${end.minutes}`;
|
|
1270
|
+
return `${startStr} | ${endStr}`;
|
|
1271
|
+
},
|
|
1272
|
+
to: (v) => {
|
|
1273
|
+
const numberPattern = /\d+/g;
|
|
1274
|
+
return v.match(numberPattern).join([]).toString();
|
|
1275
|
+
},
|
|
1276
|
+
},
|
|
1277
|
+
thermostatScheduleDay: {
|
|
1278
|
+
from: (v) => {
|
|
1279
|
+
// day splitted to 10 min segments = total 144 segments
|
|
1280
|
+
const maxPeriodsInDay = 10;
|
|
1281
|
+
const periodSize = 3;
|
|
1282
|
+
const schedule = [];
|
|
1283
|
+
|
|
1284
|
+
for (let i = 0; i < maxPeriodsInDay; i++) {
|
|
1285
|
+
const time = v[i * periodSize];
|
|
1286
|
+
const totalMinutes = time * 10;
|
|
1287
|
+
const hours = totalMinutes / 60;
|
|
1288
|
+
const rHours = Math.floor(hours);
|
|
1289
|
+
const minutes = (hours - rHours) * 60;
|
|
1290
|
+
const rMinutes = Math.round(minutes);
|
|
1291
|
+
const strHours = rHours.toString().padStart(2, '0');
|
|
1292
|
+
const strMinutes = rMinutes.toString().padStart(2, '0');
|
|
1293
|
+
const temp = parseFloat(v[i * periodSize + 2] / 10);
|
|
1294
|
+
schedule.push(`${strHours}:${strMinutes}/${temp}`);
|
|
1295
|
+
if (rHours === 24) break;
|
|
1296
|
+
}
|
|
1297
|
+
|
|
1298
|
+
return schedule.join(' ');
|
|
1299
|
+
},
|
|
1300
|
+
to: (v, meta) => {
|
|
1301
|
+
const dayByte = {
|
|
1302
|
+
monday: 1, tuesday: 2, wednesday: 4, thursday: 8,
|
|
1303
|
+
friday: 16, saturday: 32, sunday: 64,
|
|
1304
|
+
};
|
|
1305
|
+
const weekDay = v.week_day;
|
|
1306
|
+
if (Object.keys(dayByte).indexOf(weekDay) === -1) {
|
|
1307
|
+
throw new Error('Invalid "week_day" property value: ' + weekDay);
|
|
1308
|
+
}
|
|
1309
|
+
let weekScheduleType;
|
|
1310
|
+
if (meta.state && meta.state.working_day) weekScheduleType = meta.state.working_day;
|
|
1311
|
+
const payload = [];
|
|
1312
|
+
|
|
1313
|
+
switch (weekScheduleType) {
|
|
1314
|
+
case 'mon_sun':
|
|
1315
|
+
payload.push(127);
|
|
1316
|
+
break;
|
|
1317
|
+
case 'mon_fri+sat+sun':
|
|
1318
|
+
if (['saturday', 'sunday'].indexOf(weekDay) === -1) {
|
|
1319
|
+
payload.push(31);
|
|
1320
|
+
break;
|
|
1321
|
+
}
|
|
1322
|
+
payload.push(dayByte[weekDay]);
|
|
1323
|
+
break;
|
|
1324
|
+
case 'separate':
|
|
1325
|
+
payload.push(dayByte[weekDay]);
|
|
1326
|
+
break;
|
|
1327
|
+
default:
|
|
1328
|
+
throw new Error('Invalid "working_day" property, need to set it before');
|
|
1329
|
+
}
|
|
1330
|
+
|
|
1331
|
+
// day splitted to 10 min segments = total 144 segments
|
|
1332
|
+
const maxPeriodsInDay = 10;
|
|
1333
|
+
const schedule = v.schedule.split(' ');
|
|
1334
|
+
const schedulePeriods = schedule.length;
|
|
1335
|
+
if (schedulePeriods > 10) throw new Error('There cannot be more than 10 periods in the schedule: ' + v);
|
|
1336
|
+
if (schedulePeriods < 2) throw new Error('There cannot be less than 2 periods in the schedule: ' + v);
|
|
1337
|
+
let prevHour;
|
|
1338
|
+
|
|
1339
|
+
for (const period of schedule) {
|
|
1340
|
+
const timeTemp = period.split('/');
|
|
1341
|
+
const hm = timeTemp[0].split(':', 2);
|
|
1342
|
+
const h = parseInt(hm[0]);
|
|
1343
|
+
const m = parseInt(hm[1]);
|
|
1344
|
+
const temp = parseFloat(timeTemp[1]);
|
|
1345
|
+
if (h < 0 || h > 24 || m < 0 || m >= 60 || m % 10 !== 0 || temp < 5 || temp > 30 || temp % 0.5 !== 0) {
|
|
1346
|
+
throw new Error('Invalid hour, minute or temperature of: ' + period);
|
|
1347
|
+
} else if (prevHour > h) {
|
|
1348
|
+
throw new Error(`The hour of the next segment can't be less than the previous one: ${prevHour} > ${h}`);
|
|
1349
|
+
}
|
|
1350
|
+
prevHour = h;
|
|
1351
|
+
const segment = (h * 60 + m) / 10;
|
|
1352
|
+
payload.push(segment, 0, temp * 10);
|
|
1353
|
+
}
|
|
1354
|
+
|
|
1355
|
+
// Add "technical" periods to be valid payload
|
|
1356
|
+
for (let i = 0; i < maxPeriodsInDay - schedulePeriods; i++) {
|
|
1357
|
+
// by default it sends 9000b2, it's 24 hours and 18 degrees
|
|
1358
|
+
payload.push(144, 0, 180);
|
|
1359
|
+
}
|
|
1360
|
+
|
|
1361
|
+
return payload;
|
|
1362
|
+
},
|
|
1363
|
+
},
|
|
1364
|
+
TV02SystemMode: {
|
|
1365
|
+
from: (v) => {
|
|
1366
|
+
return {system_mode: v === false ? 'heat' : 'off', heating_stop: v === false ? 'OFF' : 'ON'};
|
|
1367
|
+
},
|
|
1368
|
+
},
|
|
1369
|
+
onOffNotStrict: {from: (v) => v ? 'ON' : 'OFF', to: (v) => v === 'ON'},
|
|
1218
1370
|
};
|
|
1219
1371
|
|
|
1220
1372
|
const tzDataPoints = {
|
|
1221
|
-
key: [
|
|
1373
|
+
key: [
|
|
1374
|
+
'state', 'brightness', 'min_brightness', 'max_brightness', 'power_on_behavior',
|
|
1375
|
+
'countdown', 'light_type', 'silence', 'self_test', 'child_lock', 'open_window', 'open_window_temperature', 'frost_protection',
|
|
1376
|
+
'system_mode', 'heating_stop', 'current_heating_setpoint', 'local_temperature_calibration', 'preset', 'boost_timeset_countdown',
|
|
1377
|
+
'holiday_start_stop', 'holiday_temperature', 'comfort_temperature', 'eco_temperature', 'working_day', 'week_schedule_programming',
|
|
1378
|
+
'online', 'holiday_mode_date', 'schedule',
|
|
1379
|
+
],
|
|
1222
1380
|
convertSet: async (entity, key, value, meta) => {
|
|
1223
1381
|
// A set converter is only called once; therefore we need to loop
|
|
1224
1382
|
const state = {};
|
|
@@ -1232,11 +1390,19 @@ const tzDataPoints = {
|
|
|
1232
1390
|
}
|
|
1233
1391
|
if (dpEntry[3] && dpEntry[3].skip && dpEntry[3].skip(meta)) continue;
|
|
1234
1392
|
const dpId = dpEntry[0];
|
|
1235
|
-
const convertedValue = dpEntry[2].to(value);
|
|
1393
|
+
const convertedValue = dpEntry[2].to(value, meta);
|
|
1236
1394
|
if (typeof convertedValue === 'boolean') {
|
|
1237
1395
|
await sendDataPointBool(entity, dpId, convertedValue, 'dataRequest', 1);
|
|
1238
1396
|
} else if (typeof convertedValue === 'number') {
|
|
1239
1397
|
await sendDataPointValue(entity, dpId, convertedValue, 'dataRequest', 1);
|
|
1398
|
+
} else if (typeof convertedValue === 'string') {
|
|
1399
|
+
await sendDataPointStringBuffer(entity, dpId, convertedValue, 'dataRequest', 1);
|
|
1400
|
+
} else if (Array.isArray(convertedValue)) {
|
|
1401
|
+
await sendDataPointRaw(entity, dpId, convertedValue, 'dataRequest', 1);
|
|
1402
|
+
} else if (convertedValue instanceof Enum) {
|
|
1403
|
+
await sendDataPointEnum(entity, dpId, convertedValue.valueOf(), 'dataRequest', 1);
|
|
1404
|
+
} else if (convertedValue instanceof Bitmap) {
|
|
1405
|
+
await sendDataPointBitmap(entity, dpId, convertedValue.valueOf(), 'dataRequest', 1);
|
|
1240
1406
|
} else {
|
|
1241
1407
|
throw new Error(`Don't know how to send type '${typeof convertedValue}'`);
|
|
1242
1408
|
}
|
|
@@ -1259,9 +1425,9 @@ const fzDataPoints = {
|
|
|
1259
1425
|
if (dpEntry) {
|
|
1260
1426
|
const value = getDataValue(dpValue);
|
|
1261
1427
|
if (dpEntry[1]) {
|
|
1262
|
-
result[dpEntry[1]] = dpEntry[2].from(value);
|
|
1428
|
+
result[dpEntry[1]] = dpEntry[2].from(value, meta);
|
|
1263
1429
|
} else if (dpEntry[2]) {
|
|
1264
|
-
result = {...result, ...dpEntry[2].from(value)};
|
|
1430
|
+
result = {...result, ...dpEntry[2].from(value, meta)};
|
|
1265
1431
|
}
|
|
1266
1432
|
} else {
|
|
1267
1433
|
meta.logger.warn(`Datapoint ${dpId} not defined for '${meta.device.manufacturerName}' ` +
|
|
@@ -1277,6 +1443,8 @@ module.exports = {
|
|
|
1277
1443
|
skip,
|
|
1278
1444
|
configureMagicPacket,
|
|
1279
1445
|
fingerprint,
|
|
1446
|
+
enum: (value) => new Enum(value),
|
|
1447
|
+
bitmap: (value) => new Bitmap(value),
|
|
1280
1448
|
valueConverter,
|
|
1281
1449
|
valueConverterBasic,
|
|
1282
1450
|
tzDataPoints,
|