Denwood
Well-known member
This is easily one of my favourite garage hacks ever. I have three LED segments around the door, LEFT, REAR (top of door) and RIGHT which turn green, yellow, red, or flashing red to guide (my daughter mostly) to park in the correct spot so car doors don't hit the lift posts, and the car is correctly distanced from the garage door. This YT short (about 2 minutes) pretty much illustrates how the system works. Links for all the bits you'd need to replicate this project are in the YT description.
The project uses Home Assistant and the ESPHome integration to program three ultrasonic (and quite accurate!) distance sensors. ESPHome makes this super easy and there are only three wires to connect up. This control panel in Home Assistant lets you enter the distances you want for the red, yellow, green parking/LED ranges. I will likely use this all the time (for live distances) to get cars correctly positioned for lifting on the hoist as I otherwise usually park, get out, reposition, then repeat one or two times.

These are the bits for each distance sensor:

This is the pinout for the $7 ESP32 board. You're only connecting three wires to it: ESP32 VIN to Sensor RED, ESP 32 GND to sensor BLACK and ESP32 TXD2 (Pin27) to sensor WHITE.

Here's one of three sensors. WIFI connected, powered by USB. Less than a watt each for power use.

View from the car if you get it right:

Code that you can use to program the $7 ESP32 board in ESPHome to work with the $30 ultrasonic distance sensor:
These are the generic entities you would need to replace in the code below:
binary_sensor.garage_door_contact
binary_sensor.garage_motion
sensor.garage_distance_left
sensor.garage_distance_rear
sensor.garage_distance_right
light.garage_wled_master
light.garage_wled_left_segment
light.garage_wled_rear_segment
light.garage_wled_right_segment
switch.garage_side_lights
light.garage_auxiliary_strip
This is the automation code (santized) used in Home Assistant to control the lights for parking:
The project uses Home Assistant and the ESPHome integration to program three ultrasonic (and quite accurate!) distance sensors. ESPHome makes this super easy and there are only three wires to connect up. This control panel in Home Assistant lets you enter the distances you want for the red, yellow, green parking/LED ranges. I will likely use this all the time (for live distances) to get cars correctly positioned for lifting on the hoist as I otherwise usually park, get out, reposition, then repeat one or two times.

These are the bits for each distance sensor:

This is the pinout for the $7 ESP32 board. You're only connecting three wires to it: ESP32 VIN to Sensor RED, ESP 32 GND to sensor BLACK and ESP32 TXD2 (Pin27) to sensor WHITE.

Here's one of three sensors. WIFI connected, powered by USB. Less than a watt each for power use.

View from the car if you get it right:

Code that you can use to program the $7 ESP32 board in ESPHome to work with the $30 ultrasonic distance sensor:
Code:
# Board: DOIT ESP32 DEVKIT V1
# Distance sensor: A02YYUW UART
#
# Wiring:
# Sensor red -> ESP32 VIN / 5V
# Sensor black -> ESP32 GND
# Sensor white -> ESP32 RX2 / GPIO16
# Sensor yellow -> Not connected
esphome:
name: garage-distance-sensor-left
friendly_name: Garage Distance Sensor Left
esp32:
board: esp32doit-devkit-v1
framework:
type: esp-idf
logger:
level: INFO
api:
encryption:
key: "YOUR ESPHOME NATIVE API"
ota:
- platform: esphome
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
power_save_mode: none
ap:
ssid: "Garage Distance Fallback Hotspot"
password: "YOUR SECRET PASSWORD"
captive_portal:
uart:
id: distance_sensor_uart
rx_pin: GPIO16
baud_rate: 9600
data_bits: 8
parity: NONE
stop_bits: 1
sensor:
- platform: a02yyuw
id: garage_left_distance
name: "Left Garage Distance"
uart_id: distance_sensor_uart
unit_of_measurement: "cm"
accuracy_decimals: 1
device_class: distance
state_class: measurement
filters:
# Convert millimetres to centimetres.
- multiply: 0.1
# Reject isolated false readings caused by ultrasonic crosstalk.
- median:
window_size: 5
send_every: 3
send_first_at: 1
- platform: wifi_signal
name: "Wi-Fi Signal"
update_interval: 60s
- platform: uptime
name: "Uptime"
button:
- platform: restart
name: "Restart"
These are the generic entities you would need to replace in the code below:
binary_sensor.garage_door_contact
binary_sensor.garage_motion
sensor.garage_distance_left
sensor.garage_distance_rear
sensor.garage_distance_right
light.garage_wled_master
light.garage_wled_left_segment
light.garage_wled_rear_segment
light.garage_wled_right_segment
switch.garage_side_lights
light.garage_auxiliary_strip
This is the automation code (santized) used in Home Assistant to control the lights for parking:
Code:
# Sanitized Home Assistant package: Garage LED Parking Guidance
#
# Place this file under /config/packages/ and enable packages in configuration.yaml:
#
# homeassistant:
# packages: !include_dir_named packages
#
# Replace the generic entity IDs below with your own entities.
# No IP addresses, encryption keys, Wi-Fi credentials, device IDs, or personal names
# are included in this version.
input_number:
garage_side_off_distance:
name: Garage Side - Off Distance
min: 0
max: 400
step: 1
unit_of_measurement: cm
mode: box
initial: 60
garage_side_green_distance:
name: Garage Side - Green Distance
min: 0
max: 400
step: 1
unit_of_measurement: cm
mode: box
initial: 50
garage_side_yellow_distance:
name: Garage Side - Yellow Distance
min: 0
max: 400
step: 1
unit_of_measurement: cm
mode: box
initial: 45
garage_side_red_distance:
name: Garage Side - Red Distance
min: 0
max: 400
step: 1
unit_of_measurement: cm
mode: box
initial: 40
garage_rear_off_distance:
name: Garage Rear - Off Distance
min: 0
max: 400
step: 1
unit_of_measurement: cm
mode: box
initial: 205
garage_rear_red_distance:
name: Garage Rear - Red Distance
min: 0
max: 400
step: 1
unit_of_measurement: cm
mode: box
initial: 195
garage_rear_yellow_distance:
name: Garage Rear - Yellow Distance
min: 0
max: 400
step: 1
unit_of_measurement: cm
mode: box
initial: 181
garage_rear_green_distance:
name: Garage Rear - Green Distance
min: 0
max: 400
step: 1
unit_of_measurement: cm
mode: box
initial: 170
automation:
- id: sanitized_garage_parking_guidance
alias: Garage Parking Guidance
description: >-
Uses left, rear, and right distance sensors to control three WLED
segments while a vehicle backs into the garage.
mode: restart
triggers:
# This example assumes the door contact is ON when the door is open.
# Reverse the states if your contact reports the opposite way.
- trigger: state
entity_id: binary_sensor.garage_door_contact
from: "off"
to: "on"
id: door_open
- trigger: state
entity_id: binary_sensor.garage_door_contact
from: "on"
to: "off"
id: door_closed
# Allows a manual WLED master-off command to stop the session.
- trigger: state
entity_id: light.garage_wled_master
from: "on"
to: "off"
id: master_off
conditions: []
actions:
- choose:
- conditions:
- condition: trigger
id: door_open
sequence:
- action: light.turn_on
target:
entity_id: light.garage_wled_master
data:
brightness_pct: 100
- delay:
milliseconds: 750
- variables:
session_started: "{{ as_timestamp(now()) }}"
last_movement: "{{ as_timestamp(now()) }}"
movement_seen: false
left_reference: >-
{{ states('sensor.garage_distance_left') | float(9999) }}
rear_reference: >-
{{ states('sensor.garage_distance_rear') | float(9999) }}
right_reference: >-
{{ states('sensor.garage_distance_right') | float(9999) }}
- repeat:
while:
- condition: template
value_template: >-
{% set now_ts = as_timestamp(now()) %}
{% set elapsed = now_ts - (session_started | float) %}
{% set inactive = now_ts - (last_movement | float) %}
{{
is_state('light.garage_wled_master', 'on')
and elapsed < 180
and (
((not (movement_seen | bool)) and elapsed < 120)
or
((movement_seen | bool) and inactive < 20)
)
}}
sequence:
- variables:
current_left: >-
{{ states('sensor.garage_distance_left') | float(9999) }}
current_rear: >-
{{ states('sensor.garage_distance_rear') | float(9999) }}
current_right: >-
{{ states('sensor.garage_distance_right') | float(9999) }}
# Meaningful movement: 2 cm on either side or 3 cm at rear.
- if:
- condition: template
value_template: >-
{{
((current_left | float) - (left_reference | float)) | abs >= 2
or
((current_rear | float) - (rear_reference | float)) | abs >= 3
or
((current_right | float) - (right_reference | float)) | abs >= 2
}}
then:
- variables:
movement_seen: true
last_movement: "{{ as_timestamp(now()) }}"
left_reference: "{{ current_left }}"
rear_reference: "{{ current_rear }}"
right_reference: "{{ current_right }}"
# LEFT SEGMENT
# >= off: off
# >= green: green
# >= yellow: yellow
# >= red: solid red
# below red: flashing red
- choose:
- conditions:
- condition: template
value_template: >-
{{
(current_left | float) >=
states('input_number.garage_side_off_distance') | float
}}
sequence:
- action: light.turn_off
target:
entity_id: light.garage_wled_left_segment
- conditions:
- condition: template
value_template: >-
{{
(current_left | float) >=
states('input_number.garage_side_green_distance') | float
}}
sequence:
- action: light.turn_on
target:
entity_id: light.garage_wled_left_segment
data:
brightness_pct: 100
effect: Solid
rgb_color: [0, 255, 0]
- conditions:
- condition: template
value_template: >-
{{
(current_left | float) >=
states('input_number.garage_side_yellow_distance') | float
}}
sequence:
- action: light.turn_on
target:
entity_id: light.garage_wled_left_segment
data:
brightness_pct: 100
effect: Solid
rgb_color: [255, 180, 0]
- conditions:
- condition: template
value_template: >-
{{
(current_left | float) >=
states('input_number.garage_side_red_distance') | float
}}
sequence:
- action: light.turn_on
target:
entity_id: light.garage_wled_left_segment
data:
brightness_pct: 100
effect: Solid
rgb_color: [255, 0, 0]
default:
- action: light.turn_on
target:
entity_id: light.garage_wled_left_segment
data:
brightness_pct: 100
effect: Blink
rgb_color: [255, 0, 0]
# REAR SEGMENT
# Too far: off, then red/yellow/green as the target is approached.
# Closer than the green zone: flashing red.
- choose:
- conditions:
- condition: template
value_template: >-
{{
(current_rear | float) >
states('input_number.garage_rear_off_distance') | float
}}
sequence:
- action: light.turn_off
target:
entity_id: light.garage_wled_rear_segment
- conditions:
- condition: template
value_template: >-
{{
(current_rear | float) >=
states('input_number.garage_rear_red_distance') | float
}}
sequence:
- action: light.turn_on
target:
entity_id: light.garage_wled_rear_segment
data:
brightness_pct: 100
effect: Solid
rgb_color: [255, 0, 0]
- conditions:
- condition: template
value_template: >-
{{
(current_rear | float) >=
states('input_number.garage_rear_yellow_distance') | float
}}
sequence:
- action: light.turn_on
target:
entity_id: light.garage_wled_rear_segment
data:
brightness_pct: 100
effect: Solid
rgb_color: [255, 180, 0]
- conditions:
- condition: template
value_template: >-
{{
(current_rear | float) >=
states('input_number.garage_rear_green_distance') | float
}}
sequence:
- action: light.turn_on
target:
entity_id: light.garage_wled_rear_segment
data:
brightness_pct: 100
effect: Solid
rgb_color: [0, 255, 0]
default:
- action: light.turn_on
target:
entity_id: light.garage_wled_rear_segment
data:
brightness_pct: 100
effect: Blink
rgb_color: [255, 0, 0]
# RIGHT SEGMENT: same thresholds as the left segment.
- choose:
- conditions:
- condition: template
value_template: >-
{{
(current_right | float) >=
states('input_number.garage_side_off_distance') | float
}}
sequence:
- action: light.turn_off
target:
entity_id: light.garage_wled_right_segment
- conditions:
- condition: template
value_template: >-
{{
(current_right | float) >=
states('input_number.garage_side_green_distance') | float
}}
sequence:
- action: light.turn_on
target:
entity_id: light.garage_wled_right_segment
data:
brightness_pct: 100
effect: Solid
rgb_color: [0, 255, 0]
- conditions:
- condition: template
value_template: >-
{{
(current_right | float) >=
states('input_number.garage_side_yellow_distance') | float
}}
sequence:
- action: light.turn_on
target:
entity_id: light.garage_wled_right_segment
data:
brightness_pct: 100
effect: Solid
rgb_color: [255, 180, 0]
- conditions:
- condition: template
value_template: >-
{{
(current_right | float) >=
states('input_number.garage_side_red_distance') | float
}}
sequence:
- action: light.turn_on
target:
entity_id: light.garage_wled_right_segment
data:
brightness_pct: 100
effect: Solid
rgb_color: [255, 0, 0]
default:
- action: light.turn_on
target:
entity_id: light.garage_wled_right_segment
data:
brightness_pct: 100
effect: Blink
rgb_color: [255, 0, 0]
- delay:
milliseconds: 250
- action: light.turn_off
target:
entity_id:
- light.garage_wled_left_segment
- light.garage_wled_rear_segment
- light.garage_wled_right_segment
- light.garage_wled_master
- conditions:
- condition: trigger
id:
- door_closed
- master_off
sequence:
- action: light.turn_off
target:
entity_id:
- light.garage_wled_left_segment
- light.garage_wled_rear_segment
- light.garage_wled_right_segment
- light.garage_wled_master
- id: sanitized_garage_motion_white_lighting
alias: Garage Motion White Lighting
description: >-
Turns the full WLED strip on in neutral white when garage motion is
detected, except while parking guidance is active.
mode: restart
triggers:
- trigger: state
entity_id: binary_sensor.garage_motion
from: "off"
to: "on"
id: motion_detected
- trigger: state
entity_id: binary_sensor.garage_motion
from: "on"
to: "off"
for:
minutes: 5
id: motion_cleared
- trigger: state
entity_id: automation.garage_parking_guidance
attribute: current
from: 1
to: 0
id: parking_finished
conditions: []
actions:
- choose:
- conditions:
- condition: trigger
id:
- motion_detected
- parking_finished
- condition: state
entity_id: binary_sensor.garage_motion
state: "on"
- condition: template
value_template: >-
{{
state_attr('automation.garage_parking_guidance', 'current')
| int(0) == 0
}}
sequence:
- action: light.turn_on
target:
entity_id: light.garage_wled_master
data:
brightness_pct: 100
- delay:
milliseconds: 250
# RGB approximation of roughly 5000 K. Adjust for your strip.
- action: light.turn_on
target:
entity_id:
- light.garage_wled_left_segment
- light.garage_wled_rear_segment
- light.garage_wled_right_segment
data:
brightness_pct: 100
effect: Solid
rgb_color: [255, 228, 206]
- conditions:
- condition: trigger
id: motion_cleared
- condition: template
value_template: >-
{{
state_attr('automation.garage_parking_guidance', 'current')
| int(0) == 0
}}
sequence:
- action: light.turn_off
target:
entity_id:
- light.garage_wled_left_segment
- light.garage_wled_rear_segment
- light.garage_wled_right_segment
- light.garage_wled_master
- id: sanitized_garage_auxiliary_lights_motion
alias: Garage Auxiliary Lights - Motion
description: >-
Controls the non-WLED garage lights from motion. Parking guidance is
allowed to keep them on after motion clears.
mode: restart
triggers:
- trigger: state
entity_id: binary_sensor.garage_motion
from: "off"
to: "on"
id: motion_on
- trigger: state
entity_id: binary_sensor.garage_motion
from: "on"
to: "off"
for:
minutes: 5
id: motion_off
conditions: []
actions:
- choose:
- conditions:
- condition: trigger
id: motion_on
sequence:
- action: homeassistant.turn_on
target:
entity_id:
- switch.garage_side_lights
- light.garage_auxiliary_strip
- conditions:
- condition: trigger
id: motion_off
- condition: template
value_template: >-
{{
state_attr('automation.garage_parking_guidance', 'current')
| int(0) == 0
}}
sequence:
- action: homeassistant.turn_off
target:
entity_id:
- switch.garage_side_lights
- light.garage_auxiliary_strip
- id: sanitized_garage_auxiliary_lights_parking
alias: Garage Auxiliary Lights - Parking Guidance
description: >-
Turns auxiliary garage lights on during parking guidance, then turns
them off afterward only when no motion remains.
mode: restart
triggers:
- trigger: state
entity_id: automation.garage_parking_guidance
attribute: current
from: 0
to: 1
id: parking_started
- trigger: state
entity_id: automation.garage_parking_guidance
attribute: current
from: 1
to: 0
id: parking_finished
conditions: []
actions:
- choose:
- conditions:
- condition: trigger
id: parking_started
sequence:
- action: homeassistant.turn_on
target:
entity_id:
- switch.garage_side_lights
- light.garage_auxiliary_strip
- conditions:
- condition: trigger
id: parking_finished
- condition: state
entity_id: binary_sensor.garage_motion
state: "off"
sequence:
- action: homeassistant.turn_off
target:
entity_id:
- switch.garage_side_lights
- light.garage_auxiliary_strip
Last edited:
