Shelly Fail‑Safe Detached Mode: Gen2/Gen3 Script That Auto‑Flips Gen1 (Shelly 2.5) Between Detached and Toggle When Home Assistant Is Down
A lightweight, on‑device script for Shelly Plus/Gen2/Gen3 that pings Home Assistant and automatically:
- Keeps smart‑bulb circuits in detached mode when HA is healthy.
- Switches Shelly Gen1 (e.g., Shelly 2.5) to toggle/edge for direct wall‑switch control when HA is down.
- Optionally flips the hosting Gen2/3 device’s input mode to ensure local control even with LAN/WAN outages.
This provides a guest‑proof, local‑first, no‑cloud fallback so lights still work from the wall switch during server, automation, or network failures.
Detached mode is great for Zigbee/Thread smart bulbs, but when Home Assistant or the LAN fails, presses don’t reach the bulbs. This script makes Shellys fail‑safe by switching Gen1 relays to local control automatically, and can also swap the hosting Gen2/3 input mode for immediate, on‑device control.
- Pure local operation; no Internet required.
- Runs entirely on the Shelly Plus/Gen2/Gen3 device (no Pi/NAS/VM needed).
- Sequential HTTP to Gen1 devices (safe for Shelly RPC rate limits).
- “Detached only if relay is ON” guard to avoid stranding a dark room.
- Clear logs for each action and decision.
- Easy configuration: static IPs and a device list.
- Smart‑bulb circuits controlled via Shelly detached mode.
- Mixed fleets (Gen2/3 hosting script; Gen1 receiving mode flips).
- Environments where HA, hypervisor, or router/AP might occasionally fail.
- At least one Shelly Plus/Gen2/Gen3 device to host the script.
- Gen1 devices (e.g., Shelly 2.5) reachable over the local network.
- Static IPs for Home Assistant and every Shelly referenced by the script.
- If Gen1 has HTTP auth, embed user:pass in the device URL (see notes).
- Open the device Web UI → Scripts (curly braces icon).
- Add Script → paste the code below.
- Edit the configuration block (HA URL and Gen1 device list).
- Save, Enable “Run on boot,” then Start the script.
- Watch “Debug” logs to verify health checks and mode flips.
- Use IP literals, not hostnames (fewer moving parts when DNS/WAN is down).
- For Gen1 with HTTP auth: use http://user:[email protected] in the list.
- For rocker switches, “flip” is ideal; for momentary, consider “follow.”
- If LAN/AP goes fully down: cross‑device flips can’t happen, but the hosting Gen2/3 can still change its own input mode locally (uncomment setSelf calls).
// Shelly Fail‑Safe Detached Mode Watchdog
// Host on a Shelly Plus/Gen2/Gen3 device. Runs fully on-device.
// ====== CONFIG ======
// Home Assistant URL (use IP and local port)
let haUrl = 'http://192.168.255.100:8123';
// Gen1 Shelly devices to control (e.g., Shelly 2.5)
let gen1 = [
{ip: 'http://192.168.255.101', chans:[0,1]}, // add http://user:pass@IP if auth is enabled
{ip: 'http://192.168.255.102', chans:[0,1]}
];
// Health check interval (ms)
let CHECK_INTERVAL_MS = 60000;
// Optional: also flip the hosting Gen2/3 input mode for local control.
// Uncomment setSelf(...) calls in check() to enable.
let SELF_RELAY_ID = 0; // adjust if needed (multi-relay devices)
// ====== HELPERS ======
function httpGet(url, cb) {
Shelly.call("HTTP.GET", {url: url}, function(res) {
cb(res);
});
}
// ====== GEN1 MODE CONTROL ======
// Set Gen1 button type, but only set to 'detached' if the relay is currently ON.
// Otherwise keep it on 'toggle' so a wall press still brings light.
function setGen1(mode) {
var devices = [];
for (var i = 0; i < gen1.length; i++) {
for (var j = 0; j < gen1[i].chans.length; j++) {
devices.push({ip: gen1[i].ip, ch: gen1[i].chans[j]});
}
}
function next(idx) {
if (idx >= devices.length) return;
var d = devices[idx];
print("[setGen1] Checking ", d.ip, " channel ", d.ch);
httpGet(d.ip + "/settings/relay/" + d.ch, function(cfg) {
print("[setGen1] Response for ", d.ip, "/settings/relay/", d.ch, ": ", JSON.stringify(cfg));
var relay = cfg && cfg.body ? JSON.parse(cfg.body) : null;
var isOn = relay && relay.ison;
var currentBtn = relay && relay.btn_type;
var desiredBtn = mode;
// Only detach if the relay is already on (room has power for bulbs).
if (mode === 'detached' && !isOn) {
desiredBtn = 'toggle';
}
if (currentBtn !== desiredBtn) {
print("[setGen1] Setting ", d.ip, " channel ", d.ch, " to ", desiredBtn);
httpGet(d.ip + "/settings/relay/" + d.ch + "?btn_type=" + desiredBtn, function(resp) {
print("[setGen1] Set response for ", d.ip, "/settings/relay/", d.ch, ": ", JSON.stringify(resp));
next(idx + 1);
});
} else {
print("[setGen1] No change needed for ", d.ip, " channel ", d.ch);
next(idx + 1);
}
});
}
next(0);
}
// ====== SELF MODE CONTROL (Gen2/Gen3 host) ======
// If enabling this, the device hosting the script can also switch its own input:
// - to 'detached' when HA is up (smart bulb mode)
// - to 'flip' when HA is down (local failover)
function setSelf(mode){
if (mode === 'detached') {
Shelly.call("Switch.GetStatus", {id: SELF_RELAY_ID}, function(status) {
if (status && status.output) {
Shelly.call("Switch.SetConfig", {id: SELF_RELAY_ID, config:{in_mode: 'detached'}}, null);
print("[setSelf] in_mode=detached (relay ON)");
} else {
Shelly.call("Switch.SetConfig", {id: SELF_RELAY_ID, config:{in_mode: 'flip'}}, null);
print("[setSelf] in_mode=flip (relay OFF) to preserve local control");
}
});
} else {
Shelly.call("Switch.SetConfig", {id: SELF_RELAY_ID, config:{in_mode: mode}}, null);
print("[setSelf] in_mode=", mode);
}
}
// ====== HEALTH CHECK ======
function check() {
httpGet(haUrl, function(res) {
var up = (res && res.code === 200);
if (up) {
print("[check] HA is UP");
setGen1('detached');
// setSelf('detached'); // Optional: enable if the host should swap modes too
} else {
print("[check] HA is DOWN (or unreachable)");
setGen1('toggle');
// setSelf('flip'); // Optional: enable local input->relay control on the host
}
});
}
// Run periodically
Timer.set(CHECK_INTERVAL_MS, true, check);- Stop HA or block port 8123 temporarily.
- Press wall switches: Gen1 devices should act as local relays (toggle/edge).
- Restore HA: devices should return to detached (if their relays were on).
- Cross‑device flips require LAN/Wi‑Fi between devices. If the LAN/AP is truly down, only the hosting Gen2/3 can change its own input mode locally.
- Consider placing the router/AP on a small UPS so local Wi‑Fi survives brief outages.
- For Zigbee bulbs, also configure power‑on behavior (On/Last‑state) as a safety net if a circuit is power‑cycled.
- Nothing happens: confirm static IPs, correct ports, and that Gen1 Web UI is reachable from the hosting device.
- 401 Unauthorized: add http://user:pass@IP to the gen1 list.
- Flapping: increase CHECK_INTERVAL_MS (e.g., 30–60 s) and leave devices in their last working mode.
- Momentary vs rocker: replace “flip” with “follow” if needed for momentary buttons.
SEO keywords
shelly gen2 script, shelly gen3 script, shelly 2.5 detached mode, shelly toggle edge btn_type, home assistant down fallback, smart bulb detached mode, shelly watchdog, shelly local script, shelly http api settings relay btn_type, zigbee lights fail safe, shelly plus 1pm detached, shelly 2.5 toggle, shelly script http.get, shelly failover, shelly local control