I have a Shelly 1L connected with a toggle switch. The output is connected to a smart light bulb and the relay is in detached mode (always on). Under normal circumstances (if the Shelly is connected to the network and can access HA) it should call a HA service to turn the smart light bulb. However, if something is wrong, I want to use the relay to toggle the light. Below you can find my config, which is basically an adapted version of a Shelly 1 config in the documentation.
The way I test this setup is that I turn on the smart light and then disconnect HA from the network by pulling the ethernet plug. This should make the if-statement evaluate to False and the relay should kick in when I toggle the switch. However, it doesnāt.
Iām wondering if anyone here can help me with this problem. Perhaps Iām doing something wrong in the config or the way I test this isnāt a supported use-case?
Shelly config
substitutions:
device_name: kitchen
# Basic Config
esphome:
name: ${device_name}
platform: ESP8266
board: esp01_1m
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
ap:
ssid: ${device_name}
password: !secret wifi_password
logger:
api:
ota:
web_server:
port: 80
#==============================================================
switch:
- platform: gpio
pin: GPIO5
id: shelly_1l_relay
binary_sensor:
- platform: gpio
name: ${device_name} Input
pin:
number: GPIO4
# small delay to prevent debouncing
filters:
- delayed_on_off: 50ms
# config for state change of input button
on_state:
then:
- if:
condition:
and:
- wifi.connected:
- api.connected:
- switch.is_on: shelly_1l_relay
# toggle smart light if wifi and api are connected and relay is on
then:
# - switch.toggle: shelly_1l_relay
- homeassistant.service:
service: light.toggle
data:
entity_id: light.kitchen_light
# else, toggle relay
else:
- switch.toggle: shelly_1l_relay
id: button
I would have thought that by using on_state: you will trigger this twice, once on press and once on release, thus switching the relay on then off again?
Thanks for your response! Iām using a toggle/rocker switch, not a push/momentary switch. If I flip the switch it stays on either āonā or āoffā until I switch it back, so thereās only one state change per toggle.
As an aside, you can do clicks with toggle switches, but itās a kind of special control. It works by flipping the switch from ON to OFF and then ON again for a single click. Iām actually using it to dim the smart bulb: 100% brightness with a long click, 50% with a short click, and 10% with a double click.
Iāve checked the source code for the ESPHome API server and it relies on a list of connected clients to decide the value for api.connected. If the list of clients is empty, itās False and True otherwise (I see this is also in the documentation here). I assume each client is added to the list after connecting, but theyāre only removed if theyāre explicitly disconnecting. This would mean that when I pull the ethernet cable from HA, the ESPHome device is left with a stale connection, e.g., it thinks thereās still a client connected, even though it canāt be reached. This makes sense since the alternative is to ping the client to check if itās still alive, which is a huge pain.
If this is the case, Iād need a way to periodically clean the list of clients (e.g. by pinging them) or find an alternative way to ensure the HA service call will be picked up. Alternatively, I could handle this by handling a timeout on the service call: attempt to toggle the smart bulb, and if it times out, assume HA is offline and fall back to the relay. Does anyone have experience with any of these approaches?
Ah - a toggle switchā¦ Noisy - no wonder you need a debounce.
Iām assuming your HA instance doesnāt disconnect and reconnect all day, and this is just to reliably toggle the relay when disconnected? If so then some overhead in determining if you are still connected is probably acceptable, but will will require more complex code than the simple api.connected test you are doing.
Or maybe more time? Unplug then wait a minute or so.
Indeed, itās only to reliably toggle the relay when disconnected. Iām fine with more complex code than just api.connected, but I donāt know where to start. Do you happen to have any pointers for me?
@zoogara Thanks a lot for taking the time to investigate this! Your suggestion made a lot of sense to me and I immediately tried it. Iām sorry to say it doesnāt work Iāve looked into the source code for the status component and it seems to use exactly the same logic as api.connected, so the result is the same. The state of the status component stays āonā even though HA is disconnected from the network. It doesnāt matter how long I wait; it stays this way.
The ESPHome devices and HA donāt seem to maintain an open connection (something like a WebSocket) which would be implemented by the two ends pinging each other to keep the connection alive. This means that one end can go offline without the other end knowing about it.
I found out you can send HTTP requests from ESPHome and set a timeout for them. I could call the HA light.toggle service this way. However, there doesnāt seem to be a way to bind a callback that executes when the request times out.
So it seems Iām still out of luck. I really hope someone can figure this one out, as I think itās very useful! Today I cut off the power when I was fiddling with the wall switches. When I turned it back on it took almost 10 minutes for my router to fully startup. In the meantime, the only light that worked was the one with the Shelly thatās configured with the fallback mode. It worked because HA never connected to it, so the number of clients it counted was zero and api.connected was False, which enabled the relay. Worked like a charm! This was an uncommon situation though and it wouldnāt work during a more frequent HA reboot, unfortunately.
Iāve managed to implement the fallback using the amazing ping sensor, which is an external component by trombik. I use it to regularly ping Home Assistant from the ESP. The sensor exposes two values: latency and packet loss. When flipping the wall switch, I simply check if the packet loss is 100%. If it is, something must be amiss and it falls back to the relay instead.
Itās not a pretty solution and there are some downsides, but it works well enough for me. I can tweak the number of pings and the interval. More pings take more time but make false positives less likely. If I set the interval too short, Iām pinging a lot, which can become a problem if I have many devices doing this, though I donāt expect to run into this.
Shelly 1L config with fallback mode
substitutions:
device_name: kitchen
# Basic Config
esphome:
name: ${device_name}
platform: ESP8266
board: esp01_1m
libraries:
- ESP8266WiFi
- https://github.com/akaJes/AsyncPing#95ac7e4
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
ap:
ssid: ${device_name}
password: !secret wifi_password
logger:
api:
ota:
web_server:
port: 80
external_components:
- source: github://trombik/esphome-component-ping
components: [ ping ]
#==============================================================
switch:
- platform: gpio
pin: GPIO5
id: shelly_1l_relay
restore_mode: ALWAYS_ON
binary_sensor:
- platform: gpio
name: ${device_name} Input
pin:
number: GPIO4
# small delay to prevent debouncing
filters:
- delayed_on_off: 50ms
on_state:
then:
- if:
condition:
and:
- wifi.connected:
- api.connected:
- switch.is_on: shelly_1l_relay
- lambda: return id(packet_loss).state < 100;
# toggle smart light if wifi and api are connected and relay is on
then:
# - switch.toggle: shelly_1l_relay
- logger.log:
format: "Packet loss '%.1f'"
args: [ 'id(packet_loss).state' ]
- homeassistant.service:
service: light.toggle
data:
entity_id: light.kitchen_lights
# else, toggle relay
else:
- switch.toggle: shelly_1l_relay
id: button
sensor:
- platform: ping
# IP address of Home Assistant
ip_address: 192.168.178.22
# number of packets to send
num_attempts: 5
# the timeout. however, this is not what you usually expect from `ping`
# implementation: the timeout is also the interval to send packets. if you
# set this value to 10 sec, and the network is fine (no packet loss), then
# the component sends a packet at 10 sec interval, and the total time to
# finish would be 10 sec * num_attempts = 10 * 17 = 170 sec.
timeout: 1sec
loss:
# the name to be shown.
id: packet_loss
name: Packet loss
latency:
# the name to be shown.
id: latency
name: Latency
# this should be 3 as the value is float, unit is sec, and the raw
# values are in ms.
accuracy_decimals: 3
# the interval for checking the sensors. defaults to 60s.
update_interval: 10s
status_led:
pin: GPIO0
Note that ping depends on two additional libraries, which are included in the first block.
To make the fallback mode complete, Iāve configured my Zigbee smart bulbs to turn on by default after loss of power. This ensures that if a bulb is āsoft offā (powered, but shut off by HA) and a problem occurs, I can take the power off using the wall switch (utilizing the Shelly in fallback mode) and then on again, which will turn the light on (since thatās the default).
Iām pretty confident this will enable me to use my lights as long as I have power Let me know if this was useful to any of you!