I have a use case where I am using a template switch component essentially as a virtual switch so I can turn things on/off from the HA UI, but also track that state inside the ESPHome firmware using the state of the template swtich. Since there is no hardware backing the switch, I used the optimistic
setting. All seems well, except when I try to use the state of the switch in the on_turn_on
and on_turn_off
actions, the state of the template switch isn’t the new value, it’s the old value. Only after the actions have run is the state of the switch updated.
My expected behavior would be that the state of the optimistic switch immediately reflects the new state before the actions are run. Maybe I’m misusing the template switch and this is expected behavior. Let me know. I know I could just assume the state by putting my automation directly in the action, but I’m attempting to reduce duplicated code by running a script that checks the state of the switch to decide what to do (and that script is used elsewhere besides the switch actions).
minimal example:
esphome:
name: test-device
platform: ESP8266
board: nodemcuv2
logger:
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
api:
ota:
time:
platform: homeassistant
on_time:
- seconds: /10
then:
- lambda: ESP_LOGD("timer", "State of test_switch is %d", id(test_switch).state);
switch:
- platform: template
id: test_switch
name: test_switch
optimistic: true
turn_on_action:
then:
- script.execute: test_script
turn_off_action:
then:
- script.execute: test_script
script:
- id: test_script
then:
- lambda: ESP_LOGD("script", "State of test_switch is %d", id(test_switch).state);
Log output:
[11:38:50][D][timer:022]: State of test_switch is 0
[11:39:00][D][timer:022]: State of test_switch is 0
[11:39:06][D][switch:013]: 'test_switch' Turning ON. # turn on switch with HA UI
[11:39:06][D][script:044]: State of test_switch is 0 # Expected 1 here
[11:39:06][D][switch:037]: 'test_switch': Sending state ON
[11:39:10][D][timer:022]: State of test_switch is 1 # switch is now on after automation action ran
[11:39:20][D][timer:022]: State of test_switch is 1
[11:39:28][D][switch:017]: 'test_switch' Turning OFF. # turn off switch with HA UI
[11:39:28][D][script:044]: State of test_switch is 1 # Expected 0 here
[11:39:28][D][switch:037]: 'test_switch': Sending state OFF
[11:39:30][D][timer:022]: State of test_switch is 0 # switch is now off after automation action ran
[11:39:40][D][timer:022]: State of test_switch is 0
[11:39:50][D][timer:022]: State of test_switch is 0
A work around I found is to ditch optimistic all together (although it would still work with this), and just publish the state of the switch at the start of the action. This works, but feels a bit cumbersome to remember to do. I would possibly expect a template switch to automatically do this is neither a lambda nor optimistic are set, but that might have other side effects that break other use cases.
I guess I have a few questions:
- Should template switches with optimistic set update their internal state before running actions?
- Should template switches with neither a lambda nor optimistic set implicitly update their internal state?
- Is there a less verbose way of updating a template switch’s internal state without explicitly publishing updates on actions?
Modified Yaml:
esphome:
name: test-device
platform: ESP8266
board: nodemcuv2
logger:
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
api:
ota:
time:
platform: homeassistant
on_time:
- seconds: /10
then:
- lambda: ESP_LOGD("timer", "State of test_switch is %d", id(test_switch).state);
switch:
- platform: template
id: test_switch
name: test_switch
optimistic: true
turn_on_action:
then:
- lambda: id(test_switch).publish_state(true);
- script.execute: test_script
turn_off_action:
then:
- lambda: id(test_switch).publish_state(false);
- script.execute: test_script
script:
- id: test_script
then:
- lambda: ESP_LOGD("script", "State of test_switch is %d", id(test_switch).state);
Modified output:
[12:34:30][D][timer:022]: State of test_switch is 0
[12:34:40][D][timer:022]: State of test_switch is 0
[12:34:43][D][switch:013]: 'test_switch' Turning ON.
[12:34:43][D][switch:037]: 'test_switch': Sending state ON
[12:34:43][D][script:042]: State of test_switch is 1
[12:34:50][D][timer:022]: State of test_switch is 1
[12:35:00][D][timer:022]: State of test_switch is 1
[12:35:01][D][switch:017]: 'test_switch' Turning OFF.
[12:35:01][D][switch:037]: 'test_switch': Sending state OFF
[12:35:01][D][script:042]: State of test_switch is 0
[12:35:10][D][timer:022]: State of test_switch is 0