You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

887 lines
34 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

######################################################################
# @CCOSTAN - Follow Me on X
# For more info visit https://www.vcloudinfo.com/click-here
# Original Repo : https://github.com/CCOSTAN/Home-AssistantConfig
# -------------------------------------------------------------------
# Climate Control - Seasonal HVAC automations and sensors.
# Thermostat helpers for upstairs/downstairs comfort.
# -------------------------------------------------------------------
# Related Issue: 1571, 273, 1817
# Notes: Filter due alerts use 550h downstairs/450h upstairs runtime thresholds, a 9-month hard limit, 3-day snooze, and Filter Changed push actions.
# Notes: Condenser line cleanout uses a single 360-day reminder for both HVAC systems.
# Notes: Native NFC tag triggers call the per-unit reset scripts; a short single-run guard suppresses duplicate phone scans.
# Notes: Filter history helpers restore across restarts; reset scripts seed the prior input_datetime when history is empty.
# Notes: The snooze notification pattern is also covered at https://youtu.be/y47KSflS1aw.
# Video: https://youtu.be/nScl2JxYSB8
######################################################################
# Contact: @CCOSTAN | Entity reference details below
# Entity reference and attribute summaries:
#
# Entity ID: climate.downstairs
# Supported hvac_modes: heat, cool, heat_cool, off
# Supported fan_modes: on, off
# Supported preset_modes: none, eco
# Supported temperature range: 50°F (min_temp) – 90°F (max_temp)
#
# Current state (example):
# current_temperature: 77
# temperature: 83
# target_temp_high: null
# target_temp_low: null
# current_humidity: 58
# fan_mode: off
# hvac_action: idle
# preset_mode: none
# Supported features: 411
# --------------------------------------------------------------
# Entity ID: climate.upstairs
# Supported hvac_modes: off, heat, cool, heat_cool
# Supported temperature range: 50°F (min_temp) – 90°F (max_temp)
#
# Current state (example):
# current_temperature: 76
# temperature: 78
# target_temp_high: null
# target_temp_low: null
# friendly_name: Upstairs
# Supported features: 387
######################################################################
# ---------------------------------------------------------------------------
# Template helpers – compressor status flags and numeric runtime helpers
# ---------------------------------------------------------------------------
template:
- binary_sensor:
- name: "Downstairs AC is Cooling"
unique_id: downstairs_ac_cooling
state: >
{% set action = state_attr('climate.downstairs', 'hvac_action') %}
{% set current = state_attr('climate.downstairs', 'current_temperature') | float(none) %}
{% set target = state_attr('climate.downstairs', 'temperature') | float(none) %}
{{
action == 'cooling'
or (
action is none
and is_state('climate.downstairs', 'cool')
and current is not none
and target is not none
and current > target
)
}}
- name: "Upstairs AC is Cooling"
unique_id: upstairs_ac_cooling
state: >
{% set action = state_attr('climate.upstairs', 'hvac_action') %}
{% set current = state_attr('climate.upstairs', 'current_temperature') | float(none) %}
{% set target = state_attr('climate.upstairs', 'temperature') | float(none) %}
{{
action == 'cooling'
or (
action is none
and is_state('climate.upstairs', 'cool')
and current is not none
and target is not none
and current > target
)
}}
- name: "HVAC Maintenance Due"
unique_id: hvac_maintenance_due
device_class: problem
icon: mdi:hvac
availability: >-
{{ has_value('sensor.downstairs_ac_runtime_since_last_filter_change')
and has_value('sensor.upstairs_ac_runtime_since_last_filter_change')
and has_value('sensor.hvac_condenser_lines_days_since_cleaned') }}
state: >-
{{ states('sensor.downstairs_ac_runtime_since_last_filter_change') | float(0) >= 550
or states('sensor.upstairs_ac_runtime_since_last_filter_change') | float(0) >= 450
or states('sensor.hvac_condenser_lines_days_since_cleaned') | float(0) >= 360 }}
sensor:
- name: "Downstairs AC Cooling Numeric"
unique_id: downstairs_ac_cooling_numeric
state: "{{ 1 if is_state('binary_sensor.downstairs_ac_is_cooling', 'on') else 0 }}"
- name: "Upstairs AC Cooling Numeric"
unique_id: upstairs_ac_cooling_numeric
state: "{{ 1 if is_state('binary_sensor.upstairs_ac_is_cooling', 'on') else 0 }}"
- name: "HVAC Condenser Lines Days Since Cleaned"
unique_id: hvac_condenser_lines_days_since_cleaned
unit_of_measurement: d
icon: mdi:calendar-clock
availability: >-
{% set raw = states('input_datetime.hvac_condenser_lines_last_cleaned') %}
{{ raw not in ['unknown', 'unavailable', 'none', ''] and as_timestamp(as_datetime(raw), default=none) is not none }}
state: >-
{% set raw = states('input_datetime.hvac_condenser_lines_last_cleaned') %}
{% set cleaned_ts = as_timestamp(as_datetime(raw), default=none) %}
{% if cleaned_ts is not none %}
{{ [((as_timestamp(now()) - cleaned_ts) / 86400) | int(0), 0] | max }}
{% else %}
0
{% endif %}
- name: "Downstairs AC Filter Days Since Last Change"
unique_id: downstairs_ac_filter_days_since_last_change
unit_of_measurement: d
state_class: measurement
icon: mdi:calendar-clock
availability: >-
{% set raw = states('input_datetime.downstairs_last_filter_change') %}
{{ raw not in ['unknown', 'unavailable', 'none', ''] and as_timestamp(as_datetime(raw), default=none) is not none }}
state: >-
{% set raw = states('input_datetime.downstairs_last_filter_change') %}
{% set changed_ts = as_timestamp(as_datetime(raw), default=none) %}
{% if changed_ts is not none %}
{{ [((as_timestamp(now()) - changed_ts) / 86400) | int(0), 0] | max }}
{% else %}
0
{% endif %}
- name: "Upstairs AC Filter Days Since Last Change"
unique_id: upstairs_ac_filter_days_since_last_change
unit_of_measurement: d
state_class: measurement
icon: mdi:calendar-clock
availability: >-
{% set raw = states('input_datetime.upstairs_last_filter_change') %}
{{ raw not in ['unknown', 'unavailable', 'none', ''] and as_timestamp(as_datetime(raw), default=none) is not none }}
state: >-
{% set raw = states('input_datetime.upstairs_last_filter_change') %}
{% set changed_ts = as_timestamp(as_datetime(raw), default=none) %}
{% if changed_ts is not none %}
{{ [((as_timestamp(now()) - changed_ts) / 86400) | int(0), 0] | max }}
{% else %}
0
{% endif %}
- name: "Downstairs AC Filter Average Days Between Changes"
unique_id: downstairs_ac_filter_average_days_between_changes
unit_of_measurement: d
state_class: measurement
icon: mdi:calendar-range
availability: >-
{% set raw = states('input_text.downstairs_filter_change_history') %}
{{ raw not in ['unknown', 'unavailable', 'none', ''] and '||' in raw }}
state: >-
{% set raw = states('input_text.downstairs_filter_change_history') %}
{% if raw in ['unknown', 'unavailable', 'none', ''] %}
unknown
{% else %}
{% set entries = raw.split('||') %}
{% set ns = namespace(previous_ts=none, total_days=0, sample_count=0) %}
{% for entry in entries %}
{% set dt = as_datetime(entry | default('', true) | trim, default=none) %}
{% set ts = as_timestamp(dt, default=none) if dt is not none else none %}
{% if ts is not none %}
{% if ns.previous_ts is not none and ns.previous_ts > ts %}
{% set ns.total_days = ns.total_days + ((ns.previous_ts - ts) / 86400) %}
{% set ns.sample_count = ns.sample_count + 1 %}
{% endif %}
{% set ns.previous_ts = ts %}
{% endif %}
{% endfor %}
{% if ns.sample_count > 0 %}
{{ (ns.total_days / ns.sample_count) | round(1) }}
{% else %}
0
{% endif %}
{% endif %}
- name: "Upstairs AC Filter Average Days Between Changes"
unique_id: upstairs_ac_filter_average_days_between_changes
unit_of_measurement: d
state_class: measurement
icon: mdi:calendar-range
availability: >-
{% set raw = states('input_text.upstairs_filter_change_history') %}
{{ raw not in ['unknown', 'unavailable', 'none', ''] and '||' in raw }}
state: >-
{% set raw = states('input_text.upstairs_filter_change_history') %}
{% if raw in ['unknown', 'unavailable', 'none', ''] %}
unknown
{% else %}
{% set entries = raw.split('||') %}
{% set ns = namespace(previous_ts=none, total_days=0, sample_count=0) %}
{% for entry in entries %}
{% set dt = as_datetime(entry | default('', true) | trim, default=none) %}
{% set ts = as_timestamp(dt, default=none) if dt is not none else none %}
{% if ts is not none %}
{% if ns.previous_ts is not none and ns.previous_ts > ts %}
{% set ns.total_days = ns.total_days + ((ns.previous_ts - ts) / 86400) %}
{% set ns.sample_count = ns.sample_count + 1 %}
{% endif %}
{% set ns.previous_ts = ts %}
{% endif %}
{% endfor %}
{% if ns.sample_count > 0 %}
{{ (ns.total_days / ns.sample_count) | round(1) }}
{% else %}
0
{% endif %}
{% endif %}
- name: "Downstairs AC Filter Change Count"
unique_id: downstairs_ac_filter_change_count
unit_of_measurement: changes
state_class: total
icon: mdi:counter
state: "{{ states('counter.downstairs_filter_change_count') | int(0) }}"
- name: "Upstairs AC Filter Change Count"
unique_id: upstairs_ac_filter_change_count
unit_of_measurement: changes
state_class: total
icon: mdi:counter
state: "{{ states('counter.upstairs_filter_change_count') | int(0) }}"
counter:
downstairs_filter_change_count:
name: Downstairs Filter Change Count
initial: 1
step: 1
icon: mdi:counter
upstairs_filter_change_count:
name: Upstairs Filter Change Count
initial: 1
step: 1
icon: mdi:counter
input_datetime:
downstairs_last_filter_change:
name: Downstairs Last Filter Change
has_date: true
has_time: true
downstairs_filter_snooze_until:
name: Downstairs Filter Snooze Until
has_date: true
has_time: true
upstairs_last_filter_change:
name: Upstairs Last Filter Change
has_date: true
has_time: true
upstairs_filter_snooze_until:
name: Upstairs Filter Snooze Until
has_date: true
has_time: true
hvac_condenser_lines_last_cleaned:
name: HVAC Condenser Lines Last Cleaned
has_date: true
has_time: false
initial: "2026-06-28"
hvac_condenser_lines_cleaning_snooze_until:
name: HVAC Condenser Lines Cleaning Snooze Until
has_date: true
has_time: true
initial: "2026-06-28 00:00:00"
input_text:
downstairs_filter_change_history:
name: Downstairs Filter Change History
max: 255
icon: mdi:history
upstairs_filter_change_history:
name: Upstairs Filter Change History
max: 255
icon: mdi:history
# ---------------------------------------------------------------------------
# Integration sensors tally runtime based on compressor state
# ---------------------------------------------------------------------------
sensor:
- platform: integration
name: Downstairs AC Runtime Raw
source: sensor.downstairs_ac_cooling_numeric
unit_time: h
method: left
round: 2
- platform: integration
name: Upstairs AC Runtime Raw
source: sensor.upstairs_ac_cooling_numeric
unit_time: h
method: left
round: 2
# ---------------------------------------------------------------------------
# Utility‑meters that act like odometers (reset when you swap the filter)
# ---------------------------------------------------------------------------
utility_meter:
downstairs_ac_runtime_since_last_filter_change:
source: sensor.downstairs_ac_runtime_raw
upstairs_ac_runtime_since_last_filter_change:
source: sensor.upstairs_ac_runtime_raw
script:
reset_downstairs_filter:
alias: Reset Downstairs Filter
mode: queued
variables:
occurred_at: "{{ now().strftime('%Y-%m-%d %H:%M:%S') }}"
previous_change: "{{ states('input_datetime.downstairs_last_filter_change') }}"
next_history: >-
{% set current = states('input_text.downstairs_filter_change_history') | default('', true) | string %}
{% set ns = namespace(items=[occurred_at]) %}
{% for raw in current.split('||') + [previous_change] %}
{% set value = raw | trim %}
{% if value and value not in ['unknown', 'unavailable', 'none'] and value not in ns.items and (ns.items | count) < 10 %}
{% set ns.items = ns.items + [value] %}
{% endif %}
{% endfor %}
{{ ns.items | join('||') }}
sequence:
- service: input_datetime.set_datetime
data:
entity_id: input_datetime.downstairs_last_filter_change
datetime: "{{ occurred_at }}"
- service: input_datetime.set_datetime
data:
entity_id: input_datetime.downstairs_filter_snooze_until
datetime: "{{ (now() - timedelta(minutes=1)).strftime('%Y-%m-%d %H:%M:%S') }}"
- service: utility_meter.reset
target:
entity_id: sensor.downstairs_ac_runtime_since_last_filter_change
- service: counter.increment
target:
entity_id: counter.downstairs_filter_change_count
- service: input_text.set_value
target:
entity_id: input_text.downstairs_filter_change_history
data:
value: "{{ next_history | truncate(255, true, '') }}"
reset_upstairs_filter:
alias: Reset Upstairs Filter
mode: queued
variables:
occurred_at: "{{ now().strftime('%Y-%m-%d %H:%M:%S') }}"
previous_change: "{{ states('input_datetime.upstairs_last_filter_change') }}"
next_history: >-
{% set current = states('input_text.upstairs_filter_change_history') | default('', true) | string %}
{% set ns = namespace(items=[occurred_at]) %}
{% for raw in current.split('||') + [previous_change] %}
{% set value = raw | trim %}
{% if value and value not in ['unknown', 'unavailable', 'none'] and value not in ns.items and (ns.items | count) < 10 %}
{% set ns.items = ns.items + [value] %}
{% endif %}
{% endfor %}
{{ ns.items | join('||') }}
sequence:
- service: input_datetime.set_datetime
data:
entity_id: input_datetime.upstairs_last_filter_change
datetime: "{{ occurred_at }}"
- service: input_datetime.set_datetime
data:
entity_id: input_datetime.upstairs_filter_snooze_until
datetime: "{{ (now() - timedelta(minutes=1)).strftime('%Y-%m-%d %H:%M:%S') }}"
- service: utility_meter.reset
target:
entity_id: sensor.upstairs_ac_runtime_since_last_filter_change
- service: counter.increment
target:
entity_id: counter.upstairs_filter_change_count
- service: input_text.set_value
target:
entity_id: input_text.upstairs_filter_change_history
data:
value: "{{ next_history | truncate(255, true, '') }}"
reset_hvac_condenser_lines_cleaned:
alias: Reset HVAC Condenser Lines Cleaned
mode: queued
sequence:
- service: input_datetime.set_datetime
data:
entity_id: input_datetime.hvac_condenser_lines_last_cleaned
date: "{{ now().strftime('%Y-%m-%d') }}"
- service: input_datetime.set_datetime
data:
entity_id: input_datetime.hvac_condenser_lines_cleaning_snooze_until
datetime: "{{ (now() - timedelta(minutes=1)).strftime('%Y-%m-%d %H:%M:%S') }}"
set_upstairs_cool_82:
alias: Set Upstairs Cool 82
mode: single
sequence:
- service: climate.set_hvac_mode
data:
entity_id: climate.upstairs
hvac_mode: cool
- service: climate.set_temperature
data:
entity_id: climate.upstairs
temperature: 82
##############################################################################
### AUTOMATIONS - Thermostat schedules, guardrails, and presence/weather logic
### Some shutoff automations are also in the ALARM.yaml package when windows/doors are left open.
### There are also some automations in the POWERWALL.yaml package when the grid is down.
##############################################################################
automation:
- alias: HVAC Filter NFC Stickers
id: hvac_filter_nfc_stickers
description: Log the unit whose bound HVAC filter NFC sticker was scanned
mode: single
max_exceeded: silent
trigger:
- platform: tag
tag_id: fcf8bb30-2887-406b-8f9d-b6048e6ac196
id: upstairs
- platform: tag
tag_id: 356ba9c3-312b-433a-975f-3f46a09ab33e
id: downstairs
action:
- choose:
- conditions:
- condition: trigger
id: upstairs
sequence:
- service: script.reset_upstairs_filter
- service: script.send_to_logbook
data:
topic: "MAINTENANCE"
message: "Upstairs HVAC filter change logged from NFC sticker."
- conditions:
- condition: trigger
id: downstairs
sequence:
- service: script.reset_downstairs_filter
- service: script.send_to_logbook
data:
topic: "MAINTENANCE"
message: "Downstairs HVAC filter change logged from NFC sticker."
- delay: "00:00:10"
- alias: Notify Climate Filter Change Due
id: notify_climate_filter_change_due
description: Notify when HVAC filter runtime exceeds the location threshold or age reaches 9 months
trigger:
- platform: numeric_state
entity_id: sensor.downstairs_ac_runtime_since_last_filter_change
above: 550
id: downstairs_runtime
- platform: time
at: "09:00:00"
id: downstairs_daily
- platform: numeric_state
entity_id: sensor.upstairs_ac_runtime_since_last_filter_change
above: 450
id: upstairs_runtime
- platform: time
at: "09:10:00"
id: upstairs_daily
variables:
location: "{{ 'upstairs' if trigger.id.startswith('upstairs') else 'downstairs' }}"
location_title: "{{ 'Upstairs' if location == 'upstairs' else 'Downstairs' }}"
runtime_sensor: "sensor.{{ location }}_ac_runtime_since_last_filter_change"
last_filter_entity: "input_datetime.{{ location }}_last_filter_change"
snooze_entity: "input_datetime.{{ location }}_filter_snooze_until"
runtime_hours: "{{ states(runtime_sensor) | float(0) }}"
runtime_threshold_hours: "{{ 450 if location == 'upstairs' else 550 }}"
last_filter_ts: "{{ as_timestamp(states(last_filter_entity), 0) }}"
filter_age_days: "{{ ((as_timestamp(now()) - (last_filter_ts | float(0))) / 86400) | round(0) }}"
due_reason: >-
{% set runtime_due = runtime_hours | float(0) > (runtime_threshold_hours | float(0)) %}
{% set age_due = (last_filter_ts | float(0)) == 0 or (filter_age_days | int(0)) >= 274 %}
{% if runtime_due and age_due %}
Runtime has exceeded {{ runtime_threshold_hours }}h and the filter is {{ filter_age_days }} days old.
{% elif runtime_due %}
Runtime has exceeded {{ runtime_threshold_hours }}h.
{% else %}
Filter age has reached the 9-month hard limit ({{ filter_age_days }} days).
{% endif %}
condition:
- condition: template
value_template: >-
{{
(runtime_hours | float(0) > (runtime_threshold_hours | float(0)))
or (last_filter_ts | float(0)) == 0
or (filter_age_days | int(0)) >= 274
}}
- condition: template
value_template: >-
{% set snooze_until = as_timestamp(states(snooze_entity), 0) %}
{{ snooze_until <= as_timestamp(now()) }}
action:
- service: script.send_to_logbook
data:
topic: "MAINTENANCE"
message: >-
{{ location_title }} AC filter due. {{ due_reason }} Runtime is {{ runtime_hours | round(1) }}h.
- service: script.notify_engine_two_button
data:
title: "Home Maintenance Reminder"
value1: "It's time to change your {{ location_title }} AC filter."
value2: >
{{ due_reason }} Runtime is {{ runtime_hours | round(1) }}h.
title1: "Snooze 3d"
action1: "{{ 'SNOOZE_UPSTAIRS_FILTER_3D' if location == 'upstairs' else 'SNOOZE_DOWNSTAIRS_FILTER_3D' }}"
icon1: "sfsymbols:clock"
title2: "Filter Changed"
action2: "{{ 'RESET_UPSTAIRS_FILTER' if location == 'upstairs' else 'RESET_DOWNSTAIRS_FILTER' }}"
icon2: "sfsymbols:checkmark.circle"
who: "carlo"
group: "maintenance"
level: "active"
- alias: Climate Filter Reminder Actions
id: 6d7056d0-90ce-4c4f-b8b1-fd32a7e58311
mode: queued
trigger:
- platform: event
event_type: mobile_app_notification_action
event_data:
action: SNOOZE_DOWNSTAIRS_FILTER_3D
id: downstairs_snooze
- platform: event
event_type: mobile_app_notification_action
event_data:
action: SNOOZE_UPSTAIRS_FILTER_3D
id: upstairs_snooze
- platform: event
event_type: mobile_app_notification_action
event_data:
action: RESET_DOWNSTAIRS_FILTER
id: downstairs_reset
- platform: event
event_type: mobile_app_notification_action
event_data:
action: RESET_UPSTAIRS_FILTER
id: upstairs_reset
variables:
location: "{{ 'upstairs' if trigger.id.startswith('upstairs') else 'downstairs' }}"
location_title: "{{ 'Upstairs' if location == 'upstairs' else 'Downstairs' }}"
snooze_entity: "input_datetime.{{ location }}_filter_snooze_until"
reset_script: "script.reset_{{ location }}_filter"
action:
- choose:
- conditions: "{{ trigger.id.endswith('_snooze') }}"
sequence:
- variables:
snooze_until: "{{ (now() + timedelta(days=3)).strftime('%Y-%m-%d %H:%M:%S') }}"
- service: input_datetime.set_datetime
target:
entity_id: "{{ snooze_entity }}"
data:
datetime: "{{ snooze_until }}"
- service: script.send_to_logbook
data:
topic: "MAINTENANCE"
message: "{{ location_title }} AC filter reminder snoozed until {{ snooze_until }}."
- conditions: "{{ trigger.id.endswith('_reset') }}"
sequence:
- service: "{{ reset_script }}"
- service: script.send_to_logbook
data:
topic: "MAINTENANCE"
message: "{{ location_title }} AC filter reset from notification action."
- alias: Notify HVAC Condenser Lines Cleaning Due
id: 4dafac96-3163-4d63-847a-76727005f75b
description: Notify every 360 days when the outdoor condenser lines should be cleaned
trigger:
- platform: time
at: "09:20:00"
variables:
cleaned: "{{ states('input_datetime.hvac_condenser_lines_last_cleaned') }}"
cleaned_ts: >-
{% if cleaned in ['unknown', 'unavailable', 'none', ''] %}
0
{% else %}
{{ as_timestamp(strptime(cleaned, '%Y-%m-%d'), 0) }}
{% endif %}
days_since_cleaned: "{{ ((as_timestamp(now()) - (cleaned_ts | float(0))) / 86400) | round(0) }}"
condition:
- condition: template
value_template: "{{ (cleaned_ts | float(0)) == 0 or (days_since_cleaned | int(0)) >= 360 }}"
- condition: template
value_template: >-
{% set snooze_until = as_timestamp(states('input_datetime.hvac_condenser_lines_cleaning_snooze_until'), 0) %}
{{ snooze_until <= as_timestamp(now()) }}
action:
- service: script.send_to_logbook
data:
topic: "MAINTENANCE"
message: "HVAC condenser lines cleanout due; last cleaned {{ days_since_cleaned }} days ago."
- service: script.notify_engine_two_button
data:
title: "Home Maintenance Reminder"
value1: "Clean the HVAC condenser lines."
value2: "360-day cleanout is due; last cleaned {{ days_since_cleaned }} days ago."
title1: "Snooze 3d"
action1: "SNOOZE_HVAC_CONDENSER_LINES_3D"
icon1: "sfsymbols:clock"
title2: "Cleaned"
action2: "RESET_HVAC_CONDENSER_LINES_CLEANED"
icon2: "sfsymbols:checkmark.circle"
who: "carlo"
group: "maintenance"
level: "active"
- alias: Climate Condenser Lines Reminder Actions
id: dfe89869-4bd3-47f6-bf0d-612bf7ee949f
mode: queued
trigger:
- platform: event
event_type: mobile_app_notification_action
event_data:
action: SNOOZE_HVAC_CONDENSER_LINES_3D
id: snooze
- platform: event
event_type: mobile_app_notification_action
event_data:
action: RESET_HVAC_CONDENSER_LINES_CLEANED
id: reset
action:
- choose:
- conditions: "{{ trigger.id == 'snooze' }}"
sequence:
- variables:
snooze_until: "{{ (now() + timedelta(days=3)).strftime('%Y-%m-%d %H:%M:%S') }}"
- service: input_datetime.set_datetime
target:
entity_id: input_datetime.hvac_condenser_lines_cleaning_snooze_until
data:
datetime: "{{ snooze_until }}"
- service: script.send_to_logbook
data:
topic: "MAINTENANCE"
message: "HVAC condenser lines cleanout reminder snoozed until {{ snooze_until }}."
- conditions: "{{ trigger.id == 'reset' }}"
sequence:
- service: script.reset_hvac_condenser_lines_cleaned
- service: script.send_to_logbook
data:
topic: "MAINTENANCE"
message: "HVAC condenser lines cleanout reset from notification action."
- alias: 'AC Status Announcement'
id: 7812fdaf-a3f8-498b-8f07-28e977e528fe
trigger:
- platform: state
entity_id:
- climate.downstairs
- climate.upstairs
from: 'off'
- platform: state
entity_id:
- climate.downstairs
- climate.upstairs
to: 'off'
action:
- service: script.speech_engine
data:
NestStatus: "The {{ trigger.entity_id.split('.')[1].split('_')[0]}} {{ trigger.entity_id.split('.')[1].split('_')[1]}} has now been turned on for {{(trigger.to_state.state)}}."
call_window_check: 1
- alias: Prevent Upstairs Nest Below 76
id: 7812fdaf-a3f8-498b-8f07-28e977e528ff
trigger:
- platform: numeric_state
entity_id: climate.upstairs
attribute: temperature
below: 76
condition:
- condition: template
value_template: "{{ is_state('binary_sensor.powerwall_grid_status', 'on') }}"
action:
- delay: "00:03:00"
- service: climate.set_temperature
data:
entity_id: climate.upstairs
temperature: 78
- service: script.send_to_logbook
data:
topic: "CLIMATE"
message: "Upstairs target temp corrected to 78F (guardrail)."
- alias: Set Downstairs Bedtime Target When Both in Bed
id: set_downstairs_nest_77_both_in_bed
trigger:
- platform: state
entity_id: group.bed
to: 'on'
condition:
- condition: and
conditions:
- condition: state
entity_id: binary_sensor.sleepnumber_carlo_carlo_is_in_bed
state: 'on'
- condition: state
entity_id: binary_sensor.sleepnumber_carlo_stacey_is_in_bed
state: 'on'
- condition: state
entity_id: binary_sensor.powerwall_grid_status
state: 'on'
- condition: state
entity_id: sun.sun
state: 'below_horizon'
action:
- choose:
- conditions:
- condition: numeric_state
entity_id: sensor.pirateweather_temperature
above: 80
sequence:
- service: climate.set_temperature
data:
entity_id: climate.downstairs
temperature: 77
- conditions:
- condition: numeric_state
entity_id: sensor.pirateweather_temperature
above: 75
below: 80
sequence:
- service: climate.set_temperature
data:
entity_id: climate.downstairs
temperature: 79
- alias: 'Set Downstairs Thermostat Daytime Target'
id: set_downstairs_thermostat_daytime_target
mode: single
trigger:
- platform: state
entity_id: sun.sun
to: 'above_horizon'
- platform: time
at: "08:00:00"
- platform: numeric_state
entity_id: sensor.pirateweather_temperature
above: 90
for: "01:00:00"
- platform: numeric_state
entity_id: sensor.pirateweather_temperature
below: 90
for: "01:00:00"
condition:
- condition: and
conditions:
- condition: state
entity_id: sun.sun
state: 'above_horizon'
- condition: state
entity_id: input_boolean.guest_mode
state: 'off'
- condition: state
entity_id: binary_sensor.powerwall_grid_status
state: 'on'
action:
- service: script.reconcile_hvac_state
data:
reason: daytime target refresh
# Set thermostats to eco mode when everyone is away
- alias: 'Set Thermostats to Eco When Away'
id: 1e2d3c4b-eco-thermostat-away
mode: single
trigger:
- platform: state
entity_id: group.family
to: 'not_home'
condition:
- condition: state
entity_id: group.family
state: 'not_home'
- condition: state
entity_id: input_boolean.guest_mode
state: 'off'
action:
- service: script.reconcile_hvac_state
data:
reason: family away
# Automation: Set Thermostats Back to Cool Mode
- alias: 'Set Thermostats to Cool When Home'
id: 1e2d3c4b-cool-thermostat-home
mode: single
trigger:
- platform: state
entity_id: group.family
to: 'home'
condition:
- condition: or
conditions:
- condition: state
entity_id: group.family
state: 'home'
- condition: template
value_template: "{{ is_state('input_boolean.guest_mode', 'off') }}"
- condition: state
entity_id: binary_sensor.powerwall_grid_status
state: 'on'
action:
- service: script.reconcile_hvac_state
data:
reason: family home
- alias: 'Reset Upstairs to 82 Each Night'
id: reset_upstairs_thermostat_nightly_82
mode: single
trigger:
- platform: time
at: "03:00:00"
condition:
- condition: template
value_template: "{{ states('binary_sensor.powerwall_grid_status') == 'on' }}"
action:
- service: script.set_upstairs_cool_82
- alias: 'Humidity Control'
id: AC_Humidity_Control
mode: single
trigger:
- platform: numeric_state
entity_id: sensor.downstairs_humidity
above: 60
- platform: time
at: "02:00:00"
condition:
- condition: time
after: "01:00:00"
before: "08:00:00"
- condition: numeric_state
entity_id: sensor.downstairs_humidity
above: 60
- condition: template #Only needed in the winter months
value_template: "{{ now().month in [10, 11, 12, 1, 2, 3] }}"
- condition: template # Only run if AC is idle (prevents fighting other automations)
value_template: "{{ state_attr('climate.downstairs', 'hvac_action') == 'idle' }}"
- condition: template # Never run if the grid is down and running on powerwall.
value_template: "{{ states('binary_sensor.powerwall_grid_status')|lower == 'on' }}"
- condition: state
entity_id: group.entry_points
state: 'off'
action:
- choose:
- conditions:
- condition: numeric_state
entity_id: sensor.pirateweather_temperature
below: 75
sequence:
- service: script.send_to_logbook
data:
topic: "CLIMATE"
message: "Skipping downstairs cool mode (outside temp <75F)."
default:
- service: climate.set_preset_mode
data:
entity_id: climate.downstairs
preset_mode: 'none'
- service: climate.set_hvac_mode
data:
entity_id: climate.downstairs
hvac_mode: cool
- service: climate.set_temperature
data:
entity_id: climate.downstairs
temperature: 77
- delay: "00:20:00"
- service: script.reconcile_hvac_state
data:
reason: humidity cycle complete
- delay: "02:00:00" # stops it from being triggered again too soon.

Powered by TurnKey Linux.