Files
jdillenburg-esphome/garage-door-controller.yaml
2025-09-15 08:48:37 -05:00

370 lines
10 KiB
YAML

esphome:
name: garage-door-controller
friendly_name: Garage door controller
packages:
beacon: !include { file: packages/beacon.yaml }
wifi: !include { file: packages/wifi.yaml, vars: { ssid: "garage-door-controller" }}
external_components:
- source: github://jdillenburg/esphome@main
components: [ tfmini ]
substitutions:
led_count: "60"
esp32:
board: esp32dev
framework:
type: esp-idf
# Enable logging
logger:
level: INFO
# Enable Home Assistant API
api:
encryption:
key: !secret garage_door_api_key
ota:
- platform: esphome
password: !secret garage_door_ota_password
globals:
- id: sensor_counter
type: int
initial_value: '0'
- id: effect_counter
type: int
initial_value: '0'
- id: safe_zone
type: bool
initial_value: 'false'
# Garage door button on GPIO16
switch:
- platform: gpio
device_class: switch
pin: GPIO17
id: relay
name: "Garage Door Relay"
restore_mode: ALWAYS_OFF
on_turn_on:
then:
- delay: 500ms
- switch.turn_off: relay
# configurable distance thresholds
number:
- platform: template
name: Minimum distance
id: min_distance
icon: "mdi:cogs"
optimistic: true
restore_value: true
initial_value: "0.0"
min_value: 0.0
max_value: 20000.0
step: 1.0
unit_of_measurement: cm
- platform: template
name: Maximum distance
id: max_distance
icon: "mdi:cogs"
optimistic: true
restore_value: true
initial_value: "10000.0"
min_value: 0.0
max_value: 20000.0
step: 1.0
unit_of_measurement: cm
- platform: template
name: Buffer distance
id: buffer_distance
icon: "mdi:cogs"
optimistic: true
restore_value: true
initial_value: "10.0"
min_value: 0.0
max_value: 20000.0
step: 1.0
unit_of_measurement: cm
#**************** UART ********************
uart:
tx_pin: GPIO21 #TXD
rx_pin: GPIO22 #RXD
baud_rate: 115200
id: uart_bus
debug:
direction: BOTH
dummy_receiver: false
after:
delimiter: "\n"
sequence:
- lambda: UARTDebug::log_string(direction, bytes);
#******************** Binary Sensor *********************
binary_sensor:
- platform: gpio
pin: GPIO23
id: closed_sensor
internal: True
- platform: template
id: parked_sensor
name: "Car parked"
- platform: template
id: car_exiting
name: "Car exiting"
condition:
sensor.in_range:
id: vehicle_average_speed
above: 1.0
- platform: template
id: car_entering
name: "Car entering"
condition:
sensor.in_range:
id: vehicle_average_speed
below: -1.0
#******************** Sensor *********************
sensor:
- platform: tfmini
id: distance_sensor_raw
internal: True
distance_unit: cm
name: "Distance sensor raw"
unit_of_measurement: "cm"
on_value:
then:
- sensor.template.publish:
id: distance_sensor
state: !lambda 'return x;'
- sensor.template.publish:
id: distance_sensor_leds
state: !lambda 'return x;'
- lambda: |-
id(sensor_counter) = id(sensor_counter) + 1;
# - sensor.template.publish:
# id: reading_sensor_counter
# state: !lambda 'return id(reading_counter).state + 1;'
- platform: template
name: "Distance sensor"
id: distance_sensor
internal: False
unit_of_measurement: "cm"
filters:
- clamp:
min_value: 0.0
max_value: 800.0
ignore_out_of_range: True
- throttle_average: 1000ms
- platform: template
name: "Distance sensor for LEDs"
id: distance_sensor_leds
internal: True
unit_of_measurement: "cm"
filters:
- clamp:
min_value: 0.0
max_value: 800.0
ignore_out_of_range: True
- platform: template
name: "Sensor readings per second"
id: reading_counter
unit_of_measurement: "readings/s"
icon: "mdi:speedometer"
update_interval: 10s
lambda: |-
float current_count = id(sensor_counter);
id(sensor_counter) = 0;
return current_count/10.0;
- platform: template
id: light_speed
name: "Light effect changes per second"
unit_of_measurement: "Hz"
icon: "mdi:speedometer"
update_interval: 10s
lambda: |-
float current_count = id(effect_counter);
id(effect_counter) = 0;
return current_count/10.0;
#**************** TIMEOUT OF 30 SECONDS ************************
# See lights_off_when_still script for timeout value
- platform: template
name: "Vehicle average speed"
id: vehicle_average_speed
unit_of_measurement: "cm/s"
accuracy_decimals: 3
update_interval: 500ms
lambda: |-
static float previous_distance = 0;
static uint32_t last_update = 0;
float rate = 0;
if (last_update != 0) {
float time_delta = (millis() - last_update) / 1000.0;
float value_delta = id(distance_sensor_raw).state - previous_distance;
rate = value_delta / time_delta;
}
previous_distance = id(distance_sensor_raw).state;
last_update = millis();
return rate;
filters:
- sliding_window_moving_average:
window_size: 5 # average 5 x 0.5 seconds = 2.5 seconds worth of speed
send_every: 1 # send average speed every 1/2 second
on_value:
then:
- if:
condition:
- lambda: 'return abs(id(vehicle_average_speed).state) < 1.0;' # 0.02 mph really slow!
then:
- if:
condition:
- and:
- light.is_on: led_strip
- not:
script.is_running: lights_off_when_still
then:
script.execute: lights_off_when_still
else:
- if:
condition:
- light.is_off: led_strip
then:
- script.stop: lights_off_when_still
- light.turn_on:
id: led_strip
effect: "Distance Effect"
- binary_sensor.template.publish:
id: parked_sensor
state: false
#******************** Cover *********************
cover:
- platform: template
name: "Garage Door"
id: garage_door
device_class: garage
open_action:
then:
- switch.toggle: relay
close_action:
then:
- switch.toggle: relay
lambda: |-
return id(closed_sensor).state ? COVER_OPEN : COVER_CLOSED;
#********** LED Strip configuration **********
light:
- platform: esp32_rmt_led_strip
id: led_strip
chipset: WS2812
pin: GPIO16
num_leds: ${led_count}
rgb_order: GRB
name: "Parking Assistant LEDs"
restore_mode: RESTORE_DEFAULT_ON
effects:
- addressable_lambda:
name: "Distance Effect"
update_interval: 250ms
lambda: |-
float distance = id(distance_sensor_leds).state;
if (distance <= 0 || std::isnan(distance)) return; // Invalid reading
// Get the total number of LEDs
const int total_leds = it.size();
// Calculate safe zone boundaries
float min_safe = id(min_distance).state - id(buffer_distance).state;
float max_safe = id(min_distance).state + id(buffer_distance).state;
// Measure update rate
id(effect_counter) = id(effect_counter) + 1;
// Set safe_zone flag based on distance
if (distance >= min_safe && distance <= max_safe) {
id(safe_zone) = true;
} else {
id(safe_zone) = false;
}
// blink if less than min_safe
if (distance < min_safe) {
if ((millis() % 1000) < 500) {
it.all() = Color(255,0,0); // RED
}
else {
it.all() = Color::BLACK;
}
return;
}
// solid red in safe zone
if (distance < max_safe) {
it.all() = Color(255,0,0);
return;
}
// Above max_safe distance, calculate color and LED count
float ratio;
float denominator = id(max_distance).state - max_safe;
if (denominator <= 0.0) {
ratio = 1.0;
} else {
ratio = (distance - max_safe) / denominator;
ratio = std::max(0.0f, std::min(1.0f, ratio));
}
// Calculate colors - linear interpolation from red to yellow to green
uint8_t red, green;
if (ratio <= 0.5) { // Red to Yellow
red = 255;
green = 255 * ratio * 2; // Green goes from 0 to 1
} else { // Yellow to Green
red = 255 * 2 * (1.0 - ratio); // Red goes from 1 to 0
green = 255;
}
// Create the color object
auto color = Color(red, green, 0);
// Calculate the number of LEDs to light
int leds_to_light = static_cast<int>(ratio * total_leds + 0.5);
leds_to_light = std::max(0, std::min(total_leds, leds_to_light));
// Center the lit LEDs
int start_index = (total_leds - leds_to_light) / 2;
int end_index = start_index + leds_to_light - 1;
// Turn off all LEDs
it.range(0, start_index) = Color::BLACK;
it.range(start_index, end_index) = color;
it.range(end_index, total_leds) = Color::BLACK;
return;
script:
- id: lights_off_when_still
mode: restart
then:
- delay: 30s
- light.turn_off:
id: led_strip
transition_length:
seconds: 5
- if:
condition:
- lambda: 'return id(safe_zone);'
then:
- binary_sensor.template.publish:
id: parked_sensor
state: true