ESPHome-Devices/components/garage_door/cover.py
Chris Nussbaum 5820355a7f
F/garage door (#45)
* Start work on the garage door controller

* More work on the Garage Door

* Another update

* Get garage door to compilable state

* Core functionality is mostly complete

* More work on the garage door

* Garage door (mostly) ready for testing

* Begin debugging

* More debugging work

* Closer to a working state

* Hoping this is close to the final version

* Couple more tweaks

* Still have some significant bugs

* So close!!

* Add sensor read delays

* Properly handle stopping when endstops are reached

* Adjust some timings

* Cleanup, more logging, and timing adjustments

* Fix typo

* Major refactor of the state management and fix the repeated disconnects

* More adjustments

* Add schematic

* Mostly complete

* Make device more configurable

---------

Co-authored-by: Chris Nussbaum <chris.nussbaum@protolabs.com>
2023-02-16 13:31:58 -06:00

129 lines
5.7 KiB
Python

import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import cover, output, sensor, binary_sensor, rtttl
from esphome.const import (
CONF_ID,
CONF_OPEN_DURATION,
CONF_CLOSE_DURATION,
UNIT_MILLISECOND,
DEVICE_CLASS_DURATION,
STATE_CLASS_MEASUREMENT,
ICON_TIMER,
ENTITY_CATEGORY_DIAGNOSTIC,
)
AUTO_LOAD = ["lock"]
CONF_CONTROL_OUTPUT = "control_output"
CONF_BUTTON_SENSOR = "button_sensor"
CONF_CLOSED_SENSOR = "closed_sensor"
CONF_OPEN_SENSOR = "open_sensor"
CONF_REMOTE_SENSOR = "remote_sensor"
CONF_REMOTE_LIGHT_SENSOR = "remote_light_sensor"
CONF_WARNING_RTTTL = "warning_rtttl"
CONF_CLOSE_WARNING_TONES = "close_warning_tones"
DEFAULT_CLOSE_WARNING_TONES = "Imperial:d=4, o=5, b=100:e, e, e, 8c, 16p, 16g, e, 8c, 16p, 16g, e, p, b, b, b, 8c6, 16p, 16g, d#, 8c, 16p, 16g, e, 8p"
CONF_CONTROL_ACTIVE_DURATION = "control_active_duration"
CONF_CONTROL_INACTIVE_DURATION = "control_inactive_duration"
CONF_REVERSES_ON_STOP_OPENING = "reverses_on_stop_opening"
CONF_REVERSES_ON_STOP_CLOSING = "reverses_on_stop_closing"
CONF_LAST_OPEN_TIME_SENSOR = "last_open_time_sensor"
CONF_LAST_CLOSE_TIME_SENSOR = "last_close_time_sensor"
DEVICE_CLASS = "garage"
cover_ns = cg.esphome_ns.namespace("cover")
GarageDoor = cover_ns.class_("GarageDoor", cover.Cover, cg.PollingComponent)
CONFIG_SCHEMA = (
cover.COVER_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(GarageDoor),
cv.Required(CONF_OPEN_DURATION): cv.positive_time_period_milliseconds,
cv.Required(CONF_CLOSE_DURATION): cv.positive_time_period_milliseconds,
cv.Required(CONF_CONTROL_OUTPUT): cv.use_id(output.BinaryOutput),
cv.Required(CONF_BUTTON_SENSOR): cv.use_id(sensor.Sensor),
cv.Required(CONF_CLOSED_SENSOR): cv.use_id(binary_sensor.BinarySensor),
cv.Required(CONF_OPEN_SENSOR): cv.use_id(binary_sensor.BinarySensor),
cv.Required(CONF_REMOTE_SENSOR): cv.use_id(binary_sensor.BinarySensor),
cv.Required(CONF_REMOTE_LIGHT_SENSOR): cv.use_id(binary_sensor.BinarySensor),
cv.Required(CONF_WARNING_RTTTL): cv.use_id(rtttl.Rtttl),
cv.Optional(CONF_CLOSE_WARNING_TONES, default=DEFAULT_CLOSE_WARNING_TONES): cv.string,
cv.Optional(CONF_CONTROL_ACTIVE_DURATION, default="200ms"): cv.positive_time_period_milliseconds,
cv.Optional(CONF_CONTROL_INACTIVE_DURATION, default="200ms"): cv.positive_time_period_milliseconds,
cv.Optional(CONF_REVERSES_ON_STOP_OPENING, default="false"): cv.boolean,
cv.Optional(CONF_REVERSES_ON_STOP_CLOSING, default="false"): cv.boolean,
cv.Optional(CONF_LAST_OPEN_TIME_SENSOR): sensor.sensor_schema(
unit_of_measurement=UNIT_MILLISECOND,
device_class=DEVICE_CLASS_DURATION,
state_class=STATE_CLASS_MEASUREMENT,
icon=ICON_TIMER,
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
),
cv.Optional(CONF_LAST_CLOSE_TIME_SENSOR): sensor.sensor_schema(
unit_of_measurement=UNIT_MILLISECOND,
device_class=DEVICE_CLASS_DURATION,
state_class=STATE_CLASS_MEASUREMENT,
icon=ICON_TIMER,
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
),
}
)
)
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await cover.register_cover(var, config)
cg.add(var.set_device_class(DEVICE_CLASS))
cg.add(var.set_open_duration(config[CONF_OPEN_DURATION]))
cg.add(var.set_close_duration(config[CONF_CLOSE_DURATION]))
control_output = await cg.get_variable(config[CONF_CONTROL_OUTPUT])
cg.add(var.set_control_output(control_output))
button_sensor = await cg.get_variable(config[CONF_BUTTON_SENSOR])
cg.add(var.set_button_sensor(button_sensor))
closed_sensor = await cg.get_variable(config[CONF_CLOSED_SENSOR])
cg.add(var.set_closed_sensor(closed_sensor))
open_sensor = await cg.get_variable(config[CONF_OPEN_SENSOR])
cg.add(var.set_open_sensor(open_sensor))
remote_sensor = await cg.get_variable(config[CONF_REMOTE_SENSOR])
cg.add(var.set_remote_sensor(remote_sensor))
remote_light_sensor = await cg.get_variable(config[CONF_REMOTE_LIGHT_SENSOR])
cg.add(var.set_remote_light_sensor(remote_light_sensor))
warning_rtttl = await cg.get_variable(config[CONF_WARNING_RTTTL])
cg.add(var.set_warning_rtttl(warning_rtttl))
close_warning_tones = config[CONF_CLOSE_WARNING_TONES]
cg.add(var.set_close_warning_tones(close_warning_tones))
control_active_duration = config[CONF_CONTROL_ACTIVE_DURATION]
cg.add(var.set_control_active_duration(control_active_duration))
control_inactive_duration = config[CONF_CONTROL_INACTIVE_DURATION]
cg.add(var.set_control_inactive_duration(control_inactive_duration))
reverses_on_stop_opening = config[CONF_REVERSES_ON_STOP_OPENING]
cg.add(var.set_reverses_on_stop_opening(reverses_on_stop_opening))
reverses_on_stop_closing = config[CONF_REVERSES_ON_STOP_CLOSING]
cg.add(var.set_reverses_on_stop_closing(reverses_on_stop_closing))
if CONF_LAST_OPEN_TIME_SENSOR in config:
last_open_time_config = config[CONF_LAST_OPEN_TIME_SENSOR]
last_open_time_sensor = await sensor.new_sensor(last_open_time_config)
cg.add(var.set_last_open_time_sensor(last_open_time_sensor))
if CONF_LAST_CLOSE_TIME_SENSOR in config:
last_close_time_config = config[CONF_LAST_CLOSE_TIME_SENSOR]
last_close_time_sensor = await sensor.new_sensor(last_close_time_config)
cg.add(var.set_last_close_time_sensor(last_close_time_sensor))