@matter/node 0.16.0-alpha.0-20251027-17770fb28 → 0.16.0-alpha.0-20251030-e9ca79f93

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.
Files changed (120) hide show
  1. package/dist/cjs/behavior/Behavior.d.ts +1 -0
  2. package/dist/cjs/behavior/Behavior.d.ts.map +1 -1
  3. package/dist/cjs/behavior/Behavior.js +5 -0
  4. package/dist/cjs/behavior/Behavior.js.map +1 -1
  5. package/dist/cjs/behavior/internal/BehaviorBacking.js +1 -1
  6. package/dist/cjs/behavior/internal/BehaviorBacking.js.map +1 -1
  7. package/dist/cjs/behavior/state/managed/Datasource.d.ts +4 -5
  8. package/dist/cjs/behavior/state/managed/Datasource.d.ts.map +1 -1
  9. package/dist/cjs/behavior/state/managed/Datasource.js +6 -2
  10. package/dist/cjs/behavior/state/managed/Datasource.js.map +1 -1
  11. package/dist/cjs/behavior/state/managed/ManagedReference.d.ts +3 -2
  12. package/dist/cjs/behavior/state/managed/ManagedReference.d.ts.map +1 -1
  13. package/dist/cjs/behavior/state/managed/ManagedReference.js +65 -20
  14. package/dist/cjs/behavior/state/managed/ManagedReference.js.map +1 -1
  15. package/dist/cjs/behavior/state/managed/values/ListManager.js +2 -1
  16. package/dist/cjs/behavior/state/managed/values/ListManager.js.map +1 -1
  17. package/dist/cjs/behavior/state/managed/values/StructManager.js +9 -1
  18. package/dist/cjs/behavior/state/managed/values/StructManager.js.map +1 -1
  19. package/dist/cjs/behaviors/access-control/AccessControlServer.d.ts.map +1 -1
  20. package/dist/cjs/behaviors/access-control/AccessControlServer.js +3 -3
  21. package/dist/cjs/behaviors/access-control/AccessControlServer.js.map +1 -1
  22. package/dist/cjs/behaviors/general-diagnostics/GeneralDiagnosticsServer.d.ts.map +1 -1
  23. package/dist/cjs/behaviors/general-diagnostics/GeneralDiagnosticsServer.js +3 -9
  24. package/dist/cjs/behaviors/general-diagnostics/GeneralDiagnosticsServer.js.map +1 -1
  25. package/dist/cjs/behaviors/service-area/ServiceAreaServer.js +2 -2
  26. package/dist/cjs/behaviors/service-area/ServiceAreaServer.js.map +1 -1
  27. package/dist/cjs/behaviors/thermostat/AtomicWriteHandler.d.ts +58 -0
  28. package/dist/cjs/behaviors/thermostat/AtomicWriteHandler.d.ts.map +1 -0
  29. package/dist/cjs/behaviors/thermostat/AtomicWriteHandler.js +306 -0
  30. package/dist/cjs/behaviors/thermostat/AtomicWriteHandler.js.map +6 -0
  31. package/dist/cjs/behaviors/thermostat/AtomicWriteState.d.ts +33 -0
  32. package/dist/cjs/behaviors/thermostat/AtomicWriteState.d.ts.map +1 -0
  33. package/dist/cjs/behaviors/thermostat/AtomicWriteState.js +86 -0
  34. package/dist/cjs/behaviors/thermostat/AtomicWriteState.js.map +6 -0
  35. package/dist/cjs/behaviors/thermostat/ThermostatBehavior.d.ts +12 -0
  36. package/dist/cjs/behaviors/thermostat/ThermostatBehavior.d.ts.map +1 -1
  37. package/dist/cjs/behaviors/thermostat/ThermostatInterface.d.ts +1 -0
  38. package/dist/cjs/behaviors/thermostat/ThermostatInterface.d.ts.map +1 -1
  39. package/dist/cjs/behaviors/thermostat/ThermostatServer.d.ts +894 -3
  40. package/dist/cjs/behaviors/thermostat/ThermostatServer.d.ts.map +1 -1
  41. package/dist/cjs/behaviors/thermostat/ThermostatServer.js +1216 -1
  42. package/dist/cjs/behaviors/thermostat/ThermostatServer.js.map +2 -2
  43. package/dist/cjs/devices/water-heater.d.ts +24 -0
  44. package/dist/cjs/devices/water-heater.d.ts.map +1 -1
  45. package/dist/cjs/endpoint/Endpoint.d.ts +36 -2
  46. package/dist/cjs/endpoint/Endpoint.d.ts.map +1 -1
  47. package/dist/cjs/endpoint/Endpoint.js +17 -14
  48. package/dist/cjs/endpoint/Endpoint.js.map +1 -1
  49. package/dist/cjs/endpoint/properties/EndpointContainer.d.ts +1 -0
  50. package/dist/cjs/endpoint/properties/EndpointContainer.d.ts.map +1 -1
  51. package/dist/cjs/endpoint/properties/EndpointContainer.js +3 -0
  52. package/dist/cjs/endpoint/properties/EndpointContainer.js.map +1 -1
  53. package/dist/esm/behavior/Behavior.d.ts +1 -0
  54. package/dist/esm/behavior/Behavior.d.ts.map +1 -1
  55. package/dist/esm/behavior/Behavior.js +5 -0
  56. package/dist/esm/behavior/Behavior.js.map +1 -1
  57. package/dist/esm/behavior/internal/BehaviorBacking.js +2 -2
  58. package/dist/esm/behavior/internal/BehaviorBacking.js.map +1 -1
  59. package/dist/esm/behavior/state/managed/Datasource.d.ts +4 -5
  60. package/dist/esm/behavior/state/managed/Datasource.d.ts.map +1 -1
  61. package/dist/esm/behavior/state/managed/Datasource.js +7 -3
  62. package/dist/esm/behavior/state/managed/Datasource.js.map +1 -1
  63. package/dist/esm/behavior/state/managed/ManagedReference.d.ts +3 -2
  64. package/dist/esm/behavior/state/managed/ManagedReference.d.ts.map +1 -1
  65. package/dist/esm/behavior/state/managed/ManagedReference.js +66 -21
  66. package/dist/esm/behavior/state/managed/ManagedReference.js.map +1 -1
  67. package/dist/esm/behavior/state/managed/values/ListManager.js +2 -1
  68. package/dist/esm/behavior/state/managed/values/ListManager.js.map +1 -1
  69. package/dist/esm/behavior/state/managed/values/StructManager.js +9 -1
  70. package/dist/esm/behavior/state/managed/values/StructManager.js.map +1 -1
  71. package/dist/esm/behaviors/access-control/AccessControlServer.d.ts.map +1 -1
  72. package/dist/esm/behaviors/access-control/AccessControlServer.js +3 -3
  73. package/dist/esm/behaviors/access-control/AccessControlServer.js.map +1 -1
  74. package/dist/esm/behaviors/general-diagnostics/GeneralDiagnosticsServer.d.ts.map +1 -1
  75. package/dist/esm/behaviors/general-diagnostics/GeneralDiagnosticsServer.js +3 -9
  76. package/dist/esm/behaviors/general-diagnostics/GeneralDiagnosticsServer.js.map +1 -1
  77. package/dist/esm/behaviors/service-area/ServiceAreaServer.js +2 -2
  78. package/dist/esm/behaviors/service-area/ServiceAreaServer.js.map +1 -1
  79. package/dist/esm/behaviors/thermostat/AtomicWriteHandler.d.ts +58 -0
  80. package/dist/esm/behaviors/thermostat/AtomicWriteHandler.d.ts.map +1 -0
  81. package/dist/esm/behaviors/thermostat/AtomicWriteHandler.js +293 -0
  82. package/dist/esm/behaviors/thermostat/AtomicWriteHandler.js.map +6 -0
  83. package/dist/esm/behaviors/thermostat/AtomicWriteState.d.ts +33 -0
  84. package/dist/esm/behaviors/thermostat/AtomicWriteState.d.ts.map +1 -0
  85. package/dist/esm/behaviors/thermostat/AtomicWriteState.js +66 -0
  86. package/dist/esm/behaviors/thermostat/AtomicWriteState.js.map +6 -0
  87. package/dist/esm/behaviors/thermostat/ThermostatBehavior.d.ts +12 -0
  88. package/dist/esm/behaviors/thermostat/ThermostatBehavior.d.ts.map +1 -1
  89. package/dist/esm/behaviors/thermostat/ThermostatInterface.d.ts +1 -0
  90. package/dist/esm/behaviors/thermostat/ThermostatInterface.d.ts.map +1 -1
  91. package/dist/esm/behaviors/thermostat/ThermostatServer.d.ts +894 -3
  92. package/dist/esm/behaviors/thermostat/ThermostatServer.d.ts.map +1 -1
  93. package/dist/esm/behaviors/thermostat/ThermostatServer.js +1225 -1
  94. package/dist/esm/behaviors/thermostat/ThermostatServer.js.map +2 -2
  95. package/dist/esm/devices/water-heater.d.ts +24 -0
  96. package/dist/esm/devices/water-heater.d.ts.map +1 -1
  97. package/dist/esm/endpoint/Endpoint.d.ts +36 -2
  98. package/dist/esm/endpoint/Endpoint.d.ts.map +1 -1
  99. package/dist/esm/endpoint/Endpoint.js +17 -14
  100. package/dist/esm/endpoint/Endpoint.js.map +1 -1
  101. package/dist/esm/endpoint/properties/EndpointContainer.d.ts +1 -0
  102. package/dist/esm/endpoint/properties/EndpointContainer.d.ts.map +1 -1
  103. package/dist/esm/endpoint/properties/EndpointContainer.js +3 -0
  104. package/dist/esm/endpoint/properties/EndpointContainer.js.map +1 -1
  105. package/package.json +7 -7
  106. package/src/behavior/Behavior.ts +10 -0
  107. package/src/behavior/internal/BehaviorBacking.ts +2 -2
  108. package/src/behavior/state/managed/Datasource.ts +14 -7
  109. package/src/behavior/state/managed/ManagedReference.ts +67 -19
  110. package/src/behavior/state/managed/values/ListManager.ts +1 -0
  111. package/src/behavior/state/managed/values/StructManager.ts +13 -3
  112. package/src/behaviors/access-control/AccessControlServer.ts +3 -7
  113. package/src/behaviors/general-diagnostics/GeneralDiagnosticsServer.ts +5 -9
  114. package/src/behaviors/service-area/ServiceAreaServer.ts +2 -2
  115. package/src/behaviors/thermostat/AtomicWriteHandler.ts +412 -0
  116. package/src/behaviors/thermostat/AtomicWriteState.ts +91 -0
  117. package/src/behaviors/thermostat/ThermostatInterface.ts +2 -0
  118. package/src/behaviors/thermostat/ThermostatServer.ts +1487 -3
  119. package/src/endpoint/Endpoint.ts +61 -5
  120. package/src/endpoint/properties/EndpointContainer.ts +4 -0
@@ -18,15 +18,1230 @@ var __copyProps = (to, from, except, desc) => {
18
18
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
19
  var ThermostatServer_exports = {};
20
20
  __export(ThermostatServer_exports, {
21
+ ThermostatBaseServer: () => ThermostatBaseServer,
21
22
  ThermostatServer: () => ThermostatServer
22
23
  });
23
24
  module.exports = __toCommonJS(ThermostatServer_exports);
25
+ var import_occupancy_sensing = require("#behaviors/occupancy-sensing");
26
+ var import_temperature_measurement = require("#behaviors/temperature-measurement");
27
+ var import_thermostat = require("#clusters/thermostat");
28
+ var import_general = require("#general");
29
+ var import_model = require("#model");
30
+ var import_protocol = require("#protocol");
31
+ var import_types = require("#types");
32
+ var import_AtomicWriteHandler = require("./AtomicWriteHandler.js");
24
33
  var import_ThermostatBehavior = require("./ThermostatBehavior.js");
25
34
  /**
26
35
  * @license
27
36
  * Copyright 2022-2025 Matter.js Authors
28
37
  * SPDX-License-Identifier: Apache-2.0
29
38
  */
30
- class ThermostatServer extends import_ThermostatBehavior.ThermostatBehavior {
39
+ const logger = import_general.Logger.get("ThermostatServer");
40
+ const ThermostatBehaviorLogicBase = import_ThermostatBehavior.ThermostatBehavior.with(
41
+ import_thermostat.Thermostat.Feature.Heating,
42
+ import_thermostat.Thermostat.Feature.Cooling,
43
+ import_thermostat.Thermostat.Feature.Occupancy,
44
+ import_thermostat.Thermostat.Feature.AutoMode,
45
+ import_thermostat.Thermostat.Feature.Presets
46
+ );
47
+ const schema = ThermostatBehaviorLogicBase.schema.extend({
48
+ children: [
49
+ (0, import_model.FieldElement)({
50
+ name: "PersistedPresets",
51
+ type: "list",
52
+ conformance: "[PRES]",
53
+ quality: "N",
54
+ children: [(0, import_model.FieldElement)({ name: "entry", type: "PresetStruct" })]
55
+ })
56
+ ]
57
+ });
58
+ class ThermostatBaseServer extends ThermostatBehaviorLogicBase {
59
+ static schema = schema;
60
+ async initialize() {
61
+ if (this.features.scheduleConfiguration) {
62
+ throw new import_general.ImplementationError("ScheduleConfiguration features is deprecated and not allowed to be enabled");
63
+ }
64
+ if (this.features.setback) {
65
+ throw new import_general.ImplementationError("Setback feature is deprecated and not allowed to be enabled");
66
+ }
67
+ if (this.features.matterScheduleConfiguration) {
68
+ logger.warn("MatterScheduleConfiguration feature is not yet implemented. Please do not activate it");
69
+ }
70
+ const options = this.endpoint.behaviors.optionsFor(ThermostatBaseServer);
71
+ if (this.features.presets && this.state.persistedPresets === void 0) {
72
+ this.state.persistedPresets = options?.presets ?? [];
73
+ }
74
+ if (this.state.minSetpointDeadBand > 127) {
75
+ this.state.minSetpointDeadBand = 20;
76
+ }
77
+ if (this.state.minSetpointDeadBand < 0 || this.state.minSetpointDeadBand > 127) {
78
+ throw new import_general.ImplementationError("minSetpointDeadBand is out of valid range 0..127");
79
+ }
80
+ this.#setupValidations();
81
+ this.#setupTemperatureMeasurementIntegration();
82
+ this.#setupOccupancyIntegration();
83
+ this.#setupModeHandling();
84
+ this.#setupThermostatLogic();
85
+ this.#setupPresets();
86
+ this.internal.minSetpointDeadBand = this.state.minSetpointDeadBand;
87
+ this.internal.controlSequenceOfOperation = this.state.controlSequenceOfOperation;
88
+ }
89
+ /**
90
+ * The default implementation of the SetpointRaiseLower command. It handles all validation and setpoint adjustments
91
+ * required by the Matter specification. This method only changes the Occupied setpoints.
92
+ */
93
+ setpointRaiseLower({ mode, amount }) {
94
+ if (mode === import_thermostat.Thermostat.SetpointRaiseLowerMode.Heat && !this.features.heating) {
95
+ throw new import_types.StatusResponse.InvalidCommandError(
96
+ "Heating feature is not supported but Heat mode was requested"
97
+ );
98
+ }
99
+ if (mode === import_thermostat.Thermostat.SetpointRaiseLowerMode.Cool && !this.features.cooling) {
100
+ throw new import_types.StatusResponse.InvalidCommandError(
101
+ "Cooling feature is not supported but Cool mode was requested"
102
+ );
103
+ }
104
+ amount *= 10;
105
+ if (mode === import_thermostat.Thermostat.SetpointRaiseLowerMode.Both) {
106
+ if (this.features.heating && this.features.cooling) {
107
+ let desiredCoolingSetpoint = this.state.occupiedCoolingSetpoint + amount;
108
+ const coolLimit = desiredCoolingSetpoint - this.#clampSetpointToLimits("Cool", desiredCoolingSetpoint);
109
+ let desiredHeatingSetpoint = this.state.occupiedHeatingSetpoint + amount;
110
+ const heatLimit = desiredHeatingSetpoint - this.#clampSetpointToLimits("Heat", desiredHeatingSetpoint);
111
+ if (coolLimit !== 0 || heatLimit !== 0) {
112
+ if (Math.abs(coolLimit) <= Math.abs(heatLimit)) {
113
+ desiredHeatingSetpoint = desiredHeatingSetpoint - heatLimit;
114
+ desiredCoolingSetpoint = desiredCoolingSetpoint - heatLimit;
115
+ } else {
116
+ desiredHeatingSetpoint = desiredHeatingSetpoint - coolLimit;
117
+ desiredCoolingSetpoint = desiredCoolingSetpoint - coolLimit;
118
+ }
119
+ }
120
+ this.state.occupiedCoolingSetpoint = desiredCoolingSetpoint;
121
+ this.state.occupiedHeatingSetpoint = desiredHeatingSetpoint;
122
+ } else if (this.features.cooling) {
123
+ this.state.occupiedCoolingSetpoint = this.#clampSetpointToLimits(
124
+ "Cool",
125
+ this.state.occupiedCoolingSetpoint + amount
126
+ );
127
+ } else {
128
+ this.state.occupiedHeatingSetpoint = this.#clampSetpointToLimits(
129
+ "Heat",
130
+ this.state.occupiedHeatingSetpoint + amount
131
+ );
132
+ }
133
+ return;
134
+ }
135
+ if (mode === import_thermostat.Thermostat.SetpointRaiseLowerMode.Cool) {
136
+ const desiredCoolingSetpoint = this.#clampSetpointToLimits(
137
+ "Cool",
138
+ this.state.occupiedCoolingSetpoint + amount
139
+ );
140
+ if (this.features.autoMode) {
141
+ let heatingSetpoint = this.state.occupiedHeatingSetpoint;
142
+ if (desiredCoolingSetpoint - heatingSetpoint < this.setpointDeadBand) {
143
+ heatingSetpoint = desiredCoolingSetpoint - this.setpointDeadBand;
144
+ if (heatingSetpoint === this.#clampSetpointToLimits("Heat", heatingSetpoint)) {
145
+ this.state.occupiedHeatingSetpoint = heatingSetpoint;
146
+ } else {
147
+ throw new import_types.StatusResponse.InvalidCommandError(
148
+ "Could Not adjust heating setpoint to maintain dead band!"
149
+ );
150
+ }
151
+ }
152
+ }
153
+ this.state.occupiedCoolingSetpoint = desiredCoolingSetpoint;
154
+ return;
155
+ }
156
+ if (mode === import_thermostat.Thermostat.SetpointRaiseLowerMode.Heat) {
157
+ const desiredHeatingSetpoint = this.#clampSetpointToLimits(
158
+ "Heat",
159
+ this.state.occupiedHeatingSetpoint + amount
160
+ );
161
+ if (this.features.autoMode) {
162
+ let coolingSetpoint = this.state.occupiedCoolingSetpoint;
163
+ if (coolingSetpoint - desiredHeatingSetpoint < this.setpointDeadBand) {
164
+ coolingSetpoint = desiredHeatingSetpoint + this.setpointDeadBand;
165
+ if (coolingSetpoint === this.#clampSetpointToLimits("Cool", coolingSetpoint)) {
166
+ this.state.occupiedCoolingSetpoint = coolingSetpoint;
167
+ } else {
168
+ throw new import_types.StatusResponse.InvalidCommandError(
169
+ "Could Not adjust cooling setpoint to maintain dead band!"
170
+ );
171
+ }
172
+ }
173
+ }
174
+ this.state.occupiedHeatingSetpoint = desiredHeatingSetpoint;
175
+ return;
176
+ }
177
+ throw new import_types.StatusResponse.InvalidCommandError(`Unsupported SetpointRaiseLowerMode ${mode}`);
178
+ }
179
+ /**
180
+ * Performs basic validation and sets the active preset handle when valid.
181
+ * This fulfills the basic requirements of the SetActivePresetRequest matter command. Use this method if you need
182
+ * to override setActivePresetRequest to ensure compliance.
183
+ */
184
+ handleSetActivePresetRequest({ presetHandle }) {
185
+ let preset = void 0;
186
+ if (presetHandle !== null) {
187
+ preset = this.state.persistedPresets?.find(
188
+ (p) => p.presetHandle !== null && import_general.Bytes.areEqual(p.presetHandle, presetHandle)
189
+ );
190
+ if (preset === void 0) {
191
+ throw new import_types.StatusResponse.InvalidCommandError("Requested PresetHandle not found");
192
+ }
193
+ }
194
+ logger.info(`Setting active preset handle to`, presetHandle);
195
+ this.state.activePresetHandle = presetHandle;
196
+ return preset;
197
+ }
198
+ /**
199
+ * This default implementation of the SetActivePresetRequest command handler sets the active preset and
200
+ * (additionally to specification requirements!) adjusts the occupied setpoints to the preset values if defined.
201
+ *
202
+ * If you do not want this behavior, you can override this method but should call handleSetActivePresetRequest to
203
+ * ensure compliance with the specification.
204
+ */
205
+ setActivePresetRequest({ presetHandle }) {
206
+ const preset = this.handleSetActivePresetRequest({ presetHandle });
207
+ if (preset !== void 0) {
208
+ const { heatingSetpoint, coolingSetpoint } = preset;
209
+ if (this.features.heating && heatingSetpoint !== null && heatingSetpoint !== void 0) {
210
+ this.state.occupiedHeatingSetpoint = this.#clampSetpointToLimits("Heat", heatingSetpoint);
211
+ }
212
+ if (this.features.cooling && coolingSetpoint !== null && coolingSetpoint !== void 0) {
213
+ this.state.occupiedCoolingSetpoint = this.#clampSetpointToLimits("Cool", coolingSetpoint);
214
+ }
215
+ }
216
+ }
217
+ /** Determines if the given context is from a command */
218
+ #isCommandContext(context) {
219
+ return "command" in context && context.command;
220
+ }
221
+ /**
222
+ * Whether the thermostat is currently considered occupied
223
+ * Uses the occupancy state if the feature is supported, otherwise always true
224
+ */
225
+ get occupied() {
226
+ return this.features.occupancy ? this.state.occupancy?.occupied ?? true : true;
227
+ }
228
+ /** The current heating setpoint depending on occupancy */
229
+ get heatingSetpoint() {
230
+ if (this.occupied) {
231
+ return this.state.occupiedHeatingSetpoint;
232
+ }
233
+ return this.state.unoccupiedHeatingSetpoint;
234
+ }
235
+ /** The current cooling setpoint depending on occupancy */
236
+ get coolingSetpoint() {
237
+ if (this.occupied) {
238
+ return this.state.occupiedCoolingSetpoint;
239
+ }
240
+ return this.state.unoccupiedCoolingSetpoint;
241
+ }
242
+ /** Setup basic Thermostat state and logic */
243
+ #setupThermostatLogic() {
244
+ if (this.state.temperatureSetpointHold !== void 0) {
245
+ if (this.state.temperatureSetpointHoldDuration === void 0) {
246
+ this.state.temperatureSetpointHoldDuration = null;
247
+ }
248
+ if (this.state.setpointHoldExpiryTimestamp === void 0) {
249
+ } else {
250
+ logger.warn(
251
+ "Handling for setpointHoldExpiryTimestamp is not yet implemented. To use this attribute you need to install the needed logic yourself"
252
+ );
253
+ }
254
+ }
255
+ }
256
+ // TODO Add when we adjusted the epoch-s handling to be correct
257
+ /*#handleTemperatureSetpointHoldChange(newValue: Thermostat.TemperatureSetpointHold) {
258
+ if (newValue === Thermostat.TemperatureSetpointHold.SetpointHoldOn) {
259
+ if (
260
+ this.state.temperatureSetpointHoldDuration !== null &&
261
+ this.state.temperatureSetpointHoldDuration! > 0
262
+ ) {
263
+ // TODO: convert to use of Seconds and such and real UTC time
264
+ // Also requires adjustment in encoding/decoding of the attribute
265
+ const nowUtc = Time.nowMs - 946_684_800_000; // Still not really UTC, but ok for now
266
+ this.state.setpointHoldExpiryTimestamp = Math.floor(
267
+ nowUtc / 1000 + this.state.temperatureSetpointHoldDuration! * 60,
268
+ );
269
+ }
270
+ } else {
271
+ this.state.setpointHoldExpiryTimestamp = null;
272
+ }
273
+ }*/
274
+ /** Whether heating is allowed in the current ControlSequenceOfOperation and features */
275
+ get heatingAllowed() {
276
+ return this.features.heating && ![
277
+ import_thermostat.Thermostat.ControlSequenceOfOperation.CoolingOnly,
278
+ import_thermostat.Thermostat.ControlSequenceOfOperation.CoolingAndHeatingWithReheat
279
+ ].includes(this.internal.controlSequenceOfOperation);
280
+ }
281
+ /** Whether cooling is allowed in the current ControlSequenceOfOperation and features */
282
+ get coolingAllowed() {
283
+ return this.features.cooling && ![
284
+ import_thermostat.Thermostat.ControlSequenceOfOperation.HeatingOnly,
285
+ import_thermostat.Thermostat.ControlSequenceOfOperation.HeatingWithReheat
286
+ ].includes(this.internal.controlSequenceOfOperation);
287
+ }
288
+ /**
289
+ * Adjust the running mode of the thermostat based on the new system mode when the thermostatRunningMode is supported
290
+ */
291
+ adjustRunningMode(newState) {
292
+ if (this.state.thermostatRunningMode === void 0) {
293
+ return;
294
+ }
295
+ switch (newState) {
296
+ case import_thermostat.Thermostat.ThermostatRunningMode.Heat:
297
+ if (!this.heatingAllowed) {
298
+ throw new import_general.ImplementationError("Heating is not allowed in the current ControlSequenceOfOperation");
299
+ }
300
+ break;
301
+ case import_thermostat.Thermostat.ThermostatRunningMode.Cool:
302
+ if (!this.coolingAllowed) {
303
+ throw new import_general.ImplementationError("Cooling is not allowed in the current ControlSequenceOfOperation");
304
+ }
305
+ break;
306
+ }
307
+ this.state.thermostatRunningMode = newState;
308
+ }
309
+ /**
310
+ * Setup integration with TemperatureMeasurement cluster or external temperature state and intialize internal
311
+ * localTemperature state.
312
+ */
313
+ #setupTemperatureMeasurementIntegration() {
314
+ const preferRemoteTemperature = !!this.state.remoteSensing?.localTemperature;
315
+ if (this.features.localTemperatureNotExposed) {
316
+ if (preferRemoteTemperature) {
317
+ throw new import_general.ImplementationError(
318
+ "RemoteSensing cannot be set to LocalTemperature when LocalTemperatureNotExposed feature is enabled"
319
+ );
320
+ }
321
+ logger.debug("LocalTemperatureNotExposed feature is enabled, ignoring local temperature measurement");
322
+ this.state.localTemperature = null;
323
+ }
324
+ let localTemperature = null;
325
+ if (!preferRemoteTemperature && this.agent.has(import_temperature_measurement.TemperatureMeasurementServer)) {
326
+ logger.debug(
327
+ "Using existing TemperatureMeasurement cluster on same endpoint for local temperature measurement"
328
+ );
329
+ if (this.state.externalMeasuredIndoorTemperature !== void 0) {
330
+ logger.warn(
331
+ "Both local TemperatureMeasurement cluster and externalMeasuredIndoorTemperature state are set, using local cluster"
332
+ );
333
+ }
334
+ this.reactTo(
335
+ this.agent.get(import_temperature_measurement.TemperatureMeasurementServer).events.measuredValue$Changed,
336
+ this.#handleMeasuredTemperatureChange
337
+ );
338
+ localTemperature = this.endpoint.stateOf(import_temperature_measurement.TemperatureMeasurementServer).measuredValue;
339
+ } else {
340
+ if (this.state.externalMeasuredIndoorTemperature === void 0) {
341
+ logger.warn(
342
+ "No local TemperatureMeasurement cluster available and externalMeasuredIndoorTemperature state not set. Setting localTemperature to null"
343
+ );
344
+ } else {
345
+ logger.info("Using measured temperature via externalMeasuredIndoorTemperature state");
346
+ localTemperature = this.state.externalMeasuredIndoorTemperature ?? null;
347
+ }
348
+ this.reactTo(this.events.externalMeasuredIndoorTemperature$Changed, this.#handleMeasuredTemperatureChange);
349
+ }
350
+ this.#handleMeasuredTemperatureChange(localTemperature);
351
+ }
352
+ /**
353
+ * Handles changes to the measured temperature, applies calibration and update internal and official state.
354
+ */
355
+ #handleMeasuredTemperatureChange(temperature) {
356
+ if (temperature !== null && this.state.localTemperatureCalibration !== void 0) {
357
+ temperature += this.state.localTemperatureCalibration * 10;
358
+ }
359
+ if (!this.features.localTemperatureNotExposed) {
360
+ this.state.localTemperature = temperature;
361
+ }
362
+ const oldTemperature = this.internal.localTemperature;
363
+ if (temperature !== null && oldTemperature !== temperature) {
364
+ this.internal.localTemperature = temperature;
365
+ this.events.calibratedTemperature$Changed.emit(temperature, oldTemperature, this.context);
366
+ }
367
+ }
368
+ /**
369
+ * Setup integration with OccupancySensing cluster or external occupancy state and initialize internal occupancy
370
+ * state.
371
+ */
372
+ #setupOccupancyIntegration() {
373
+ if (!this.features.occupancy) {
374
+ return;
375
+ }
376
+ let currentOccupancy;
377
+ const preferRemoteOccupancy = !!this.state.remoteSensing?.occupancy;
378
+ if (!preferRemoteOccupancy && this.agent.has(import_occupancy_sensing.OccupancySensingServer)) {
379
+ logger.debug("Using existing OccupancySensing cluster on same endpoint for local occupancy sensing");
380
+ if (this.state.externallyMeasuredOccupancy !== void 0) {
381
+ logger.warn(
382
+ "Both local OccupancySensing cluster and externallyMeasuredOccupancy state are set, using local cluster"
383
+ );
384
+ }
385
+ this.reactTo(this.agent.get(import_occupancy_sensing.OccupancySensingServer).events.occupancy$Changed, this.#handleOccupancyChange);
386
+ currentOccupancy = !!this.endpoint.stateOf(import_occupancy_sensing.OccupancySensingServer).occupancy.occupied;
387
+ } else {
388
+ if (this.state.externallyMeasuredOccupancy === void 0) {
389
+ currentOccupancy = true;
390
+ logger.warn(
391
+ "No local OccupancySensing cluster available and externallyMeasuredOccupancy state not set"
392
+ );
393
+ } else {
394
+ logger.info("Using occupancy via externallyMeasuredOccupancy state");
395
+ currentOccupancy = this.state.externallyMeasuredOccupancy;
396
+ }
397
+ this.reactTo(this.events.externallyMeasuredOccupancy$Changed, this.#handleExternalOccupancyChange);
398
+ }
399
+ this.#handleExternalOccupancyChange(currentOccupancy);
400
+ }
401
+ #handleExternalOccupancyChange(newValue) {
402
+ this.state.occupancy = { occupied: newValue };
403
+ }
404
+ #handleOccupancyChange(newValue) {
405
+ this.state.occupancy = newValue;
406
+ }
407
+ /** Setup all validations for the Thermostat behavior */
408
+ #setupValidations() {
409
+ this.#assertUserSetpointLimits("HeatSetpointLimit");
410
+ this.#assertUserSetpointLimits("CoolSetpointLimit");
411
+ this.#clampSetpointToLimits("Heat", this.state.occupiedHeatingSetpoint);
412
+ this.#clampSetpointToLimits("Heat", this.state.unoccupiedHeatingSetpoint);
413
+ this.#clampSetpointToLimits("Cool", this.state.occupiedCoolingSetpoint);
414
+ this.#clampSetpointToLimits("Cool", this.state.unoccupiedCoolingSetpoint);
415
+ this.maybeReactTo(this.events.absMinHeatSetpointLimit$Changing, this.#assertAbsMinHeatSetpointLimitChanging);
416
+ this.maybeReactTo(this.events.minHeatSetpointLimit$Changing, this.#assertMinHeatSetpointLimitChanging);
417
+ this.maybeReactTo(this.events.maxHeatSetpointLimit$Changing, this.#assertMaxHeatSetpointLimitChanging);
418
+ this.maybeReactTo(this.events.absMaxHeatSetpointLimit$Changing, this.#assertAbsMaxHeatSetpointLimitChanging);
419
+ this.maybeReactTo(this.events.absMinCoolSetpointLimit$Changing, this.#assertAbsMinCoolSetpointLimitChanging);
420
+ this.maybeReactTo(this.events.minCoolSetpointLimit$Changing, this.#assertMinCoolSetpointLimitChanging);
421
+ this.maybeReactTo(this.events.maxCoolSetpointLimit$Changing, this.#assertMaxCoolSetpointLimitChanging);
422
+ this.maybeReactTo(this.events.absMaxCoolSetpointLimit$Changing, this.#assertAbsMaxCoolSetpointLimitChanging);
423
+ this.maybeReactTo(this.events.occupiedHeatingSetpoint$Changing, this.#assertOccupiedHeatingSetpointChanging);
424
+ this.maybeReactTo(
425
+ this.events.unoccupiedHeatingSetpoint$Changing,
426
+ this.#assertUnoccupiedHeatingSetpointChanging
427
+ );
428
+ this.maybeReactTo(this.events.occupiedCoolingSetpoint$Changing, this.#assertOccupiedCoolingSetpointChanging);
429
+ this.maybeReactTo(
430
+ this.events.unoccupiedCoolingSetpoint$Changing,
431
+ this.#assertUnoccupiedCoolingSetpointChanging
432
+ );
433
+ this.maybeReactTo(this.events.remoteSensing$Changing, this.#assertRemoteSensingChanging);
434
+ this.maybeReactTo(this.events.minSetpointDeadBand$Changing, this.#ensureMinSetpointDeadBandNotWritable);
435
+ this.reactTo(
436
+ this.events.controlSequenceOfOperation$Changing,
437
+ this.#ensureControlSequenceOfOperationNotWritable
438
+ );
439
+ this.reactTo(this.events.systemMode$Changing, this.#assertSystemModeChanging);
440
+ this.maybeReactTo(this.events.thermostatRunningMode$Changing, this.#assertThermostatRunningModeChanging);
441
+ }
442
+ #assertThermostatRunningModeChanging(newRunningMode) {
443
+ const forbiddenRunningModes = new Array();
444
+ switch (this.internal.controlSequenceOfOperation) {
445
+ case import_thermostat.Thermostat.ControlSequenceOfOperation.CoolingOnly:
446
+ case import_thermostat.Thermostat.ControlSequenceOfOperation.CoolingAndHeatingWithReheat:
447
+ forbiddenRunningModes.push(import_thermostat.Thermostat.ThermostatRunningMode.Heat);
448
+ break;
449
+ case import_thermostat.Thermostat.ControlSequenceOfOperation.HeatingOnly:
450
+ case import_thermostat.Thermostat.ControlSequenceOfOperation.HeatingWithReheat:
451
+ forbiddenRunningModes.push(import_thermostat.Thermostat.ThermostatRunningMode.Cool);
452
+ break;
453
+ }
454
+ if (forbiddenRunningModes.includes(newRunningMode)) {
455
+ throw new import_types.StatusResponse.ConstraintErrorError(
456
+ `ThermostatRunningMode ${import_thermostat.Thermostat.ThermostatRunningMode[newRunningMode]} is not allowed with ControlSequenceOfOperation ${import_thermostat.Thermostat.ControlSequenceOfOperation[this.internal.controlSequenceOfOperation]}`
457
+ );
458
+ }
459
+ }
460
+ #assertSystemModeChanging(newMode) {
461
+ const forbiddenModes = new Array();
462
+ switch (this.internal.controlSequenceOfOperation) {
463
+ case import_thermostat.Thermostat.ControlSequenceOfOperation.CoolingOnly:
464
+ case import_thermostat.Thermostat.ControlSequenceOfOperation.CoolingAndHeatingWithReheat:
465
+ forbiddenModes.push(import_thermostat.Thermostat.SystemMode.Heat, import_thermostat.Thermostat.SystemMode.EmergencyHeat);
466
+ break;
467
+ case import_thermostat.Thermostat.ControlSequenceOfOperation.HeatingOnly:
468
+ case import_thermostat.Thermostat.ControlSequenceOfOperation.HeatingWithReheat:
469
+ forbiddenModes.push(import_thermostat.Thermostat.SystemMode.Cool, import_thermostat.Thermostat.SystemMode.Precooling);
470
+ break;
471
+ }
472
+ if (forbiddenModes.includes(newMode)) {
473
+ throw new import_types.StatusResponse.ConstraintErrorError(
474
+ `SystemMode ${import_thermostat.Thermostat.SystemMode[newMode]} is not allowed with ControlSequenceOfOperation ${import_thermostat.Thermostat.ControlSequenceOfOperation[this.internal.controlSequenceOfOperation]}`
475
+ );
476
+ }
477
+ }
478
+ /** Attribute is not writable, revert any changes */
479
+ #ensureControlSequenceOfOperationNotWritable() {
480
+ this.state.controlSequenceOfOperation = this.internal.controlSequenceOfOperation;
481
+ }
482
+ /** Attribute is not writable, revert any changes, but also ensure proper errors when write try was invalid */
483
+ #ensureMinSetpointDeadBandNotWritable(value) {
484
+ if (value < 0 || value > 127) {
485
+ throw new import_types.StatusResponse.ConstraintErrorError("MinSetpointDeadBand is out of valid range 0..127");
486
+ }
487
+ this.state.minSetpointDeadBand = this.internal.minSetpointDeadBand;
488
+ }
489
+ #assertRemoteSensingChanging(remoteSensing) {
490
+ if (this.features.localTemperatureNotExposed && remoteSensing.localTemperature) {
491
+ throw new import_types.StatusResponse.ConstraintErrorError(
492
+ "LocalTemperature is not exposed, so RemoteSensing cannot be set to LocalTemperature"
493
+ );
494
+ }
495
+ }
496
+ #assertUnoccupiedCoolingSetpointChanging(setpoint, _old, context) {
497
+ this.#assertSetpointWithinLimits("Cool", "Unoccupied", setpoint);
498
+ this.#assertSetpointDeadband("Cooling", setpoint);
499
+ if (!this.#isCommandContext(context)) {
500
+ this.#ensureSetpointDeadband("Cooling", "unoccupied", setpoint);
501
+ if (this.features.presets && this.state.activePresetHandle !== null && !this.occupied) {
502
+ this.agent.asLocalActor(() => {
503
+ this.state.activePresetHandle = null;
504
+ });
505
+ }
506
+ }
507
+ }
508
+ #assertUnoccupiedHeatingSetpointChanging(setpoint, _old, context) {
509
+ this.#assertSetpointWithinLimits("Heat", "Unoccupied", setpoint);
510
+ this.#assertSetpointDeadband("Heating", setpoint);
511
+ if (!this.#isCommandContext(context)) {
512
+ this.#ensureSetpointDeadband("Heating", "unoccupied", setpoint);
513
+ if (this.features.presets && this.state.activePresetHandle !== null && !this.occupied) {
514
+ this.agent.asLocalActor(() => {
515
+ this.state.activePresetHandle = null;
516
+ });
517
+ }
518
+ }
519
+ }
520
+ #assertAbsMaxCoolSetpointLimitChanging(absMax) {
521
+ this.#assertUserSetpointLimits("CoolSetpointLimit", { absMax });
522
+ }
523
+ #assertMaxCoolSetpointLimitChanging(max) {
524
+ this.#assertUserSetpointLimits("CoolSetpointLimit", { max });
525
+ if (this.features.autoMode) {
526
+ if (max < this.heatSetpointMaximum + this.setpointDeadBand) {
527
+ throw new import_types.StatusResponse.ConstraintErrorError(
528
+ `maxCoolSetpointLimit (${max}) must be greater than or equal to maxHeatSetpointLimit (${this.heatSetpointMaximum}) plus minSetpointDeadBand (${this.setpointDeadBand})`
529
+ );
530
+ }
531
+ }
532
+ }
533
+ #assertMinCoolSetpointLimitChanging(min) {
534
+ this.#assertUserSetpointLimits("CoolSetpointLimit", { min });
535
+ if (this.features.autoMode) {
536
+ if (min < this.heatSetpointMinimum + this.setpointDeadBand) {
537
+ throw new import_types.StatusResponse.ConstraintErrorError(
538
+ `minCoolSetpointLimit (${min}) must be greater than or equal to minHeatSetpointLimit (${this.heatSetpointMinimum}) plus minSetpointDeadBand (${this.setpointDeadBand})`
539
+ );
540
+ }
541
+ }
542
+ }
543
+ #assertAbsMinCoolSetpointLimitChanging(absMin) {
544
+ this.#assertUserSetpointLimits("CoolSetpointLimit", { absMin });
545
+ }
546
+ #assertAbsMaxHeatSetpointLimitChanging(absMax) {
547
+ this.#assertUserSetpointLimits("HeatSetpointLimit", { absMax });
548
+ }
549
+ #assertMaxHeatSetpointLimitChanging(max) {
550
+ this.#assertUserSetpointLimits("HeatSetpointLimit", { max });
551
+ if (this.features.autoMode) {
552
+ if (max > this.coolSetpointMaximum - this.setpointDeadBand) {
553
+ throw new import_types.StatusResponse.ConstraintErrorError(
554
+ `maxHeatSetpointLimit (${max}) must be less than or equal to maxCoolSetpointLimit (${this.coolSetpointMaximum}) minus minSetpointDeadBand (${this.setpointDeadBand})`
555
+ );
556
+ }
557
+ }
558
+ }
559
+ #assertMinHeatSetpointLimitChanging(min) {
560
+ this.#assertUserSetpointLimits("HeatSetpointLimit", { min });
561
+ if (this.features.autoMode) {
562
+ if (min > this.coolSetpointMinimum - this.setpointDeadBand) {
563
+ throw new import_types.StatusResponse.ConstraintErrorError(
564
+ `minHeatSetpointLimit (${min}) must be less than or equal to minCoolSetpointLimit (${this.state.minCoolSetpointLimit}) minus minSetpointDeadBand (${this.setpointDeadBand})`
565
+ );
566
+ }
567
+ }
568
+ }
569
+ #assertAbsMinHeatSetpointLimitChanging(absMin) {
570
+ this.#assertUserSetpointLimits("HeatSetpointLimit", { absMin });
571
+ }
572
+ #assertOccupiedCoolingSetpointChanging(setpoint, _old, context) {
573
+ this.#assertSetpointWithinLimits("Cool", "Occupied", setpoint);
574
+ this.#assertSetpointDeadband("Cooling", setpoint);
575
+ if (!this.#isCommandContext(context)) {
576
+ this.#ensureSetpointDeadband("Cooling", "occupied", setpoint);
577
+ if (this.features.presets && this.state.activePresetHandle !== null && this.occupied) {
578
+ this.agent.asLocalActor(() => {
579
+ this.state.activePresetHandle = null;
580
+ });
581
+ }
582
+ }
583
+ }
584
+ #assertOccupiedHeatingSetpointChanging(setpoint, _old, context) {
585
+ this.#assertSetpointWithinLimits("Heat", "Occupied", setpoint);
586
+ this.#assertSetpointDeadband("Heating", setpoint);
587
+ if (!this.#isCommandContext(context)) {
588
+ this.#ensureSetpointDeadband("Heating", "occupied", setpoint);
589
+ if (this.features.presets && this.state.activePresetHandle !== null && this.occupied) {
590
+ this.agent.asLocalActor(() => {
591
+ this.state.activePresetHandle = null;
592
+ });
593
+ }
594
+ }
595
+ }
596
+ /**
597
+ * The current mode the thermostat is considered to be in based on local temperature and setpoints
598
+ */
599
+ get temperatureConsideration() {
600
+ const localTemp = this.internal.localTemperature;
601
+ if (localTemp === null) {
602
+ return void 0;
603
+ }
604
+ const minSetPointDeadband = this.setpointDeadBand;
605
+ const heatingSetpoint = this.heatingSetpoint;
606
+ const coolingSetpoint = this.coolingSetpoint;
607
+ switch (this.state.systemMode) {
608
+ case import_thermostat.Thermostat.SystemMode.Heat:
609
+ if (localTemp < heatingSetpoint) {
610
+ return "belowTarget";
611
+ }
612
+ if (localTemp > coolingSetpoint) {
613
+ return "onTarget";
614
+ }
615
+ break;
616
+ case import_thermostat.Thermostat.SystemMode.Cool:
617
+ if (localTemp < heatingSetpoint) {
618
+ return "onTarget";
619
+ }
620
+ if (localTemp > coolingSetpoint) {
621
+ return "aboveTarget";
622
+ }
623
+ break;
624
+ case import_thermostat.Thermostat.SystemMode.Auto:
625
+ if (localTemp < heatingSetpoint - minSetPointDeadband) {
626
+ return "belowTarget";
627
+ }
628
+ if (localTemp > coolingSetpoint + minSetPointDeadband) {
629
+ return "aboveTarget";
630
+ }
631
+ break;
632
+ }
633
+ return "onTarget";
634
+ }
635
+ get #heatDefaults() {
636
+ return {
637
+ absMin: 700,
638
+ absMax: 3e3
639
+ };
640
+ }
641
+ get #coolDefaults() {
642
+ return {
643
+ absMin: 1600,
644
+ absMax: 3200
645
+ };
646
+ }
647
+ /**
648
+ * Used to validate generically that user configurable limits must be within device limits follow:
649
+ * * AbsMinHeatSetpointLimit <= MinHeatSetpointLimit <= MaxHeatSetpointLimit <= AbsMaxHeatSetpointLimit
650
+ * * AbsMinCoolSetpointLimit <= MinCoolSetpointLimit <= MaxCoolSetpointLimit <= AbsMaxCoolSetpointLimit
651
+ * Values not provided are taken from the state
652
+ */
653
+ #assertUserSetpointLimits(scope, details = {}) {
654
+ const defaults = scope === "HeatSetpointLimit" ? this.#heatDefaults : this.#coolDefaults;
655
+ const {
656
+ absMin = this.state[`absMin${scope}`] ?? defaults.absMin,
657
+ min = this.state[`min${scope}`] ?? defaults.absMin,
658
+ max = this.state[`max${scope}`] ?? defaults.absMax,
659
+ absMax = this.state[`absMax${scope}`] ?? defaults.absMax
660
+ } = details;
661
+ logger.debug(
662
+ `Validating user setpoint limits for ${scope}: absMin=${absMin}, min=${min}, max=${max}, absMax=${absMax}`
663
+ );
664
+ if (absMin > min) {
665
+ throw new import_types.StatusResponse.ConstraintErrorError(
666
+ `absMin${scope} (${absMin}) must be less than or equal to min${scope} (${min})`
667
+ );
668
+ }
669
+ if (min > max) {
670
+ throw new import_types.StatusResponse.ConstraintErrorError(
671
+ `min${scope} (${min}) must be less than or equal to max${scope} (${max})`
672
+ );
673
+ }
674
+ if (max > absMax) {
675
+ throw new import_types.StatusResponse.ConstraintErrorError(
676
+ `max${scope} (${max}) must be less than or equal to absMax${scope} (${absMax})`
677
+ );
678
+ }
679
+ }
680
+ get heatSetpointMinimum() {
681
+ const absMin = this.state.absMinHeatSetpointLimit ?? this.#heatDefaults.absMin;
682
+ const min = this.state.minHeatSetpointLimit ?? this.#heatDefaults.absMin;
683
+ return Math.max(min, absMin);
684
+ }
685
+ get heatSetpointMaximum() {
686
+ const absMax = this.state.absMaxHeatSetpointLimit ?? this.#heatDefaults.absMax;
687
+ const max = this.state.maxHeatSetpointLimit ?? this.#heatDefaults.absMax;
688
+ return Math.min(max, absMax);
689
+ }
690
+ get coolSetpointMinimum() {
691
+ const absMin = this.state.absMinCoolSetpointLimit ?? this.#coolDefaults.absMin;
692
+ const min = this.state.minCoolSetpointLimit ?? this.#coolDefaults.absMin;
693
+ return Math.max(min, absMin);
694
+ }
695
+ get coolSetpointMaximum() {
696
+ const absMax = this.state.absMaxCoolSetpointLimit ?? this.#coolDefaults.absMax;
697
+ const max = this.state.maxCoolSetpointLimit ?? this.#coolDefaults.absMax;
698
+ return Math.min(max, absMax);
699
+ }
700
+ get setpointDeadBand() {
701
+ return this.features.autoMode ? this.internal.minSetpointDeadBand * 10 : 0;
702
+ }
703
+ #clampSetpointToLimits(scope, setpoint) {
704
+ const limitMin = scope === "Heat" ? this.heatSetpointMinimum : this.coolSetpointMinimum;
705
+ const limitMax = scope === "Heat" ? this.heatSetpointMaximum : this.coolSetpointMaximum;
706
+ const result = (0, import_general.cropValueRange)(setpoint, limitMin, limitMax);
707
+ if (result !== setpoint) {
708
+ logger.debug(
709
+ `${scope} setpoint (${setpoint}) is out of limits [${limitMin}, ${limitMax}], clamping to ${result}`
710
+ );
711
+ }
712
+ return result;
713
+ }
714
+ /**
715
+ * Used to validate that Setpoints must be within user configurable limits
716
+ */
717
+ #assertSetpointWithinLimits(scope, type, setpoint) {
718
+ const limitMin = scope === "Heat" ? this.heatSetpointMinimum : this.coolSetpointMinimum;
719
+ const limitMax = scope === "Heat" ? this.heatSetpointMaximum : this.coolSetpointMaximum;
720
+ if (limitMin !== void 0 && setpoint < limitMin) {
721
+ throw new import_types.StatusResponse.ConstraintErrorError(
722
+ `${scope}${type}Setpoint (${setpoint}) must be greater than or equal to min${scope}SetpointLimit (${limitMin})`
723
+ );
724
+ }
725
+ if (limitMax !== void 0 && setpoint > limitMax) {
726
+ throw new import_types.StatusResponse.ConstraintErrorError(
727
+ `${scope}${type}Setpoint (${setpoint}) must be less than or equal to max${scope}SetpointLimit (${limitMax})`
728
+ );
729
+ }
730
+ }
731
+ /**
732
+ * Attempts to ensure that a change to the heating/cooling setpoint maintains the deadband with the cooling/heating
733
+ * setpoint by adjusting the cooling setpoint
734
+ */
735
+ #ensureSetpointDeadband(scope, type, value) {
736
+ if (!this.features.autoMode) {
737
+ return;
738
+ }
739
+ const otherType = scope === "Heating" ? "Cooling" : "Heating";
740
+ const deadband = this.setpointDeadBand;
741
+ const otherSetpoint = otherType === "Heating" ? this.heatingSetpoint : this.coolingSetpoint;
742
+ const otherLimit = otherType === "Heating" ? this.heatSetpointMinimum : this.coolSetpointMaximum;
743
+ if (otherType === "Cooling") {
744
+ const minValidSetpoint = value + deadband;
745
+ logger.debug(
746
+ `Ensuring deadband for ${type}${otherType}Setpoint, min valid setpoint is ${minValidSetpoint}`
747
+ );
748
+ if (otherSetpoint >= minValidSetpoint) {
749
+ return;
750
+ }
751
+ if (minValidSetpoint > otherLimit) {
752
+ throw new import_types.StatusResponse.ConstraintErrorError(
753
+ `Cannot adjust cooling setpoint to maintain deadband, would exceed max cooling setpoint (${otherLimit})`
754
+ );
755
+ }
756
+ logger.debug(`Adjusting ${type}${otherType}Setpoint to ${minValidSetpoint} to maintain deadband`);
757
+ this.state[`${type}${otherType}Setpoint`] = minValidSetpoint;
758
+ } else {
759
+ const maxValidSetpoint = value - deadband;
760
+ logger.debug(
761
+ `Ensuring deadband for ${type}${otherType}Setpoint, max valid setpoint is ${maxValidSetpoint}`
762
+ );
763
+ if (otherSetpoint <= maxValidSetpoint) {
764
+ return;
765
+ }
766
+ if (maxValidSetpoint < otherLimit) {
767
+ throw new import_types.StatusResponse.ConstraintErrorError(
768
+ `Cannot adjust heating setpoint to maintain deadband, would exceed min heating setpoint (${otherLimit})`
769
+ );
770
+ }
771
+ logger.debug(`Adjusting ${type}${otherType}Setpoint to ${maxValidSetpoint} to maintain deadband`);
772
+ this.state[`${type}${otherType}Setpoint`] = maxValidSetpoint;
773
+ }
774
+ }
775
+ /**
776
+ * Checks to see if it's possible to adjust the heating/cooling setpoint to preserve a given deadband if the
777
+ * cooling/heating setpoint is changed
778
+ */
779
+ #assertSetpointDeadband(type, value) {
780
+ if (!this.features.autoMode) {
781
+ return;
782
+ }
783
+ const deadband = this.setpointDeadBand;
784
+ const otherValue = type === "Heating" ? this.coolSetpointMaximum : this.heatSetpointMinimum;
785
+ if (type === "Heating" && value + deadband > otherValue) {
786
+ throw new import_types.StatusResponse.ConstraintErrorError(
787
+ `HeatingSetpoint (${value}) plus deadband (${deadband}) exceeds CoolingSetpoint (${otherValue})`
788
+ );
789
+ } else if (type === "Cooling" && value - deadband < otherValue) {
790
+ throw new import_types.StatusResponse.ConstraintErrorError(
791
+ `CoolingSetpoint (${value}) minus deadband (${deadband}) is less than HeatingSetpoint (${otherValue})`
792
+ );
793
+ }
794
+ }
795
+ #setupModeHandling() {
796
+ this.reactTo(this.events.systemMode$Changed, this.#handleSystemModeChange);
797
+ this.maybeReactTo(this.events.thermostatRunningMode$Changed, this.#handleThermostatRunningModeChange);
798
+ if (this.state.useAutomaticModeManagement && this.state.thermostatRunningMode !== void 0) {
799
+ this.reactTo(this.events.calibratedTemperature$Changed, this.#handleTemperatureChangeForMode);
800
+ this.#handleTemperatureChangeForMode(this.internal.localTemperature);
801
+ }
802
+ }
803
+ #handleSystemModeChange(newMode) {
804
+ if (this.state.thermostatRunningMode !== void 0 && newMode !== import_thermostat.Thermostat.SystemMode.Auto) {
805
+ if (newMode === import_thermostat.Thermostat.SystemMode.Off) {
806
+ this.state.thermostatRunningMode = import_thermostat.Thermostat.ThermostatRunningMode.Off;
807
+ } else if (newMode === import_thermostat.Thermostat.SystemMode.Heat) {
808
+ this.state.thermostatRunningMode = import_thermostat.Thermostat.ThermostatRunningMode.Heat;
809
+ } else if (newMode === import_thermostat.Thermostat.SystemMode.Cool) {
810
+ this.state.thermostatRunningMode = import_thermostat.Thermostat.ThermostatRunningMode.Cool;
811
+ }
812
+ }
813
+ }
814
+ #handleThermostatRunningModeChange(newRunningMode) {
815
+ if (this.state.piCoolingDemand !== void 0) {
816
+ if (newRunningMode === import_thermostat.Thermostat.ThermostatRunningMode.Off || newRunningMode === import_thermostat.Thermostat.ThermostatRunningMode.Heat) {
817
+ this.state.piCoolingDemand = 0;
818
+ }
819
+ }
820
+ if (this.state.piHeatingDemand !== void 0) {
821
+ if (newRunningMode === import_thermostat.Thermostat.ThermostatRunningMode.Off || newRunningMode === import_thermostat.Thermostat.ThermostatRunningMode.Cool) {
822
+ this.state.piHeatingDemand = 0;
823
+ }
824
+ }
825
+ }
826
+ /**
827
+ * Handles temperature changes to automatically adjust the system mode based on the current temperature
828
+ * consideration. This logic is disabled by default and will be enabled by setting useAutomaticModeManagement to
829
+ * true.
830
+ */
831
+ #handleTemperatureChangeForMode(temperature) {
832
+ if (temperature == null) {
833
+ return;
834
+ }
835
+ const consideration = this.temperatureConsideration;
836
+ switch (this.state.systemMode) {
837
+ case import_thermostat.Thermostat.SystemMode.Heat:
838
+ switch (consideration) {
839
+ case "belowTarget":
840
+ this.adjustRunningMode(import_thermostat.Thermostat.ThermostatRunningMode.Heat);
841
+ break;
842
+ default:
843
+ this.adjustRunningMode(import_thermostat.Thermostat.ThermostatRunningMode.Off);
844
+ break;
845
+ }
846
+ break;
847
+ case import_thermostat.Thermostat.SystemMode.Cool:
848
+ switch (consideration) {
849
+ case "aboveTarget":
850
+ this.adjustRunningMode(import_thermostat.Thermostat.ThermostatRunningMode.Cool);
851
+ break;
852
+ default:
853
+ this.adjustRunningMode(import_thermostat.Thermostat.ThermostatRunningMode.Off);
854
+ break;
855
+ }
856
+ break;
857
+ case import_thermostat.Thermostat.SystemMode.Auto:
858
+ switch (consideration) {
859
+ case "belowTarget":
860
+ this.adjustRunningMode(import_thermostat.Thermostat.ThermostatRunningMode.Heat);
861
+ break;
862
+ case "aboveTarget":
863
+ this.adjustRunningMode(import_thermostat.Thermostat.ThermostatRunningMode.Cool);
864
+ break;
865
+ default:
866
+ this.adjustRunningMode(import_thermostat.Thermostat.ThermostatRunningMode.Off);
867
+ break;
868
+ }
869
+ break;
870
+ }
871
+ }
872
+ #setupPresets() {
873
+ if (!this.features.presets) {
874
+ return;
875
+ }
876
+ this.reactTo(this.events.presets$AtomicChanging, this.#handlePresetsChanging);
877
+ this.reactTo(this.events.presets$AtomicChanged, this.#handlePresetsChanged);
878
+ this.reactTo(this.events.persistedPresets$Changing, this.#handlePresetsChanging);
879
+ this.reactTo(this.events.persistedPresets$Changed, this.#handlePersistedPresetsChanged);
880
+ this.reactTo(this.events.updatePresets, this.#updatePresets, { lock: true });
881
+ }
882
+ /** Handles changes to the Presets attribute and ensures persistedPresets are updated accordingly */
883
+ #updatePresets(newPresets) {
884
+ this.state.persistedPresets = newPresets;
885
+ }
886
+ /**
887
+ * Handles "In-flight" validation of newly written Presets via atomic-write and does the required validations.
888
+ */
889
+ #handlePresetsChanging(newPresets, oldPresets) {
890
+ if (newPresets.length > this.state.numberOfPresets) {
891
+ throw new import_types.StatusResponse.ResourceExhaustedError(
892
+ `Number of presets (${newPresets.length}) exceeds NumberOfPresets (${this.state.numberOfPresets})`
893
+ );
894
+ }
895
+ const oldPresetsMap = /* @__PURE__ */ new Map();
896
+ if (oldPresets !== void 0) {
897
+ for (const preset of oldPresets) {
898
+ if (preset.presetHandle !== null) {
899
+ const presetHex = import_general.Bytes.toHex(preset.presetHandle);
900
+ oldPresetsMap.set(presetHex, preset);
901
+ }
902
+ }
903
+ }
904
+ const persistedPresetsMap = /* @__PURE__ */ new Map();
905
+ if (this.state.persistedPresets !== void 0) {
906
+ for (const preset of this.state.persistedPresets) {
907
+ if (preset.presetHandle === null) {
908
+ throw new import_general.InternalError("Persisted preset is missing presetHandle, this should not happen");
909
+ }
910
+ const presetHex = import_general.Bytes.toHex(preset.presetHandle);
911
+ persistedPresetsMap.set(presetHex, preset);
912
+ }
913
+ }
914
+ const presetTypeMap = /* @__PURE__ */ new Map();
915
+ for (const type of this.state.presetTypes) {
916
+ presetTypeMap.set(type.presetScenario, type);
917
+ }
918
+ const presetScenarioNames = /* @__PURE__ */ new Map();
919
+ const presetScenarioCounts = /* @__PURE__ */ new Map();
920
+ const newPresetsSet = /* @__PURE__ */ new Set();
921
+ const newBuildInPresets = /* @__PURE__ */ new Set();
922
+ for (const preset of newPresets) {
923
+ if (preset.presetHandle !== null) {
924
+ const presetHex = import_general.Bytes.toHex(preset.presetHandle);
925
+ if (newPresetsSet.has(presetHex)) {
926
+ throw new import_types.StatusResponse.ConstraintErrorError(`Duplicate presetHandle ${presetHex} in new Presets`);
927
+ }
928
+ if (this.state.persistedPresets !== void 0) {
929
+ const persistedPreset = persistedPresetsMap.get(presetHex);
930
+ if (persistedPreset === void 0) {
931
+ throw new import_types.StatusResponse.NotFoundError(
932
+ `Preset with presetHandle ${presetHex} does not exist in old Presets, cannot add new Presets with non-null presetHandle`
933
+ );
934
+ }
935
+ if (preset.builtIn !== null && persistedPreset.builtIn !== preset.builtIn) {
936
+ throw new import_types.StatusResponse.ConstraintErrorError(
937
+ `Cannot change built-in status of preset with presetHandle ${presetHex}`
938
+ );
939
+ }
940
+ }
941
+ newPresetsSet.add(presetHex);
942
+ } else if (preset.builtIn) {
943
+ throw new import_types.StatusResponse.ConstraintErrorError(`Can not add a new built-in preset`);
944
+ }
945
+ const presetType = presetTypeMap.get(preset.presetScenario);
946
+ if (presetType === void 0) {
947
+ throw new import_types.StatusResponse.ConstraintErrorError(
948
+ `No PresetType defined for scenario ${import_thermostat.Thermostat.PresetScenario[preset.presetScenario]}`
949
+ );
950
+ }
951
+ if (preset.name !== void 0) {
952
+ const scenarioNames = presetScenarioNames.get(preset.presetScenario) ?? [];
953
+ if (scenarioNames.includes(preset.name)) {
954
+ throw new import_types.StatusResponse.ConstraintErrorError(
955
+ `Duplicate preset name "${preset.name}" for scenario ${import_thermostat.Thermostat.PresetScenario[preset.presetScenario]}`
956
+ );
957
+ }
958
+ if (!presetType.presetTypeFeatures.supportsNames) {
959
+ throw new import_types.StatusResponse.ConstraintErrorError(
960
+ `Preset names are not supported for scenario ${import_thermostat.Thermostat.PresetScenario[preset.presetScenario]}`
961
+ );
962
+ }
963
+ scenarioNames.push(preset.name);
964
+ presetScenarioNames.set(preset.presetScenario, scenarioNames);
965
+ }
966
+ const count = presetScenarioCounts.get(preset.presetScenario) ?? 0;
967
+ if (count === presetType.numberOfPresets) {
968
+ throw new import_types.StatusResponse.ResourceExhaustedError(
969
+ `Number of presets (${count}) for scenario ${import_thermostat.Thermostat.PresetScenario[preset.presetScenario]} exceeds allowed number (${presetType.numberOfPresets})`
970
+ );
971
+ }
972
+ presetScenarioCounts.set(preset.presetScenario, count + 1);
973
+ if (this.features.cooling) {
974
+ if (preset.coolingSetpoint === void 0) {
975
+ throw new import_types.StatusResponse.ConstraintErrorError(
976
+ `Preset for scenario ${import_thermostat.Thermostat.PresetScenario[preset.presetScenario]} is missing required coolingSetpoint`
977
+ );
978
+ }
979
+ if (preset.coolingSetpoint < this.coolSetpointMinimum || preset.coolingSetpoint > this.coolSetpointMaximum) {
980
+ throw new import_types.StatusResponse.ConstraintErrorError(
981
+ `Preset coolingSetpoint (${preset.coolingSetpoint}) for scenario ${import_thermostat.Thermostat.PresetScenario[preset.presetScenario]} is out of bounds [${this.coolSetpointMinimum}, ${this.coolSetpointMaximum}]`
982
+ );
983
+ }
984
+ }
985
+ if (this.features.heating) {
986
+ if (preset.heatingSetpoint === void 0) {
987
+ throw new import_types.StatusResponse.ConstraintErrorError(
988
+ `Preset for scenario ${import_thermostat.Thermostat.PresetScenario[preset.presetScenario]} is missing required heatingSetpoint`
989
+ );
990
+ }
991
+ if (preset.heatingSetpoint < this.heatSetpointMinimum || preset.heatingSetpoint > this.heatSetpointMaximum) {
992
+ throw new import_types.StatusResponse.ConstraintErrorError(
993
+ `Preset heatingSetpoint (${preset.heatingSetpoint}) for scenario ${import_thermostat.Thermostat.PresetScenario[preset.presetScenario]} is out of bounds [${this.heatSetpointMinimum}, ${this.heatSetpointMaximum}]`
994
+ );
995
+ }
996
+ }
997
+ if (preset.builtIn && preset.presetHandle !== null) {
998
+ newBuildInPresets.add(import_general.Bytes.toHex(preset.presetHandle));
999
+ }
1000
+ }
1001
+ }
1002
+ /**
1003
+ * Handles additional validation of preset changes when all chunks were written in an atomic write operation.
1004
+ */
1005
+ #handlePresetsChanged(newPresets, oldPresets) {
1006
+ this.#handlePersistedPresetsChanged(newPresets, oldPresets);
1007
+ const oldPresetsMap = /* @__PURE__ */ new Map();
1008
+ const oldBuildInPresets = /* @__PURE__ */ new Set();
1009
+ if (oldPresets !== void 0) {
1010
+ for (const preset of oldPresets) {
1011
+ if (preset.presetHandle === null) {
1012
+ throw new import_general.InternalError("Old preset is missing presetHandle, this must not happen");
1013
+ }
1014
+ const presetHex = import_general.Bytes.toHex(preset.presetHandle);
1015
+ oldPresetsMap.set(presetHex, preset);
1016
+ if (preset.builtIn) {
1017
+ oldBuildInPresets.add(presetHex);
1018
+ }
1019
+ }
1020
+ }
1021
+ for (const preset of newPresets) {
1022
+ if (preset.presetHandle === null) {
1023
+ if (preset.builtIn) {
1024
+ throw new import_types.StatusResponse.ConstraintErrorError(
1025
+ `Preset for scenario ${import_thermostat.Thermostat.PresetScenario[preset.presetScenario]} is built-in and must have a non-null presetHandle`
1026
+ );
1027
+ }
1028
+ }
1029
+ }
1030
+ }
1031
+ /**
1032
+ * Handles additional validation and required value adjustments of persistedPresets changes when all chunks were
1033
+ * written in an atomic write.
1034
+ */
1035
+ #handlePersistedPresetsChanged(newPresets, oldPresets) {
1036
+ if (oldPresets === void 0) {
1037
+ logger.debug(
1038
+ "Old presets is undefined, skipping some checks. This should only happen on setup of the behavior."
1039
+ );
1040
+ }
1041
+ const entropy = this.endpoint.env.get(import_general.Entropy);
1042
+ let changed = false;
1043
+ const newPresetHandles = /* @__PURE__ */ new Set();
1044
+ for (const preset of newPresets) {
1045
+ if (preset.presetHandle === null) {
1046
+ logger.error("Preset is missing presetHandle, generating a new one");
1047
+ preset.presetHandle = entropy.randomBytes(16);
1048
+ changed = true;
1049
+ }
1050
+ newPresetHandles.add(import_general.Bytes.toHex(preset.presetHandle));
1051
+ if (oldPresets === void 0) {
1052
+ if (preset.builtIn === null) {
1053
+ preset.builtIn = false;
1054
+ changed = true;
1055
+ }
1056
+ } else {
1057
+ if (preset.builtIn === null) {
1058
+ const oldPreset = oldPresets.find(
1059
+ (p) => p.presetHandle && preset.presetHandle && import_general.Bytes.areEqual(p.presetHandle, preset.presetHandle)
1060
+ );
1061
+ if (oldPreset !== void 0) {
1062
+ preset.builtIn = oldPreset.builtIn;
1063
+ } else {
1064
+ preset.builtIn = false;
1065
+ }
1066
+ changed = true;
1067
+ }
1068
+ }
1069
+ }
1070
+ const newBuildInPresets = /* @__PURE__ */ new Set();
1071
+ for (const preset of newPresets) {
1072
+ if (preset.builtIn) {
1073
+ newBuildInPresets.add(import_general.Bytes.toHex(preset.presetHandle));
1074
+ }
1075
+ }
1076
+ const oldBuildInPresets = /* @__PURE__ */ new Set();
1077
+ if (oldPresets !== void 0) {
1078
+ for (const preset of oldPresets) {
1079
+ if (preset.builtIn) {
1080
+ oldBuildInPresets.add(import_general.Bytes.toHex(preset.presetHandle));
1081
+ }
1082
+ }
1083
+ }
1084
+ for (const oldBuiltInPreset of oldBuildInPresets) {
1085
+ if (!newBuildInPresets.has(oldBuiltInPreset)) {
1086
+ throw new import_types.StatusResponse.ConstraintErrorError(
1087
+ `Cannot remove built-in preset with presetHandle ${oldBuiltInPreset}`
1088
+ );
1089
+ }
1090
+ }
1091
+ if (this.state.activePresetHandle !== null && !newPresetHandles.has(import_general.Bytes.toHex(this.state.activePresetHandle))) {
1092
+ throw new import_types.StatusResponse.InvalidInStateError(`ActivePresetHandle references non-existing presetHandle`);
1093
+ }
1094
+ if (changed) {
1095
+ logger.error("PresetHandles or BuiltIn flags were updated, updating persistedPresets");
1096
+ this.state.persistedPresets = (0, import_general.deepCopy)(newPresets);
1097
+ }
1098
+ }
1099
+ async [Symbol.asyncDispose]() {
1100
+ this.endpoint.env.close(import_AtomicWriteHandler.AtomicWriteHandler);
1101
+ }
1102
+ /** Implementation of the atomic request handling */
1103
+ async atomicRequest(request) {
1104
+ const atomicWriteHandler = this.endpoint.env.get(import_AtomicWriteHandler.AtomicWriteHandler);
1105
+ const { requestType } = request;
1106
+ switch (requestType) {
1107
+ case import_thermostat.Thermostat.RequestType.BeginWrite:
1108
+ return atomicWriteHandler.beginWrite(request, this.context, this.endpoint, this.type);
1109
+ case import_thermostat.Thermostat.RequestType.CommitWrite:
1110
+ return await atomicWriteHandler.commitWrite(
1111
+ request,
1112
+ this.context,
1113
+ this.endpoint,
1114
+ this.type,
1115
+ this.state
1116
+ );
1117
+ case import_thermostat.Thermostat.RequestType.RollbackWrite:
1118
+ return atomicWriteHandler.rollbackWrite(request, this.context, this.endpoint, this.type);
1119
+ }
1120
+ }
1121
+ }
1122
+ ((ThermostatBaseServer2) => {
1123
+ class State extends ThermostatBehaviorLogicBase.State {
1124
+ /**
1125
+ * Otherwise measured temperature in Matter format as uint16 with a factor of 100. A calibration offset is applied
1126
+ * additionally from localTemperatureCalibration if set.
1127
+ * Use this if you have an external temperature sensor that should be used for thermostat control instead of a
1128
+ * local temperature measurement cluster.
1129
+ */
1130
+ externalMeasuredIndoorTemperature;
1131
+ /**
1132
+ * Otherwise measured occupancy as boolean.
1133
+ * Use this if you have an external occupancy sensor that should be used for thermostat control instead of a
1134
+ * internal occupancy sensing cluster.
1135
+ */
1136
+ externallyMeasuredOccupancy;
1137
+ /**
1138
+ * Use to enable the automatic mode management, implemented by this standard implementation. This is beyond
1139
+ * Matter specification! It reacts to temperature changes to adjust system running mode automatically. It also
1140
+ * requires the Auto feature to be enabled and the ThermostatRunningMode attribute to be present.
1141
+ */
1142
+ useAutomaticModeManagement = false;
1143
+ /**
1144
+ * Persisted presets stored in the device, needed because the original "presets" is a virtual property
1145
+ */
1146
+ persistedPresets;
1147
+ /**
1148
+ * Implementation of the needed Preset attribute logic for Atomic Write handling.
1149
+ */
1150
+ [import_protocol.Val.properties](endpoint, session) {
1151
+ const properties = {};
1152
+ if (endpoint.behaviors.optionsFor(ThermostatBaseServer2)?.presets !== void 0 || endpoint.behaviors.defaultsFor(ThermostatBaseServer2)?.presets !== void 0) {
1153
+ Object.defineProperty(properties, "presets", {
1154
+ /**
1155
+ * Getter will return a pending atomic write state when there is one, otherwise the stored value or
1156
+ * the default value.
1157
+ */
1158
+ get() {
1159
+ const pendingValue = endpoint.env.get(import_AtomicWriteHandler.AtomicWriteHandler).pendingValueForAttributeAndPeer(
1160
+ session,
1161
+ endpoint,
1162
+ ThermostatBaseServer2,
1163
+ import_thermostat.Thermostat.Complete.attributes.presets.id
1164
+ );
1165
+ if (pendingValue !== void 0) {
1166
+ return pendingValue;
1167
+ }
1168
+ let value = endpoint.stateOf(ThermostatBaseServer2.id).persistedPresets;
1169
+ if (value === void 0) {
1170
+ value = endpoint.behaviors.optionsFor(ThermostatBaseServer2)?.presets;
1171
+ }
1172
+ return value ?? [];
1173
+ },
1174
+ /**
1175
+ * Setter will either emit an update event directly when in local actor context or command context,
1176
+ * otherwise it will go through the AtomicWriteHandler to ensure proper atomic write handling.
1177
+ */
1178
+ set(value) {
1179
+ if ((0, import_protocol.hasLocalActor)(session) || "command" in session && session.command) {
1180
+ endpoint.eventsOf(ThermostatBaseServer2.id).updatePresets.emit(value);
1181
+ } else {
1182
+ endpoint.env.get(import_AtomicWriteHandler.AtomicWriteHandler).writeAttribute(
1183
+ session,
1184
+ endpoint,
1185
+ ThermostatBaseServer2,
1186
+ import_thermostat.Thermostat.Complete.attributes.presets.id,
1187
+ value
1188
+ );
1189
+ }
1190
+ }
1191
+ });
1192
+ }
1193
+ return properties;
1194
+ }
1195
+ }
1196
+ ThermostatBaseServer2.State = State;
1197
+ class Events extends ThermostatBehaviorLogicBase.Events {
1198
+ externalMeasuredIndoorTemperature$Changed = (0, import_general.Observable)();
1199
+ externallyMeasuredOccupancy$Changed = (0, import_general.Observable)();
1200
+ persistedPresets$Changed = (0, import_general.Observable)();
1201
+ persistedPresets$Changing = (0, import_general.Observable)();
1202
+ /**
1203
+ * Custom event emitted when the calibrated temperature changes.
1204
+ */
1205
+ calibratedTemperature$Changed = (0, import_general.Observable)();
1206
+ /**
1207
+ * Custom event emitted when the Presets attribute is "virtually" changing as part of an atomic write operation.
1208
+ * Info: The events is currently needed to be a pure Observable to get errors thrown in the event handler be
1209
+ * reported back to the emitter.
1210
+ */
1211
+ presets$AtomicChanging = (0, import_general.Observable)();
1212
+ /**
1213
+ * Custom event emitted when the Presets attribute has "virtually" changed as part of an atomic write operation.
1214
+ * Info: The events is currently needed to be a pure Observable to get errors thrown in the event handler be
1215
+ * reported back to the emitter.
1216
+ */
1217
+ presets$AtomicChanged = (0, import_general.Observable)();
1218
+ /**
1219
+ * Custom event emitted to inform the behavior implementation of an update of the PersistedPresets attribute.
1220
+ */
1221
+ updatePresets = (0, import_general.Observable)();
1222
+ }
1223
+ ThermostatBaseServer2.Events = Events;
1224
+ class Internal {
1225
+ /**
1226
+ * Local temperature in Matter format as uint16 with a factor of 100. It is the same value as the one reported
1227
+ * in the localTemperature Attribute, but also present when the LocalTemperatureNotExposed feature is enabled.
1228
+ * Means all logic and calculations are always done with this value.
1229
+ * The value will be updated on initialization and when the localTemperature Attribute changes.
1230
+ */
1231
+ localTemperature = null;
1232
+ /**
1233
+ * Storing fixed value internally to ensure it can not be modified.
1234
+ * This value will be initialized when the behavior is initialized and is static afterward.
1235
+ */
1236
+ minSetpointDeadBand = 0;
1237
+ /**
1238
+ * Storing fixed value internally to ensure it can not be modified.
1239
+ * This value will be initialized when the behavior is initialized and is static afterward.
1240
+ */
1241
+ controlSequenceOfOperation;
1242
+ }
1243
+ ThermostatBaseServer2.Internal = Internal;
1244
+ })(ThermostatBaseServer || (ThermostatBaseServer = {}));
1245
+ class ThermostatServer extends ThermostatBaseServer.for((0, import_types.ClusterType)(import_thermostat.Thermostat.Base)) {
31
1246
  }
32
1247
  //# sourceMappingURL=ThermostatServer.js.map