@gluecksmann
Ich habe hier mal das Skript etwas umgeschrieben. Du kannst es als ein neues anlegen und mal ausprobieren. Die Erklärung und was das Skript macht, habe ich geschrieben.
/**
* Haustür-Lampensteuerung mit:
* - Dunkelheitsprüfung (nur bei dunkel Türöffnung wird Licht hochgezogen)
* - Speichern des vorherigen Lampen-Levels (persistenter State)
* - Rücksetzen beim Schließen (mit Verzögerung)
* - Entprellung
* - Schutz gegen Überschneidungen
* - Logging
* - Nur der persistente State wird bei Bedarf erstellt; andere States werden vorausgesetzt, es wird aber gewarnt, wenn sie fehlen.
* - Verwendung von "number" mit -1 als Marker für "kein gespeicherter Wert"
*/
// === Konfiguration / Konstanten ===
const doorSensor = 'alias.0.Sensor Haustür.opened'; // Trigger: Tür geöffnet/geschlossen
const hueDoorLevel = 'hue.0.Haustür.level'; // Lichtlevel Haustürlampe
const messageLampOn = 'hue.0.Nachrichtenlampe.on'; // Nachrichtenlampe
const savedLevelState = 'javascript.0.Aussenlampe_Zustand'; // Persistenter interner Speicher, -1 = leer
// === Einstellungen für Robustheit ===
const DEBOUNCE_MS = 2000; // Entprellzeit in ms
let busy = false;
let lastEventTime = 0;
// Logging ein/aus
const enableLogging = true;
function logInfo(msg) {
if (enableLogging) log(`[Haustür-Script] ${msg}`, 'info');
}
function logWarn(msg) {
if (enableLogging) log(`[Haustür-Script] ${msg}`, 'warn');
}
// === Helfer ===
// Prüft, ob das Level eine valide Zahl zwischen 0 und 100 ist
function isValidLevel(lvl) {
return typeof lvl === 'number' && lvl >= 0 && lvl <= 100;
}
// Prüft, ob es gerade dunkel ist (also NICHT zwischen sunrise und sunset)
function isDark() {
return !compareTime(getAstroDate('sunrise', undefined, 0), getAstroDate('sunset', undefined, 0), 'between', null);
}
// === State sicherstellen (nur der persistente State) ===
if (!existsState(savedLevelState)) {
createState(savedLevelState, -1, {
read: true,
write: true,
name: 'Gespeichertes Haustür-Level vor Änderung',
role: 'level',
type: 'number',
def: -1,
min: -1,
max: 100
});
logInfo(`State "${savedLevelState}" (number mit -1 als Leerwert) wurde angelegt.`);
}
// === Hauptlistener auf Tür-Statusänderung ===
on({ id: doorSensor, change: 'ne' }, async (obj) => {
const now = Date.now();
// Entprellung: zu schnelle Wiederholungen ignorieren
if (now - lastEventTime < DEBOUNCE_MS) {
logInfo('Event verworfen wegen Entprellung.');
return;
}
lastEventTime = now;
// Schutz gegen gleichzeitige Ausführung
if (busy) {
logWarn('Übersprungen: Script ist noch in Bearbeitung.');
return;
}
busy = true;
try {
const isOpen = obj.state.val === true;
const stored = getState(savedLevelState)?.val; // -1 heißt leer
const currentLevelObj = getState(hueDoorLevel);
const currentLevel = currentLevelObj?.val;
if (isOpen) {
logInfo('Tür geöffnet erkannt.');
if (isDark()) {
logInfo('Es ist dunkel – Lampenstatus wird angepasst.');
// Vorheriges Level nur speichern, wenn noch nichts gespeichert ist und aktuelles Level valide ist
if (stored === -1 && isValidLevel(currentLevel)) {
setState(savedLevelState, currentLevel, true);
logInfo(`Vorheriges Level gespeichert: ${currentLevel}`);
} else if (stored !== -1) {
logInfo(`Vorheriger Level war bereits gespeichert: ${stored}`);
} else if (!isValidLevel(currentLevel)) {
logWarn('Kein gültiger aktueller Level zum Speichern vorhanden.');
}
// Haustür-Lampe auf 100 setzen
if (existsState(hueDoorLevel)) {
setState(hueDoorLevel, 100);
logInfo('Haustür-Lampe auf 100 gesetzt.');
} else {
logWarn(`State "${hueDoorLevel}" existiert nicht. Kann Lampen-Level nicht setzen.`);
}
// Nachrichtenlampe einschalten
if (existsState(messageLampOn)) {
setState(messageLampOn, true);
logInfo('Nachrichtenlampe eingeschaltet.');
} else {
logWarn(`State "${messageLampOn}" existiert nicht. Kann Nachrichtenlampe nicht setzen.`);
}
} else {
logInfo('Es ist Tag – Nachrichtenlampe aus und gespeicherten Level verwerfen.');
if (existsState(messageLampOn)) {
setState(messageLampOn, false);
logInfo('Nachrichtenlampe ausgeschaltet.');
} else {
logWarn(`State "${messageLampOn}" existiert nicht. Kann Nachrichtenlampe nicht setzen.`);
}
// Gespeicherten Level löschen (auf -1 setzen)
setState(savedLevelState, -1, true);
logInfo('Gespeicherter Level zurückgesetzt (Tag-Modus).');
}
} else {
logInfo('Tür geschlossen erkannt.');
if (stored !== -1) {
if (existsState(hueDoorLevel)) {
logInfo(`Haustür-Lampe wird in 2 Minuten auf gespeicherten Wert (${stored}) zurückgesetzt.`);
setStateDelayed(hueDoorLevel, stored, 120000, false);
} else {
logWarn(`State "${hueDoorLevel}" existiert nicht. Kann Lampen-Level nicht zurücksetzen.`);
}
// Gespeicherten Level wieder auf "leer" setzen
setState(savedLevelState, -1, true);
logInfo('Gespeicherter Level danach gelöscht.');
} else {
logInfo('Kein gespeicherter Level vorhanden – nichts zum Zurücksetzen.');
}
// Nachrichtenlampe nach Verzögerung aus
if (existsState(messageLampOn)) {
setStateDelayed(messageLampOn, false, 45000, false);
logInfo('Nachrichtenlampe wird nach 45 Sekunden ausgeschaltet.');
} else {
logWarn(`State "${messageLampOn}" existiert nicht. Kann Nachrichtenlampe nicht setzen.`);
}
}
} catch (e) {
logWarn(`Fehler im Script: ${e.message}`);
} finally {
busy = false;
}
});
Erklärung:
Was macht das Script insgesamt?
Wenn die Haustür geöffnet wird, prüft das Script:
-
Ist es dunkel draußen?
-
Ja: Dann merkt es sich, wie hell die Haustür-Lampe vorher war, schaltet sie auf 100% und aktiviert die Nachrichtenlampe.
-
Nein (also Tag): Es tut nur: schaltet die Nachrichtenlampe aus und verwirft einen alten gespeicherten Zustand (falls einer da war).
Wenn die Tür wieder geschlossen wird, stellt es nach einer kurzen Verzögerung (2 Minuten) die Haustür-Lampe auf den vorherigen Wert zurück, falls dieser gespeichert war, und schaltet die Nachrichtenlampe nach 45 Sekunden aus.
Wichtige Zusatzfunktionen (für Stabilität & Verständlichkeit)
-
Entprellung: Der Türsensor kann manchmal sehr schnell mehrfach hintereinander melden „offen/zu“. Das Script ignoriert solche Schnellfolgen, wenn sie in weniger als 2 Sekunden passieren, damit nichts durcheinandergerät.
-
Verarbeitungsschutz: Wenn das Script gerade dabei ist, eine Aktion durchzuführen, dann wartet es ab und führt nicht gleichzeitig nochmal etwas aus. Das verhindert unsauberes Überschreiben.
-
Speichern des vorherigen Levels: Der vorherige Lampenwert wird nicht nur im flüchtigen Speicher (also nur während dieses Laufes) behalten, sondern in einem echten ioBroker-State (javascript.0.Aussenlampe_Zustand
), der auch nach einem Neustart des Scripts erhalten bleibt.
-
Logging: Das Script schreibt (wenn aktiviert) in das ioBroker-Protokoll, was passiert: z. B. „Tür geöffnet“, „Es ist dunkel, Lampe hochsetzen“, „Level gespeichert“, „Zurücksetzen beim Schließen“ usw. Damit kann man im Log nachvollziehen, warum etwas passiert ist oder nicht.
Was kann der Nutzer tun?