zigbee-herdsman-converters 25.45.0 → 25.47.0
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/CHANGELOG.md +41 -0
- package/dist/devices/bosch.d.ts.map +1 -1
- package/dist/devices/bosch.js +82 -563
- package/dist/devices/bosch.js.map +1 -1
- package/dist/devices/box.d.ts.map +1 -1
- package/dist/devices/box.js +5 -4
- package/dist/devices/box.js.map +1 -1
- package/dist/devices/develco.js +2 -2
- package/dist/devices/develco.js.map +1 -1
- package/dist/devices/elko.js +4 -4
- package/dist/devices/elko.js.map +1 -1
- package/dist/devices/evn.d.ts.map +1 -1
- package/dist/devices/evn.js +7 -0
- package/dist/devices/evn.js.map +1 -1
- package/dist/devices/gewiss.js +4 -4
- package/dist/devices/gewiss.js.map +1 -1
- package/dist/devices/ikea.d.ts.map +1 -1
- package/dist/devices/ikea.js +14 -0
- package/dist/devices/ikea.js.map +1 -1
- package/dist/devices/innr.d.ts.map +1 -1
- package/dist/devices/innr.js +15 -1
- package/dist/devices/innr.js.map +1 -1
- package/dist/devices/lumi.d.ts.map +1 -1
- package/dist/devices/lumi.js +11 -6
- package/dist/devices/lumi.js.map +1 -1
- package/dist/devices/moes.js +4 -4
- package/dist/devices/moes.js.map +1 -1
- package/dist/devices/nordtronic.d.ts.map +1 -1
- package/dist/devices/nordtronic.js +7 -1
- package/dist/devices/nordtronic.js.map +1 -1
- package/dist/devices/paulmann.d.ts.map +1 -1
- package/dist/devices/paulmann.js +10 -3
- package/dist/devices/paulmann.js.map +1 -1
- package/dist/devices/philips.d.ts.map +1 -1
- package/dist/devices/philips.js +6 -9
- package/dist/devices/philips.js.map +1 -1
- package/dist/devices/schneider_electric.js +1 -1
- package/dist/devices/schneider_electric.js.map +1 -1
- package/dist/devices/sengled.js +1 -1
- package/dist/devices/sengled.js.map +1 -1
- package/dist/devices/sonoff.d.ts.map +1 -1
- package/dist/devices/sonoff.js +9 -0
- package/dist/devices/sonoff.js.map +1 -1
- package/dist/devices/third_reality.d.ts.map +1 -1
- package/dist/devices/third_reality.js +22 -0
- package/dist/devices/third_reality.js.map +1 -1
- package/dist/devices/tuya.d.ts.map +1 -1
- package/dist/devices/tuya.js +82 -18
- package/dist/devices/tuya.js.map +1 -1
- package/dist/devices/wirenboard.js +1 -1
- package/dist/devices/wirenboard.js.map +1 -1
- package/dist/lib/bosch.d.ts +134 -12
- package/dist/lib/bosch.d.ts.map +1 -1
- package/dist/lib/bosch.js +983 -69
- package/dist/lib/bosch.js.map +1 -1
- package/dist/lib/exposes.d.ts +0 -1
- package/dist/lib/exposes.d.ts.map +1 -1
- package/dist/lib/exposes.js +4 -8
- package/dist/lib/exposes.js.map +1 -1
- package/dist/lib/generateDefinition.js +2 -2
- package/dist/lib/generateDefinition.js.map +1 -1
- package/dist/lib/modernExtend.d.ts +28 -10
- package/dist/lib/modernExtend.d.ts.map +1 -1
- package/dist/lib/modernExtend.js +71 -24
- package/dist/lib/modernExtend.js.map +1 -1
- package/dist/lib/utils.js +3 -3
- package/dist/lib/utils.js.map +1 -1
- package/dist/models-index.json +1 -1
- package/package.json +1 -1
package/dist/lib/bosch.js
CHANGED
|
@@ -33,7 +33,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.boschSmartPlugExtend = exports.boschBsenExtend = exports.boschDoorWindowContactExtend = exports.boschBsirExtend = exports.boschBmctExtend = exports.boschGeneralExtend = exports.manufacturerOptions = void 0;
|
|
36
|
+
exports.boschThermostatExtend = exports.boschSmartPlugExtend = exports.boschWaterAlarmExtend = exports.boschBsenExtend = exports.boschDoorWindowContactExtend = exports.boschBsirExtend = exports.boschBmctExtend = exports.boschGeneralSensorDeviceExtend = exports.boschGeneralEnergyDeviceExtend = exports.boschGeneralExtend = exports.manufacturerOptions = void 0;
|
|
37
37
|
const zigbee_herdsman_1 = require("zigbee-herdsman");
|
|
38
38
|
const fz = __importStar(require("../converters/fromZigbee"));
|
|
39
39
|
const tz = __importStar(require("../converters/toZigbee"));
|
|
@@ -47,7 +47,11 @@ const utils_1 = require("./utils");
|
|
|
47
47
|
const e = exposes.presets;
|
|
48
48
|
const ea = exposes.access;
|
|
49
49
|
const NS = "zhc:bosch";
|
|
50
|
-
exports.manufacturerOptions = {
|
|
50
|
+
exports.manufacturerOptions = {
|
|
51
|
+
manufacturerCode: zigbee_herdsman_1.Zcl.ManufacturerCode.ROBERT_BOSCH_GMBH,
|
|
52
|
+
sendPolicy: "immediate",
|
|
53
|
+
};
|
|
54
|
+
//region Generally used Bosch functionality
|
|
51
55
|
exports.boschGeneralExtend = {
|
|
52
56
|
/** Some devices now use a different name for some custom clusters than
|
|
53
57
|
* originally used. This can lead to issues like those described in
|
|
@@ -98,10 +102,20 @@ exports.boschGeneralExtend = {
|
|
|
98
102
|
isModernExtend: true,
|
|
99
103
|
};
|
|
100
104
|
},
|
|
105
|
+
/** Some Bosch devices ask the coordinator for their ZCL version
|
|
106
|
+
* during deviceAnnouncement. Without answer, these devices regularly
|
|
107
|
+
* re-join the network. To avoid that, we have to make sure that a readRequest
|
|
108
|
+
* for the zclVersion is always being answered. The answered zclVersion is
|
|
109
|
+
* taken from the Bosch Smart Home Controller II.
|
|
110
|
+
*
|
|
111
|
+
* Exception: BTH-RM and BTH-RM230Z ask the coordinator at regular
|
|
112
|
+
* intervals for their zclVersion (maybe availability check like Z2M does?)
|
|
113
|
+
* and *not* during interview! To avoid code-duplication, we handle that
|
|
114
|
+
* case here as well. */
|
|
101
115
|
handleZclVersionReadRequest: () => {
|
|
102
116
|
const onEvent = [
|
|
103
117
|
(event) => {
|
|
104
|
-
if (event.type !== "
|
|
118
|
+
if (event.type !== "start") {
|
|
105
119
|
return;
|
|
106
120
|
}
|
|
107
121
|
event.data.device.customReadResponse = (frame, endpoint) => {
|
|
@@ -124,7 +138,16 @@ exports.boschGeneralExtend = {
|
|
|
124
138
|
isModernExtend: true,
|
|
125
139
|
};
|
|
126
140
|
},
|
|
127
|
-
|
|
141
|
+
batteryWithPercentageAndLowStatus: (args) => m.battery({
|
|
142
|
+
percentage: true,
|
|
143
|
+
percentageReportingConfig: false,
|
|
144
|
+
lowStatus: true,
|
|
145
|
+
lowStatusReportingConfig: { min: "MIN", max: "MAX", change: null },
|
|
146
|
+
...args,
|
|
147
|
+
}),
|
|
148
|
+
};
|
|
149
|
+
exports.boschGeneralEnergyDeviceExtend = {
|
|
150
|
+
customMeteringCluster: () => m.deviceAddCustomCluster("seMetering", {
|
|
128
151
|
ID: zigbee_herdsman_1.Zcl.Clusters.seMetering.ID,
|
|
129
152
|
attributes: {},
|
|
130
153
|
commands: {
|
|
@@ -252,6 +275,118 @@ exports.boschGeneralExtend = {
|
|
|
252
275
|
};
|
|
253
276
|
},
|
|
254
277
|
};
|
|
278
|
+
exports.boschGeneralSensorDeviceExtend = {
|
|
279
|
+
customIasZoneCluster: () => m.deviceAddCustomCluster("ssIasZone", {
|
|
280
|
+
ID: zigbee_herdsman_1.Zcl.Clusters.ssIasZone.ID,
|
|
281
|
+
attributes: {},
|
|
282
|
+
commands: {
|
|
283
|
+
initCustomTestMode: {
|
|
284
|
+
ID: 0x02,
|
|
285
|
+
parameters: [
|
|
286
|
+
{ name: "timeoutInSeconds", type: zigbee_herdsman_1.Zcl.DataType.UINT8 },
|
|
287
|
+
{ name: "reportTestInZoneStatus", type: zigbee_herdsman_1.Zcl.DataType.ENUM8 },
|
|
288
|
+
],
|
|
289
|
+
},
|
|
290
|
+
},
|
|
291
|
+
commandsResponse: {},
|
|
292
|
+
}),
|
|
293
|
+
testMode: (args) => {
|
|
294
|
+
const { testModeDescription, supportTimeout = false } = args;
|
|
295
|
+
const testModeLookup = {
|
|
296
|
+
ON: true,
|
|
297
|
+
OFF: false,
|
|
298
|
+
};
|
|
299
|
+
const defaultTimeout = 3;
|
|
300
|
+
const enableTestMode = async (endpoint, timeout) => {
|
|
301
|
+
await endpoint.command("ssIasZone", "initCustomTestMode", {
|
|
302
|
+
timeoutInSeconds: timeout,
|
|
303
|
+
reportTestInZoneStatus: 0x80,
|
|
304
|
+
});
|
|
305
|
+
};
|
|
306
|
+
const disableTestMode = async (endpoint) => {
|
|
307
|
+
await endpoint.command("ssIasZone", "initNormalOpMode", {});
|
|
308
|
+
};
|
|
309
|
+
const exposes = [
|
|
310
|
+
e
|
|
311
|
+
.binary("test_mode", ea.ALL, utils.getFromLookupByValue(true, testModeLookup), utils.getFromLookupByValue(false, testModeLookup))
|
|
312
|
+
.withDescription(testModeDescription)
|
|
313
|
+
.withCategory("config"),
|
|
314
|
+
];
|
|
315
|
+
if (supportTimeout) {
|
|
316
|
+
exposes.push(e
|
|
317
|
+
.numeric("test_mode_timeout", ea.ALL)
|
|
318
|
+
.withDescription(`Determines how long the test mode should be activated. The default length is ${defaultTimeout} seconds.`)
|
|
319
|
+
.withValueMin(1)
|
|
320
|
+
.withValueMax(255)
|
|
321
|
+
.withUnit("seconds")
|
|
322
|
+
.withCategory("config"));
|
|
323
|
+
}
|
|
324
|
+
const fromZigbee = [
|
|
325
|
+
{
|
|
326
|
+
cluster: "ssIasZone",
|
|
327
|
+
type: ["commandStatusChangeNotification", "attributeReport", "readResponse"],
|
|
328
|
+
convert: (model, msg, publish, options, meta) => {
|
|
329
|
+
const zoneStatus = "zonestatus" in msg.data ? msg.data.zonestatus : msg.data.zoneStatus;
|
|
330
|
+
if (zoneStatus === undefined) {
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
const result = {};
|
|
334
|
+
const testModeEnabled = (zoneStatus & (1 << 8)) > 0;
|
|
335
|
+
result.test_mode = utils.getFromLookupByValue(testModeEnabled, testModeLookup);
|
|
336
|
+
return result;
|
|
337
|
+
},
|
|
338
|
+
},
|
|
339
|
+
];
|
|
340
|
+
const toZigbee = [
|
|
341
|
+
{
|
|
342
|
+
key: ["test_mode", "test_mode_timeout"],
|
|
343
|
+
convertSet: async (entity, key, value, meta) => {
|
|
344
|
+
if (key === "test_mode") {
|
|
345
|
+
if (value === utils.getFromLookupByValue(true, testModeLookup)) {
|
|
346
|
+
let timeout;
|
|
347
|
+
if (supportTimeout) {
|
|
348
|
+
const currentTimeout = meta.state.test_mode_timeout;
|
|
349
|
+
if (currentTimeout == null) {
|
|
350
|
+
timeout = defaultTimeout;
|
|
351
|
+
meta.publish({ test_mode_timeout: timeout });
|
|
352
|
+
}
|
|
353
|
+
else {
|
|
354
|
+
timeout = utils.toNumber(currentTimeout);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
else {
|
|
358
|
+
timeout = 0;
|
|
359
|
+
}
|
|
360
|
+
await enableTestMode(entity, timeout);
|
|
361
|
+
}
|
|
362
|
+
else {
|
|
363
|
+
await disableTestMode(entity);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
if (key === "test_mode_timeout") {
|
|
367
|
+
return { state: { test_mode_timeout: value } };
|
|
368
|
+
}
|
|
369
|
+
},
|
|
370
|
+
convertGet: async (entity, key, meta) => {
|
|
371
|
+
if (key === "test_mode") {
|
|
372
|
+
await entity.read("ssIasZone", ["zoneStatus"]);
|
|
373
|
+
}
|
|
374
|
+
if (key === "test_mode_timeout" && meta.state.test_mode_timeout == null) {
|
|
375
|
+
meta.publish({ test_mode_timeout: supportTimeout ? defaultTimeout : 0 });
|
|
376
|
+
}
|
|
377
|
+
},
|
|
378
|
+
},
|
|
379
|
+
];
|
|
380
|
+
const configure = [m.setupConfigureForBinding("ssIasZone", "input"), m.setupConfigureForReading("ssIasZone", ["zoneStatus"])];
|
|
381
|
+
return {
|
|
382
|
+
exposes,
|
|
383
|
+
fromZigbee,
|
|
384
|
+
toZigbee,
|
|
385
|
+
configure,
|
|
386
|
+
isModernExtend: true,
|
|
387
|
+
};
|
|
388
|
+
},
|
|
389
|
+
};
|
|
255
390
|
exports.boschBmctExtend = {
|
|
256
391
|
switchMode: (args) => {
|
|
257
392
|
const { endpoint, deviceModeLookup, switchModeLookup, switchTypeLookup } = args;
|
|
@@ -1183,20 +1318,6 @@ exports.boschBsirExtend = {
|
|
|
1183
1318
|
},
|
|
1184
1319
|
access: "STATE_GET",
|
|
1185
1320
|
}),
|
|
1186
|
-
battery: () => m.battery({
|
|
1187
|
-
percentage: true,
|
|
1188
|
-
percentageReportingConfig: {
|
|
1189
|
-
min: "MIN",
|
|
1190
|
-
max: "MAX",
|
|
1191
|
-
change: 1,
|
|
1192
|
-
},
|
|
1193
|
-
lowStatus: true,
|
|
1194
|
-
lowStatusReportingConfig: {
|
|
1195
|
-
min: "MIN",
|
|
1196
|
-
max: "MAX",
|
|
1197
|
-
change: 0,
|
|
1198
|
-
},
|
|
1199
|
-
}),
|
|
1200
1321
|
lightDelay: () => m.numeric({
|
|
1201
1322
|
name: "light_delay",
|
|
1202
1323
|
cluster: "ssIasWd",
|
|
@@ -1415,11 +1536,6 @@ exports.boschDoorWindowContactExtend = {
|
|
|
1415
1536
|
commands: {},
|
|
1416
1537
|
commandsResponse: {},
|
|
1417
1538
|
}),
|
|
1418
|
-
battery: () => m.battery({
|
|
1419
|
-
percentage: true,
|
|
1420
|
-
lowStatus: true,
|
|
1421
|
-
lowStatusReportingConfig: { min: "MIN", max: "MAX", change: 0 },
|
|
1422
|
-
}),
|
|
1423
1539
|
reportContactState: () => m.iasZoneAlarm({
|
|
1424
1540
|
zoneType: "contact",
|
|
1425
1541
|
zoneAttributes: ["alarm_1"],
|
|
@@ -1693,18 +1809,9 @@ exports.boschDoorWindowContactExtend = {
|
|
|
1693
1809
|
};
|
|
1694
1810
|
},
|
|
1695
1811
|
};
|
|
1812
|
+
//endregion
|
|
1813
|
+
//region Bosch BSEN-M device (Motion detector)
|
|
1696
1814
|
exports.boschBsenExtend = {
|
|
1697
|
-
customIasZoneCluster: () => m.deviceAddCustomCluster("ssIasZone", {
|
|
1698
|
-
ID: zigbee_herdsman_1.Zcl.Clusters.ssIasZone.ID,
|
|
1699
|
-
attributes: {},
|
|
1700
|
-
commands: {
|
|
1701
|
-
initCustomTestMode: {
|
|
1702
|
-
ID: 0x02,
|
|
1703
|
-
parameters: [{ name: "data", type: zigbee_herdsman_1.Zcl.BuffaloZclDataType.LIST_UINT8 }],
|
|
1704
|
-
},
|
|
1705
|
-
},
|
|
1706
|
-
commandsResponse: {},
|
|
1707
|
-
}),
|
|
1708
1815
|
battery: () => m.battery({
|
|
1709
1816
|
percentage: false,
|
|
1710
1817
|
percentageReporting: false,
|
|
@@ -1712,7 +1819,12 @@ exports.boschBsenExtend = {
|
|
|
1712
1819
|
voltageReporting: true,
|
|
1713
1820
|
voltageToPercentage: { min: 2500, max: 3000 },
|
|
1714
1821
|
lowStatus: true,
|
|
1715
|
-
lowStatusReportingConfig: { min: "MIN", max: "MAX", change:
|
|
1822
|
+
lowStatusReportingConfig: { min: "MIN", max: "MAX", change: null },
|
|
1823
|
+
}),
|
|
1824
|
+
testMode: () => exports.boschGeneralSensorDeviceExtend.testMode({
|
|
1825
|
+
testModeDescription: "Activates the test mode. In this mode, the device blinks on every detected motion " +
|
|
1826
|
+
"without any wait time in between to verify the installation. Please keep in mind " +
|
|
1827
|
+
"that it can take up to 45 seconds for the test mode to be activated.",
|
|
1716
1828
|
}),
|
|
1717
1829
|
illuminance: () => m.illuminance({ reporting: { min: "1_SECOND", max: 600, change: 3522 } }),
|
|
1718
1830
|
// The temperature sensor isn't used at all by Bosch on the BSEN-M.
|
|
@@ -1867,26 +1979,101 @@ exports.boschBsenExtend = {
|
|
|
1867
1979
|
isModernExtend: true,
|
|
1868
1980
|
};
|
|
1869
1981
|
},
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1982
|
+
};
|
|
1983
|
+
exports.boschWaterAlarmExtend = {
|
|
1984
|
+
waterAlarmCluster: () => m.deviceAddCustomCluster("boschWaterAlarm", {
|
|
1985
|
+
ID: 0xfcac,
|
|
1986
|
+
manufacturerCode: exports.manufacturerOptions.manufacturerCode,
|
|
1987
|
+
attributes: {
|
|
1988
|
+
alarmOnMotion: { ID: 0x0003, type: zigbee_herdsman_1.Zcl.DataType.BOOLEAN },
|
|
1989
|
+
},
|
|
1990
|
+
commands: {
|
|
1991
|
+
muteAlarmControl: { ID: 0x00, parameters: [{ name: "data", type: zigbee_herdsman_1.Zcl.DataType.UINT8 }] },
|
|
1992
|
+
muteAlarmControlResponse: { ID: 0x01, parameters: [{ name: "data", type: zigbee_herdsman_1.Zcl.DataType.ENUM8 }] },
|
|
1993
|
+
},
|
|
1994
|
+
commandsResponse: {},
|
|
1995
|
+
}),
|
|
1996
|
+
changedSensitivityLevel: () => {
|
|
1997
|
+
const configure = [
|
|
1998
|
+
m.setupConfigureForBinding("ssIasZone", "input"),
|
|
1999
|
+
m.setupConfigureForReading("ssIasZone", ["numZoneSensitivityLevelsSupported", "currentZoneSensitivityLevel"]),
|
|
2000
|
+
async (device, coordinatorEndpoint, definition) => {
|
|
2001
|
+
const endpoint = device.getEndpoint(1);
|
|
2002
|
+
// The write request is made when using the proprietary
|
|
2003
|
+
// Bosch Smart Home Controller II as of 16-10-2025. Looks like
|
|
2004
|
+
// the default value was too high, and they didn't want to
|
|
2005
|
+
// push a firmware update. We mimic it here to avoid complaints.
|
|
2006
|
+
await endpoint.write("ssIasZone", { currentZoneSensitivityLevel: 5 });
|
|
2007
|
+
},
|
|
2008
|
+
];
|
|
2009
|
+
return {
|
|
2010
|
+
configure,
|
|
2011
|
+
isModernExtend: true,
|
|
1874
2012
|
};
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
2013
|
+
},
|
|
2014
|
+
waterAndTamperAlarm: () => m.iasZoneAlarm({
|
|
2015
|
+
zoneType: "water_leak",
|
|
2016
|
+
zoneAttributes: ["alarm_1", "tamper"],
|
|
2017
|
+
}),
|
|
2018
|
+
muteAlarmControl: () => {
|
|
2019
|
+
const muteAlarmControlLookup = {
|
|
2020
|
+
UNMUTED: false,
|
|
2021
|
+
MUTED: true,
|
|
1879
2022
|
};
|
|
1880
|
-
const
|
|
1881
|
-
|
|
2023
|
+
const muteAlarmControlResponseLookup = {
|
|
2024
|
+
muted: 0x00,
|
|
2025
|
+
error: 0x01,
|
|
2026
|
+
no_change: 0x02,
|
|
2027
|
+
unmuted: 0x03,
|
|
1882
2028
|
};
|
|
1883
2029
|
const exposes = [
|
|
1884
2030
|
e
|
|
1885
|
-
.binary("
|
|
1886
|
-
.
|
|
1887
|
-
.
|
|
2031
|
+
.binary("water_leak_alarm_control", ea.ALL, utils.getFromLookupByValue(true, muteAlarmControlLookup), utils.getFromLookupByValue(false, muteAlarmControlLookup))
|
|
2032
|
+
.withLabel("Mute water leak alarm")
|
|
2033
|
+
.withDescription("In case of an water leak, you can mute and unmute the audible alarm here"),
|
|
2034
|
+
];
|
|
2035
|
+
const toZigbee = [
|
|
2036
|
+
{
|
|
2037
|
+
key: ["water_leak_alarm_control"],
|
|
2038
|
+
convertSet: async (entity, key, value, meta) => {
|
|
2039
|
+
if (value === utils.getFromLookupByValue(false, muteAlarmControlLookup)) {
|
|
2040
|
+
await entity.command("boschWaterAlarm", "muteAlarmControl", { data: 0x00 }, exports.manufacturerOptions);
|
|
2041
|
+
}
|
|
2042
|
+
else {
|
|
2043
|
+
await entity.command("boschWaterAlarm", "muteAlarmControl", { data: 0x01 }, exports.manufacturerOptions);
|
|
2044
|
+
}
|
|
2045
|
+
},
|
|
2046
|
+
convertGet: async (entity, key, meta) => {
|
|
2047
|
+
await entity.read("ssIasZone", ["zoneStatus"]);
|
|
2048
|
+
},
|
|
2049
|
+
},
|
|
1888
2050
|
];
|
|
1889
2051
|
const fromZigbee = [
|
|
2052
|
+
{
|
|
2053
|
+
cluster: "boschWaterAlarm",
|
|
2054
|
+
type: ["raw"],
|
|
2055
|
+
convert: (model, msg, publish, options, meta) => {
|
|
2056
|
+
const command = msg.data[4];
|
|
2057
|
+
if (command !== 0x01) {
|
|
2058
|
+
return;
|
|
2059
|
+
}
|
|
2060
|
+
const muteAlarmControlResponse = msg.data[5];
|
|
2061
|
+
switch (muteAlarmControlResponse) {
|
|
2062
|
+
case muteAlarmControlResponseLookup.muted:
|
|
2063
|
+
logger_1.logger.debug(`Alarm on device '${meta.device.ieeeAddr}' was muted`, NS);
|
|
2064
|
+
break;
|
|
2065
|
+
case muteAlarmControlResponseLookup.error:
|
|
2066
|
+
logger_1.logger.error(`Alarm on device '${meta.device.ieeeAddr}' could not be muted right now (e.g., no active alarm)!`, NS);
|
|
2067
|
+
break;
|
|
2068
|
+
case muteAlarmControlResponseLookup.no_change:
|
|
2069
|
+
logger_1.logger.debug(`Alarm on device '${meta.device.ieeeAddr}' is already in requested state`, NS);
|
|
2070
|
+
break;
|
|
2071
|
+
case muteAlarmControlResponseLookup.unmuted:
|
|
2072
|
+
logger_1.logger.debug(`Alarm on device '${meta.device.ieeeAddr}' was unmuted`, NS);
|
|
2073
|
+
break;
|
|
2074
|
+
}
|
|
2075
|
+
},
|
|
2076
|
+
},
|
|
1890
2077
|
{
|
|
1891
2078
|
cluster: "ssIasZone",
|
|
1892
2079
|
type: ["commandStatusChangeNotification", "attributeReport", "readResponse"],
|
|
@@ -1896,39 +2083,36 @@ exports.boschBsenExtend = {
|
|
|
1896
2083
|
return;
|
|
1897
2084
|
}
|
|
1898
2085
|
const result = {};
|
|
1899
|
-
const
|
|
1900
|
-
result.
|
|
2086
|
+
const alarmMuted = (zoneStatus & (1 << 1)) > 0;
|
|
2087
|
+
result.water_leak_alarm_control = utils.getFromLookupByValue(alarmMuted, muteAlarmControlLookup);
|
|
1901
2088
|
return result;
|
|
1902
2089
|
},
|
|
1903
2090
|
},
|
|
1904
2091
|
];
|
|
1905
|
-
const toZigbee = [
|
|
1906
|
-
{
|
|
1907
|
-
key: ["test_mode"],
|
|
1908
|
-
convertSet: async (entity, key, value, meta) => {
|
|
1909
|
-
if (key === "test_mode") {
|
|
1910
|
-
if (value === utils.getFromLookupByValue(true, testModeLookup)) {
|
|
1911
|
-
await enableTestMode(entity);
|
|
1912
|
-
}
|
|
1913
|
-
else {
|
|
1914
|
-
await disableTestMode(entity);
|
|
1915
|
-
}
|
|
1916
|
-
}
|
|
1917
|
-
},
|
|
1918
|
-
convertGet: async (entity, key, meta) => {
|
|
1919
|
-
await entity.read("ssIasZone", ["zoneStatus"]);
|
|
1920
|
-
},
|
|
1921
|
-
},
|
|
1922
|
-
];
|
|
1923
2092
|
const configure = [m.setupConfigureForBinding("ssIasZone", "input"), m.setupConfigureForReading("ssIasZone", ["zoneStatus"])];
|
|
1924
2093
|
return {
|
|
1925
2094
|
exposes,
|
|
1926
|
-
fromZigbee,
|
|
1927
2095
|
toZigbee,
|
|
2096
|
+
fromZigbee,
|
|
1928
2097
|
configure,
|
|
1929
2098
|
isModernExtend: true,
|
|
1930
2099
|
};
|
|
1931
2100
|
},
|
|
2101
|
+
alarmOnMotion: () => m.binary({
|
|
2102
|
+
name: "alarm_on_motion",
|
|
2103
|
+
cluster: "boschWaterAlarm",
|
|
2104
|
+
attribute: "alarmOnMotion",
|
|
2105
|
+
description: "If your water alarm is moved, an acoustic signal sounds",
|
|
2106
|
+
valueOn: ["ON", 0x01],
|
|
2107
|
+
valueOff: ["OFF", 0x00],
|
|
2108
|
+
entityCategory: "config",
|
|
2109
|
+
}),
|
|
2110
|
+
testMode: () => exports.boschGeneralSensorDeviceExtend.testMode({
|
|
2111
|
+
testModeDescription: "Activates the test mode. In this mode, the device acts like it would when " +
|
|
2112
|
+
"detecting any water to verify the installation. Please keep in mind " +
|
|
2113
|
+
"that it can take up to 10 seconds for the test mode to be activated.",
|
|
2114
|
+
supportTimeout: true,
|
|
2115
|
+
}),
|
|
1932
2116
|
};
|
|
1933
2117
|
exports.boschSmartPlugExtend = {
|
|
1934
2118
|
smartPlugCluster: () => m.deviceAddCustomCluster("boschEnergyDevice", {
|
|
@@ -2068,5 +2252,735 @@ exports.boschSmartPlugExtend = {
|
|
|
2068
2252
|
...args,
|
|
2069
2253
|
}),
|
|
2070
2254
|
};
|
|
2255
|
+
const boschThermostatLookup = {
|
|
2256
|
+
systemModes: {
|
|
2257
|
+
heat: 0x04,
|
|
2258
|
+
cool: 0x03,
|
|
2259
|
+
},
|
|
2260
|
+
raRunningStates: ["idle", "heat"],
|
|
2261
|
+
heaterType: {
|
|
2262
|
+
underfloor_heating: 0x00,
|
|
2263
|
+
radiator: 0x02,
|
|
2264
|
+
central_heating: 0x01,
|
|
2265
|
+
manual_control: 0x03,
|
|
2266
|
+
},
|
|
2267
|
+
};
|
|
2268
|
+
exports.boschThermostatExtend = {
|
|
2269
|
+
customThermostatCluster: () => m.deviceAddCustomCluster("hvacThermostat", {
|
|
2270
|
+
ID: zigbee_herdsman_1.Zcl.Clusters.hvacThermostat.ID,
|
|
2271
|
+
attributes: {
|
|
2272
|
+
operatingMode: { ID: 0x4007, type: zigbee_herdsman_1.Zcl.DataType.ENUM8, manufacturerCode: exports.manufacturerOptions.manufacturerCode },
|
|
2273
|
+
heatingDemand: { ID: 0x4020, type: zigbee_herdsman_1.Zcl.DataType.ENUM8, manufacturerCode: exports.manufacturerOptions.manufacturerCode },
|
|
2274
|
+
valveAdaptStatus: { ID: 0x4022, type: zigbee_herdsman_1.Zcl.DataType.ENUM8, manufacturerCode: exports.manufacturerOptions.manufacturerCode },
|
|
2275
|
+
unknownAttribute0: { ID: 0x4025, type: zigbee_herdsman_1.Zcl.DataType.ENUM8, manufacturerCode: exports.manufacturerOptions.manufacturerCode },
|
|
2276
|
+
remoteTemperature: { ID: 0x4040, type: zigbee_herdsman_1.Zcl.DataType.INT16, manufacturerCode: exports.manufacturerOptions.manufacturerCode },
|
|
2277
|
+
unknownAttribute1: { ID: 0x4041, type: zigbee_herdsman_1.Zcl.DataType.ENUM8, manufacturerCode: exports.manufacturerOptions.manufacturerCode },
|
|
2278
|
+
windowOpenMode: { ID: 0x4042, type: zigbee_herdsman_1.Zcl.DataType.ENUM8, manufacturerCode: exports.manufacturerOptions.manufacturerCode },
|
|
2279
|
+
boostHeating: { ID: 0x4043, type: zigbee_herdsman_1.Zcl.DataType.ENUM8, manufacturerCode: exports.manufacturerOptions.manufacturerCode },
|
|
2280
|
+
cableSensorTemperature: { ID: 0x4052, type: zigbee_herdsman_1.Zcl.DataType.INT16, manufacturerCode: exports.manufacturerOptions.manufacturerCode },
|
|
2281
|
+
valveType: { ID: 0x4060, type: zigbee_herdsman_1.Zcl.DataType.ENUM8, manufacturerCode: exports.manufacturerOptions.manufacturerCode },
|
|
2282
|
+
unknownAttribute2: { ID: 0x4061, type: zigbee_herdsman_1.Zcl.DataType.ENUM8, manufacturerCode: exports.manufacturerOptions.manufacturerCode },
|
|
2283
|
+
cableSensorMode: { ID: 0x4062, type: zigbee_herdsman_1.Zcl.DataType.ENUM8, manufacturerCode: exports.manufacturerOptions.manufacturerCode },
|
|
2284
|
+
heaterType: { ID: 0x4063, type: zigbee_herdsman_1.Zcl.DataType.ENUM8, manufacturerCode: exports.manufacturerOptions.manufacturerCode },
|
|
2285
|
+
errorState: { ID: 0x5000, type: zigbee_herdsman_1.Zcl.DataType.BITMAP8, manufacturerCode: exports.manufacturerOptions.manufacturerCode },
|
|
2286
|
+
automaticValveAdapt: { ID: 0x5010, type: zigbee_herdsman_1.Zcl.DataType.ENUM8, manufacturerCode: exports.manufacturerOptions.manufacturerCode },
|
|
2287
|
+
},
|
|
2288
|
+
commands: {
|
|
2289
|
+
calibrateValve: { ID: 0x41, parameters: [] },
|
|
2290
|
+
},
|
|
2291
|
+
commandsResponse: {},
|
|
2292
|
+
}),
|
|
2293
|
+
customUserInterfaceCfgCluster: () => m.deviceAddCustomCluster("hvacUserInterfaceCfg", {
|
|
2294
|
+
ID: zigbee_herdsman_1.Zcl.Clusters.hvacUserInterfaceCfg.ID,
|
|
2295
|
+
attributes: {
|
|
2296
|
+
displayOrientation: { ID: 0x400b, type: zigbee_herdsman_1.Zcl.DataType.UINT8, manufacturerCode: exports.manufacturerOptions.manufacturerCode },
|
|
2297
|
+
activityLed: { ID: 0x4033, type: zigbee_herdsman_1.Zcl.DataType.ENUM8, manufacturerCode: exports.manufacturerOptions.manufacturerCode },
|
|
2298
|
+
displayedTemperature: { ID: 0x4039, type: zigbee_herdsman_1.Zcl.DataType.ENUM8, manufacturerCode: exports.manufacturerOptions.manufacturerCode },
|
|
2299
|
+
displaySwitchOnDuration: { ID: 0x403a, type: zigbee_herdsman_1.Zcl.DataType.ENUM8, manufacturerCode: exports.manufacturerOptions.manufacturerCode },
|
|
2300
|
+
displayBrightness: { ID: 0x403b, type: zigbee_herdsman_1.Zcl.DataType.ENUM8, manufacturerCode: exports.manufacturerOptions.manufacturerCode },
|
|
2301
|
+
},
|
|
2302
|
+
commands: {},
|
|
2303
|
+
commandsResponse: {},
|
|
2304
|
+
}),
|
|
2305
|
+
relayState: () => m.onOff({ description: "The state of the relay controlling the connected heating/cooling device", powerOnBehavior: false }),
|
|
2306
|
+
cableSensorMode: () => m.enumLookup({
|
|
2307
|
+
name: "cable_sensor_mode",
|
|
2308
|
+
cluster: "hvacThermostat",
|
|
2309
|
+
attribute: "cableSensorMode",
|
|
2310
|
+
description: 'Select a configuration for the sensor connection. If you select "with_regulation", ' +
|
|
2311
|
+
"the measured temperature on the cable sensor is used by the heating/cooling algorithm " +
|
|
2312
|
+
"instead of the local temperature.",
|
|
2313
|
+
lookup: { not_used: 0x00, cable_sensor_without_regulation: 0xb0, cable_sensor_with_regulation: 0xb1 },
|
|
2314
|
+
reporting: false,
|
|
2315
|
+
entityCategory: "config",
|
|
2316
|
+
}),
|
|
2317
|
+
cableSensorTemperature: () => m.numeric({
|
|
2318
|
+
name: "cable_sensor_temperature",
|
|
2319
|
+
cluster: "hvacThermostat",
|
|
2320
|
+
attribute: "cableSensorTemperature",
|
|
2321
|
+
description: "Measured temperature value on the cable sensor (if enabled)",
|
|
2322
|
+
unit: "°C",
|
|
2323
|
+
scale: 100,
|
|
2324
|
+
reporting: { min: 30, max: "MAX", change: 20 },
|
|
2325
|
+
access: "STATE_GET",
|
|
2326
|
+
}),
|
|
2327
|
+
heaterType: () => m.enumLookup({
|
|
2328
|
+
name: "heater_type",
|
|
2329
|
+
cluster: "hvacThermostat",
|
|
2330
|
+
attribute: "heaterType",
|
|
2331
|
+
description: "Select the connected heater type or 'manual_control' if you like to activate the relay manually when necessary",
|
|
2332
|
+
lookup: boschThermostatLookup.heaterType,
|
|
2333
|
+
reporting: false,
|
|
2334
|
+
entityCategory: "config",
|
|
2335
|
+
}),
|
|
2336
|
+
valveType: () => m.enumLookup({
|
|
2337
|
+
name: "valve_type",
|
|
2338
|
+
cluster: "hvacThermostat",
|
|
2339
|
+
attribute: "valveType",
|
|
2340
|
+
description: "Select the connected valve type",
|
|
2341
|
+
lookup: { normally_closed: 0x00, normally_open: 0x01 },
|
|
2342
|
+
reporting: false,
|
|
2343
|
+
entityCategory: "config",
|
|
2344
|
+
}),
|
|
2345
|
+
humidity: () => m.humidity({ reporting: false }),
|
|
2346
|
+
windowOpenMode: (args) => m.binary({
|
|
2347
|
+
name: "window_open_mode",
|
|
2348
|
+
cluster: "hvacThermostat",
|
|
2349
|
+
attribute: "windowOpenMode",
|
|
2350
|
+
description: "Activates the window open mode, where the thermostat disables any heating/cooling " +
|
|
2351
|
+
"to prevent unnecessary energy consumption. Please keep in mind that the device " +
|
|
2352
|
+
"itself does not detect any open windows!",
|
|
2353
|
+
valueOn: ["ON", 0x01],
|
|
2354
|
+
valueOff: ["OFF", 0x00],
|
|
2355
|
+
reporting: args?.enableReporting ? { min: "MIN", max: "MAX", change: null } : false,
|
|
2356
|
+
}),
|
|
2357
|
+
childLock: () => m.binary({
|
|
2358
|
+
name: "child_lock",
|
|
2359
|
+
cluster: "hvacUserInterfaceCfg",
|
|
2360
|
+
attribute: "keypadLockout",
|
|
2361
|
+
description: "Enables/disables physical input on the thermostat",
|
|
2362
|
+
valueOn: ["LOCK", 0x01],
|
|
2363
|
+
valueOff: ["UNLOCK", 0x00],
|
|
2364
|
+
reporting: { min: "MIN", max: "MAX", change: null },
|
|
2365
|
+
}),
|
|
2366
|
+
displayBrightness: () => m.numeric({
|
|
2367
|
+
name: "display_brightness",
|
|
2368
|
+
cluster: "hvacUserInterfaceCfg",
|
|
2369
|
+
attribute: "displayBrightness",
|
|
2370
|
+
description: "Sets brightness of the display",
|
|
2371
|
+
valueMin: 0,
|
|
2372
|
+
valueMax: 100,
|
|
2373
|
+
valueStep: 10,
|
|
2374
|
+
unit: "%",
|
|
2375
|
+
scale: 0.1,
|
|
2376
|
+
reporting: false,
|
|
2377
|
+
entityCategory: "config",
|
|
2378
|
+
}),
|
|
2379
|
+
displaySwitchOnDuration: () => m.numeric({
|
|
2380
|
+
name: "display_switch_on_duration",
|
|
2381
|
+
cluster: "hvacUserInterfaceCfg",
|
|
2382
|
+
attribute: "displaySwitchOnDuration",
|
|
2383
|
+
label: "Display switch-on duration",
|
|
2384
|
+
description: "Sets the time before the display is automatically switched off",
|
|
2385
|
+
valueMin: 5,
|
|
2386
|
+
valueMax: 30,
|
|
2387
|
+
unit: "s",
|
|
2388
|
+
reporting: false,
|
|
2389
|
+
entityCategory: "config",
|
|
2390
|
+
}),
|
|
2391
|
+
displayOrientation: () => m.enumLookup({
|
|
2392
|
+
name: "display_orientation",
|
|
2393
|
+
cluster: "hvacUserInterfaceCfg",
|
|
2394
|
+
attribute: "displayOrientation",
|
|
2395
|
+
description: "You can rotate the display content by 180° here. This is recommended if your thermostat is fitted vertically, for instance.",
|
|
2396
|
+
lookup: { standard_arrangement: 0x00, rotated_by_180_degrees: 0x01 },
|
|
2397
|
+
reporting: false,
|
|
2398
|
+
entityCategory: "config",
|
|
2399
|
+
}),
|
|
2400
|
+
displayedTemperature: () => m.enumLookup({
|
|
2401
|
+
name: "displayed_temperature",
|
|
2402
|
+
cluster: "hvacUserInterfaceCfg",
|
|
2403
|
+
attribute: "displayedTemperature",
|
|
2404
|
+
description: "Select which temperature should be displayed on your radiator thermostat display",
|
|
2405
|
+
lookup: { set_temperature: 0x00, measured_temperature: 0x01 },
|
|
2406
|
+
reporting: false,
|
|
2407
|
+
entityCategory: "config",
|
|
2408
|
+
}),
|
|
2409
|
+
activityLedState: () => m.enumLookup({
|
|
2410
|
+
name: "activity_led",
|
|
2411
|
+
cluster: "hvacUserInterfaceCfg",
|
|
2412
|
+
attribute: "activityLed",
|
|
2413
|
+
label: "Activity LED state",
|
|
2414
|
+
description: "Determines the state of the little dot on the display next to the heating/cooling symbol",
|
|
2415
|
+
lookup: { off: 0x00, auto: 0x01, on: 0x02 },
|
|
2416
|
+
reporting: false,
|
|
2417
|
+
entityCategory: "config",
|
|
2418
|
+
}),
|
|
2419
|
+
remoteTemperature: () => m.numeric({
|
|
2420
|
+
name: "remote_temperature",
|
|
2421
|
+
cluster: "hvacThermostat",
|
|
2422
|
+
attribute: "remoteTemperature",
|
|
2423
|
+
description: "Input for remote temperature sensor. Required at least every 30 minutes to prevent fallback to the internal sensor!",
|
|
2424
|
+
valueMin: 0.0,
|
|
2425
|
+
valueMax: 35.0,
|
|
2426
|
+
valueStep: 0.2,
|
|
2427
|
+
unit: "°C",
|
|
2428
|
+
scale: 100,
|
|
2429
|
+
reporting: false,
|
|
2430
|
+
entityCategory: "config",
|
|
2431
|
+
}),
|
|
2432
|
+
setpointChangeSource: (args) => m.enumLookup({
|
|
2433
|
+
name: "setpoint_change_source",
|
|
2434
|
+
cluster: "hvacThermostat",
|
|
2435
|
+
attribute: "setpointChangeSource",
|
|
2436
|
+
description: "Source of the current setpoint temperature",
|
|
2437
|
+
lookup: { manual: 0x00, schedule: 0x01, externally: 0x02 },
|
|
2438
|
+
access: "STATE_GET",
|
|
2439
|
+
reporting: args?.enableReporting ? { min: "MIN", max: "MAX", change: null } : false,
|
|
2440
|
+
entityCategory: "diagnostic",
|
|
2441
|
+
}),
|
|
2442
|
+
customHeatingDemand: () => m.numeric({
|
|
2443
|
+
name: "pi_heating_demand",
|
|
2444
|
+
cluster: "hvacThermostat",
|
|
2445
|
+
attribute: "heatingDemand",
|
|
2446
|
+
label: "PI heating demand",
|
|
2447
|
+
description: "Position of the valve (= demanded heat) where 0% is fully closed and 100% is fully open",
|
|
2448
|
+
unit: "%",
|
|
2449
|
+
valueMin: 0,
|
|
2450
|
+
valueMax: 100,
|
|
2451
|
+
access: "ALL",
|
|
2452
|
+
reporting: { min: "MIN", max: "MAX", change: null },
|
|
2453
|
+
}),
|
|
2454
|
+
rmBattery: () => m.battery({
|
|
2455
|
+
percentage: true,
|
|
2456
|
+
percentageReporting: false,
|
|
2457
|
+
voltage: true,
|
|
2458
|
+
voltageReporting: true,
|
|
2459
|
+
voltageReportingConfig: false,
|
|
2460
|
+
voltageToPercentage: { min: 4400, max: 6400 },
|
|
2461
|
+
lowStatus: true,
|
|
2462
|
+
lowStatusReportingConfig: { min: "MIN", max: "MAX", change: null },
|
|
2463
|
+
}),
|
|
2464
|
+
rmThermostat: () => {
|
|
2465
|
+
const thermostat = m.thermostat({
|
|
2466
|
+
localTemperature: {
|
|
2467
|
+
configure: { reporting: false },
|
|
2468
|
+
},
|
|
2469
|
+
localTemperatureCalibration: {
|
|
2470
|
+
values: { min: -5, max: 5, step: 0.1 },
|
|
2471
|
+
configure: { reporting: false },
|
|
2472
|
+
},
|
|
2473
|
+
setpoints: {
|
|
2474
|
+
values: {
|
|
2475
|
+
occupiedHeatingSetpoint: { min: 5, max: 30, step: 0.5 },
|
|
2476
|
+
occupiedCoolingSetpoint: { min: 5, max: 30, step: 0.5 },
|
|
2477
|
+
},
|
|
2478
|
+
configure: { reporting: false },
|
|
2479
|
+
},
|
|
2480
|
+
systemMode: {
|
|
2481
|
+
values: ["heat", "cool"],
|
|
2482
|
+
toZigbee: { skip: true },
|
|
2483
|
+
configure: { skip: true },
|
|
2484
|
+
},
|
|
2485
|
+
runningState: {
|
|
2486
|
+
values: ["idle", "heat", "cool"],
|
|
2487
|
+
configure: { reporting: false },
|
|
2488
|
+
},
|
|
2489
|
+
});
|
|
2490
|
+
const expose = (device, options) => {
|
|
2491
|
+
const returnedThermostat = thermostat.exposes;
|
|
2492
|
+
if (utils.isDummyDevice(device)) {
|
|
2493
|
+
return returnedThermostat;
|
|
2494
|
+
}
|
|
2495
|
+
let currentSystemMode;
|
|
2496
|
+
try {
|
|
2497
|
+
currentSystemMode = utils.getFromLookupByValue(device.getEndpoint(1).getClusterAttributeValue("hvacThermostat", "systemMode"), boschThermostatLookup.systemModes);
|
|
2498
|
+
}
|
|
2499
|
+
catch {
|
|
2500
|
+
currentSystemMode = "heat";
|
|
2501
|
+
}
|
|
2502
|
+
// The thermostat is a singleton, thus the values must be set
|
|
2503
|
+
// manually as filtering will lead to an array without
|
|
2504
|
+
// heat/cool in them after two systemMode changes.
|
|
2505
|
+
returnedThermostat[0].features.forEach((exposedAttribute, index, array) => {
|
|
2506
|
+
if (exposedAttribute.type === "enum") {
|
|
2507
|
+
if (exposedAttribute.name === "system_mode") {
|
|
2508
|
+
exposedAttribute.label = "Active system mode";
|
|
2509
|
+
exposedAttribute.description =
|
|
2510
|
+
"Currently used system mode by the thermostat. This field is primarily " +
|
|
2511
|
+
"used to configure the thermostat in Home Assistant correctly.";
|
|
2512
|
+
exposedAttribute.values = [currentSystemMode];
|
|
2513
|
+
exposedAttribute.access = ea.STATE;
|
|
2514
|
+
}
|
|
2515
|
+
if (exposedAttribute.name === "running_state") {
|
|
2516
|
+
exposedAttribute.values = ["idle", currentSystemMode];
|
|
2517
|
+
}
|
|
2518
|
+
}
|
|
2519
|
+
});
|
|
2520
|
+
return returnedThermostat;
|
|
2521
|
+
};
|
|
2522
|
+
return {
|
|
2523
|
+
exposes: [expose],
|
|
2524
|
+
fromZigbee: thermostat.fromZigbee,
|
|
2525
|
+
toZigbee: thermostat.toZigbee,
|
|
2526
|
+
configure: thermostat.configure,
|
|
2527
|
+
isModernExtend: true,
|
|
2528
|
+
};
|
|
2529
|
+
},
|
|
2530
|
+
customSystemMode: () => {
|
|
2531
|
+
const exposes = [
|
|
2532
|
+
e
|
|
2533
|
+
.enum("custom_system_mode", ea.ALL, Object.keys(boschThermostatLookup.systemModes))
|
|
2534
|
+
.withLabel("Available system modes")
|
|
2535
|
+
.withDescription("Select if the thermostat is connected to a heating or a cooling device")
|
|
2536
|
+
.withCategory("config"),
|
|
2537
|
+
];
|
|
2538
|
+
const fromZigbee = [
|
|
2539
|
+
{
|
|
2540
|
+
cluster: "hvacThermostat",
|
|
2541
|
+
type: ["attributeReport", "readResponse"],
|
|
2542
|
+
convert: (model, msg, publish, options, meta) => {
|
|
2543
|
+
const result = {};
|
|
2544
|
+
const data = msg.data;
|
|
2545
|
+
if (data.systemMode !== undefined) {
|
|
2546
|
+
result.custom_system_mode = utils.getFromLookupByValue(data.systemMode, boschThermostatLookup.systemModes);
|
|
2547
|
+
meta.deviceExposesChanged();
|
|
2548
|
+
}
|
|
2549
|
+
return result;
|
|
2550
|
+
},
|
|
2551
|
+
},
|
|
2552
|
+
];
|
|
2553
|
+
const toZigbee = [
|
|
2554
|
+
{
|
|
2555
|
+
key: ["custom_system_mode"],
|
|
2556
|
+
convertSet: async (entity, key, value, meta) => {
|
|
2557
|
+
await entity.write("hvacThermostat", {
|
|
2558
|
+
systemMode: utils.toNumber(utils.getFromLookup(value, boschThermostatLookup.systemModes)),
|
|
2559
|
+
});
|
|
2560
|
+
return { state: { custom_system_mode: value } };
|
|
2561
|
+
},
|
|
2562
|
+
convertGet: async (entity, key, meta) => {
|
|
2563
|
+
await entity.read("hvacThermostat", ["systemMode"]);
|
|
2564
|
+
},
|
|
2565
|
+
},
|
|
2566
|
+
];
|
|
2567
|
+
const configure = [
|
|
2568
|
+
m.setupConfigureForReporting("hvacThermostat", "systemMode", {
|
|
2569
|
+
config: false,
|
|
2570
|
+
access: ea.ALL,
|
|
2571
|
+
}),
|
|
2572
|
+
];
|
|
2573
|
+
return {
|
|
2574
|
+
exposes,
|
|
2575
|
+
fromZigbee,
|
|
2576
|
+
toZigbee,
|
|
2577
|
+
configure,
|
|
2578
|
+
isModernExtend: true,
|
|
2579
|
+
};
|
|
2580
|
+
},
|
|
2581
|
+
raThermostat: () => {
|
|
2582
|
+
// Native thermostat
|
|
2583
|
+
const thermostat = m.thermostat({
|
|
2584
|
+
localTemperature: {
|
|
2585
|
+
values: {
|
|
2586
|
+
description: "Temperature used by the heating algorithm. This is the " +
|
|
2587
|
+
"temperature measured on the device (by default) or the " +
|
|
2588
|
+
"remote temperature (if set within the last 30 min).",
|
|
2589
|
+
},
|
|
2590
|
+
configure: {
|
|
2591
|
+
reporting: { min: 30, max: 900, change: 20 },
|
|
2592
|
+
},
|
|
2593
|
+
},
|
|
2594
|
+
localTemperatureCalibration: {
|
|
2595
|
+
values: { min: -5, max: 5, step: 0.1 },
|
|
2596
|
+
configure: { reporting: false },
|
|
2597
|
+
},
|
|
2598
|
+
setpoints: {
|
|
2599
|
+
values: {
|
|
2600
|
+
occupiedHeatingSetpoint: { min: 5, max: 30, step: 0.5 },
|
|
2601
|
+
},
|
|
2602
|
+
configure: {
|
|
2603
|
+
reporting: { min: "MIN", max: "MAX", change: 1 },
|
|
2604
|
+
},
|
|
2605
|
+
},
|
|
2606
|
+
systemMode: {
|
|
2607
|
+
values: ["heat"],
|
|
2608
|
+
configure: {
|
|
2609
|
+
reporting: false,
|
|
2610
|
+
},
|
|
2611
|
+
},
|
|
2612
|
+
runningState: {
|
|
2613
|
+
values: boschThermostatLookup.raRunningStates,
|
|
2614
|
+
toZigbee: {
|
|
2615
|
+
skip: true,
|
|
2616
|
+
},
|
|
2617
|
+
configure: {
|
|
2618
|
+
reporting: false,
|
|
2619
|
+
},
|
|
2620
|
+
},
|
|
2621
|
+
piHeatingDemand: {
|
|
2622
|
+
values: ea.ALL,
|
|
2623
|
+
toZigbee: {
|
|
2624
|
+
skip: true,
|
|
2625
|
+
},
|
|
2626
|
+
configure: {
|
|
2627
|
+
skip: true,
|
|
2628
|
+
},
|
|
2629
|
+
},
|
|
2630
|
+
});
|
|
2631
|
+
const exposes = thermostat.exposes;
|
|
2632
|
+
const fromZigbee = thermostat.fromZigbee;
|
|
2633
|
+
const toZigbee = thermostat.toZigbee;
|
|
2634
|
+
let configure = thermostat.configure;
|
|
2635
|
+
// Add converters for custom running state
|
|
2636
|
+
const runningState = exports.boschThermostatExtend.customRunningState();
|
|
2637
|
+
fromZigbee.push(...runningState.fromZigbee);
|
|
2638
|
+
toZigbee.push(...runningState.toZigbee);
|
|
2639
|
+
// Add converters and configure for custom heating demand
|
|
2640
|
+
const piHeatingDemand = exports.boschThermostatExtend.customHeatingDemand();
|
|
2641
|
+
fromZigbee.push(...piHeatingDemand.fromZigbee);
|
|
2642
|
+
toZigbee.push(...piHeatingDemand.toZigbee);
|
|
2643
|
+
configure = [...configure, ...piHeatingDemand.configure];
|
|
2644
|
+
return {
|
|
2645
|
+
exposes,
|
|
2646
|
+
fromZigbee,
|
|
2647
|
+
toZigbee,
|
|
2648
|
+
configure,
|
|
2649
|
+
isModernExtend: true,
|
|
2650
|
+
};
|
|
2651
|
+
},
|
|
2652
|
+
customRunningState: () => {
|
|
2653
|
+
const fromZigbee = [
|
|
2654
|
+
{
|
|
2655
|
+
cluster: "hvacThermostat",
|
|
2656
|
+
type: ["attributeReport", "readResponse"],
|
|
2657
|
+
convert: (model, msg, publish, options, meta) => {
|
|
2658
|
+
const result = {};
|
|
2659
|
+
const data = msg.data;
|
|
2660
|
+
if (data.heatingDemand !== undefined) {
|
|
2661
|
+
result.running_state =
|
|
2662
|
+
utils.toNumber(data.heatingDemand) > 0
|
|
2663
|
+
? boschThermostatLookup.raRunningStates[1]
|
|
2664
|
+
: boschThermostatLookup.raRunningStates[0];
|
|
2665
|
+
}
|
|
2666
|
+
return result;
|
|
2667
|
+
},
|
|
2668
|
+
},
|
|
2669
|
+
];
|
|
2670
|
+
const toZigbee = [
|
|
2671
|
+
{
|
|
2672
|
+
key: ["running_state"],
|
|
2673
|
+
convertGet: async (entity, key, meta) => {
|
|
2674
|
+
await entity.read("hvacThermostat", ["heatingDemand"]);
|
|
2675
|
+
},
|
|
2676
|
+
},
|
|
2677
|
+
];
|
|
2678
|
+
return {
|
|
2679
|
+
fromZigbee,
|
|
2680
|
+
toZigbee,
|
|
2681
|
+
isModernExtend: true,
|
|
2682
|
+
};
|
|
2683
|
+
},
|
|
2684
|
+
operatingMode: (args) => {
|
|
2685
|
+
const operatingModeLookup = { schedule: 0x00, manual: 0x01, pause: 0x05 };
|
|
2686
|
+
const operatingMode = m.enumLookup({
|
|
2687
|
+
name: "operating_mode",
|
|
2688
|
+
cluster: "hvacThermostat",
|
|
2689
|
+
attribute: "operatingMode",
|
|
2690
|
+
description: "Bosch-specific operating mode. This is being used as mode on the exposed thermostat when using Home Assistant.",
|
|
2691
|
+
lookup: operatingModeLookup,
|
|
2692
|
+
reporting: args?.enableReporting ? { min: "MIN", max: "MAX", change: null } : false,
|
|
2693
|
+
entityCategory: "config",
|
|
2694
|
+
});
|
|
2695
|
+
const exposes = operatingMode.exposes;
|
|
2696
|
+
const fromZigbee = operatingMode.fromZigbee;
|
|
2697
|
+
const toZigbee = operatingMode.toZigbee;
|
|
2698
|
+
const configure = operatingMode.configure;
|
|
2699
|
+
const removeLowAndHighTemperatureFields = (payload) => {
|
|
2700
|
+
payload.temperature_high_command_topic = undefined;
|
|
2701
|
+
payload.temperature_low_command_topic = undefined;
|
|
2702
|
+
payload.temperature_high_state_template = undefined;
|
|
2703
|
+
payload.temperature_low_state_template = undefined;
|
|
2704
|
+
payload.temperature_high_state_topic = undefined;
|
|
2705
|
+
payload.temperature_low_state_topic = undefined;
|
|
2706
|
+
};
|
|
2707
|
+
// Override the payload send to Home Assistant to achieve the following:
|
|
2708
|
+
// 1. Use the Bosch operating mode instead of system modes
|
|
2709
|
+
// See: https://github.com/Koenkk/zigbee2mqtt/pull/23075#issue-2355829475
|
|
2710
|
+
// 2. Remove setpoints not compatible with the currently used system mode
|
|
2711
|
+
// See: https://github.com/Koenkk/zigbee2mqtt/issues/28892
|
|
2712
|
+
const meta = {
|
|
2713
|
+
overrideHaDiscoveryPayload: (payload) => {
|
|
2714
|
+
if (payload.modes !== undefined) {
|
|
2715
|
+
if (payload.modes.includes("heat")) {
|
|
2716
|
+
payload.mode_command_template =
|
|
2717
|
+
`{% set values = { 'auto':'schedule', 'heat':'manual', 'off':'pause' } %}` +
|
|
2718
|
+
`{"operating_mode": "{{ values[value] if value in values.keys() else 'pause' }}"}`;
|
|
2719
|
+
payload.mode_state_template =
|
|
2720
|
+
`{% set values = { 'schedule':'auto', 'manual':'heat', 'pause':'off' } %}` +
|
|
2721
|
+
"{% set value = value_json.operating_mode %}" +
|
|
2722
|
+
`{{ values[value] if value in values.keys() else 'off' }}`;
|
|
2723
|
+
if (payload.temperature_low_command_topic !== undefined) {
|
|
2724
|
+
payload.temperature_command_topic = payload.temperature_low_command_topic;
|
|
2725
|
+
payload.temperature_state_template = payload.temperature_low_state_template;
|
|
2726
|
+
payload.temperature_state_topic = payload.temperature_low_state_topic;
|
|
2727
|
+
removeLowAndHighTemperatureFields(payload);
|
|
2728
|
+
}
|
|
2729
|
+
}
|
|
2730
|
+
else if (payload.modes.includes("cool")) {
|
|
2731
|
+
payload.mode_command_template =
|
|
2732
|
+
`{% set values = { 'auto':'schedule', 'cool':'manual', 'off':'pause' } %}` +
|
|
2733
|
+
`{"operating_mode": "{{ values[value] if value in values.keys() else 'pause' }}"}`;
|
|
2734
|
+
payload.mode_state_template =
|
|
2735
|
+
`{% set values = { 'schedule':'auto', 'manual':'cool', 'pause':'off' } %}` +
|
|
2736
|
+
"{% set value = value_json.operating_mode %}" +
|
|
2737
|
+
`{{ values[value] if value in values.keys() else 'off' }}`;
|
|
2738
|
+
if (payload.temperature_high_command_topic !== undefined) {
|
|
2739
|
+
payload.temperature_command_topic = payload.temperature_high_command_topic;
|
|
2740
|
+
payload.temperature_state_template = payload.temperature_high_state_template;
|
|
2741
|
+
payload.temperature_state_topic = payload.temperature_high_state_topic;
|
|
2742
|
+
removeLowAndHighTemperatureFields(payload);
|
|
2743
|
+
}
|
|
2744
|
+
}
|
|
2745
|
+
payload.modes = ["off", ...payload.modes, "auto"];
|
|
2746
|
+
payload.mode_command_topic = payload.mode_command_topic.replace("/system_mode", "");
|
|
2747
|
+
}
|
|
2748
|
+
},
|
|
2749
|
+
};
|
|
2750
|
+
return {
|
|
2751
|
+
exposes,
|
|
2752
|
+
fromZigbee,
|
|
2753
|
+
toZigbee,
|
|
2754
|
+
configure,
|
|
2755
|
+
meta,
|
|
2756
|
+
isModernExtend: true,
|
|
2757
|
+
};
|
|
2758
|
+
},
|
|
2759
|
+
boostHeating: (args) => {
|
|
2760
|
+
const boostHeatingLookup = {
|
|
2761
|
+
OFF: 0x00,
|
|
2762
|
+
ON: 0x01,
|
|
2763
|
+
};
|
|
2764
|
+
const exposes = [
|
|
2765
|
+
e
|
|
2766
|
+
.binary("boost_heating", ea.ALL, utils.getFromLookupByValue(0x01, boostHeatingLookup), utils.getFromLookupByValue(0x00, boostHeatingLookup))
|
|
2767
|
+
.withLabel("Activate boost heating")
|
|
2768
|
+
.withDescription("Activate boost heating (opens TRV for 5 minutes)"),
|
|
2769
|
+
];
|
|
2770
|
+
const fromZigbee = [
|
|
2771
|
+
{
|
|
2772
|
+
cluster: "hvacThermostat",
|
|
2773
|
+
type: ["attributeReport", "readResponse"],
|
|
2774
|
+
convert: (model, msg, publish, options, meta) => {
|
|
2775
|
+
const result = {};
|
|
2776
|
+
const data = msg.data;
|
|
2777
|
+
if (data.boostHeating !== undefined) {
|
|
2778
|
+
result.boost_heating = utils.getFromLookupByValue(data.boostHeating, boostHeatingLookup);
|
|
2779
|
+
}
|
|
2780
|
+
return result;
|
|
2781
|
+
},
|
|
2782
|
+
},
|
|
2783
|
+
];
|
|
2784
|
+
const toZigbee = [
|
|
2785
|
+
{
|
|
2786
|
+
key: ["boost_heating"],
|
|
2787
|
+
convertSet: async (entity, key, value, meta) => {
|
|
2788
|
+
const enableBoostHeating = value === utils.getFromLookupByValue(boostHeatingLookup.ON, boostHeatingLookup);
|
|
2789
|
+
if (enableBoostHeating) {
|
|
2790
|
+
const systemModeNotSetToHeat = "system_mode" in meta.state && meta.state.system_mode !== "heat";
|
|
2791
|
+
if (systemModeNotSetToHeat) {
|
|
2792
|
+
throw new Error("Boost heating is only possible when system mode is set to 'heat'!");
|
|
2793
|
+
}
|
|
2794
|
+
const heaterTypeNotSetToRadiator = "heater_type" in meta.state &&
|
|
2795
|
+
meta.state.heater_type !==
|
|
2796
|
+
utils.getFromLookupByValue(boschThermostatLookup.heaterType.radiator, boschThermostatLookup.heaterType);
|
|
2797
|
+
if (heaterTypeNotSetToRadiator) {
|
|
2798
|
+
throw new Error("Boost heating is only possible when heater type is set to 'radiator'!");
|
|
2799
|
+
}
|
|
2800
|
+
}
|
|
2801
|
+
await entity.write("hvacThermostat", {
|
|
2802
|
+
boostHeating: utils.toNumber(utils.getFromLookup(value, boostHeatingLookup)),
|
|
2803
|
+
});
|
|
2804
|
+
return { state: { boost_heating: value } };
|
|
2805
|
+
},
|
|
2806
|
+
convertGet: async (entity, key, meta) => {
|
|
2807
|
+
await entity.read("hvacThermostat", ["boostHeating"]);
|
|
2808
|
+
},
|
|
2809
|
+
},
|
|
2810
|
+
];
|
|
2811
|
+
const configure = [
|
|
2812
|
+
m.setupConfigureForReporting("hvacThermostat", "boostHeating", {
|
|
2813
|
+
config: args?.enableReporting ? { min: "MIN", max: "MAX", change: null } : false,
|
|
2814
|
+
access: ea.ALL,
|
|
2815
|
+
}),
|
|
2816
|
+
];
|
|
2817
|
+
return {
|
|
2818
|
+
exposes,
|
|
2819
|
+
fromZigbee,
|
|
2820
|
+
toZigbee,
|
|
2821
|
+
configure,
|
|
2822
|
+
isModernExtend: true,
|
|
2823
|
+
};
|
|
2824
|
+
},
|
|
2825
|
+
errorState: (args) => {
|
|
2826
|
+
const exposes = [
|
|
2827
|
+
e
|
|
2828
|
+
.text("error_state", ea.STATE_GET)
|
|
2829
|
+
.withDescription("Indicates whether the device encounters any errors or not")
|
|
2830
|
+
.withCategory("diagnostic"),
|
|
2831
|
+
];
|
|
2832
|
+
const fromZigbee = [
|
|
2833
|
+
{
|
|
2834
|
+
cluster: "hvacThermostat",
|
|
2835
|
+
type: ["attributeReport", "readResponse"],
|
|
2836
|
+
convert: (model, msg, publish, options, meta) => {
|
|
2837
|
+
const result = {};
|
|
2838
|
+
const data = msg.data;
|
|
2839
|
+
if (data.errorState !== undefined) {
|
|
2840
|
+
const receivedErrorState = data.errorState;
|
|
2841
|
+
if (receivedErrorState === 0) {
|
|
2842
|
+
result.error_state = "ok";
|
|
2843
|
+
}
|
|
2844
|
+
else {
|
|
2845
|
+
result.error_state = "";
|
|
2846
|
+
const bitmapLength = (receivedErrorState >>> 0).toString(2).length;
|
|
2847
|
+
for (let errorNumber = 0; errorNumber < bitmapLength; errorNumber++) {
|
|
2848
|
+
if ((receivedErrorState >> errorNumber) & 1) {
|
|
2849
|
+
if (String(result.error_state).length > 0) {
|
|
2850
|
+
result.error_state += " - ";
|
|
2851
|
+
}
|
|
2852
|
+
result.error_state += `E${String(errorNumber + 1).padStart(2, "0")}`;
|
|
2853
|
+
}
|
|
2854
|
+
}
|
|
2855
|
+
}
|
|
2856
|
+
}
|
|
2857
|
+
return result;
|
|
2858
|
+
},
|
|
2859
|
+
},
|
|
2860
|
+
];
|
|
2861
|
+
const toZigbee = [
|
|
2862
|
+
{
|
|
2863
|
+
key: ["error_state"],
|
|
2864
|
+
convertGet: async (entity, key, meta) => {
|
|
2865
|
+
await entity.read("hvacThermostat", ["errorState"]);
|
|
2866
|
+
},
|
|
2867
|
+
},
|
|
2868
|
+
];
|
|
2869
|
+
const configure = [
|
|
2870
|
+
m.setupConfigureForReporting("hvacThermostat", "errorState", {
|
|
2871
|
+
config: args?.enableReporting ? { min: "MIN", max: "MAX", change: null } : false,
|
|
2872
|
+
access: ea.STATE_GET,
|
|
2873
|
+
}),
|
|
2874
|
+
];
|
|
2875
|
+
return {
|
|
2876
|
+
exposes,
|
|
2877
|
+
fromZigbee,
|
|
2878
|
+
toZigbee,
|
|
2879
|
+
configure,
|
|
2880
|
+
isModernExtend: true,
|
|
2881
|
+
};
|
|
2882
|
+
},
|
|
2883
|
+
valveAdaptation: () => {
|
|
2884
|
+
const valveAdaptStatusLookup = {
|
|
2885
|
+
none: 0x00,
|
|
2886
|
+
ready_to_calibrate: 0x01,
|
|
2887
|
+
calibration_in_progress: 0x02,
|
|
2888
|
+
error: 0x03,
|
|
2889
|
+
success: 0x04,
|
|
2890
|
+
};
|
|
2891
|
+
const triggerValveAdaptation = async (state, endpoint, throwError = true) => {
|
|
2892
|
+
let adaptStatus;
|
|
2893
|
+
try {
|
|
2894
|
+
adaptStatus = utils.getFromLookup(state.valve_adapt_status, valveAdaptStatusLookup);
|
|
2895
|
+
}
|
|
2896
|
+
catch {
|
|
2897
|
+
adaptStatus = valveAdaptStatusLookup.none;
|
|
2898
|
+
}
|
|
2899
|
+
switch (adaptStatus) {
|
|
2900
|
+
case valveAdaptStatusLookup.ready_to_calibrate:
|
|
2901
|
+
case valveAdaptStatusLookup.error:
|
|
2902
|
+
await endpoint.command("hvacThermostat", "calibrateValve", {}, exports.manufacturerOptions);
|
|
2903
|
+
break;
|
|
2904
|
+
default:
|
|
2905
|
+
if (throwError) {
|
|
2906
|
+
throw new Error("Valve adaptation process not possible right now!");
|
|
2907
|
+
}
|
|
2908
|
+
}
|
|
2909
|
+
};
|
|
2910
|
+
const exposes = [
|
|
2911
|
+
e
|
|
2912
|
+
.enum("valve_adapt_status", ea.STATE_GET, Object.keys(valveAdaptStatusLookup))
|
|
2913
|
+
.withLabel("Valve adaptation status")
|
|
2914
|
+
.withDescription("Specifies the current status of the valve adaptation")
|
|
2915
|
+
.withCategory("diagnostic"),
|
|
2916
|
+
e
|
|
2917
|
+
.binary("automatic_valve_adapt", ea.STATE_GET, true, false)
|
|
2918
|
+
.withLabel("Automatic valve adaptation requested")
|
|
2919
|
+
.withDescription("Specifies if an automatic valve adaptation is being requested by the thermostat " +
|
|
2920
|
+
"(for example after a successful firmware upgrade). If this is the case, the " +
|
|
2921
|
+
"valve adaptation will be automatically started as soon as the adaptation status " +
|
|
2922
|
+
"is 'ready_to_calibrate' or 'error'.")
|
|
2923
|
+
.withCategory("diagnostic"),
|
|
2924
|
+
e
|
|
2925
|
+
.enum("valve_adapt_process", ea.SET, ["adapt"])
|
|
2926
|
+
.withLabel("Trigger adaptation process")
|
|
2927
|
+
.withDescription("Trigger the valve adaptation process. Only possible when the adaptation status is 'ready_to_calibrate' or 'error'.")
|
|
2928
|
+
.withCategory("config"),
|
|
2929
|
+
];
|
|
2930
|
+
const fromZigbee = [
|
|
2931
|
+
{
|
|
2932
|
+
cluster: "hvacThermostat",
|
|
2933
|
+
type: ["attributeReport", "readResponse"],
|
|
2934
|
+
convert: async (model, msg, publish, options, meta) => {
|
|
2935
|
+
const result = {};
|
|
2936
|
+
const data = msg.data;
|
|
2937
|
+
if (data.valveAdaptStatus !== undefined) {
|
|
2938
|
+
result.valve_adapt_status = utils.getFromLookupByValue(data.valveAdaptStatus, valveAdaptStatusLookup);
|
|
2939
|
+
const automaticValveAdapt = meta.state.automatic_valve_adapt ?? false;
|
|
2940
|
+
if (automaticValveAdapt === true) {
|
|
2941
|
+
await triggerValveAdaptation(meta.state, msg.endpoint, false);
|
|
2942
|
+
}
|
|
2943
|
+
}
|
|
2944
|
+
if (data.automaticValveAdapt !== undefined) {
|
|
2945
|
+
result.automatic_valve_adapt = !!data.automaticValveAdapt;
|
|
2946
|
+
}
|
|
2947
|
+
return result;
|
|
2948
|
+
},
|
|
2949
|
+
},
|
|
2950
|
+
];
|
|
2951
|
+
const toZigbee = [
|
|
2952
|
+
{
|
|
2953
|
+
key: ["valve_adapt_status", "automatic_valve_adapt", "valve_adapt_process"],
|
|
2954
|
+
convertSet: async (entity, key, value, meta) => {
|
|
2955
|
+
if (key === "valve_adapt_process") {
|
|
2956
|
+
await triggerValveAdaptation(meta.state, entity);
|
|
2957
|
+
}
|
|
2958
|
+
},
|
|
2959
|
+
convertGet: async (entity, key, meta) => {
|
|
2960
|
+
if (key === "valve_adapt_status") {
|
|
2961
|
+
await entity.read("hvacThermostat", ["valveAdaptStatus"]);
|
|
2962
|
+
}
|
|
2963
|
+
if (key === "automatic_valve_adapt") {
|
|
2964
|
+
await entity.read("hvacThermostat", ["automaticValveAdapt"]);
|
|
2965
|
+
}
|
|
2966
|
+
},
|
|
2967
|
+
},
|
|
2968
|
+
];
|
|
2969
|
+
const configure = [
|
|
2970
|
+
m.setupConfigureForReporting("hvacThermostat", "valveAdaptStatus", {
|
|
2971
|
+
config: { min: "MIN", max: "MAX", change: null },
|
|
2972
|
+
access: ea.STATE_GET,
|
|
2973
|
+
}),
|
|
2974
|
+
m.setupConfigureForReading("hvacThermostat", ["automaticValveAdapt"]),
|
|
2975
|
+
];
|
|
2976
|
+
return {
|
|
2977
|
+
exposes,
|
|
2978
|
+
fromZigbee,
|
|
2979
|
+
toZigbee,
|
|
2980
|
+
configure,
|
|
2981
|
+
isModernExtend: true,
|
|
2982
|
+
};
|
|
2983
|
+
},
|
|
2984
|
+
};
|
|
2071
2985
|
//endregion
|
|
2072
2986
|
//# sourceMappingURL=bosch.js.map
|