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,30 @@
1
+ import type { Control } from '../structure-file';
2
+ import { ColorpickerBase } from './ColorpickerBase';
3
+ import type { ControlType } from './control-base';
4
+
5
+ /**
6
+ * Handler for Colorpicker controls.
7
+ */
8
+ export class Colorpicker extends ColorpickerBase {
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: 'light.color.rgb',
22
+ },
23
+ native: { control },
24
+ });
25
+
26
+ await this.loadOtherControlStatesAsync(control.name, uuid, control.states, ['color', 'favorites']);
27
+
28
+ await this.loadColorPickerControlBaseAsync(uuid, control);
29
+ }
30
+ }
@@ -0,0 +1,326 @@
1
+ import type { RGB } from 'color-convert';
2
+ import colorConvert from 'color-convert';
3
+ import type { CurrentStateValue } from '../main';
4
+ import type { Control } from '../structure-file';
5
+ import { ControlBase } from './control-base';
6
+
7
+ /**
8
+ * Base class for color picker controls.
9
+ */
10
+ export abstract class ColorpickerBase extends ControlBase {
11
+ private colorUpdateTimer?: NodeJS.Timeout;
12
+
13
+ /**
14
+ * Loads the color picker control and sets up state objects and event handlers.
15
+ *
16
+ * @param uuid The unique identifier of the control.
17
+ * @param control The control data from the structure file.
18
+ * @returns A promise that resolves when the loading is complete.
19
+ */
20
+ protected async loadColorPickerControlBaseAsync(uuid: string, control: Control): Promise<void> {
21
+ if (!control.states || !('color' in control.states)) {
22
+ return;
23
+ }
24
+
25
+ if (control.details.pickerType === 'Rgb') {
26
+ await this.loadRgbColorPickerAsync(uuid, control);
27
+ } else if (control.details.pickerType === 'Lumitech') {
28
+ await this.loadLumitechColorPickerAsync(uuid, control);
29
+ } else {
30
+ throw new Error(`Unsupported color picker type: ${control.details.pickerType as string}`);
31
+ }
32
+ }
33
+
34
+ private async loadRgbColorPickerAsync(uuid: string, control: Control): Promise<void> {
35
+ await this.updateStateObjectAsync(
36
+ `${uuid}.red`,
37
+ {
38
+ name: `${control.name}: red`,
39
+ read: true,
40
+ write: true,
41
+ type: 'number',
42
+ role: 'level.color.red',
43
+ min: 0,
44
+ max: 255,
45
+ // TODO: re-add: smartIgnore: true,
46
+ },
47
+ control.states.color,
48
+ async (name: string, value: CurrentStateValue) => {
49
+ const rgb = this.loxoneColorToRgb(value);
50
+ if (rgb !== undefined) {
51
+ await this.setStateAck(name, rgb[0]);
52
+ }
53
+ },
54
+ );
55
+ await this.updateStateObjectAsync(
56
+ `${uuid}.green`,
57
+ {
58
+ name: `${control.name}: green`,
59
+ read: true,
60
+ write: true,
61
+ type: 'number',
62
+ role: 'level.color.green',
63
+ min: 0,
64
+ max: 255,
65
+ // TODO: re-add: smartIgnore: true,
66
+ },
67
+ control.states.color,
68
+ async (name: string, value: CurrentStateValue) => {
69
+ const rgb = this.loxoneColorToRgb(value);
70
+ if (rgb !== undefined) {
71
+ await this.setStateAck(name, rgb[1]);
72
+ }
73
+ },
74
+ );
75
+ await this.updateStateObjectAsync(
76
+ `${uuid}.blue`,
77
+ {
78
+ name: `${control.name}: blue`,
79
+ read: true,
80
+ write: true,
81
+ type: 'number',
82
+ role: 'level.color.blue',
83
+ min: 0,
84
+ max: 255,
85
+ // TODO: re-add: smartIgnore: true,
86
+ },
87
+ control.states.color,
88
+ async (name: string, value: CurrentStateValue) => {
89
+ const rgb = this.loxoneColorToRgb(value);
90
+ if (rgb !== undefined) {
91
+ await this.setStateAck(name, rgb[2]);
92
+ }
93
+ },
94
+ );
95
+ await this.updateStateObjectAsync(
96
+ `${uuid}.rgb`,
97
+ {
98
+ name: `${control.name}: RGB`,
99
+ read: true,
100
+ write: false,
101
+ type: 'string',
102
+ role: 'level.color.rgb',
103
+ // TODO: re-add: smartIgnore: true,
104
+ },
105
+ control.states.color,
106
+ async (name: string, value: CurrentStateValue) => {
107
+ const rgb = this.loxoneColorToRgb(value);
108
+ if (rgb !== undefined) {
109
+ await this.setStateAck(name, `${rgb[0]},${rgb[1]},${rgb[2]}`);
110
+ }
111
+ },
112
+ );
113
+ await this.updateStateObjectAsync(
114
+ `${uuid}.level`,
115
+ {
116
+ name: `${control.name}: Level (only with colorTemperature)`,
117
+ read: true,
118
+ write: false,
119
+ type: 'number',
120
+ role: 'level.color.level',
121
+ min: 0,
122
+ max: 100,
123
+ // TODO: re-add: smartIgnore: true,
124
+ },
125
+ control.states.color,
126
+ async (name: string, value: CurrentStateValue) => {
127
+ const brightnessTemperature = this.lumitechOrLoxoneColorToBrightnessTemperature(value);
128
+ if (brightnessTemperature !== undefined) {
129
+ await this.setStateAck(name, brightnessTemperature[0]);
130
+ }
131
+ },
132
+ );
133
+ await this.updateStateObjectAsync(
134
+ `${uuid}.colorTemperature`,
135
+ {
136
+ name: `${control.name}: The temperature of the light in °K 2700-6500`,
137
+ read: true,
138
+ write: false,
139
+ type: 'number',
140
+ role: 'level.color.temperature',
141
+ // TODO: re-add: smartIgnore: true,
142
+ },
143
+ control.states.color,
144
+ async (name: string, value: CurrentStateValue) => {
145
+ const brightnessTemperature = this.lumitechOrLoxoneColorToBrightnessTemperature(value);
146
+ if (brightnessTemperature !== undefined) {
147
+ await this.setStateAck(name, brightnessTemperature[1]);
148
+ }
149
+ },
150
+ );
151
+ await this.updateStateObjectAsync(
152
+ `${uuid}.colorTemperatureHue`,
153
+ {
154
+ name: `${control.name}: The temperature of the light in °K scaled for Hue 2000-6500`,
155
+ read: true,
156
+ write: false,
157
+ type: 'number',
158
+ role: 'level.color.temperature',
159
+ // TODO: re-add: smartIgnore: true,
160
+ },
161
+ control.states.color,
162
+ async (name: string, value: CurrentStateValue) => {
163
+ const brightnessTemperature = this.lumitechOrLoxoneColorToBrightnessTemperature(value);
164
+ if (brightnessTemperature !== undefined) {
165
+ await this.setStateAck(
166
+ name,
167
+ Math.round((brightnessTemperature[1] - 2700) * 1.184210526315789 + 2000),
168
+ );
169
+ }
170
+ },
171
+ );
172
+
173
+ // we use a timer (100 ms) to update the three color values,
174
+ // so if somebody sends us the three values (almost) at once,
175
+ // we don't change the color three times using commands
176
+ const parentId = `${this.adapter.namespace}.${uuid}`;
177
+ const updateColorValue = async (): Promise<void> => {
178
+ const states = await this.adapter.getStatesAsync(`${uuid}.*`);
179
+ const red = this.convertStateToInt(states[`${parentId}.red`].val);
180
+ const green = this.convertStateToInt(states[`${parentId}.green`].val);
181
+ const blue = this.convertStateToInt(states[`${parentId}.blue`].val);
182
+
183
+ const hsv = colorConvert.rgb.hsv([red, green, blue]);
184
+ const command = `hsv(${hsv[0]},${hsv[1]},${hsv[2]})`;
185
+ this.sendCommand(control.uuidAction, command);
186
+ };
187
+ const startUpdateTimer = (): void => {
188
+ if (this.colorUpdateTimer) {
189
+ clearTimeout(this.colorUpdateTimer);
190
+ }
191
+ this.colorUpdateTimer = setTimeout(updateColorValue, 100);
192
+ };
193
+ this.addStateChangeListener(`${uuid}.red`, startUpdateTimer);
194
+ this.addStateChangeListener(`${uuid}.green`, startUpdateTimer);
195
+ this.addStateChangeListener(`${uuid}.blue`, startUpdateTimer);
196
+ }
197
+
198
+ private async loadLumitechColorPickerAsync(uuid: string, control: Control): Promise<void> {
199
+ await this.updateStateObjectAsync(
200
+ `${uuid}.brightness`,
201
+ {
202
+ name: `${control.name}: Brightness`,
203
+ read: true,
204
+ write: true,
205
+ type: 'number',
206
+ role: 'level.color.level',
207
+ min: 0,
208
+ max: 100,
209
+ // TODO: re-add: smartIgnore: true,
210
+ },
211
+ control.states.color,
212
+ async (name: string, value: CurrentStateValue) => {
213
+ const brightnessTemperature = this.lumitechOrLoxoneColorToBrightnessTemperature(value);
214
+ if (brightnessTemperature !== undefined) {
215
+ await this.setStateAck(name, brightnessTemperature[0]);
216
+ }
217
+ },
218
+ );
219
+ await this.updateStateObjectAsync(
220
+ `${uuid}.temperature`,
221
+ {
222
+ name: `${control.name}: Temperature`,
223
+ read: true,
224
+ write: true,
225
+ type: 'number',
226
+ role: 'level.color.temperature',
227
+ min: 2000,
228
+ max: 8000,
229
+ // TODO: re-add: smartIgnore: true,
230
+ },
231
+ control.states.color,
232
+ async (name: string, value: CurrentStateValue) => {
233
+ const brightnessTemperature = this.lumitechOrLoxoneColorToBrightnessTemperature(value);
234
+ if (brightnessTemperature !== undefined) {
235
+ await this.setStateAck(name, brightnessTemperature[1]);
236
+ }
237
+ },
238
+ );
239
+
240
+ // we use a timer (100 ms) to update the two color values,
241
+ // so if somebody sends us the two values (almost) at once,
242
+ // we don't change the color twice using commands
243
+ const parentId = `${this.adapter.namespace}.${uuid}`;
244
+ const updateColorValue = async (): Promise<void> => {
245
+ const states = await this.adapter.getStatesAsync(`${uuid}.*`);
246
+ const brightness = this.convertStateToInt(states[`${parentId}.brightness`].val);
247
+ const temperature = this.convertStateToInt(states[`${parentId}.temperature`].val);
248
+
249
+ const command = `lumitech(${brightness},${temperature})`;
250
+ this.sendCommand(control.uuidAction, command);
251
+ };
252
+ const startUpdateTimer = (): void => {
253
+ if (this.colorUpdateTimer) {
254
+ clearTimeout(this.colorUpdateTimer);
255
+ }
256
+ this.colorUpdateTimer = setTimeout(updateColorValue, 100);
257
+ };
258
+ this.addStateChangeListener(`${uuid}.brightness`, startUpdateTimer);
259
+ this.addStateChangeListener(`${uuid}.temperature`, startUpdateTimer);
260
+ }
261
+
262
+ private loxoneColorToRgb(value: CurrentStateValue): RGB | undefined {
263
+ if (!value) {
264
+ return undefined;
265
+ }
266
+
267
+ value = value.toString();
268
+ let match = value.match(/hsv\((\d+),(\d+),(\d+)\)/i);
269
+ if (match) {
270
+ const hue = parseInt(match[1]);
271
+ const sat = parseInt(match[2]);
272
+ const val = parseInt(match[3]);
273
+
274
+ return colorConvert.hsv.rgb([hue, sat, val]);
275
+ }
276
+
277
+ match = value.match(/temp\((\d+),(\d+)\)/i);
278
+ if (match) {
279
+ const brightness = parseFloat(match[1]) / 100;
280
+ const temperature = parseFloat(match[2]) / 100;
281
+
282
+ // based on http://www.tannerhelland.com/4435/convert-temperature-rgb-algorithm-code/
283
+ let red = 255;
284
+ let green = 255;
285
+ let blue = 255;
286
+
287
+ if (temperature <= 66) {
288
+ green = 99.4708025861 * Math.log(temperature) - 161.1195681661;
289
+ if (temperature <= 19) {
290
+ blue = 0;
291
+ } else {
292
+ blue = 138.5177312231 * Math.log(temperature - 10) - 305.0447927307;
293
+ }
294
+ } else {
295
+ red = 329.698727446 * Math.pow(temperature - 60, -0.1332047592);
296
+ green = 288.1221695283 * Math.pow(temperature - 60, -0.0755148492);
297
+ }
298
+
299
+ red = Math.min(Math.max(red, 0), 255) * brightness;
300
+ green = Math.min(Math.max(green, 0), 255) * brightness;
301
+ blue = Math.min(Math.max(blue, 0), 255) * brightness;
302
+
303
+ return [Math.round(red), Math.round(green), Math.round(blue)];
304
+ }
305
+
306
+ return undefined;
307
+ }
308
+
309
+ private lumitechOrLoxoneColorToBrightnessTemperature(value: CurrentStateValue): [number, number] | undefined {
310
+ if (!value) {
311
+ return undefined;
312
+ }
313
+
314
+ value = value.toString();
315
+
316
+ const match = value.match(/(lumitech|temp)\((\d+),(\d+)\)/i);
317
+ if (match) {
318
+ const brightness = parseFloat(match[1]);
319
+ const temperature = parseFloat(match[2]);
320
+
321
+ return [Math.round(brightness), Math.round(temperature)];
322
+ }
323
+
324
+ return undefined;
325
+ }
326
+ }
@@ -0,0 +1,201 @@
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 Daytimer controls.
7
+ */
8
+ export class Daytimer 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: 'timer',
22
+ },
23
+ native: { control },
24
+ });
25
+
26
+ await this.loadOtherControlStatesAsync(control.name, uuid, control.states, [
27
+ 'mode',
28
+ 'override',
29
+ 'value',
30
+ 'needsActivation',
31
+ 'modeList',
32
+ 'entriesAndDefaultValue',
33
+ 'resetActive',
34
+ ]);
35
+
36
+ await this.createSimpleControlStateObjectAsync(control.name, uuid, control.states, 'mode', 'number', 'value');
37
+ await this.createSimpleControlStateObjectAsync(
38
+ control.name,
39
+ uuid,
40
+ control.states,
41
+ 'override',
42
+ 'number',
43
+ 'value',
44
+ { unit: 'sec' },
45
+ );
46
+
47
+ // value is a number for analog daytimers and a boolean for digital ones
48
+ if (control.details.analog) {
49
+ await this.createSimpleControlStateObjectAsync(
50
+ control.name,
51
+ uuid,
52
+ control.states,
53
+ 'value',
54
+ 'number',
55
+ 'value',
56
+ );
57
+
58
+ if ('format' in control.details) {
59
+ await this.updateStateObjectAsync(
60
+ `${uuid}.value-formatted`,
61
+ {
62
+ name: `${control.name}: formatted value`,
63
+ read: true,
64
+ write: false,
65
+ type: 'string',
66
+ role: 'text',
67
+ // TODO: re-add: smartIgnore: true,
68
+ },
69
+ control.states.value,
70
+ async (name: string, value: any) => {
71
+ await this.setFormattedStateAck(name, value, control.details.format as string);
72
+ },
73
+ );
74
+ }
75
+ } else {
76
+ await this.createSimpleControlStateObjectAsync(
77
+ control.name,
78
+ uuid,
79
+ control.states,
80
+ 'value',
81
+ 'boolean',
82
+ 'indicator',
83
+ );
84
+
85
+ if ('text' in control.details) {
86
+ const text = control.details?.text as
87
+ | {
88
+ /** The text to display when the value is on */
89
+ on: string;
90
+ /** The text to display when the value is off */
91
+ off: string;
92
+ }
93
+ | undefined;
94
+ await this.updateStateObjectAsync(
95
+ `${uuid}.value-formatted`,
96
+ {
97
+ name: `${control.name}: formatted value`,
98
+ read: true,
99
+ write: false,
100
+ type: 'string',
101
+ role: 'text',
102
+ // TODO: re-add: smartIgnore: true,
103
+ },
104
+ control.states.value,
105
+ async (name: string, value: any) => {
106
+ await this.setStateAck(
107
+ name,
108
+ (this.convertStateToBoolean(value) ? text?.on : text?.off) || null,
109
+ );
110
+ },
111
+ );
112
+ }
113
+ }
114
+
115
+ await this.createSimpleControlStateObjectAsync(
116
+ control.name,
117
+ uuid,
118
+ control.states,
119
+ 'needsActivation',
120
+ 'boolean',
121
+ 'indicator',
122
+ );
123
+
124
+ await this.createSimpleControlStateObjectAsync(
125
+ control.name,
126
+ uuid,
127
+ control.states,
128
+ 'resetActive',
129
+ 'boolean',
130
+ 'indicator',
131
+ );
132
+
133
+ if ('mode' in control.states && 'modeList' in control.states) {
134
+ const obj: ioBroker.SettableObjectWorker<ioBroker.StateObject> = {
135
+ type: 'state',
136
+ common: {
137
+ name: `${control.name}: mode as text`,
138
+ read: true,
139
+ write: false,
140
+ type: 'string',
141
+ role: 'text',
142
+ // TODO: re-add: smartIgnore: true,
143
+ },
144
+ native: {
145
+ mode: control.states.mode,
146
+ modeList: control.states.modeList,
147
+ },
148
+ };
149
+ await this.updateObjectAsync(`${uuid}.mode-text`, obj);
150
+
151
+ let modeNames: Record<string, string> = {};
152
+ let mode = '-';
153
+ const updateModeText = async (): Promise<void> => {
154
+ if (mode in modeNames) {
155
+ await this.setStateAck(`${uuid}.mode-text`, modeNames[mode]);
156
+ }
157
+ };
158
+ this.addStateEventHandler(control.states.mode, async (value: ioBroker.StateValue) => {
159
+ mode = this.convertStateToInt(value).toString();
160
+ await updateModeText();
161
+ });
162
+ this.addStateEventHandler(control.states.modeList, async (value: ioBroker.StateValue) => {
163
+ if (!value) {
164
+ return;
165
+ }
166
+
167
+ // format of value: 0:mode=0;name=\"Feiertag\",1:mode=1;name=\"Urlaub\"
168
+ modeNames = value
169
+ .toString()
170
+ .split(',')
171
+ .map(item =>
172
+ item
173
+ .split(':', 2)[1]
174
+ .split(';')
175
+ .map(pair => pair.split('=', 2)),
176
+ )
177
+ .reduce(
178
+ (old, pairs) => {
179
+ const modePair = pairs.find(p => p[0] === 'mode');
180
+ const namePair = pairs.find(p => p[0] === 'name');
181
+ if (!modePair || !namePair) {
182
+ return old;
183
+ }
184
+ return { ...old, [modePair[1]]: namePair[1].replace(/^\\?"(.+?)\\?"$/g, '$1') };
185
+ },
186
+ {} as Record<string, string>,
187
+ );
188
+ await updateModeText();
189
+ });
190
+ }
191
+
192
+ await this.createButtonCommandStateObjectAsync(control.name, uuid, 'pulse');
193
+ this.addStateChangeListener(
194
+ `${uuid}.pulse`,
195
+ () => {
196
+ this.sendCommand(control.uuidAction, 'pulse');
197
+ },
198
+ { selfAck: true },
199
+ );
200
+ }
201
+ }
@@ -0,0 +1,64 @@
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 Dimmer controls.
8
+ */
9
+ export class Dimmer extends ControlBase {
10
+ /**
11
+ * Loads the control and sets up state objects and event handlers.
12
+ *
13
+ * @param type The type of the control ('device' or 'channel').
14
+ * @param uuid The unique identifier of the control.
15
+ * @param control The control data from the structure file.
16
+ */
17
+ async loadAsync(type: ControlType, uuid: string, control: Control): Promise<void> {
18
+ await this.updateObjectAsync(uuid, {
19
+ type: type,
20
+ common: {
21
+ name: control.name,
22
+ role: 'light',
23
+ },
24
+ native: { control },
25
+ });
26
+
27
+ await this.loadOtherControlStatesAsync(control.name, uuid, control.states, ['position', 'min', 'max', 'step']);
28
+
29
+ await this.createSimpleControlStateObjectAsync(
30
+ control.name,
31
+ uuid,
32
+ control.states,
33
+ 'position',
34
+ 'number',
35
+ 'level.dimmer',
36
+ { write: true },
37
+ );
38
+ await this.createSimpleControlStateObjectAsync(control.name, uuid, control.states, 'min', 'number', 'value');
39
+ await this.createSimpleControlStateObjectAsync(control.name, uuid, control.states, 'max', 'number', 'value');
40
+ await this.createSimpleControlStateObjectAsync(control.name, uuid, control.states, 'step', 'number', 'value');
41
+
42
+ this.addStateChangeListener(`${uuid}.position`, (oldValue: OldStateValue, newValue: CurrentStateValue) => {
43
+ this.sendCommand(control.uuidAction, this.convertStateToInt(newValue).toString());
44
+ });
45
+
46
+ await this.createButtonCommandStateObjectAsync(control.name, uuid, 'on');
47
+ this.addStateChangeListener(
48
+ `${uuid}.on`,
49
+ () => {
50
+ this.sendCommand(control.uuidAction, 'on');
51
+ },
52
+ { selfAck: true },
53
+ );
54
+
55
+ await this.createButtonCommandStateObjectAsync(control.name, uuid, 'off');
56
+ this.addStateChangeListener(
57
+ `${uuid}.off`,
58
+ () => {
59
+ this.sendCommand(control.uuidAction, 'off');
60
+ },
61
+ { selfAck: true },
62
+ );
63
+ }
64
+ }
@@ -0,0 +1,61 @@
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 EIBDimmer controls.
8
+ */
9
+ export class EIBDimmer extends ControlBase {
10
+ /**
11
+ * Loads the control and sets up state objects and event handlers.
12
+ *
13
+ * @param type The type of the control ('device' or 'channel').
14
+ * @param uuid The unique identifier of the control.
15
+ * @param control The control data from the structure file.
16
+ */
17
+ async loadAsync(type: ControlType, uuid: string, control: Control): Promise<void> {
18
+ await this.updateObjectAsync(uuid, {
19
+ type: type,
20
+ common: {
21
+ name: control.name,
22
+ role: 'light',
23
+ },
24
+ native: { control },
25
+ });
26
+
27
+ await this.loadOtherControlStatesAsync(control.name, uuid, control.states, ['position']);
28
+
29
+ await this.createSimpleControlStateObjectAsync(
30
+ control.name,
31
+ uuid,
32
+ control.states,
33
+ 'position',
34
+ 'number',
35
+ 'level.dimmer',
36
+ { write: true },
37
+ );
38
+
39
+ this.addStateChangeListener(`${uuid}.position`, (oldValue: OldStateValue, newValue: CurrentStateValue) => {
40
+ this.sendCommand(control.uuidAction, this.convertStateToInt(newValue).toString());
41
+ });
42
+
43
+ await this.createButtonCommandStateObjectAsync(control.name, uuid, 'on');
44
+ this.addStateChangeListener(
45
+ `${uuid}.on`,
46
+ () => {
47
+ this.sendCommand(control.uuidAction, 'on');
48
+ },
49
+ { selfAck: true },
50
+ );
51
+
52
+ await this.createButtonCommandStateObjectAsync(control.name, uuid, 'off');
53
+ this.addStateChangeListener(
54
+ `${uuid}.off`,
55
+ () => {
56
+ this.sendCommand(control.uuidAction, 'off');
57
+ },
58
+ { selfAck: true },
59
+ );
60
+ }
61
+ }