zigbee-herdsman-converters 25.34.0 → 25.35.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 +14 -0
- package/dist/devices/awox.d.ts.map +1 -1
- package/dist/devices/awox.js +9 -0
- package/dist/devices/awox.js.map +1 -1
- package/dist/devices/bosch.d.ts.map +1 -1
- package/dist/devices/bosch.js +63 -513
- package/dist/devices/bosch.js.map +1 -1
- package/dist/devices/tuya.d.ts.map +1 -1
- package/dist/devices/tuya.js +2 -1
- package/dist/devices/tuya.js.map +1 -1
- package/dist/lib/bosch.d.ts +98 -3
- package/dist/lib/bosch.d.ts.map +1 -1
- package/dist/lib/bosch.js +543 -23
- 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 +0 -6
- package/dist/lib/exposes.js.map +1 -1
- package/dist/lib/modernExtend.d.ts +4 -0
- package/dist/lib/modernExtend.d.ts.map +1 -1
- package/dist/lib/modernExtend.js +5 -2
- package/dist/lib/modernExtend.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.boschBsenExtend = exports.boschDoorWindowContactExtend = exports.boschBsirExtend = exports.boschBmctExtend = 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"));
|
|
@@ -49,10 +49,20 @@ const ea = exposes.access;
|
|
|
49
49
|
const NS = "zhc:bosch";
|
|
50
50
|
exports.manufacturerOptions = { manufacturerCode: zigbee_herdsman_1.Zcl.ManufacturerCode.ROBERT_BOSCH_GMBH };
|
|
51
51
|
exports.boschGeneralExtend = {
|
|
52
|
+
/** Some Bosch devices ask the coordinator for their ZCL version
|
|
53
|
+
* during deviceAnnouncement. Without answer, these devices regularly
|
|
54
|
+
* re-join the network. To avoid that, we have to make sure that a readRequest
|
|
55
|
+
* for the zclVersion is always being answered. The answered zclVersion is
|
|
56
|
+
* taken from the Bosch Smart Home Controller II.
|
|
57
|
+
*
|
|
58
|
+
* Exception: BTH-RM and BTH-RM230Z ask the coordinator at regular
|
|
59
|
+
* intervals for their zclVersion (maybe availability check like Z2M does?)
|
|
60
|
+
* and *not* during interview! To avoid code-duplication, we handle that
|
|
61
|
+
* case here as well. */
|
|
52
62
|
handleZclVersionReadRequest: () => {
|
|
53
63
|
const onEvent = [
|
|
54
64
|
(event) => {
|
|
55
|
-
if (event.type !== "
|
|
65
|
+
if (event.type !== "start") {
|
|
56
66
|
return;
|
|
57
67
|
}
|
|
58
68
|
event.data.device.customReadResponse = (frame, endpoint) => {
|
|
@@ -75,7 +85,7 @@ exports.boschGeneralExtend = {
|
|
|
75
85
|
isModernExtend: true,
|
|
76
86
|
};
|
|
77
87
|
},
|
|
78
|
-
|
|
88
|
+
customMeteringCluster: () => m.deviceAddCustomCluster("seMetering", {
|
|
79
89
|
ID: zigbee_herdsman_1.Zcl.Clusters.seMetering.ID,
|
|
80
90
|
attributes: {},
|
|
81
91
|
commands: {
|
|
@@ -107,6 +117,12 @@ exports.boschGeneralExtend = {
|
|
|
107
117
|
isModernExtend: true,
|
|
108
118
|
};
|
|
109
119
|
},
|
|
120
|
+
batteryWithPercentageAndLowStatus: (args) => m.battery({
|
|
121
|
+
percentage: true,
|
|
122
|
+
lowStatus: true,
|
|
123
|
+
lowStatusReportingConfig: { min: "MIN", max: "MAX", change: null },
|
|
124
|
+
...args,
|
|
125
|
+
}),
|
|
110
126
|
autoOff: (args) => {
|
|
111
127
|
const { endpoint } = args ?? {};
|
|
112
128
|
const offOnLookup = {
|
|
@@ -1134,20 +1150,6 @@ exports.boschBsirExtend = {
|
|
|
1134
1150
|
},
|
|
1135
1151
|
access: "STATE_GET",
|
|
1136
1152
|
}),
|
|
1137
|
-
battery: () => m.battery({
|
|
1138
|
-
percentage: true,
|
|
1139
|
-
percentageReportingConfig: {
|
|
1140
|
-
min: "MIN",
|
|
1141
|
-
max: "MAX",
|
|
1142
|
-
change: 1,
|
|
1143
|
-
},
|
|
1144
|
-
lowStatus: true,
|
|
1145
|
-
lowStatusReportingConfig: {
|
|
1146
|
-
min: "MIN",
|
|
1147
|
-
max: "MAX",
|
|
1148
|
-
change: 0,
|
|
1149
|
-
},
|
|
1150
|
-
}),
|
|
1151
1153
|
lightDelay: () => m.numeric({
|
|
1152
1154
|
name: "light_delay",
|
|
1153
1155
|
cluster: "ssIasWd",
|
|
@@ -1366,11 +1368,6 @@ exports.boschDoorWindowContactExtend = {
|
|
|
1366
1368
|
commands: {},
|
|
1367
1369
|
commandsResponse: {},
|
|
1368
1370
|
}),
|
|
1369
|
-
battery: () => m.battery({
|
|
1370
|
-
percentage: true,
|
|
1371
|
-
lowStatus: true,
|
|
1372
|
-
lowStatusReportingConfig: { min: "MIN", max: "MAX", change: 0 },
|
|
1373
|
-
}),
|
|
1374
1371
|
reportContactState: () => m.iasZoneAlarm({
|
|
1375
1372
|
zoneType: "contact",
|
|
1376
1373
|
zoneAttributes: ["alarm_1"],
|
|
@@ -1660,7 +1657,7 @@ exports.boschBsenExtend = {
|
|
|
1660
1657
|
voltageReporting: true,
|
|
1661
1658
|
voltageToPercentage: { min: 2500, max: 3000 },
|
|
1662
1659
|
lowStatus: true,
|
|
1663
|
-
lowStatusReportingConfig: { min: "MIN", max: "MAX", change:
|
|
1660
|
+
lowStatusReportingConfig: { min: "MIN", max: "MAX", change: null },
|
|
1664
1661
|
}),
|
|
1665
1662
|
illuminance: () => m.illuminance({ reporting: { min: "1_SECOND", max: 600, change: 3522 } }),
|
|
1666
1663
|
// The temperature sensor isn't used at all by Bosch on the BSEN-M.
|
|
@@ -2016,5 +2013,528 @@ exports.boschSmartPlugExtend = {
|
|
|
2016
2013
|
...args,
|
|
2017
2014
|
}),
|
|
2018
2015
|
};
|
|
2016
|
+
const boschThermostatLookup = {
|
|
2017
|
+
heaterType: {
|
|
2018
|
+
underfloor_heating: 0x00,
|
|
2019
|
+
central_heating: 0x03,
|
|
2020
|
+
radiator: 0x02,
|
|
2021
|
+
},
|
|
2022
|
+
};
|
|
2023
|
+
exports.boschThermostatExtend = {
|
|
2024
|
+
customThermostatCluster: () => m.deviceAddCustomCluster("hvacThermostat", {
|
|
2025
|
+
ID: zigbee_herdsman_1.Zcl.Clusters.hvacThermostat.ID,
|
|
2026
|
+
attributes: {
|
|
2027
|
+
operatingMode: { ID: 0x4007, type: zigbee_herdsman_1.Zcl.DataType.ENUM8, manufacturerCode: exports.manufacturerOptions.manufacturerCode },
|
|
2028
|
+
heatingDemand: { ID: 0x4020, type: zigbee_herdsman_1.Zcl.DataType.ENUM8, manufacturerCode: exports.manufacturerOptions.manufacturerCode },
|
|
2029
|
+
valveAdaptStatus: { ID: 0x4022, type: zigbee_herdsman_1.Zcl.DataType.ENUM8, manufacturerCode: exports.manufacturerOptions.manufacturerCode },
|
|
2030
|
+
unknownAttribute0: { ID: 0x4025, type: zigbee_herdsman_1.Zcl.DataType.ENUM8, manufacturerCode: exports.manufacturerOptions.manufacturerCode },
|
|
2031
|
+
remoteTemperature: { ID: 0x4040, type: zigbee_herdsman_1.Zcl.DataType.INT16, manufacturerCode: exports.manufacturerOptions.manufacturerCode },
|
|
2032
|
+
unknownAttribute1: { ID: 0x4041, type: zigbee_herdsman_1.Zcl.DataType.ENUM8, manufacturerCode: exports.manufacturerOptions.manufacturerCode },
|
|
2033
|
+
windowOpenMode: { ID: 0x4042, type: zigbee_herdsman_1.Zcl.DataType.ENUM8, manufacturerCode: exports.manufacturerOptions.manufacturerCode },
|
|
2034
|
+
boostHeating: { ID: 0x4043, type: zigbee_herdsman_1.Zcl.DataType.ENUM8, manufacturerCode: exports.manufacturerOptions.manufacturerCode },
|
|
2035
|
+
cableSensorTemperature: { ID: 0x4052, type: zigbee_herdsman_1.Zcl.DataType.INT16, manufacturerCode: exports.manufacturerOptions.manufacturerCode },
|
|
2036
|
+
valveType: { ID: 0x4060, type: zigbee_herdsman_1.Zcl.DataType.ENUM8, manufacturerCode: exports.manufacturerOptions.manufacturerCode },
|
|
2037
|
+
unknownAttribute2: { ID: 0x4061, type: zigbee_herdsman_1.Zcl.DataType.ENUM8, manufacturerCode: exports.manufacturerOptions.manufacturerCode },
|
|
2038
|
+
cableSensorMode: { ID: 0x4062, type: zigbee_herdsman_1.Zcl.DataType.ENUM8, manufacturerCode: exports.manufacturerOptions.manufacturerCode },
|
|
2039
|
+
heaterType: { ID: 0x4063, type: zigbee_herdsman_1.Zcl.DataType.ENUM8, manufacturerCode: exports.manufacturerOptions.manufacturerCode },
|
|
2040
|
+
errorState: { ID: 0x5000, type: zigbee_herdsman_1.Zcl.DataType.BITMAP8, manufacturerCode: exports.manufacturerOptions.manufacturerCode },
|
|
2041
|
+
},
|
|
2042
|
+
commands: {
|
|
2043
|
+
calibrateValve: { ID: 0x41, parameters: [] },
|
|
2044
|
+
},
|
|
2045
|
+
commandsResponse: {},
|
|
2046
|
+
}),
|
|
2047
|
+
customUserInterfaceCfgCluster: () => m.deviceAddCustomCluster("hvacUserInterfaceCfg", {
|
|
2048
|
+
ID: zigbee_herdsman_1.Zcl.Clusters.hvacUserInterfaceCfg.ID,
|
|
2049
|
+
attributes: {
|
|
2050
|
+
displayOrientation: { ID: 0x400b, type: zigbee_herdsman_1.Zcl.DataType.UINT8, manufacturerCode: exports.manufacturerOptions.manufacturerCode },
|
|
2051
|
+
displayedTemperature: { ID: 0x4039, type: zigbee_herdsman_1.Zcl.DataType.ENUM8, manufacturerCode: exports.manufacturerOptions.manufacturerCode },
|
|
2052
|
+
displaySwitchOnDuration: { ID: 0x403a, type: zigbee_herdsman_1.Zcl.DataType.ENUM8, manufacturerCode: exports.manufacturerOptions.manufacturerCode },
|
|
2053
|
+
displayBrightness: { ID: 0x403b, type: zigbee_herdsman_1.Zcl.DataType.ENUM8, manufacturerCode: exports.manufacturerOptions.manufacturerCode },
|
|
2054
|
+
},
|
|
2055
|
+
commands: {},
|
|
2056
|
+
commandsResponse: {},
|
|
2057
|
+
}),
|
|
2058
|
+
rmThermostat: () => m.thermostat({
|
|
2059
|
+
setpoints: {
|
|
2060
|
+
occupiedHeatingSetpoint: { min: 5, max: 30, step: 0.5 },
|
|
2061
|
+
occupiedCoolingSetpoint: { min: 5, max: 30, step: 0.5 },
|
|
2062
|
+
},
|
|
2063
|
+
localTemperatureCalibration: { min: -5, max: 5, step: 0.1 },
|
|
2064
|
+
systemMode: ["off", "heat", "cool"],
|
|
2065
|
+
runningState: ["idle", "heat", "cool"],
|
|
2066
|
+
}),
|
|
2067
|
+
customHeatingDemand: () => m.numeric({
|
|
2068
|
+
name: "pi_heating_demand",
|
|
2069
|
+
cluster: "hvacThermostat",
|
|
2070
|
+
attribute: "heatingDemand",
|
|
2071
|
+
label: "PI heating demand",
|
|
2072
|
+
description: "Position of the valve (= demanded heat) where 0% is fully closed and 100% is fully open",
|
|
2073
|
+
unit: "%",
|
|
2074
|
+
valueMin: 0,
|
|
2075
|
+
valueMax: 100,
|
|
2076
|
+
reporting: { min: "MIN", max: "MAX", change: null },
|
|
2077
|
+
access: "ALL",
|
|
2078
|
+
}),
|
|
2079
|
+
cableSensorMode: () => m.enumLookup({
|
|
2080
|
+
name: "cable_sensor_mode",
|
|
2081
|
+
cluster: "hvacThermostat",
|
|
2082
|
+
attribute: "cableSensorMode",
|
|
2083
|
+
description: 'Select a configuration for the sensor connection. If you select "with_regulation", the measured temperature on the cable sensor is used by the heating/cooling algorithm instead of the local temperature.',
|
|
2084
|
+
lookup: { not_used: 0x00, cable_sensor_without_regulation: 0xb0, cable_sensor_with_regulation: 0xb1 },
|
|
2085
|
+
entityCategory: "config",
|
|
2086
|
+
}),
|
|
2087
|
+
cableSensorTemperature: () => m.numeric({
|
|
2088
|
+
name: "cable_sensor_temperature",
|
|
2089
|
+
cluster: "hvacThermostat",
|
|
2090
|
+
attribute: "cableSensorTemperature",
|
|
2091
|
+
description: "Measured temperature value on the cable sensor (if enabled)",
|
|
2092
|
+
unit: "°C",
|
|
2093
|
+
scale: 100,
|
|
2094
|
+
reporting: { min: 30, max: "MAX", change: 20 },
|
|
2095
|
+
access: "STATE_GET",
|
|
2096
|
+
}),
|
|
2097
|
+
heaterType: () => m.enumLookup({
|
|
2098
|
+
name: "heater_type",
|
|
2099
|
+
cluster: "hvacThermostat",
|
|
2100
|
+
attribute: "heaterType",
|
|
2101
|
+
description: "Select the connected heater type",
|
|
2102
|
+
lookup: boschThermostatLookup.heaterType,
|
|
2103
|
+
entityCategory: "config",
|
|
2104
|
+
}),
|
|
2105
|
+
valveType: () => m.enumLookup({
|
|
2106
|
+
name: "valve_type",
|
|
2107
|
+
cluster: "hvacThermostat",
|
|
2108
|
+
attribute: "valveType",
|
|
2109
|
+
description: "Select the connected valve type",
|
|
2110
|
+
lookup: { normally_closed: 0x00, normally_open: 0x01 },
|
|
2111
|
+
entityCategory: "config",
|
|
2112
|
+
}),
|
|
2113
|
+
humidity: () => m.humidity(),
|
|
2114
|
+
operatingMode: (args) => m.enumLookup({
|
|
2115
|
+
name: "operating_mode",
|
|
2116
|
+
cluster: "hvacThermostat",
|
|
2117
|
+
attribute: "operatingMode",
|
|
2118
|
+
description: "Bosch-specific operating mode",
|
|
2119
|
+
lookup: { schedule: 0x00, manual: 0x01, pause: 0x05 },
|
|
2120
|
+
reporting: { min: "MIN", max: "MAX", change: null },
|
|
2121
|
+
...args,
|
|
2122
|
+
}),
|
|
2123
|
+
windowOpenMode: () => m.binary({
|
|
2124
|
+
name: "window_open_mode",
|
|
2125
|
+
cluster: "hvacThermostat",
|
|
2126
|
+
attribute: "windowOpenMode",
|
|
2127
|
+
description: "Activates the window open mode, where the thermostat disables any heating/cooling to prevent unnecessary energy consumption. Please keep in mind that the device itself does not detect any open windows!",
|
|
2128
|
+
valueOn: ["ON", 0x01],
|
|
2129
|
+
valueOff: ["OFF", 0x00],
|
|
2130
|
+
reporting: { min: "MIN", max: "MAX", change: null, attribute: "windowOpenMode" },
|
|
2131
|
+
}),
|
|
2132
|
+
childLock: () => m.binary({
|
|
2133
|
+
name: "child_lock",
|
|
2134
|
+
cluster: "hvacUserInterfaceCfg",
|
|
2135
|
+
attribute: "keypadLockout",
|
|
2136
|
+
description: "Enables/disables physical input on the device",
|
|
2137
|
+
valueOn: ["LOCK", 0x01],
|
|
2138
|
+
valueOff: ["UNLOCK", 0x00],
|
|
2139
|
+
reporting: { min: "MIN", max: "MAX", change: null, attribute: "keypadLockout" },
|
|
2140
|
+
}),
|
|
2141
|
+
displayBrightness: () => m.numeric({
|
|
2142
|
+
name: "display_brightness",
|
|
2143
|
+
cluster: "hvacUserInterfaceCfg",
|
|
2144
|
+
attribute: "displayBrightness",
|
|
2145
|
+
description: "Sets brightness of the display",
|
|
2146
|
+
valueMin: 0,
|
|
2147
|
+
valueMax: 100,
|
|
2148
|
+
valueStep: 10,
|
|
2149
|
+
unit: "%",
|
|
2150
|
+
scale: 0.1,
|
|
2151
|
+
entityCategory: "config",
|
|
2152
|
+
}),
|
|
2153
|
+
displaySwitchOnDuration: () => m.numeric({
|
|
2154
|
+
name: "display_switch_on_duration",
|
|
2155
|
+
cluster: "hvacUserInterfaceCfg",
|
|
2156
|
+
attribute: "displaySwitchOnDuration",
|
|
2157
|
+
label: "Display switch-on duration",
|
|
2158
|
+
description: "Sets the time before the display is automatically switched off",
|
|
2159
|
+
valueMin: 5,
|
|
2160
|
+
valueMax: 30,
|
|
2161
|
+
unit: "s",
|
|
2162
|
+
entityCategory: "config",
|
|
2163
|
+
}),
|
|
2164
|
+
displayOrientation: () => m.enumLookup({
|
|
2165
|
+
name: "display_orientation",
|
|
2166
|
+
cluster: "hvacUserInterfaceCfg",
|
|
2167
|
+
attribute: "displayOrientation",
|
|
2168
|
+
description: "You can rotate the display content by 180° here. This is recommended if your thermostat is fitted vertically, for instance.",
|
|
2169
|
+
lookup: { standard_arrangement: 0x00, rotated_by_180_degrees: 0x01 },
|
|
2170
|
+
entityCategory: "config",
|
|
2171
|
+
}),
|
|
2172
|
+
displayedTemperature: () => m.enumLookup({
|
|
2173
|
+
name: "displayed_temperature",
|
|
2174
|
+
cluster: "hvacUserInterfaceCfg",
|
|
2175
|
+
attribute: "displayedTemperature",
|
|
2176
|
+
description: "Select which temperature should be displayed on your radiator thermostat display",
|
|
2177
|
+
lookup: { set_temperature: 0x00, measured_temperature: 0x01 },
|
|
2178
|
+
entityCategory: "config",
|
|
2179
|
+
}),
|
|
2180
|
+
remoteTemperature: () => m.numeric({
|
|
2181
|
+
name: "remote_temperature",
|
|
2182
|
+
cluster: "hvacThermostat",
|
|
2183
|
+
attribute: "remoteTemperature",
|
|
2184
|
+
description: "Input for remote temperature sensor. Required at least every 30 minutes to prevent fallback to the internal sensor!",
|
|
2185
|
+
valueMin: 0.0,
|
|
2186
|
+
valueMax: 35.0,
|
|
2187
|
+
valueStep: 0.2,
|
|
2188
|
+
unit: "°C",
|
|
2189
|
+
scale: 100,
|
|
2190
|
+
}),
|
|
2191
|
+
setpointChangeSource: () => m.enumLookup({
|
|
2192
|
+
name: "setpoint_change_source",
|
|
2193
|
+
cluster: "hvacThermostat",
|
|
2194
|
+
attribute: "setpointChangeSource",
|
|
2195
|
+
reporting: { min: "10_SECONDS", max: "MAX", change: null },
|
|
2196
|
+
description: "Source of the current setpoint temperature",
|
|
2197
|
+
lookup: { manual: 0x00, schedule: 0x01, externally: 0x02 },
|
|
2198
|
+
access: "STATE_GET",
|
|
2199
|
+
}),
|
|
2200
|
+
rmBattery: () => m.battery({
|
|
2201
|
+
percentage: true,
|
|
2202
|
+
percentageReporting: false,
|
|
2203
|
+
voltage: true,
|
|
2204
|
+
voltageReporting: true,
|
|
2205
|
+
voltageToPercentage: { min: 4400, max: 6400 },
|
|
2206
|
+
lowStatus: true,
|
|
2207
|
+
lowStatusReportingConfig: { min: "MIN", max: "MAX", change: null },
|
|
2208
|
+
}),
|
|
2209
|
+
raThermostat: () => {
|
|
2210
|
+
const thermostat = m.thermostat({
|
|
2211
|
+
localTemperature: {
|
|
2212
|
+
description: "Temperature used by the heating algorithm. This is the temperature measured on the device (by default) or the remote temperature (if set within the last 30 min).",
|
|
2213
|
+
},
|
|
2214
|
+
setpoints: {
|
|
2215
|
+
occupiedHeatingSetpoint: { min: 5, max: 30, step: 0.5 },
|
|
2216
|
+
},
|
|
2217
|
+
localTemperatureCalibration: { min: -5, max: 5, step: 0.1 },
|
|
2218
|
+
systemMode: ["heat"],
|
|
2219
|
+
});
|
|
2220
|
+
const exposes = thermostat.exposes;
|
|
2221
|
+
const fromZigbee = thermostat.fromZigbee;
|
|
2222
|
+
const toZigbee = thermostat.toZigbee;
|
|
2223
|
+
const configure = thermostat.configure;
|
|
2224
|
+
const meta = {
|
|
2225
|
+
overrideHaDiscoveryPayload: (payload) => {
|
|
2226
|
+
// Override climate discovery
|
|
2227
|
+
// https://github.com/Koenkk/zigbee2mqtt/pull/23075#issue-2355829475
|
|
2228
|
+
if (payload.mode_command_topic?.endsWith("/system_mode")) {
|
|
2229
|
+
payload.mode_command_topic = payload.mode_command_topic.substring(0, payload.mode_command_topic.lastIndexOf("/system_mode"));
|
|
2230
|
+
payload.mode_command_template =
|
|
2231
|
+
"{% set values = " +
|
|
2232
|
+
`{ 'auto':'schedule','heat':'manual','off':'pause'} %}` +
|
|
2233
|
+
`{"operating_mode": "{{ values[value] if value in values.keys() else 'pause' }}"}`;
|
|
2234
|
+
payload.mode_state_template =
|
|
2235
|
+
"{% set values = " +
|
|
2236
|
+
`{'schedule':'auto','manual':'heat','pause':'off'} %}` +
|
|
2237
|
+
`{% set value = value_json.operating_mode %}{{ values[value] if value in values.keys() else 'off' }}`;
|
|
2238
|
+
payload.modes = ["off", "heat", "auto"];
|
|
2239
|
+
}
|
|
2240
|
+
},
|
|
2241
|
+
};
|
|
2242
|
+
return {
|
|
2243
|
+
exposes,
|
|
2244
|
+
fromZigbee,
|
|
2245
|
+
toZigbee,
|
|
2246
|
+
configure,
|
|
2247
|
+
meta,
|
|
2248
|
+
isModernExtend: true,
|
|
2249
|
+
};
|
|
2250
|
+
},
|
|
2251
|
+
customRunningState: () => {
|
|
2252
|
+
const runningStates = ["idle", "heat"];
|
|
2253
|
+
const exposes = [e.enum("running_state", ea.STATE_GET, runningStates).withDescription("The current running state")];
|
|
2254
|
+
const fromZigbee = [
|
|
2255
|
+
{
|
|
2256
|
+
cluster: "hvacThermostat",
|
|
2257
|
+
type: ["attributeReport", "readResponse"],
|
|
2258
|
+
convert: (model, msg, publish, options, meta) => {
|
|
2259
|
+
const result = {};
|
|
2260
|
+
const data = msg.data;
|
|
2261
|
+
if (data.heatingDemand !== undefined) {
|
|
2262
|
+
result.running_state = utils.toNumber(data.heatingDemand) > 0 ? runningStates[1] : runningStates[0];
|
|
2263
|
+
}
|
|
2264
|
+
return result;
|
|
2265
|
+
},
|
|
2266
|
+
},
|
|
2267
|
+
];
|
|
2268
|
+
const toZigbee = [
|
|
2269
|
+
{
|
|
2270
|
+
key: ["running_state"],
|
|
2271
|
+
convertGet: async (entity, key, meta) => {
|
|
2272
|
+
await entity.read("hvacThermostat", ["heatingDemand"]);
|
|
2273
|
+
},
|
|
2274
|
+
},
|
|
2275
|
+
];
|
|
2276
|
+
return {
|
|
2277
|
+
exposes,
|
|
2278
|
+
fromZigbee,
|
|
2279
|
+
toZigbee,
|
|
2280
|
+
isModernExtend: true,
|
|
2281
|
+
};
|
|
2282
|
+
},
|
|
2283
|
+
boostHeating: () => {
|
|
2284
|
+
const boostHeatingLookup = {
|
|
2285
|
+
OFF: 0x00,
|
|
2286
|
+
ON: 0x01,
|
|
2287
|
+
};
|
|
2288
|
+
const exposes = [
|
|
2289
|
+
e
|
|
2290
|
+
.binary("boost_heating", ea.ALL, utils.getFromLookupByValue(0x01, boostHeatingLookup), utils.getFromLookupByValue(0x00, boostHeatingLookup))
|
|
2291
|
+
.withLabel("Activate boost heating")
|
|
2292
|
+
.withDescription("Activate boost heating (opens TRV for 5 minutes)"),
|
|
2293
|
+
];
|
|
2294
|
+
const fromZigbee = [
|
|
2295
|
+
{
|
|
2296
|
+
cluster: "hvacThermostat",
|
|
2297
|
+
type: ["attributeReport", "readResponse"],
|
|
2298
|
+
convert: (model, msg, publish, options, meta) => {
|
|
2299
|
+
const result = {};
|
|
2300
|
+
const data = msg.data;
|
|
2301
|
+
if (data.boostHeating !== undefined) {
|
|
2302
|
+
result.boost_heating = utils.getFromLookupByValue(data.boostHeating, boostHeatingLookup);
|
|
2303
|
+
}
|
|
2304
|
+
return result;
|
|
2305
|
+
},
|
|
2306
|
+
},
|
|
2307
|
+
];
|
|
2308
|
+
const toZigbee = [
|
|
2309
|
+
{
|
|
2310
|
+
key: ["boost_heating"],
|
|
2311
|
+
convertSet: async (entity, key, value, meta) => {
|
|
2312
|
+
const enableBoostHeating = value === utils.getFromLookupByValue(boostHeatingLookup.ON, boostHeatingLookup);
|
|
2313
|
+
if (enableBoostHeating) {
|
|
2314
|
+
const systemModeNotSetToHeat = "system_mode" in meta.state && meta.state.system_mode !== "heat";
|
|
2315
|
+
if (systemModeNotSetToHeat) {
|
|
2316
|
+
throw new Error("Boost heating is only possible when system mode is set to 'heat'!");
|
|
2317
|
+
}
|
|
2318
|
+
const heaterTypeNotSetToRadiator = "heater_type" in meta.state &&
|
|
2319
|
+
meta.state.heater_type !==
|
|
2320
|
+
utils.getFromLookupByValue(boschThermostatLookup.heaterType.radiator, boschThermostatLookup.heaterType);
|
|
2321
|
+
if (heaterTypeNotSetToRadiator) {
|
|
2322
|
+
throw new Error("Boost heating is only possible when heater type is set to 'radiator'!");
|
|
2323
|
+
}
|
|
2324
|
+
}
|
|
2325
|
+
await entity.write("hvacThermostat", {
|
|
2326
|
+
boostHeating: utils.toNumber(utils.getFromLookup(value, boostHeatingLookup)),
|
|
2327
|
+
});
|
|
2328
|
+
return { state: { boost_heating: value } };
|
|
2329
|
+
},
|
|
2330
|
+
convertGet: async (entity, key, meta) => {
|
|
2331
|
+
await entity.read("hvacThermostat", ["boostHeating"]);
|
|
2332
|
+
},
|
|
2333
|
+
},
|
|
2334
|
+
];
|
|
2335
|
+
const configure = [
|
|
2336
|
+
m.setupConfigureForReporting("hvacThermostat", "boostHeating", {
|
|
2337
|
+
config: { min: "MIN", max: "MAX", change: null },
|
|
2338
|
+
}),
|
|
2339
|
+
m.setupConfigureForReading("hvacThermostat", ["boostHeating"]),
|
|
2340
|
+
];
|
|
2341
|
+
return {
|
|
2342
|
+
exposes,
|
|
2343
|
+
fromZigbee,
|
|
2344
|
+
toZigbee,
|
|
2345
|
+
configure,
|
|
2346
|
+
isModernExtend: true,
|
|
2347
|
+
};
|
|
2348
|
+
},
|
|
2349
|
+
errorState: () => {
|
|
2350
|
+
const exposes = [
|
|
2351
|
+
e
|
|
2352
|
+
.text("error_state", ea.STATE_GET)
|
|
2353
|
+
.withDescription("Indicates whether the device encounters any errors or not")
|
|
2354
|
+
.withCategory("diagnostic"),
|
|
2355
|
+
];
|
|
2356
|
+
const fromZigbee = [
|
|
2357
|
+
{
|
|
2358
|
+
cluster: "hvacThermostat",
|
|
2359
|
+
type: ["attributeReport", "readResponse"],
|
|
2360
|
+
convert: (model, msg, publish, options, meta) => {
|
|
2361
|
+
const result = {};
|
|
2362
|
+
const data = msg.data;
|
|
2363
|
+
if (data.errorState !== undefined) {
|
|
2364
|
+
const receivedErrorState = data.errorState;
|
|
2365
|
+
if (receivedErrorState === 0) {
|
|
2366
|
+
result.error_state = "ok";
|
|
2367
|
+
}
|
|
2368
|
+
else {
|
|
2369
|
+
result.error_state = "";
|
|
2370
|
+
const bitmapLength = (receivedErrorState >>> 0).toString(2).length;
|
|
2371
|
+
for (let errorNumber = 0; errorNumber < bitmapLength; errorNumber++) {
|
|
2372
|
+
if ((receivedErrorState >> errorNumber) & 1) {
|
|
2373
|
+
if (String(result.error_state).length > 0) {
|
|
2374
|
+
result.error_state += " - ";
|
|
2375
|
+
}
|
|
2376
|
+
result.error_state += `E${String(errorNumber + 1).padStart(2, "0")}`;
|
|
2377
|
+
}
|
|
2378
|
+
}
|
|
2379
|
+
}
|
|
2380
|
+
}
|
|
2381
|
+
return result;
|
|
2382
|
+
},
|
|
2383
|
+
},
|
|
2384
|
+
];
|
|
2385
|
+
const toZigbee = [
|
|
2386
|
+
{
|
|
2387
|
+
key: ["error_state"],
|
|
2388
|
+
convertGet: async (entity, key, meta) => {
|
|
2389
|
+
await entity.read("hvacThermostat", ["errorState"]);
|
|
2390
|
+
},
|
|
2391
|
+
},
|
|
2392
|
+
];
|
|
2393
|
+
const configure = [
|
|
2394
|
+
m.setupConfigureForReporting("hvacThermostat", "errorState", {
|
|
2395
|
+
config: { min: "MIN", max: "MAX", change: null },
|
|
2396
|
+
}),
|
|
2397
|
+
m.setupConfigureForReading("hvacThermostat", ["errorState"]),
|
|
2398
|
+
];
|
|
2399
|
+
return {
|
|
2400
|
+
exposes,
|
|
2401
|
+
fromZigbee,
|
|
2402
|
+
toZigbee,
|
|
2403
|
+
configure,
|
|
2404
|
+
isModernExtend: true,
|
|
2405
|
+
};
|
|
2406
|
+
},
|
|
2407
|
+
valveAdaptation: () => {
|
|
2408
|
+
const valveAdaptStatusLookup = {
|
|
2409
|
+
none: 0x00,
|
|
2410
|
+
ready_to_calibrate: 0x01,
|
|
2411
|
+
calibration_in_progress: 0x02,
|
|
2412
|
+
error: 0x03,
|
|
2413
|
+
success: 0x04,
|
|
2414
|
+
};
|
|
2415
|
+
const exposes = [
|
|
2416
|
+
e
|
|
2417
|
+
.enum("valve_adapt_status", ea.STATE_GET, Object.keys(valveAdaptStatusLookup))
|
|
2418
|
+
.withLabel("Valve adaptation status")
|
|
2419
|
+
.withDescription("Specifies the current status of the valve adaptation")
|
|
2420
|
+
.withCategory("diagnostic"),
|
|
2421
|
+
e
|
|
2422
|
+
.enum("valve_adapt_process", ea.SET, ["adapt"])
|
|
2423
|
+
.withLabel("Trigger adaptation process")
|
|
2424
|
+
.withDescription('Trigger the valve adaptation process. Only possible when adaptation status is "ready_to_calibrate" or "error".')
|
|
2425
|
+
.withCategory("config"),
|
|
2426
|
+
];
|
|
2427
|
+
const fromZigbee = [
|
|
2428
|
+
{
|
|
2429
|
+
cluster: "hvacThermostat",
|
|
2430
|
+
type: ["attributeReport", "readResponse"],
|
|
2431
|
+
convert: (model, msg, publish, options, meta) => {
|
|
2432
|
+
const result = {};
|
|
2433
|
+
const data = msg.data;
|
|
2434
|
+
if (data.valveAdaptStatus !== undefined) {
|
|
2435
|
+
result.valve_adapt_status = utils.getFromLookupByValue(data.valveAdaptStatus, valveAdaptStatusLookup);
|
|
2436
|
+
}
|
|
2437
|
+
return result;
|
|
2438
|
+
},
|
|
2439
|
+
},
|
|
2440
|
+
];
|
|
2441
|
+
const toZigbee = [
|
|
2442
|
+
{
|
|
2443
|
+
key: ["valve_adapt_status", "valve_adapt_process"],
|
|
2444
|
+
convertSet: async (entity, key, value, meta) => {
|
|
2445
|
+
if (key === "valve_adapt_process") {
|
|
2446
|
+
let adaptStatus;
|
|
2447
|
+
try {
|
|
2448
|
+
adaptStatus = utils.getFromLookup(meta.state.valve_adapt_status, valveAdaptStatusLookup);
|
|
2449
|
+
}
|
|
2450
|
+
catch {
|
|
2451
|
+
adaptStatus = valveAdaptStatusLookup.none;
|
|
2452
|
+
}
|
|
2453
|
+
switch (adaptStatus) {
|
|
2454
|
+
case valveAdaptStatusLookup.ready_to_calibrate:
|
|
2455
|
+
case valveAdaptStatusLookup.error:
|
|
2456
|
+
await entity.command("hvacThermostat", "calibrateValve", {}, exports.manufacturerOptions);
|
|
2457
|
+
break;
|
|
2458
|
+
default:
|
|
2459
|
+
throw new Error("Valve adaptation process not possible right now!");
|
|
2460
|
+
}
|
|
2461
|
+
}
|
|
2462
|
+
},
|
|
2463
|
+
convertGet: async (entity, key, meta) => {
|
|
2464
|
+
if (key === "valve_adapt_status") {
|
|
2465
|
+
await entity.read("hvacThermostat", ["valveAdaptStatus"]);
|
|
2466
|
+
}
|
|
2467
|
+
},
|
|
2468
|
+
},
|
|
2469
|
+
];
|
|
2470
|
+
const configure = [
|
|
2471
|
+
m.setupConfigureForReporting("hvacThermostat", "valveAdaptStatus", {
|
|
2472
|
+
config: { min: "MIN", max: "MAX", change: null },
|
|
2473
|
+
}),
|
|
2474
|
+
];
|
|
2475
|
+
return {
|
|
2476
|
+
exposes,
|
|
2477
|
+
fromZigbee,
|
|
2478
|
+
toZigbee,
|
|
2479
|
+
configure,
|
|
2480
|
+
isModernExtend: true,
|
|
2481
|
+
};
|
|
2482
|
+
},
|
|
2483
|
+
/** The Bosch thermostats ask the coordinator on several occasions for
|
|
2484
|
+
* information about daylight saving time. As we don't support the setup
|
|
2485
|
+
* of schedules, we just answer with 0x00 so that the device stops asking.
|
|
2486
|
+
*
|
|
2487
|
+
* Since Bosch devices don't seem to like it when we offer incomplete
|
|
2488
|
+
* answers, we have to use a customReadResponse here and duplicate
|
|
2489
|
+
* some code from zigbee-herdsman. */
|
|
2490
|
+
handleDaylightSavingTimeReadRequest: () => {
|
|
2491
|
+
const onEvent = [
|
|
2492
|
+
(event) => {
|
|
2493
|
+
if (event.type !== "start") {
|
|
2494
|
+
return;
|
|
2495
|
+
}
|
|
2496
|
+
event.data.device.customReadResponse = (frame, endpoint) => {
|
|
2497
|
+
const isTimeClusterRequest = frame.isCluster("genTime");
|
|
2498
|
+
if (!isTimeClusterRequest) {
|
|
2499
|
+
return false;
|
|
2500
|
+
}
|
|
2501
|
+
const daylightSavingTimeRequested = frame.payload.find((i) => i.attrId === zigbee_herdsman_1.Zcl.Clusters.genTime.attributes.dstStart.ID) &&
|
|
2502
|
+
frame.payload.find((i) => i.attrId === zigbee_herdsman_1.Zcl.Clusters.genTime.attributes.dstEnd.ID) &&
|
|
2503
|
+
frame.payload.find((i) => i.attrId === zigbee_herdsman_1.Zcl.Clusters.genTime.attributes.dstShift.ID);
|
|
2504
|
+
if (!daylightSavingTimeRequested) {
|
|
2505
|
+
return false;
|
|
2506
|
+
}
|
|
2507
|
+
const payload = {
|
|
2508
|
+
dstStart: 0x00,
|
|
2509
|
+
dstEnd: 0x00,
|
|
2510
|
+
dstShift: 0x00,
|
|
2511
|
+
};
|
|
2512
|
+
const OneJanuary2000 = new Date("January 01, 2000 00:00:00 UTC+00:00").getTime();
|
|
2513
|
+
const time = Math.round((Date.now() - OneJanuary2000) / 1000);
|
|
2514
|
+
const timeRequested = frame.payload.find((i) => i.attrId === zigbee_herdsman_1.Zcl.Clusters.genTime.attributes.time.ID);
|
|
2515
|
+
if (timeRequested) {
|
|
2516
|
+
payload.time = time;
|
|
2517
|
+
}
|
|
2518
|
+
const timeZoneRequested = frame.payload.find((i) => i.attrId === zigbee_herdsman_1.Zcl.Clusters.genTime.attributes.timeZone.ID);
|
|
2519
|
+
if (timeZoneRequested) {
|
|
2520
|
+
payload.timeZone = new Date().getTimezoneOffset() * -1 * 60;
|
|
2521
|
+
}
|
|
2522
|
+
const localTimeRequested = frame.payload.find((i) => i.attrId === zigbee_herdsman_1.Zcl.Clusters.genTime.attributes.localTime.ID);
|
|
2523
|
+
if (localTimeRequested) {
|
|
2524
|
+
payload.localTime = time - new Date().getTimezoneOffset() * 60;
|
|
2525
|
+
}
|
|
2526
|
+
endpoint.readResponse(frame.cluster.name, frame.header.transactionSequenceNumber, payload).catch((e) => {
|
|
2527
|
+
logger_1.logger.warning(`Custom genTime response failed for '${event.data.device.ieeeAddr}': ${e}`, NS);
|
|
2528
|
+
});
|
|
2529
|
+
return true;
|
|
2530
|
+
};
|
|
2531
|
+
},
|
|
2532
|
+
];
|
|
2533
|
+
return {
|
|
2534
|
+
onEvent,
|
|
2535
|
+
isModernExtend: true,
|
|
2536
|
+
};
|
|
2537
|
+
},
|
|
2538
|
+
};
|
|
2019
2539
|
//endregion
|
|
2020
2540
|
//# sourceMappingURL=bosch.js.map
|