Garage door operation and position (open, close, etc) based on timer, without extra sensors

Hey guys,

I have a simple garage operating with a hacked sonoff (on both sw and hw) inside the machine.
The operation is pretty straightforward, just one click to alternate between moves.
I tried to find a solution to calculate my garage door’s approximate position based on time.
I know, I know, I should have hooked up a magnetic sensor but I was too lazy so I came up with a code that works for me and here it is:

(original community post here)

Some info:

  • The sonoff is located within the garage machine box.
  • It is running Tasmota firmware.
  • It has been (hw) modified in this way in order to be able to close/open any circuit and not just provide 230ac to its output end.
  • So it is powered by the garage doors main power and on the other end I hooked up the signal pins of the garage control board which will control the door whenever the two pins are shorted.
  • The door is operated by some 25y old Faac model which has a 3rd party control board on it as I was asked €600 to replace the original board a couple of years ago. It does work on the original board too though.
  • Usual high voltage precautions, warnings, and waivers of liability apply here.

So, despite everyone’s understandable disagreement, I’ve done it with code.
It’s not perfect but it works.
The main advantage is that I have an (unsafe) indication of where my garage doors are, in case I forget to close them when leaving the house.

Going to share for comments and to, perhaps, help someone out.

notes:

  • the up part in naming is due to me having two doors, one is named up, the other dn, so you can ignore it
  • The only two things you need to change for this to work are the variable for total time the door takes to move from end to start (garage_up_total_time) and the name of the switch that toggles your garage, in my case switch.sonoff_garage_2.
  • The switch could also be an RF transmitter

So here it is:

Please keep in mind that my door works with the simple way possible with only one signal input:

  • Signal while closed -> start opening.
  • Signal while opening -> stop (will start closing on next signal)
  • Signal while stopped in the middle -> start closing (depending on the previous state)
  • Signal while closing -> stop (will start opening on next signal)
  • Signal while stopped in the middle -> start opening (depending on the previous state)
  • Signal while open -> start closing.

Code:

  1. An input_select to state the 6 different states of the door:
    This should go in your configuration.yaml under input_select: or in your input_select.yaml if you have input_select: !include input_select.yaml already in your conf.
  garage_up_state:
    name: Garage UP State
    initial: Closed
    options:
      - Closed
      - Opening
      - Stopped Next Closing
      - Stopped Next Opening
      - Closing
      - Open
  1. I used this component to be able to store some variables (instead of input numbers and stuff)
    hass-variables
    With hass-variables, I created several variables to be able to track down times, the position of the door and the total time the door takes to open/close (starting from closed/open, respectively)
    This goes in configuration.yaml under variable: or in variables.yaml if you have variable: !include variables.yaml already in your conf.
  garage_up_total_time:
    value: 16 #change this value accordingly
  garage_up_position:
    value: 0
  garage_up_last_use:
    value: 0
  garage_up_prev_use:
    value: 0
  1. A script to run different scripts depending on the state of the input_select above
garage_up_operation:
  alias: "Garage Up Toggle"
  sequence:
    - service: variable.set_variable
      data:
        variable: "garage_up_prev_use"
        value_template: "{{(states.variable.garage_up_last_use.state)| int}}"
    - service: variable.set_variable
      data:
        variable: "garage_up_last_use"
        value_template: "{{(as_timestamp(now())| round(0))+(2*60*60)| int}}"
    - service: script.turn_on
      data_template:
        entity_id: >
          {% if is_state("input_select.garage_up_state", "Closed") %}
            script.garage_up_closed
          {% elif is_state("input_select.garage_up_state", "Opening") %}
            script.garage_up_opening
          {% elif is_state("input_select.garage_up_state", "Stopped Next Closing") %}
            script.garage_up_stopped_next_closing
          {% elif is_state("input_select.garage_up_state", "Stopped Next Opening") %}
            script.garage_up_stopped_next_opening
          {% elif is_state("input_select.garage_up_state", "Closing") %}
            script.garage_up_closing
          {% elif is_state("input_select.garage_up_state", "Open") %}
            script.garage_up_open
          {% endif %}

  1. And then some more scripts that would handle the operation switch and change the input_select according to where the door is.
garage_up_closed:
  alias: "When Garage Up Closed"
  sequence:
    - service: switch.turn_on
      entity_id: switch.sonoff_garage_2
    - service: input_select.select_option
      data:
        entity_id: input_select.garage_up_state
        option: "Opening"
        
    - delay:
        seconds: "{{(states.variable.garage_up_total_time.state)| int}}"
        
    - service: variable.set_variable
      data:
        variable: "garage_up_position"
        value_template: "{{(states.variable.garage_up_total_time.state)| int}}"
    - service: input_select.select_option
      data:
        entity_id: input_select.garage_up_state
        option: "Open"
        
        
        
garage_up_opening:
  alias: "When Garage Up Opening"
  sequence:
    - service: switch.turn_on
      entity_id: switch.sonoff_garage_2
    - service: script.turn_off
      entity_id: script.garage_up_closed, script.garage_up_stopped_next_opening 
    - service: variable.set_variable
      data:
        variable: "garage_up_position"
        value_template: "{{(states.variable.garage_up_position.state)| int +(states.variable.garage_up_last_use.state)| int-(states.variable.garage_up_prev_use.state)| int}}"
    - service: input_select.select_option
      data:
        entity_id: input_select.garage_up_state
        option: "Stopped Next Closing"



garage_up_stopped_next_closing: #was Opening
  alias: "When Garage Up Stopped, next: Closing"
  sequence:
    - service: switch.turn_on
      entity_id: switch.sonoff_garage_2
    - service: input_select.select_option
      data:
        entity_id: input_select.garage_up_state
        option: "Closing"
        
    - delay:
        seconds: "{{(states.variable.garage_up_position.state)| int}}" 

    - service: variable.set_variable
      data:
        variable: "garage_up_position" 
        value: "0" # set position 0
    - service: input_select.select_option
      data:
        entity_id: input_select.garage_up_state
        option: "Closed"
        
        
garage_up_stopped_next_opening: #was Closing
  alias: "When Garage Up Stopped, next: Opening"
  sequence:
    - service: switch.turn_on
      entity_id: switch.sonoff_garage_2
    - service: input_select.select_option
      data:
        entity_id: input_select.garage_up_state
        option: "Opening"

    - delay:
        seconds: "{{(states.variable.garage_up_total_time.state)| int - (states.variable.garage_up_position.state)| int}}"

    - service: variable.set_variable
      data:
        variable: "garage_up_position"
        value_template: "{{(states.variable.garage_up_total_time.state)| int}}"  
    - service: input_select.select_option
      data:
        entity_id: input_select.garage_up_state
        option: "Open"
        
        
garage_up_closing:
  alias: "When Garage Up Closing"
  sequence:
    - service: switch.turn_on
      entity_id: switch.sonoff_garage_2
    - service: script.turn_off
      entity_id: script.garage_up_open, script.garage_up_stopped_next_closing
    - service: variable.set_variable
      data:
        variable: "garage_up_position"
        value_template: "{{(states.variable.garage_up_position.state)| int -((states.variable.garage_up_last_use.state)| int - (states.variable.garage_up_prev_use.state)| int)}}" 
    - service: input_select.select_option
      data:
        entity_id: input_select.garage_up_state
        option: "Stopped Next Opening"
        
        

garage_up_open:
  alias: "When Garage Up Open"
  sequence:
    - service: switch.turn_on
      entity_id: switch.sonoff_garage_2
    - service: input_select.select_option
      data:
        entity_id: input_select.garage_up_state
        option: "Closing"
        
    - delay:
        seconds: "{{(states.variable.garage_up_total_time.state)| int}}"
        
    - service: variable.set_variable
      data:
        variable: "garage_up_position"
        value: "0"
    - service: input_select.select_option
      data:
        entity_id: input_select.garage_up_state
        option: "Closed"

The code needs some refactoring and could probably be re-written in a much more smart way but, that’s what I came up with :slight_smile: - open to suggestions

  1. Lastly, for aesthetic reasons, i created a template_sensor to see the door state without being able to alter it. (I still keep the input_select editable in some other page of my frontend in case i need to manually reset the whole thing.)
    Inside sensors.yaml or under sensor: in configuration.yaml
      garage_up_state:
        friendly_name: "Γκαράζ Πάνω" #ignore my greek :)
        value_template: "{{ states.input_select.garage_up_state.state }}"
  1. And lastly v.2, a group to show them in my front-end:
  door_switches:
    control: hidden
    entities:
      - script.garage_up_operation
      - sensor.garage_up_state

I just wrote this up but it seems to be working. Will keep testing and come back with comments.

The main flaw is that I’m using UNIX timestamp to do my calculations which means I’m working with seconds, not fragments of seconds. It does not pose a great threat but if say you open and close the door several times without reaching the endpoint (using the switch while the door is moving) you get an inaccurate reading of the precise position of the door between the two ends (open/closed). Other than that, you still get an accurate reading of the general state of the door.

Another flaw is that one of my doors is using a sensor when closing that will stop it if it finds an obstacle. This means it will change state (from closing to stopped) without HA knowing that. If this happens you need to manually reset the input_select to the correct state afterward.

Next step would be an automation to send me a notification if the door is not closed for xx time, using the states of my new template sensor.

edit: and here it is.

- id: '1542799053404'
  alias: Notify - Garage Up Open
  trigger:
  - entity_id: sensor.garage_up_state
    for: 00:01:00
    platform: state
    to: Stopped Next Closing
  - entity_id: sensor.garage_up_state
    for: 00:01:00
    platform: state
    to: Stopped Next Opening
  - entity_id: sensor.garage_up_state
    for: 00:01:00
    platform: state
    to: Open
  - entity_id: sensor.garage_dn_state
    for: 00:01:00
    platform: state
    to: Opening #this is probably unnecessary but i just put it there to be sure
  condition: []
  action:
  - data:
      message: Garage UP left Open
      title: Home Security
    service: notify.notify_html5

And of course this, for extra bragging rights on my friends:
could.yaml:

  google_actions:
    filter:
      include_entities:
        - script.garage_up_operation

    entity_config:
      script.garage_up_operation:
        name: "Garage Up"

  alexa:
    filter:
      include_entities:
        - script.garage_up_operation

    entity_config:
      script.garage_up_operation:
        name: "Garage Up"
        display_categories: SWITCH

Hope this helps someone out who’s in the same need as me and doesn’t want or cannot install additional sensors.
Now ill go grease the doors cause they screech like harpies :persevere:

k.

That’s a great idea.

Maybe Shelly have been reading this. Shelly have just released a beta firmware update for the Shelly 2 that does what you have here.

That’s cool, nice to see some competition on the sonoff/shelly/tasmota arena. This means we might see more wonderful stuff soon.
On another subject tho, i dont think this could be commerciallised without some magnetic (or other kind of sensors). I mean it’s cool to have it if you acknowledge the limitations but, as a commercial solution i think it would be safe enough :slight_smile:

I uploaded the code on github, as a package, If anyone would like to use it :slight_smile:

So what happens if the door stops prematurely due to an obstruction or mechanical failure, etc.

Without a simple sensor to confirm the door is truly closed, how does your solution handle this failure mode?

@123 we ve had this discussion before :slight_smile:
It doesn’t but it worked well for me over the past 4 months

Lots of things work well under ideal conditions. You don’t need circuit-breakers in an electrical panel … as long as you never short or overload any circuit. Basically, most anything works when conditions are in its favor. What distinguishes a robust solution is how well it works when conditions are not in its favor.

Garage doors are designed to stop if they encounter excessive mechanical resistance or if their obstruction sensor detects an object. Your timer-based solution is based on the optimistic assumption that the door will always open/close. It has no feedback from the door (a simple contact sensor) to detect the door’s actual state. It’ll incorrectly report the door is closed when in fact the door might have stopped prematurely and is either partially-closed or has returned to its full-open position.

The cost of adding a contact (or tilt) sensor is negligible. It also obviates the need for additional automations and lets one use the existing MQTT or Template Cover component.

For garage door automation, there is definitely a place for timers. Combined with a contact sensor, a timer can be used to check if a door closed within a nominal time period. If the timer expires and the door is still not closed, that means something has prevented the door from closing and can be reported as a ‘door failed to close’ message.