Looking for examples of "custom cover" from ESPHome.io

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.

1 Like

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. :slight_smile:

1 Like

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 the closed_endstop or open_endstop triggers on_press and on_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
1 Like

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.

Sure thing! Although, about half of the entities from my screenshot above come from “boilerplate yaml” that is included with every ESPHome device (highly recommended). You’ll see the line device_base: !include common/device_base_esp8266.yaml which points at more files to build up the boilerplate. I’ve included all the important files at the bottom. I’m not including my wifi.yaml because it has some customizations in it but it’s only the wifi: block.

This build is using:

Here’s the big one: raygarage.yaml

substitutions:
  node_name: raygarage
  friendly_name: Ray's Garage
  board: d1_mini
  restore_from_flash: 'False'
  log_level: NONE
  library_priority: '0'
  garage_priority: '1'
  
packages:
  wifi: !include common/wifi.yaml
  device_base: !include common/device_base_esp8266.yaml

i2c:
  scl: D2
  sda: D4
  scan: True

ads1115:
  - address: 0x48

globals:
  - id: performing_last_movement
    type: boolean
    restore_value: no
    initial_value: 'false'

sensor:
  - platform: ultrasonic
    update_interval: 5s
    timeout: 4.0m
    trigger_pin: D0
    echo_pin: D7
    name: Ray's Bay Distance
    id: ray_s_bay_distance
    filters:
      - median:
          window_size: 5
          send_every: 5
          send_first_at: 3
  # Use D3 for DHT22 and I don't need an external pull-up
  - platform: dht
    update_interval: 5s
    pin: D3
    model: AM2302
    temperature:
      name: "Garage Temperature"
      filters:
        - lambda: return x * (9.0/5.0) + 32.0;
        - median:
            window_size: 31
            send_every: 6
            send_first_at: 2
      unit_of_measurement: "°F"
    humidity:
      name: "Garage Humidity"
      filters:
        - median:
            window_size: 31
            send_every: 6
            send_first_at: 2
  - platform: ads1115
    update_interval: 6s
    multiplexer: 'A0_GND'
    gain: 6.144
    name: "Garage Illuminance"
    icon: mdi:candle
    # Added device_class to customize icon.
    device_class: ""
    accuracy_decimals: 0
    filters:
      - lambda: return (x / 10000.0) * 2000000.0;
      - median:
          window_size: 15
          send_every: 10
          send_first_at: 3
    unit_of_measurement: 'lx'
  - platform: ads1115
    update_interval: 6s
    multiplexer: 'A1_GND'
    gain: 6.144
    name: "Garage CO Content"
    icon: mdi:molecule-co
    # Added device_class to customize icon.
    device_class: ""
    accuracy_decimals: 2
    unit_of_measurement: 'V'
    filters:
      - median:
          window_size: 15
          send_every: 10
          send_first_at: 9

switch:
  - platform: gpio
    id: ray_button
    internal: true
    pin:
      number: D1
      mode: OUTPUT
    restore_mode: ALWAYS_OFF
    on_turn_on:
      - delay: 1500ms
      - switch.turn_off: ray_button

cover:
  - platform: template
    name: Ray's Garage Door
    id: ray_s_garage_door
    device_class: garage
    lambda: |-
      if (id(closed_endstop).state) //Door at closed endstop
      {
        if (id(ray_s_garage_door).current_operation ==  esphome::cover::COVER_OPERATION_OPENING) //We should be opening
        {
          if (!id(performing_last_movement)) //Make sure we don't trigger this logic twice otherwise it will do unwanted things
          {
            delay(1000); //Wait for door to stop in case reed is triggered too early
            id(ray_button).turn_on(); //Press button again
            id(performing_last_movement) = true; //Set flag to indicate we madeknow where the door is
          }
        }
        else if (id(ray_s_garage_door).current_operation ==  esphome::cover::COVER_OPERATION_CLOSING)
        {
          //We should be closing, so all is good
          id(performing_last_movement) = false;
          id(ray_s_garage_door).current_operation =  esphome::cover::COVER_OPERATION_IDLE;
          id(ray_s_garage_door).position = COVER_CLOSED;
          id(ray_s_garage_door).publish_state();
          return COVER_CLOSED;
        }
        else
        {
          //No operation in progress, just send state
          id(performing_last_movement) = false;
          if (!id(ray_s_garage_door).position == esphome::cover::COVER_CLOSED)
          {
            id(ray_s_garage_door).position = COVER_CLOSED;
            id(ray_s_garage_door).publish_state();
            return COVER_CLOSED;
          }
        }
      }
      else if (id(open_endstop).state) //Door at open endstop
      {
        if (id(ray_s_garage_door).current_operation ==  esphome::cover::COVER_OPERATION_CLOSING) //We should be closing
        {
          if (!id(performing_last_movement))  //Make sure we don't trigger this logic twice otherwise it will do unwanted things
          {
            delay(1000);  //Wait for door to stop in case reed is triggered too early
            id(ray_button).turn_on(); //Press button again
            id(performing_last_movement) = true; //Set flag to indicate we madeknow where the door is
          }
        }
        else if (id(ray_s_garage_door).current_operation ==  esphome::cover::COVER_OPERATION_OPENING)
        {
          //We should be opening, so all is good
          id(performing_last_movement) = false;
          id(ray_s_garage_door).current_operation =  esphome::cover::COVER_OPERATION_IDLE;
          id(ray_s_garage_door).position = COVER_OPEN;
          id(ray_s_garage_door).publish_state();
          return COVER_OPEN;
        }
        else //Door not at any endstop
        {
          //No operation in progress, just send state
          id(performing_last_movement) = false;
          if (id(ray_s_garage_door).position != esphome::cover::COVER_OPEN)
          {
            id(ray_s_garage_door).position = COVER_OPEN;
            id(ray_s_garage_door).publish_state();
            return COVER_OPEN;
          }
        }
      }
      else
      {
        //The door is halfway open, so set it to OPEN
        if (id(ray_s_garage_door).position != esphome::cover::COVER_OPEN)
          {
            id(ray_s_garage_door).position = COVER_OPEN;
            id(ray_s_garage_door).publish_state();
            return COVER_OPEN;
          }
      }
      return {};
    open_action: 
      - lambda: |-
          id(ray_s_garage_door).current_operation =  esphome::cover::COVER_OPERATION_OPENING;
          if (!id(open_endstop).state) {
            id(ray_button).turn_on();
            if (id(closed_endstop).state) {
              id(performing_last_movement) = true; //Set flag to indicate we know where the door is
            }
          }
    close_action:
      - lambda: |-
          id(ray_s_garage_door).current_operation =  esphome::cover::COVER_OPERATION_CLOSING;
          if (!id(closed_endstop).state) {
            id(ray_button).turn_on();
            if (id(open_endstop).state) {
              id(performing_last_movement) = true; //Set flag to indicate we know where the door is
            }
          }
    stop_action:
      - lambda: |-
          if (id(ray_s_garage_door).current_operation ==  esphome::cover::COVER_OPERATION_CLOSING || id(ray_s_garage_door).current_operation ==  esphome::cover::COVER_OPERATION_OPENING )
          {
            id(ray_s_garage_door).current_operation =  esphome::cover::COVER_OPERATION_IDLE;
            //Stop the door if it is moving
            id(performing_last_movement) = false;
            id(ray_button).turn_on();
          }

binary_sensor:
  - platform: template
    name: Ray's Car Presence
    lambda: |-
      if (id(ray_s_bay_distance).state < 1.3) {
        return true;
      } else {
        return false;
      }
    # D5 will need an external pull down, and reed switch goes to 3.3V
  - platform: gpio
    pin: D5
    device_class: opening
    id: open_endstop
    internal: True
    filters:
      - delayed_on_off: 500ms
    # D6 will need an external pull down, and reed switch goes to 3.3V
  - platform: gpio
    pin: D6
    device_class: opening
    id: closed_endstop
    internal: True
    filters:
      - delayed_on_off: 500ms

Here are the boilerplate files.

common/device_base_esp8266.yaml:

esphome:
  name: ${node_name}
  platform: ESP8266
  board: ${board}
  esp8266_restore_from_flash: ${restore_from_flash}
  build_path: ./build/${node_name}

captive_portal:

logger:
  level: ${log_level}

api:
  password: ${api_password}

ota:
  password: ${ota_password}
  id: ${node_name}_ota

<<: !include common.button.yaml
<<: !include common.binary_sensor.yaml
<<: !include common.sensor.yaml
<<: !include common.text_sensor.yaml

common/common.button.yaml:

button:
  - platform: restart
    name: ${friendly_name} Restart
  - platform: template
    name: ${friendly_name} Reset WiFi
    on_press:
      then:
        - lambda: |-
            WiFi.disconnect();
            WiFi.begin();

common/common.binary_sensor.yaml:

binary_sensor:
  - platform: status
    name: ${friendly_name} Status

common/common.sensor.yaml:

sensor:
  - platform: uptime
    name: ${friendly_name} Uptime Raw
    id: ${node_name}_uptime_raw
    internal: true
    update_interval: 30s
  - platform: wifi_signal
    name: ${friendly_name} WiFi Signal Strength
    update_interval: 600s

common/common.text_sensor.yaml:

text_sensor:
  - platform: version
    name: ${friendly_name} ESPHome Version
    hide_timestamp: True
  - platform: wifi_info
    ip_address:
      name: ${friendly_name} IP Address
      icon: mdi:ip
    ssid:
      name: ${friendly_name} SSID
      icon: mdi:access-point-network
    mac_address:
      name: ${friendly_name} MAC
      icon: mdi:folder-key-network
  - platform: template
    name: ${friendly_name} Uptime
    lambda: |-
      int seconds = (id(${node_name}_uptime_raw).state);
      int days = seconds / (24 * 3600);
      seconds = seconds % (24 * 3600);
      int hours = seconds / 3600;
      seconds = seconds % 3600;
      int minutes = seconds /  60;
      seconds = seconds % 60;
      if ( days > 3650 ) {
        return { "Starting up" };
      } else if ( days ) {
        return { (String(days) +"d " + String(hours) +"h " + String(minutes) +"m "+ String(seconds) +"s").c_str() };
      } else if ( hours ) {
        return { (String(hours) +"h " + String(minutes) +"m "+ String(seconds) +"s").c_str() };
      } else if ( minutes ) {
        return { (String(minutes) +"m "+ String(seconds) +"s").c_str() };
      } else {
        return { (String(seconds) +"s").c_str() };
      }
    icon: mdi:clock-start
    update_interval: 600s

BTW, my other garage door openers both include PIR motion sensors. That’s why this one doesn’t have them, it was intentionally designed this way because of my garage shape. If you only have one garage door opener DEFINITELY include a PIR sensor so you can see if something moves in the garage. Having that detection and reporting in HA is VERY nice.

Good luck!

2 Likes

Ray - many thanks for this. A huge help for me :grinning: