mirror of
https://github.com/jdillenburg/esphome.git
synced 2026-01-07 22:20:38 -07:00
370 lines
10 KiB
YAML
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
|