Last active
May 25, 2024 18:41
-
-
Save muxa/68a6e8f8cdbb734c9e0d06a9b87d0147 to your computer and use it in GitHub Desktop.
ESPHome Garage Switch with State Machine
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
substitutions: | |
room: Garage | |
device_name: Garage Switch | |
esphome: | |
name: garage_switch | |
platform: ESP8266 | |
board: esp01_1m | |
wifi: | |
ssid: !secret wifi_ssid | |
password: !secret wifi_password | |
fast_connect: true | |
logger: | |
level: DEBUG | |
api: | |
password: !secret api_password | |
ota: | |
password: !secret ota_password | |
external_components: | |
- source: | |
type: git | |
url: https://github.com/muxa/esphome-state-machine | |
binary_sensor: | |
- platform: gpio | |
pin: | |
number: GPIO16 #D0 | |
mode: INPUT | |
inverted: True | |
name: "${device_name} Button 1" | |
internal: true | |
on_press: | |
- switch.toggle: load1 | |
- platform: gpio | |
pin: | |
number: GPIO5 #D1 | |
mode: INPUT | |
inverted: True | |
name: "${device_name} Button 2" | |
internal: true | |
on_multi_click: | |
- timing: | |
- ON for at least 500ms | |
then: | |
- logger.log: "- Garage door button held over 1s" | |
- switch.turn_on: garage_opener_relay | |
- state_machine.transition: PRESS | |
- platform: status | |
id: status_sensor | |
internal: true | |
name: "${device_name} API Status" | |
on_press: # connected | |
- logger.log: "CONNECTED" | |
- delay: 1s | |
- if: | |
condition: | |
binary_sensor.is_on: closed_endstop | |
then: # open | |
- logger.log: Garage door is open according to endstop | |
- if: | |
condition: | |
- state_machine.state: CLOSED | |
then: | |
- state_machine.set: OPEN | |
else: # closed | |
- state_machine.set: CLOSED | |
on_release: # disconnected | |
- logger.log: "DISCONNECTED" | |
- platform: homeassistant | |
name: "Garage Door Contact" | |
internal: true | |
id: closed_endstop | |
entity_id: binary_sensor.garage_door_sensor_contact | |
on_press: # garage is open(ing) | |
- state_machine.transition: OPENING_DETECTED | |
on_release: # garage is closed | |
- state_machine.transition: CLOSED_DETECTED | |
- platform: template | |
name: "Garage Door Obstruction" | |
device_class: problem # on means problem detected, off means no problem (OK) | |
lambda: |- | |
auto current_state = id(garage_sm).current_state(); | |
return current_state.compare("CLOSING_INTERRUPTED") == 0 | |
|| current_state.compare("OPENING_INTERRUPTED") == 0; | |
filters: | |
delayed_on: 2s | |
switch: | |
- platform: gpio | |
name: "Garage Light" | |
pin: GPIO13 #D7 | |
id: load1 | |
- platform: gpio | |
name: "Garage Door Switch" | |
pin: GPIO12 #D6 | |
id: garage_opener_relay | |
internal: true | |
icon: "mdi:garage" | |
on_turn_on: | |
- delay: 200ms | |
- switch.turn_off: garage_opener_relay | |
status_led: | |
pin: | |
number: GPIO2 #D4 | |
inverted: yes | |
sensor: | |
- platform: wifi_signal | |
name: "${device_name} WiFi Signal" | |
update_interval: 60s | |
- platform: uptime | |
name: "${device_name} Uptime" | |
filters: | |
- lambda: return x / 86400.0; | |
unit_of_measurement: "days" | |
accuracy_decimals: 1 | |
# text_sensor: | |
# - platform: state_machine | |
# name: Garage Door State | |
state_machine: | |
- name: Garage Door State Machine | |
id: garage_sm | |
states: | |
- name: CLOSED | |
on_enter: | |
- cover.template.publish: | |
id: garage_door | |
state: CLOSED | |
current_operation: IDLE | |
- name: OPENING | |
on_enter: | |
- cover.template.publish: | |
id: garage_door | |
state: OPEN | |
current_operation: OPENING | |
- delay: 16s | |
- state_machine.transition: TIMEOUT | |
- name: OPEN | |
on_enter: | |
- cover.template.publish: | |
id: garage_door | |
state: OPEN | |
current_operation: IDLE | |
- name: OPENING_INTERRUPTED | |
on_enter: | |
- cover.template.publish: | |
id: garage_door | |
state: OPEN | |
current_operation: IDLE | |
- name: CLOSING | |
on_enter: | |
- cover.template.publish: | |
id: garage_door | |
state: OPEN | |
current_operation: CLOSING | |
- delay: 17s | |
- state_machine.transition: TIMEOUT | |
- name: CLOSING_INTERRUPTED | |
on_enter: | |
- cover.template.publish: | |
id: garage_door | |
state: OPEN | |
current_operation: IDLE | |
inputs: | |
- name: PRESS | |
transitions: | |
- CLOSED -> OPENING | |
- OPENING -> OPENING_INTERRUPTED | |
- OPENING_INTERRUPTED -> CLOSING | |
- OPEN -> CLOSING | |
- CLOSING -> CLOSING_INTERRUPTED | |
- CLOSING_INTERRUPTED -> OPENING | |
- name: TIMEOUT | |
transitions: | |
- OPENING -> OPEN | |
- CLOSING -> CLOSED | |
- name: OPENING_DETECTED | |
transitions: | |
- CLOSED -> OPENING | |
# below is a fallback so that contact sensor can override "normal" transitions | |
- CLOSING -> OPENING | |
- OPENING_INTERRUPTED -> OPENING | |
- CLOSING_INTERRUPTED -> OPENING | |
- name: CLOSED_DETECTED | |
transitions: | |
- CLOSING -> CLOSED | |
# below is a fallback so that contact sensor can override "normal" transitions | |
- OPEN -> CLOSED | |
- OPENING -> CLOSED | |
- OPENING_INTERRUPTED -> CLOSED | |
- CLOSING_INTERRUPTED -> CLOSED | |
- name: OPEN | |
transitions: | |
- CLOSED -> OPENING | |
- CLOSING -> CLOSING_INTERRUPTED | |
- CLOSING_INTERRUPTED -> OPENING | |
- OPENING_INTERRUPTED -> CLOSING | |
on_input: | |
- switch.turn_on: garage_opener_relay | |
- delay: 1500ms | |
- state_machine.transition: OPEN | |
- name: CLOSE | |
transitions: | |
- OPEN -> CLOSING | |
- OPENING -> OPENING_INTERRUPTED | |
- OPENING_INTERRUPTED -> CLOSING | |
- CLOSING_INTERRUPTED -> OPENING | |
on_input: | |
- switch.turn_on: garage_opener_relay | |
- delay: 1500ms | |
- state_machine.transition: CLOSE | |
- name: STOP | |
transitions: | |
- OPENING -> OPENING_INTERRUPTED | |
- CLOSING -> CLOSING_INTERRUPTED | |
on_input: | |
- switch.turn_on: garage_opener_relay | |
cover: | |
- platform: template | |
name: "Garage Door" | |
id: garage_door | |
device_class: garage | |
lambda: |- | |
if (id(garage_sm).current_state().compare("CLOSED") == 0) { | |
return COVER_CLOSED; | |
} else { | |
return COVER_OPEN; | |
} | |
open_action: | |
- logger.log: "Garage door is being opened by HA" | |
- state_machine.transition: OPEN | |
close_action: | |
- logger.log: "Garage door is being closed by HA" | |
- state_machine.transition: CLOSE | |
stop_action: | |
- logger.log: "Garage door is stopped by HA" | |
- state_machine.transition: STOP |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I have a garage door with a reed switch on GPIO5 that only tells me if the door is closed. I don't have a sensor to tell me direction of travel or if the door is fully open. A Shelly1 controls the door by toggling a relay. The issue is that toggling the relay while the door is still moving creates a different situation than if you toggle the relay when the door has stopped moving.
That's where I believe your state machine would help.
I'm trying to understand your code above. As far as I can see, in this example, you have two buttons:
you also have a status led on GPIO2
I think that "Garage Door Contact" is a virtual/software sensor that you create as you don't have a physical endstop sensor.
In my situation, I have a physical endstop sensor on GPIO5 that tells me if the door is closed. I don't actually have button 1&2, everything is controlled via HA.
How would you suggest being able to override your "Garage Door Contact" with GPIO sensor values when the door is closed, or when the state machine says the door should be open/opening but the GPIO sensor says it's still closed?