zigbee-herdsman-converters 14.0.512 → 14.0.515
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 +111 -2
- package/converters/toZigbee.js +96 -3
- package/devices/easyaccess.js +1 -0
- package/devices/ikea.js +4 -1
- package/devices/inovelli.js +1225 -0
- package/devices/lellki.js +6 -25
- package/devices/mecrator.js +41 -0
- package/devices/moes.js +12 -1
- package/devices/namron.js +1 -1
- package/devices/onesti.js +1 -1
- package/devices/prolight.js +5 -2
- package/devices/tuya.js +26 -8
- package/devices/useelink.js +2 -0
- package/devices/xiaomi.js +11 -22
- package/lib/ota/index.js +1 -0
- package/lib/ota/inovelli.js +72 -0
- package/lib/tuya.js +9 -0
- package/lib/xiaomi.js +1 -1
- package/package.json +2 -2
package/converters/fromZigbee.js
CHANGED
|
@@ -532,6 +532,24 @@ const converters = {
|
|
|
532
532
|
}
|
|
533
533
|
}
|
|
534
534
|
|
|
535
|
+
// onLevel - range 0x00 to 0xff - optional
|
|
536
|
+
// Any value outside of MinLevel to MaxLevel, including 0xff and 0x00, is interpreted as "previous".
|
|
537
|
+
if (msg.data.hasOwnProperty('onLevel') && (msg.data['onLevel'] !== undefined)) {
|
|
538
|
+
result.level_config.on_level = Number(msg.data['onLevel']);
|
|
539
|
+
if (result.level_config.on_level === 255) {
|
|
540
|
+
result.level_config.on_level = 'previous';
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
// options - 8-bit map
|
|
545
|
+
// bit 0: ExecuteIfOff - when 0, Move commands are ignored if the device is off;
|
|
546
|
+
// when 1, CurrentLevel can be changed while the device is off.
|
|
547
|
+
// bit 1: CoupleColorTempToLevel - when 1, changes to level also change color temperature.
|
|
548
|
+
// (What this means is not defined, but it's most likely to be "dim to warm".)
|
|
549
|
+
if (msg.data.hasOwnProperty('options') && msg.data['options'] !== undefined) {
|
|
550
|
+
result.level_config.execute_if_off = !!(Number(msg.data['options']) & 1);
|
|
551
|
+
}
|
|
552
|
+
|
|
535
553
|
if (Object.keys(result.level_config).length > 0) {
|
|
536
554
|
return result;
|
|
537
555
|
}
|
|
@@ -1699,7 +1717,7 @@ const converters = {
|
|
|
1699
1717
|
result.hysterersis = precisionRound(data[0x100A], 2) / 10;
|
|
1700
1718
|
}
|
|
1701
1719
|
if (data.hasOwnProperty(0x100B)) { // DisplayAutoOffEnable
|
|
1702
|
-
const lookup = {0: '
|
|
1720
|
+
const lookup = {0: 'enabled', 1: 'disabled'};
|
|
1703
1721
|
result.display_auto_off_enabled = lookup[data[0x100B]];
|
|
1704
1722
|
}
|
|
1705
1723
|
if (data.hasOwnProperty(0x2001)) { // AlarmAirTempOverValue
|
|
@@ -4341,7 +4359,8 @@ const converters = {
|
|
|
4341
4359
|
// DP2: Smart Air Box: co2, Smart Air Housekeeper: MP25
|
|
4342
4360
|
case tuya.dataPoints.tuyaSabCO2:
|
|
4343
4361
|
if (meta.device.manufacturerName === '_TZE200_dwcarsat') {
|
|
4344
|
-
|
|
4362
|
+
// Ignore: https://github.com/Koenkk/zigbee2mqtt/issues/11033#issuecomment-1109808552
|
|
4363
|
+
if (value === 0xaaac || value === 0xaaab) return;
|
|
4345
4364
|
return {pm25: calibrateAndPrecisionRoundOptions(value, options, 'pm25')};
|
|
4346
4365
|
} else if (meta.device.manufacturerName === '_TZE200_ryfmq5rl') {
|
|
4347
4366
|
return {formaldehyd: calibrateAndPrecisionRoundOptions(value, options, 'formaldehyd') / 100};
|
|
@@ -5909,6 +5928,29 @@ const converters = {
|
|
|
5909
5928
|
return result;
|
|
5910
5929
|
},
|
|
5911
5930
|
},
|
|
5931
|
+
xiaomi_curtain_hagl04_status: {
|
|
5932
|
+
cluster: 'genMultistateOutput',
|
|
5933
|
+
type: ['attributeReport'],
|
|
5934
|
+
convert: (model, msg, publish, options, meta) => {
|
|
5935
|
+
let running = false;
|
|
5936
|
+
const data = msg.data;
|
|
5937
|
+
const lookup = {
|
|
5938
|
+
0: 'closing',
|
|
5939
|
+
1: 'opening',
|
|
5940
|
+
2: 'stop',
|
|
5941
|
+
};
|
|
5942
|
+
if (data && data.hasOwnProperty('presentValue')) {
|
|
5943
|
+
const value = data['presentValue'];
|
|
5944
|
+
if (value < 2) {
|
|
5945
|
+
running = true;
|
|
5946
|
+
}
|
|
5947
|
+
return {
|
|
5948
|
+
motor_state: lookup[value],
|
|
5949
|
+
running: running,
|
|
5950
|
+
};
|
|
5951
|
+
}
|
|
5952
|
+
},
|
|
5953
|
+
},
|
|
5912
5954
|
xiaomi_curtain_acn002_status: {
|
|
5913
5955
|
cluster: 'genMultistateOutput',
|
|
5914
5956
|
type: ['attributeReport'],
|
|
@@ -8496,6 +8538,73 @@ const converters = {
|
|
|
8496
8538
|
return result;
|
|
8497
8539
|
},
|
|
8498
8540
|
},
|
|
8541
|
+
ZG204ZL_lms: {
|
|
8542
|
+
cluster: 'manuSpecificTuya',
|
|
8543
|
+
type: ['commandDataResponse', 'commandDataReport'],
|
|
8544
|
+
convert: (model, msg, publish, options, meta) => {
|
|
8545
|
+
const result = {};
|
|
8546
|
+
for (const dpValue of msg.data.dpValues) {
|
|
8547
|
+
const dp = dpValue.dp;
|
|
8548
|
+
const value = tuya.getDataValue(dpValue);
|
|
8549
|
+
switch (dp) {
|
|
8550
|
+
case tuya.dataPoints.lmsState:
|
|
8551
|
+
result.occupancy = (value === 0);
|
|
8552
|
+
break;
|
|
8553
|
+
case tuya.dataPoints.lmsBattery:
|
|
8554
|
+
result.battery = value;
|
|
8555
|
+
break;
|
|
8556
|
+
case tuya.dataPoints.lmsSensitivity:
|
|
8557
|
+
result.sensitivity = {'0': 'low', '1': 'medium', '2': 'high'}[value];
|
|
8558
|
+
break;
|
|
8559
|
+
case tuya.dataPoints.lmsKeepTime:
|
|
8560
|
+
result.keep_time = {'0': '10', '1': '30', '2': '60', '3': '120'}[value];
|
|
8561
|
+
break;
|
|
8562
|
+
case tuya.dataPoints.lmsIlluminance:
|
|
8563
|
+
result.illuminance = value;
|
|
8564
|
+
break;
|
|
8565
|
+
default:
|
|
8566
|
+
meta.logger.warn(`zigbee-herdsman-converters:ZG204ZL_lms: NOT RECOGNIZED DP #${
|
|
8567
|
+
dp} with data ${JSON.stringify(dpValue)}`);
|
|
8568
|
+
}
|
|
8569
|
+
}
|
|
8570
|
+
return result;
|
|
8571
|
+
},
|
|
8572
|
+
},
|
|
8573
|
+
moes_cover: {
|
|
8574
|
+
cluster: 'manuSpecificTuya',
|
|
8575
|
+
type: ['commandDataResponse', 'commandDataReport'],
|
|
8576
|
+
options: [exposes.options.invert_cover()],
|
|
8577
|
+
convert: (model, msg, publish, options, meta) => {
|
|
8578
|
+
const dpValue = tuya.firstDpValue(msg, meta, 'moes_cover');
|
|
8579
|
+
const dp = dpValue.dp;
|
|
8580
|
+
const value = tuya.getDataValue(dpValue);
|
|
8581
|
+
let result = null;
|
|
8582
|
+
switch (dp) {
|
|
8583
|
+
case tuya.dataPoints.coverPosition: {
|
|
8584
|
+
const invert = !tuya.isCoverInverted(meta.device.manufacturerName) ?
|
|
8585
|
+
!options.invert_cover : options.invert_cover;
|
|
8586
|
+
const position = invert ? 100 - value : value;
|
|
8587
|
+
result = {position: position};
|
|
8588
|
+
break;
|
|
8589
|
+
}
|
|
8590
|
+
case tuya.dataPoints.state:
|
|
8591
|
+
result = {state: {0: 'OPEN', 1: 'STOP', 2: 'CLOSE'}[value], running: {0: true, 1: false, 2: true}[value]};
|
|
8592
|
+
break;
|
|
8593
|
+
case tuya.dataPoints.moesCoverBacklight:
|
|
8594
|
+
result = {backlight: {false: 'OFF', true: 'ON'}[value]};
|
|
8595
|
+
break;
|
|
8596
|
+
case tuya.dataPoints.moesCoverCalibration:
|
|
8597
|
+
result = {calibration: {0: 'ON', 1: 'OFF'}[value]};
|
|
8598
|
+
break;
|
|
8599
|
+
case tuya.dataPoints.moesCoverMotorReversal:
|
|
8600
|
+
result = {motor_reversal: {0: 'OFF', 1: 'ON'}[value]};
|
|
8601
|
+
break;
|
|
8602
|
+
default:
|
|
8603
|
+
meta.logger.warn(`fromZigbee.moes_cover: NOT RECOGNIZED DP ${dp} with data ${JSON.stringify(dpValue)}`);
|
|
8604
|
+
}
|
|
8605
|
+
return result;
|
|
8606
|
+
},
|
|
8607
|
+
},
|
|
8499
8608
|
// #endregion
|
|
8500
8609
|
|
|
8501
8610
|
// #region Ignore converters (these message dont need parsing).
|
package/converters/toZigbee.js
CHANGED
|
@@ -535,12 +535,41 @@ const converters = {
|
|
|
535
535
|
Object.assign(state, {current_level_startup: startUpCurrentLevelValue});
|
|
536
536
|
}
|
|
537
537
|
|
|
538
|
+
// onLevel - range 0x00 to 0xff - optional
|
|
539
|
+
// Any value outside of MinLevel to MaxLevel, including 0xff and 0x00, is interpreted as "previous".
|
|
540
|
+
if (value.hasOwnProperty('on_level')) {
|
|
541
|
+
let onLevel = value.on_level;
|
|
542
|
+
if (typeof onLevel === 'string' && onLevel.toLowerCase() == 'previous') {
|
|
543
|
+
onLevel = 255;
|
|
544
|
+
} else {
|
|
545
|
+
onLevel = Number(onLevel);
|
|
546
|
+
}
|
|
547
|
+
if (onLevel > 255) onLevel = 254;
|
|
548
|
+
if (onLevel < 1) onLevel = 1;
|
|
549
|
+
await entity.write('genLevelCtrl', {onLevel}, utils.getOptions(meta.mapped, entity));
|
|
550
|
+
Object.assign(state, {on_level: onLevel == 255 ? 'previous' : onLevel});
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
// options - 8-bit map
|
|
554
|
+
// bit 0: ExecuteIfOff - when 0, Move commands are ignored if the device is off;
|
|
555
|
+
// when 1, CurrentLevel can be changed while the device is off.
|
|
556
|
+
// bit 1: CoupleColorTempToLevel - when 1, changes to level also change color temperature.
|
|
557
|
+
// (What this means is not defined, but it's most likely to be "dim to warm".)
|
|
558
|
+
if (value.hasOwnProperty('execute_if_off')) {
|
|
559
|
+
const executeIfOffValue = !!value.execute_if_off;
|
|
560
|
+
await entity.write('genLevelCtrl', {options: executeIfOffValue ? 1 : 0}, utils.getOptions(meta.mapped, entity));
|
|
561
|
+
Object.assign(state, {execute_if_off: executeIfOffValue});
|
|
562
|
+
}
|
|
563
|
+
|
|
538
564
|
if (Object.keys(state).length > 0) {
|
|
539
565
|
return {state: {level_config: state}};
|
|
540
566
|
}
|
|
541
567
|
},
|
|
542
568
|
convertGet: async (entity, key, meta) => {
|
|
543
|
-
for (const attribute of [
|
|
569
|
+
for (const attribute of [
|
|
570
|
+
'onOffTransitionTime', 'onTransitionTime', 'offTransitionTime', 'startUpCurrentLevel',
|
|
571
|
+
'onLevel', 'options',
|
|
572
|
+
]) {
|
|
544
573
|
try {
|
|
545
574
|
await entity.read('genLevelCtrl', [attribute]);
|
|
546
575
|
} catch (ex) {
|
|
@@ -3092,10 +3121,10 @@ const converters = {
|
|
|
3092
3121
|
const payload = {0x1009: {value: value * 2, type: 0x20}};
|
|
3093
3122
|
await entity.write('hvacThermostat', payload, manufacturerOptions.sunricher);
|
|
3094
3123
|
} else if (key==='hysterersis') {
|
|
3095
|
-
const payload = {0x100A: {value: value* 10, type: 0x20}};
|
|
3124
|
+
const payload = {0x100A: {value: value * 10, type: 0x20}};
|
|
3096
3125
|
await entity.write('hvacThermostat', payload, manufacturerOptions.sunricher);
|
|
3097
3126
|
} else if (key==='display_auto_off_enabled') {
|
|
3098
|
-
const lookup = {'
|
|
3127
|
+
const lookup = {'enabled': 0, 'disabled': 1};
|
|
3099
3128
|
const payload = {0x100B: {value: lookup[value], type: herdsman.Zcl.DataType.enum8}};
|
|
3100
3129
|
await entity.write('hvacThermostat', payload, manufacturerOptions.sunricher);
|
|
3101
3130
|
} else if (key==='alarm_airtemp_overvalue') {
|
|
@@ -7436,6 +7465,70 @@ const converters = {
|
|
|
7436
7465
|
}
|
|
7437
7466
|
},
|
|
7438
7467
|
},
|
|
7468
|
+
ZG204ZL_lms: {
|
|
7469
|
+
key: ['sensitivity', 'keep_time'],
|
|
7470
|
+
convertSet: async (entity, key, value, meta) => {
|
|
7471
|
+
switch (key) {
|
|
7472
|
+
case 'sensitivity':
|
|
7473
|
+
await tuya.sendDataPointEnum(entity, tuya.dataPoints.lmsSensitivity, {'low': 0, 'medium': 1, 'high': 2}[value]);
|
|
7474
|
+
break;
|
|
7475
|
+
case 'keep_time':
|
|
7476
|
+
await tuya.sendDataPointEnum(entity, tuya.dataPoints.lmsKeepTime, {'10': 0, '30': 1, '60': 2, '120': 3}[value]);
|
|
7477
|
+
break;
|
|
7478
|
+
default: // Unknown key
|
|
7479
|
+
meta.logger.warn(`tz.ZG204ZL_lms: Unhandled key ${key}`);
|
|
7480
|
+
}
|
|
7481
|
+
},
|
|
7482
|
+
convertGet: async (entity, key, meta) => {
|
|
7483
|
+
switch (key) {
|
|
7484
|
+
case 'sensitivity':
|
|
7485
|
+
await tuya.sendDataPointEnum(entity, tuya.dataPoints.lmsSensitivity, 0, 'dataQuery' );
|
|
7486
|
+
break;
|
|
7487
|
+
case 'keep_time':
|
|
7488
|
+
await tuya.sendDataPointEnum(entity, tuya.dataPoints.lmsKeepTime, 0, 'dataQuery' );
|
|
7489
|
+
break;
|
|
7490
|
+
default: // Unknown key
|
|
7491
|
+
meta.logger.warn(`Unhandled key toZigbee.ZG204ZL_lms.convertGet ${key}`);
|
|
7492
|
+
}
|
|
7493
|
+
},
|
|
7494
|
+
},
|
|
7495
|
+
moes_cover: {
|
|
7496
|
+
key: ['backlight', 'calibration', 'motor_reversal', 'state', 'position'],
|
|
7497
|
+
options: [exposes.options.invert_cover()],
|
|
7498
|
+
convertSet: async (entity, key, value, meta) => {
|
|
7499
|
+
switch (key) {
|
|
7500
|
+
case 'position':
|
|
7501
|
+
if (value >= 0 && value <= 100) {
|
|
7502
|
+
const invert = !tuya.isCoverInverted(meta.device.manufacturerName) ?
|
|
7503
|
+
!meta.options.invert_cover : meta.options.invert_cover;
|
|
7504
|
+
const position = invert ? 100 - value : value;
|
|
7505
|
+
await tuya.sendDataPointValue(entity, tuya.dataPoints.coverPosition, position);
|
|
7506
|
+
return {position: value};
|
|
7507
|
+
}
|
|
7508
|
+
break;
|
|
7509
|
+
case 'state': {
|
|
7510
|
+
const state = {'OPEN': 0, 'STOP': 1, 'CLOSE': 2}[value.toUpperCase()];
|
|
7511
|
+
await tuya.sendDataPointEnum(entity, tuya.dataPoints.state, state);
|
|
7512
|
+
break;
|
|
7513
|
+
}
|
|
7514
|
+
case 'backlight': {
|
|
7515
|
+
const backlight = value.toUpperCase() === 'ON' ? true : false;
|
|
7516
|
+
await tuya.sendDataPointBool(entity, tuya.dataPoints.moesCoverBacklight, backlight);
|
|
7517
|
+
return {backlight: value};
|
|
7518
|
+
}
|
|
7519
|
+
case 'calibration': {
|
|
7520
|
+
const calibration = value.toUpperCase() === 'ON' ? 0 : 1;
|
|
7521
|
+
await tuya.sendDataPointEnum(entity, tuya.dataPoints.moesCoverCalibration, calibration);
|
|
7522
|
+
break;
|
|
7523
|
+
}
|
|
7524
|
+
case 'motor_reversal': {
|
|
7525
|
+
const motorReversal = value.toUpperCase() === 'ON' ? 1 : 0;
|
|
7526
|
+
await tuya.sendDataPointEnum(entity, tuya.dataPoints.moesCoverMotorReversal, motorReversal);
|
|
7527
|
+
return {motor_reversal: value};
|
|
7528
|
+
}
|
|
7529
|
+
}
|
|
7530
|
+
},
|
|
7531
|
+
},
|
|
7439
7532
|
// #endregion
|
|
7440
7533
|
|
|
7441
7534
|
// #region Ignore converters
|
package/devices/easyaccess.js
CHANGED
|
@@ -22,5 +22,6 @@ module.exports = [
|
|
|
22
22
|
exposes: [e.lock(), e.battery(), e.sound_volume(),
|
|
23
23
|
e.action(['zigbee_unlock', 'lock', 'rfid_unlock', 'keypad_unlock']),
|
|
24
24
|
exposes.binary('auto_relock', ea.STATE_SET, true, false).withDescription('Auto relock after 7 seconds.')],
|
|
25
|
+
whiteLabel: [{vendor: 'Datek Wireless', model: 'EasyCode903G2.1'}],
|
|
25
26
|
},
|
|
26
27
|
];
|
package/devices/ikea.js
CHANGED
|
@@ -36,9 +36,12 @@ const bulbOnEvent = async (type, data, device, options, state) => {
|
|
|
36
36
|
|
|
37
37
|
// NOTE: execute_if_off default is false
|
|
38
38
|
// we only restore if true, to save unneeded network writes
|
|
39
|
-
if (state.color_options.execute_if_off === true) {
|
|
39
|
+
if (state.color_options !== undefined && state.color_options.execute_if_off === true) {
|
|
40
40
|
device.endpoints[0].write('lightingColorCtrl', {'options': 1});
|
|
41
41
|
}
|
|
42
|
+
if (state.level_config !== undefined && state.level_config.execute_if_off === true) {
|
|
43
|
+
device.endpoints[0].write('genLevelCtrl', {'options': 1});
|
|
44
|
+
}
|
|
42
45
|
}
|
|
43
46
|
};
|
|
44
47
|
|