I found slatri’s post: Cover component - Custom cover? and he’s doing some stuff with a rotary encoder. That’s more advanced than what I’m trying to do. I want to try to build up a 4-state garage door opener that has CLOSED/OPEN/CLOSING/OPENING
based on a state machine that contains two reed switches, a top and bottom. Something like this:
Well, I finally got it working after 3 hours… anyone else who wants to do this should look at Cover Template Publish Action it has OPENING and CLOSING operation states. Totally doable from within ESPHome and without having to use the Custom Cover.
Here’s the source code for reference (I threw the led in there for fun):
globals:
- id: last_door_state
type: int
initial_value: '0'
# last_door_states are:
# 0 = closed
# 1 = open
# 2 = opening
# 3 = closing
# 4 = error
cover:
- platform: template
name: Garage Door
id: garage_door
device_class: garage
binary_sensor:
- platform: gpio
pin: D5
device_class: opening
id: top_reed_switch
internal: True
on_state:
then:
- lambda: |-
if (id(top_reed_switch).state and not id(bot_reed_switch).state) {
id(garage_door).position = 1.0;
id(garage_door).current_operation = COVER_OPERATION_IDLE;
id(garage_door).publish_state();
id(last_door_state) = 1;
auto call = id(led).turn_off();
call.perform();
} else if (id(bot_reed_switch).state and not id(top_reed_switch).state) {
id(garage_door).position = 0.0;
id(garage_door).current_operation = COVER_OPERATION_IDLE;
id(garage_door).publish_state();
id(last_door_state) = 0;
auto call = id(led).turn_off();
call.perform();
} else if (not id(top_reed_switch).state and (id(last_door_state) == 1 or id(last_door_state) == 4)) {
id(garage_door).position = 1.0;
id(garage_door).current_operation = COVER_OPERATION_CLOSING;
id(garage_door).publish_state();
id(last_door_state) = 3;
auto call = id(led).turn_on();
call.perform();
} else if (not id(bot_reed_switch).state and (id(last_door_state) == 0 or id(last_door_state) == 4)) {
id(garage_door).position = 1.0;
id(garage_door).current_operation = COVER_OPERATION_OPENING;
id(garage_door).publish_state();
id(last_door_state) = 2;
auto call = id(led).turn_on();
call.perform();
} else {
id(last_door_state) = 4;
auto call = id(led).turn_off();
call.perform();
}
- platform: gpio
pin: D6
device_class: opening
id: bot_reed_switch
internal: True
on_state:
then:
- lambda: |-
if (id(top_reed_switch).state and not id(bot_reed_switch).state) {
id(garage_door).position = 1.0;
id(garage_door).current_operation = COVER_OPERATION_IDLE;
id(garage_door).publish_state();
id(last_door_state) = 1;
auto call = id(led).turn_off();
call.perform();
} else if (id(bot_reed_switch).state and not id(top_reed_switch).state) {
id(garage_door).position = 0.0;
id(garage_door).current_operation = COVER_OPERATION_IDLE;
id(garage_door).publish_state();
id(last_door_state) = 0;
auto call = id(led).turn_off();
call.perform();
} else if (not id(bot_reed_switch).state and (id(last_door_state) == 0 or id(last_door_state) == 4)) {
id(garage_door).position = 1.0;
id(garage_door).current_operation = COVER_OPERATION_OPENING;
id(garage_door).publish_state();
id(last_door_state) = 2;
auto call = id(led).turn_on();
call.perform();
} else if (not id(top_reed_switch).state and (id(last_door_state) == 1 or id(last_door_state) == 4)) {
id(garage_door).position = 1.0;
id(garage_door).current_operation = COVER_OPERATION_CLOSING;
id(garage_door).publish_state();
id(last_door_state) = 3;
auto call = id(led).turn_on();
call.perform();
} else {
id(last_door_state) = 4;
auto call = id(led).turn_off();
call.perform();
}
output:
- platform: gpio
id: d4_gpio2
pin:
number: D4
inverted: True
light:
- platform: binary
id: led
output: d4_gpio2
I found this while working on a similar project. We are attempting to track the door even if it’s stopped halfway. And (hopefully) eventually even adding features like being able to tell the door to open to xx% based on it learning how long it takes for the door to open/close under normal conditions.
You are welcome to check it out on github and if you require any additional functionality that we can help with, or just have some ideas, create a feature request there.
Thanks for sharing this. I actually converted to using it tonight over my solution (on all three of my garage door monitors). Primarily because I power cycled the stuff in the garage today and I noticed after a few hours that the default state when they powered up was set to OPEN even though all of their CLOSED reed sensors should have been registered. I noticed that your solution moves the lambda to the cover instead of using the sensors to set the state, this allows the reed switch values to be registered by the cover when the device powers up. Works like a charm!
I was going to use this code for my garage door but i dont see anywhere you’re outputting to a relay, is this purely for monitoring and not controlling?
@mikeycv, I recommend you use the code that @NeilDuToit92 shared on github. The code I posted at the top was a proof of concept to show that you could use two reed switches to get the state of the door fully opened or fully closed and represent it with an ESPHome cover.
The code on github is full blown and works better (because the correct state of the door is realized on boot). Anyway, you can see in that code where the reference to the relay exists starting on line 31:
switch:
# The switch that turns the UP direction on
- platform: gpio
pin: D1
id: garage_switch
# If ESP reboots, do not attempt to restore switch state
restore_mode: ALWAYS_OFF
on_turn_on:
- delay: 500ms
- switch.turn_off: garage_switch
I have three garage doors and two of them use button technology that isn’t easily manipulated (cannot just short the pins to trigger a button press event). I found this awesome post from @TaperCrimp which explains how to wire in a garage door opener remote control.
I then created three separate units:
- one that handles it’s own button and door state (fully self contained)
- one that watches its door state and has the remote wired in with two relays for hitting two of the buttons, it publishes a service to Home Assistant for pressing the button for the other garage door
- the last one that just monitors state, it has a
template
switch on it that calls a Home Assistant service to press the button on the other device.
It seems a little janky when you think about the process but in reality it’s completely transparent and the interface works like you’d expect.
Interesting read. I have been an advocator of two reed switches on a garage door for a long time. Way better feed back.
For myself I made a template sensor in Home Assistant for it.
- platform: template
sensors:
garage_door:
friendly_name: 'Garage Door'
value_template: >-
{%- if is_state("binary_sensor.garage_closed", "on") %}
Garage Door Closed
{%- elif is_state("binary_sensor.garage_open", "on") %}
Garage Door Open
{%- else %}
Garage Door In Transit
{%- endif %}
## And for the garage door cover
- platform: template
sensors:
garage_door_sensor:
friendly_name: 'Garage Door Sensor'
value_template: >-
{%- if is_state("binary_sensor.garage_closed", "on") %}
closed
{%- elif is_state("binary_sensor.garage_open", "on") %}
open
{%- endif %}
Downside is I still have yet to solve the log errors I get with it
2021-01-25 17:18:33 ERROR (MainThread) [homeassistant.components.template.cover] Received invalid cover is_on state: . Expected: open, opening, closed, closing, true, false
If you haven’t started dabbling in ESPHome this would be a great way to start. Highly recommend using ESPHome to handle these low-level operations as opposed to using template sensors in HA. Don’t get me wrong, template sensors from HA have their place but for ‘smarting’ a garage door opener, I don’t think anything beats ESPHome. As a tantalizer/example of what you can do, here are the controls and outputs from my center garage door opener. It’s run from a Wemos D1 Mini:
I have motion sensors on the other two and much of those outputs (like uptime, wifi strength, SSID, etc.) are templated in ESPHome so they exist on all my ESPHome devices automatically. The raw reed switch outputs are hidden from HA and only exist as the “cover status”: the icon shows you that the door is closed, opening, open, or closing – exactly as you’d hope/expect.
ESPHome FTW!
Yeh, I’m a big advocator of ESPhome. Converted from Tasmota due to Tasmota not sending current switch status after reboot of the sensor or home assistant. Status wouldn’t get updated until a state change. ESPhome did it out of the box. Before that I was using my sensors pre 2.0. Love my ESP’s.
I think I’ll be diving into throwing templates on the device as you said.
Revisiting this post again as it’s one of the only decent garage door sensor posts, and I’m trying to get a sensor working how I want it with my n00b skills.
All this code seems a little too complicated for what it needs to do. Or I’m not understanding it totally.
So from my point of view I wanted to discuss with @SpikeyGG and @NeilDuToit92 who have their heads around it.
Home Assistant cover has 4 states I want it to see. Open, Closed, Opening, Closing.
With two sensors on the door we can have logic to get that.
Open = Open switch closed
Closed = Closed switch closed
Opening = State change of Closed switch to open
Closing = State change of Open switch to open
Should it not be as simple as a lambda with states for open and closed and an on_release state for opening or closing?
I can’t seem to get the code right. and the doc’s aren’t quite the best either for what I’m trying to do.
Note, I’m only trying to deal with sensors and not any control.
This is what I was trying to do, and ESPHome didn’t like my template.
I set up a global called transit to track the logic of the sensors
1= open
2=closed
3=closing
4=opening
I was using the binary sensor to set the global data with on_set and on_release
Trying to get a template lambda to react to the numbers 1-4 with the associated situation of the door.
The binary sensor part of the code was working, the template obviously didn’t compile lol
globals:
- id: transit
type: int
binary_sensor:
- platform: gpio
pin:
number: GPIO13
inverted: false
name: "Garage_Open"
device_class: door
filters:
- delayed_on: 1000ms
- delayed_off: 1000ms
- invert:
on_release:
- globals.set:
id: transit
value: '3'
- logger.log: " Transit = 3 = Closing"
on_press:
- globals.set:
id: transit
value: '1'
- logger.log: " Transit = 1 = Open"
- cover.template.publish
id: door_position_sensor
state: OPEN
- platform: gpio
pin:
number: GPIO12
inverted: false
name: "Garage_Closed"
device_class: door
filters:
- delayed_on: 1000ms
- delayed_off: 1000ms
on_press:
- globals.set:
id: transit
value: '4'
- logger.log: " Transit = 4 = Opening"
on_release:
- globals.set:
id: transit
value: '2'
- logger.log: " Transit = 2 = Closed"
sensor:
- platform: template
name: "Door Position Sensor"
id: door_position_sensor
lambda: |-
if (id(transit)=1) {
return COVER_OPEN;
}
if (id(transit)=2) {
return COVER_CLOSED;
}
if (id(transit)=3) {
return COVER_CLOSING;
}
if (id(transit)=4) {
return COVER_OPENING;
}
Been looking for this for a while.
I want a ‘passive’ cover monitor, not one that controls the door, but just tells me its state.
I long ago tapped into the door-opener’s internal controller and found a pair of traveling contactors that were happy to share their grounded/not-grounded state (floating to logic-level when not grounded) which indicates where the opener believes the door to be. A simple voltage divider makes them GPIO-compatible signals.
So I wrote my own code back then to deliver that 4-state info via MQTT, but would like to convert that to ESPHome for easier maintenance ongoing.
EDIT: Fixed missing sections of yml
To just monitor the door, in theory, you can do the following (I have not tested it as I don’t have access to devices at the moment)
- You don’t need a global variable, you just set the
door_position_sensor
template directly when one of theclosed_endstop
oropen_endstop
triggerson_press
andon_release
- You don’t need to specify the gpio pins are of class door
- You can (Optionally) set the gpio pins to internal as you don’t care about their status in HA, only the status of the template
- You don’t need to add filters because there is no logic being triggered other than setting the sensor
- You don’t need a lambda on the template sensor as it doesn’t need to determine it’s own value, it gets set when the GPIO changes.
binary_sensor:
- platform: gpio
internal: true
pin:
number: D5
mode: INPUT_PULLUP
inverted: true
name: "Garage Closed Reed Switch"
id: closed_endstop
on_press:
- logger.log: " Transit Transit = 2 = Closed"
- sensor.template.publish:
id: door_position_sensor
state: '2'
on_release:
- logger.log: " Transit = 3 = Closing"
- sensor.template.publish:
id: door_position_sensor
state: '3'
- platform: gpio
internal: true
pin:
number: D6
mode: INPUT_PULLUP
inverted: true
name: "Garage Open Reed Switch"
id: open_endstop
on_press:
- logger.log: " Transit = 1 = Open"
- sensor.template.publish:
id: door_position_sensor
state: '1'
on_release:
- logger.log: " Transit = 4 = Opening"
- sensor.template.publish:
id: door_position_sensor
state: '4'
sensor:
- platform: template
name: "Door Position Sensor"
id: door_position_sensor
In this example, HA will have to map status 1-4 to the relevant status, you can make the 1-4 any values you want like OPENING, CLOSING, OPEN, CLOSED and it will pull those values through to HA.
Thanks @NeilDuToit92, Didn’t think ESP could be setup like you have written.
Getting help with code is one awesome thing, but I like to know what it’s doing.
I didn’t think / didn’t fully understand what the doc’s was saying, you could just set up a blank per se template sensor without defining what it does.
But it seems you can, then as you have written, the templates state is publishes a change in state when there is a automations on the GPIO.
I was trying to code assuming I could only change the state of a template sensor in a lambda / automation nested in said template sensor. Hence the whole 1-4 numbering.
I want ESPhome to send the data out how HA wants the sensor data seen for a template sensor eg
value_template
Defines a template to get the state of the cover. Valid values are open
/true
or opening
/closing
/closed
/false
.
So the above was typed before I did some coding, ESPhome still doesn’t like what I am doing.
binary_sensor:
- platform: gpio
pin:
number: GPIO12
mode: INPUT_PULLUP
inverted: false
filters:
- delayed_on: 1000ms
- delayed_off: 1000ms
name: "Garage Closed Sensor"
id: closed_endstop
on_press:
- logger.log: "CLOSED"
- sensor.template.publish:
id: door_position_sensor
state: 'CLOSED'
on_release:
- logger.log: "CLOSING"
- sensor.template.publish:
id: door_position_sensor
state: "CLOSING"
- platform: gpio
pin:
number: GPIO13
mode: INPUT_PULLUP
inverted: false
filters:
- delayed_on: 1000ms
- delayed_off: 1000ms
name: "Garage Open Sensor"
id: open_endstop
on_press:
- logger.log: "OPEN"
- sensor.template.publish:
id: door_position_sensor
state: "OPEN"
on_release:
- logger.log: "OPENING"
- sensor.template.publish:
id: door_position_sensor
state: "OPENING"
sensor:
- platform: template
name: "Door Position Sensor"
id: door_position_sensor
It gives an error on the all 4 state’s for sensor.template.publish
Eg
state: CLOSED
The error is “expected float”
Few notes from your reply before
Not keeping the binary sensor internal as I want to see the raw data from the binary sensor in HA
My reed switches needed the debouncing filter, as they were bouncing.
Well I solved that one quick.
I had set it up as a template sensor, which expects a number as its state. rather obvious now.
So that’s all fixed and now will submit a pull request as the template cover does not allow opening or closing attributes. Which it should to line up with a template cover’s possible options in HA.
Well here is how I have it working in Home Assistant.
The Current_Operation publish action will tell home assistant that its opening or closing.
binary_sensor:
- platform: gpio
pin:
number: GPIO12
mode: INPUT
inverted: true
filters:
- delayed_on: 1000ms
- delayed_off: 1000ms
name: "Garage Closed Sensor"
id: closed_endstop
on_press:
- logger.log: "CLOSED"
- cover.template.publish:
id: door_position_sensor
current_operation: IDLE
state: 'CLOSED'
on_release:
- logger.log: "OPENING"
- cover.template.publish:
id: door_position_sensor
current_operation: OPENING
state: "OPEN"
- platform: gpio
pin:
number: GPIO13
mode: INPUT
inverted: true
filters:
- delayed_on: 1000ms
- delayed_off: 1000ms
name: "Garage Open Sensor"
id: open_endstop
on_press:
- logger.log: "OPEN"
- cover.template.publish:
id: door_position_sensor
current_operation: IDLE
state: "OPEN"
on_release:
- logger.log: "CLOSING"
- cover.template.publish:
id: door_position_sensor
current_operation: CLOSING
state: "OPEN"
cover:
- platform: template
name: "Door Position Sensor"
id: door_position_sensor
So the next big problem is that should the ESP loose power, When it powers back up it will assume the template cover is closed, due to all the state and operation changes are changed via an action. And if that action takes place when there is no power on the ESP, well it didn’t happen.
Garage door coveres need to be bomb proof, you need to trust their states and actions 100%
(over christmas I had my garage door open uncommanded in an electrical surge. I was 400kms away, the surge also took out my wifi router)
And now what seems to be a final decent code, will report if its open or closed on power up.
binary_sensor:
- platform: gpio
pin:
number: GPIO12
mode: INPUT
inverted: true
filters:
- delayed_on: 1000ms
- delayed_off: 1000ms
name: "Garage Closed Sensor"
id: closed_endstop
on_press:
- logger.log: "CLOSED"
- cover.template.publish:
id: door_position_sensor
current_operation: IDLE
state: 'CLOSED'
on_release:
- logger.log: "OPENING"
- cover.template.publish:
id: door_position_sensor
current_operation: OPENING
state: "OPEN"
- platform: gpio
pin:
number: GPIO13
mode: INPUT
inverted: true
filters:
- delayed_on: 1000ms
- delayed_off: 1000ms
name: "Garage Open Sensor"
id: open_endstop
on_press:
- logger.log: "OPEN"
- cover.template.publish:
id: door_position_sensor
current_operation: IDLE
state: "OPEN"
on_release:
- logger.log: "CLOSING"
- cover.template.publish:
id: door_position_sensor
current_operation: CLOSING
state: "OPEN"
cover:
- platform: template
name: "Door Position Sensor"
id: door_position_sensor
lambda: |-
if (id(closed_endstop).state) {
return COVER_CLOSED;
} else {
return COVER_OPEN;
}
This last code looks pretty good, have you developed it further more to allow for example positioning the cover in terms of %%?
eg: open cover to 20%?
No, It’s used on my garage door, it only has position for full open and full closed.
@SpikeyGG - any chance you can share your YAML from your garage setup? Seems you have a number of entities that mirror what I’m trying to do.