Long story short, I flashed a new ESP32 and got everything working again apart from one automation that I’m stuck with.
The full ESP home config is as follows:
esphome:
name: study-blinds
platform: ESP32
board: esp32dev
on_boot:
then:
- switch.turn_off: blinds_fault_light
- wait_until:
api.connected
- homeassistant.service:
service: input_boolean.turn_off
data:
entity_id: input_boolean.blinds_are_closed_boolean
- delay: 1s
- script.execute: homeblinds
- delay: 45s
- script.execute: homeslats
# Enable logging
logger:
# Enable Home Assistant API
api:
services:
- service: blinds_position
variables:
target: int
then:
- stepper.set_target:
id: blinds_motor
target: !lambda "return target;"
- sensor.template.publish:
id: blinds_position
state: !lambda 'return target;'
- service: slats_position
variables:
target: int
then:
- stepper.set_target:
id: slats_motor
target: !lambda "return target;"
- sensor.template.publish:
id: slats_position
state: !lambda 'return target;'
- service: blinds_speed
variables:
target: int
then:
- stepper.set_speed:
id: blinds_motor
speed: !lambda "return target;"
- service: home_slats
then:
- script.execute: homeslats
- service: home_blinds
then:
- script.execute: homeblinds
- service: open_slats
then:
- stepper.set_target:
id: slats_motor
target: 0
- service: open_blinds
then:
- stepper.set_target:
id: blinds_motor
target: -17600
- cover.template.publish:
id: Study_Blind
state: OPEN
cover:
- platform: template
name: "Study Blind"
id: Study_Blind
device_class: blind
open_action:
- stepper.set_target:
id: slats_motor
target: 0
- delay: 30s
- sensor.template.publish:
id: slats_position
state: !lambda return id(slats_motor).target_position;
- stepper.set_target:
id: blinds_motor
target: -17600
- sensor.template.publish:
id: blinds_position
state: !lambda return id(blinds_motor).target_position;
- homeassistant.service:
service: input_boolean.turn_off
data:
entity_id: input_boolean.blinds_are_closed_boolean
close_action:
- stepper.set_target:
id: blinds_motor
target: 0
- delay: 45s
- sensor.template.publish:
id: blinds_position
state: !lambda return id(blinds_motor).target_position;
- stepper.set_target:
id: slats_motor
target: -10400
- sensor.template.publish:
id: slats_position
state: !lambda return id(slats_motor).target_position;
stop_action:
- stepper.set_target:
id: blinds_motor
target: !lambda return id(blinds_motor).current_position;
- sensor.template.publish:
id: blinds_position
state: !lambda return id(blinds_motor).current_position;
- stepper.set_target:
id: slats_motor
target: !lambda return id(slats_motor).current_position;
- sensor.template.publish:
id: slats_position
state: !lambda return id(slats_motor).current_position;
# optimistic: true
# assumed_state: false
ota:
password: "#########f"
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
# Enable fallback hotspot (captive portal) in case wifi connection fails
ap:
ssid: "Study-Blinds Fallback Hotspot"
password: "###########"
captive_portal:
switch:
- platform: gpio
pin: GPIO23
name: Blinds_Fault
id: blinds_fault_light
- platform: restart
name: "Study Blinds Restart"
sensor:
- platform: template
name: "Study Blind Position"
id: blinds_position
- platform: template
name: "Study Slats Position"
id: slats_position
- platform: adc
pin: GPIO34
name: "Study Sun Brightness"
id: study_sun_level
attenuation: 11db
update_interval: 5s
binary_sensor:
- platform: status
name: "Study Blinds Status"
- platform: gpio
pin:
number: GPIO19
mode: INPUT_PULLUP
inverted: True
name: "Slats Endstop"
id: slats_endstop
on_press:
then:
# endstop hit, reset stepper position to -10400 and set target also to -10400. This will stop the motor from doing whatever it's doing
- stepper.report_position:
id: slats_motor
position: -10400
# just in case, also set the target to 0
- stepper.set_target:
id: slats_motor
target: -10400
- switch.turn_off: blinds_fault_light # start of homing will turn this on.
- delay: 1s
- stepper.set_target:
id: slats_motor
target: -10200 # back off slightly so that endstop is not in triggered state
- sensor.template.publish:
id: slats_position
state: !lambda return id(slats_motor).target_position;
- platform: gpio
pin:
number: GPIO18
mode: INPUT_PULLUP
inverted: True
name: "Blinds Endstop"
id: blinds_endstop
on_press:
then:
# endstop hit, reset stepper position to 0 and set target also to 0 - this will stop the motor from doing whatever it's doing
- stepper.report_position:
id: blinds_motor
position: 0
# just in case, also set the target to 0
- stepper.set_target:
id: blinds_motor
target: 0
- switch.turn_off: blinds_fault_light
- delay: 1s
- stepper.set_target:
id: blinds_motor
target: -50 # back off slightly so that end stop is not left in triggered state
- sensor.template.publish:
id: blinds_position
state: !lambda return id(blinds_motor).target_position;
- homeassistant.service:
service: input_boolean.turn_on
data:
entity_id: input_boolean.blinds_are_closed_boolean
stepper:
- platform: a4988
id: blinds_motor
step_pin: GPIO32
dir_pin: GPIO33
max_speed: 500 steps/s
# Optional:
sleep_pin:
number: GPIO25
inverted: True
acceleration: 800
deceleration: 800
- platform: a4988
id: slats_motor
step_pin: GPIO27
dir_pin: GPIO26
max_speed: 700 steps/s
# Optional:
sleep_pin:
number: GPIO14
inverted: True
acceleration: 800
deceleration: 800
script:
# add a check that the blinds end stop is triggered first.
- id: homeslats
then:
- switch.turn_on: blinds_fault_light # turn on the fault light. If the end stop switch triggers, it'll turn it off.
- stepper.report_position:
id: slats_motor
position: 0 # set position counter to zero
- stepper.set_target: # just to be sure
id: slats_motor
target: 0
- stepper.set_target:
id: slats_motor
target: -22000 # set to minus 22000 which is more than the maximum it travel before the end stop triggered
# note. The "on press" action for the endstop switch will stop the motor and set its position to -10400
- id: homeblinds
then:
- switch.turn_on: blinds_fault_light # turn on liht at start, end stop switch will turn it off if it triggers
- stepper.report_position:
id: blinds_motor
position: 0 # set posotioncounter to zero
- stepper.set_target: #just to be sure
id: blinds_motor
target: 0
- stepper.set_target:
id: blinds_motor
target: 21000. # max from fully open to fully closed is about 19500 so switch should tigger before then
# the "on press" action of the endstop will stop the motor and set it's position toxzero (as well as turning off the fault light).
- cover.template.publish:
id: Study_Blind
state: CLOSED
The automation in question is this:
alias: "Rotate Study Blinds Slats "
description: >-
This will rotate the study blind slats with the sun relative to the window but
only during daylight hours, the blinds are closed and not in a fault
condition.
trigger:
- platform: state
entity_id: sensor.slat_pos
condition:
- condition: sun
after: sunrise
after_offset: "00:05:00"
- condition: sun
before: sunset
before_offset: "-00:05:00"
- type: is_on
condition: device
device_id: 4c52cc10fd000ad2af5aa397c7efd055
entity_id: binary_sensor.study_blinds_slats_endstop
domain: binary_sensor
- condition: state
entity_id: input_boolean.blinds_are_closed_boolean
state: "on"
action:
- service: esphome.study_blinds_slats_position
data_template:
target: "{{ trigger.to_state.state | int }}"
mode: single
When I run it, then check the trace, the error message is this:
Executed: 13 March 2023 at 12:26:51
Error: Error rendering data template: UndefinedError: 'dict object' has no attribute 'to_state'
and the offending piece of code is this:
service: esphome.study_blinds_slats_position
data_template:
target: '{{ trigger.to_state.state | int }}'
What is driving me nuts is that this all worked flawlessly and has been doing so for a couple of years or so. The problem has occurred since I flashed a new device with the same code. i had a lot of problems with entity names being changed so whether this has anything to do with it, I’m not sure. Also, I put this all together couple of years ago mostly from code that I scraped from various sources, and can’t remember how or why it works.
I’d really appreciate any help.
TIA