Nachdem ich in einer Wohnung smarte Beleuchtung inkl. adaptivem Licht (Dank GitHub - basnijholt/adaptive-lighting: Adaptive Lighting custom component for Home Assistant) erlebt habe, sind normale Glühbirnen gefühlte Steinzeit.
Über so Tatsachen wie, dass ich aktuell Schalter drücken muss, möchte ich dabei schon gar nicht sprechen … (Meist bleibt also das Licht aus … )
Und weil dauerhaft Dunkel keine Lösung ist, muss wieder smarte Beleuchtung her. Hier aber das Problem mit dem Wife acceptance factor (WAF) und der Frage nach dem Fallback.
Nur Shelly um dumme Glühbirnen zu schalten sind also keine Lösung.
Nur smarte Beleuchtung, die bei Ausfall von HA nicht funktionieren sind auf Grund des WAF keine (gesunde) Alternative.
Also habe ich beides gemacht!
Ich habe hinter den Lichtschalter einen Shelly 1 Mini Gen 3 verbaut und diesen auf detached gestellt. Dadurch lässt er dauerhaft den Strom durch, meldet aber an Home Assistant den Zustand von input 0, den ich zu Schalter umbenannt habe.
In der Fassung hängt eine smarte Lampe, die durch den detached Mode dauerhaft mit Strom versorgt wird.
Wird nun der Schalter betätigt, meldet der Shelly dies an Home Assistant und via Automation wird die Lampe ein- oder ausgeschaltet.
So weit, so unspektakulär. Denn in diesem Setup lässt sich die Lampe nicht mehr schalten, wenn Home Assistant down ist. (In diesem Fall auch, wenn mein WLAN down ist, weil aktuell WiZ Lampen verwendet werden … Und das WLAN ist öfter kurz tot, weil der Router neu gestartet werden muss, wenn dessen Verbindung mal wieder zusammenbricht … Eine andere HA Automatisierung regelt …)
Für den Fall des Ausfalls kommt nun ein Shelly Script ins Spiel …
// Shelly 1 (mini) Toggle/Detached Script
// Automatischer Wechsel zwischen Toggle- und Detached-Modus mit Optimierung und Konfigurations- und Logging-Variablen
// === Globale Konstanten ===
const RELAY_ID = 0;
const INPUT_ID = 0;
const HA_URL = "http://192.168.1.10:8123";
const POLL_INTERVAL = 10000; // in Millisekunden
const DEBUG = false; // true = alle Logs, false = nur Errors & wichtige Infos
// === Logging-Funktionen ===
function logDebug() {
if (DEBUG) print.apply(null, arguments);
}
function logInfo() {
print.apply(null, arguments);
}
function logError() {
print.apply(null, arguments);
}
// === Detached-Modus: nur schalten, wenn nötig ===
function activateDetachedMode() {
logDebug("Versuche, den Detached Modus zu aktivieren...");
Shelly.call("Switch.GetConfig", { id: RELAY_ID }, function(cfg, cfgErr) {
if (cfgErr) {
logError("Fehler beim Abfragen des Modus:", JSON.stringify(cfgErr));
return;
}
if (cfg.in_mode !== "detached") {
logInfo("Wechsle Modus von", cfg.in_mode, "zu detached");
Timer.set(1000, false, function() {
Shelly.call("Switch.SetConfig", {
id: RELAY_ID,
config: {
in_mode: "detached",
swap_inputs: false,
initial_state: "on"
}
}, function(setCfgRes, setCfgErr) {
if (setCfgErr) {
logError("Fehler beim Setzen des Detached Modus:", JSON.stringify(setCfgErr));
} else {
logInfo("Detached-Modus aktiviert. Relais wird eingeschaltet.");
Shelly.call("Switch.Set", { id: RELAY_ID, on: true }, function(setRes, setErr) {
if (setErr) logError("Fehler beim Einschalten im Detached Modus:", JSON.stringify(setErr));
else logInfo("Relais ist jetzt EIN im Detached Modus.");
});
}
});
});
} else {
// Bereits detached: prüfe Status und schalte nur, wenn aus
Shelly.call("Switch.GetStatus", { id: RELAY_ID }, function(stat, statErr) {
if (statErr) {
logError("Fehler beim Abfragen des Relais-Status:", JSON.stringify(statErr));
} else if (!stat.output) {
logInfo("Relais ist AUS; schalte jetzt EIN.");
Shelly.call("Switch.Set", { id: RELAY_ID, on: true }, function(r, e) {
if (e) logError("Fehler beim Einschalten:", JSON.stringify(e));
else logInfo("Relais ist jetzt EIN im Detached Modus.");
});
} else {
logDebug("Relais ist bereits EIN.");
}
});
}
});
}
// === Toggle/Follow-Modus: Relais sofort an Input 0 anpassen ===
function activateFollowMode() {
logDebug("Versuche, den Toggle-Modus zu aktivieren...");
Shelly.call("Switch.GetConfig", { id: RELAY_ID }, function(cfg, cfgErr) {
if (cfgErr) {
logError("Fehler beim Abfragen des Modus:", JSON.stringify(cfgErr));
return;
}
if (cfg.in_mode !== "follow" || cfg.in_type !== "switch" || cfg.initial_state !== "match_input") {
logInfo("Aktualisiere Toggle-Modus. Aktuell:", cfg);
Timer.set(1000, false, function() {
Shelly.call("Switch.SetConfig", {
id: RELAY_ID,
config: {
in_mode: "follow",
in_type: "switch",
initial_state: "match_input"
}
}, function(setRes, setErr) {
if (setErr) {
logError("Fehler beim Setzen des Toggle Modus:", JSON.stringify(setErr));
} else {
logInfo("Toggle-Modus aktiviert; passe Relais an Input 0 an.");
Shelly.call("Input.GetStatus", { id: INPUT_ID }, function(inRes, inErr) {
if (inErr) {
logError("Fehler beim Lesen von Input 0:", JSON.stringify(inErr));
} else if (inRes && inRes.state !== undefined) {
let desired = !!inRes.state;
logInfo("Input 0 State =", inRes.state, "→ Relais auf", desired ? "EIN" : "AUS");
Shelly.call("Switch.Set", { id: RELAY_ID, on: desired }, function(sRes, sErr) {
if (sErr) logError("Fehler beim Setzen des Relais:", JSON.stringify(sErr));
else logDebug("Relais gesetzt auf", desired);
});
} else {
logError("Ungültige Antwort von Input.GetStatus:", JSON.stringify(inRes));
}
});
}
});
});
} else {
logDebug("Follow/Toggle-Modus bereits korrekt konfiguriert.");
}
});
}
// === Home Assistant Status prüfen und alle POLL-Calls durch Timer kontrollieren ===
function checkHomeAssistantStatus() {
logDebug("Prüfe Home Assistant Erreichbarkeit...");
Shelly.call("http.get", { url: HA_URL }, function(response, error) {
if (error || response.code !== 200) {
logInfo("Home Assistant nicht erreichbar → Toggle-Modus");
activateFollowMode();
} else {
logInfo("Home Assistant online → Detached-Modus");
activateDetachedMode();
}
// Nächsten Poll-Timer setzen
Timer.set(POLL_INTERVAL, false, checkHomeAssistantStatus);
});
}
// === Starte zyklische Statusprüfung ===
checkHomeAssistantStatus();
Dieses Script prüft alle 10 Sekunden, ob Home Assistant erreichbar ist. Falls Ja schaltet es in den Detached Mode und aktiviert den Shelly Relai. Die smarte Lampe wird also dauerhaft mit Strom versorgt. (Standard Mode …)
Falls Nein greift das Script als Fallback ein und schaltet den Shelly auf Toggle Mode, also die Funktion eines Schalters, zurück. Und als Zustand (ein oder aus) wird der Zustand des Schalters genommen.
Ist der Schalter also ein, wird der Strom eingeschaltet. Ist der Schalter aus, wird der Strom abgeschaltet. Und bei jeder Betätigung des Schalters schaltet der Shelly die Lampe entsprechend ein bzw. stromlos.
Eine normale Schalterfunktion bleibt erhalten bzw. wird als Fallback wiederhergestellt.
Sobald der Shelly (in seiner 10 Sekunden Prüfung) feststellt, dass HA wieder verfügbar ist, schaltet es zurück in Detached Mode und schaltet das Relai an. Die Lampe bekommt wieder dauerhaft Strom.
Die Leuchte selbst muss dafür so eingestellt sein, dass Sie nach einem Stromausfall wieder angeht …
Dies bedeutet jedoch auch, dass die Lampe sich einschaltet, sobald HA wieder verfügbar / erreichbar ist, auch wenn Sie vorher aus war. (Wir können schließlich nicht wissen, ob während der Abwesenheit von HA die Lampe geschaltet wurde oder nicht …)
Daher gibt es als Gegenstück selbstverständlich eine HA Automatisierung.
alias: Flurlicht Steuerung
description: >-
Steuert die WiZ-Lampe im Flur nach Shelly-Input oder nach
Verfügbarkeits-Restoration. Verzögert nur beim HA-Neustart.
triggers:
- id: startup
event: start
trigger: homeassistant
- id: restored
entity_id: light.wiz_tunable_white_212200
from:
- unavailable
- unknown
to:
- "on"
- "off"
trigger: state
- id: input_change
entity_id: binary_sensor.shelly_flur_licht_input_0
trigger: state
- trigger: state
entity_id:
- switch.shelly_flur_licht
to: "on"
id: shelly_on
conditions: []
actions:
- choose:
- conditions:
- condition: trigger
id:
- startup
sequence:
- delay: "00:00:10"
- choose:
- conditions:
- condition: state
entity_id: binary_sensor.shelly_flur_licht_input_0
state: "on"
- condition: state
entity_id: light.wiz_tunable_white_212200
state: "off"
sequence:
- target:
entity_id: light.wiz_tunable_white_212200
action: light.turn_on
data: {}
- conditions:
- condition: state
entity_id: binary_sensor.shelly_flur_licht_input_0
state: "off"
sequence:
- target:
entity_id: light.wiz_tunable_white_212200
action: light.turn_off
data: {}
mode: single
Diese wird getriggert, wenn Home Assistant hochgefahren wird, die Lampe aus dem Zustand unbekannt oder nicht verfügbar wieder ein- oder ausgeschaltet wird, wenn der Schalter selbst betätigt wird und wenn das Shelly Relai wieder eingeschaltet wird, die Lampe über dieses also zuvor stromlos war.
Wenn HA gerade startet, wird die Automatisierung noch einmal um 10 Sekunden verzögert, damit auch alles verfügbar / erreichbar ist. Gerade WiZ z.B. wird ja über eine eigene Integration angesteuert.
Danach wird die Lampe auf den Status gesetzt, den der Lichtschalter hat. Ist der Lichtschalter also ein ein, wird die Lampe eingeschaltet bzw. bleibt eingeschaltet. Hier wird zuvor noch geprüft, ob die Lampe bereits eingeschaltet ist, um adaptive lightning nicht zu übersteuern, falls dies aktiviert ist.
Ist der Lichtschalter auf aus gestellt / gekippt, wird die Lampe entsprechend wieder ausgeschaltet.
Damit wird die Lampe selbst nach einem HA-Ausfall o.ä. wieder auf den Status gestellt, die der Schalter hat, bleibt auch ohne HA schaltbar, übernimmt jedoch Dinge wie Farbkorrekturen usw.
Wenn man nun noch mit Bewegungs- oder Präsenzmeldern arbeitet, sich über ein wake up light wecken lässt oder ähnliches, müsste natürlich entsprechende Bedingungen in der Automatisierung berücksichtigen / einpflegen.
Die Entitäten müssen natürlich angepasst werden.
Meine Leuchte: light.wiz_tunable_white_212200
Mein Shelly selbst: switch.shelly_flur_licht
Mein Schalter: binary_sensor.shelly_flur_licht_input_0
(ist zu Beginn eine deaktivierte Entität)
Als Basis für mein Shelly Script diente Shelly Mini Script um vom "Getrennter Schalter" zum "Toggle" Modus umschalten - User Scripte - Shelly Forum - Hier sind Toggle und Detach jedoch genau verdreht.
Viel übergeblieben vom ursprünglichen Code ist vermutlich ohnehin nicht.
Und ja, die Codes sind unterzuhilfenahme von KI entstanden. Mit einem bezahlten Modell, einer Menge eigener Arbeit und mehreren Stunden Zeiteinsatz …
Insbesondere das Shelly Script sollte auch für andere Shelly Modelle funktionieren. Die Home Assistant IP muss selbstverständlich angepasst werden. Dies habe ich jedoch extra als Variable gelegt.
Ich habe alle Ausfallszenarien zwischenzeitlich mehrfach simuliert und getestet. Bei mir / für mich läuft es stabil.
Wie passend die HA Automatisierung für den eigenen Anwendungsfall ist, ob man diese anpasst, evtl. auf mehrere Automatisierungen aufteilt usw. bleibt selbstverständlich jedem selbst überlassen.
Ich denke / hoffe jedoch, dass damit zumindest der Proof of Concept erbracht ist, dass ein Smarthome mit smarter Beleuchtung und als Fallback Bedienung ohne diesen Komfort sich nicht ausschließen.