zigbee-herdsman-converters 14.0.306 → 14.0.310
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/README.md +1 -0
- package/converters/fromZigbee.js +7 -12
- package/converters/toZigbee.js +13 -2
- package/devices/bitron.js +1 -2
- package/devices/candeo.js +1 -1
- package/devices/danfoss.js +4 -4
- package/devices/envilar.js +18 -0
- package/devices/eurotronic.js +5 -5
- package/devices/girier.js +20 -0
- package/devices/heiman.js +13 -13
- package/devices/hive.js +38 -38
- package/devices/ikea.js +4 -3
- package/devices/moes.js +3 -1
- package/devices/philips.js +27 -0
- package/devices/popp.js +4 -4
- package/devices/schneider_electric.js +8 -9
- package/devices/sengled.js +7 -0
- package/devices/sinope.js +15 -15
- package/devices/stelpro.js +43 -28
- package/devices/tuya.js +16 -7
- package/devices/viessmann.js +4 -4
- package/devices/xiaomi.js +27 -16
- package/lib/exposes.js +2 -2
- package/lib/legacy.js +2 -2
- package/npm-shrinkwrap.json +1065 -1163
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -31,3 +31,4 @@ If any of those commands finish with an error your PR won't pass the tests and w
|
|
|
31
31
|
- `battery`:
|
|
32
32
|
- `{dontDividePercentage: true}`: prevents batteryPercentageRemainig from being divided (ZCL 200=100%, but some report 100=100%) (default: false)
|
|
33
33
|
- `{voltageToPercentage: '3V_2100'}`: convert voltage to percentage using specified option. See utils.batteryVoltageToPercentage() (default: null, no voltage to percentage conversion)
|
|
34
|
+
- `fanStateOn`: value used for fan_mode when using fan_state="ON", the default is "on"
|
package/converters/fromZigbee.js
CHANGED
|
@@ -4752,6 +4752,10 @@ const converters = {
|
|
|
4752
4752
|
const lookup = {4: 'anti_flicker_mode', 1: 'quick_mode'};
|
|
4753
4753
|
payload.mode_switch = lookup[msg.data['4']];
|
|
4754
4754
|
}
|
|
4755
|
+
if (msg.data.hasOwnProperty('10')) {
|
|
4756
|
+
const lookup = {1: 'toggle', 2: 'momentary'};
|
|
4757
|
+
payload.switch_type = lookup[msg.data['10']];
|
|
4758
|
+
}
|
|
4755
4759
|
if (msg.data.hasOwnProperty('512')) {
|
|
4756
4760
|
if (['ZNCZ15LM', 'QBCZ14LM', 'QBCZ15LM'].includes(model.model)) {
|
|
4757
4761
|
payload.button_lock = msg.data['512'] === 1 ? 'OFF' : 'ON';
|
|
@@ -4841,7 +4845,7 @@ const converters = {
|
|
|
4841
4845
|
}
|
|
4842
4846
|
|
|
4843
4847
|
let buttonLookup = null;
|
|
4844
|
-
if (['WXKG02LM_rev2', 'WXKG07LM'].includes(model.model)) buttonLookup = {1: 'left', 2: 'right', 3: 'both'};
|
|
4848
|
+
if (['WXKG02LM_rev2', 'WXKG07LM', 'WXKG17LM'].includes(model.model)) buttonLookup = {1: 'left', 2: 'right', 3: 'both'};
|
|
4845
4849
|
if (['QBKG12LM', 'QBKG24LM'].includes(model.model)) buttonLookup = {5: 'left', 6: 'right', 7: 'both'};
|
|
4846
4850
|
if (['QBKG25LM', 'QBKG26LM'].includes(model.model)) buttonLookup = {41: 'left', 42: 'center', 43: 'right'};
|
|
4847
4851
|
if (['QBKG39LM', 'QBKG41LM', 'WS-EUK02', 'WS-EUK04', 'QBKG20LM', 'QBKG31LM'].includes(model.model)) {
|
|
@@ -4986,15 +4990,6 @@ const converters = {
|
|
|
4986
4990
|
}
|
|
4987
4991
|
},
|
|
4988
4992
|
},
|
|
4989
|
-
WSDCGQ11LM_pressure: {
|
|
4990
|
-
cluster: 'msPressureMeasurement',
|
|
4991
|
-
type: ['attributeReport', 'readResponse'],
|
|
4992
|
-
options: [exposes.options.precision('pressure'), exposes.options.calibration('pressure')],
|
|
4993
|
-
convert: (model, msg, publish, options, meta) => {
|
|
4994
|
-
const pressure = msg.data.hasOwnProperty('16') ? parseFloat(msg.data['16']) / 10 : parseFloat(msg.data['measuredValue']);
|
|
4995
|
-
return {pressure: calibrateAndPrecisionRoundOptions(pressure, options, 'pressure')};
|
|
4996
|
-
},
|
|
4997
|
-
},
|
|
4998
4993
|
W2_module_carbon_monoxide: {
|
|
4999
4994
|
cluster: 'ssIasZone',
|
|
5000
4995
|
type: 'commandStatusChangeNotification',
|
|
@@ -7179,10 +7174,10 @@ const converters = {
|
|
|
7179
7174
|
result.voltage_rms = (value[1] | value[0] << 8) / 10;
|
|
7180
7175
|
}
|
|
7181
7176
|
if (dp === tuya.dataPoints.hochCurrent) {
|
|
7182
|
-
result.current = value[2] | value[1] << 8;
|
|
7177
|
+
result.current = (value[2] | value[1] << 8) / 1000;
|
|
7183
7178
|
}
|
|
7184
7179
|
if (dp === tuya.dataPoints.hochHistoricalCurrent) {
|
|
7185
|
-
result.current_average = value[2] | value[1] << 8;
|
|
7180
|
+
result.current_average = (value[2] | value[1] << 8) / 1000;
|
|
7186
7181
|
}
|
|
7187
7182
|
if (dp === tuya.dataPoints.hochActivePower) {
|
|
7188
7183
|
result.power = (value[2] | value[1] << 8) / 10;
|
package/converters/toZigbee.js
CHANGED
|
@@ -1192,6 +1192,9 @@ const converters = {
|
|
|
1192
1192
|
fan_mode: {
|
|
1193
1193
|
key: ['fan_mode', 'fan_state'],
|
|
1194
1194
|
convertSet: async (entity, key, value, meta) => {
|
|
1195
|
+
if (key == 'fan_state' && value.toLowerCase() == 'on') {
|
|
1196
|
+
value = utils.getMetaValue(entity, meta.mapped, 'fanStateOn', 'allEqual', 'on');
|
|
1197
|
+
}
|
|
1195
1198
|
const fanMode = constants.fanMode[value.toLowerCase()];
|
|
1196
1199
|
await entity.write('hvacFanCtrl', {fanMode});
|
|
1197
1200
|
return {state: {fan_mode: value.toLowerCase(), fan_state: value.toLowerCase() === 'off' ? 'OFF' : 'ON'}};
|
|
@@ -2075,7 +2078,7 @@ const converters = {
|
|
|
2075
2078
|
xiaomi_led_disabled_night: {
|
|
2076
2079
|
key: ['led_disabled_night'],
|
|
2077
2080
|
convertSet: async (entity, key, value, meta) => {
|
|
2078
|
-
if (['ZNCZ04LM', 'ZNCZ15LM', 'QBCZ15LM', 'QBCZ14LM', 'QBKG20LM', 'QBKG25LM', 'QBKG19LM',
|
|
2081
|
+
if (['ZNCZ04LM', 'ZNCZ15LM', 'QBCZ15LM', 'QBCZ14LM', 'QBKG20LM', 'QBKG25LM', 'QBKG19LM', 'DLKZMK11LM',
|
|
2079
2082
|
'QBKG34LM'].includes(meta.mapped.model)) {
|
|
2080
2083
|
await entity.write('aqaraOpple', {0x0203: {value: value ? 1 : 0, type: 0x10}}, manufacturerOptions.xiaomi);
|
|
2081
2084
|
} else if (['ZNCZ11LM'].includes(meta.mapped.model)) {
|
|
@@ -2090,7 +2093,7 @@ const converters = {
|
|
|
2090
2093
|
return {state: {led_disabled_night: value}};
|
|
2091
2094
|
},
|
|
2092
2095
|
convertGet: async (entity, key, meta) => {
|
|
2093
|
-
if (['ZNCZ04LM', 'ZNCZ15LM', 'QBCZ15LM', 'QBCZ14LM', 'QBKG20LM', 'QBKG25LM', 'QBKG19LM',
|
|
2096
|
+
if (['ZNCZ04LM', 'ZNCZ15LM', 'QBCZ15LM', 'QBCZ14LM', 'QBKG20LM', 'QBKG25LM', 'QBKG19LM', 'DLKZMK11LM',
|
|
2094
2097
|
'QBKG34LM'].includes(meta.mapped.model)) {
|
|
2095
2098
|
await entity.read('aqaraOpple', [0x0203], manufacturerOptions.xiaomi);
|
|
2096
2099
|
} else {
|
|
@@ -6114,6 +6117,14 @@ const converters = {
|
|
|
6114
6117
|
await endpoint.read('genOnOff', ['tuyaOperationMode']);
|
|
6115
6118
|
},
|
|
6116
6119
|
},
|
|
6120
|
+
xiaomi_switch_click_mode: {
|
|
6121
|
+
key: ['click_mode'],
|
|
6122
|
+
convertSet: async (entity, key, value, meta) => {
|
|
6123
|
+
const lookupState = {'fast': 0x1, 'multi': 0x02};
|
|
6124
|
+
await entity.write('aqaraOpple', {0x0125: {value: lookupState[value], type: 0x20}}, manufacturerOptions.xiaomi);
|
|
6125
|
+
return {state: {click_mode: value}};
|
|
6126
|
+
},
|
|
6127
|
+
},
|
|
6117
6128
|
// #endregion
|
|
6118
6129
|
|
|
6119
6130
|
// #region Ignore converters
|
package/devices/bitron.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
const exposes = require('../lib/exposes');
|
|
2
2
|
const fz = {...require('../converters/fromZigbee'), legacy: require('../lib/legacy').fromZigbee};
|
|
3
3
|
const tz = require('../converters/toZigbee');
|
|
4
|
-
const constants = require('../lib/constants');
|
|
5
4
|
const reporting = require('../lib/reporting');
|
|
6
5
|
const extend = require('../lib/extend');
|
|
7
6
|
const e = exposes.presets;
|
|
@@ -199,7 +198,7 @@ module.exports = [
|
|
|
199
198
|
'genBasic', 'genPowerCfg', 'genIdentify', 'genPollCtrl', 'hvacThermostat', 'hvacUserInterfaceCfg',
|
|
200
199
|
];
|
|
201
200
|
await reporting.bind(endpoint, coordinatorEndpoint, binds);
|
|
202
|
-
await reporting.thermostatTemperature(endpoint
|
|
201
|
+
await reporting.thermostatTemperature(endpoint);
|
|
203
202
|
await reporting.thermostatTemperatureCalibration(endpoint);
|
|
204
203
|
await reporting.thermostatOccupiedHeatingSetpoint(endpoint);
|
|
205
204
|
await reporting.thermostatRunningState(endpoint);
|
package/devices/candeo.js
CHANGED
|
@@ -13,7 +13,7 @@ module.exports = [
|
|
|
13
13
|
const endpoint = device.getEndpoint(1);
|
|
14
14
|
await reporting.bind(endpoint, coordinatorEndpoint, ['genOnOff', 'genLevelCtrl']);
|
|
15
15
|
await reporting.onOff(endpoint);
|
|
16
|
-
await reporting.brightness(endpoint
|
|
16
|
+
await reporting.brightness(endpoint);
|
|
17
17
|
},
|
|
18
18
|
},
|
|
19
19
|
];
|
package/devices/danfoss.js
CHANGED
|
@@ -73,15 +73,15 @@ module.exports = [
|
|
|
73
73
|
|
|
74
74
|
// standard ZCL attributes
|
|
75
75
|
await reporting.batteryPercentageRemaining(endpoint);
|
|
76
|
-
await reporting.thermostatTemperature(endpoint
|
|
76
|
+
await reporting.thermostatTemperature(endpoint);
|
|
77
77
|
await reporting.thermostatPIHeatingDemand(endpoint);
|
|
78
|
-
await reporting.thermostatOccupiedHeatingSetpoint(endpoint
|
|
78
|
+
await reporting.thermostatOccupiedHeatingSetpoint(endpoint);
|
|
79
79
|
|
|
80
80
|
// danfoss attributes
|
|
81
81
|
await endpoint.configureReporting('hvacThermostat', [{
|
|
82
82
|
attribute: 'danfossMountedModeActive',
|
|
83
83
|
minimumReportInterval: constants.repInterval.MINUTE,
|
|
84
|
-
maximumReportInterval:
|
|
84
|
+
maximumReportInterval: constants.repInterval.MAX,
|
|
85
85
|
reportableChange: 1,
|
|
86
86
|
}], options);
|
|
87
87
|
await endpoint.configureReporting('hvacThermostat', [{
|
|
@@ -93,7 +93,7 @@ module.exports = [
|
|
|
93
93
|
await endpoint.configureReporting('hvacThermostat', [{
|
|
94
94
|
attribute: 'danfossHeatRequired',
|
|
95
95
|
minimumReportInterval: constants.repInterval.MINUTE,
|
|
96
|
-
maximumReportInterval: constants.repInterval.
|
|
96
|
+
maximumReportInterval: constants.repInterval.HOUR,
|
|
97
97
|
reportableChange: 1,
|
|
98
98
|
}], options);
|
|
99
99
|
await endpoint.configureReporting('hvacThermostat', [{
|
package/devices/envilar.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
const reporting = require('../lib/reporting');
|
|
2
2
|
const extend = require('../lib/extend');
|
|
3
|
+
const exposes = require('zigbee-herdsman-converters/lib/exposes');
|
|
4
|
+
const e = exposes.presets;
|
|
3
5
|
|
|
4
6
|
module.exports = [
|
|
5
7
|
{
|
|
@@ -26,4 +28,20 @@ module.exports = [
|
|
|
26
28
|
await reporting.bind(endpoint, coordinatorEndpoint, ['genBasic', 'genIdentify', 'genOnOff']);
|
|
27
29
|
},
|
|
28
30
|
},
|
|
31
|
+
{
|
|
32
|
+
zigbeeModel: ['2CH-ZG-BOX-RELAY'],
|
|
33
|
+
model: '2CH-ZG-BOX-RELAY',
|
|
34
|
+
vendor: 'Envilar',
|
|
35
|
+
description: '2 channel box relay',
|
|
36
|
+
extend: extend.switch(),
|
|
37
|
+
exposes: [e.switch().withEndpoint('l1'), e.switch().withEndpoint('l2')],
|
|
38
|
+
endpoint: (device) => {
|
|
39
|
+
return {'l1': 1, 'l2': 2};
|
|
40
|
+
},
|
|
41
|
+
meta: {multiEndpoint: true},
|
|
42
|
+
configure: async (device, coordinatorEndpoint, logger) => {
|
|
43
|
+
await reporting.bind(device.getEndpoint(1), coordinatorEndpoint, ['genOnOff']);
|
|
44
|
+
await reporting.bind(device.getEndpoint(2), coordinatorEndpoint, ['genOnOff']);
|
|
45
|
+
},
|
|
46
|
+
},
|
|
29
47
|
];
|
package/devices/eurotronic.js
CHANGED
|
@@ -34,12 +34,12 @@ module.exports = [
|
|
|
34
34
|
const endpoint = device.getEndpoint(1);
|
|
35
35
|
const options = {manufacturerCode: 4151};
|
|
36
36
|
await reporting.bind(endpoint, coordinatorEndpoint, ['genPowerCfg', 'hvacThermostat']);
|
|
37
|
-
await reporting.thermostatTemperature(endpoint
|
|
38
|
-
await reporting.thermostatPIHeatingDemand(endpoint
|
|
39
|
-
await reporting.thermostatOccupiedHeatingSetpoint(endpoint
|
|
40
|
-
await reporting.thermostatUnoccupiedHeatingSetpoint(endpoint
|
|
37
|
+
await reporting.thermostatTemperature(endpoint);
|
|
38
|
+
await reporting.thermostatPIHeatingDemand(endpoint);
|
|
39
|
+
await reporting.thermostatOccupiedHeatingSetpoint(endpoint);
|
|
40
|
+
await reporting.thermostatUnoccupiedHeatingSetpoint(endpoint);
|
|
41
41
|
await endpoint.configureReporting('hvacThermostat', [{attribute: {ID: 0x4003, type: 41}, minimumReportInterval: 0,
|
|
42
|
-
maximumReportInterval: constants.repInterval.
|
|
42
|
+
maximumReportInterval: constants.repInterval.HOUR, reportableChange: 25}], options);
|
|
43
43
|
await endpoint.configureReporting('hvacThermostat', [{attribute: {ID: 0x4008, type: 34}, minimumReportInterval: 0,
|
|
44
44
|
maximumReportInterval: constants.repInterval.HOUR, reportableChange: 1}], options);
|
|
45
45
|
},
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
const fz = require('../converters/fromZigbee');
|
|
2
|
+
const tz = require('../converters/toZigbee');
|
|
3
|
+
const exposes = require('../lib/exposes');
|
|
4
|
+
const reporting = require('../lib/reporting');
|
|
5
|
+
const extend = require('../lib/extend');
|
|
6
|
+
|
|
7
|
+
module.exports = [
|
|
8
|
+
{
|
|
9
|
+
fingerprint: [{modelID: 'TS0001', manufacturerName: '_TZ3000_majwnphg'}],
|
|
10
|
+
model: 'JR-ZDS01',
|
|
11
|
+
vendor: 'Girier',
|
|
12
|
+
description: '1 gang mini switch',
|
|
13
|
+
toZigbee: extend.switch().toZigbee.concat([tz.moes_power_on_behavior, tz.tuya_switch_type]),
|
|
14
|
+
fromZigbee: extend.switch().fromZigbee.concat([fz.moes_power_on_behavior, fz.tuya_switch_type]),
|
|
15
|
+
exposes: extend.switch().exposes.concat([exposes.presets.power_on_behavior(), exposes.presets.switch_type_2()]),
|
|
16
|
+
configure: async (device, coordinatorEndpoint, logger) => {
|
|
17
|
+
await reporting.bind(device.getEndpoint(1), coordinatorEndpoint, ['genOnOff']);
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
];
|
package/devices/heiman.js
CHANGED
|
@@ -149,7 +149,7 @@ module.exports = [
|
|
|
149
149
|
configure: async (device, coordinatorEndpoint, logger) => {
|
|
150
150
|
const endpoint = device.getEndpoint(1);
|
|
151
151
|
await reporting.bind(endpoint, coordinatorEndpoint, ['genPowerCfg']);
|
|
152
|
-
await reporting.batteryPercentageRemaining(endpoint
|
|
152
|
+
await reporting.batteryPercentageRemaining(endpoint);
|
|
153
153
|
await endpoint.read('genPowerCfg', ['batteryPercentageRemaining']);
|
|
154
154
|
},
|
|
155
155
|
exposes: [e.contact(), e.battery(), e.battery_low(), e.tamper()],
|
|
@@ -164,7 +164,7 @@ module.exports = [
|
|
|
164
164
|
configure: async (device, coordinatorEndpoint, logger) => {
|
|
165
165
|
const endpoint = device.getEndpoint(1);
|
|
166
166
|
await reporting.bind(endpoint, coordinatorEndpoint, ['genPowerCfg']);
|
|
167
|
-
await reporting.batteryPercentageRemaining(endpoint
|
|
167
|
+
await reporting.batteryPercentageRemaining(endpoint);
|
|
168
168
|
await endpoint.read('genPowerCfg', ['batteryPercentageRemaining']);
|
|
169
169
|
},
|
|
170
170
|
exposes: [e.contact(), e.battery_low(), e.tamper(), e.battery()],
|
|
@@ -188,7 +188,7 @@ module.exports = [
|
|
|
188
188
|
configure: async (device, coordinatorEndpoint, logger) => {
|
|
189
189
|
const endpoint = device.getEndpoint(1);
|
|
190
190
|
await reporting.bind(endpoint, coordinatorEndpoint, ['genPowerCfg']);
|
|
191
|
-
await reporting.batteryPercentageRemaining(endpoint
|
|
191
|
+
await reporting.batteryPercentageRemaining(endpoint);
|
|
192
192
|
await endpoint.read('genPowerCfg', ['batteryPercentageRemaining']);
|
|
193
193
|
},
|
|
194
194
|
exposes: [e.water_leak(), e.battery_low(), e.tamper(), e.battery()],
|
|
@@ -204,7 +204,7 @@ module.exports = [
|
|
|
204
204
|
configure: async (device, coordinatorEndpoint, logger) => {
|
|
205
205
|
const endpoint = device.getEndpoint(1);
|
|
206
206
|
await reporting.bind(endpoint, coordinatorEndpoint, ['genPowerCfg']);
|
|
207
|
-
await reporting.batteryPercentageRemaining(endpoint
|
|
207
|
+
await reporting.batteryPercentageRemaining(endpoint);
|
|
208
208
|
await endpoint.read('genPowerCfg', ['batteryPercentageRemaining']);
|
|
209
209
|
},
|
|
210
210
|
},
|
|
@@ -219,7 +219,7 @@ module.exports = [
|
|
|
219
219
|
configure: async (device, coordinatorEndpoint, logger) => {
|
|
220
220
|
const endpoint = device.getEndpoint(1);
|
|
221
221
|
await reporting.bind(endpoint, coordinatorEndpoint, ['genPowerCfg']);
|
|
222
|
-
await reporting.batteryPercentageRemaining(endpoint
|
|
222
|
+
await reporting.batteryPercentageRemaining(endpoint);
|
|
223
223
|
await endpoint.read('genPowerCfg', ['batteryPercentageRemaining']);
|
|
224
224
|
},
|
|
225
225
|
onEvent: async (type, data, device) => {
|
|
@@ -241,7 +241,7 @@ module.exports = [
|
|
|
241
241
|
configure: async (device, coordinatorEndpoint, logger) => {
|
|
242
242
|
const endpoint = device.getEndpoint(1);
|
|
243
243
|
await reporting.bind(endpoint, coordinatorEndpoint, ['genPowerCfg']);
|
|
244
|
-
await reporting.batteryPercentageRemaining(endpoint
|
|
244
|
+
await reporting.batteryPercentageRemaining(endpoint);
|
|
245
245
|
await endpoint.read('genPowerCfg', ['batteryPercentageRemaining']);
|
|
246
246
|
},
|
|
247
247
|
},
|
|
@@ -334,7 +334,7 @@ module.exports = [
|
|
|
334
334
|
const endpoint1 = device.getEndpoint(1);
|
|
335
335
|
await reporting.bind(endpoint1, coordinatorEndpoint, ['msTemperatureMeasurement', 'genPowerCfg']);
|
|
336
336
|
await reporting.temperature(endpoint1);
|
|
337
|
-
await reporting.batteryPercentageRemaining(endpoint1
|
|
337
|
+
await reporting.batteryPercentageRemaining(endpoint1);
|
|
338
338
|
await endpoint1.read('genPowerCfg', ['batteryPercentageRemaining']);
|
|
339
339
|
|
|
340
340
|
const endpoint2 = device.getEndpoint(2);
|
|
@@ -405,7 +405,7 @@ module.exports = [
|
|
|
405
405
|
const bindClusters = ['msTemperatureMeasurement', 'msRelativeHumidity', 'genPowerCfg'];
|
|
406
406
|
await reporting.bind(endpoint, coordinatorEndpoint, bindClusters);
|
|
407
407
|
await reporting.temperature(endpoint);
|
|
408
|
-
await reporting.humidity(endpoint
|
|
408
|
+
await reporting.humidity(endpoint);
|
|
409
409
|
await reporting.batteryVoltage(endpoint);
|
|
410
410
|
},
|
|
411
411
|
exposes: [e.temperature(), e.humidity(), e.battery()],
|
|
@@ -421,7 +421,7 @@ module.exports = [
|
|
|
421
421
|
configure: async (device, coordinatorEndpoint, logger) => {
|
|
422
422
|
const endpoint = device.getEndpoint(1);
|
|
423
423
|
await reporting.bind(endpoint, coordinatorEndpoint, ['genPowerCfg']);
|
|
424
|
-
await reporting.batteryPercentageRemaining(endpoint
|
|
424
|
+
await reporting.batteryPercentageRemaining(endpoint);
|
|
425
425
|
await endpoint.read('genPowerCfg', ['batteryPercentageRemaining']);
|
|
426
426
|
},
|
|
427
427
|
},
|
|
@@ -436,7 +436,7 @@ module.exports = [
|
|
|
436
436
|
configure: async (device, coordinatorEndpoint, logger) => {
|
|
437
437
|
const endpoint = device.getEndpoint(1);
|
|
438
438
|
await reporting.bind(endpoint, coordinatorEndpoint, ['genPowerCfg', 'heimanSpecificScenes']);
|
|
439
|
-
await reporting.batteryPercentageRemaining(endpoint
|
|
439
|
+
await reporting.batteryPercentageRemaining(endpoint);
|
|
440
440
|
},
|
|
441
441
|
},
|
|
442
442
|
{
|
|
@@ -468,7 +468,7 @@ module.exports = [
|
|
|
468
468
|
configure: async (device, coordinatorEndpoint, logger) => {
|
|
469
469
|
const endpoint = device.getEndpoint(1);
|
|
470
470
|
await reporting.bind(endpoint, coordinatorEndpoint, ['genPowerCfg']);
|
|
471
|
-
await reporting.batteryPercentageRemaining(endpoint
|
|
471
|
+
await reporting.batteryPercentageRemaining(endpoint);
|
|
472
472
|
await endpoint.read('genPowerCfg', ['batteryPercentageRemaining']);
|
|
473
473
|
},
|
|
474
474
|
exposes: [e.vibration(), e.battery_low(), e.tamper(), e.battery()],
|
|
@@ -483,7 +483,7 @@ module.exports = [
|
|
|
483
483
|
configure: async (device, coordinatorEndpoint, logger) => {
|
|
484
484
|
const endpoint = device.getEndpoint(1);
|
|
485
485
|
await reporting.bind(endpoint, coordinatorEndpoint, ['genPowerCfg']);
|
|
486
|
-
await reporting.batteryPercentageRemaining(endpoint
|
|
486
|
+
await reporting.batteryPercentageRemaining(endpoint);
|
|
487
487
|
await endpoint.read('genPowerCfg', ['batteryPercentageRemaining']);
|
|
488
488
|
},
|
|
489
489
|
exposes: [e.vibration(), e.battery_low(), e.tamper(), e.battery()],
|
|
@@ -564,7 +564,7 @@ module.exports = [
|
|
|
564
564
|
configure: async (device, coordinatorEndpoint, logger) => {
|
|
565
565
|
const endpoint = device.getEndpoint(1);
|
|
566
566
|
await reporting.bind(endpoint, coordinatorEndpoint, ['genPowerCfg', 'heimanSpecificInfraRedRemote']);
|
|
567
|
-
await reporting.batteryPercentageRemaining(endpoint
|
|
567
|
+
await reporting.batteryPercentageRemaining(endpoint);
|
|
568
568
|
},
|
|
569
569
|
},
|
|
570
570
|
{
|
package/devices/hive.js
CHANGED
|
@@ -213,15 +213,15 @@ module.exports = [
|
|
|
213
213
|
|
|
214
214
|
// standard ZCL attributes
|
|
215
215
|
await reporting.batteryPercentageRemaining(endpoint);
|
|
216
|
-
await reporting.thermostatTemperature(endpoint
|
|
216
|
+
await reporting.thermostatTemperature(endpoint);
|
|
217
217
|
await reporting.thermostatPIHeatingDemand(endpoint);
|
|
218
|
-
await reporting.thermostatOccupiedHeatingSetpoint(endpoint
|
|
218
|
+
await reporting.thermostatOccupiedHeatingSetpoint(endpoint);
|
|
219
219
|
|
|
220
220
|
// danfoss attributes
|
|
221
221
|
await endpoint.configureReporting('hvacThermostat', [{
|
|
222
222
|
attribute: 'danfossMountedModeActive',
|
|
223
223
|
minimumReportInterval: constants.repInterval.MINUTE,
|
|
224
|
-
maximumReportInterval:
|
|
224
|
+
maximumReportInterval: constants.repInterval.MAX,
|
|
225
225
|
reportableChange: 1,
|
|
226
226
|
}], options);
|
|
227
227
|
await endpoint.configureReporting('hvacThermostat', [{
|
|
@@ -233,7 +233,7 @@ module.exports = [
|
|
|
233
233
|
await endpoint.configureReporting('hvacThermostat', [{
|
|
234
234
|
attribute: 'danfossHeatRequired',
|
|
235
235
|
minimumReportInterval: constants.repInterval.MINUTE,
|
|
236
|
-
maximumReportInterval: constants.repInterval.
|
|
236
|
+
maximumReportInterval: constants.repInterval.HOUR,
|
|
237
237
|
reportableChange: 1,
|
|
238
238
|
}], options);
|
|
239
239
|
await endpoint.configureReporting('hvacThermostat', [{
|
|
@@ -293,12 +293,12 @@ module.exports = [
|
|
|
293
293
|
const endpoint = device.getEndpoint(5);
|
|
294
294
|
const binds = ['genBasic', 'genIdentify', 'genAlarms', 'genTime', 'hvacThermostat'];
|
|
295
295
|
await reporting.bind(endpoint, coordinatorEndpoint, binds);
|
|
296
|
-
await reporting.thermostatTemperature(endpoint
|
|
297
|
-
await reporting.thermostatRunningState(endpoint
|
|
298
|
-
await reporting.thermostatSystemMode(endpoint
|
|
299
|
-
await reporting.thermostatOccupiedHeatingSetpoint(endpoint
|
|
300
|
-
await reporting.thermostatTemperatureSetpointHold(endpoint
|
|
301
|
-
await reporting.thermostatTemperatureSetpointHoldDuration(endpoint
|
|
296
|
+
await reporting.thermostatTemperature(endpoint);
|
|
297
|
+
await reporting.thermostatRunningState(endpoint);
|
|
298
|
+
await reporting.thermostatSystemMode(endpoint);
|
|
299
|
+
await reporting.thermostatOccupiedHeatingSetpoint(endpoint);
|
|
300
|
+
await reporting.thermostatTemperatureSetpointHold(endpoint);
|
|
301
|
+
await reporting.thermostatTemperatureSetpointHoldDuration(endpoint);
|
|
302
302
|
},
|
|
303
303
|
},
|
|
304
304
|
{
|
|
@@ -324,12 +324,12 @@ module.exports = [
|
|
|
324
324
|
const endpoint = device.getEndpoint(5);
|
|
325
325
|
const binds = ['genBasic', 'genIdentify', 'genAlarms', 'genTime', 'hvacThermostat'];
|
|
326
326
|
await reporting.bind(endpoint, coordinatorEndpoint, binds);
|
|
327
|
-
await reporting.thermostatTemperature(endpoint
|
|
328
|
-
await reporting.thermostatRunningState(endpoint
|
|
329
|
-
await reporting.thermostatSystemMode(endpoint
|
|
330
|
-
await reporting.thermostatOccupiedHeatingSetpoint(endpoint
|
|
331
|
-
await reporting.thermostatTemperatureSetpointHold(endpoint
|
|
332
|
-
await reporting.thermostatTemperatureSetpointHoldDuration(endpoint
|
|
327
|
+
await reporting.thermostatTemperature(endpoint);
|
|
328
|
+
await reporting.thermostatRunningState(endpoint);
|
|
329
|
+
await reporting.thermostatSystemMode(endpoint);
|
|
330
|
+
await reporting.thermostatOccupiedHeatingSetpoint(endpoint);
|
|
331
|
+
await reporting.thermostatTemperatureSetpointHold(endpoint);
|
|
332
|
+
await reporting.thermostatTemperatureSetpointHoldDuration(endpoint);
|
|
333
333
|
},
|
|
334
334
|
},
|
|
335
335
|
{
|
|
@@ -352,18 +352,18 @@ module.exports = [
|
|
|
352
352
|
'genBasic', 'genIdentify', 'genAlarms', 'genTime', 'hvacThermostat',
|
|
353
353
|
];
|
|
354
354
|
await reporting.bind(heatEndpoint, coordinatorEndpoint, binds);
|
|
355
|
-
await reporting.thermostatTemperature(heatEndpoint
|
|
356
|
-
await reporting.thermostatRunningState(heatEndpoint
|
|
357
|
-
await reporting.thermostatSystemMode(heatEndpoint
|
|
358
|
-
await reporting.thermostatOccupiedHeatingSetpoint(heatEndpoint
|
|
359
|
-
await reporting.thermostatTemperatureSetpointHold(heatEndpoint
|
|
360
|
-
await reporting.thermostatTemperatureSetpointHoldDuration(heatEndpoint
|
|
355
|
+
await reporting.thermostatTemperature(heatEndpoint);
|
|
356
|
+
await reporting.thermostatRunningState(heatEndpoint);
|
|
357
|
+
await reporting.thermostatSystemMode(heatEndpoint);
|
|
358
|
+
await reporting.thermostatOccupiedHeatingSetpoint(heatEndpoint);
|
|
359
|
+
await reporting.thermostatTemperatureSetpointHold(heatEndpoint);
|
|
360
|
+
await reporting.thermostatTemperatureSetpointHoldDuration(heatEndpoint);
|
|
361
361
|
await reporting.bind(waterEndpoint, coordinatorEndpoint, binds);
|
|
362
|
-
await reporting.thermostatRunningState(waterEndpoint
|
|
363
|
-
await reporting.thermostatSystemMode(waterEndpoint
|
|
364
|
-
await reporting.thermostatOccupiedHeatingSetpoint(waterEndpoint
|
|
365
|
-
await reporting.thermostatTemperatureSetpointHold(waterEndpoint
|
|
366
|
-
await reporting.thermostatTemperatureSetpointHoldDuration(waterEndpoint
|
|
362
|
+
await reporting.thermostatRunningState(waterEndpoint);
|
|
363
|
+
await reporting.thermostatSystemMode(waterEndpoint);
|
|
364
|
+
await reporting.thermostatOccupiedHeatingSetpoint(waterEndpoint);
|
|
365
|
+
await reporting.thermostatTemperatureSetpointHold(waterEndpoint);
|
|
366
|
+
await reporting.thermostatTemperatureSetpointHoldDuration(waterEndpoint);
|
|
367
367
|
},
|
|
368
368
|
exposes: [
|
|
369
369
|
exposes.climate().withSetpoint('occupied_heating_setpoint', 5, 32, 0.5).withLocalTemperature()
|
|
@@ -403,18 +403,18 @@ module.exports = [
|
|
|
403
403
|
'genBasic', 'genIdentify', 'genAlarms', 'genTime', 'hvacThermostat',
|
|
404
404
|
];
|
|
405
405
|
await reporting.bind(heatEndpoint, coordinatorEndpoint, binds);
|
|
406
|
-
await reporting.thermostatTemperature(heatEndpoint
|
|
407
|
-
await reporting.thermostatRunningState(heatEndpoint
|
|
408
|
-
await reporting.thermostatSystemMode(heatEndpoint
|
|
409
|
-
await reporting.thermostatOccupiedHeatingSetpoint(heatEndpoint
|
|
410
|
-
await reporting.thermostatTemperatureSetpointHold(heatEndpoint
|
|
411
|
-
await reporting.thermostatTemperatureSetpointHoldDuration(heatEndpoint
|
|
406
|
+
await reporting.thermostatTemperature(heatEndpoint);
|
|
407
|
+
await reporting.thermostatRunningState(heatEndpoint);
|
|
408
|
+
await reporting.thermostatSystemMode(heatEndpoint);
|
|
409
|
+
await reporting.thermostatOccupiedHeatingSetpoint(heatEndpoint);
|
|
410
|
+
await reporting.thermostatTemperatureSetpointHold(heatEndpoint);
|
|
411
|
+
await reporting.thermostatTemperatureSetpointHoldDuration(heatEndpoint);
|
|
412
412
|
await reporting.bind(waterEndpoint, coordinatorEndpoint, binds);
|
|
413
|
-
await reporting.thermostatRunningState(waterEndpoint
|
|
414
|
-
await reporting.thermostatSystemMode(waterEndpoint
|
|
415
|
-
await reporting.thermostatOccupiedHeatingSetpoint(waterEndpoint
|
|
416
|
-
await reporting.thermostatTemperatureSetpointHold(waterEndpoint
|
|
417
|
-
await reporting.thermostatTemperatureSetpointHoldDuration(waterEndpoint
|
|
413
|
+
await reporting.thermostatRunningState(waterEndpoint);
|
|
414
|
+
await reporting.thermostatSystemMode(waterEndpoint);
|
|
415
|
+
await reporting.thermostatOccupiedHeatingSetpoint(waterEndpoint);
|
|
416
|
+
await reporting.thermostatTemperatureSetpointHold(waterEndpoint);
|
|
417
|
+
await reporting.thermostatTemperatureSetpointHoldDuration(waterEndpoint);
|
|
418
418
|
},
|
|
419
419
|
exposes: [
|
|
420
420
|
exposes.climate().withSetpoint('occupied_heating_setpoint', 5, 32, 0.5).withLocalTemperature()
|
package/devices/ikea.js
CHANGED
|
@@ -446,8 +446,8 @@ module.exports = [
|
|
|
446
446
|
model: 'E1812',
|
|
447
447
|
vendor: 'IKEA',
|
|
448
448
|
description: 'TRADFRI shortcut button',
|
|
449
|
-
fromZigbee: [fz.command_on, fz.command_move, fz.command_stop, fz.battery],
|
|
450
|
-
exposes: [e.battery(), e.action(['on', 'brightness_move_up', 'brightness_stop'])],
|
|
449
|
+
fromZigbee: [fz.command_on, fz.command_off, fz.command_move, fz.command_stop, fz.battery],
|
|
450
|
+
exposes: [e.battery(), e.action(['on', 'off', 'brightness_move_up', 'brightness_stop'])],
|
|
451
451
|
toZigbee: [],
|
|
452
452
|
ota: ota.tradfri,
|
|
453
453
|
meta: {disableActionGroup: true, battery: {dontDividePercentage: true}},
|
|
@@ -625,7 +625,8 @@ module.exports = [
|
|
|
625
625
|
model: 'E2007',
|
|
626
626
|
vendor: 'IKEA',
|
|
627
627
|
description: 'STARKVIND air purifier',
|
|
628
|
-
exposes: [e.fan().withModes(['off', 'low', 'medium', 'high', '
|
|
628
|
+
exposes: [e.fan().withModes(['off', 'low', 'medium', 'high', 'auto'])],
|
|
629
|
+
meta: {fanStateOn: 'auto'},
|
|
629
630
|
fromZigbee: [fz.fan],
|
|
630
631
|
toZigbee: [tz.fan_mode],
|
|
631
632
|
configure: async (device, coordinatorEndpoint, logger) => {
|
package/devices/moes.js
CHANGED
|
@@ -288,11 +288,13 @@ module.exports = [
|
|
|
288
288
|
exposes.binary('motor_reversal', ea.ALL, 'ON', 'OFF')],
|
|
289
289
|
},
|
|
290
290
|
{
|
|
291
|
-
fingerprint: [{modelID: 'TS0601', manufacturerName: '_TZE200_e9ba97vf'}
|
|
291
|
+
fingerprint: [{modelID: 'TS0601', manufacturerName: '_TZE200_e9ba97vf'},
|
|
292
|
+
{modelID: 'TS0601', manufacturerName: '_TZE200_husqqvux'}],
|
|
292
293
|
model: 'TV01-ZB',
|
|
293
294
|
vendor: 'Moes',
|
|
294
295
|
description: 'Thermostat radiator valve',
|
|
295
296
|
fromZigbee: [fz.moes_thermostat_tv, fz.ignore_tuya_set_time],
|
|
297
|
+
whiteLabel: [{vendor: 'Tesla Smart', model: 'TSL-TRV-TV01ZG'}],
|
|
296
298
|
toZigbee: [tz.moes_thermostat_tv],
|
|
297
299
|
exposes: [
|
|
298
300
|
e.battery(), e.child_lock(), e.window_detection(),
|
package/devices/philips.js
CHANGED
|
@@ -350,6 +350,15 @@ module.exports = [
|
|
|
350
350
|
extend: hueExtend.light_onoff_brightness_colortemp_color({colorTempRange: [153, 500]}),
|
|
351
351
|
ota: ota.zigbeeOTA,
|
|
352
352
|
},
|
|
353
|
+
{
|
|
354
|
+
zigbeeModel: ['1742330P7'],
|
|
355
|
+
model: '1742330P7',
|
|
356
|
+
vendor: 'Philips',
|
|
357
|
+
description: 'Hue Calla outdoor',
|
|
358
|
+
meta: {turnsOffAtBrightness1: true},
|
|
359
|
+
extend: hueExtend.light_onoff_brightness_colortemp_color({colorTempRange: [153, 500]}),
|
|
360
|
+
ota: ota.zigbeeOTA,
|
|
361
|
+
},
|
|
353
362
|
{
|
|
354
363
|
zigbeeModel: ['1744130P7'],
|
|
355
364
|
model: '1744130P7',
|
|
@@ -555,6 +564,15 @@ module.exports = [
|
|
|
555
564
|
extend: hueExtend.light_onoff_brightness_colortemp(),
|
|
556
565
|
ota: ota.zigbeeOTA,
|
|
557
566
|
},
|
|
567
|
+
{
|
|
568
|
+
zigbeeModel: ['LTA004'],
|
|
569
|
+
model: '8719514328242',
|
|
570
|
+
vendor: 'Philips',
|
|
571
|
+
description: 'Hue white ambiance E27 800lm with Bluetooth',
|
|
572
|
+
meta: {turnsOffAtBrightness1: true},
|
|
573
|
+
extend: hueExtend.light_onoff_brightness_colortemp({colorTempRange: [153, 454]}),
|
|
574
|
+
ota: ota.zigbeeOTA,
|
|
575
|
+
},
|
|
558
576
|
{
|
|
559
577
|
zigbeeModel: ['LTA009'],
|
|
560
578
|
model: '9290024684',
|
|
@@ -564,6 +582,15 @@ module.exports = [
|
|
|
564
582
|
extend: hueExtend.light_onoff_brightness_colortemp({colorTempRange: [153, 454]}),
|
|
565
583
|
ota: ota.zigbeeOTA,
|
|
566
584
|
},
|
|
585
|
+
{
|
|
586
|
+
zigbeeModel: ['LTA010'],
|
|
587
|
+
model: '9290024683',
|
|
588
|
+
vendor: 'Philips',
|
|
589
|
+
description: 'Hue white ambiance A19 1100lm with Bluetooth',
|
|
590
|
+
meta: {turnsOffAtBrightness1: true},
|
|
591
|
+
extend: hueExtend.light_onoff_brightness_colortemp({colorTempRange: [153, 454]}),
|
|
592
|
+
ota: ota.zigbeeOTA,
|
|
593
|
+
},
|
|
567
594
|
{
|
|
568
595
|
zigbeeModel: ['LTA008'],
|
|
569
596
|
model: '9290022267A',
|
package/devices/popp.js
CHANGED
|
@@ -73,15 +73,15 @@ module.exports = [
|
|
|
73
73
|
|
|
74
74
|
// standard ZCL attributes
|
|
75
75
|
await reporting.batteryPercentageRemaining(endpoint);
|
|
76
|
-
await reporting.thermostatTemperature(endpoint
|
|
76
|
+
await reporting.thermostatTemperature(endpoint);
|
|
77
77
|
await reporting.thermostatPIHeatingDemand(endpoint);
|
|
78
|
-
await reporting.thermostatOccupiedHeatingSetpoint(endpoint
|
|
78
|
+
await reporting.thermostatOccupiedHeatingSetpoint(endpoint);
|
|
79
79
|
|
|
80
80
|
// danfoss attributes
|
|
81
81
|
await endpoint.configureReporting('hvacThermostat', [{
|
|
82
82
|
attribute: 'danfossMountedModeActive',
|
|
83
83
|
minimumReportInterval: constants.repInterval.MINUTE,
|
|
84
|
-
maximumReportInterval:
|
|
84
|
+
maximumReportInterval: constants.repInterval.MAX,
|
|
85
85
|
reportableChange: 1,
|
|
86
86
|
}], options);
|
|
87
87
|
await endpoint.configureReporting('hvacThermostat', [{
|
|
@@ -93,7 +93,7 @@ module.exports = [
|
|
|
93
93
|
await endpoint.configureReporting('hvacThermostat', [{
|
|
94
94
|
attribute: 'danfossHeatRequired',
|
|
95
95
|
minimumReportInterval: constants.repInterval.MINUTE,
|
|
96
|
-
maximumReportInterval: constants.repInterval.
|
|
96
|
+
maximumReportInterval: constants.repInterval.HOUR,
|
|
97
97
|
reportableChange: 1,
|
|
98
98
|
}], options);
|
|
99
99
|
await endpoint.configureReporting('hvacThermostat', [{
|
|
@@ -23,9 +23,9 @@ module.exports = [
|
|
|
23
23
|
const binds = ['genBasic', 'genPowerCfg', 'hvacThermostat', 'haDiagnostic'];
|
|
24
24
|
await reporting.bind(endpoint, coordinatorEndpoint, binds);
|
|
25
25
|
await reporting.batteryVoltage(endpoint);
|
|
26
|
-
await reporting.thermostatTemperature(endpoint
|
|
27
|
-
await reporting.thermostatOccupiedHeatingSetpoint(endpoint
|
|
28
|
-
await reporting.thermostatPIHeatingDemand(endpoint
|
|
26
|
+
await reporting.thermostatTemperature(endpoint);
|
|
27
|
+
await reporting.thermostatOccupiedHeatingSetpoint(endpoint);
|
|
28
|
+
await reporting.thermostatPIHeatingDemand(endpoint);
|
|
29
29
|
// bind of hvacUserInterfaceCfg fails with 'Table Full', does this have any effect?
|
|
30
30
|
await endpoint.configureReporting('hvacUserInterfaceCfg', [{attribute: 'keypadLockout', reportableChange: 1,
|
|
31
31
|
minimumReportInterval: constants.repInterval.MINUTE, maximumReportInterval: constants.repInterval.HOUR}]);
|
|
@@ -354,8 +354,8 @@ module.exports = [
|
|
|
354
354
|
const endpoint1 = device.getEndpoint(1);
|
|
355
355
|
const endpoint2 = device.getEndpoint(2);
|
|
356
356
|
await reporting.bind(endpoint1, coordinatorEndpoint, ['hvacThermostat']);
|
|
357
|
-
await reporting.thermostatOccupiedHeatingSetpoint(endpoint1
|
|
358
|
-
await reporting.thermostatPIHeatingDemand(endpoint1
|
|
357
|
+
await reporting.thermostatOccupiedHeatingSetpoint(endpoint1);
|
|
358
|
+
await reporting.thermostatPIHeatingDemand(endpoint1);
|
|
359
359
|
await reporting.bind(endpoint2, coordinatorEndpoint, ['seMetering']);
|
|
360
360
|
await reporting.instantaneousDemand(endpoint2, {min: 0, max: 60, change: 1});
|
|
361
361
|
await reporting.currentSummDelivered(endpoint2, {min: 0, max: 60, change: 1});
|
|
@@ -458,9 +458,8 @@ module.exports = [
|
|
|
458
458
|
const binds = ['genBasic', 'genPowerCfg', 'hvacThermostat'];
|
|
459
459
|
await reporting.bind(endpoint, coordinatorEndpointB, binds);
|
|
460
460
|
await reporting.batteryVoltage(endpoint);
|
|
461
|
-
await reporting.thermostatTemperature(endpoint
|
|
462
|
-
|
|
463
|
-
await reporting.thermostatOccupiedHeatingSetpoint(endpoint, {min: 0, max: constants.repInterval.MINUTES_15, change: 25});
|
|
461
|
+
await reporting.thermostatTemperature(endpoint);
|
|
462
|
+
await reporting.thermostatOccupiedHeatingSetpoint(endpoint);
|
|
464
463
|
await endpoint.configureReporting('hvacUserInterfaceCfg', [{attribute: 'keypadLockout',
|
|
465
464
|
minimumReportInterval: constants.repInterval.MINUTE,
|
|
466
465
|
maximumReportInterval: constants.repInterval.HOUR,
|
|
@@ -514,7 +513,7 @@ module.exports = [
|
|
|
514
513
|
const endpoint = device.getEndpoint(11);
|
|
515
514
|
const binds = ['genBasic', 'genPowerCfg', 'hvacThermostat', 'msTemperatureMeasurement'];
|
|
516
515
|
await reporting.bind(endpoint, coordinatorEndpoint, binds);
|
|
517
|
-
await reporting.thermostatOccupiedHeatingSetpoint(endpoint
|
|
516
|
+
await reporting.thermostatOccupiedHeatingSetpoint(endpoint);
|
|
518
517
|
},
|
|
519
518
|
},
|
|
520
519
|
{
|