mirror of
https://github.com/brianhanifin/esphome-config.git
synced 2026-07-28 04:05:55 +00:00
382 lines
11 KiB
YAML
382 lines
11 KiB
YAML
---
|
|
# Device: Shelly 1 PM
|
|
# Location: Upstairs bathroom in wall
|
|
#
|
|
substitutions:
|
|
project: Upstairs bathroom exhaust
|
|
id: switch_exhaust
|
|
hostname: switch-exhaust
|
|
ip: 10.0.20.50
|
|
|
|
#humidity_reference_sensor: "sensor.upstairs_bathroom_humidity_average_last_24_hours"
|
|
humidity_reference_sensor: "sensor.upstairs_humidity_average"
|
|
remaining_time_duration: "600" # 600 seconds = 10 minutes
|
|
max_temp: "70.0" # Max power measurement board temperature in °C.
|
|
|
|
<<: !include common/substitutions/gpio/shelly1pm.yaml
|
|
|
|
esphome:
|
|
name: $hostname
|
|
on_boot:
|
|
- sensor.template.publish:
|
|
id: remaining_time
|
|
state: 0
|
|
|
|
esp8266:
|
|
board: esp01_1m
|
|
framework:
|
|
version: recommended
|
|
restore_from_flash: true
|
|
|
|
<<: !include common/common.yaml
|
|
|
|
interval:
|
|
- interval: 5s
|
|
then:
|
|
# Check the humidity every 5 seconds.
|
|
- script.execute: humidity_delta_calculate
|
|
- script.execute: humidity_actions
|
|
|
|
script:
|
|
- id: humidity_actions
|
|
then:
|
|
- if:
|
|
condition:
|
|
- switch.is_on: relay
|
|
then:
|
|
# When the humidity is not the trigger, and the humidity is between the lower and upper trigger values.
|
|
- if:
|
|
condition:
|
|
and:
|
|
- lambda: return id(diagnostic_last_trigger).state != "HUMIDITY";
|
|
- lambda: return id(humidity_delta).state >= id(delta_lower_trigger).state;
|
|
- lambda: return id(humidity_delta).state < id(delta_upper_trigger).state;
|
|
then:
|
|
# Allow humidity to warm up the room.
|
|
- switch.turn_off: relay
|
|
|
|
# When the humidity is less than or equal to the lower trigger.
|
|
- if:
|
|
condition:
|
|
- lambda: return id(humidity_delta).state < id(delta_lower_trigger).state;
|
|
then:
|
|
# When the countdown has not yet started.
|
|
- if:
|
|
condition:
|
|
- lambda: return id(remaining_time).state == 0;
|
|
then:
|
|
# Begin the countdown.
|
|
- script.execute: remaining_time_start
|
|
else:
|
|
# Continue the remaining time countdown.
|
|
- script.execute: remaining_time_decrement
|
|
|
|
- id: humidity_delta_calculate
|
|
then:
|
|
# Recalculate the difference between the bathroom humidity and the reference humidity.
|
|
- sensor.template.publish:
|
|
id: humidity_delta
|
|
state: !lambda return id(humidity_now).state - id(humidity_reference).state;
|
|
|
|
- id: humidity_trigger_check
|
|
then:
|
|
# Turn the fan on when the humidity delta reaches the upper trigger.
|
|
- if:
|
|
condition:
|
|
and:
|
|
- switch.is_off: relay
|
|
- lambda: return id(humidity_delta).state >= id(delta_upper_trigger).state;
|
|
then:
|
|
- switch.turn_on: relay
|
|
|
|
# Set the last trigger.
|
|
- if:
|
|
condition:
|
|
- lambda: return id(diagnostic_last_trigger).state != "HUMIDITY";
|
|
then:
|
|
- text_sensor.template.publish:
|
|
id: diagnostic_last_trigger
|
|
state: HUMIDITY
|
|
|
|
- id: motion_actions
|
|
then:
|
|
# When motion is detected.
|
|
- if:
|
|
condition:
|
|
- binary_sensor.is_on: motion
|
|
then:
|
|
- script.execute: motion_detected_actions
|
|
|
|
# When motion clears, the fan is on, the countdown has not already started, and humidity is below the lower trigger.
|
|
- if:
|
|
condition:
|
|
and:
|
|
- binary_sensor.is_off: motion
|
|
- switch.is_on: relay
|
|
- lambda: return id(remaining_time).state == 0;
|
|
- lambda: return id(humidity_delta).state <= id(delta_lower_trigger).state;
|
|
then:
|
|
# Start the countdown.
|
|
- script.execute: remaining_time_start
|
|
|
|
- id: motion_detected_actions
|
|
then:
|
|
# Turn on the fan.
|
|
- if:
|
|
condition:
|
|
- switch.is_off: relay
|
|
then:
|
|
- switch.turn_on: relay
|
|
|
|
# Stop the countdown timer.
|
|
- script.execute: remaining_time_clear
|
|
|
|
# Set the last trigger.
|
|
- if:
|
|
condition:
|
|
- lambda: return id(diagnostic_last_trigger).state != "MOTION";
|
|
then:
|
|
- text_sensor.template.publish:
|
|
id: diagnostic_last_trigger
|
|
state: MOTION
|
|
|
|
- id: remaining_time_clear
|
|
then:
|
|
- if:
|
|
condition:
|
|
- lambda: return id(remaining_time).state > 0;
|
|
then:
|
|
- sensor.template.publish:
|
|
id: remaining_time
|
|
state: "0"
|
|
|
|
- id: remaining_time_decrement
|
|
then:
|
|
# Decrement the timer.
|
|
- if:
|
|
condition:
|
|
and:
|
|
- lambda: return id(remaining_time).state > 0;
|
|
- switch.is_on: relay
|
|
then:
|
|
- sensor.template.publish:
|
|
id: remaining_time
|
|
state: !lambda return id(remaining_time).state -= 5;
|
|
|
|
# Turn off the relay when the remaining time reaches zero.
|
|
- if:
|
|
condition:
|
|
and:
|
|
- lambda: return id(remaining_time).state == 0;
|
|
- switch.is_on: relay
|
|
then:
|
|
- switch.turn_off: relay
|
|
|
|
- id: remaining_time_start
|
|
then:
|
|
- sensor.template.publish:
|
|
id: remaining_time
|
|
state: !lambda return $remaining_time_duration;
|
|
|
|
binary_sensor:
|
|
# Physical Toggle Switch. Monitor for switch position changes internally. Do not expose to Home Assistant.
|
|
- platform: gpio
|
|
pin:
|
|
number: $button_gpio
|
|
id: button_toggle
|
|
internal: true
|
|
filters:
|
|
- delayed_on_off: 50ms
|
|
on_state:
|
|
- switch.toggle: relay
|
|
|
|
# Set the last trigger.
|
|
- if:
|
|
condition:
|
|
and:
|
|
- switch.is_on: relay
|
|
- lambda: return id(diagnostic_last_trigger).state != "PHYSICAL";
|
|
then:
|
|
- text_sensor.template.publish:
|
|
id: diagnostic_last_trigger
|
|
state: PHYSICAL
|
|
|
|
# Use motion detection to keep fan running.
|
|
- platform: homeassistant
|
|
id: motion
|
|
entity_id: binary_sensor.upstairs_bathroom_motion
|
|
on_state:
|
|
- script.execute: motion_actions
|
|
|
|
number:
|
|
- platform: template
|
|
name: "$project humidity delta upper trigger"
|
|
id: delta_upper_trigger
|
|
icon: "mdi:timer-play"
|
|
entity_category: config
|
|
unit_of_measurement: "%"
|
|
optimistic: true
|
|
restore_value: true
|
|
min_value: 0.5
|
|
max_value: 30.0
|
|
step: 0.5
|
|
|
|
- platform: template
|
|
name: "$project humidity delta lower trigger"
|
|
id: delta_lower_trigger
|
|
icon: "mdi:timer-stop"
|
|
entity_category: config
|
|
unit_of_measurement: "%"
|
|
optimistic: true
|
|
restore_value: true
|
|
min_value: 0.5
|
|
max_value: 15.0
|
|
step: 0.5
|
|
|
|
sensor:
|
|
- platform: template
|
|
id: humidity_delta
|
|
name: "$project humidity delta"
|
|
accuracy_decimals: 1
|
|
icon: "mdi:delta"
|
|
entity_category: diagnostic
|
|
unit_of_measurement: "%"
|
|
on_value:
|
|
then:
|
|
- script.execute: humidity_trigger_check
|
|
|
|
- platform: homeassistant
|
|
id: humidity_now
|
|
internal: true
|
|
accuracy_decimals: 1
|
|
entity_id: sensor.upstairs_bathroom_humidity
|
|
unit_of_measurement: "%"
|
|
|
|
- platform: homeassistant
|
|
id: humidity_reference
|
|
internal: true
|
|
accuracy_decimals: 1
|
|
entity_id: $humidity_reference_sensor
|
|
unit_of_measurement: "%"
|
|
|
|
- platform: template
|
|
id: remaining_time
|
|
name: "$project remaining time"
|
|
icon: "mdi:sort-clock-ascending"
|
|
accuracy_decimals: 0
|
|
entity_category: diagnostic
|
|
unit_of_measurement: "seconds"
|
|
|
|
## Power monitoring
|
|
- platform: adc
|
|
id: temp_analog_reading
|
|
pin: A0
|
|
|
|
- platform: hlw8012
|
|
cf_pin: GPIO05
|
|
cf1_pin: GPIO13 # not used because it is not available on the 1PM but it is needed to compile
|
|
sel_pin: GPIO14 # not used because it is not available on the 1PM but it is needed to compile
|
|
power:
|
|
name: "$project power"
|
|
id: "shelly1pm_power"
|
|
device_class: power
|
|
state_class: measurement
|
|
unit_of_measurement: W
|
|
accuracy_decimals: 0
|
|
filters:
|
|
# Map from sensor -> measured value
|
|
- calibrate_linear:
|
|
- 0.0 -> 1.0
|
|
- 110.33186 -> 20.62
|
|
- 131.01909 -> 24.32
|
|
- 341.33920 -> 62.08
|
|
- 5561.41553 -> 1000.0
|
|
- 2975.51221 -> 535.7
|
|
- 9612.66309 -> 1720.0
|
|
- 14891.35352 -> 2679.0
|
|
# Make everything below 2W appear as just 0W.
|
|
- lambda: if (x < 2) return 0; else return x;
|
|
update_interval: 60s
|
|
|
|
- platform: total_daily_energy
|
|
name: "$project daily energy"
|
|
power_id: "shelly1pm_power"
|
|
device_class: energy
|
|
state_class: total_increasing
|
|
unit_of_measurement: kWh
|
|
filters:
|
|
- multiply: 0.001
|
|
|
|
# Temperature overheat monitoring.
|
|
- platform: ntc
|
|
sensor: temp_resistance_reading
|
|
name: "$project temperature"
|
|
accuracy_decimals: 1
|
|
device_class: temperature
|
|
unit_of_measurement: "°C"
|
|
disabled_by_default: true
|
|
entity_category: diagnostic
|
|
calibration:
|
|
b_constant: 3350
|
|
reference_resistance: 10kOhm
|
|
reference_temperature: 298.15K
|
|
on_value_range:
|
|
- above: $max_temp
|
|
then:
|
|
- homeassistant.event:
|
|
event: esphome.overheat
|
|
data:
|
|
title: "$project has overheated."
|
|
|
|
- platform: resistance
|
|
id: temp_resistance_reading
|
|
sensor: temp_analog_reading
|
|
configuration: DOWNSTREAM
|
|
resistor: 32kOhm
|
|
|
|
switch:
|
|
- platform: gpio
|
|
id: relay
|
|
pin: $relay_gpio
|
|
on_turn_off:
|
|
- script.execute: remaining_time_clear
|
|
|
|
- platform: template
|
|
id: virtual_switch
|
|
name: $project
|
|
optimistic: no
|
|
lambda: return (id(relay).state);
|
|
turn_off_action:
|
|
- if:
|
|
condition:
|
|
- switch.is_on: relay
|
|
then:
|
|
- switch.turn_off: relay
|
|
turn_on_action:
|
|
- if:
|
|
condition:
|
|
- switch.is_off: relay
|
|
then:
|
|
- switch.turn_on: relay
|
|
|
|
- if:
|
|
condition:
|
|
- lambda: return id(diagnostic_last_trigger).state != "VIRTUAL";
|
|
then:
|
|
- text_sensor.template.publish:
|
|
id: diagnostic_last_trigger
|
|
state: VIRTUAL
|
|
|
|
text_sensor:
|
|
# Trigger states: PHYSICAL, VIRTUAL (UI or voice assistant), MOTION, HUMIDITY
|
|
- platform: template
|
|
id: diagnostic_last_trigger
|
|
name: "$project last trigger"
|
|
disabled_by_default: true
|
|
entity_category: diagnostic
|
|
|
|
time:
|
|
- platform: homeassistant
|
|
id: homeassistant_time
|
|
timezone: America/Los_Angeles
|