ESPHome variables from Home Assistant
-
Comments:
- here.
One of my key tenets of Home Automation is that as much stuff should be done locally as possible. Whilst with HomeAssistant, that means “in the local network”, I feel that “in the device” is even better. However, sometimes values should still be configurable (from within Home Assistant), but the device should still work even if there is not (and has never been) a connection to Home Assistant since boot.
For instance, I have a sensor light. The device contains a PIR sensor, with configurable delay, sensitivity and lux settings. This in turn drives a relay connected to a pair of PAR bulbs, and additionally has a green LED. This device usually only has the ESP8266 controlling if the thing should ever turn on at all, but I hacked it so that it exposes the PIR sensor, and allows triggering the relay.
But the delay between the motion no longer being detected and the light turning off should be configurable. I don’t want to have to re-flash the device if I decide to change this value, so I wanted to make it that the value will be fetched from Home Assistant (if it is set).
This turned out to work really well. There are a few parts that need to be set up in the YAML (this is not the complete file):
esphome:
name: ${device_name}
platform: ESP8266
board: esp01_1m
on_boot:
- logger.log:
level: DEBUG
format: 'Light ON time is set to %d'
args: ['id(light_on_time)']
globals:
- id: light_on_time
type: int
restore_value: true
initial_value: '30'
sensor:
- platform: homeassistant
id: on_time
entity_id: input_number.${device_name}
on_value:
then:
- globals.set:
id: light_on_time
value: !lambda 'return int(x);'
It really is that simple. The first boot will set the light_on_time
variable to 30
. Then, when it connects to Home Assistant, it will look for an input_number.<device_name>
(which matches the device name). If it finds one (or is ever told about this value changing), then it will commit that new value to the flash, and this will be be restored after a reboot.
There is one other thing we could do here, to make it so that we don’t write the value to the flash if it has not changed (and prevent wear to that, since it is limited to a number of writes):
sensor:
- platform: homeassistant
id: on_time
entity_id: input_number.${device_name}
on_value:
then:
if:
condition:
lambda: 'return id(light_on_time) != int(x);'
then:
- logger.log:
level: DEBUG
format: 'Light ON time changed from %d to %d seconds'
args: ['id(light_on_time)', 'int(x)']
- globals.set:
id: light_on_time
value: !lambda 'return int(x);'
With regards to a similar problem, detecting if it is dark enough to turn the light on should be something like “a bit before sunset to a bit after sunrise”. I could set the lux threshold on the device, but it would be nice to have motion detection work during the day too.
Here, we can have a global variable sun_is_down
that defaults to being true
, and is only set to false
when the sun comes up. However, we would also want this to trigger when first connecting to Home Assistant and the sun is already up.
We can use a wait_until
in our on_boot
handler to trigger this:
globals:
- id: sun_is_down
type: bool
restore_value: false
initial_value: 'true'
esphome:
...
on_boot:
- wait_until:
api.connected:
- if:
condition:
sun.is_above_horizon:
then:
- logger.log:
level: DEBUG
format: 'Sun is up, light will not turn on.'
- globals.set:
id: sun_is_down
value: 'false'
else:
- logger.log:
level: DEBUG
format: 'Sun is down, light will turn on.'
sun:
on_sunrise:
- elevation: 10°
then:
- logger.log:
format: 'The sun is now down, light will turn on.'
- globals.set:
id: sun_is_down
value: 'false'
on_sunset:
- elevation: 10°
then:
- logger.log:
format: 'The sun is now up, light will not turn on.'
- globals.set:
id: sun_is_down
value: 'true'
I’ve used a 10° elevation to trigger the light to turn on a bit before sunset and still turn on a bit after sunrise when motion is detected. I haven’t figured out a way to get the same check to apply in the sun.is_above_horizon:
check.