Skip to content

Commit 8947ccd

Browse files
committed
Update / Modification from the production live system: 2026-03-05 12:15
1 parent befea5a commit 8947ccd

1 file changed

Lines changed: 0 additions & 283 deletions

File tree

Lines changed: 0 additions & 283 deletions
Original file line numberDiff line numberDiff line change
@@ -1,283 +0,0 @@
1-
/**
2-
* @file Intelligente & träge Anwesenheitserkennung im Bett (ioBroker)
3-
* @version 3.0 FINAL
4-
* @author Ihr Name
5-
* @license MIT
6-
* @description
7-
* Dieses Skript realisiert eine träge und intelligente Nachtschaltung. Es ist die
8-
* stabile und erweiterte ioBroker-Umsetzung der Logik aus dem Homematic-Forum.
9-
*
10-
* FUNKTIONSWEISE:
11-
* 1. Träge Anwesenheitserkennung: Ein kurzes Aufstehen (z.B. für ein Glas Wasser) wird
12-
* ignoriert. Der Status wird erst nach einer saisonal angepassten Verzögerung auf
13-
* "frei" gesetzt.
14-
* 2. Aktivierung bei zwei Personen: Die Nachtschaltung wird SOFORT aktiv, wenn beide
15-
* Personen bei Nacht im Bett sind.
16-
* 3. Aktivierung bei einer Person: Geht nur eine Person bei Nacht ins Bett, startet ein
17-
* 30-Minuten-Timer. Die Nachtschaltung wird erst nach Ablauf aktiv. Kommt die
18-
* zweite Person hinzu, wird der Timer abgebrochen und die Schaltung sofort aktiviert.
19-
* 4. Master-Reset durch Abwesenheit: Wenn niemand im Haus anwesend ist, werden alle
20-
* Timer sofort gestoppt und die Nachtschaltung deaktiviert, um Fehlzustände zu verhindern.
21-
*
22-
* @changelog
23-
* V3.0 (Final):
24-
* - Kommentare umfassend überarbeitet und ergänzt für maximale Verständlichkeit.
25-
* - Code-Struktur durch Hilfsfunktionen für Timer und Zustandsänderungen optimiert.
26-
* - Redundante Code-Teile entfernt und Logik für Klarheit neu geordnet.
27-
* - Visuelle Gliederung des Skripts für bessere Lesbarkeit verbessert.
28-
*/
29-
30-
// ======================================================================================
31-
// │ KONFIGURATION │
32-
// └────────────────────────────────────────────────────────────────────────────────────┘
33-
34-
// --- 1. DATENPUNKTE DER SENSOREN ---
35-
// IDs der STATE-Datenpunkte Ihrer Bett-Sensoren (z.B. Homematic Tür-/Fensterkontakte).
36-
// Annahme: false = belegt (Kontakt geschlossen), true = frei (Kontakt offen).
37-
const SENSOR_LINKS_DP = 'hm-rpc.0.001E1D899E94B0.1.STATE';
38-
const SENSOR_RECHTS_DP = 'hm-rpc.0.001E1D899E922D.1.STATE';
39-
40-
// --- 2. ZIEL-DATENPUNKTE IN IO-BROKER ---
41-
// IDs der Zieldatenpunkte, die das Skript steuert.
42-
// Werden bei Bedarf automatisch unter '0_userdata.0' angelegt.
43-
const STATUS_LINKS_VAR = '0_userdata.0.System.Nachtschaltung.BettLinks';
44-
const STATUS_RECHTS_VAR = '0_userdata.0.System.Nachtschaltung.BettRechts';
45-
const AKTIV_VAR = '0_userdata.0.System.Nachtschaltung.Aktiv';
46-
47-
// --- 3. STEUERNDE DATENPUNKTE ---
48-
// ID des Datenpunkts, der Tag (true) oder Nacht (false) signalisiert.
49-
const TAG_NACHT_VAR = '0_userdata.0.System.Astro.Tag';
50-
// ID des Datenpunkts für die globale Anwesenheit im Haus.
51-
const ANWESENHEIT_VAR = '0_userdata.0.Anwesenheit.Status'; // true = anwesend, false = abwesend
52-
53-
// --- 4. VERZÖGERUNGSZEITEN IN MINUTEN ---
54-
const DELAY_MINUTEN_SOMMER = 3; // Verzögerung für das Verlassen des Bettes (Juni - Sep)
55-
const DELAY_MINUTEN_WINTER = 4; // Verzögerung für das Verlassen des Bettes (Okt - Mai)
56-
const DELAY_MINUTEN_EINZEL = 30; // Verzögerung, bis die Schaltung für nur eine Person aktiv wird.
57-
58-
// --- 5. DEBUG-MODUS ---
59-
// Bei 'true' werden detaillierte Log-Ausgaben erzeugt.
60-
const DEBUG = true;
61-
62-
63-
// ======================================================================================
64-
// │ GLOBALE VARIABLEN & ZUSTÄNDE │
65-
// └────────────────────────────────────────────────────────────────────────────────────┘
66-
67-
const LOG_PREFIX = '[NACHTSCHALTUNG] '; // Prefix für alle Log-Ausgaben.
68-
69-
let timerLinks = null; // Hält die Timer-ID für die linke Bettseite.
70-
let timerRechts = null; // Hält die Timer-ID für die rechte Bettseite.
71-
let einzelTimer = null; // Hält die Timer-ID für die Einzelbelegungs-Logik.
72-
73-
74-
// ======================================================================================
75-
// │ HILFSFUNKTIONEN │
76-
// └────────────────────────────────────────────────────────────────────────────────────┘
77-
78-
/**
79-
* Setzt einen Datenpunkt nur dann, wenn sich der Wert geändert hat, und loggt die Aktion.
80-
* @param {string} id - Die ID des Datenpunkts.
81-
* @param {boolean | number | string} value - Der zu setzende Wert.
82-
*/
83-
async function setStateChangedAndLog(id, value) {
84-
if ((await getStateAsync(id)).val !== value) {
85-
if (DEBUG) log(`${LOG_PREFIX}AKTION: Setze '${id}' auf '${value}'.`);
86-
await setStateAsync(id, value, true);
87-
}
88-
}
89-
90-
/**
91-
* Stoppt und löscht alle laufenden Timer im Skript.
92-
* Nützlich als zentraler Reset-Mechanismus.
93-
*/
94-
function clearAllTimers() {
95-
if (timerLinks) {
96-
clearTimeout(timerLinks);
97-
timerLinks = null;
98-
if (DEBUG) log(`${LOG_PREFIX}TIMER-STOP: Abwesenheits-Timer (Links) abgebrochen.`);
99-
}
100-
if (timerRechts) {
101-
clearTimeout(timerRechts);
102-
timerRechts = null;
103-
if (DEBUG) log(`${LOG_PREFIX}TIMER-STOP: Abwesenheits-Timer (Rechts) abgebrochen.`);
104-
}
105-
if (einzelTimer) {
106-
clearTimeout(einzelTimer);
107-
einzelTimer = null;
108-
if (DEBUG) log(`${LOG_PREFIX}TIMER-STOP: Einzelbelegungs-Timer abgebrochen.`);
109-
}
110-
}
111-
112-
113-
// ======================================================================================
114-
// │ SKRIPT-INITIALISIERUNG │
115-
// └────────────────────────────────────────────────────────────────────────────────────┘
116-
117-
/**
118-
* Stellt sicher, dass alle benötigten Datenpunkte existieren.
119-
* Fehlende Datenpunkte werden mit Standardwerten angelegt.
120-
*/
121-
async function initializeDataPoints() {
122-
const statesToCreate = {
123-
[STATUS_LINKS_VAR]: { name: 'Status Bett Links (belegt/frei)', type: 'boolean', def: false, role: 'state.switch' },
124-
[STATUS_RECHTS_VAR]: { name: 'Status Bett Rechts (belegt/frei)', type: 'boolean', def: false, role: 'state.switch' },
125-
[AKTIV_VAR]: { name: 'Nachtschaltung aktiv', type: 'boolean', def: false, role: 'switch' }
126-
};
127-
128-
for (const [id, config] of Object.entries(statesToCreate)) {
129-
if (!(await existsStateAsync(id))) {
130-
log(`${LOG_PREFIX}Datenpunkt '${id}' wird erstellt.`, 'info');
131-
await createStateAsync(id, {
132-
type: config.type, name: config.name, def: config.def,
133-
read: true, write: true, role: config.role
134-
});
135-
}
136-
}
137-
138-
// Überprüfung der externen Steuerungs-Datenpunkte
139-
const externalDps = [TAG_NACHT_VAR, ANWESENHEIT_VAR];
140-
for (const dp of externalDps) {
141-
if (!(await existsStateAsync(dp))) {
142-
log(`${LOG_PREFIX}WARNUNG: Der Steuer-Datenpunkt '${dp}' existiert nicht. Das Skript funktioniert möglicherweise nicht korrekt.`, 'warn');
143-
}
144-
}
145-
}
146-
147-
148-
// ======================================================================================
149-
// │ KERNLOGIK │
150-
// └────────────────────────────────────────────────────────────────────────────────────┘
151-
152-
/**
153-
* Verarbeitet die Status-Änderung eines einzelnen Bett-Sensors.
154-
* @param {'links' | 'rechts'} side - Die Bettseite, die aktualisiert wird.
155-
* @param {boolean} sensorValue - Der vom Sensor gemeldete Wert (true=offen, false=geschlossen).
156-
*/
157-
async function processSensor(side, sensorValue) {
158-
const statusVar = side === 'links' ? STATUS_LINKS_VAR : STATUS_RECHTS_VAR;
159-
let timerRef = side === 'links' ? timerLinks : timerRechts;
160-
const isBelegt = !sensorValue; // Sensor 'offen' (true) bedeutet, Bett ist 'nicht belegt' (false)
161-
162-
if (isBelegt) {
163-
// Person ist im Bett oder zurückgekehrt.
164-
if (timerRef) {
165-
clearTimeout(timerRef);
166-
if (side === 'links') timerLinks = null; else timerRechts = null;
167-
if (DEBUG) log(`${LOG_PREFIX}TIMER-STOP (${side.toUpperCase()}): Person ist zurück. Abwesenheits-Timer gestoppt.`);
168-
}
169-
await setStateChangedAndLog(statusVar, true);
170-
} else {
171-
// Person hat das Bett verlassen.
172-
if ((await getStateAsync(statusVar)).val) {
173-
const currentMonth = new Date().getMonth() + 1; // 1 (Jan) - 12 (Dez)
174-
const delayMinuten = (currentMonth >= 6 && currentMonth <= 9) ? DELAY_MINUTEN_SOMMER : DELAY_MINUTEN_WINTER;
175-
const delayMilliseconds = delayMinuten * 60 * 1000;
176-
177-
if (DEBUG) log(`${LOG_PREFIX}TIMER-START (${side.toUpperCase()}): Person hat Bett verlassen. Starte ${delayMinuten}min Abwesenheits-Timer.`);
178-
179-
const newTimer = setTimeout(async () => {
180-
if (DEBUG) log(`${LOG_PREFIX}TIMER-ENDE (${side.toUpperCase()}): Verzögerung abgelaufen. Setze Status auf 'frei'.`);
181-
await setStateChangedAndLog(statusVar, false);
182-
if (side === 'links') timerLinks = null; else timerRechts = null;
183-
}, delayMilliseconds);
184-
185-
if (side === 'links') timerLinks = newTimer; else timerRechts = newTimer;
186-
}
187-
}
188-
}
189-
190-
/**
191-
* Die zentrale Funktion, die den finalen Status der Nachtschaltung berechnet und setzt.
192-
* Wird bei jeder relevanten Zustandsänderung aufgerufen.
193-
*/
194-
async function updateNightMode() {
195-
// 1. Alle relevanten Zustände einlesen
196-
const isBelegtLinks = (await getStateAsync(STATUS_LINKS_VAR)).val;
197-
const isBelegtRechts = (await getStateAsync(STATUS_RECHTS_VAR)).val;
198-
const isNacht = !(await getStateAsync(TAG_NACHT_VAR)).val;
199-
const isAnwesend = (await getStateAsync(ANWESENHEIT_VAR)).val;
200-
const isAktivCurrent = (await getStateAsync(AKTIV_VAR)).val;
201-
202-
if (DEBUG) log(`${LOG_PREFIX}ANALYSE: Links=${isBelegtLinks}, Rechts=${isBelegtRechts}, Nacht=${isNacht}, Anwesend=${isAnwesend}, AktuellAktiv=${isAktivCurrent}`);
203-
204-
// --- ENTSCHEIDUNGS-LOGIK ---
205-
206-
// MASTER-REGEL: Bei Tag oder wenn niemand anwesend ist, wird die Nachtschaltung immer beendet.
207-
if (!isNacht || !isAnwesend) {
208-
clearAllTimers();
209-
await setStateChangedAndLog(AKTIV_VAR, false);
210-
return; // Verarbeitung hier beenden
211-
}
212-
213-
// REGELN FÜR DIE NACHT BEI ANWESENHEIT:
214-
const beideBelegt = isBelegtLinks && isBelegtRechts;
215-
const einerBelegt = isBelegtLinks || isBelegtRechts;
216-
217-
if (beideBelegt) {
218-
// Fall 1: Beide Personen im Bett -> Sofort aktivieren.
219-
if (DEBUG) log(`${LOG_PREFIX}LOGIK: Beide Personen im Bett. Aktiviere Nachtschaltung sofort.`);
220-
clearAllTimers();
221-
await setStateChangedAndLog(AKTIV_VAR, true);
222-
} else if (einerBelegt) {
223-
// Fall 2: Genau eine Person im Bett.
224-
if (!isAktivCurrent && !einzelTimer) {
225-
// Wenn Schaltung noch aus ist und kein Timer läuft, starte den 30-Minuten-Timer.
226-
const delayMilliseconds = DELAY_MINUTEN_EINZEL * 60 * 1000;
227-
if (DEBUG) log(`${LOG_PREFIX}LOGIK: Eine Person im Bett (von inaktiv). Starte ${DELAY_MINUTEN_EINZEL}min Timer.`);
228-
einzelTimer = setTimeout(async () => {
229-
if (DEBUG) log(`${LOG_PREFIX}TIMER-ENDE (EINZEL): ${DELAY_MINUTEN_EINZEL}min abgelaufen. Aktiviere Nachtschaltung.`);
230-
einzelTimer = null;
231-
await setStateChangedAndLog(AKTIV_VAR, true);
232-
}, delayMilliseconds);
233-
}
234-
// Falls bereits aktiv (weil zweite Person aufgestanden ist), bleibt der Zustand erhalten.
235-
} else {
236-
// Fall 3: Niemand im Bett -> Immer deaktivieren.
237-
if (DEBUG) log(`${LOG_PREFIX}LOGIK: Niemand mehr im Bett. Deaktiviere Nachtschaltung.`);
238-
clearAllTimers();
239-
await setStateChangedAndLog(AKTIV_VAR, false);
240-
}
241-
}
242-
243-
244-
// ======================================================================================
245-
// │ TRIGGER-DEFINITIONEN │
246-
// └────────────────────────────────────────────────────────────────────────────────────┘
247-
248-
// Trigger für die einzelnen Bett-Sensoren
249-
on({ id: SENSOR_LINKS_DP, change: 'ne' }, async (obj) => {
250-
if (DEBUG) log(`${LOG_PREFIX}TRIGGER: Sensor Links meldet: ${obj.state.val}`);
251-
await processSensor('links', obj.state.val);
252-
});
253-
254-
on({ id: SENSOR_RECHTS_DP, change: 'ne' }, async (obj) => {
255-
if (DEBUG) log(`${LOG_PREFIX}TRIGGER: Sensor Rechts meldet: ${obj.state.val}`);
256-
await processSensor('rechts', obj.state.val);
257-
});
258-
259-
// Ein kombinierter Trigger für die finale Logik, um unnötige Aufrufe zu vermeiden.
260-
const finalLogicTrigger = [STATUS_LINKS_VAR, STATUS_RECHTS_VAR, TAG_NACHT_VAR, ANWESENHEIT_VAR];
261-
on({ id: finalLogicTrigger, change: 'ne' }, async (obj) => {
262-
if (DEBUG) log(`${LOG_PREFIX}TRIGGER: Eine für die Logik relevante Variable hat sich geändert -> ${obj.id}`);
263-
await updateNightMode();
264-
});
265-
266-
267-
// ======================================================================================
268-
// │ SKRIPT-START & STOP │
269-
// └────────────────────────────────────────────────────────────────────────────────────┘
270-
271-
onStop(function () {
272-
clearAllTimers();
273-
log(`${LOG_PREFIX}Skript gestoppt.`, 'info');
274-
});
275-
276-
// Hauptfunktion, die beim Skriptstart ausgeführt wird.
277-
(async function main() {
278-
await initializeDataPoints();
279-
log(`${LOG_PREFIX}Skript gestartet.`, 'info');
280-
// Beim Start einmal die Logik ausführen, um den korrekten Zustand sicherzustellen.
281-
await updateNightMode();
282-
})();
283-

0 commit comments

Comments
 (0)