iobroker.loxone 3.0.1 → 4.0.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.
Files changed (185) hide show
  1. package/LICENSE +183 -183
  2. package/README.md +417 -411
  3. package/admin/i18n/de.json +15 -0
  4. package/admin/i18n/en.json +15 -0
  5. package/admin/i18n/es.json +18 -0
  6. package/admin/i18n/fr.json +18 -0
  7. package/admin/i18n/it.json +18 -0
  8. package/admin/i18n/nl.json +18 -0
  9. package/admin/i18n/pl.json +18 -0
  10. package/admin/i18n/pt.json +18 -0
  11. package/admin/i18n/ru.json +18 -0
  12. package/admin/i18n/uk.json +18 -0
  13. package/admin/i18n/zh-cn.json +18 -0
  14. package/admin/jsonConfig.json +96 -0
  15. package/io-package.json +49 -38
  16. package/package.json +36 -49
  17. package/src/controls/AalEmergency.ts +90 -0
  18. package/src/controls/AalSmartAlarm.ts +99 -0
  19. package/src/controls/Alarm.ts +146 -0
  20. package/src/controls/AlarmClock.ts +137 -0
  21. package/src/controls/Application.ts +13 -0
  22. package/src/controls/AudioZone.ts +227 -0
  23. package/src/controls/AudioZoneV2.ts +135 -0
  24. package/src/controls/CentralAlarm.ts +59 -0
  25. package/src/controls/CentralAudioZone.ts +41 -0
  26. package/src/controls/CentralGate.ts +53 -0
  27. package/src/controls/CentralJalousie.ts +67 -0
  28. package/src/controls/CentralLightController.ts +45 -0
  29. package/src/controls/ColorPickerV2.ts +34 -0
  30. package/src/controls/Colorpicker.ts +30 -0
  31. package/src/controls/ColorpickerBase.ts +326 -0
  32. package/src/controls/Daytimer.ts +201 -0
  33. package/src/controls/Dimmer.ts +64 -0
  34. package/src/controls/EIBDimmer.ts +61 -0
  35. package/src/controls/Fronius.ts +217 -0
  36. package/src/controls/Gate.ts +150 -0
  37. package/src/controls/Hourcounter.ts +115 -0
  38. package/src/controls/IRCDaytimer.ts +4 -0
  39. package/src/controls/IRCV2Daytimer.ts +4 -0
  40. package/src/controls/IRoomControllerV2.ts +595 -0
  41. package/src/controls/InfoOnlyAnalog.ts +56 -0
  42. package/src/controls/InfoOnlyDigital.ts +95 -0
  43. package/src/controls/InfoOnlyText.ts +52 -0
  44. package/{build/controls/Intercom.js → src/controls/Intercom.ts} +27 -11
  45. package/src/controls/Jalousie.ts +219 -0
  46. package/src/controls/LightController.ts +112 -0
  47. package/src/controls/LightControllerV2.ts +246 -0
  48. package/src/controls/MailBox.ts +92 -0
  49. package/src/controls/Meter.ts +94 -0
  50. package/src/controls/None.ts +29 -0
  51. package/src/controls/PresenceDetector.ts +47 -0
  52. package/src/controls/Pushbutton.ts +55 -0
  53. package/src/controls/Radio.ts +61 -0
  54. package/{build/controls/Remote.js → src/controls/Remote.ts} +19 -11
  55. package/src/controls/Slider.ts +78 -0
  56. package/src/controls/SmokeAlarm.ts +163 -0
  57. package/src/controls/Switch.ts +46 -0
  58. package/src/controls/SystemScheme.ts +13 -0
  59. package/src/controls/TextInput.ts +96 -0
  60. package/src/controls/TextState.ts +37 -0
  61. package/src/controls/TimedSwitch.ts +75 -0
  62. package/src/controls/Tracker.ts +30 -0
  63. package/{build/controls/Unknown.js → src/controls/Unknown.ts} +18 -11
  64. package/src/controls/UpDownAnalog.ts +78 -0
  65. package/{build/controls/ValueSelector.js → src/controls/ValueSelector.ts} +21 -9
  66. package/src/controls/WindowMonitor.ts +139 -0
  67. package/src/controls/all-controls.ts +99 -0
  68. package/src/controls/control-base.ts +28 -0
  69. package/src/lib/adapter-config.d.ts +22 -0
  70. package/src/loxone-handler-base.ts +285 -0
  71. package/src/lxcommunicator.d.ts +1 -0
  72. package/src/main.test.ts +24 -0
  73. package/{build/main.js → src/main.ts} +545 -270
  74. package/src/structure-file.d.ts +154 -0
  75. package/src/weather-server-handler.ts +266 -0
  76. package/admin/admin.d.ts +0 -58
  77. package/admin/index_m.html +0 -126
  78. package/admin/style.css +0 -32
  79. package/admin/words.js +0 -25
  80. package/build/controls/AalEmergency.js +0 -45
  81. package/build/controls/AalEmergency.js.map +0 -1
  82. package/build/controls/AalSmartAlarm.js +0 -48
  83. package/build/controls/AalSmartAlarm.js.map +0 -1
  84. package/build/controls/Alarm.js +0 -81
  85. package/build/controls/Alarm.js.map +0 -1
  86. package/build/controls/AlarmClock.js +0 -59
  87. package/build/controls/AlarmClock.js.map +0 -1
  88. package/build/controls/Application.js +0 -11
  89. package/build/controls/Application.js.map +0 -1
  90. package/build/controls/AudioZone.js +0 -116
  91. package/build/controls/AudioZone.js.map +0 -1
  92. package/build/controls/CentralAlarm.js +0 -30
  93. package/build/controls/CentralAlarm.js.map +0 -1
  94. package/build/controls/CentralAudioZone.js +0 -22
  95. package/build/controls/CentralAudioZone.js.map +0 -1
  96. package/build/controls/CentralGate.js +0 -30
  97. package/build/controls/CentralGate.js.map +0 -1
  98. package/build/controls/CentralJalousie.js +0 -39
  99. package/build/controls/CentralJalousie.js.map +0 -1
  100. package/build/controls/CentralLightController.js +0 -27
  101. package/build/controls/CentralLightController.js.map +0 -1
  102. package/build/controls/ColorPickerV2.js +0 -24
  103. package/build/controls/ColorPickerV2.js.map +0 -1
  104. package/build/controls/Colorpicker.js +0 -20
  105. package/build/controls/Colorpicker.js.map +0 -1
  106. package/build/controls/ColorpickerBase.js +0 -263
  107. package/build/controls/ColorpickerBase.js.map +0 -1
  108. package/build/controls/Daytimer.js +0 -119
  109. package/build/controls/Daytimer.js.map +0 -1
  110. package/build/controls/Dimmer.js +0 -34
  111. package/build/controls/Dimmer.js.map +0 -1
  112. package/build/controls/EIBDimmer.js +0 -31
  113. package/build/controls/EIBDimmer.js.map +0 -1
  114. package/build/controls/Fronius.js +0 -64
  115. package/build/controls/Fronius.js.map +0 -1
  116. package/build/controls/Gate.js +0 -104
  117. package/build/controls/Gate.js.map +0 -1
  118. package/build/controls/Hourcounter.js +0 -46
  119. package/build/controls/Hourcounter.js.map +0 -1
  120. package/build/controls/IRCDaytimer.js +0 -9
  121. package/build/controls/IRCDaytimer.js.map +0 -1
  122. package/build/controls/IRCV2Daytimer.js +0 -9
  123. package/build/controls/IRCV2Daytimer.js.map +0 -1
  124. package/build/controls/IRoomControllerV2.js +0 -371
  125. package/build/controls/IRoomControllerV2.js.map +0 -1
  126. package/build/controls/InfoOnlyAnalog.js +0 -38
  127. package/build/controls/InfoOnlyAnalog.js.map +0 -1
  128. package/build/controls/InfoOnlyDigital.js +0 -65
  129. package/build/controls/InfoOnlyDigital.js.map +0 -1
  130. package/build/controls/InfoOnlyText.js +0 -35
  131. package/build/controls/InfoOnlyText.js.map +0 -1
  132. package/build/controls/Intercom.js.map +0 -1
  133. package/build/controls/Jalousie.js +0 -153
  134. package/build/controls/Jalousie.js.map +0 -1
  135. package/build/controls/LightController.js +0 -68
  136. package/build/controls/LightController.js.map +0 -1
  137. package/build/controls/LightControllerV2.js +0 -195
  138. package/build/controls/LightControllerV2.js.map +0 -1
  139. package/build/controls/MailBox.js +0 -41
  140. package/build/controls/MailBox.js.map +0 -1
  141. package/build/controls/Meter.js +0 -52
  142. package/build/controls/Meter.js.map +0 -1
  143. package/build/controls/None.js +0 -23
  144. package/build/controls/None.js.map +0 -1
  145. package/build/controls/PresenceDetector.js +0 -29
  146. package/build/controls/PresenceDetector.js.map +0 -1
  147. package/build/controls/Pushbutton.js +0 -35
  148. package/build/controls/Pushbutton.js.map +0 -1
  149. package/build/controls/Radio.js +0 -39
  150. package/build/controls/Radio.js.map +0 -1
  151. package/build/controls/Remote.js.map +0 -1
  152. package/build/controls/Slider.js +0 -44
  153. package/build/controls/Slider.js.map +0 -1
  154. package/build/controls/SmokeAlarm.js +0 -85
  155. package/build/controls/SmokeAlarm.js.map +0 -1
  156. package/build/controls/Switch.js +0 -31
  157. package/build/controls/Switch.js.map +0 -1
  158. package/build/controls/SystemScheme.js +0 -11
  159. package/build/controls/SystemScheme.js.map +0 -1
  160. package/build/controls/TextInput.js +0 -55
  161. package/build/controls/TextInput.js.map +0 -1
  162. package/build/controls/TextState.js +0 -20
  163. package/build/controls/TextState.js.map +0 -1
  164. package/build/controls/TimedSwitch.js +0 -36
  165. package/build/controls/TimedSwitch.js.map +0 -1
  166. package/build/controls/Tracker.js +0 -20
  167. package/build/controls/Tracker.js.map +0 -1
  168. package/build/controls/Unknown.js.map +0 -1
  169. package/build/controls/UpDownAnalog.js +0 -44
  170. package/build/controls/UpDownAnalog.js.map +0 -1
  171. package/build/controls/ValueSelector.js.map +0 -1
  172. package/build/controls/WindowMonitor.js +0 -84
  173. package/build/controls/WindowMonitor.js.map +0 -1
  174. package/build/controls/control-base.js +0 -12
  175. package/build/controls/control-base.js.map +0 -1
  176. package/build/loxone-handler-base.js +0 -186
  177. package/build/loxone-handler-base.js.map +0 -1
  178. package/build/main.js.map +0 -1
  179. package/build/weather-server-handler.js +0 -120
  180. package/build/weather-server-handler.js.map +0 -1
  181. package/doc/details-missing-control-type.png +0 -0
  182. package/doc/log-missing-control-type.png +0 -0
  183. package/doc/loxone-config-display-diagnostics.png +0 -0
  184. package/doc/loxone-config-info-only-digital.png +0 -0
  185. package/doc/loxone-config-use-in-visualization.png +0 -0
@@ -0,0 +1,595 @@
1
+ import type { CurrentStateValue, OldStateValue } from '../main';
2
+ import type { Control } from '../structure-file';
3
+ import type { ControlType } from './control-base';
4
+ import { ControlBase } from './control-base';
5
+
6
+ /**
7
+ * Handler for IRoomControllerV2 controls.
8
+ */
9
+ export class IRoomControllerV2 extends ControlBase {
10
+ private uuid = '';
11
+
12
+ /**
13
+ * Loads the control and sets up state objects and event handlers.
14
+ *
15
+ * @param type The type of the control ('device' or 'channel').
16
+ * @param uuid The unique identifier of the control.
17
+ * @param control The control data from the structure file.
18
+ */
19
+ async loadAsync(type: ControlType, uuid: string, control: Control): Promise<void> {
20
+ this.uuid = uuid;
21
+
22
+ await this.updateObjectAsync(uuid, {
23
+ type: type,
24
+ common: {
25
+ name: control.name,
26
+ role: 'thermo',
27
+ },
28
+ native: { control },
29
+ });
30
+
31
+ // TODO: other details not implemented - needed to get temperature unit for example (C/F).
32
+ // TODO: connectedInputs has bits for frost/heat protection but no set control for them. Eh?
33
+ const comfortTemperatureWrite = (<number>control.details.connectedInputs) & 1 ? false : true;
34
+ const comfortTemperatureCoolWrite = (<number>control.details.connectedInputs) & 2 ? false : true;
35
+
36
+ // comfortToleranceWrite marked as deprecated in Loxone doco
37
+ const comfortToleranceWrite = (<number>control.details.connectedInputs) & 4 ? false : true;
38
+
39
+ const absentMinOffsetWrite = (<number>control.details.connectedInputs) & 8 ? false : true;
40
+ const absentMaxOffsetWrite = (<number>control.details.connectedInputs) & 16 ? false : true;
41
+
42
+ const shadingHeatTempWrite = (<number>control.details.connectedInputs) & 32 ? false : true;
43
+ const shadingCoolTempWrite = (<number>control.details.connectedInputs) & 64 ? false : true;
44
+
45
+ await this.createSimpleControlStateObjectAsync(control.name, uuid, control.states, 'jLocked', 'string', 'json');
46
+ await this.createListControlStateObjectAsync(control.name, uuid, control.states, 'modeList');
47
+ await this.createListControlStateObjectAsync(control.name, uuid, control.states, 'overrideEntries');
48
+ await this.createSimpleControlStateObjectAsync(
49
+ control.name,
50
+ uuid,
51
+ control.states,
52
+ 'overrideReason',
53
+ 'number',
54
+ 'value',
55
+ {
56
+ states: {
57
+ 0: 'None',
58
+ 1: 'Presence -> Comfort',
59
+ 2: 'Window open -> Eco+',
60
+ 3: 'Comfort',
61
+ 4: 'Eco',
62
+ 5: 'Eco+',
63
+ 6: 'Heat Up',
64
+ 7: 'Cool Down',
65
+ },
66
+ },
67
+ );
68
+ await this.createSimpleControlStateObjectAsync(
69
+ control.name,
70
+ uuid,
71
+ control.states,
72
+ 'prepareState',
73
+ 'number',
74
+ 'value',
75
+ {
76
+ states: {
77
+ '-1': 'Cooling Down',
78
+ 0: 'None',
79
+ 1: 'Heating Up',
80
+ },
81
+ },
82
+ );
83
+ await this.createBooleanControlStateObjectAsync(control.name, uuid, control.states, 'openWindow', 'indicator');
84
+ await this.createBooleanControlStateObjectAsync(control.name, uuid, control.states, 'useOutdoor', 'indicator');
85
+
86
+ await this.createSimpleControlStateObjectAsync(
87
+ control.name,
88
+ uuid,
89
+ control.states,
90
+ 'frostProtectTemperature',
91
+ 'number',
92
+ 'value.temperature',
93
+ );
94
+ await this.createSimpleControlStateObjectAsync(
95
+ control.name,
96
+ uuid,
97
+ control.states,
98
+ 'heatProtectTemperature',
99
+ 'number',
100
+ 'value.temperature',
101
+ );
102
+ // Add event handler to frost/heat protection values so we re-calc target min/max if they change
103
+ this.addStateEventHandler(`${uuid}.frostProtectTemperature`, async () => {
104
+ await this.updateTempTargetMinMax();
105
+ });
106
+ this.addStateEventHandler(`${uuid}.heatProtectTemperature`, async () => {
107
+ await this.updateTempTargetMinMax();
108
+ });
109
+
110
+ await this.updateStateObjectAsync(
111
+ `${uuid}.activeMode`,
112
+ {
113
+ name: `${control.name}: activeMode`,
114
+ read: true,
115
+ write: true,
116
+ type: 'number',
117
+ role: 'level',
118
+ states: {
119
+ 0: 'Economy',
120
+ 1: 'Comfort',
121
+ 2: 'Fabric Protection',
122
+ 3: 'Manual',
123
+ },
124
+ },
125
+ control.states.activeMode,
126
+ async (name: string, value: any) => {
127
+ await this.setStateAck(name, value);
128
+ await this.updateTempTargetMinMax();
129
+ },
130
+ );
131
+ this.addStateChangeListener(`${uuid}.activeMode`, (oldValue: OldStateValue, newValue: CurrentStateValue) => {
132
+ this.sendCommand(control.uuidAction, `override/${newValue}`);
133
+ });
134
+
135
+ await this.updateStateObjectAsync(
136
+ `${uuid}.comfortTemperature`,
137
+ {
138
+ name: `${control.name}: comfortTemperature`,
139
+ read: true,
140
+ write: comfortTemperatureWrite,
141
+ type: 'number',
142
+ role: comfortTemperatureWrite ? 'level.temperature' : 'value.temperature',
143
+ },
144
+ control.states.comfortTemperature,
145
+ async (name: string, value: any) => {
146
+ await this.setStateAck(name, value);
147
+ },
148
+ );
149
+ if (comfortTemperatureWrite) {
150
+ this.addStateChangeListener(
151
+ `${uuid}.comfortTemperature`,
152
+ (oldValue: OldStateValue, newValue: CurrentStateValue) => {
153
+ this.sendCommand(control.uuidAction, `setComfortTemperature/${newValue}`);
154
+ },
155
+ );
156
+ }
157
+
158
+ await this.updateStateObjectAsync(
159
+ `${uuid}.comfortTemperatureCool`,
160
+ {
161
+ name: `${control.name}: comfortTemperatureCool`,
162
+ read: true,
163
+ write: comfortTemperatureCoolWrite,
164
+ type: 'number',
165
+ role: comfortTemperatureCoolWrite ? 'level.temperature' : 'value.temperature',
166
+ },
167
+ control.states.comfortTemperatureCool,
168
+ async (name: string, value: any) => {
169
+ await this.setStateAck(name, value);
170
+ },
171
+ );
172
+ if (comfortTemperatureCoolWrite) {
173
+ this.addStateChangeListener(
174
+ `${uuid}.comfortTemperatureCool`,
175
+ (oldValue: OldStateValue, newValue: CurrentStateValue) => {
176
+ this.sendCommand(control.uuidAction, `setComfortTemperatureCool/${newValue}`);
177
+ },
178
+ );
179
+ }
180
+
181
+ await this.updateStateObjectAsync(
182
+ `${uuid}.comfortTolerance`,
183
+ {
184
+ name: `${control.name}: comfortTolerance`,
185
+ read: true,
186
+ write: comfortToleranceWrite,
187
+ type: 'number',
188
+ min: 0.5,
189
+ max: 3.0,
190
+ role: comfortToleranceWrite ? 'level.temperature' : 'value.temperature',
191
+ },
192
+ control.states.comfortTolerance,
193
+ async (name: string, value: any) => {
194
+ await this.setStateAck(name, value);
195
+ },
196
+ );
197
+ if (comfortToleranceWrite) {
198
+ this.addStateChangeListener(
199
+ `${uuid}.comfortTolerance`,
200
+ (oldValue: OldStateValue, newValue: CurrentStateValue) => {
201
+ this.sendCommand(control.uuidAction, `setComfortTolerance/${newValue}`);
202
+ },
203
+ );
204
+ }
205
+
206
+ await this.updateStateObjectAsync(
207
+ `${uuid}.absentMinOffset`,
208
+ {
209
+ name: `${control.name}: absentMinOffset`,
210
+ read: true,
211
+ write: absentMinOffsetWrite,
212
+ type: 'number',
213
+ role: absentMinOffsetWrite ? 'level.temperature' : 'value.temperature',
214
+ },
215
+ control.states.absentMinOffset,
216
+ async (name: string, value: any) => {
217
+ await this.setStateAck(name, value);
218
+ await this.updateTempTargetMinMax();
219
+ },
220
+ );
221
+ if (absentMinOffsetWrite) {
222
+ this.addStateChangeListener(
223
+ `${uuid}.absentMinOffset`,
224
+ (oldValue: OldStateValue, newValue: CurrentStateValue) => {
225
+ this.sendCommand(control.uuidAction, `setAbsentMinTemperature/${newValue}`);
226
+ },
227
+ );
228
+ }
229
+
230
+ await this.updateStateObjectAsync(
231
+ `${uuid}.absentMaxOffset`,
232
+ {
233
+ name: `${control.name}: absentMaxOffset`,
234
+ read: true,
235
+ write: absentMaxOffsetWrite,
236
+ type: 'number',
237
+ role: absentMaxOffsetWrite ? 'level.temperature' : 'value.temperature',
238
+ },
239
+ control.states.absentMaxOffset,
240
+ async (name: string, value: any) => {
241
+ await this.setStateAck(name, value);
242
+ await this.updateTempTargetMinMax();
243
+ },
244
+ );
245
+ if (absentMaxOffsetWrite) {
246
+ this.addStateChangeListener(
247
+ `${uuid}.absentMaxOffset`,
248
+ (oldValue: OldStateValue, newValue: CurrentStateValue) => {
249
+ this.sendCommand(control.uuidAction, `setAbsentMaxTemperature/${newValue}`);
250
+ },
251
+ );
252
+ }
253
+
254
+ await this.updateStateObjectAsync(
255
+ `${uuid}.comfortTemperatureOffset`,
256
+ {
257
+ name: `${control.name}: comfortTemperatureOffset`,
258
+ read: true,
259
+ write: true,
260
+ type: 'number',
261
+ role: 'level.temperature',
262
+ },
263
+ control.states.comfortTemperatureOffset,
264
+ async (name: string, value: any) => {
265
+ await this.setStateAck(name, value);
266
+ await this.updateTempTargetMinMax();
267
+ },
268
+ );
269
+ this.addStateChangeListener(
270
+ `${uuid}.comfortTemperatureOffset`,
271
+ (oldValue: OldStateValue, newValue: CurrentStateValue) => {
272
+ this.sendCommand(control.uuidAction, `setComfortModeTemp/${newValue}`);
273
+ },
274
+ );
275
+
276
+ await this.updateStateObjectAsync(
277
+ `${uuid}.shadingHeatTemp`,
278
+ {
279
+ name: `${control.name}: shadingHeatTemp`,
280
+ read: true,
281
+ write: shadingHeatTempWrite,
282
+ type: 'number',
283
+ role: shadingHeatTempWrite ? 'level.temperature' : 'value.temperature',
284
+ },
285
+ control.states.shadingHeatTemp,
286
+ async (name: string, value: any) => {
287
+ await this.setStateAck(name, value);
288
+ },
289
+ );
290
+ if (shadingHeatTempWrite) {
291
+ this.addStateChangeListener(
292
+ `${uuid}.shadingHeatTemp`,
293
+ (oldValue: OldStateValue, newValue: CurrentStateValue) => {
294
+ this.sendCommand(control.uuidAction, `setShadingHeatTemp/${newValue}`);
295
+ },
296
+ );
297
+ }
298
+
299
+ await this.updateStateObjectAsync(
300
+ `${uuid}.shadingCoolTemp`,
301
+ {
302
+ name: `${control.name}: shadingCoolTemp`,
303
+ read: true,
304
+ write: shadingCoolTempWrite,
305
+ type: 'number',
306
+ role: shadingCoolTempWrite ? 'level.temperature' : 'value.temperature',
307
+ },
308
+ control.states.shadingCoolTemp,
309
+ async (name: string, value: any) => {
310
+ await this.setStateAck(name, value);
311
+ },
312
+ );
313
+ if (shadingHeatTempWrite) {
314
+ this.addStateChangeListener(
315
+ `${uuid}.shadingCoolTemp`,
316
+ (oldValue: OldStateValue, newValue: CurrentStateValue) => {
317
+ this.sendCommand(control.uuidAction, `setShadingCoolTemp/${newValue}`);
318
+ },
319
+ );
320
+ }
321
+
322
+ await this.createSimpleControlStateObjectAsync(
323
+ control.name,
324
+ uuid,
325
+ control.states,
326
+ 'shadingOut',
327
+ 'boolean',
328
+ 'indicator',
329
+ );
330
+
331
+ await this.createSimpleControlStateObjectAsync(
332
+ control.name,
333
+ uuid,
334
+ control.states,
335
+ 'tempActual',
336
+ 'number',
337
+ 'value.temperature',
338
+ );
339
+
340
+ await this.createSimpleControlStateObjectAsync(
341
+ control.name,
342
+ uuid,
343
+ control.states,
344
+ 'actualOutdoorTemp',
345
+ 'number',
346
+ 'value.temperature',
347
+ );
348
+
349
+ await this.createSimpleControlStateObjectAsync(
350
+ control.name,
351
+ uuid,
352
+ control.states,
353
+ 'averageOutdoorTemp',
354
+ 'number',
355
+ 'value.temperature',
356
+ );
357
+
358
+ await this.createSimpleControlStateObjectAsync(
359
+ control.name,
360
+ uuid,
361
+ control.states,
362
+ 'excessEnergyTempOffset',
363
+ 'number',
364
+ 'value.temperature',
365
+ );
366
+
367
+ await this.updateStateObjectAsync(
368
+ `${uuid}.temperatureBoundaryInfo`,
369
+ {
370
+ name: `${control.name}: temperatureBoundaryInfo`,
371
+ read: true,
372
+ write: false,
373
+ type: 'number',
374
+ role: 'level',
375
+ states: {
376
+ 0: 'Not enough data',
377
+ 1: 'OK',
378
+ 2: 'No data at all',
379
+ },
380
+ },
381
+ control.states.temperatureBoundaryInfo,
382
+ async (name: string, value: any) => {
383
+ await this.setStateAck(name, value);
384
+ },
385
+ );
386
+
387
+ await this.updateStateObjectAsync(
388
+ `${uuid}.tempTarget`,
389
+ {
390
+ name: `${control.name}: tempTarget`,
391
+ read: true,
392
+ write: true,
393
+ type: 'number',
394
+ role: 'level.temperature',
395
+ },
396
+ control.states.tempTarget,
397
+ async (name: string, value: any) => {
398
+ await this.setStateAck(name, value);
399
+ await this.updateTempTargetMinMax();
400
+ },
401
+ );
402
+ this.addStateChangeListener(`${uuid}.tempTarget`, (oldValue: OldStateValue, newValue: CurrentStateValue) => {
403
+ // Target has been set manually so start an override at that value
404
+ // setManualTemperature is redundant in this context as simply setting that value
405
+ // does not actually tell IRC to switch to manual mode.
406
+ // We could force whatever updates the tempTarget to then need to change mode to manual
407
+ // but that would be illogical as the target now shown would not actually be the target
408
+ // being worked with.
409
+ // So because of this, just start an override at the given temperature.
410
+ this.sendCommand(control.uuidAction, `override/3//${newValue}`);
411
+ });
412
+
413
+ // TODO: capabilities is a bitmask, possibility to decode it's meaning?
414
+ await this.createSimpleControlStateObjectAsync(
415
+ control.name,
416
+ uuid,
417
+ control.states,
418
+ 'capabilities',
419
+ 'number',
420
+ 'value',
421
+ );
422
+
423
+ await this.updateStateObjectAsync(
424
+ `${uuid}.currentMode`,
425
+ {
426
+ name: `${control.name}: currentMode`,
427
+ read: true,
428
+ write: false,
429
+ type: 'number',
430
+ role: 'level',
431
+ states: {
432
+ 0: 'No requirement',
433
+ 1: 'Heating',
434
+ 2: 'Cooling',
435
+ 3: 'Heating boost',
436
+ 4: 'Cooling boost',
437
+ 5: 'Service mode',
438
+ 6: 'External Heater',
439
+ },
440
+ },
441
+ control.states.currentMode,
442
+ async (name: string, value: any) => {
443
+ await this.setStateAck(name, value);
444
+ },
445
+ );
446
+
447
+ await this.updateStateObjectAsync(
448
+ `${uuid}.autoMode`,
449
+ {
450
+ name: `${control.name}: autoMode`,
451
+ read: true,
452
+ write: false,
453
+ type: 'number',
454
+ role: 'level',
455
+ states: {
456
+ 0: 'Heating and cooling',
457
+ 1: 'Heating',
458
+ 2: 'Cooling',
459
+ },
460
+ },
461
+ control.states.autoMode,
462
+ async (name: string, value: any) => {
463
+ await this.setStateAck(name, value);
464
+ },
465
+ );
466
+
467
+ await this.updateStateObjectAsync(
468
+ `${uuid}.operatingMode`,
469
+ {
470
+ name: `${control.name}: operatingMode`,
471
+ read: true,
472
+ write: true,
473
+ type: 'number',
474
+ role: 'level',
475
+ states: {
476
+ 0: 'Automatic heating/cooling',
477
+ 1: 'Automatic heating',
478
+ 2: 'Automatic cooling',
479
+ 3: 'Manual heating/cooling',
480
+ 4: 'Manual heating',
481
+ 5: 'Manual cooling',
482
+ },
483
+ },
484
+ control.states.operatingMode,
485
+ async (name: string, value: any) => {
486
+ await this.setStateAck(name, value);
487
+ },
488
+ );
489
+ this.addStateChangeListener(`${uuid}.operatingMode`, (oldValue: OldStateValue, newValue: CurrentStateValue) => {
490
+ this.sendCommand(control.uuidAction, `setOperatingMode/${newValue}`);
491
+ });
492
+
493
+ await this.createButtonCommandStateObjectAsync(control.name, uuid, 'stopOverride');
494
+ this.addStateChangeListener(
495
+ `${uuid}.stopOverride`,
496
+ () => {
497
+ this.sendCommand(control.uuidAction, 'stopOverride');
498
+ },
499
+ { selfAck: true },
500
+ );
501
+ // When in Eco/Building Protection modes Loxone app/web shows target as min..max range
502
+ // Handy so create derived state for each
503
+ await this.updateObjectAsync(`${uuid}.tempTargetMin`, {
504
+ type: 'state',
505
+ common: {
506
+ name: `${control.name}: tempTargetMin`,
507
+ type: 'number',
508
+ role: 'value.temperature',
509
+ read: true,
510
+ write: false,
511
+ },
512
+ native: {},
513
+ });
514
+ await this.updateObjectAsync(`${uuid}.tempTargetMax`, {
515
+ type: 'state',
516
+ common: {
517
+ name: `${control.name}: tempTargetMax`,
518
+ type: 'number',
519
+ role: 'value.temperature',
520
+ read: true,
521
+ write: false,
522
+ },
523
+ native: {},
524
+ });
525
+
526
+ // TODO: Controls not yet implemented:
527
+ // override (at least not fully)
528
+ // setComfortModeTemp (redundant as setComfortTemperature implemented - I think!)
529
+ // set
530
+ // modeslist
531
+
532
+ await this.loadSubControlsAsync(uuid, control);
533
+ }
534
+
535
+ // Calculate and update tempTargetMin/Max
536
+ // Should be triggered when any inputs change
537
+ // TODO: this is called when any of the input variables change in handlers above.
538
+ // Would be slightly more efficient (& complex) to only call this update if the
539
+ // inputs relating to current mode change. Ie. dont call this on absentMinOffset
540
+ // change if mode is 1.
541
+ protected async updateTempTargetMinMax(): Promise<void> {
542
+ const activeMode = this.convertStateToInt(this.getCachedStateValue(`${this.uuid}.activeMode`));
543
+ let tempTargetMin;
544
+ let tempTargetMax;
545
+ switch (activeMode) {
546
+ case 0: {
547
+ // Economy, min/max are target +/- absent offset
548
+ const absentMinOffset = this.convertStateToFloat(
549
+ this.getCachedStateValue(`${this.uuid}.absentMinOffset`),
550
+ );
551
+ const absentMaxOffset = this.convertStateToFloat(
552
+ this.getCachedStateValue(`${this.uuid}.absentMaxOffset`),
553
+ );
554
+ const comfortTemperature = this.convertStateToFloat(
555
+ this.getCachedStateValue(`${this.uuid}.comfortTemperature`),
556
+ );
557
+
558
+ tempTargetMin = comfortTemperature - absentMinOffset;
559
+ tempTargetMax = comfortTemperature + absentMaxOffset;
560
+ this.adapter.log.debug(
561
+ `Mode ${activeMode}: ${absentMinOffset}/${absentMaxOffset}/${comfortTemperature} -> ${tempTargetMin}/${tempTargetMax}`,
562
+ );
563
+ break;
564
+ }
565
+
566
+ case 2:
567
+ // Building protection uses frost & heat protection
568
+ tempTargetMin = this.convertStateToFloat(
569
+ this.getCachedStateValue(`${this.uuid}.frostProtectTemperature`),
570
+ );
571
+ tempTargetMax = this.convertStateToFloat(
572
+ this.getCachedStateValue(`${this.uuid}.heatProtectTemperature`),
573
+ );
574
+ this.adapter.log.debug(`Mode ${activeMode}: -> ${tempTargetMin}/${tempTargetMax}`);
575
+ break;
576
+
577
+ default:
578
+ // Comfort, manual & override modes just use direct target
579
+ // TODO: Loxone doco doesn't properly explain what activeMode is on override so just assume it's OK.
580
+ tempTargetMin = tempTargetMax = this.convertStateToFloat(
581
+ this.getCachedStateValue(`${this.uuid}.tempTarget`),
582
+ );
583
+ this.adapter.log.debug(`Mode ${activeMode}: ${tempTargetMin} -> ${tempTargetMin}/${tempTargetMax}`);
584
+ break;
585
+ }
586
+
587
+ // Don't do any update if not calculated (can only happen in error condition)
588
+ if (tempTargetMin !== undefined) {
589
+ await this.setStateAck(`${this.uuid}.tempTargetMin`, tempTargetMin);
590
+ }
591
+ if (tempTargetMax !== undefined) {
592
+ await this.setStateAck(`${this.uuid}.tempTargetMax`, tempTargetMax);
593
+ }
594
+ }
595
+ }
@@ -0,0 +1,56 @@
1
+ import type { Control } from '../structure-file';
2
+ import type { ControlType } from './control-base';
3
+ import { ControlBase } from './control-base';
4
+
5
+ /**
6
+ * Handler for InfoOnlyAnalog controls.
7
+ */
8
+ export class InfoOnlyAnalog extends ControlBase {
9
+ /**
10
+ * Loads the control and sets up state objects and event handlers.
11
+ *
12
+ * @param type The type of the control ('device' or 'channel').
13
+ * @param uuid The unique identifier of the control.
14
+ * @param control The control data from the structure file.
15
+ */
16
+ async loadAsync(type: ControlType, uuid: string, control: Control): Promise<void> {
17
+ await this.updateObjectAsync(uuid, {
18
+ type: type,
19
+ common: {
20
+ name: control.name,
21
+ role: 'sensor',
22
+ },
23
+ native: { control },
24
+ });
25
+
26
+ await this.loadOtherControlStatesAsync(control.name, uuid, control.states, ['value']);
27
+
28
+ if (!('states' in control) || !('value' in control.states)) {
29
+ return;
30
+ }
31
+
32
+ await this.createSimpleControlStateObjectAsync(control.name, uuid, control.states, 'value', 'number', 'value');
33
+
34
+ if (!('details' in control)) {
35
+ return;
36
+ }
37
+
38
+ if ('format' in control.details) {
39
+ await this.updateStateObjectAsync(
40
+ `${uuid}.value-formatted`,
41
+ {
42
+ name: `${control.name}: formatted value`,
43
+ read: true,
44
+ write: false,
45
+ type: 'string',
46
+ role: 'text',
47
+ // TODO: re-add: smartIgnore: true,
48
+ },
49
+ control.states.value,
50
+ async (name: string, value: any) => {
51
+ await this.setFormattedStateAck(name, value, control.details.format as string);
52
+ },
53
+ );
54
+ }
55
+ }
56
+ }