Garage Presence - Open and close garage doors automatically with ESPHome

I’ve been using the MQTT Garage Presence project with an Arduino to Open/Close my garage door when the car enters/exits my WiFi range. Since installing ESPHome, I decided to try using that instead. It was a lot simpler to implement. I’m not sure if it is as fast as the MQTT version, but I’ll test that later.

Assumptions:

  1. You have a MyQ type garage door, that detects at least the following states {open, closed, opening}.
  2. Your car(s) have a USB port whose power is in sync with the ignition’s power state.
  3. You have a motion sensor in your garage.
  4. You have ESPHome working with Home Assistant
  5. You are using an Arduino type microcontroller (with WiFi) as your car’s ESPHome “presence” device.
  6. You have two cars, for one garage door. This will work for single or multi-car garages, and it should be obvious how to modify the automations accordingly. So this is written for the “more difficult” case.
  7. You have MQTT installed

The objective of this process is for Home Assistant to open/close your garage automatically and work in conjunction with manual control of your garage doors. In other words, you can still operate your garage door with an RF remote, or physically attached button without the HA Automations fighting you. There are two uses cases to consider: leaving and arriving home. When leaving home, we would like for the garage to close as soon as possible. When arriving home, that may not be what you want. For example, you might want to leave the garage open for a few minutes in order to get things out of the car.

The ESPHome sensor works by it sending an incrementing number every second when the sensor connects to your WiFi. When that sensor becomes “unavailable” in Home Assistant, that means either the sensor lost power (the car was turned off) or the car drove away and lost connection to your WiFi.

The motion sensor that is placed inside your garage is used to determine if the ESPHome sensor (and thus your car) is “coming or going”. If the motion sensor is triggered before your garage is opened, then HA knows the car is leaving. If it is triggered after (or not at all) we know the sensor came into range and thus the car is returning. Make sure you place the motion sensor in your garage such that it will capture motion from the driver entering the car, and not from motion outside of the garage. You should also make sure you motion sensor has a timeout of at least a few minutes. Remember, the sensor has to be triggered before the garage is opened in order to set the event as “leaving”

In order to keep track of this dance, you will need to declare the following input_text sensors. You can hard-code the long-delay in your secrets.yaml file if you wish. This delay is how many seconds the garage should stay open after your car “comes home”. When the automations determine whether you are arriving or leaving, esphome_garage_delay is used to store how long HA will wait before shutting a door when it loses connection to the sensor.

long_delay:
  initial: !secret garage_long_delay
esphome_garage_delay:
  initial: '0'

You will also need to declare the following timer. This will use the value stored in esphome_garage_delay.

car_close_garage:
  duration: '00:00:01'

You will also need several input_booleans. Add or remove them for each car needed. esp_car_debug isn’t strictly needed, but it’s useful to switch it to on if you don’t want your garage opening/closing for some reason, such as debugging or updating your ESPhome sensor.

esphome_sensor_detected:
  initial: off
esp_car_debug:
  initial: off
bmw_opened_garage:
  initial: off
hyundai_opened_garage:
  initial: off

Create the following automations:

- alias: Vehicle Sensor in Range - turn on boolean
  trigger:
    platform: numeric_state 
    entity_id: sensor.bmw, sensor.hyundai
    above: 0
  action: 
    - service: input_boolean.turn_on
      data_template:
        entity_id: "input_boolean.{{trigger.to_state.attributes.esp_home_input_boolean}}"
    - service: input_boolean.turn_on
      entity_id: input_boolean.esphome_sensor_detected

- alias: Vehicle Sensor Unavailable - start timer to close garage
  trigger:
    platform: state
    entity_id: sensor.bmw, sensor.hyundai
    to: 'unavailable'
  condition:
    - condition: state
      entity_id: input_boolean.esphome_sensor_detected
      state: 'on'
    - condition: template
      value_template: '{{ states.sensor.hyundai | int == 0 }}'
    - condition: template
      value_template: '{{ states.sensor.bmw | int == 0 }}'
  action:
    - service: input_boolean.turn_off
      entity_id: input_boolean.esphome_sensor_detected
    - service: script.turn_on
      entity_id: script.start_car_close_garage_timer

- alias: Vehicle Sensor Unavailable - cleanup
  trigger:
    platform: state
    entity_id: sensor.bmw, sensor.hyundai
    to: 'unavailable'
  action:
    - service: input_boolean.turn_off
      data_template:
        entity_id: "input_boolean.{{trigger.to_state.attributes.esp_home_input_boolean}}" 

- alias: sensor_boolean_turned_on-open_garage
  trigger:
    platform: state
    entity_id: input_boolean.esphome_sensor_detected
    to: 'on'
  condition:   
    - condition: state
      entity_id: cover.garage_door_opener
      state: 'closed'
  action:
    - service: script.turn_on
      entity_id: script.car_open_garage   
  
- alias: Garage Opened and sensors detected - quick close
  trigger:
    platform: state
    entity_id: cover.garage_door_opener
    to: 'opening'
  action:
    - service: input_text.set_value
      data_template:    
        entity_id: input_text.esphome_garage_delay
        value: >-
            {% set long_delay = states('input_text.long_delay') %}
            {% set motion_detected = states('binary_sensor.1st_garage_motion_sensor') %}
            {% set vehicle_detected = states('input_boolean.esphome_sensor_detected') %}
            {% if (motion_detected == 'on' or vehicle_detected == 'off')  %}
                {{1}}
            {% else %}
                {{long_delay}}
            {% endif %}

- alias: Vehicle Timer Elapsed - Close Garage
  trigger:
    - platform: event
      event_type: timer.finished
      event_data:
        entity_id: timer.car_close_garage
  action:
    - service: script.turn_on
      entity_id: script.car_close_garage

- alias: garage debug
  trigger:
    platform: state
    entity_id: cover.garage_door_opener
  condition:
    - condition: state
      entity_id: input_text.esp_car_debug
      state: 'on'
  action:
    - service: persistent_notification.create
      data_template:
        title: 'garage debug'
        message: '{{trigger.to_state}}'

- alias: Garage_Closed-Cancel_Vehicle_Timer-Set_default_close_time
  trigger:
    platform: state
    entity_id: cover.garage_door_opener
    to: 'closed'
  action:
    - service: timer.cancel
      entity_id: timer.car_close_garage
    - service: input_boolean.turn_off
      entity_id: input_boolean.esphome_sensor_detected

And create the following scripts:

start_car_close_garage_timer:
  sequence:
    - service: timer.cancel
      entity_id: timer.car_close_garage
    - service: timer.start
      data_template:    
        entity_id: timer.car_close_garage
        duration: >
          {{ states.input_text.esphome_garage_delay.state }}
         
car_open_garage:
  sequence:
    - condition: state
      entity_id: input_boolean.esp_car_debug
      state: 'off'
    - service: timer.cancel
      entity_id: timer.car_close_garage
    - service: cover.open_cover
      entity_id: cover.garage_door_opener
      
car_close_garage:
  sequence:
    - condition: state
      entity_id: input_boolean.esp_car_debug
      state: 'off'
    - service: timer.cancel
      entity_id: timer.car_close_garage
    - service: cover.close_cover
      entity_id: cover.garage_door_opener

Here is the YAML for the ESPHome sensor. Obviously you will need to modify this for each car you use. You should also assign a static IP as it is faster. This too needs to be unique.

esphome:
  name: car_bmw
  platform: ESP8266
  board: d1_mini

wifi:
  ssid: "MysSID"
  password: "0123456789"
  fast_connect: true
  use_address: 172.16.68.121
  reboot_timeout: 0s
  
  manual_ip:
    static_ip: 172.16.68.121
    gateway: 172.16.68.1
    subnet: 255.255.255.0

mqtt:
  discovery: true
  discovery_prefix: homeassistant
  client_id: home-assistant-esphome-bmw
  broker: 172.16.68.62
  username: mqttuser
  password: mqttpassword
  reboot_timeout: 0s
  keepalive: 2s
    
logger:

ota:
  password: 'password'

binary_sensor:
  - platform: status
    name: "Garage Opened BMW"
    id: garage_opened_bmw

sensor:
  - platform: template
    name: "BMW"
    lambda: |-
    
      static int num_cycles = 0;
      static int prev_count = 0;
      int retVal;
      num_cycles += 1;
      bool ha_responded = (id(garage_opened_bmw).state); // Home Assistant set binary_sensor ON
      if (ha_responded) {
        retVal = prev_count;
      }
      else
      {
        retVal = num_cycles;
        prev_count = num_cycles;
      }
      return retVal;
    
    update_interval: 1000ms
3 Likes

Friendly advice: use spaces not tabs to indent for YAML.

Interested in doing this as well. Have you had a chance to compare response time between the two?

I am getting a duplicate error two places in the code where you have an and condition. Since Conditions are and by default you just have to drop condition:and and make 3 normal conditions instead.

			- condition: state
				entity_id: cover.garage_door_opener
				state: 'closed'
			- condition:
				condition: and
				conditions:
					- condition: state
						entity_id: input_boolean.bmw_opened_garage
						state: 'off'
					- condition: state
						entity_id: input_boolean.hyundai_opened_garage
						state: 'off'

Also need this:

timer:
  car_close_garage:
    duration: '00:01:00'

I’ve redone it quite a bit. I’ll let you know when I update it here. It’s actually working quite nicely now.

2 Likes

@AZDane, updated.

1 Like

Thanks. I will take a look and try out. Not sure a WiFi solution is good enough for me though. I end up having to wait for the garage door. If I use my garage opener button I can drive right in.

pushing a button is work

1 Like

I agree. But sitting and waiting for the garage to open is even worse. What I need is a D1Mini/GPS/Homelink Solution. It has to be selfcontained since I don’t have cell service where it needs to work. My house is in a deadzone.

I have a Pi Zero sitting around that I was going to try and use BLE to pick up the cars sooner. Also those little “tags”. Anything should plug into these automations however.

FYI, I did some tweaking and rebuilt the sensors using MQTT. The door opens within 6 seconds of being plugged into power and in range. Tonight as I round the corner my door started opening and was fully opened by the time I pulled up. It was like my very own Bat Cave.

3 Likes