iobroker.acinfinity 0.3.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 +21 -0
- package/README.md +86 -0
- package/admin/acinfinity.png +0 -0
- package/admin/index_m.html +151 -0
- package/admin/jsonConfig.json +77 -0
- package/admin/words.js +54 -0
- package/io-package.json +97 -0
- package/lib/client.js +625 -0
- package/lib/constants.js +173 -0
- package/lib/creators/deviceCreator.js +202 -0
- package/lib/creators/portCreator.js +536 -0
- package/lib/creators/stateCreator.js +177 -0
- package/lib/dataModels.js +135 -0
- package/lib/handlers/deviceSettingsHandler.js +163 -0
- package/lib/handlers/portModeHandler.js +630 -0
- package/lib/handlers/portSettingsHandler.js +212 -0
- package/lib/stateManager.js +313 -0
- package/lib/updaters/deviceUpdater.js +202 -0
- package/lib/updaters/portUpdater.js +527 -0
- package/main.js +325 -0
- package/package.json +62 -0
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PortSettingsHandler für AC Infinity Adapter
|
|
3
|
+
* Verarbeitet Änderungen an den erweiterten Port-Einstellungen
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
"use strict";
|
|
7
|
+
|
|
8
|
+
const {
|
|
9
|
+
ADVANCED_SETTINGS_KEY,
|
|
10
|
+
DEVICE_LOAD_TYPE_OPTIONS,
|
|
11
|
+
DYNAMIC_RESPONSE_OPTIONS
|
|
12
|
+
} = require('../constants');
|
|
13
|
+
|
|
14
|
+
class PortSettingsHandler {
|
|
15
|
+
/**
|
|
16
|
+
* Erstellt einen neuen PortSettingsHandler
|
|
17
|
+
* @param {object} stateManager - Referenz zum StateManager
|
|
18
|
+
*/
|
|
19
|
+
constructor(stateManager) {
|
|
20
|
+
this.stateManager = stateManager;
|
|
21
|
+
this.adapter = stateManager.adapter;
|
|
22
|
+
this.client = null; // API-Client-Referenz wird später gesetzt
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Setzt den API-Client für diesen Handler
|
|
27
|
+
* @param {object} client - AC Infinity API-Client
|
|
28
|
+
*/
|
|
29
|
+
setClient(client) {
|
|
30
|
+
this.client = client;
|
|
31
|
+
this.adapter.log.debug("API-Client erfolgreich an PortSettingsHandler übergeben");
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Verarbeitet Änderungen an erweiterten Port-Einstellungen
|
|
36
|
+
* @param {string} deviceId - Geräte-ID
|
|
37
|
+
* @param {number} portId - Port-ID
|
|
38
|
+
* @param {Array} path - Pfadkomponenten
|
|
39
|
+
* @param {any} value - Neuer Wert
|
|
40
|
+
*/
|
|
41
|
+
async handlePortAdvancedSettingsChange(deviceId, portId, path, value) {
|
|
42
|
+
if (path.length < 1) {
|
|
43
|
+
this.adapter.log.warn(`Ungültiger Pfad für erweiterte Port-Einstellungen: ${path.join('.')}`);
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const setting = path[0];
|
|
48
|
+
const statePath = `devices.${deviceId}.ports.${portId}.settings.${setting}`;
|
|
49
|
+
|
|
50
|
+
// UI sofort aktualisieren, um Flackern zu vermeiden
|
|
51
|
+
await this.stateManager.updateUIState(statePath, value);
|
|
52
|
+
|
|
53
|
+
try {
|
|
54
|
+
// Prüfen, ob der Client gesetzt ist
|
|
55
|
+
if (!this.client) {
|
|
56
|
+
throw new Error("API-Client nicht gesetzt. Kann Port-Einstellungen nicht aktualisieren.");
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Hole den Port-Namen für den Aktualisierungsaufruf
|
|
60
|
+
let portName = await this.adapter.getStateAsync(`devices.${deviceId}.ports.${portId}.info.name`);
|
|
61
|
+
portName = portName && portName.val ? portName.val : `Port ${portId}`;
|
|
62
|
+
|
|
63
|
+
// Aktuelle Temperatureinheit abrufen
|
|
64
|
+
const tempUnitState = await this.adapter.getStateAsync(`devices.${deviceId}.settings.temperatureUnit`);
|
|
65
|
+
const isUnitC = tempUnitState && tempUnitState.val === "C";
|
|
66
|
+
|
|
67
|
+
this.adapter.log.debug(`Verarbeite Port-Einstellungsänderung: deviceId=${deviceId}, portId=${portId}, setting=${setting}, value=${value}`);
|
|
68
|
+
|
|
69
|
+
try {
|
|
70
|
+
switch (setting) {
|
|
71
|
+
case 'deviceType':
|
|
72
|
+
// Finde die Last-Typ-ID für den angegebenen Gerätetyp-Namen
|
|
73
|
+
let loadTypeId = null;
|
|
74
|
+
for (const [id, name] of Object.entries(DEVICE_LOAD_TYPE_OPTIONS)) {
|
|
75
|
+
if (name === value) {
|
|
76
|
+
loadTypeId = parseInt(id);
|
|
77
|
+
break;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (loadTypeId !== null) {
|
|
82
|
+
this.adapter.log.debug(`Sende Gerätetyp an API: deviceId=${deviceId}, portId=${portId}, type=${value}, loadTypeId=${loadTypeId}`);
|
|
83
|
+
await this.client.updateAdvancedSettings(deviceId, portId, portName, [
|
|
84
|
+
[ADVANCED_SETTINGS_KEY.DEVICE_LOAD_TYPE, loadTypeId]
|
|
85
|
+
]);
|
|
86
|
+
this.adapter.log.debug(`API-Antwort für Gerätetyp erfolgreich`);
|
|
87
|
+
} else {
|
|
88
|
+
this.adapter.log.warn(`Ungültiger Gerätetyp: ${value}`);
|
|
89
|
+
}
|
|
90
|
+
break;
|
|
91
|
+
|
|
92
|
+
case 'dynamicResponse':
|
|
93
|
+
const responseTypeIndex = DYNAMIC_RESPONSE_OPTIONS.indexOf(value);
|
|
94
|
+
if (responseTypeIndex >= 0) {
|
|
95
|
+
this.adapter.log.debug(`Sende dynamischen Antworttyp an API: deviceId=${deviceId}, portId=${portId}, type=${value}, index=${responseTypeIndex}`);
|
|
96
|
+
await this.client.updateAdvancedSettings(deviceId, portId, portName, [
|
|
97
|
+
[ADVANCED_SETTINGS_KEY.DYNAMIC_RESPONSE_TYPE, responseTypeIndex]
|
|
98
|
+
]);
|
|
99
|
+
this.adapter.log.debug(`API-Antwort für dynamischen Antworttyp erfolgreich`);
|
|
100
|
+
} else {
|
|
101
|
+
this.adapter.log.warn(`Ungültiger dynamischer Antworttyp: ${value}`);
|
|
102
|
+
}
|
|
103
|
+
break;
|
|
104
|
+
|
|
105
|
+
case 'dynamicTransitionTemp':
|
|
106
|
+
const transitionTempValue = parseInt(value);
|
|
107
|
+
this.adapter.log.debug(`Sende dynamische Übergangstemperatur an API: deviceId=${deviceId}, portId=${portId}, temp=${transitionTempValue}, isUnitC=${isUnitC}`);
|
|
108
|
+
|
|
109
|
+
if (isUnitC) {
|
|
110
|
+
await this.client.updateAdvancedSettings(deviceId, portId, portName, [
|
|
111
|
+
[ADVANCED_SETTINGS_KEY.DYNAMIC_TRANSITION_TEMP, transitionTempValue],
|
|
112
|
+
[ADVANCED_SETTINGS_KEY.DYNAMIC_TRANSITION_TEMP_F, transitionTempValue * 2]
|
|
113
|
+
]);
|
|
114
|
+
} else {
|
|
115
|
+
await this.client.updateAdvancedSettings(deviceId, portId, portName, [
|
|
116
|
+
[ADVANCED_SETTINGS_KEY.DYNAMIC_TRANSITION_TEMP, Math.floor(transitionTempValue / 2)],
|
|
117
|
+
[ADVANCED_SETTINGS_KEY.DYNAMIC_TRANSITION_TEMP_F, transitionTempValue]
|
|
118
|
+
]);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
this.adapter.log.debug(`API-Antwort für dynamische Übergangstemperatur erfolgreich`);
|
|
122
|
+
break;
|
|
123
|
+
|
|
124
|
+
case 'dynamicBufferTemp':
|
|
125
|
+
const bufferTempValue = parseInt(value);
|
|
126
|
+
this.adapter.log.debug(`Sende dynamische Puffertemperatur an API: deviceId=${deviceId}, portId=${portId}, temp=${bufferTempValue}, isUnitC=${isUnitC}`);
|
|
127
|
+
|
|
128
|
+
if (isUnitC) {
|
|
129
|
+
await this.client.updateAdvancedSettings(deviceId, portId, portName, [
|
|
130
|
+
[ADVANCED_SETTINGS_KEY.DYNAMIC_BUFFER_TEMP, bufferTempValue],
|
|
131
|
+
[ADVANCED_SETTINGS_KEY.DYNAMIC_BUFFER_TEMP_F, bufferTempValue * 2]
|
|
132
|
+
]);
|
|
133
|
+
} else {
|
|
134
|
+
await this.client.updateAdvancedSettings(deviceId, portId, portName, [
|
|
135
|
+
[ADVANCED_SETTINGS_KEY.DYNAMIC_BUFFER_TEMP, Math.floor(bufferTempValue / 2)],
|
|
136
|
+
[ADVANCED_SETTINGS_KEY.DYNAMIC_BUFFER_TEMP_F, bufferTempValue]
|
|
137
|
+
]);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
this.adapter.log.debug(`API-Antwort für dynamische Puffertemperatur erfolgreich`);
|
|
141
|
+
break;
|
|
142
|
+
|
|
143
|
+
// Weitere Fälle für andere Einstellungen...
|
|
144
|
+
case 'dynamicTransitionHumidity':
|
|
145
|
+
this.adapter.log.debug(`Sende dynamische Übergangsfeuchtigkeit an API: deviceId=${deviceId}, portId=${portId}, humidity=${parseInt(value)}`);
|
|
146
|
+
await this.client.updateAdvancedSettings(deviceId, portId, portName, [
|
|
147
|
+
[ADVANCED_SETTINGS_KEY.DYNAMIC_TRANSITION_HUMIDITY, parseInt(value)]
|
|
148
|
+
]);
|
|
149
|
+
this.adapter.log.debug(`API-Antwort für dynamische Übergangsfeuchtigkeit erfolgreich`);
|
|
150
|
+
break;
|
|
151
|
+
|
|
152
|
+
case 'dynamicBufferHumidity':
|
|
153
|
+
this.adapter.log.debug(`Sende dynamische Pufferfeuchtigkeit an API: deviceId=${deviceId}, portId=${portId}, humidity=${parseInt(value)}`);
|
|
154
|
+
await this.client.updateAdvancedSettings(deviceId, portId, portName, [
|
|
155
|
+
[ADVANCED_SETTINGS_KEY.DYNAMIC_BUFFER_HUMIDITY, parseInt(value)]
|
|
156
|
+
]);
|
|
157
|
+
this.adapter.log.debug(`API-Antwort für dynamische Pufferfeuchtigkeit erfolgreich`);
|
|
158
|
+
break;
|
|
159
|
+
|
|
160
|
+
case 'dynamicTransitionVPD':
|
|
161
|
+
const transitionVpdValue = Math.round(parseFloat(value) * 10);
|
|
162
|
+
this.adapter.log.debug(`Sende dynamisches Übergangs-VPD an API: deviceId=${deviceId}, portId=${portId}, vpd=${value}, scaledValue=${transitionVpdValue}`);
|
|
163
|
+
await this.client.updateAdvancedSettings(deviceId, portId, portName, [
|
|
164
|
+
[ADVANCED_SETTINGS_KEY.DYNAMIC_TRANSITION_VPD, transitionVpdValue]
|
|
165
|
+
]);
|
|
166
|
+
this.adapter.log.debug(`API-Antwort für dynamisches Übergangs-VPD erfolgreich`);
|
|
167
|
+
break;
|
|
168
|
+
|
|
169
|
+
case 'dynamicBufferVPD':
|
|
170
|
+
const bufferVpdValue = Math.round(parseFloat(value) * 10);
|
|
171
|
+
this.adapter.log.debug(`Sende dynamisches Puffer-VPD an API: deviceId=${deviceId}, portId=${portId}, vpd=${value}, scaledValue=${bufferVpdValue}`);
|
|
172
|
+
await this.client.updateAdvancedSettings(deviceId, portId, portName, [
|
|
173
|
+
[ADVANCED_SETTINGS_KEY.DYNAMIC_BUFFER_VPD, bufferVpdValue]
|
|
174
|
+
]);
|
|
175
|
+
this.adapter.log.debug(`API-Antwort für dynamisches Puffer-VPD erfolgreich`);
|
|
176
|
+
break;
|
|
177
|
+
|
|
178
|
+
case 'sunriseTimerEnabled':
|
|
179
|
+
this.adapter.log.debug(`Sende Sonnenaufgang/Sonnenuntergang-Timer-Aktivierung an API: deviceId=${deviceId}, portId=${portId}, enabled=${value}`);
|
|
180
|
+
await this.client.updateAdvancedSettings(deviceId, portId, portName, [
|
|
181
|
+
[ADVANCED_SETTINGS_KEY.SUNRISE_TIMER_ENABLED, value ? 1 : 0]
|
|
182
|
+
]);
|
|
183
|
+
this.adapter.log.debug(`API-Antwort für Sonnenaufgang/Sonnenuntergang-Timer-Aktivierung erfolgreich`);
|
|
184
|
+
break;
|
|
185
|
+
|
|
186
|
+
case 'sunriseTimerMinutes':
|
|
187
|
+
this.adapter.log.debug(`Sende Sonnenaufgang/Sonnenuntergang-Timer-Minuten an API: deviceId=${deviceId}, portId=${portId}, minutes=${parseInt(value)}`);
|
|
188
|
+
await this.client.updateAdvancedSettings(deviceId, portId, portName, [
|
|
189
|
+
[ADVANCED_SETTINGS_KEY.SUNRISE_TIMER_DURATION, parseInt(value)]
|
|
190
|
+
]);
|
|
191
|
+
this.adapter.log.debug(`API-Antwort für Sonnenaufgang/Sonnenuntergang-Timer-Minuten erfolgreich`);
|
|
192
|
+
break;
|
|
193
|
+
|
|
194
|
+
default:
|
|
195
|
+
this.adapter.log.warn(`Unbekannte erweiterte Port-Einstellung: ${setting}`);
|
|
196
|
+
}
|
|
197
|
+
} catch (error) {
|
|
198
|
+
this.adapter.log.error(`API-Fehler beim Aktualisieren der Port-Einstellung ${setting}: ${error.message}`);
|
|
199
|
+
if (error.response) {
|
|
200
|
+
this.adapter.log.error(`API-Antwort: ${JSON.stringify(error.response.data || {})}`);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Aktualisierung der Daten auslösen, um die Zustände zu aktualisieren
|
|
205
|
+
await this.stateManager.refreshWithThrottle();
|
|
206
|
+
} catch (error) {
|
|
207
|
+
this.adapter.log.error(`Fehler beim Aktualisieren erweiterter Port-Einstellungen: ${error.message}`);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
module.exports = PortSettingsHandler;
|
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* StateManager für AC Infinity Adapter
|
|
3
|
+
* Zentrale Klasse zum Verwalten von Zuständen für AC Infinity-Geräte
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
"use strict";
|
|
7
|
+
|
|
8
|
+
// Importiere Handler-Module
|
|
9
|
+
const DeviceSettingsHandler = require('./handlers/deviceSettingsHandler');
|
|
10
|
+
const PortSettingsHandler = require('./handlers/portSettingsHandler');
|
|
11
|
+
const PortModeHandler = require('./handlers/portModeHandler');
|
|
12
|
+
|
|
13
|
+
// Importiere Creator-Module
|
|
14
|
+
const StateCreator = require('./creators/stateCreator');
|
|
15
|
+
const DeviceCreator = require('./creators/deviceCreator');
|
|
16
|
+
const PortCreator = require('./creators/portCreator');
|
|
17
|
+
|
|
18
|
+
// Importiere Updater-Module
|
|
19
|
+
const DeviceUpdater = require('./updaters/deviceUpdater');
|
|
20
|
+
const PortUpdater = require('./updaters/portUpdater');
|
|
21
|
+
|
|
22
|
+
class StateManager {
|
|
23
|
+
/**
|
|
24
|
+
* Erstellt einen neuen StateManager
|
|
25
|
+
* @param {object} adapter - ioBroker-Adapter-Instanz
|
|
26
|
+
*/
|
|
27
|
+
constructor(adapter) {
|
|
28
|
+
this.adapter = adapter;
|
|
29
|
+
this.deviceStates = new Map(); // Verfolgt erstellte Gerätezustände
|
|
30
|
+
this._refreshPending = false;
|
|
31
|
+
this._refreshTimer = null;
|
|
32
|
+
this.client = null; // API-Client-Referenz
|
|
33
|
+
|
|
34
|
+
// Initialisiere Handler
|
|
35
|
+
this.deviceSettingsHandler = new DeviceSettingsHandler(this);
|
|
36
|
+
this.portSettingsHandler = new PortSettingsHandler(this);
|
|
37
|
+
this.portModeHandler = new PortModeHandler(this);
|
|
38
|
+
|
|
39
|
+
// Initialisiere Creator
|
|
40
|
+
this.stateCreator = new StateCreator(this);
|
|
41
|
+
this.deviceCreator = new DeviceCreator(this);
|
|
42
|
+
this.portCreator = new PortCreator(this);
|
|
43
|
+
|
|
44
|
+
// Initialisiere Updater
|
|
45
|
+
this.deviceUpdater = new DeviceUpdater(this);
|
|
46
|
+
this.portUpdater = new PortUpdater(this);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Setzt den API-Client und gibt ihn an die Handler weiter
|
|
51
|
+
* @param {object} client - AC Infinity API-Client
|
|
52
|
+
*/
|
|
53
|
+
setClient(client) {
|
|
54
|
+
this.client = client;
|
|
55
|
+
|
|
56
|
+
// Gib den Client an alle Handler weiter
|
|
57
|
+
this.deviceSettingsHandler.setClient(client);
|
|
58
|
+
this.portSettingsHandler.setClient(client);
|
|
59
|
+
this.portModeHandler.setClient(client);
|
|
60
|
+
|
|
61
|
+
this.adapter.log.debug("API client successfully passed to StateManager and handlers");
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Initialisiert Zustände für alle Geräte
|
|
66
|
+
* @param {Array} devices - Array mit Geräteobjekten
|
|
67
|
+
*/
|
|
68
|
+
async initializeDevices(devices) {
|
|
69
|
+
for (const device of devices) {
|
|
70
|
+
const deviceId = device.devId;
|
|
71
|
+
this.adapter.log.info(`Initializing device: ${device.devName} (ID: ${deviceId})`);
|
|
72
|
+
|
|
73
|
+
// Erstelle Geräteobjekt
|
|
74
|
+
await this.deviceCreator.createDeviceObject(deviceId, device.devName);
|
|
75
|
+
|
|
76
|
+
// Erstelle Controller-Info-Kanal
|
|
77
|
+
await this.deviceCreator.createInfoChannel(deviceId);
|
|
78
|
+
|
|
79
|
+
// Erstelle Controller-Sensor-Zustände
|
|
80
|
+
await this.deviceCreator.createSensorChannel(deviceId);
|
|
81
|
+
|
|
82
|
+
// Erstelle Ports
|
|
83
|
+
const ports = device.deviceInfo.ports;
|
|
84
|
+
if (Array.isArray(ports)) {
|
|
85
|
+
for (const port of ports) {
|
|
86
|
+
const portId = port.port;
|
|
87
|
+
await this.portCreator.createPortChannel(deviceId, portId, port.portName);
|
|
88
|
+
}
|
|
89
|
+
} else {
|
|
90
|
+
this.adapter.log.warn(`No ports found for device ${deviceId}`);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Aktualisiere Gerätedaten mit neuesten Werten
|
|
97
|
+
* @param {object} device - Geräteobjekt
|
|
98
|
+
*/
|
|
99
|
+
async updateDeviceData(device) {
|
|
100
|
+
const deviceId = device.devId;
|
|
101
|
+
this.adapter.log.debug(`Updating device data for ${deviceId}`);
|
|
102
|
+
|
|
103
|
+
// Aktualisiere Gerätedaten über den DeviceUpdater
|
|
104
|
+
await this.deviceUpdater.updateDeviceData(deviceId, device);
|
|
105
|
+
|
|
106
|
+
// Aktualisiere Port-Zustände
|
|
107
|
+
if (device.deviceInfo && Array.isArray(device.deviceInfo.ports)) {
|
|
108
|
+
for (const port of device.deviceInfo.ports) {
|
|
109
|
+
const portId = port.port;
|
|
110
|
+
await this.portUpdater.updatePortData(deviceId, portId, port);
|
|
111
|
+
}
|
|
112
|
+
} else {
|
|
113
|
+
this.adapter.log.warn(`No ports found in device data for ${deviceId}`);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Aktualisiere Port-Einstellungen
|
|
119
|
+
* @param {string|number} deviceId - Geräte-ID
|
|
120
|
+
* @param {number} portId - Port-ID
|
|
121
|
+
* @param {object} settings - Einstellungsobjekt
|
|
122
|
+
*/
|
|
123
|
+
async updatePortSettings(deviceId, portId, settings) {
|
|
124
|
+
await this.portUpdater.updatePortSettings(deviceId, portId, settings);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Aktualisiere erweiterte Einstellungen
|
|
129
|
+
* @param {string|number} deviceId - Geräte-ID
|
|
130
|
+
* @param {number} portId - Port-ID (0 für Controller-Einstellungen)
|
|
131
|
+
* @param {object} settings - Einstellungsobjekt
|
|
132
|
+
*/
|
|
133
|
+
async updateAdvancedSettings(deviceId, portId, settings) {
|
|
134
|
+
if (portId === 0) {
|
|
135
|
+
// Controller-Einstellungen
|
|
136
|
+
await this.deviceUpdater.updateAdvancedSettings(deviceId, settings);
|
|
137
|
+
} else {
|
|
138
|
+
// Port-Einstellungen
|
|
139
|
+
await this.portUpdater.updateAdvancedPortSettings(deviceId, portId, settings);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Verarbeitet eine vom Benutzer initiierte Zustandsänderung
|
|
145
|
+
* @param {string} id - Zustands-ID
|
|
146
|
+
* @param {object} state - Zustandsobjekt
|
|
147
|
+
*/
|
|
148
|
+
async handleStateChange(id, state) {
|
|
149
|
+
if (!state || state.ack) {
|
|
150
|
+
// Ignoriere bestätigte Zustandsänderungen oder Zustände, die nicht existieren
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
this.adapter.log.debug(`Processing user state change: ${id} = ${state.val}`);
|
|
155
|
+
|
|
156
|
+
try {
|
|
157
|
+
// Überprüfen, ob der Client gesetzt ist
|
|
158
|
+
if (!this.client) {
|
|
159
|
+
throw new Error("API client not set. Cannot process state change.");
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Parse ID, um Geräte-, Port- und Parameterinformationen zu erhalten
|
|
163
|
+
const idParts = id.split('.');
|
|
164
|
+
|
|
165
|
+
// Entferne Adaptername und Instanz vom Pfad
|
|
166
|
+
let path;
|
|
167
|
+
if (idParts[0] === this.adapter.name && idParts[1].match(/^\d+$/)) {
|
|
168
|
+
// ID ist vollständig (z.B. "acinfinity.0.devices.1234")
|
|
169
|
+
path = idParts.slice(2);
|
|
170
|
+
} else {
|
|
171
|
+
// ID ist bereits ohne Präfix (z.B. "devices.1234")
|
|
172
|
+
path = idParts;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
this.adapter.log.debug(`Processing path parts: ${JSON.stringify(path)}`);
|
|
176
|
+
|
|
177
|
+
if (path[0] !== 'devices' || path.length < 4) {
|
|
178
|
+
this.adapter.log.warn(`Received state change with invalid path: ${id}`);
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const deviceId = path[1];
|
|
183
|
+
|
|
184
|
+
// Verarbeite Geräteebenen-Einstellungen
|
|
185
|
+
if (path[2] === 'settings') {
|
|
186
|
+
await this.deviceSettingsHandler.handleDeviceSettingsChange(deviceId, path.slice(3), state.val);
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Verarbeite Port-Ebenen-Einstellungen
|
|
191
|
+
if (path[2] === 'ports' && path.length >= 5) {
|
|
192
|
+
const portId = parseInt(path[3]);
|
|
193
|
+
const subPath = path.slice(4);
|
|
194
|
+
|
|
195
|
+
// Leite die Änderung an den richtigen Handler weiter
|
|
196
|
+
if (subPath[0] === 'mode') {
|
|
197
|
+
this.adapter.log.info(`Processing mode change: Device ${deviceId}, Port ${portId}, Path ${subPath.join('.')}, Value ${state.val}`);
|
|
198
|
+
await this.portModeHandler.handlePortModeChange(deviceId, portId, subPath.slice(1), state.val);
|
|
199
|
+
} else if (subPath[0] === 'settings') {
|
|
200
|
+
this.adapter.log.info(`Processing settings change: Device ${deviceId}, Port ${portId}, Path ${subPath.join('.')}, Value ${state.val}`);
|
|
201
|
+
await this.portSettingsHandler.handlePortAdvancedSettingsChange(deviceId, portId, subPath.slice(1), state.val);
|
|
202
|
+
} else {
|
|
203
|
+
this.adapter.log.warn(`Unknown port settings category: ${subPath[0]}`);
|
|
204
|
+
}
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
this.adapter.log.warn(`Unprocessed state change: ${id}`);
|
|
209
|
+
} catch (error) {
|
|
210
|
+
this.adapter.log.error(`Error processing state change: ${error.message}`);
|
|
211
|
+
if (error.stack) {
|
|
212
|
+
this.adapter.log.debug(`Stack trace: ${error.stack}`);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Helfermethode zum Erstellen eines Zustands
|
|
219
|
+
* Wird vom StateCreator verwendet
|
|
220
|
+
* @param {string} id - Zustands-ID
|
|
221
|
+
* @param {string} name - Zustandsname
|
|
222
|
+
* @param {string} type - Zustandstyp
|
|
223
|
+
* @param {string} role - Zustandsrolle
|
|
224
|
+
* @param {string|null} unit - Zustandseinheit
|
|
225
|
+
* @param {boolean} write - Zustand beschreibbar
|
|
226
|
+
* @param {Array|null} states - Zustandswerte (für Dropdown)
|
|
227
|
+
* @param {object|null} common - Zusätzliche Common-Eigenschaften
|
|
228
|
+
*/
|
|
229
|
+
async createState(id, name, type, role, unit = null, write = false, states = null, common = null) {
|
|
230
|
+
return this.stateCreator.createState(id, name, type, role, unit, write, states, common);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Aktualisiere einen Zustandswert
|
|
235
|
+
* @param {string} id - Zustands-ID
|
|
236
|
+
* @param {any} value - Zustandswert
|
|
237
|
+
*/
|
|
238
|
+
async updateState(id, value) {
|
|
239
|
+
try {
|
|
240
|
+
// Prüfe, ob Wert null oder undefined ist, in diesem Fall nicht aktualisieren
|
|
241
|
+
if (value === null || value === undefined) {
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// Für Debug-Zwecke
|
|
246
|
+
this.adapter.log.debug(`Updating state ${id} with value ${value}`);
|
|
247
|
+
|
|
248
|
+
await this.adapter.setStateAsync(id, { val: value, ack: true });
|
|
249
|
+
} catch (error) {
|
|
250
|
+
this.adapter.log.error(`Error updating state ${id}: ${error.message}`);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Hilfsmethode, um Änderungen im UI temporär zu aktualisieren, bevor die tatsächliche Aktualisierung stattfindet
|
|
256
|
+
* Dies verhindert, dass das UI zurückspringt, solange die API-Anfrage bearbeitet wird
|
|
257
|
+
* @param {string} id - Zustands-ID
|
|
258
|
+
* @param {any} value - Zustandswert
|
|
259
|
+
*/
|
|
260
|
+
async updateUIState(id, value) {
|
|
261
|
+
try {
|
|
262
|
+
// Mit false ack markieren, aber in der UI anzeigen
|
|
263
|
+
await this.adapter.setStateAsync(id, { val: value, ack: false });
|
|
264
|
+
} catch (error) {
|
|
265
|
+
this.adapter.log.warn(`Error temporarily updating UI state ${id}: ${error.message}`);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Verzögert die Ausführung einer Funktion
|
|
271
|
+
* @param {number} ms - Verzögerung in Millisekunden
|
|
272
|
+
* @returns {Promise} - Promise, das nach der Verzögerung aufgelöst wird
|
|
273
|
+
*/
|
|
274
|
+
async delay(ms) {
|
|
275
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Aktualisiert alle Daten mit Ratenbegrenzung
|
|
280
|
+
* Um Überflutung der API zu vermeiden
|
|
281
|
+
*/
|
|
282
|
+
async refreshWithThrottle() {
|
|
283
|
+
if (this._refreshPending) {
|
|
284
|
+
this.adapter.log.debug("Update already in progress, skipping");
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// Falls noch ein ausstehender Timer existiert, diesen löschen
|
|
289
|
+
if (this._refreshTimer) {
|
|
290
|
+
clearTimeout(this._refreshTimer);
|
|
291
|
+
this._refreshTimer = null;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
this._refreshPending = true;
|
|
295
|
+
try {
|
|
296
|
+
// Warte einen Moment, um die API nicht zu überlasten
|
|
297
|
+
await this.delay(2000);
|
|
298
|
+
await this.adapter.updateDeviceData();
|
|
299
|
+
} catch (error) {
|
|
300
|
+
this.adapter.log.error(`Error during delayed data update: ${error.message}`);
|
|
301
|
+
} finally {
|
|
302
|
+
// Wichtig: Flag nach Abschluss zurücksetzen, auch bei Fehlern
|
|
303
|
+
// Verwende einen Timer um sicherzustellen, dass genug Zeit zwischen Updates ist
|
|
304
|
+
this._refreshTimer = setTimeout(() => {
|
|
305
|
+
this._refreshPending = false;
|
|
306
|
+
this._refreshTimer = null;
|
|
307
|
+
this.adapter.log.debug("Update lock released");
|
|
308
|
+
}, 3000); // Wartezeit, um übermäßige Aufrufe zu verhindern
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
module.exports = StateManager;
|