zigbee-herdsman-converters 14.0.632 → 14.0.634
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/converters/fromZigbee.js +0 -100
- package/converters/toZigbee.js +0 -29
- package/devices/develco.js +277 -53
- package/devices/hfh.js +11 -0
- package/devices/philips.js +15 -1
- package/devices/tuya.js +24 -3
- package/lib/ota/common.js +2 -2
- package/lib/tuya.js +129 -0
- package/package.json +1 -1
package/converters/fromZigbee.js
CHANGED
|
@@ -706,30 +706,6 @@ const converters = {
|
|
|
706
706
|
return result;
|
|
707
707
|
},
|
|
708
708
|
},
|
|
709
|
-
develco_metering: {
|
|
710
|
-
cluster: 'seMetering',
|
|
711
|
-
type: ['attributeReport', 'readResponse'],
|
|
712
|
-
convert: (model, msg, publish, options, meta) => {
|
|
713
|
-
const result = {};
|
|
714
|
-
if (msg.data.hasOwnProperty('develcoPulseConfiguration')) {
|
|
715
|
-
result[postfixWithEndpointName('pulse_configuration', msg, model, meta)] =
|
|
716
|
-
msg.data['develcoPulseConfiguration'];
|
|
717
|
-
}
|
|
718
|
-
|
|
719
|
-
if (msg.data.hasOwnProperty('develcoInterfaceMode')) {
|
|
720
|
-
result[postfixWithEndpointName('interface_mode', msg, model, meta)] =
|
|
721
|
-
constants.develcoInterfaceMode.hasOwnProperty(msg.data['develcoInterfaceMode']) ?
|
|
722
|
-
constants.develcoInterfaceMode[msg.data['develcoInterfaceMode']] :
|
|
723
|
-
msg.data['develcoInterfaceMode'];
|
|
724
|
-
}
|
|
725
|
-
if (msg.data.hasOwnProperty('status')) {
|
|
726
|
-
result['battery_low'] = (msg.data.status & 2) > 0;
|
|
727
|
-
result['check_meter'] = (msg.data.status & 1) > 0;
|
|
728
|
-
}
|
|
729
|
-
|
|
730
|
-
return result;
|
|
731
|
-
},
|
|
732
|
-
},
|
|
733
709
|
electrical_measurement: {
|
|
734
710
|
/**
|
|
735
711
|
* When using this converter also add the following to the configure method of the device:
|
|
@@ -1927,49 +1903,6 @@ const converters = {
|
|
|
1927
1903
|
return {...payload1, ...payload2};
|
|
1928
1904
|
},
|
|
1929
1905
|
},
|
|
1930
|
-
develco_voc: {
|
|
1931
|
-
cluster: 'develcoSpecificAirQuality',
|
|
1932
|
-
type: ['attributeReport', 'readResponse'],
|
|
1933
|
-
options: [exposes.options.precision('voc'), exposes.options.calibration('voc')],
|
|
1934
|
-
convert: (model, msg, publish, options, meta) => {
|
|
1935
|
-
const voc = parseFloat(msg.data['measuredValue']);
|
|
1936
|
-
const vocProperty = postfixWithEndpointName('voc', msg, model, meta);
|
|
1937
|
-
|
|
1938
|
-
let airQuality;
|
|
1939
|
-
const airQualityProperty = postfixWithEndpointName('air_quality', msg, model, meta);
|
|
1940
|
-
if (voc <= 65) {
|
|
1941
|
-
airQuality = 'excellent';
|
|
1942
|
-
} else if (voc <= 220) {
|
|
1943
|
-
airQuality = 'good';
|
|
1944
|
-
} else if (voc <= 660) {
|
|
1945
|
-
airQuality = 'moderate';
|
|
1946
|
-
} else if (voc <= 2200) {
|
|
1947
|
-
airQuality = 'poor';
|
|
1948
|
-
} else if (voc <= 5500) {
|
|
1949
|
-
airQuality = 'unhealthy';
|
|
1950
|
-
} else if (voc > 5500) {
|
|
1951
|
-
airQuality = 'out_of_range';
|
|
1952
|
-
} else {
|
|
1953
|
-
airQuality = 'unknown';
|
|
1954
|
-
}
|
|
1955
|
-
return {[vocProperty]: calibrateAndPrecisionRoundOptions(voc, options, 'voc'), [airQualityProperty]: airQuality};
|
|
1956
|
-
},
|
|
1957
|
-
},
|
|
1958
|
-
develco_voc_battery: {
|
|
1959
|
-
cluster: 'genPowerCfg',
|
|
1960
|
-
type: ['attributeReport', 'readResponse'],
|
|
1961
|
-
convert: (model, msg, publish, options, meta) => {
|
|
1962
|
-
/*
|
|
1963
|
-
* Per the technical documentation for AQSZB-110:
|
|
1964
|
-
* To detect low battery the system can monitor the "BatteryVoltage" by setting up a reporting interval of every 12 hour.
|
|
1965
|
-
* When a voltage of 2.5V is measured the battery should be replaced.
|
|
1966
|
-
* Low batt LED indication–RED LED will blink twice every 60 second.
|
|
1967
|
-
*/
|
|
1968
|
-
const result = converters.battery.convert(model, msg, publish, options, meta);
|
|
1969
|
-
result.battery_low = (result.voltage <= 2500);
|
|
1970
|
-
return result;
|
|
1971
|
-
},
|
|
1972
|
-
},
|
|
1973
1906
|
tuya_temperature_humidity_sensor: {
|
|
1974
1907
|
cluster: 'manuSpecificTuya',
|
|
1975
1908
|
type: ['commandDataReport', 'commandDataResponse'],
|
|
@@ -7818,39 +7751,6 @@ const converters = {
|
|
|
7818
7751
|
return {action: `scene_${scenes[msg.data.level]}`};
|
|
7819
7752
|
},
|
|
7820
7753
|
},
|
|
7821
|
-
develco_fw: {
|
|
7822
|
-
cluster: 'genBasic',
|
|
7823
|
-
type: ['attributeReport', 'readResponse'],
|
|
7824
|
-
convert: (model, msg, publish, options, meta) => {
|
|
7825
|
-
const result = {};
|
|
7826
|
-
if (0x8000 in msg.data) {
|
|
7827
|
-
const firmware = msg.data[0x8000].join('.');
|
|
7828
|
-
result.current_firmware = firmware;
|
|
7829
|
-
meta.device.softwareBuildID = firmware;
|
|
7830
|
-
}
|
|
7831
|
-
|
|
7832
|
-
if (0x8020 in msg.data) {
|
|
7833
|
-
meta.device.hardwareVersion = msg.data[0x8020].join('.');
|
|
7834
|
-
}
|
|
7835
|
-
|
|
7836
|
-
return result;
|
|
7837
|
-
},
|
|
7838
|
-
},
|
|
7839
|
-
develco_genbinaryinput: {
|
|
7840
|
-
cluster: 'genBinaryInput',
|
|
7841
|
-
type: ['attributeReport', 'readResponse'],
|
|
7842
|
-
convert: (model, msg, publish, options, meta) => {
|
|
7843
|
-
const result = {};
|
|
7844
|
-
if (msg.data.hasOwnProperty('reliability')) {
|
|
7845
|
-
const lookup = {0: 'no_fault_detected', 7: 'unreliable_other', 8: 'process_error'};
|
|
7846
|
-
result.reliability = lookup[msg.data['reliability']];
|
|
7847
|
-
}
|
|
7848
|
-
if (msg.data.hasOwnProperty('statusFlags')) {
|
|
7849
|
-
result.fault = (msg.data['statusFlags']===1);
|
|
7850
|
-
}
|
|
7851
|
-
return result;
|
|
7852
|
-
},
|
|
7853
|
-
},
|
|
7854
7754
|
xiaomi_tvoc: {
|
|
7855
7755
|
cluster: 'genAnalogInput',
|
|
7856
7756
|
type: ['attributeReport', 'readResponse'],
|
package/converters/toZigbee.js
CHANGED
|
@@ -16,7 +16,6 @@ const manufacturerOptions = {
|
|
|
16
16
|
osram: {manufacturerCode: herdsman.Zcl.ManufacturerCode.OSRAM},
|
|
17
17
|
eurotronic: {manufacturerCode: herdsman.Zcl.ManufacturerCode.JENNIC},
|
|
18
18
|
danfoss: {manufacturerCode: herdsman.Zcl.ManufacturerCode.DANFOSS},
|
|
19
|
-
develco: {manufacturerCode: herdsman.Zcl.ManufacturerCode.DEVELCO},
|
|
20
19
|
hue: {manufacturerCode: herdsman.Zcl.ManufacturerCode.PHILIPS},
|
|
21
20
|
ikea: {manufacturerCode: herdsman.Zcl.ManufacturerCode.IKEA_OF_SWEDEN},
|
|
22
21
|
sinope: {manufacturerCode: herdsman.Zcl.ManufacturerCode.SINOPE_TECH},
|
|
@@ -3081,34 +3080,6 @@ const converters = {
|
|
|
3081
3080
|
await entity.read('haDiagnostic', ['danfossMultimasterRole'], manufacturerOptions.danfoss);
|
|
3082
3081
|
},
|
|
3083
3082
|
},
|
|
3084
|
-
develco_pulse_configuration: {
|
|
3085
|
-
key: ['pulse_configuration'],
|
|
3086
|
-
convertSet: async (entity, key, value, meta) => {
|
|
3087
|
-
await entity.write('seMetering', {'develcoPulseConfiguration': value}, manufacturerOptions.develco);
|
|
3088
|
-
return {readAfterWriteTime: 200, state: {'pulse_configuration': value}};
|
|
3089
|
-
},
|
|
3090
|
-
convertGet: async (entity, key, meta) => {
|
|
3091
|
-
await entity.read('seMetering', ['develcoPulseConfiguration'], manufacturerOptions.develco);
|
|
3092
|
-
},
|
|
3093
|
-
},
|
|
3094
|
-
develco_interface_mode: {
|
|
3095
|
-
key: ['interface_mode'],
|
|
3096
|
-
convertSet: async (entity, key, value, meta) => {
|
|
3097
|
-
const payload = {'develcoInterfaceMode': utils.getKey(constants.develcoInterfaceMode, value, undefined, Number)};
|
|
3098
|
-
await entity.write('seMetering', payload, manufacturerOptions.develco);
|
|
3099
|
-
return {readAfterWriteTime: 200, state: {'interface_mode': value}};
|
|
3100
|
-
},
|
|
3101
|
-
convertGet: async (entity, key, meta) => {
|
|
3102
|
-
await entity.read('seMetering', ['develcoInterfaceMode'], manufacturerOptions.develco);
|
|
3103
|
-
},
|
|
3104
|
-
},
|
|
3105
|
-
develco_current_summation: {
|
|
3106
|
-
key: ['current_summation'],
|
|
3107
|
-
convertSet: async (entity, key, value, meta) => {
|
|
3108
|
-
await entity.write('seMetering', {'develcoCurrentSummation': value}, manufacturerOptions.develco);
|
|
3109
|
-
return {state: {'current_summation': value}};
|
|
3110
|
-
},
|
|
3111
|
-
},
|
|
3112
3083
|
ZMCSW032D_cover_position: {
|
|
3113
3084
|
key: ['position', 'tilt'],
|
|
3114
3085
|
convertSet: async (entity, key, value, meta) => {
|
package/devices/develco.js
CHANGED
|
@@ -4,35 +4,247 @@ const tz = require('../converters/toZigbee');
|
|
|
4
4
|
const constants = require('../lib/constants');
|
|
5
5
|
const reporting = require('../lib/reporting');
|
|
6
6
|
const globalStore = require('../lib/store');
|
|
7
|
+
const utils = require('../lib/utils');
|
|
7
8
|
const ota = require('../lib/ota');
|
|
8
9
|
const e = exposes.presets;
|
|
9
10
|
const ea = exposes.access;
|
|
10
11
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
12
|
+
// develco specific cosntants
|
|
13
|
+
const manufacturerOptions = {manufacturerCode: 0x1015};
|
|
14
|
+
|
|
15
|
+
/* MOSZB-1xx - ledControl - bitmap8 - r/w
|
|
16
|
+
* 0x00 Disable LED when movement is detected.
|
|
17
|
+
* 0x01 Enables periodic fault flashes. These flashes are used to indicate e.g. low battery level.
|
|
18
|
+
* 0x02 Enables green application defined LED. This is e.g. used to indicate motion detection.
|
|
19
|
+
* Default value 0xFF ( seems to be fault + motion)
|
|
20
|
+
*/
|
|
21
|
+
const develcoLedControlMap = {
|
|
22
|
+
0x00: 'off',
|
|
23
|
+
0x01: 'fault_only',
|
|
24
|
+
0x02: 'motion_only',
|
|
25
|
+
0xFF: 'both',
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
// develco specific convertors
|
|
29
|
+
const develco = {
|
|
30
|
+
fz: {
|
|
31
|
+
// SPLZB-134 and SPLZB-131 reports strange values sometimes
|
|
32
|
+
// https://github.com/Koenkk/zigbee2mqtt/issues/13329
|
|
33
|
+
electrical_measurement: {
|
|
34
|
+
...fz.electrical_measurement,
|
|
35
|
+
convert: (model, msg, publish, options, meta) => {
|
|
36
|
+
if (msg.data.rmsVoltage !== 0xFFFF && msg.data.rmsCurrent !== 0xFFFF && msg.data.activePower !== -0x8000) {
|
|
37
|
+
return fz.electrical_measurement.convert(model, msg, publish, options, meta);
|
|
38
|
+
}
|
|
39
|
+
},
|
|
20
40
|
},
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
}
|
|
41
|
+
device_temperature: {
|
|
42
|
+
...fz.device_temperature,
|
|
43
|
+
convert: (model, msg, publish, options, meta) => {
|
|
44
|
+
if (msg.data.currentTemperature !== -0x8000) {
|
|
45
|
+
return fz.device_temperature.convert(model, msg, publish, options, meta);
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
metering: {
|
|
50
|
+
...fz.metering,
|
|
51
|
+
convert: (model, msg, publish, options, meta) => {
|
|
52
|
+
if (msg.data.instantaneousDemand !== -0x800000) {
|
|
53
|
+
return fz.metering.convert(model, msg, publish, options, meta);
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
pulse_configuration: {
|
|
58
|
+
cluster: 'seMetering',
|
|
59
|
+
type: ['attributeReport', 'readResponse'],
|
|
60
|
+
convert: (model, msg, publish, options, meta) => {
|
|
61
|
+
const result = {};
|
|
62
|
+
if (msg.data.hasOwnProperty('develcoPulseConfiguration')) {
|
|
63
|
+
result[utils.postfixWithEndpointName('pulse_configuration', msg, model, meta)] =
|
|
64
|
+
msg.data['develcoPulseConfiguration'];
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return result;
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
interface_mode: {
|
|
71
|
+
cluster: 'seMetering',
|
|
72
|
+
type: ['attributeReport', 'readResponse'],
|
|
73
|
+
convert: (model, msg, publish, options, meta) => {
|
|
74
|
+
const result = {};
|
|
75
|
+
if (msg.data.hasOwnProperty('develcoInterfaceMode')) {
|
|
76
|
+
result[utils.postfixWithEndpointName('interface_mode', msg, model, meta)] =
|
|
77
|
+
constants.develcoInterfaceMode.hasOwnProperty(msg.data['develcoInterfaceMode']) ?
|
|
78
|
+
constants.develcoInterfaceMode[msg.data['develcoInterfaceMode']] :
|
|
79
|
+
msg.data['develcoInterfaceMode'];
|
|
80
|
+
}
|
|
81
|
+
if (msg.data.hasOwnProperty('status')) {
|
|
82
|
+
result['battery_low'] = (msg.data.status & 2) > 0;
|
|
83
|
+
result['check_meter'] = (msg.data.status & 1) > 0;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return result;
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
firmware_version: {
|
|
90
|
+
cluster: 'genBasic',
|
|
91
|
+
type: ['attributeReport', 'readResponse'],
|
|
92
|
+
convert: (model, msg, publish, options, meta) => {
|
|
93
|
+
const result = {};
|
|
94
|
+
if (0x8000 in msg.data) {
|
|
95
|
+
const firmware = msg.data[0x8000].join('.');
|
|
96
|
+
result.current_firmware = firmware;
|
|
97
|
+
meta.device.softwareBuildID = firmware;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (0x8020 in msg.data) {
|
|
101
|
+
meta.device.hardwareVersion = msg.data[0x8020].join('.');
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return result;
|
|
105
|
+
},
|
|
106
|
+
},
|
|
107
|
+
fault_status: {
|
|
108
|
+
cluster: 'genBinaryInput',
|
|
109
|
+
type: ['attributeReport', 'readResponse'],
|
|
110
|
+
convert: (model, msg, publish, options, meta) => {
|
|
111
|
+
const result = {};
|
|
112
|
+
if (msg.data.hasOwnProperty('reliability')) {
|
|
113
|
+
const lookup = {0: 'no_fault_detected', 7: 'unreliable_other', 8: 'process_error'};
|
|
114
|
+
result.reliability = lookup[msg.data['reliability']];
|
|
115
|
+
}
|
|
116
|
+
if (msg.data.hasOwnProperty('statusFlags')) {
|
|
117
|
+
result.fault = (msg.data['statusFlags']===1);
|
|
118
|
+
}
|
|
119
|
+
return result;
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
voc: {
|
|
123
|
+
cluster: 'develcoSpecificAirQuality',
|
|
124
|
+
type: ['attributeReport', 'readResponse'],
|
|
125
|
+
options: [exposes.options.precision('voc'), exposes.options.calibration('voc')],
|
|
126
|
+
convert: (model, msg, publish, options, meta) => {
|
|
127
|
+
const voc = parseFloat(msg.data['measuredValue']);
|
|
128
|
+
const vocProperty = utils.postfixWithEndpointName('voc', msg, model, meta);
|
|
129
|
+
|
|
130
|
+
let airQuality;
|
|
131
|
+
const airQualityProperty = utils.postfixWithEndpointName('air_quality', msg, model, meta);
|
|
132
|
+
if (voc <= 65) {
|
|
133
|
+
airQuality = 'excellent';
|
|
134
|
+
} else if (voc <= 220) {
|
|
135
|
+
airQuality = 'good';
|
|
136
|
+
} else if (voc <= 660) {
|
|
137
|
+
airQuality = 'moderate';
|
|
138
|
+
} else if (voc <= 2200) {
|
|
139
|
+
airQuality = 'poor';
|
|
140
|
+
} else if (voc <= 5500) {
|
|
141
|
+
airQuality = 'unhealthy';
|
|
142
|
+
} else if (voc > 5500) {
|
|
143
|
+
airQuality = 'out_of_range';
|
|
144
|
+
} else {
|
|
145
|
+
airQuality = 'unknown';
|
|
146
|
+
}
|
|
147
|
+
return {[vocProperty]: utils.calibrateAndPrecisionRoundOptions(voc, options, 'voc'), [airQualityProperty]: airQuality};
|
|
148
|
+
},
|
|
149
|
+
},
|
|
150
|
+
voc_battery: {
|
|
151
|
+
cluster: 'genPowerCfg',
|
|
152
|
+
type: ['attributeReport', 'readResponse'],
|
|
153
|
+
convert: (model, msg, publish, options, meta) => {
|
|
154
|
+
/*
|
|
155
|
+
* Per the technical documentation for AQSZB-110:
|
|
156
|
+
* To detect low battery the system can monitor the "BatteryVoltage" by setting up a reporting interval of every 12 hour.
|
|
157
|
+
* When a voltage of 2.5V is measured the battery should be replaced.
|
|
158
|
+
* Low batt LED indication–RED LED will blink twice every 60 second.
|
|
159
|
+
*/
|
|
160
|
+
const result = fz.battery.convert(model, msg, publish, options, meta);
|
|
161
|
+
result.battery_low = (result.voltage <= 2500);
|
|
162
|
+
return result;
|
|
163
|
+
},
|
|
164
|
+
},
|
|
165
|
+
led_control: {
|
|
166
|
+
cluster: 'genBasic',
|
|
167
|
+
type: ['attributeReport', 'readResponse'],
|
|
168
|
+
options: [],
|
|
169
|
+
convert: (model, msg, publish, options, meta) => {
|
|
170
|
+
const state = {};
|
|
171
|
+
|
|
172
|
+
if (msg.data.hasOwnProperty('develcoLedControl')) {
|
|
173
|
+
state['led_control'] = develcoLedControlMap[msg.data['develcoLedControl']];
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return state;
|
|
177
|
+
},
|
|
178
|
+
},
|
|
179
|
+
ias_occupancy_timeout: {
|
|
180
|
+
cluster: 'ssIasZone',
|
|
181
|
+
type: ['attributeReport', 'readResponse'],
|
|
182
|
+
options: [],
|
|
183
|
+
convert: (model, msg, publish, options, meta) => {
|
|
184
|
+
const state = {};
|
|
185
|
+
|
|
186
|
+
if (msg.data.hasOwnProperty('develcoAlarmOffDelay')) {
|
|
187
|
+
state['occupancy_timeout'] = msg.data['develcoAlarmOffDelay'];
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
return state;
|
|
191
|
+
},
|
|
28
192
|
},
|
|
29
193
|
},
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
194
|
+
tz: {
|
|
195
|
+
pulse_configuration: {
|
|
196
|
+
key: ['pulse_configuration'],
|
|
197
|
+
convertSet: async (entity, key, value, meta) => {
|
|
198
|
+
await entity.write('seMetering', {'develcoPulseConfiguration': value}, manufacturerOptions);
|
|
199
|
+
return {readAfterWriteTime: 200, state: {'pulse_configuration': value}};
|
|
200
|
+
},
|
|
201
|
+
convertGet: async (entity, key, meta) => {
|
|
202
|
+
await entity.read('seMetering', ['develcoPulseConfiguration'], manufacturerOptions);
|
|
203
|
+
},
|
|
204
|
+
},
|
|
205
|
+
interface_mode: {
|
|
206
|
+
key: ['interface_mode'],
|
|
207
|
+
convertSet: async (entity, key, value, meta) => {
|
|
208
|
+
const payload = {'develcoInterfaceMode': utils.getKey(constants.develcoInterfaceMode, value, undefined, Number)};
|
|
209
|
+
await entity.write('seMetering', payload, manufacturerOptions.develco);
|
|
210
|
+
return {readAfterWriteTime: 200, state: {'interface_mode': value}};
|
|
211
|
+
},
|
|
212
|
+
convertGet: async (entity, key, meta) => {
|
|
213
|
+
await entity.read('seMetering', ['develcoInterfaceMode'], manufacturerOptions);
|
|
214
|
+
},
|
|
215
|
+
},
|
|
216
|
+
current_summation: {
|
|
217
|
+
key: ['current_summation'],
|
|
218
|
+
convertSet: async (entity, key, value, meta) => {
|
|
219
|
+
await entity.write('seMetering', {'develcoCurrentSummation': value}, manufacturerOptions);
|
|
220
|
+
return {state: {'current_summation': value}};
|
|
221
|
+
},
|
|
222
|
+
},
|
|
223
|
+
led_control: {
|
|
224
|
+
key: ['led_control'],
|
|
225
|
+
convertSet: async (entity, key, value, meta) => {
|
|
226
|
+
const ledControl = utils.getKey(develcoLedControlMap, value, value, Number);
|
|
227
|
+
await entity.write('genBasic', {'develcoLedControl': ledControl}, manufacturerOptions);
|
|
228
|
+
return {state: {led_control: value}};
|
|
229
|
+
},
|
|
230
|
+
convertGet: async (entity, key, meta) => {
|
|
231
|
+
await entity.read('genBasic', ['develcoLedControl'], manufacturerOptions);
|
|
232
|
+
},
|
|
233
|
+
},
|
|
234
|
+
ias_occupancy_timeout: {
|
|
235
|
+
key: ['occupancy_timeout'],
|
|
236
|
+
convertSet: async (entity, key, value, meta) => {
|
|
237
|
+
let timeoutValue = value;
|
|
238
|
+
if (timeoutValue < 20) {
|
|
239
|
+
meta.logger.warn(`Minimum occupancy_timeout is 20, using 20 instead of ${timeoutValue}!`);
|
|
240
|
+
timeoutValue = 20;
|
|
241
|
+
}
|
|
242
|
+
await entity.write('ssIasZone', {'develcoAlarmOffDelay': timeoutValue}, manufacturerOptions);
|
|
243
|
+
return {state: {occupancy_timeout: timeoutValue}};
|
|
244
|
+
},
|
|
245
|
+
convertGet: async (entity, key, meta) => {
|
|
246
|
+
await entity.read('ssIasZone', ['develcoAlarmOffDelay'], manufacturerOptions);
|
|
247
|
+
},
|
|
36
248
|
},
|
|
37
249
|
},
|
|
38
250
|
};
|
|
@@ -43,7 +255,7 @@ module.exports = [
|
|
|
43
255
|
model: 'SPLZB-131',
|
|
44
256
|
vendor: 'Develco',
|
|
45
257
|
description: 'Power plug',
|
|
46
|
-
fromZigbee: [fz.on_off,
|
|
258
|
+
fromZigbee: [fz.on_off, develco.fz.electrical_measurement, develco.fz.metering],
|
|
47
259
|
toZigbee: [tz.on_off],
|
|
48
260
|
exposes: [e.switch(), e.power(), e.current(), e.voltage(), e.energy()],
|
|
49
261
|
configure: async (device, coordinatorEndpoint, logger) => {
|
|
@@ -94,7 +306,7 @@ module.exports = [
|
|
|
94
306
|
model: 'SPLZB-134',
|
|
95
307
|
vendor: 'Develco',
|
|
96
308
|
description: 'Power plug (type G)',
|
|
97
|
-
fromZigbee: [fz.on_off,
|
|
309
|
+
fromZigbee: [fz.on_off, develco.fz.electrical_measurement, develco.fz.metering, develco.fz.device_temperature],
|
|
98
310
|
toZigbee: [tz.on_off],
|
|
99
311
|
exposes: [e.switch(), e.power(), e.current(), e.voltage(), e.energy(), e.device_temperature()],
|
|
100
312
|
configure: async (device, coordinatorEndpoint, logger) => {
|
|
@@ -144,13 +356,12 @@ module.exports = [
|
|
|
144
356
|
model: 'EMIZB-132',
|
|
145
357
|
vendor: 'Develco',
|
|
146
358
|
description: 'Wattle AMS HAN power-meter sensor',
|
|
147
|
-
fromZigbee: [
|
|
359
|
+
fromZigbee: [develco.fz.metering, develco.fz.electrical_measurement, develco.fz.firmware_version],
|
|
148
360
|
toZigbee: [tz.EMIZB_132_mode],
|
|
149
361
|
ota: ota.zigbeeOTA,
|
|
150
362
|
configure: async (device, coordinatorEndpoint, logger) => {
|
|
151
363
|
const endpoint = device.getEndpoint(2);
|
|
152
|
-
|
|
153
|
-
await endpoint.read('genBasic', [0x8000, 0x8010, 0x8020], options);
|
|
364
|
+
await endpoint.read('genBasic', [0x8000, 0x8010, 0x8020], manufacturerOptions);
|
|
154
365
|
await reporting.bind(endpoint, coordinatorEndpoint, ['haElectricalMeasurement', 'seMetering']);
|
|
155
366
|
|
|
156
367
|
try {
|
|
@@ -186,17 +397,16 @@ module.exports = [
|
|
|
186
397
|
vendor: 'Develco',
|
|
187
398
|
description: 'Smoke detector with siren',
|
|
188
399
|
fromZigbee: [fz.temperature, fz.battery, fz.ias_smoke_alarm_1_develco, fz.ignore_basic_report,
|
|
189
|
-
fz.
|
|
400
|
+
develco.fz.firmware_version, fz.ias_enroll, fz.ias_wd, develco.fz.fault_status],
|
|
190
401
|
toZigbee: [tz.warning, tz.ias_max_duration, tz.warning_simple],
|
|
191
402
|
ota: ota.zigbeeOTA,
|
|
192
403
|
meta: {battery: {voltageToPercentage: '3V_2500'}},
|
|
193
404
|
configure: async (device, coordinatorEndpoint, logger) => {
|
|
194
|
-
const options = {manufacturerCode: 4117};
|
|
195
405
|
const endpoint = device.getEndpoint(35);
|
|
196
406
|
|
|
197
407
|
await reporting.bind(endpoint, coordinatorEndpoint, ['genPowerCfg', 'ssIasZone', 'ssIasWd', 'genBasic', 'genBinaryInput']);
|
|
198
408
|
await reporting.batteryVoltage(endpoint);
|
|
199
|
-
await endpoint.read('genBasic', [0x8000, 0x8010, 0x8020],
|
|
409
|
+
await endpoint.read('genBasic', [0x8000, 0x8010, 0x8020], manufacturerOptions);
|
|
200
410
|
await endpoint.read('ssIasZone', ['iasCieAddr', 'zoneState', 'zoneId']);
|
|
201
411
|
await endpoint.read('genBinaryInput', ['reliability', 'statusFlags']);
|
|
202
412
|
await endpoint.read('ssIasWd', ['maxDuration']);
|
|
@@ -221,16 +431,15 @@ module.exports = [
|
|
|
221
431
|
vendor: 'Develco',
|
|
222
432
|
description: 'Fire detector with siren',
|
|
223
433
|
fromZigbee: [fz.temperature, fz.battery, fz.ias_smoke_alarm_1_develco, fz.ignore_basic_report,
|
|
224
|
-
fz.
|
|
434
|
+
develco.fz.firmware_version, fz.ias_enroll, fz.ias_wd, develco.fz.fault_status],
|
|
225
435
|
toZigbee: [tz.warning, tz.ias_max_duration, tz.warning_simple],
|
|
226
436
|
meta: {battery: {voltageToPercentage: '3V_2500'}},
|
|
227
437
|
configure: async (device, coordinatorEndpoint, logger) => {
|
|
228
|
-
const options = {manufacturerCode: 4117};
|
|
229
438
|
const endpoint = device.getEndpoint(35);
|
|
230
439
|
|
|
231
440
|
await reporting.bind(endpoint, coordinatorEndpoint, ['genPowerCfg', 'ssIasZone', 'ssIasWd', 'genBasic', 'genBinaryInput']);
|
|
232
441
|
await reporting.batteryVoltage(endpoint);
|
|
233
|
-
await endpoint.read('genBasic', [0x8000],
|
|
442
|
+
await endpoint.read('genBasic', [0x8000], manufacturerOptions);
|
|
234
443
|
await endpoint.read('ssIasZone', ['iasCieAddr', 'zoneState', 'zoneId']);
|
|
235
444
|
await endpoint.read('genBinaryInput', ['reliability', 'statusFlags']);
|
|
236
445
|
await endpoint.read('ssIasWd', ['maxDuration']);
|
|
@@ -249,15 +458,6 @@ module.exports = [
|
|
|
249
458
|
.withDescription('Indicates reason if any fault'),
|
|
250
459
|
exposes.binary('fault', ea.STATE, true, false).withDescription('Indicates whether the device are in fault state')],
|
|
251
460
|
},
|
|
252
|
-
{
|
|
253
|
-
zigbeeModel: ['MOSZB-130'],
|
|
254
|
-
model: 'MOSZB-130',
|
|
255
|
-
vendor: 'Develco',
|
|
256
|
-
description: 'Motion sensor',
|
|
257
|
-
fromZigbee: [fz.ias_occupancy_alarm_1],
|
|
258
|
-
toZigbee: [],
|
|
259
|
-
exposes: [e.occupancy(), e.battery_low(), e.tamper()],
|
|
260
|
-
},
|
|
261
461
|
{
|
|
262
462
|
zigbeeModel: ['WISZB-120'],
|
|
263
463
|
model: 'WISZB-120',
|
|
@@ -285,22 +485,48 @@ module.exports = [
|
|
|
285
485
|
toZigbee: [],
|
|
286
486
|
exposes: [e.contact(), e.battery_low()],
|
|
287
487
|
},
|
|
488
|
+
{
|
|
489
|
+
zigbeeModel: ['MOSZB-130'],
|
|
490
|
+
model: 'MOSZB-130',
|
|
491
|
+
vendor: 'Develco',
|
|
492
|
+
description: 'Motion sensor',
|
|
493
|
+
fromZigbee: [fz.ias_occupancy_alarm_1],
|
|
494
|
+
toZigbee: [],
|
|
495
|
+
exposes: [e.occupancy(), e.battery_low(), e.tamper()],
|
|
496
|
+
},
|
|
288
497
|
{
|
|
289
498
|
zigbeeModel: ['MOSZB-140'],
|
|
290
499
|
model: 'MOSZB-140',
|
|
291
500
|
vendor: 'Develco',
|
|
292
501
|
description: 'Motion sensor',
|
|
293
|
-
fromZigbee: [
|
|
294
|
-
|
|
295
|
-
|
|
502
|
+
fromZigbee: [
|
|
503
|
+
fz.temperature, fz.illuminance, fz.ias_occupancy_alarm_1, fz.battery,
|
|
504
|
+
develco.fz.led_control, develco.fz.ias_occupancy_timeout,
|
|
505
|
+
],
|
|
506
|
+
toZigbee: [develco.tz.led_control, develco.tz.ias_occupancy_timeout],
|
|
507
|
+
exposes: [
|
|
508
|
+
e.occupancy(), e.battery(), e.battery_low(),
|
|
509
|
+
e.tamper(), e.temperature(), e.illuminance_lux(),
|
|
510
|
+
exposes.enum('led_control', ea.ALL, ['off', 'fault_only', 'motion_only', 'both']).
|
|
511
|
+
withDescription('Control LED indicator usage.'),
|
|
512
|
+
exposes.numeric('occupancy_timeout', ea.ALL).withUnit('second').withValueMin(20).withValueMax(65535),
|
|
513
|
+
],
|
|
296
514
|
meta: {battery: {voltageToPercentage: '3V_2500'}},
|
|
515
|
+
endpoint: (device) => {
|
|
516
|
+
return {default: 35};
|
|
517
|
+
},
|
|
297
518
|
configure: async (device, coordinatorEndpoint, logger) => {
|
|
298
519
|
const endpoint1 = device.getEndpoint(35);
|
|
299
520
|
await reporting.bind(endpoint1, coordinatorEndpoint, ['genPowerCfg']);
|
|
300
521
|
await reporting.batteryVoltage(endpoint1, {min: constants.repInterval.HOUR, max: 43200, change: 100});
|
|
522
|
+
await endpoint1.read('genPowerCfg', ['batteryVoltage']);
|
|
523
|
+
await endpoint1.read('genBasic', ['develcoLedControl'], manufacturerOptions);
|
|
524
|
+
await endpoint1.read('ssIasZone', ['develcoAlarmOffDelay'], manufacturerOptions);
|
|
525
|
+
|
|
301
526
|
const endpoint2 = device.getEndpoint(38);
|
|
302
527
|
await reporting.bind(endpoint2, coordinatorEndpoint, ['msTemperatureMeasurement']);
|
|
303
528
|
await reporting.temperature(endpoint2);
|
|
529
|
+
|
|
304
530
|
const endpoint3 = device.getEndpoint(39);
|
|
305
531
|
await reporting.bind(endpoint3, coordinatorEndpoint, ['msIlluminanceMeasurement']);
|
|
306
532
|
await reporting.illuminance(endpoint3);
|
|
@@ -337,8 +563,8 @@ module.exports = [
|
|
|
337
563
|
model: 'ZHEMI101',
|
|
338
564
|
vendor: 'Develco',
|
|
339
565
|
description: 'Energy meter',
|
|
340
|
-
fromZigbee: [fz.metering, fz.
|
|
341
|
-
toZigbee: [tz.
|
|
566
|
+
fromZigbee: [fz.metering, develco.fz.pulse_configuration, develco.fz.interface_mode],
|
|
567
|
+
toZigbee: [develco.tz.pulse_configuration, develco.tz.interface_mode, develco.tz.current_summation],
|
|
342
568
|
endpoint: (device) => {
|
|
343
569
|
return {'default': 2};
|
|
344
570
|
},
|
|
@@ -401,7 +627,7 @@ module.exports = [
|
|
|
401
627
|
model: 'AQSZB-110',
|
|
402
628
|
vendor: 'Develco',
|
|
403
629
|
description: 'Air quality sensor',
|
|
404
|
-
fromZigbee: [fz.
|
|
630
|
+
fromZigbee: [develco.fz.voc, develco.fz.voc_battery, fz.temperature, fz.humidity],
|
|
405
631
|
toZigbee: [],
|
|
406
632
|
exposes: [
|
|
407
633
|
e.voc(), e.temperature(), e.humidity(),
|
|
@@ -414,11 +640,10 @@ module.exports = [
|
|
|
414
640
|
meta: {battery: {voltageToPercentage: '3V_2500'}},
|
|
415
641
|
configure: async (device, coordinatorEndpoint, logger) => {
|
|
416
642
|
const endpoint = device.getEndpoint(38);
|
|
417
|
-
const options = {manufacturerCode: 0x1015};
|
|
418
643
|
await reporting.bind(endpoint, coordinatorEndpoint,
|
|
419
644
|
['develcoSpecificAirQuality', 'msTemperatureMeasurement', 'msRelativeHumidity', 'genPowerCfg']);
|
|
420
645
|
await endpoint.configureReporting('develcoSpecificAirQuality', [{attribute: 'measuredValue', minimumReportInterval: 60,
|
|
421
|
-
maximumReportInterval: 3600, reportableChange: 10}],
|
|
646
|
+
maximumReportInterval: 3600, reportableChange: 10}], manufacturerOptions);
|
|
422
647
|
await reporting.temperature(endpoint, {min: constants.repInterval.MINUTE, max: constants.repInterval.MINUTES_10, change: 10});
|
|
423
648
|
await reporting.humidity(endpoint, {min: constants.repInterval.MINUTE, max: constants.repInterval.MINUTES_10, change: 300});
|
|
424
649
|
await reporting.batteryVoltage(endpoint, {min: constants.repInterval.HOUR, max: 43200, change: 100});
|
|
@@ -429,15 +654,14 @@ module.exports = [
|
|
|
429
654
|
model: 'SIRZB-110',
|
|
430
655
|
vendor: 'Develco Products A/S',
|
|
431
656
|
description: 'Customizable siren',
|
|
432
|
-
fromZigbee: [fz.temperature, fz.battery, fz.ias_enroll, fz.ias_wd, fz.
|
|
657
|
+
fromZigbee: [fz.temperature, fz.battery, fz.ias_enroll, fz.ias_wd, develco.fz.firmware_version, fz.ias_siren],
|
|
433
658
|
toZigbee: [tz.warning, tz.warning_simple, tz.ias_max_duration, tz.squawk],
|
|
434
659
|
meta: {battery: {voltageToPercentage: '3V_2500'}},
|
|
435
660
|
configure: async (device, coordinatorEndpoint, logger) => {
|
|
436
|
-
const options = {manufacturerCode: 4117};
|
|
437
661
|
const endpoint = device.getEndpoint(43);
|
|
438
662
|
await reporting.bind(endpoint, coordinatorEndpoint, ['genPowerCfg', 'ssIasZone', 'ssIasWd', 'genBasic']);
|
|
439
663
|
await reporting.batteryVoltage(endpoint);
|
|
440
|
-
await endpoint.read('genBasic', [0x8000],
|
|
664
|
+
await endpoint.read('genBasic', [0x8000], manufacturerOptions);
|
|
441
665
|
await endpoint.read('ssIasZone', ['iasCieAddr', 'zoneState', 'zoneId']);
|
|
442
666
|
await endpoint.read('ssIasWd', ['maxDuration']);
|
|
443
667
|
|
package/devices/hfh.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
const extend = require('../lib/extend');
|
|
2
|
+
|
|
3
|
+
module.exports = [
|
|
4
|
+
{
|
|
5
|
+
zigbeeModel: ['On-Air Combi CTW,303-0136'],
|
|
6
|
+
model: '303-0136',
|
|
7
|
+
vendor: 'HFH Solutions',
|
|
8
|
+
description: 'LED controller',
|
|
9
|
+
extend: extend.light_onoff_brightness_colortemp({colorTempRange: [155, 495]}),
|
|
10
|
+
},
|
|
11
|
+
];
|
package/devices/philips.js
CHANGED
|
@@ -700,6 +700,13 @@ module.exports = [
|
|
|
700
700
|
description: 'Hue Flourish white and color ambiance table light with Bluetooth',
|
|
701
701
|
extend: hueExtend.light_onoff_brightness_colortemp_color({colorTempRange: [153, 500]}),
|
|
702
702
|
},
|
|
703
|
+
{
|
|
704
|
+
zigbeeModel: ['929003052601'],
|
|
705
|
+
model: '929003052601',
|
|
706
|
+
vendor: 'Philips',
|
|
707
|
+
description: 'Hue Flourish white and color ambiance table light with Bluetooth',
|
|
708
|
+
extend: hueExtend.light_onoff_brightness_colortemp_color({colorTempRange: [153, 500]}),
|
|
709
|
+
},
|
|
703
710
|
{
|
|
704
711
|
zigbeeModel: ['LCG002'],
|
|
705
712
|
model: '929001953101',
|
|
@@ -2803,7 +2810,7 @@ module.exports = [
|
|
|
2803
2810
|
zigbeeModel: ['915005998101'],
|
|
2804
2811
|
model: '915005998101',
|
|
2805
2812
|
vendor: 'Philips',
|
|
2806
|
-
description: 'Hue white ambiance
|
|
2813
|
+
description: 'Hue white ambiance pendant black Enrave',
|
|
2807
2814
|
extend: hueExtend.light_onoff_brightness_colortemp({colorTempRange: [153, 454]}),
|
|
2808
2815
|
},
|
|
2809
2816
|
{
|
|
@@ -2813,4 +2820,11 @@ module.exports = [
|
|
|
2813
2820
|
description: 'Hue Tuar outdoor wall light',
|
|
2814
2821
|
extend: hueExtend.light_onoff_brightness(),
|
|
2815
2822
|
},
|
|
2823
|
+
{
|
|
2824
|
+
zigbeeModel: ['915005998001'],
|
|
2825
|
+
model: '915005998001',
|
|
2826
|
+
vendor: 'Philips',
|
|
2827
|
+
description: 'Hue white ambiance pendant white Enrave',
|
|
2828
|
+
extend: hueExtend.light_onoff_brightness_colortemp({colorTempRange: [153, 454]}),
|
|
2829
|
+
},
|
|
2816
2830
|
];
|
package/devices/tuya.js
CHANGED
|
@@ -897,6 +897,7 @@ module.exports = [
|
|
|
897
897
|
{modelID: 'TS0505B', manufacturerName: '_TZ3210_mzdax7ha'},
|
|
898
898
|
{modelID: 'TS0505B', manufacturerName: '_TZB210_tmi0rihb'},
|
|
899
899
|
{modelID: 'TS0505B', manufacturerName: '_TZ3210_a4s41wm4'},
|
|
900
|
+
{modelID: 'TS0505B', manufacturerName: '_TZ3210_awrucboq'},
|
|
900
901
|
{modelID: 'TS0505B', manufacturerName: '_TZ3210_ijczzg9h'},
|
|
901
902
|
{modelID: 'TS0505B', manufacturerName: '_TZ3210_qxenlrin'},
|
|
902
903
|
{modelID: 'TS0505B', manufacturerName: '_TZ3210_iwbaamgh'},
|
|
@@ -907,7 +908,8 @@ module.exports = [
|
|
|
907
908
|
{modelID: 'TS0505B', manufacturerName: '_TZ3210_6amjviba'},
|
|
908
909
|
{modelID: 'TS0505B', manufacturerName: '_TZ3000_xr5m6kfg'},
|
|
909
910
|
{modelID: 'TS0505B', manufacturerName: '_TZ3210_xr5m6kfg'},
|
|
910
|
-
{modelID: 'TS0505B', manufacturerName: '_TZ3210_bf175wi4'}
|
|
911
|
+
{modelID: 'TS0505B', manufacturerName: '_TZ3210_bf175wi4'},
|
|
912
|
+
{modelID: 'TS0505B', manufacturerName: '_TZB210_3zfp8mki'}],
|
|
911
913
|
model: 'TS0505B',
|
|
912
914
|
vendor: 'TuYa',
|
|
913
915
|
description: 'Zigbee RGB+CCT light',
|
|
@@ -1137,8 +1139,6 @@ module.exports = [
|
|
|
1137
1139
|
{modelID: 'TS0601', manufacturerName: '_TZE200_ojzhk75b'},
|
|
1138
1140
|
{modelID: 'TS0601', manufacturerName: '_TZE200_swaamsoy'},
|
|
1139
1141
|
{modelID: 'TS0601', manufacturerName: '_TZE200_3p5ydos3'},
|
|
1140
|
-
{modelID: 'TS0601', manufacturerName: '_TZE200_1agwnems'},
|
|
1141
|
-
{modelID: 'TS0601', manufacturerName: '_TZE200_ip2akl4w'},
|
|
1142
1142
|
],
|
|
1143
1143
|
model: 'TS0601_dimmer',
|
|
1144
1144
|
vendor: 'TuYa',
|
|
@@ -1162,6 +1162,27 @@ module.exports = [
|
|
|
1162
1162
|
{vendor: 'Moes', model: 'EDM-1ZBB-EU'},
|
|
1163
1163
|
],
|
|
1164
1164
|
},
|
|
1165
|
+
{
|
|
1166
|
+
fingerprint: tuya.fingerprint('TS0601', ['_TZE200_ip2akl4w', '_TZE200_1agwnems']),
|
|
1167
|
+
model: 'TS0601_dimmer_1',
|
|
1168
|
+
vendor: 'TuYa',
|
|
1169
|
+
description: 'Zigbee smart dimmer',
|
|
1170
|
+
fromZigbee: [tuya.fzDataPoints],
|
|
1171
|
+
toZigbee: [tuya.tzDataPoints],
|
|
1172
|
+
configure: tuya.configureMagicPacket,
|
|
1173
|
+
exposes: [tuya.exposes.lightBrightnessWithMinMax, tuya.exposes.powerOnBehavior, tuya.exposes.countdown, tuya.exposes.lightType],
|
|
1174
|
+
meta: {
|
|
1175
|
+
tuyaDatapoints: [
|
|
1176
|
+
[1, 'state', tuya.valueConverter.onOff, {skip: tuya.skip.stateOnAndBrightnessPresent}],
|
|
1177
|
+
[2, 'brightness', tuya.valueConverter.scale0_254to0_1000],
|
|
1178
|
+
[3, 'min_brightness', tuya.valueConverter.scale0_254to0_1000],
|
|
1179
|
+
[4, 'light_type', tuya.valueConverter.lightType],
|
|
1180
|
+
[5, 'max_brightness', tuya.valueConverter.scale0_254to0_1000],
|
|
1181
|
+
[6, 'countdown', tuya.valueConverter.countdown],
|
|
1182
|
+
[14, 'power_on_behavior', tuya.valueConverter.powerOnBehavior],
|
|
1183
|
+
],
|
|
1184
|
+
},
|
|
1185
|
+
},
|
|
1165
1186
|
{
|
|
1166
1187
|
fingerprint: [{modelID: 'TS011F', manufacturerName: '_TZ3000_oiymh3qu'}],
|
|
1167
1188
|
model: 'TS011F_socket_module',
|
package/lib/ota/common.js
CHANGED
|
@@ -182,12 +182,12 @@ function sendQueryNextImageResponse(endpoint, image, logger) {
|
|
|
182
182
|
}
|
|
183
183
|
|
|
184
184
|
function imageNotify(endpoint) {
|
|
185
|
-
return endpoint.commandResponse('genOta', 'imageNotify', {payloadType: 0, queryJitter: 100});
|
|
185
|
+
return endpoint.commandResponse('genOta', 'imageNotify', {payloadType: 0, queryJitter: 100}, {sendWhen: 'immediate'});
|
|
186
186
|
}
|
|
187
187
|
|
|
188
188
|
async function requestOTA(endpoint) {
|
|
189
189
|
// Some devices (e.g. Insta) take very long trying to discover the correct coordinator EP for OTA.
|
|
190
|
-
const queryNextImageRequest = endpoint.waitForCommand('genOta', 'queryNextImageRequest', null,
|
|
190
|
+
const queryNextImageRequest = endpoint.waitForCommand('genOta', 'queryNextImageRequest', null, 60000);
|
|
191
191
|
try {
|
|
192
192
|
await imageNotify(endpoint);
|
|
193
193
|
return await queryNextImageRequest.promise;
|
package/lib/tuya.js
CHANGED
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
const constants = require('./constants');
|
|
4
4
|
const globalStore = require('./store');
|
|
5
|
+
const exposes = require('./exposes');
|
|
6
|
+
const utils = require('./utils');
|
|
7
|
+
const e = exposes.presets;
|
|
8
|
+
const ea = exposes.access;
|
|
5
9
|
|
|
6
10
|
const dataTypes = {
|
|
7
11
|
raw: 0, // [ bytes ]
|
|
@@ -1131,7 +1135,132 @@ async function sendDataPointStringBuffer(entity, dp, value, cmd, seq=undefined)
|
|
|
1131
1135
|
);
|
|
1132
1136
|
}
|
|
1133
1137
|
|
|
1138
|
+
const tuyaExposes = {
|
|
1139
|
+
powerOnBehavior: exposes.enum('power_on_behavior', ea.STATE_SET, ['off', 'on', 'previous'])
|
|
1140
|
+
.withDescription('Controls the behavior when the device is powered on'),
|
|
1141
|
+
lightType: exposes.enum('light_type', ea.STATE_SET, ['led', 'incandescent', 'halogen'])
|
|
1142
|
+
.withDescription('Type of light attached to the device'),
|
|
1143
|
+
lightBrightnessWithMinMax: e.light_brightness().withMinBrightness().withMaxBrightness().setAccess(
|
|
1144
|
+
'state', ea.STATE_SET).setAccess('brightness', ea.STATE_SET).setAccess(
|
|
1145
|
+
'min_brightness', ea.STATE_SET).setAccess('max_brightness', ea.STATE_SET),
|
|
1146
|
+
countdown: exposes.numeric('countdown', ea.STATE_SET).withValueMin(0).withValueMax(43200).withValueStep(1).withUnit('s')
|
|
1147
|
+
.withDescription('Countdown to turn device off after a certain time'),
|
|
1148
|
+
};
|
|
1149
|
+
|
|
1150
|
+
const skip = {
|
|
1151
|
+
// Prevent state from being published when already ON and brightness is also published.
|
|
1152
|
+
// This prevents 100% -> X% brightness jumps when the switch is already on
|
|
1153
|
+
// https://github.com/Koenkk/zigbee2mqtt/issues/13800#issuecomment-1263592783
|
|
1154
|
+
stateOnAndBrightnessPresent: (meta) => meta.message.hasOwnProperty('brightness') && meta.state.state === 'ON',
|
|
1155
|
+
};
|
|
1156
|
+
|
|
1157
|
+
const configureMagicPacket = async (device, coordinatorEndpoint, logger) => {
|
|
1158
|
+
const endpoint = device.endpoints[0];
|
|
1159
|
+
await endpoint.read('genBasic', ['manufacturerName', 'zclVersion', 'appVersion', 'modelId', 'powerSource', 0xfffe]);
|
|
1160
|
+
};
|
|
1161
|
+
|
|
1162
|
+
const fingerprint = (modelID, manufacturerNames) => {
|
|
1163
|
+
return manufacturerNames.map((manufacturerName) => {
|
|
1164
|
+
return {modelID, manufacturerName};
|
|
1165
|
+
});
|
|
1166
|
+
};
|
|
1167
|
+
|
|
1168
|
+
const valueConverterBasic = {
|
|
1169
|
+
lookup: (map) => {
|
|
1170
|
+
return {
|
|
1171
|
+
to: (v) => {
|
|
1172
|
+
if (map[v] === undefined) throw new Error(`Value '${v}' is not allowed, expected one of ${Object.keys(map)}`);
|
|
1173
|
+
return map[v];
|
|
1174
|
+
},
|
|
1175
|
+
from: (v) => {
|
|
1176
|
+
const value = Object.entries(map).find((i) => i[1] === v);
|
|
1177
|
+
if (!value) throw new Error(`Value '${v}' is not allowed, expected one of ${Object.values(map)}`);
|
|
1178
|
+
return value[0];
|
|
1179
|
+
},
|
|
1180
|
+
};
|
|
1181
|
+
},
|
|
1182
|
+
scale: (min1, max1, min2, max2) => {
|
|
1183
|
+
return {to: (v) => utils.mapNumberRange(v, min1, max1, min2, max2), from: (v) => utils.mapNumberRange(v, min2, max2, min1, max1)};
|
|
1184
|
+
},
|
|
1185
|
+
raw: () => {
|
|
1186
|
+
return {to: (v) => v, from: (v) => v};
|
|
1187
|
+
},
|
|
1188
|
+
divideBy: (value) => {
|
|
1189
|
+
return {to: (v) => v * value, from: (v) => v / value};
|
|
1190
|
+
},
|
|
1191
|
+
};
|
|
1192
|
+
|
|
1193
|
+
const valueConverter = {
|
|
1194
|
+
trueFalse: valueConverterBasic.lookup({1: true, 0: false}),
|
|
1195
|
+
onOff: valueConverterBasic.lookup({'ON': true, 'OFF': false}),
|
|
1196
|
+
powerOnBehavior: valueConverterBasic.lookup({'off': 0, 'on': 1, 'previous': 2}),
|
|
1197
|
+
lightType: valueConverterBasic.lookup({'led': 0, 'incandescent': 1, 'halogen': 2}),
|
|
1198
|
+
countdown: valueConverterBasic.raw(),
|
|
1199
|
+
scale0_254to0_1000: valueConverterBasic.scale(0, 254, 0, 1000),
|
|
1200
|
+
scale0_1to0_1000: valueConverterBasic.scale(0, 1, 0, 1000),
|
|
1201
|
+
divideBy100: valueConverterBasic.divideBy(100),
|
|
1202
|
+
};
|
|
1203
|
+
|
|
1204
|
+
const tzDataPoints = {
|
|
1205
|
+
key: ['state', 'brightness', 'min_brightness', 'max_brightness', 'power_on_behavior', 'countdown', 'light_type'],
|
|
1206
|
+
convertSet: async (entity, key, value, meta) => {
|
|
1207
|
+
// A set converter is only called once; therefore we need to loop
|
|
1208
|
+
const state = {};
|
|
1209
|
+
if (!meta.mapped.meta || !meta.mapped.meta.tuyaDatapoints) throw new Error('No datapoints map defined');
|
|
1210
|
+
const datapoints = meta.mapped.meta.tuyaDatapoints;
|
|
1211
|
+
for (const [key, value] of Object.entries(meta.message)) {
|
|
1212
|
+
const convertedKey = meta.mapped.meta.multiEndpoint ? `${key}_${meta.endpoint_name}` : key;
|
|
1213
|
+
const dpEntry = datapoints.find((d) => d[1] === convertedKey);
|
|
1214
|
+
if (!dpEntry || !dpEntry[1]) {
|
|
1215
|
+
throw new Error(`No datapoint defined for '${key}'`);
|
|
1216
|
+
}
|
|
1217
|
+
if (dpEntry[3] && dpEntry[3].skip && dpEntry[3].skip(meta)) continue;
|
|
1218
|
+
const dpId = dpEntry[0];
|
|
1219
|
+
const convertedValue = dpEntry[2].to(value);
|
|
1220
|
+
if (typeof convertedValue === 'boolean') {
|
|
1221
|
+
await sendDataPointBool(entity, dpId, convertedValue, 'dataRequest', 1);
|
|
1222
|
+
} else if (typeof convertedValue === 'number') {
|
|
1223
|
+
await sendDataPointValue(entity, dpId, convertedValue, 'dataRequest', 1);
|
|
1224
|
+
} else {
|
|
1225
|
+
throw new Error(`Don't know how to send type '${typeof convertedValue}'`);
|
|
1226
|
+
}
|
|
1227
|
+
state[convertedKey] = value;
|
|
1228
|
+
}
|
|
1229
|
+
return {state};
|
|
1230
|
+
},
|
|
1231
|
+
};
|
|
1232
|
+
|
|
1233
|
+
const fzDataPoints = {
|
|
1234
|
+
cluster: 'manuSpecificTuya',
|
|
1235
|
+
type: ['commandDataResponse', 'commandDataReport'],
|
|
1236
|
+
convert: (model, msg, publish, options, meta) => {
|
|
1237
|
+
const result = {};
|
|
1238
|
+
if (!model.meta || !model.meta.tuyaDatapoints) throw new Error('No datapoints map defined');
|
|
1239
|
+
const datapoints = model.meta.tuyaDatapoints;
|
|
1240
|
+
for (const dpValue of msg.data.dpValues) {
|
|
1241
|
+
const dpId = dpValue.dp;
|
|
1242
|
+
const dpEntry = datapoints.find((d) => d[0] === dpId);
|
|
1243
|
+
if (dpEntry) {
|
|
1244
|
+
const value = getDataValue(dpValue);
|
|
1245
|
+
result[dpEntry[1]] = dpEntry[2].from(value);
|
|
1246
|
+
} else {
|
|
1247
|
+
meta.logger.warn(`Datapoint ${dpId} not defined for '${meta.device.manufacturerName}' ` +
|
|
1248
|
+
`with data ${JSON.stringify(dpValue)}`);
|
|
1249
|
+
}
|
|
1250
|
+
}
|
|
1251
|
+
return result;
|
|
1252
|
+
},
|
|
1253
|
+
};
|
|
1254
|
+
|
|
1134
1255
|
module.exports = {
|
|
1256
|
+
exposes: tuyaExposes,
|
|
1257
|
+
skip,
|
|
1258
|
+
configureMagicPacket,
|
|
1259
|
+
fingerprint,
|
|
1260
|
+
valueConverterBasic,
|
|
1261
|
+
valueConverter,
|
|
1262
|
+
tzDataPoints,
|
|
1263
|
+
fzDataPoints,
|
|
1135
1264
|
sendDataPoint,
|
|
1136
1265
|
sendDataPoints,
|
|
1137
1266
|
sendDataPointValue,
|