Setup: Zigbee smart lights, Shelly 1 Plus relays in detached mode and Home Assistant automations. Problem: wall switches don’t work if Home Assistant or Wi-Fi is down. Solution: on-device script that checks if Home Assistant is available and changes the Shelly’s switch mode. The code:
let outputId = 0; // For Shelly 1 Plus it's always 0. Change if needed
let checkPeriod = 10 * 1000 // in ms
let currentMode = "detached";
// In the GUI, operation mode is set in the input section, but via RPC calls, it is set on the output
// "id" is the ID of the output to which the new configuration will be applied, and "config" is the actual configuration
let config = {
id: outputId,
config: {
in_mode: "detached"
}
};
function updateConfig(oldConfig) {
// Apply the new config only if the switch is not already working in the desired mode
if (oldConfig.in_mode !== currentMode) {
Shelly.call("Switch.SetConfig", config);
}
}
function setSwitchFromRespons(resp) {
// If HA is accessible the response code is 200
if (resp.code === 200) {
currentMode = "detached";
} else {
currentMode = "flip";
}
// Update the mode in the new configuration
config.config.in_mode = currentMode;
// Get the current output configuration and pass it to updateConfig
Shelly.call("Switch.GetConfig", {id: outputId}, updateConfig)
}
function testHA() {
// For RPC command HTTP.GET, both url and body are required.
Shelly.call("HTTP.GET", {url:'http://YOUR_HOME_ASSISTAN_URL_OR_IP:8123', body:{}}, setSwitchFromRespons);
}
// Timer.set(period_in_ms, reapeate_indefinitely, function)
Timer.set(checkPeriod, true, testHA);
This code can easily be adapted for Shelly 2 Plus and other 2nd gen Shelly devices. 1st gen devices do not support scripting.
I’m running this now on one of my Shellies. I’ll try it out and see how it works.
Furthermore, I’ve made some minor changes (I hope this is okay with you):
My version
Put home assistant URL in a separate variable so it can be easily changed
Changed function name setSwitchFromRespons to setSwitchFromResponse
Changed resp to response in function
// Adapted by Bo from AlesAlitis (Nedyalko Vasilev)
// https://community.home-assistant.io/t/control-smart-lights-with-shelly-with-automated-detached-mode/576881
let outputId = 0; // For Shelly 1 Plus it's always 0. Change if needed
let checkPeriod = 10 * 1000; // in milliseconds
let homeassistantUrl = "http://YOUR_HOME_ASSISTANT_IP:8123";
let currentMode = "detached";
// In the GUI, operation mode is set in the input section, but via RPC calls, it is set on the output
// "id" is the ID of the output to which the new configuration will be applied, and "config" is the actual configuration
let config = {
id: outputId,
config: {
in_mode: "detached"
}
};
function updateConfig(oldConfig) {
// Apply the new config only if the switch is not already working in the desired mode
if (oldConfig.in_mode !== currentMode) {
Shelly.call("Switch.SetConfig", config);
}
}
function setSwitchFromResponse(response) {
// If HA is accessible the response code is 200
if (response.code === 200) {
currentMode = "detached";
} else {
currentMode = "flip";
}
// Update the mode in the new configuration
config.config.in_mode = currentMode;
// Get the current output configuration and pass it to updateConfig
Shelly.call("Switch.GetConfig", {id: outputId}, updateConfig)
}
function testHA() {
// For RPC command HTTP.GET, both url and body are required.
Shelly.call("HTTP.GET", {url: homeassistantUrl, body:{}}, setSwitchFromResponse);
}
// Timer.set(period_in_ms, reapeate_indefinitely, function)
Timer.set(checkPeriod, true, testHA);
Streamlined the code and removed some unnecessary checks and variables. This should reduce the load on the Shelly’s limited resources.
Added a check if switching to “detached” mode and turning the relay on because I keep the switch entity in HA disabled. If you keep it enable, you can comment it out
Version 2
// If firmware version is lower than 1, relace const with let
const switchId = 0; // For Shelly 1 Plus it's always 0. Change if needed
const checkPeriod = 10 * 1000; // in ms
const haUrl = 'http://YOUR_HOME_ASSISTAN_URL_OR_IP:8123'; // As sugested by bvhme
let mode = "detached"; // Could be "detached", "flip", "follow" or "momentary". Use "detached" when HA is in control
function updateConfig(oldConfig) {
//Aplly the new config only if the switch is not already working in the desired mode
if (oldConfig.in_mode !== mode) {
Shelly.call("Switch.SetConfig", { id: switchId, config: { in_mode: mode } });
// If moving to HA controlled mode, make sure that the relay is switched on
if (mode === "detached") Shelly.call("Switch.Set", { id: switchId, on: true });
}
}
function setSwitchFromRespons(response) {
// If HA is accessible the response code is 200 update the current mode and the new configuration
mode = (response.code === 200)? "detached" : "flip";
// Get the current output configuration and pass it to updateConfig
Shelly.call("Switch.GetConfig", {id: switchId}, updateConfig)
}
function testHA() {
// For RPC command HTTP.GET, both url and body are required.
Shelly.call("HTTP.GET", {url: haUrl, body:{}}, setSwitchFromRespons);
}
// Timer.set(period_in_ms, reapeate_indefinitely, function)
Timer.set(checkPeriod, true, testHA);
Thanks for posting your code @AlesAlitis. Not sure if it’s something unique about my setup, but when HA was offline I was getting an error at the “response.code” line that stopped the script. I added a tweak to the if statement and it seems to be working fine.
My Version
let switchId = 0; // For Shelly 1 Plus it's always 0. Change if needed
let checkPeriod = 10 * 1000; // in ms
let haUrl = 'http://YOUR_HOME_ASSISTAN_URL_OR_IP:8123'; // As sugested by bvhme
let mode = "detached"; // Could be "detached", "flip", "follow" or "momentary". Use "detached" when HA is in control
function updateConfig(oldConfig) {
//Aplly the new config only if the switch is not already working in the desired mode
if (oldConfig.in_mode !== mode) {
Shelly.call("Switch.SetConfig", { id: switchId, config: { in_mode: mode } });
// If moving to HA controlled mode, make sure that the relay is switched on
if (mode === "detached") Shelly.call("Switch.Set", { id: switchId, on: true });
}
}
function setSwitchFromRespons(response) {
// If HA is accessible the response code is 200 update the current mode and the new configuration
// response.code was giving error in original code modified to flip if response is null
if (response === null) {
mode = "flip";
} else if (response.code === 200) {
mode = "detached";
} else {
mode = "flip";
}
// Get the current output configuration and pass it to updateConfig
Shelly.call("Switch.GetConfig", {id: switchId}, updateConfig)
}
function testHA() {
// For RPC command HTTP.GET, both url and body are required.
Shelly.call("HTTP.GET", {url: haUrl, body:{}}, setSwitchFromRespons);
}
// Timer.set(period_in_ms, reapeate_indefinitely, function)
Timer.set(checkPeriod, true, testHA);
For anyone else who comes to this thread. Another issue I have is I have some cheap WiFi bulbs that I connect using Local Tuya. The connection is usually solid but can have issues on a HA reboot. To make sure all the lights turn off/on as expected I added a check in my automation to see if all the lights were detected by HA. If not, I have the automation switch the mains at the Shelly.
i stumbled over this idea and it might be the solution for my setup-problems. Now I was asking myself: as long as the switch is in detached mode, how do you control the zigbee bulbs? A script?
To customize the light version:
change url to your local home assistant address or IP
id: 0 points to switch 1, id: 1 to switch 2. Default is id: 0, since with 1PM there is only one switch. If you want to apply this to switch to in Plus PM2, replace all id: 0 to id: 1
in let mode… line. “detached” : “flip” is ok, if you are using normal physical flip switch. I’m personally using button, so I changed this to “detached” : “momentary”
How about the shelly devices like 2pm and zigbee bulbs. Some times Home Assistant can be up and running, but the add-on like Zigbee2mqtt can be down or Mqtt integration “Failed to set up”. In these cases this script isn’t working. What changes need to be made so that the script takes these into account?
So I think this adjustment to the script works on the 2PM shelly
Note that I change the mode for when Home assistant is unavailable from flip to follow
Thanks to this thread I finally also was able to solve the dilemma between smart bulbs and working light switches.
I rewrote the script to be event baased instead of periodically checking the status. So far it works pretty good with my shelly plus 1 that I put behind normal switches:
// Preconditions:
// Shelly connected to a manual switch and to a smart light
// Shelly set to detached mode and to restore power to on after power loss
// Input mode is switch
// Shelly is added to Home Assistant
// An automation is created in Home Assistant to toggle the light when online
// config
let ha_url = "http://homeassistant:8123/api/ping";
let input_id = 0 //default for shelly plus 1
let output_id = 0 //default for shelly plus 1
let online_status = 0;
// Process response from the HA URL, the 404 error would be part of the result variable
function processHttpResponse(result, error_code, error) {
if (error_code !== 0) {
online_status = 0
//print("Offline")
} else {
online_status = 1
//print("Online")
}
}
function do_online_check() {
Shelly.call("HTTP.GET", { url: ha_url, timeout: 1 }, processHttpResponse);
}
//switch toggled event -> if we are online and ha is reachable, make sure the relay is on and leave controlling the smart bulb to home assistant
Shelly.addEventHandler(function (event) {
if (typeof event.info.state !== 'undefined') {
if (event.info.id === input_id) {
do_online_check();
if (online_status === 1) {
Shelly.call("Switch.set", { 'id': output_id, 'on': true });
}
else {
Shelly.call("Switch.toggle", { 'id': output_id });
}
}
}
});
Credits go to this thread and also to this blogpost
I’m trying to refine this to a seamless transition, so I configured homeassistant to match smartlight on-off calls with Input(0), and changed flip to follow so that there would be no on-off logic discrepancy between my dumb light switch in follow configuration and the smart light detatched configuration.
When this script changes shelly’s configuration from detatched to follow, it does not update Output(0) to match Input(0). I have to flip Input(0) on and off before Output(0) starts matching Input(0)'s condition.
How can I add a line like the below logic to fix this?
if (mode === "follow") Shelly.call("Switch.Set", { id: 0, on: match Input });
I tried the code in my environment, but somehow the URL thing does not work properly.
Immediately after starting the script, the switch is set to “edge”.
I tried the original url that you provided. I tried IP:8123 and I tried IP.home:8123 as well…
What am I’m doing wrong?
Thomas