zigbee-herdsman-converters 15.0.71 → 15.0.72
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/devices/gs.js +1 -1
- package/devices/heiman.js +2 -2
- package/devices/lidl.js +8 -4
- package/devices/lixee.js +211 -123
- package/devices/philips.js +1 -1
- package/devices/qoto.js +1 -1
- package/package.json +1 -1
package/devices/gs.js
CHANGED
package/devices/heiman.js
CHANGED
|
@@ -395,9 +395,9 @@ module.exports = [
|
|
|
395
395
|
model: 'SGMHM-I1',
|
|
396
396
|
vendor: 'HEIMAN',
|
|
397
397
|
description: 'Combustible gas sensor',
|
|
398
|
-
fromZigbee: [fz.
|
|
398
|
+
fromZigbee: [fz.ias_gas_alarm_2],
|
|
399
399
|
toZigbee: [],
|
|
400
|
-
exposes: [e.gas()
|
|
400
|
+
exposes: [e.gas()],
|
|
401
401
|
},
|
|
402
402
|
{
|
|
403
403
|
zigbeeModel: ['STHM-I1H'],
|
package/devices/lidl.js
CHANGED
|
@@ -193,8 +193,8 @@ const tzLocal = {
|
|
|
193
193
|
// Load current state or defaults
|
|
194
194
|
const newSettings = {
|
|
195
195
|
brightness: meta.state.brightness ?? 254, // full brightness
|
|
196
|
-
hue: (meta.state.color ?? {}).
|
|
197
|
-
saturation: (meta.state.color ?? {}).
|
|
196
|
+
hue: (meta.state.color ?? {}).hue ?? 0, // red
|
|
197
|
+
saturation: (meta.state.color ?? {}).saturation ?? 100, // full saturation
|
|
198
198
|
};
|
|
199
199
|
|
|
200
200
|
// Apply changes
|
|
@@ -217,8 +217,8 @@ const tzLocal = {
|
|
|
217
217
|
newSettings.saturation = color.saturation;
|
|
218
218
|
|
|
219
219
|
newState.color = {
|
|
220
|
-
|
|
221
|
-
|
|
220
|
+
hue: color.hue,
|
|
221
|
+
saturation: color.saturation,
|
|
222
222
|
};
|
|
223
223
|
}
|
|
224
224
|
|
|
@@ -234,6 +234,10 @@ const tzLocal = {
|
|
|
234
234
|
}
|
|
235
235
|
}
|
|
236
236
|
|
|
237
|
+
// If we're in white mode, calculate a matching display color for the set color temperature. This also kind
|
|
238
|
+
// of works in the other direction.
|
|
239
|
+
Object.assign(newState, libColor.syncColorState(newState, meta.state, entity, meta.options, meta.logger));
|
|
240
|
+
|
|
237
241
|
return {state: newState};
|
|
238
242
|
},
|
|
239
243
|
convertGet: async (entity, key, meta) => {
|
package/devices/lixee.js
CHANGED
|
@@ -11,6 +11,73 @@ const e = exposes.presets;
|
|
|
11
11
|
const utils = require('../lib/utils');
|
|
12
12
|
const ota = require('../lib/ota');
|
|
13
13
|
const {Buffer} = require('buffer');
|
|
14
|
+
const herdsman = require('zigbee-herdsman');
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
/* Start ZiPulses */
|
|
18
|
+
|
|
19
|
+
const unitsZiPulses = [
|
|
20
|
+
'kWh',
|
|
21
|
+
'm3',
|
|
22
|
+
'ft3',
|
|
23
|
+
'ccf',
|
|
24
|
+
'US gl',
|
|
25
|
+
'IMP gl',
|
|
26
|
+
'BTUs',
|
|
27
|
+
'L (litre)',
|
|
28
|
+
'kPA (jauge)',
|
|
29
|
+
'kPA (absolu)',
|
|
30
|
+
'kPA (absolu)',
|
|
31
|
+
'sans unité',
|
|
32
|
+
'MJ',
|
|
33
|
+
'kVar',
|
|
34
|
+
];
|
|
35
|
+
|
|
36
|
+
const tzSeMetering = {
|
|
37
|
+
key: ['divisor', 'multiplier', 'unitOfMeasure'],
|
|
38
|
+
convertSet: async (entity, key, value, meta) => {
|
|
39
|
+
if (key === 'unitOfMeasure') {
|
|
40
|
+
const val = unitsZiPulses.indexOf(value);
|
|
41
|
+
const payload = {768: {value: val, type: herdsman.Zcl.DataType.enum8}};
|
|
42
|
+
await entity.write('seMetering', payload);
|
|
43
|
+
await entity.read('seMetering', [key]);
|
|
44
|
+
return {state: {'unitOfMeasure': value}};
|
|
45
|
+
} else {
|
|
46
|
+
await entity.write('seMetering', {
|
|
47
|
+
[key]: value,
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return {state: {[key]: value}};
|
|
52
|
+
},
|
|
53
|
+
// convertGet: async (entity, key, meta) => {
|
|
54
|
+
// await entity.read('seMetering', [key]);
|
|
55
|
+
// },
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
const fzZiPulses = {
|
|
60
|
+
cluster: 'seMetering',
|
|
61
|
+
type: ['attributeReport', 'readResponse'],
|
|
62
|
+
convert: (model, msg, publish, options, meta) => {
|
|
63
|
+
const payload = {};
|
|
64
|
+
if (msg.data.hasOwnProperty('multiplier')) {
|
|
65
|
+
payload['multiplier'] = msg.data['multiplier'];
|
|
66
|
+
}
|
|
67
|
+
if (msg.data.hasOwnProperty('divisor')) {
|
|
68
|
+
payload['divisor'] = msg.data['divisor'];
|
|
69
|
+
}
|
|
70
|
+
if (msg.data.hasOwnProperty('unitOfMeasure')) {
|
|
71
|
+
const val = msg.data['unitOfMeasure'];
|
|
72
|
+
payload['unitOfMeasure'] = unitsZiPulses[val];
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return payload;
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
/* End ZiPulses */
|
|
14
81
|
|
|
15
82
|
const fzLocal = {
|
|
16
83
|
lixee_ha_electrical_measurement: {
|
|
@@ -555,137 +622,158 @@ function getCurrentConfig(device, options, logger=console) {
|
|
|
555
622
|
|
|
556
623
|
return myExpose;
|
|
557
624
|
}
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
.
|
|
571
|
-
index
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
625
|
+
module.exports = [
|
|
626
|
+
{
|
|
627
|
+
zigbeeModel: ['ZLinky_TIC', 'ZLinky_TIC\u0000'],
|
|
628
|
+
model: 'ZLinky_TIC',
|
|
629
|
+
vendor: 'LiXee',
|
|
630
|
+
description: 'Lixee ZLinky',
|
|
631
|
+
fromZigbee: [fzLocal.lixee_metering, fz.meter_identification, fzLocal.lixee_ha_electrical_measurement, fzLocal.lixee_private_fz],
|
|
632
|
+
toZigbee: [],
|
|
633
|
+
exposes: (device, options) => {
|
|
634
|
+
// docs generation
|
|
635
|
+
let exposes;
|
|
636
|
+
if (device == null && options == null) {
|
|
637
|
+
exposes = exposedData.map((e) => e.exposes)
|
|
638
|
+
.filter((value, index, self) =>
|
|
639
|
+
index === self.findIndex((t) => (
|
|
640
|
+
t.property === value.property // Remove duplicates
|
|
641
|
+
)),
|
|
642
|
+
);
|
|
643
|
+
} else {
|
|
644
|
+
exposes = getCurrentConfig(device, options).map((e) => e.exposes);
|
|
645
|
+
}
|
|
578
646
|
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
647
|
+
exposes.push(e.linkquality());
|
|
648
|
+
return exposes;
|
|
649
|
+
},
|
|
650
|
+
options: [
|
|
651
|
+
exposes.options.measurement_poll_interval(),
|
|
652
|
+
exposes.enum(`linky_mode`, ea.SET, ['auto', linkyModeDef.legacy, linkyModeDef.standard])
|
|
653
|
+
.withDescription(`Counter with TIC in mode standard or historique. May require restart (default: auto)`),
|
|
654
|
+
exposes.enum(`energy_phase`, ea.SET, ['auto', linkyPhaseDef.single, linkyPhaseDef.three])
|
|
655
|
+
.withDescription(`Power with single or three phase. May require restart (default: auto)`),
|
|
656
|
+
exposes.enum(`production`, ea.SET, ['auto', 'true', 'false']).withDescription(`If you produce energy back to the grid (works ONLY when linky_mode: ${linkyModeDef.standard}, default: auto)`),
|
|
657
|
+
exposes.enum(`tarif`, ea.SET, [...Object.entries(tarifsDef).map(( [k, v] ) => (v.fname)), 'auto'])
|
|
658
|
+
.withDescription(`Overrides the automatic current tarif. This option will exclude unnecesary attributes. Open a issue to support more of them. Default: auto`),
|
|
659
|
+
exposes.options.precision(`kWh`),
|
|
660
|
+
exposes.numeric(`measurement_poll_chunk`, ea.SET).withValueMin(1).withDescription(`During the poll, request multiple exposes to the Zlinky at once for reducing Zigbee network overload. Too much request at once could exceed device limit. Requieres Z2M restart. Default: 1`),
|
|
661
|
+
exposes.text(`tic_command_whitelist`, ea.SET).withDescription(`List of TIC commands to be exposed (separated by comma). Reconfigure device after change. Default: all`),
|
|
662
|
+
],
|
|
663
|
+
configure: async (device, coordinatorEndpoint, logger, options) => {
|
|
664
|
+
const endpoint = device.getEndpoint(1);
|
|
665
|
+
|
|
666
|
+
await reporting.bind(endpoint, coordinatorEndpoint, [
|
|
667
|
+
clustersDef._0x0702, /* seMetering */
|
|
668
|
+
clustersDef._0x0B01, /* haMeterIdentification */
|
|
669
|
+
clustersDef._0x0B04, /* haElectricalMeasurement */
|
|
670
|
+
clustersDef._0xFF66, /* liXeePrivate */
|
|
671
|
+
]);
|
|
672
|
+
|
|
673
|
+
await endpoint.read('liXeePrivate', ['linkyMode', 'currentTarif'], {manufacturerCode: null})
|
|
674
|
+
.catch((e) => {
|
|
675
|
+
// https://github.com/Koenkk/zigbee2mqtt/issues/11674
|
|
676
|
+
logger.warn(`Failed to read zigbee attributes: ${e}`);
|
|
677
|
+
});
|
|
610
678
|
|
|
611
|
-
|
|
612
|
-
|
|
679
|
+
const configReportings = [];
|
|
680
|
+
const suscribeNew = getCurrentConfig(device, options, logger).filter((e) => e.reportable);
|
|
613
681
|
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
682
|
+
const unsuscribe = endpoint.configuredReportings
|
|
683
|
+
.filter((e) => !suscribeNew.some((r) => e.cluster.name == r.cluster && e.attribute.name == r.att));
|
|
684
|
+
// Unsuscribe reports that doesn't correspond with the current config
|
|
685
|
+
(await Promise.allSettled(unsuscribe.map((e) => endpoint.configureReporting(e.cluster.name, reporting.payload(e.attribute.name, e.minimumReportInterval, 65535, e.reportableChange), {manufacturerCode: null}))))
|
|
686
|
+
.filter((e) => e.status == 'rejected')
|
|
687
|
+
.forEach((e) => {
|
|
688
|
+
throw e.reason;
|
|
689
|
+
});
|
|
622
690
|
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
691
|
+
for (const e of suscribeNew) {
|
|
692
|
+
let params = {
|
|
693
|
+
att: e.att,
|
|
694
|
+
min: repInterval.MINUTE,
|
|
695
|
+
max: repInterval.MINUTES_15,
|
|
696
|
+
change: 1,
|
|
697
|
+
};
|
|
698
|
+
// Override reportings
|
|
699
|
+
if (e.hasOwnProperty('report')) {
|
|
700
|
+
params = {...params, ...e.report};
|
|
701
|
+
}
|
|
702
|
+
configReportings.push(endpoint
|
|
703
|
+
.configureReporting(
|
|
704
|
+
e.cluster, reporting.payload(params.att, params.min, params.max, params.change),
|
|
705
|
+
{manufacturerCode: null}),
|
|
706
|
+
);
|
|
633
707
|
}
|
|
634
|
-
|
|
635
|
-
.
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
);
|
|
639
|
-
}
|
|
640
|
-
(await Promise.allSettled(configReportings))
|
|
641
|
-
.filter((e) => e.status == 'rejected')
|
|
642
|
-
.forEach((e) => {
|
|
643
|
-
throw e.reason;
|
|
644
|
-
});
|
|
645
|
-
},
|
|
646
|
-
ota: ota.lixee,
|
|
647
|
-
onEvent: async (type, data, device, options) => {
|
|
648
|
-
const endpoint = device.getEndpoint(1);
|
|
649
|
-
if (type === 'start') {
|
|
650
|
-
endpoint.read('liXeePrivate', ['linkyMode', 'currentTarif'], {manufacturerCode: null})
|
|
651
|
-
.catch((e) => {
|
|
652
|
-
// https://github.com/Koenkk/zigbee2mqtt/issues/11674
|
|
653
|
-
console.warn(`Failed to read zigbee attributes: ${e}`);
|
|
708
|
+
(await Promise.allSettled(configReportings))
|
|
709
|
+
.filter((e) => e.status == 'rejected')
|
|
710
|
+
.forEach((e) => {
|
|
711
|
+
throw e.reason;
|
|
654
712
|
});
|
|
655
|
-
}
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
713
|
+
},
|
|
714
|
+
ota: ota.lixee,
|
|
715
|
+
onEvent: async (type, data, device, options) => {
|
|
716
|
+
const endpoint = device.getEndpoint(1);
|
|
717
|
+
if (type === 'start') {
|
|
718
|
+
endpoint.read('liXeePrivate', ['linkyMode', 'currentTarif'], {manufacturerCode: null})
|
|
719
|
+
.catch((e) => {
|
|
720
|
+
// https://github.com/Koenkk/zigbee2mqtt/issues/11674
|
|
721
|
+
console.warn(`Failed to read zigbee attributes: ${e}`);
|
|
722
|
+
});
|
|
723
|
+
} else if (type === 'stop') {
|
|
724
|
+
clearInterval(globalStore.getValue(device, 'interval'));
|
|
725
|
+
globalStore.clearValue(device, 'interval');
|
|
726
|
+
} else if (!globalStore.hasValue(device, 'interval')) {
|
|
727
|
+
const seconds = options && options.measurement_poll_interval ? options.measurement_poll_interval : 60;
|
|
728
|
+
const measurement_poll_chunk = options && options.measurement_poll_chunk ? options.measurement_poll_chunk : 1;
|
|
729
|
+
|
|
730
|
+
const interval = setInterval(async () => {
|
|
731
|
+
const currentExposes = getCurrentConfig(device, options)
|
|
732
|
+
.filter((e) => !endpoint.configuredReportings.some((r) => r.cluster.name == e.cluster && r.attribute.name == e.att));
|
|
733
|
+
|
|
734
|
+
for (const key in clustersDef) {
|
|
735
|
+
if (Object.hasOwnProperty.call(clustersDef, key)) {
|
|
736
|
+
const cluster = clustersDef[key];
|
|
737
|
+
|
|
738
|
+
const targ = currentExposes.filter((e)=> e.cluster == cluster).map((e)=> e.att);
|
|
739
|
+
if (targ.length) {
|
|
740
|
+
let i; let j;
|
|
741
|
+
// Split array by chunks
|
|
742
|
+
for (i = 0, j = targ.length; i < j; i += measurement_poll_chunk) {
|
|
743
|
+
await endpoint
|
|
744
|
+
.read(cluster, targ.slice(i, i + measurement_poll_chunk), {manufacturerCode: null})
|
|
745
|
+
.catch((e) => {
|
|
746
|
+
// https://github.com/Koenkk/zigbee2mqtt/issues/11674
|
|
747
|
+
console.warn(`Failed to read zigbee attributes: ${e}`);
|
|
748
|
+
});
|
|
749
|
+
}
|
|
681
750
|
}
|
|
682
751
|
}
|
|
683
752
|
}
|
|
684
|
-
}
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
}
|
|
753
|
+
}, seconds * 1000);
|
|
754
|
+
globalStore.putValue(device, 'interval', interval);
|
|
755
|
+
}
|
|
756
|
+
},
|
|
688
757
|
},
|
|
689
|
-
};
|
|
690
758
|
|
|
691
|
-
|
|
759
|
+
{
|
|
760
|
+
zigbeeModel: ['ZiPulses'],
|
|
761
|
+
model: 'ZiPulses',
|
|
762
|
+
vendor: 'LiXee',
|
|
763
|
+
description: 'Lixee ZiPulses',
|
|
764
|
+
fromZigbee: [fz.battery, fz.temperature, fz.metering, fzZiPulses],
|
|
765
|
+
toZigbee: [tzSeMetering],
|
|
766
|
+
exposes: [e.battery_voltage(), e.temperature(),
|
|
767
|
+
exposes.numeric('multiplier', ea.STATE_SET).withValueMin(1).withValueMax(1000).withDescription('It is necessary to press the link button to update'),
|
|
768
|
+
exposes.numeric('divisor', ea.STATE_SET).withValueMin(1).withValueMax(1000).withDescription('It is necessary to press the link button to update'),
|
|
769
|
+
exposes.enum('unitOfMeasure', ea.STATE_SET, unitsZiPulses).withDescription('It is necessary to press the link button to update'),
|
|
770
|
+
exposes.numeric('energy', ea.STATE),
|
|
771
|
+
],
|
|
772
|
+
configure: async (device, coordinatorEndpoint, logger) => {
|
|
773
|
+
const endpoint = device.getEndpoint(1);
|
|
774
|
+
const binds = ['genPowerCfg', 'seMetering', 'msTemperatureMeasurement'];
|
|
775
|
+
await reporting.bind(endpoint, coordinatorEndpoint, binds);
|
|
776
|
+
await endpoint.read('seMetering', ['divisor', 'unitOfMeasure', 'multiplier']);
|
|
777
|
+
},
|
|
778
|
+
},
|
|
779
|
+
];
|
package/devices/philips.js
CHANGED
|
@@ -2480,7 +2480,7 @@ module.exports = [
|
|
|
2480
2480
|
extend: philips.extend.light_onoff_brightness_colortemp({colorTempRange: [153, 454]}),
|
|
2481
2481
|
},
|
|
2482
2482
|
{
|
|
2483
|
-
zigbeeModel: ['5309331P6', '5309330P6', '929003046301_03', '929003046301_02'],
|
|
2483
|
+
zigbeeModel: ['5309331P6', '5309330P6', '929003046301_03', '929003046301_02', '929003046301_01'],
|
|
2484
2484
|
model: '5309331P6',
|
|
2485
2485
|
vendor: 'Philips',
|
|
2486
2486
|
description: 'Hue White ambiance Runner triple spotlight',
|
package/devices/qoto.js
CHANGED
|
@@ -89,7 +89,7 @@ const tzLocal = {
|
|
|
89
89
|
|
|
90
90
|
module.exports = [
|
|
91
91
|
{
|
|
92
|
-
fingerprint:
|
|
92
|
+
fingerprint: tuya.fingerprint('TS0601', ['_TZE200_arge1ptm', '_TZE200_anv5ujhv']),
|
|
93
93
|
model: 'QT-05M',
|
|
94
94
|
vendor: 'QOTO',
|
|
95
95
|
description: 'Solar powered garden watering timer',
|