Skip to content

Instantly share code, notes, and snippets.

@muxa
Last active December 15, 2023 10:57
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save muxa/9d0b1be8ea7c3daed5a0d4f0db058e4f to your computer and use it in GitHub Desktop.
Save muxa/9d0b1be8ea7c3daed5a0d4f0db058e4f to your computer and use it in GitHub Desktop.
ESPHome cover single button control
#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);
}
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