Last active
December 15, 2023 10:57
-
-
Save muxa/9d0b1be8ea7c3daed5a0d4f0db058e4f to your computer and use it in GitHub Desktop.
ESPHome cover single button control
This file contains 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
#include "esphome.h" | |
const extern short STATE_UNKNOWN = 0; | |
const extern short STATE_CLOSED = 1; | |
const extern short STATE_OPENING = 2; | |
const extern short STATE_OPEN = 3; | |
const extern short STATE_OPENING_INTERRUPTED = 4; | |
const extern short STATE_CLOSING = 5; | |
const extern short STATE_CLOSING_INTERRUPTED = 6; | |
bool is_problem(short state) { | |
switch (state) { | |
case STATE_UNKNOWN: | |
case STATE_OPENING_INTERRUPTED: | |
case STATE_CLOSING_INTERRUPTED: | |
return true; | |
default: | |
return false; | |
} | |
} | |
void log_state(short state) { | |
switch (state) { | |
case STATE_UNKNOWN: | |
ESP_LOGD("SM", "STATE_UNKNOWN"); | |
break; | |
case STATE_CLOSED: | |
ESP_LOGD("SM", "STATE_CLOSED"); | |
break; | |
case STATE_OPENING: | |
ESP_LOGD("SM", "STATE_OPENING"); | |
break; | |
case STATE_OPEN: | |
ESP_LOGD("SM", "STATE_OPEN"); | |
break; | |
case STATE_OPENING_INTERRUPTED: | |
ESP_LOGD("SM", "STATE_OPENING_INTERRUPTED"); | |
break; | |
case STATE_CLOSING: | |
ESP_LOGD("SM", "STATE_CLOSING"); | |
break; | |
case STATE_CLOSING_INTERRUPTED: | |
ESP_LOGD("SM", "STATE_CLOSING_INTERRUPTED"); | |
break; | |
default: | |
ESP_LOGW("SM", "Invalid state: %d", state); | |
break; | |
} | |
} | |
void set_cover_state(short state) { | |
switch (state) { | |
case STATE_CLOSED: | |
id(garage_door).current_operation = esphome::cover::COVER_OPERATION_IDLE; | |
id(garage_door).position = esphome::cover::COVER_CLOSED; | |
id(garage_door).publish_state(); | |
break; | |
case STATE_OPENING: | |
id(garage_door).current_operation = esphome::cover::COVER_OPERATION_OPENING; | |
id(garage_door).position = esphome::cover::COVER_OPEN; | |
id(garage_door).publish_state(); | |
break; | |
case STATE_CLOSING: | |
id(garage_door).current_operation = esphome::cover::COVER_OPERATION_CLOSING; | |
id(garage_door).position = esphome::cover::COVER_OPEN; | |
id(garage_door).publish_state(); | |
break; | |
case STATE_OPEN: | |
case STATE_OPENING_INTERRUPTED: | |
case STATE_CLOSING_INTERRUPTED: | |
id(garage_door).current_operation = esphome::cover::COVER_OPERATION_IDLE; | |
id(garage_door).position = esphome::cover::COVER_OPEN; | |
id(garage_door).publish_state(); | |
break; | |
default: | |
ESP_LOGW("SM", "Invalid state: %d", state); | |
break; | |
} | |
} | |
void set_cover_sm_state(short state) { | |
id(current_state) = state; | |
set_cover_state(state); | |
log_state(state); | |
} |
This file contains 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 | |
includes: | |
- garage-switch.h | |
wifi: | |
ssid: !secret wifi_ssid | |
password: !secret wifi_password | |
logger: | |
level: DEBUG | |
api: | |
password: !secret api_password | |
ota: | |
password: !secret ota_password | |
binary_sensor: | |
- platform: gpio | |
pin: | |
number: GPIO5 | |
mode: INPUT | |
inverted: True | |
name: "${device_name} Button" | |
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 | |
- script.execute: process_press | |
- platform: status | |
id: status_sensor | |
internal: true | |
name: "${device_name} API Status" | |
on_press: # connected | |
- logger.log: "CONNECTED" | |
- delay: 1s | |
- lambda: |- | |
if (id(closed_endstop).state) { | |
// open | |
ESP_LOGD("SM", "Garage door is open according to endstop"); | |
set_cover_sm_state(STATE_OPEN); | |
} else { | |
// closed | |
id(process_endstop_closed).execute(); | |
} | |
on_release: # disconnected | |
- logger.log: "DISCONNECTED" | |
- lambda: |- | |
id(begin_open_timeout).stop(); | |
id(begin_close_timeout).stop(); | |
id(delayed_open).stop(); | |
id(delayed_close).stop(); | |
id(current_state) = STATE_UNKNOWN; | |
- 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) | |
- script.execute: process_endstop_open | |
on_release: # garage is closed | |
- script.execute: process_endstop_closed | |
- platform: template | |
name: "Garage Door Obstruction" | |
device_class: problem # on means problem detected, off means no problem (OK) | |
lambda: |- | |
return is_problem(id(current_state)); | |
filters: | |
delayed_on: 2s | |
switch: | |
- platform: gpio | |
name: "Garage Door Switch" | |
pin: GPIO12 | |
id: garage_opener_relay | |
icon: "mdi:garage" | |
on_turn_on: | |
- delay: 200ms | |
- switch.turn_off: garage_opener_relay | |
globals: | |
- id: current_state | |
type: short | |
restore_value: no | |
initial_value: '1' | |
script: | |
- id: process_press | |
then: | |
- lambda: |- | |
id(begin_open_timeout).stop(); | |
id(begin_close_timeout).stop(); | |
switch (id(current_state)) { | |
case STATE_CLOSED: | |
set_cover_sm_state(STATE_OPENING); | |
id(begin_open_timeout).execute(); | |
break; | |
case STATE_OPENING: | |
set_cover_sm_state(STATE_OPENING_INTERRUPTED); | |
break; | |
case STATE_OPEN: | |
set_cover_sm_state(STATE_CLOSING); | |
id(begin_close_timeout).execute(); | |
break; | |
case STATE_OPENING_INTERRUPTED: | |
set_cover_sm_state(STATE_CLOSING); | |
id(begin_close_timeout).execute(); | |
break; | |
case STATE_CLOSING: | |
set_cover_sm_state(STATE_CLOSING_INTERRUPTED); | |
break; | |
case STATE_CLOSING_INTERRUPTED: | |
set_cover_sm_state(STATE_OPENING); | |
id(begin_open_timeout).execute(); | |
break; | |
default: | |
ESP_LOGW("SM", "Invalid state for press: %d", id(current_state)); | |
break; | |
} | |
- id: process_open | |
then: | |
- lambda: |- | |
id(begin_open_timeout).stop(); | |
id(begin_close_timeout).stop(); | |
switch (id(current_state)) { | |
case STATE_CLOSED: | |
set_cover_sm_state(STATE_OPENING); | |
id(garage_opener_relay).turn_on(); | |
id(begin_open_timeout).execute(); | |
break; | |
case STATE_OPENING_INTERRUPTED: | |
set_cover_sm_state(STATE_CLOSING); | |
id(garage_opener_relay).turn_on(); // this starts closing | |
id(delayed_open).execute(); // this will stop and start opening | |
break; | |
case STATE_CLOSING: | |
set_cover_sm_state(STATE_CLOSING_INTERRUPTED); | |
id(garage_opener_relay).turn_on(); // this stops closing | |
id(delayed_open).execute(); // this will start opening | |
break; | |
case STATE_CLOSING_INTERRUPTED: | |
set_cover_sm_state(STATE_OPENING); | |
id(garage_opener_relay).turn_on(); | |
id(begin_open_timeout).execute(); | |
break; | |
default: | |
ESP_LOGW("SM", "Invalid state for open: %d", id(current_state)); | |
break; | |
} | |
- id: process_stop | |
then: | |
- lambda: |- | |
id(begin_open_timeout).stop(); | |
id(begin_close_timeout).stop(); | |
switch (id(current_state)) { | |
case STATE_OPENING: | |
set_cover_sm_state(STATE_OPENING_INTERRUPTED); | |
id(garage_opener_relay).turn_on(); | |
break; | |
case STATE_CLOSING: | |
set_cover_sm_state(STATE_CLOSING_INTERRUPTED); | |
id(garage_opener_relay).turn_on(); | |
break; | |
default: | |
ESP_LOGW("SM", "Invalid state for stop: %d", id(current_state)); | |
break; | |
} | |
- id: process_close | |
then: | |
- lambda: |- | |
id(begin_open_timeout).stop(); | |
id(begin_close_timeout).stop(); | |
switch (id(current_state)) { | |
case STATE_OPEN: | |
set_cover_sm_state(STATE_CLOSING); | |
id(garage_opener_relay).turn_on(); | |
id(begin_close_timeout).execute(); | |
break; | |
case STATE_OPENING: | |
set_cover_sm_state(STATE_OPENING_INTERRUPTED); | |
id(garage_opener_relay).turn_on(); // this stops opening | |
id(delayed_close).execute(); // this will start closing | |
break; | |
case STATE_OPENING_INTERRUPTED: | |
set_cover_sm_state(STATE_CLOSING); | |
id(garage_opener_relay).turn_on(); | |
id(begin_close_timeout).execute(); | |
break; | |
case STATE_CLOSING_INTERRUPTED: | |
set_cover_sm_state(STATE_OPENING); | |
id(garage_opener_relay).turn_on(); // this starts opening | |
id(delayed_close).execute(); // this will stop and start closing | |
break; | |
default: | |
ESP_LOGW("SM", "Invalid state for close: %d", id(current_state)); | |
break; | |
} | |
- id: process_timeout | |
then: | |
- lambda: |- | |
switch (id(current_state)) { | |
case STATE_OPENING: | |
set_cover_sm_state(STATE_OPEN); | |
break; | |
case STATE_CLOSING: | |
ESP_LOGW("SM", "Garage failed to closed in time."); | |
set_cover_sm_state(STATE_CLOSING_INTERRUPTED); | |
break; | |
default: | |
ESP_LOGW("SM", "Invalid state for timeout: %d", id(current_state)); | |
break; | |
} | |
- id: process_endstop_open | |
then: | |
- lambda: |- | |
switch (id(current_state)) { | |
case STATE_CLOSED: | |
ESP_LOGD("SM", "Garage door is being opened by a remote"); | |
set_cover_sm_state(STATE_OPENING); | |
id(begin_open_timeout).execute(); | |
break; | |
default: | |
ESP_LOGW("SM", "Invalid state for endstop open: %d", id(current_state)); | |
break; | |
} | |
- id: process_endstop_closed | |
then: | |
- logger.log: "Garage door is closed as detected by endstop" | |
- lambda: |- | |
id(begin_open_timeout).stop(); | |
id(begin_close_timeout).stop(); | |
id(delayed_open).stop(); | |
id(delayed_close).stop(); | |
set_cover_sm_state(STATE_CLOSED); | |
- id: begin_open_timeout | |
mode: restart | |
then: | |
- script.stop: begin_close_timeout | |
- delay: 16s | |
- script.execute: process_timeout | |
- id: delayed_open | |
mode: restart | |
then: | |
- script.stop: delayed_close | |
- delay: 1500ms | |
- script.execute: process_open | |
- id: delayed_close | |
mode: restart | |
then: | |
- script.stop: delayed_open | |
- delay: 1500ms | |
- script.execute: process_close | |
- id: begin_close_timeout | |
mode: restart | |
then: | |
- script.stop: begin_open_timeout | |
- delay: 17s | |
- script.execute: process_timeout | |
cover: | |
- platform: template | |
name: "Garage Door" | |
id: garage_door | |
device_class: garage | |
assumed_state: true | |
open_action: | |
- logger.log: "Garage door is being opened by HA" | |
- script.execute: process_open | |
close_action: | |
- logger.log: "Garage door is being closed by HA" | |
- script.execute: process_close | |
stop_action: | |
- logger.log: "Garage door is stopped by HA" | |
- script.execute: process_stop |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment