|
1 | 1 | /** |
2 | | - * Haushaltsgeräte-Überwachung PRO (v4.1.0 - Next Level) |
| 2 | + * Haushaltsgeräte-Überwachung PRO (v4.2.0 - Auto-Influx) |
3 | 3 | * - Prädiktive Zeitvorhersage (InfluxDB Durchschnitt) |
4 | 4 | * - Intelligentes Benachrichtigungs-Management (Quiet Hours & Reminder) |
5 | 5 | * - Anomalie-Erkennung (Sicherheits-Abschaltung/Alarm) |
6 | | - * - Auto-State Creation & InfluxDB 2.x Support |
| 6 | + * - Auto-State Creation & AUTO-INFLUX CONFIGURATION |
7 | 7 | */ |
8 | 8 |
|
9 | 9 | (async function() { |
|
72 | 72 | } |
73 | 73 | ]; |
74 | 74 |
|
75 | | - // --- 3. AKTIONEN (Smart Notifications) --- |
| 75 | + // --- 3. HILFSFUNKTIONEN --- |
| 76 | + |
| 77 | + // Automatische InfluxDB Aktivierung |
| 78 | + async function enableInfluxLogging(id) { |
| 79 | + try { |
| 80 | + const obj = await getObjectAsync(id); |
| 81 | + if (obj && obj.common) { |
| 82 | + const custom = obj.common.custom || {}; |
| 83 | + if (!custom[INFLUXDB_INSTANCE] || !custom[INFLUXDB_INSTANCE].enabled) { |
| 84 | + log(`${id}: Aktiviere InfluxDB-Logging automatisch.`); |
| 85 | + await extendObjectAsync(id, { |
| 86 | + common: { |
| 87 | + custom: { |
| 88 | + [INFLUXDB_INSTANCE]: { |
| 89 | + enabled: true, |
| 90 | + changesOnly: true, |
| 91 | + debounce: 0, |
| 92 | + retention: 0, |
| 93 | + changesRelativ: 0, |
| 94 | + changesMinDelta: 0, |
| 95 | + storageType: "", |
| 96 | + aliasId: "" |
| 97 | + } |
| 98 | + } |
| 99 | + } |
| 100 | + }); |
| 101 | + } |
| 102 | + } |
| 103 | + } catch (e) { |
| 104 | + log(`Fehler beim Aktivieren von InfluxDB für ${id}: ${e}`, 'warn'); |
| 105 | + } |
| 106 | + } |
| 107 | + |
76 | 108 | function isQuietTime() { |
77 | 109 | const now = new Date(); |
78 | 110 | const currentTime = now.getHours() * 60 + now.getMinutes(); |
|
89 | 121 | log("Nachtruhe aktiv: Benachrichtigung nur im Log."); |
90 | 122 | return; |
91 | 123 | } |
92 | | - // Beispiel für Telegram Integration: |
93 | | - // sendTo('telegram.0', { text: `*${title}*\n${message}`, parse_mode: 'Markdown' }); |
94 | 124 | } |
95 | 125 |
|
96 | 126 | // --- 4. SKRIPT-LOGIK --- |
|
107 | 137 | CURRENT_ENERGY: `${SCRIPT_DP_PATH}.Aktueller_Verbrauch_kWh`, |
108 | 138 | RUN_START_TIME: `${SCRIPT_DP_PATH}.Aktueller_Startzeitpunkt`, |
109 | 139 | RUN_START_ENERGY: `${SCRIPT_DP_PATH}.Aktueller_Energie_Startwert`, |
110 | | - // Next Level DPs |
111 | 140 | PREDICTED_END: `${SCRIPT_DP_PATH}.Voraussichtliches_Ende`, |
112 | 141 | REMAINING_TIME: `${SCRIPT_DP_PATH}.Restlaufzeit_Minuten`, |
113 | 142 | ANOMALY_ALARM: `${SCRIPT_DP_PATH}.Anomalie_Alarm`, |
114 | | - // Analyse |
115 | 143 | ANALYSE_START: `${SCRIPT_DP_PATH}.Analyse.Starten`, |
116 | 144 | ANALYSE_STATUS: `${SCRIPT_DP_PATH}.Analyse.Status`, |
117 | 145 | VORSCHLAG_START: `${SCRIPT_DP_PATH}.Analyse.Vorschlag_startingThreshold`, |
|
139 | 167 | await createStateAsync(DP.VORSCHLAG_FINISH, 0, { type: 'number', unit: 'W' }); |
140 | 168 | })(); |
141 | 169 |
|
142 | | - // Durchschnittliche Laufzeit aus InfluxDB ermitteln |
| 170 | + // Automatisches Logging für Quellen und notwendige Statistik-DPs aktivieren |
| 171 | + await enableInfluxLogging(config.powerSensorId); |
| 172 | + await enableInfluxLogging(config.energySensorId); |
| 173 | + await enableInfluxLogging(DP.LAST_RUN_DURATION); |
| 174 | + |
143 | 175 | async function getAverageDuration() { |
144 | 176 | return new Promise((resolve) => { |
145 | 177 | const end = Date.now(); |
146 | 178 | const start = end - (30 * 24 * 60 * 60 * 1000); |
147 | 179 | getHistory(INFLUXDB_INSTANCE, { id: DP.LAST_RUN_DURATION, start: start, end: end, aggregate: 'none' }, (err, result) => { |
148 | | - if (err || !result || result.length === 0) resolve(120); // Fallback 2h |
| 180 | + if (err || !result || result.length === 0) resolve(120); |
149 | 181 | const avg = result.reduce((a, b) => a + b.val, 0) / result.length; |
150 | 182 | resolve(Math.round(avg)); |
151 | 183 | }); |
|
154 | 186 |
|
155 | 187 | function updatePrediction(avgDuration) { |
156 | 188 | const startTime = getState(DP.RUN_START_TIME).val; |
| 189 | + if (!startTime) return; |
157 | 190 | const elapsed = (Date.now() - startTime) / 60000; |
158 | 191 | const remaining = Math.max(0, Math.round(avgDuration - elapsed)); |
159 | 192 | const end = new Date(Date.now() + remaining * 60000); |
|
177 | 210 | setState(DP.RUN_START_ENERGY, energyNow, true); |
178 | 211 | setState(DP.ANOMALY_ALARM, false, true); |
179 | 212 |
|
180 | | - // Vorhersage starten |
181 | 213 | const avg = await getAverageDuration(); |
182 | 214 | updatePrediction(avg); |
183 | 215 | predictionInterval = setInterval(() => updatePrediction(avg), 60000); |
184 | 216 |
|
185 | | - // Sicherheits-Timer |
186 | 217 | safetyTimer = setTimeout(() => { |
187 | 218 | setState(DP.ANOMALY_ALARM, true, true); |
188 | 219 | sendSmartNotification("ALARM", `${config.deviceName} läuft ungewöhnlich lange (> ${SAFETY_TIMEOUT_MIN} Min)! Bitte prüfen.`, 2); |
|
208 | 239 |
|
209 | 240 | sendSmartNotification(config.deviceName, `${config.deviceName} ist fertig! Dauer: ${durationMin.toFixed(0)} Min, Verbrauch: ${energyKWh.toFixed(2)} kWh.`, 1); |
210 | 241 |
|
211 | | - // Reminder-Timer starten |
212 | 242 | reminderTimer = setInterval(() => { |
213 | 243 | sendSmartNotification(config.deviceName, "Erinnerung: Die Wäsche liegt noch im Gerät.", 1); |
214 | 244 | }, REMINDER_INTERVAL_MIN * 60000); |
|
219 | 249 | const power = obj.state.val; |
220 | 250 | const state = getState(DP.STATUS).val; |
221 | 251 |
|
222 | | - // Aktuellen Verbrauch während des Laufs berechnen |
223 | 252 | if (state !== STATES.IDLE && state !== STATES.STARTING) { |
224 | 253 | const startE = getState(DP.RUN_START_ENERGY).val; |
225 | 254 | const currE = getState(config.energySensorId).val || 0; |
226 | 255 | setState(DP.CURRENT_ENERGY, parseFloat((currE - startE).toFixed(3)), true); |
227 | 256 | } |
228 | 257 |
|
229 | | - // Türöffnung / Entleerung erkennen (Power > 0.5W aber Status IDLE -> Reminder stoppen) |
230 | 258 | if (power > 0.5 && state === STATES.IDLE && reminderTimer) { |
231 | 259 | clearInterval(reminderTimer); |
232 | 260 | reminderTimer = null; |
|
250 | 278 | if (ns === STATES.PAUSED) pauseTimeout = setTimeout(() => setApplianceState(STATES.IDLE), runtimeConfig.pauseHysteresisMin * 60000); |
251 | 279 | }); |
252 | 280 |
|
253 | | - // Analyse bleibt wie im Original-Script (Histogramm-Methode) |
254 | 281 | on({id: DP.ANALYSE_START, val: true}, () => { |
255 | 282 | log(`Analyse für ${config.deviceName} gestartet...`); |
256 | 283 | const end = Date.now(); |
|
0 commit comments