iobroker.loxone 3.0.1 → 4.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +183 -183
- package/README.md +421 -411
- package/admin/i18n/de.json +15 -0
- package/admin/i18n/en.json +15 -0
- package/admin/i18n/es.json +18 -0
- package/admin/i18n/fr.json +18 -0
- package/admin/i18n/it.json +18 -0
- package/admin/i18n/nl.json +18 -0
- package/admin/i18n/pl.json +18 -0
- package/admin/i18n/pt.json +18 -0
- package/admin/i18n/ru.json +18 -0
- package/admin/i18n/uk.json +18 -0
- package/admin/i18n/zh-cn.json +18 -0
- package/admin/jsonConfig.json +96 -0
- package/io-package.json +60 -49
- package/package.json +36 -49
- package/src/controls/AalEmergency.ts +90 -0
- package/src/controls/AalSmartAlarm.ts +99 -0
- package/src/controls/Alarm.ts +146 -0
- package/src/controls/AlarmClock.ts +137 -0
- package/src/controls/Application.ts +13 -0
- package/src/controls/AudioZone.ts +227 -0
- package/src/controls/AudioZoneV2.ts +135 -0
- package/src/controls/CentralAlarm.ts +59 -0
- package/src/controls/CentralAudioZone.ts +41 -0
- package/src/controls/CentralGate.ts +53 -0
- package/src/controls/CentralJalousie.ts +67 -0
- package/src/controls/CentralLightController.ts +45 -0
- package/src/controls/ColorPickerV2.ts +34 -0
- package/src/controls/Colorpicker.ts +30 -0
- package/src/controls/ColorpickerBase.ts +326 -0
- package/src/controls/Daytimer.ts +201 -0
- package/src/controls/Dimmer.ts +64 -0
- package/src/controls/EIBDimmer.ts +61 -0
- package/src/controls/Fronius.ts +217 -0
- package/src/controls/Gate.ts +150 -0
- package/src/controls/Hourcounter.ts +115 -0
- package/src/controls/IRCDaytimer.ts +4 -0
- package/src/controls/IRCV2Daytimer.ts +4 -0
- package/src/controls/IRoomControllerV2.ts +595 -0
- package/src/controls/InfoOnlyAnalog.ts +56 -0
- package/src/controls/InfoOnlyDigital.ts +95 -0
- package/src/controls/InfoOnlyText.ts +52 -0
- package/{build/controls/Intercom.js → src/controls/Intercom.ts} +27 -11
- package/src/controls/Jalousie.ts +219 -0
- package/src/controls/LightController.ts +112 -0
- package/src/controls/LightControllerV2.ts +246 -0
- package/src/controls/MailBox.ts +92 -0
- package/src/controls/Meter.ts +94 -0
- package/src/controls/None.ts +29 -0
- package/src/controls/PresenceDetector.ts +47 -0
- package/src/controls/Pushbutton.ts +55 -0
- package/src/controls/Radio.ts +61 -0
- package/{build/controls/Remote.js → src/controls/Remote.ts} +19 -11
- package/src/controls/Slider.ts +78 -0
- package/src/controls/SmokeAlarm.ts +163 -0
- package/src/controls/Switch.ts +46 -0
- package/src/controls/SystemScheme.ts +13 -0
- package/src/controls/TextInput.ts +96 -0
- package/src/controls/TextState.ts +37 -0
- package/src/controls/TimedSwitch.ts +75 -0
- package/src/controls/Tracker.ts +30 -0
- package/{build/controls/Unknown.js → src/controls/Unknown.ts} +18 -11
- package/src/controls/UpDownAnalog.ts +78 -0
- package/{build/controls/ValueSelector.js → src/controls/ValueSelector.ts} +21 -9
- package/src/controls/WindowMonitor.ts +139 -0
- package/src/controls/all-controls.ts +99 -0
- package/src/controls/control-base.ts +28 -0
- package/src/lib/adapter-config.d.ts +22 -0
- package/src/loxone-handler-base.ts +285 -0
- package/src/lxcommunicator.d.ts +1 -0
- package/src/main.test.ts +24 -0
- package/{build/main.js → src/main.ts} +516 -310
- package/src/structure-file.d.ts +154 -0
- package/src/weather-server-handler.ts +266 -0
- package/admin/admin.d.ts +0 -58
- package/admin/index_m.html +0 -126
- package/admin/style.css +0 -32
- package/admin/words.js +0 -25
- package/build/controls/AalEmergency.js +0 -45
- package/build/controls/AalEmergency.js.map +0 -1
- package/build/controls/AalSmartAlarm.js +0 -48
- package/build/controls/AalSmartAlarm.js.map +0 -1
- package/build/controls/Alarm.js +0 -81
- package/build/controls/Alarm.js.map +0 -1
- package/build/controls/AlarmClock.js +0 -59
- package/build/controls/AlarmClock.js.map +0 -1
- package/build/controls/Application.js +0 -11
- package/build/controls/Application.js.map +0 -1
- package/build/controls/AudioZone.js +0 -116
- package/build/controls/AudioZone.js.map +0 -1
- package/build/controls/CentralAlarm.js +0 -30
- package/build/controls/CentralAlarm.js.map +0 -1
- package/build/controls/CentralAudioZone.js +0 -22
- package/build/controls/CentralAudioZone.js.map +0 -1
- package/build/controls/CentralGate.js +0 -30
- package/build/controls/CentralGate.js.map +0 -1
- package/build/controls/CentralJalousie.js +0 -39
- package/build/controls/CentralJalousie.js.map +0 -1
- package/build/controls/CentralLightController.js +0 -27
- package/build/controls/CentralLightController.js.map +0 -1
- package/build/controls/ColorPickerV2.js +0 -24
- package/build/controls/ColorPickerV2.js.map +0 -1
- package/build/controls/Colorpicker.js +0 -20
- package/build/controls/Colorpicker.js.map +0 -1
- package/build/controls/ColorpickerBase.js +0 -263
- package/build/controls/ColorpickerBase.js.map +0 -1
- package/build/controls/Daytimer.js +0 -119
- package/build/controls/Daytimer.js.map +0 -1
- package/build/controls/Dimmer.js +0 -34
- package/build/controls/Dimmer.js.map +0 -1
- package/build/controls/EIBDimmer.js +0 -31
- package/build/controls/EIBDimmer.js.map +0 -1
- package/build/controls/Fronius.js +0 -64
- package/build/controls/Fronius.js.map +0 -1
- package/build/controls/Gate.js +0 -104
- package/build/controls/Gate.js.map +0 -1
- package/build/controls/Hourcounter.js +0 -46
- package/build/controls/Hourcounter.js.map +0 -1
- package/build/controls/IRCDaytimer.js +0 -9
- package/build/controls/IRCDaytimer.js.map +0 -1
- package/build/controls/IRCV2Daytimer.js +0 -9
- package/build/controls/IRCV2Daytimer.js.map +0 -1
- package/build/controls/IRoomControllerV2.js +0 -371
- package/build/controls/IRoomControllerV2.js.map +0 -1
- package/build/controls/InfoOnlyAnalog.js +0 -38
- package/build/controls/InfoOnlyAnalog.js.map +0 -1
- package/build/controls/InfoOnlyDigital.js +0 -65
- package/build/controls/InfoOnlyDigital.js.map +0 -1
- package/build/controls/InfoOnlyText.js +0 -35
- package/build/controls/InfoOnlyText.js.map +0 -1
- package/build/controls/Intercom.js.map +0 -1
- package/build/controls/Jalousie.js +0 -153
- package/build/controls/Jalousie.js.map +0 -1
- package/build/controls/LightController.js +0 -68
- package/build/controls/LightController.js.map +0 -1
- package/build/controls/LightControllerV2.js +0 -195
- package/build/controls/LightControllerV2.js.map +0 -1
- package/build/controls/MailBox.js +0 -41
- package/build/controls/MailBox.js.map +0 -1
- package/build/controls/Meter.js +0 -52
- package/build/controls/Meter.js.map +0 -1
- package/build/controls/None.js +0 -23
- package/build/controls/None.js.map +0 -1
- package/build/controls/PresenceDetector.js +0 -29
- package/build/controls/PresenceDetector.js.map +0 -1
- package/build/controls/Pushbutton.js +0 -35
- package/build/controls/Pushbutton.js.map +0 -1
- package/build/controls/Radio.js +0 -39
- package/build/controls/Radio.js.map +0 -1
- package/build/controls/Remote.js.map +0 -1
- package/build/controls/Slider.js +0 -44
- package/build/controls/Slider.js.map +0 -1
- package/build/controls/SmokeAlarm.js +0 -85
- package/build/controls/SmokeAlarm.js.map +0 -1
- package/build/controls/Switch.js +0 -31
- package/build/controls/Switch.js.map +0 -1
- package/build/controls/SystemScheme.js +0 -11
- package/build/controls/SystemScheme.js.map +0 -1
- package/build/controls/TextInput.js +0 -55
- package/build/controls/TextInput.js.map +0 -1
- package/build/controls/TextState.js +0 -20
- package/build/controls/TextState.js.map +0 -1
- package/build/controls/TimedSwitch.js +0 -36
- package/build/controls/TimedSwitch.js.map +0 -1
- package/build/controls/Tracker.js +0 -20
- package/build/controls/Tracker.js.map +0 -1
- package/build/controls/Unknown.js.map +0 -1
- package/build/controls/UpDownAnalog.js +0 -44
- package/build/controls/UpDownAnalog.js.map +0 -1
- package/build/controls/ValueSelector.js.map +0 -1
- package/build/controls/WindowMonitor.js +0 -84
- package/build/controls/WindowMonitor.js.map +0 -1
- package/build/controls/control-base.js +0 -12
- package/build/controls/control-base.js.map +0 -1
- package/build/loxone-handler-base.js +0 -186
- package/build/loxone-handler-base.js.map +0 -1
- package/build/main.js.map +0 -1
- package/build/weather-server-handler.js +0 -120
- package/build/weather-server-handler.js.map +0 -1
- package/doc/details-missing-control-type.png +0 -0
- package/doc/log-missing-control-type.png +0 -0
- package/doc/loxone-config-display-diagnostics.png +0 -0
- package/doc/loxone-config-info-only-digital.png +0 -0
- 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
|
+
}
|