mirror of
https://github.com/CCOSTAN/Home-AssistantConfig.git
synced 2026-07-01 20:14:17 -07:00
Add HVAC and holiday lighting helpers
This commit is contained in:
@@ -17,8 +17,19 @@
|
||||
entity: sensor.upstairs_ac_runtime_since_last_filter_change
|
||||
- type: tile
|
||||
entity: input_datetime.downstairs_last_filter_change
|
||||
tap_action:
|
||||
action: more-info
|
||||
entity: script.reset_downstairs_filter
|
||||
- type: tile
|
||||
entity: input_datetime.upstairs_last_filter_change
|
||||
tap_action:
|
||||
action: more-info
|
||||
entity: script.reset_upstairs_filter
|
||||
- type: tile
|
||||
entity: input_datetime.hvac_condenser_lines_last_cleaned
|
||||
tap_action:
|
||||
action: more-info
|
||||
entity: script.reset_hvac_condenser_lines_cleaned
|
||||
- type: custom:power-flow-card-plus
|
||||
entities:
|
||||
battery:
|
||||
|
||||
@@ -41,10 +41,10 @@ Live collection of plug-and-play Home Assistant packages. Each YAML file in this
|
||||
| [alarm.yaml](alarm.yaml) | NodeMCU-powered perimeter monitoring with arm/disarm helpers and rich notifications. | `binary_sensor.mcu*_gpio*`, `group.family`, notify + siren scripts |
|
||||
| [alexa_media_player.yaml](alexa_media_player.yaml) | Alexa Media helper sensors including stable bedroom wake-alarm wrappers for Carlo and Stacey plus a combined next-wake view. | `sensor.last_alexa`, `sensor.bedroom_next_wake_alarm`, `sensor.bedroom_next_wake_alarm_source`, `binary_sensor.bedroom_next_wake_alarm_active` |
|
||||
| [fridge.yaml](fridge.yaml) | SmartThinQ fridge monitoring with 6-minute raw door-open alerts plus fridge/freezer status announcements. | `binary_sensor.refrigerator_door_open`, `script.notify_engine`, `script.speech_engine` |
|
||||
| [climate.yaml](climate.yaml) | Nest climate schedules plus runtime-based AC filter reminders with snooze and filter-changed actions. | `input_datetime.*_filter_snooze_until`, `script.notify_engine_two_button`, mobile app action events |
|
||||
| [climate.yaml](climate.yaml) | Nest climate schedules plus 350-hour/9-month AC filter reminders and 360-day condenser-line cleanout reminders with snooze and changed/cleaned actions. | `input_datetime.*_filter_snooze_until`, `sensor.*_ac_runtime_since_last_filter_change`, `input_datetime.hvac_condenser_lines_last_cleaned`, `script.notify_engine_two_button`, mobile app action events |
|
||||
| [garadget.yaml](garadget.yaml) | MQTT-based garage door control plus arrival helpers, entry prompts, wind checks, nighttime reminders, and camera context. | `cover.large_garage_door`, `cover.small_garage_door`, `group.garage_doors`, `script.open_large_garage_door_if_ready` |
|
||||
| [august.yaml](august.yaml) | Front-door August smart lock with Alexa Show camera pop-up when unlocked. | `lock.front_door`, media_player actions for front doorbell camera |
|
||||
| [holiday.yaml](holiday.yaml) | REST-driven US holiday + flag sensors that color scenes and exterior lighting. | `sensor.holiday`, `sensor.flag`, JSON feed at `config/json_data/holidays.json` |
|
||||
| [holiday.yaml](holiday.yaml) | REST-driven US holiday + flag sensors plus the inspectable exterior lighting mode. | `sensor.holiday`, `sensor.flag`, `sensor.holiday_lighting_mode`, `sensor.holiday_lighting_scene`, JSON feed at `config/www/json_data/holidays.json` |
|
||||
| [lightning.yaml](lightning.yaml) | Blitzortung lightning counter monitoring with snoozeable push actions. | `sensor.blitzortung_lightning_counter`, `input_boolean.snooze_lightning`, notify engine actions |
|
||||
| [logbook_activity_feed.yaml](logbook_activity_feed.yaml) | Dummy `sensor.activity_feed` + helper to write clean Activity entries (Issue #1550). | `sensor.activity_feed`, `script.send_to_logbook` |
|
||||
| [mariadb_monitoring.yaml](mariadb_monitoring.yaml) | MariaDB health sensors and Lovelace dashboard snippet for recorder stats. | `sensor.mariadb_status`, `sensor.database_size` |
|
||||
@@ -66,7 +66,7 @@ Live collection of plug-and-play Home Assistant packages. Each YAML file in this
|
||||
| [vacation_mode.yaml](vacation_mode.yaml) | Auto-enable vacation mode after 24 hours away or no bed use, track sitter analytics/secure-house checks, stale-visit timeout protection, deliver sitter-facing briefings, and send 10 AM/10 PM house digests with Powerwall/security/backup status plus Joanna review. | `input_boolean.vacation_mode`, `input_boolean.house_sitter_present`, `input_datetime.vacation_house_sitter_*`, `input_datetime.vacation_house_status_digest_last_sent`, `sensor.vacation_house_sitter_*`, `binary_sensor.powerwall_grid_status`, `sensor.powerwall_charge`, `group.garage_doors`, `lock.front_door`, `script.vacation_house_sitter_clear_presence`, `script.vacation_house_status_digest`, `script.notify_engine`, `rest_command.bearclaw_command`, `script.joanna_send_telegram` |
|
||||
| [maintenance_log.yaml](maintenance_log.yaml) | Joanna maintenance webhook ingest for water softener salt with idempotent event handling, Activity feed logging, and recorder-backed helper history for long-term graphing. | `automation.maintenance_log_joanna_webhook_ingest`, `input_number.water_softener_salt_total_added_lb`, `counter.water_softener_salt_event_count`, `sensor.water_softener_salt_days_since_last_add` |
|
||||
| [kiosk_tablet.yaml](kiosk_tablet.yaml) | Keeps the bedroom Fully Kiosk Fire tablet pinned to the dedicated camera kiosk dashboard when the page or foreground app drifts; normal wake events only restore brightness/focus. See the [video walkthrough](https://youtu.be/ChgEu0IDWzc). | `sensor.alarm_panel_1_current_page`, `sensor.alarm_panel_1_foreground_app`, `button.alarm_panel_1_bring_to_foreground`, `button.alarm_panel_1_load_start_url` |
|
||||
| [powerwall.yaml](powerwall.yaml) | Track Tesla Powerwall grid status, push live outage tracking to mobile targets, and shed loads automatically when off-grid (alerts include Activity feed + Repairs). | `binary_sensor.powerwall_grid_status`, `sensor.powerwall_*`, `script.notify_live_activity`, `repairs.create` |
|
||||
| [powerwall.yaml](powerwall.yaml) | Track Tesla Powerwall grid status, push live outage tracking to mobile targets, shed loads automatically when off-grid, and alert if the Powerwall stays low after grid power returns; see the [watchdog video](https://youtu.be/hR_0lFEE2bA) and [companion post](https://www.vcloudinfo.com/2026/06/tesla-powerwall-home-assistant-watchdog.html). | `binary_sensor.powerwall_grid_status`, `sensor.powerwall_*`, `script.notify_live_activity`, `repairs.create` |
|
||||
| [tesla_model_y.yaml](tesla_model_y.yaml) | Remind the garage and parents to plug in the Model Y after the large garage door closes with a low battery, charging still off, both parents home, and a 24-hour mobile snooze available from the push reminder. | `sensor.spaceship_battery_level`, `switch.spaceship_charge`, `person.carlo`, `person.stacey`, `cover.large_garage_door`, `input_datetime.tesla_model_y_last_garage_close`, `input_datetime.tesla_model_y_plug_in_snooze_until`, `notify.alexa_media_garage`, `script.notify_engine_two_button` |
|
||||
| [vacuum.yaml](vacuum.yaml) | Dreame vacuum orchestration with room tracking, push alerts, Activity feed, Repairs issues on errors, and Alexa one-off room-clean switches. | `input_select.l10s_vacuum_phase`, `sensor.l10s_vacuum_error`, `repairs.create` |
|
||||
| [hass_agent_homepc.yaml](hass_agent_homepc.yaml) | Mirrors PC lock/unlock state to the office lamp and wakes `CARLO-HOMEPC` on workday morning bed exits. | `sensor.carlo_homepc_carlo_homepc_sessionstate`, `button.carlo_home`, `switch.office_lamp_switch` |
|
||||
@@ -110,7 +110,7 @@ When a package has a dedicated blog post or video, I link it right inside the YA
|
||||
| [holiday.yaml](holiday.yaml) | How the holiday/flag sensor works and drives lighting playlists. | [Blog + video breakdown](https://www.vcloudinfo.com/2019/02/breaking-down-the-flag-sensor-in-home-assistant.html) |
|
||||
| [lightning.yaml](lightning.yaml) | Blitzortung detector wiring, strike alerts, and snooze workflow. | [Blog](https://www.vcloudinfo.com/2020/08/adding-a-lightning-sensor-to-home-assistant.html) |
|
||||
| [phynplus.yaml](phynplus.yaml) | Leak-detection response loop with valve state, maintenance guard, Activity Feed context, Repairs tracking, and critical push recovery. | [Video walkthrough](https://youtu.be/xbhgWnomFYI) · [Companion post](https://www.vcloudinfo.com/2026/06/home-assistant-leak-detection-automations.html) · [Original Phyn Plus post](https://www.vcloudinfo.com/2020/05/phyn-plus-smart-water-shutoff-device.html) |
|
||||
| [powerwall.yaml](powerwall.yaml) | Monitoring Tesla Powerwall health + what to automate when the grid drops. | [Blog](https://www.vcloudinfo.com/2018/01/going-green-to-save-some-green-in-2018.html) |
|
||||
| [powerwall.yaml](powerwall.yaml) | Monitoring Tesla Powerwall health + what to automate when the grid drops. | [Video walkthrough](https://youtu.be/hR_0lFEE2bA) · [Companion post](https://www.vcloudinfo.com/2026/06/tesla-powerwall-home-assistant-watchdog.html) · [Original solar/Powerwall blog](https://www.vcloudinfo.com/2018/01/going-green-to-save-some-green-in-2018.html) |
|
||||
| [vacation_mode.yaml](vacation_mode.yaml) | Sustained-away Vacation Mode, house-sitter visit tracking, reminders, missed-visit alerts, secure-house checks, and vacation house digests that keep Powerwall status visible. | [Video walkthrough](https://youtu.be/15kRcFaVV2Y) · [Blog](https://www.vcloudinfo.com/2026/05/home-assistant-vacation-mode-house-sitter-automation.html) |
|
||||
| [vacuum.yaml](vacuum.yaml) | Dreame away-only cleaning, room queues, sweep/mop phases, Alexa one-off room commands, and rescue notifications. | [Video walkthrough](https://youtu.be/KKOWSKuF5jA) · [Companion post](https://www.vcloudinfo.com/2026/05/home-assistant-vacuum-automations-dreame-2026.html) · [Older Neato post](https://www.vcloudinfo.com/2020/05/home-assistant-neato-vacuum-automation.html) |
|
||||
| [pihole_ha.yaml](pihole_ha.yaml) | Sync Pi-hole blocking state across HA DNS nodes. | |
|
||||
@@ -128,7 +128,7 @@ These are the devices that power the packages above. Affiliate links never chang
|
||||
| Amazon Echo Show | Pops up the front doorbell camera when the August lock unlocks. | [august.yaml](august.yaml) | [](https://amzn.to/4ptA3YO) |
|
||||
| Phyn Plus water shutoff | [phynplus.yaml](phynplus.yaml) | Leak events trigger valve closes + critical push notifications. [Video walkthrough](https://youtu.be/xbhgWnomFYI) and [companion post](https://www.vcloudinfo.com/2026/06/home-assistant-leak-detection-automations.html). | [](https://amzn.to/2Zy3sbJ) |
|
||||
| Rachio sprinkler controller | [rachio.yaml](rachio.yaml) | Rain skips and seasonal watering adjustments happen automatically. | [](https://amzn.to/2eoPKBW) |
|
||||
| Tesla Powerwall 2 | [powerwall.yaml](powerwall.yaml) | Grid outages kick off load-shed scripts and status pings. | [](https://amzn.to/3UM4BZ5) |
|
||||
| Tesla Powerwall 2 | [powerwall.yaml](powerwall.yaml) | Grid outages kick off status pings, load-shed scripts, and low-charge charging watchdog alerts; see the [video walkthrough](https://youtu.be/hR_0lFEE2bA). | [](https://amzn.to/3UM4BZ5) |
|
||||
| Google Nest thermostat | [climate.yaml](climate.yaml) | Presence/weather/grid-aware cooling targets, humidity pulses, and eco recovery. | [](https://amzn.to/4olpINw) |
|
||||
| Dreame/Neato vacuum | [vacuum.yaml](vacuum.yaml) | Away-only room queues, sweep/mop phases, Alexa room cleans, rescue notifications, and voice callouts. [Video walkthrough](https://youtu.be/KKOWSKuF5jA) and [companion post](https://www.vcloudinfo.com/2026/05/home-assistant-vacuum-automations-dreame-2026.html). | [](https://amzn.to/4f7NpFP) |
|
||||
| NodeMCU motion/contact sensor | [alarm.yaml](alarm.yaml), [office_motion.yaml](office_motion.yaml) | ESP8266 nodes feed the alarm matrix and room-aware lighting. | [](https://amzn.to/2oUgj5i) |
|
||||
|
||||
+177
-70
@@ -7,7 +7,8 @@
|
||||
# Thermostat helpers for upstairs/downstairs comfort.
|
||||
# -------------------------------------------------------------------
|
||||
# Related Issue: 1571
|
||||
# Notes: Filter due alerts include 3-day snooze and Filter Changed push actions.
|
||||
# Notes: Filter due alerts use a 350h runtime threshold, 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.
|
||||
# Video: https://youtu.be/y47KSflS1aw
|
||||
# Blog: https://www.vcloudinfo.com/2026/06/home-assistant-notification-snooze-buttons.html
|
||||
######################################################################
|
||||
@@ -53,19 +54,43 @@ template:
|
||||
- name: "Downstairs AC is Cooling"
|
||||
unique_id: downstairs_ac_cooling
|
||||
state: >
|
||||
{{ state_attr('climate.downstairs', 'hvac_action') == 'cooling' }}
|
||||
{% 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: >
|
||||
{{ state_attr('climate.upstairs', 'hvac_action') == 'cooling' }}
|
||||
{% 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
|
||||
)
|
||||
}}
|
||||
|
||||
sensor:
|
||||
- name: "Downstairs AC Cooling Numeric"
|
||||
unique_id: downstairs_ac_cooling_numeric
|
||||
state: "{{ 1 if is_state('binary_sensor.downstairs_ac_cooling', 'on') else 0 }}"
|
||||
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_cooling', 'on') else 0 }}"
|
||||
state: "{{ 1 if is_state('binary_sensor.upstairs_ac_is_cooling', 'on') else 0 }}"
|
||||
|
||||
input_datetime:
|
||||
downstairs_last_filter_change:
|
||||
@@ -84,6 +109,16 @@ input_datetime:
|
||||
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"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Integration sensors tally runtime based on compressor state
|
||||
@@ -144,6 +179,19 @@ script:
|
||||
target:
|
||||
entity_id: sensor.upstairs_ac_runtime_since_last_filter_change
|
||||
|
||||
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_downstairs_target_temp_based_on_conditions:
|
||||
alias: Set Downstairs Target Temperature Based on Conditions
|
||||
mode: single
|
||||
@@ -285,81 +333,76 @@ script:
|
||||
### There are also some automations in the POWERWALL.yaml package when the grid is down.
|
||||
##############################################################################
|
||||
automation:
|
||||
- alias: Notify Downstairs Filter Change Due
|
||||
description: Notify when downstairs runtime exceeds threshold since last filter change
|
||||
- alias: Notify Climate Filter Change Due
|
||||
description: Notify when HVAC filter runtime exceeds 350h or age reaches 9 months
|
||||
trigger:
|
||||
- platform: numeric_state
|
||||
entity_id: sensor.downstairs_ac_runtime_since_last_filter_change
|
||||
above: 800 # hours
|
||||
above: 350
|
||||
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: 350
|
||||
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) }}"
|
||||
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) > 350 %}
|
||||
{% 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 350h and the filter is {{ filter_age_days }} days old.
|
||||
{% elif runtime_due %}
|
||||
Runtime has exceeded 350h.
|
||||
{% else %}
|
||||
Filter age has reached the 9-month hard limit ({{ filter_age_days }} days).
|
||||
{% endif %}
|
||||
condition:
|
||||
- condition: numeric_state
|
||||
entity_id: sensor.downstairs_ac_runtime_since_last_filter_change
|
||||
above: 800
|
||||
- condition: template
|
||||
value_template: >-
|
||||
{% set snooze_until = as_timestamp(states('input_datetime.downstairs_filter_snooze_until'), 0) %}
|
||||
{{
|
||||
(runtime_hours | float(0) > 350)
|
||||
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: >-
|
||||
Downstairs AC filter due (runtime >800h). Last changed {{ ((now() - states.input_datetime.downstairs_last_filter_change.last_changed).total_seconds() / 86400) | round(0) }} days ago.
|
||||
{{ 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 Downstairs AC filter."
|
||||
value1: "It's time to change your {{ location_title }} AC filter."
|
||||
value2: >
|
||||
Runtime has exceeded 800h. Last changed {{ ((now() - states.input_datetime.downstairs_last_filter_change.last_changed).total_seconds() / 86400) | round(0) }} days ago.
|
||||
{{ due_reason }} Runtime is {{ runtime_hours | round(1) }}h.
|
||||
title1: "Snooze 3d"
|
||||
action1: "SNOOZE_DOWNSTAIRS_FILTER_3D"
|
||||
action1: "{{ 'SNOOZE_UPSTAIRS_FILTER_3D' if location == 'upstairs' else 'SNOOZE_DOWNSTAIRS_FILTER_3D' }}"
|
||||
icon1: "sfsymbols:clock"
|
||||
title2: "Filter Changed"
|
||||
action2: "RESET_DOWNSTAIRS_FILTER"
|
||||
action2: "{{ 'RESET_UPSTAIRS_FILTER' if location == 'upstairs' else 'RESET_DOWNSTAIRS_FILTER' }}"
|
||||
icon2: "sfsymbols:checkmark.circle"
|
||||
who: "carlo"
|
||||
group: "maintenance"
|
||||
level: "active"
|
||||
|
||||
- alias: Notify Upstairs Filter Change Due
|
||||
description: Notify when upstairs runtime exceeds threshold since last filter change
|
||||
trigger:
|
||||
- platform: numeric_state
|
||||
entity_id: sensor.upstairs_ac_runtime_since_last_filter_change
|
||||
above: 450 # hours
|
||||
- platform: time
|
||||
at: "09:10:00"
|
||||
condition:
|
||||
- condition: numeric_state
|
||||
entity_id: sensor.upstairs_ac_runtime_since_last_filter_change
|
||||
above: 450
|
||||
- condition: template
|
||||
value_template: >-
|
||||
{% set snooze_until = as_timestamp(states('input_datetime.upstairs_filter_snooze_until'), 0) %}
|
||||
{{ snooze_until <= as_timestamp(now()) }}
|
||||
action:
|
||||
- service: script.send_to_logbook
|
||||
data:
|
||||
topic: "MAINTENANCE"
|
||||
message: >-
|
||||
Upstairs AC filter due (runtime >450h). Last changed {{ ((now() - states.input_datetime.upstairs_last_filter_change.last_changed).total_seconds() / 86400) | round(0) }} days ago.
|
||||
- service: script.notify_engine_two_button
|
||||
data:
|
||||
title: "Home Maintenance Reminder"
|
||||
value1: "It's time to change your Upstairs AC filter."
|
||||
value2: >
|
||||
Runtime has exceeded 450h. Last changed {{ ((now() - states.input_datetime.upstairs_last_filter_change.last_changed).total_seconds() / 86400) | round(0) }} days ago.
|
||||
title1: "Snooze 3d"
|
||||
action1: "SNOOZE_UPSTAIRS_FILTER_3D"
|
||||
icon1: "sfsymbols:clock"
|
||||
title2: "Filter Changed"
|
||||
action2: "RESET_UPSTAIRS_FILTER"
|
||||
icon2: "sfsymbols:checkmark.circle"
|
||||
who: "carlo"
|
||||
group: "maintenance"
|
||||
|
||||
- alias: Climate Filter Reminder Actions
|
||||
id: 6d7056d0-90ce-4c4f-b8b1-fd32a7e58311
|
||||
mode: queued
|
||||
@@ -384,48 +427,112 @@ automation:
|
||||
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 == 'downstairs_snooze' }}"
|
||||
- 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: input_datetime.downstairs_filter_snooze_until
|
||||
entity_id: "{{ snooze_entity }}"
|
||||
data:
|
||||
datetime: "{{ snooze_until }}"
|
||||
- service: script.send_to_logbook
|
||||
data:
|
||||
topic: "MAINTENANCE"
|
||||
message: "Downstairs AC filter reminder snoozed until {{ snooze_until }}."
|
||||
- conditions: "{{ trigger.id == 'upstairs_snooze' }}"
|
||||
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.upstairs_filter_snooze_until
|
||||
entity_id: input_datetime.hvac_condenser_lines_cleaning_snooze_until
|
||||
data:
|
||||
datetime: "{{ snooze_until }}"
|
||||
- service: script.send_to_logbook
|
||||
data:
|
||||
topic: "MAINTENANCE"
|
||||
message: "Upstairs AC filter reminder snoozed until {{ snooze_until }}."
|
||||
- conditions: "{{ trigger.id == 'downstairs_reset' }}"
|
||||
message: "HVAC condenser lines cleanout reminder snoozed until {{ snooze_until }}."
|
||||
- conditions: "{{ trigger.id == 'reset' }}"
|
||||
sequence:
|
||||
- service: script.reset_downstairs_filter
|
||||
- service: script.reset_hvac_condenser_lines_cleaned
|
||||
- service: script.send_to_logbook
|
||||
data:
|
||||
topic: "MAINTENANCE"
|
||||
message: "Downstairs AC filter reset from notification action."
|
||||
- conditions: "{{ trigger.id == 'upstairs_reset' }}"
|
||||
sequence:
|
||||
- service: script.reset_upstairs_filter
|
||||
- service: script.send_to_logbook
|
||||
data:
|
||||
topic: "MAINTENANCE"
|
||||
message: "Upstairs AC filter reset from notification action."
|
||||
message: "HVAC condenser lines cleanout reset from notification action."
|
||||
|
||||
- alias: 'AC Status Announcement'
|
||||
id: 7812fdaf-a3f8-498b-8f07-28e977e528fe
|
||||
|
||||
+120
-10
@@ -3,9 +3,11 @@
|
||||
# For more info visit https://www.vcloudinfo.com/click-here
|
||||
# Original Repo : https://github.com/CCOSTAN/Home-AssistantConfig
|
||||
# -------------------------------------------------------------------
|
||||
# Holiday Package - Flag/holiday sensors and lighting triggers - Holiday routines, notifications, and lighting tweaks.
|
||||
# Centralizes the Holiday Package - Flag/holiday sensors and lighting triggers package configuration and helpers.
|
||||
# Holiday Package - Flag/holiday sensors and lighting mode
|
||||
# Related Issue: 1774
|
||||
# Centralizes holiday calendar data and the active exterior lighting mode.
|
||||
# -------------------------------------------------------------------
|
||||
# Notes: /local/json_data is served from config/www/json_data and drives lighting mode coverage.
|
||||
######################################################################
|
||||
# Video breakdown: https://www.vcloudinfo.com/2019/02/breaking-down-the-flag-sensor-in-home-assistant.html
|
||||
# Modified for my own fun stuff!
|
||||
@@ -26,7 +28,7 @@ homeassistant:
|
||||
# Sensor updates once every 4 hours (14400 seconds) & runs 6 times in 24 hours
|
||||
#
|
||||
# First it checks for holiday in static section, if that doesn't exist,
|
||||
# it checks in the dynamic section. If neither exists, the value will be empty
|
||||
# it checks in the dynamic section. If neither exists, the value will be empty.
|
||||
###############################################################################
|
||||
sensor:
|
||||
- platform: rest
|
||||
@@ -34,13 +36,21 @@ sensor:
|
||||
name: Holiday
|
||||
scan_interval: 14400
|
||||
value_template: >
|
||||
{% set today = now().month ~ '/' ~ now().day %}
|
||||
{% set holiday = value_json.MAJOR_US.static[today] if today in value_json.MAJOR_US.static else "" %}
|
||||
{% if holiday | trim == "" %}
|
||||
{% set today = now().month ~ '/' ~ now().day ~ '/' ~ now().year %}
|
||||
{% set holiday = value_json.MAJOR_US.dynamic[today] if today in value_json.MAJOR_US.dynamic else "" %}
|
||||
{% set holiday_data = value_json.MAJOR_US if value_json is defined and value_json.MAJOR_US is defined else {} %}
|
||||
{% set static_days = holiday_data.static if holiday_data.static is defined else {} %}
|
||||
{% set dynamic_days = holiday_data.dynamic if holiday_data.dynamic is defined else {} %}
|
||||
{% set today = now().month ~ '/' ~ now().day %}
|
||||
{% set today_full = now().strftime('%m/%d/%Y') %}
|
||||
{% set today_full_alt = now().month ~ '/' ~ now().day ~ '/' ~ now().year %}
|
||||
{% if today in static_days %}
|
||||
{{ static_days[today] }}
|
||||
{% elif today_full in dynamic_days %}
|
||||
{{ dynamic_days[today_full] }}
|
||||
{% elif today_full_alt in dynamic_days %}
|
||||
{{ dynamic_days[today_full_alt] }}
|
||||
{% endif %}
|
||||
{{ holiday }}
|
||||
json_attributes:
|
||||
- MAJOR_US
|
||||
|
||||
################################################################################
|
||||
# Sensor Uses Flag data generated by AI
|
||||
@@ -52,16 +62,19 @@ sensor:
|
||||
value_template: >-
|
||||
{% set now_string = now().month ~ '/' ~ now().day %}
|
||||
{% set now_full_string = now().strftime('%m/%d/%Y') %}
|
||||
{% set now_full_alt_string = now().month ~ '/' ~ now().day ~ '/' ~ now().year %}
|
||||
{% set flag_data = value_json.Flag_Days_US if value_json is defined and value_json.Flag_Days_US is defined else {} %}
|
||||
{% set static_days = flag_data.static if flag_data.static is defined else {} %}
|
||||
{% set dynamic_days = flag_data.dynamic if flag_data.dynamic is defined else {} %}
|
||||
{% if now_string in static_days %}
|
||||
True
|
||||
{% elif now_full_string in dynamic_days %}
|
||||
{% elif now_full_string in dynamic_days or now_full_alt_string in dynamic_days %}
|
||||
True
|
||||
{% else %}
|
||||
False
|
||||
{% endif %}
|
||||
json_attributes:
|
||||
- Flag_Days_US
|
||||
|
||||
################################################################################
|
||||
# Countdown Sensor using WolfRam Alpha Natural language queries
|
||||
@@ -129,3 +142,100 @@ sensor:
|
||||
value_template: "{{ (value|replace(' days', '')) | int }}"
|
||||
unit_of_measurement: Days
|
||||
scan_interval: 43200
|
||||
|
||||
template:
|
||||
- sensor:
|
||||
- name: Holiday Lighting Mode
|
||||
unique_id: holiday_lighting_mode
|
||||
icon: mdi:string-lights
|
||||
state: >-
|
||||
{%- set today = today_at('00:00') -%}
|
||||
{%- set mmdd = now().strftime('%m%d') | int -%}
|
||||
{%- set holiday_data = state_attr('sensor.holiday', 'MAJOR_US') or {} -%}
|
||||
{%- set flag_data = state_attr('sensor.flag', 'Flag_Days_US') or {} -%}
|
||||
{%- set holiday_dynamic = holiday_data.get('dynamic', {}) if holiday_data is mapping else {} -%}
|
||||
{%- set flag_dynamic = flag_data.get('dynamic', {}) if flag_data is mapping else {} -%}
|
||||
{%- set days_to = namespace(easter=9999, mothers=9999, fathers=9999, memorial=9999, labor=9999, thanksgiving=9999) -%}
|
||||
{%- set mode = namespace(value='standard') -%}
|
||||
{%- for date_text, name in holiday_dynamic.items() -%}
|
||||
{%- set event_date = strptime(date_text, '%m/%d/%Y') -%}
|
||||
{%- set days = ((as_timestamp(event_date) - as_timestamp(today)) / 86400) | int -%}
|
||||
{%- if days >= 0 and name == 'Easter Sunday' and days < days_to.easter -%}
|
||||
{%- set days_to.easter = days -%}
|
||||
{%- elif days >= 0 and name == 'Mothers Day' and days < days_to.mothers -%}
|
||||
{%- set days_to.mothers = days -%}
|
||||
{%- elif days >= 0 and name == 'Fathers Day' and days < days_to.fathers -%}
|
||||
{%- set days_to.fathers = days -%}
|
||||
{%- elif days >= 0 and name == 'Thanksgiving Day' and days < days_to.thanksgiving -%}
|
||||
{%- set days_to.thanksgiving = days -%}
|
||||
{%- endif -%}
|
||||
{%- endfor -%}
|
||||
{%- for date_text, name in flag_dynamic.items() -%}
|
||||
{%- set event_date = strptime(date_text, '%m/%d/%Y') -%}
|
||||
{%- set days = ((as_timestamp(event_date) - as_timestamp(today)) / 86400) | int -%}
|
||||
{%- if days >= 0 and name == 'Memorial Day' and days < days_to.memorial -%}
|
||||
{%- set days_to.memorial = days -%}
|
||||
{%- elif days >= 0 and name == 'Labor Day' and days < days_to.labor -%}
|
||||
{%- set days_to.labor = days -%}
|
||||
{%- endif -%}
|
||||
{%- endfor -%}
|
||||
{%- set christmas = strptime(now().year ~ '-12-25', '%Y-%m-%d') -%}
|
||||
{%- set christmas_days = ((as_timestamp(christmas) - as_timestamp(today)) / 86400) | int -%}
|
||||
{%- if is_state('sensor.flag', 'True') -%}
|
||||
{%- set mode.value = 'RWB' -%}
|
||||
{%- elif mmdd == 101 -%}
|
||||
{%- set mode.value = 'new_years_day' -%}
|
||||
{%- elif mmdd >= 210 and mmdd <= 214 -%}
|
||||
{%- set mode.value = 'valentine' -%}
|
||||
{%- elif mmdd == 305 -%}
|
||||
{%- set mode.value = 'mardi_gras' -%}
|
||||
{%- elif mmdd == 314 -%}
|
||||
{%- set mode.value = 'pi' -%}
|
||||
{%- elif mmdd >= 315 and mmdd <= 317 -%}
|
||||
{%- set mode.value = 'st_patty' -%}
|
||||
{%- elif days_to.easter < 4 -%}
|
||||
{%- set mode.value = 'easter' -%}
|
||||
{%- elif mmdd == 504 -%}
|
||||
{%- set mode.value = 'starwars' -%}
|
||||
{%- elif mmdd == 505 -%}
|
||||
{%- set mode.value = 'cinco_de_mayo' -%}
|
||||
{%- elif days_to.mothers < 4 -%}
|
||||
{%- set mode.value = 'mothers_day' -%}
|
||||
{%- elif days_to.fathers < 4 -%}
|
||||
{%- set mode.value = 'fathers_day' -%}
|
||||
{%- elif days_to.memorial < 3 -%}
|
||||
{%- set mode.value = 'RWB' -%}
|
||||
{%- elif mmdd == 704 -%}
|
||||
{%- set mode.value = 'RWB' -%}
|
||||
{%- elif days_to.labor < 3 -%}
|
||||
{%- set mode.value = 'RWB' -%}
|
||||
{%- elif mmdd >= 1001 and mmdd <= 1031 -%}
|
||||
{%- set mode.value = 'halloween' -%}
|
||||
{%- elif mmdd == 1111 -%}
|
||||
{%- set mode.value = 'veterans' -%}
|
||||
{%- elif days_to.thanksgiving < 4 -%}
|
||||
{%- set mode.value = 'thanksgiving' -%}
|
||||
{%- elif states('sensor.chanukkah_countdown') | int(9999) <= 1 -%}
|
||||
{%- set mode.value = 'hanukkah' -%}
|
||||
{%- elif christmas_days >= 0 and christmas_days <= 25 -%}
|
||||
{%- set mode.value = 'christmas' -%}
|
||||
{%- elif mmdd == 1231 -%}
|
||||
{%- set mode.value = 'new_years_day' -%}
|
||||
{%- endif -%}
|
||||
{{- mode.value -}}
|
||||
|
||||
- name: Holiday Lighting Scene
|
||||
unique_id: holiday_lighting_scene
|
||||
icon: mdi:palette
|
||||
state: >-
|
||||
{%- set mode = states('sensor.holiday_lighting_mode') | trim -%}
|
||||
{%- if mode in ['unknown', 'unavailable', 'none', ''] -%}
|
||||
scene.month_standard_colors
|
||||
{%- else -%}
|
||||
scene.month_{{ mode }}_colors
|
||||
{%- endif -%}
|
||||
attributes:
|
||||
mode: "{{ states('sensor.holiday_lighting_mode') }}"
|
||||
holiday: "{{ states('sensor.holiday') }}"
|
||||
flag_day: "{{ states('sensor.flag') }}"
|
||||
source: config/www/json_data holiday and flag calendars
|
||||
|
||||
+18
-18
@@ -37,27 +37,27 @@ Reusable lighting and ambiance presets. Automations and scripts call these scene
|
||||
| Red_living_Room | All fixtures red, mid/high brightness | Alert/entry automations (garage/doors) |
|
||||
| Living_Room_Daytime_Cool | 5500K cool white, full brightness | Living room default automation (day) |
|
||||
| Living_Room_Evening_Amber | 2700K warm/amber, softer brightness | Living room default automation (night) |
|
||||
| month_standard_colors | Baseline white/neutral monthly palette | `script.monthly_color_scene` after sunset |
|
||||
| month_RWB_colors | Red/white/blue set (patriotic/July 4th) | `script.monthly_color_scene` (flag/holiday) |
|
||||
| month_valentine_colors | Valentine pinks/reds | `script.monthly_color_scene` (Feb 10–14) |
|
||||
| month_mardi_gras_colors | Purple/green/gold Mardi Gras | `script.monthly_color_scene` (Mar 5) |
|
||||
| month_st_patty_colors | Green-centric St. Patrick's | `script.monthly_color_scene` (Mar 15–17) |
|
||||
| month_pi_colors | Pi Day playful hues | `script.monthly_color_scene` (Mar 14) |
|
||||
| month_easter_colors | Pastel Easter set | `script.monthly_color_scene` (Easter countdown) |
|
||||
| month_starwars_colors | Star Wars themed mix | `script.monthly_color_scene` (May 4) |
|
||||
| month_cinco_de_mayo_colors | Cinco de Mayo festive mix | `script.monthly_color_scene` (May 5) |
|
||||
| month_mothers_day_colors | Mother's Day palette | `script.monthly_color_scene` (countdown) |
|
||||
| month_fathers_day_colors | Father's Day palette | `script.monthly_color_scene` (countdown) |
|
||||
| month_halloween_colors | Halloween oranges/purples | `script.monthly_color_scene` (Oct 1–31) |
|
||||
| month_veterans_colors | Veterans Day palette | `script.monthly_color_scene` (Nov 11) |
|
||||
| month_thanksgiving_colors | Autumn harvest tones | `script.monthly_color_scene` (countdown) |
|
||||
| month_hanukkah_colors | Hanukkah blues/whites | `script.monthly_color_scene` (Hanukkah countdown) |
|
||||
| month_christmas_colors | Christmas reds/greens | `script.monthly_color_scene` (Christmas countdown) |
|
||||
| month_new_years_day_colors | New Year's bright/celebratory | `script.monthly_color_scene` (Jan 1 & Dec 31) |
|
||||
| month_standard_colors | Baseline white/neutral monthly palette | `sensor.holiday_lighting_mode` = `standard` |
|
||||
| month_RWB_colors | Red/white/blue set (patriotic/July 4th) | `sensor.holiday_lighting_mode` = `RWB` |
|
||||
| month_valentine_colors | Valentine pinks/reds | `sensor.holiday_lighting_mode` = `valentine` |
|
||||
| month_mardi_gras_colors | Purple/green/gold Mardi Gras | `sensor.holiday_lighting_mode` = `mardi_gras` |
|
||||
| month_st_patty_colors | Green-centric St. Patrick's | `sensor.holiday_lighting_mode` = `st_patty` |
|
||||
| month_pi_colors | Pi Day playful hues | `sensor.holiday_lighting_mode` = `pi` |
|
||||
| month_easter_colors | Pastel Easter set | `sensor.holiday_lighting_mode` = `easter` |
|
||||
| month_starwars_colors | Star Wars themed mix | `sensor.holiday_lighting_mode` = `starwars` |
|
||||
| month_cinco_de_mayo_colors | Cinco de Mayo festive mix | `sensor.holiday_lighting_mode` = `cinco_de_mayo` |
|
||||
| month_mothers_day_colors | Mother's Day palette | `sensor.holiday_lighting_mode` = `mothers_day` |
|
||||
| month_fathers_day_colors | Father's Day palette | `sensor.holiday_lighting_mode` = `fathers_day` |
|
||||
| month_halloween_colors | Halloween oranges/purples | `sensor.holiday_lighting_mode` = `halloween` |
|
||||
| month_veterans_colors | Veterans Day palette | `sensor.holiday_lighting_mode` = `veterans` |
|
||||
| month_thanksgiving_colors | Autumn harvest tones | `sensor.holiday_lighting_mode` = `thanksgiving` |
|
||||
| month_hanukkah_colors | Hanukkah blues/whites | `sensor.holiday_lighting_mode` = `hanukkah` |
|
||||
| month_christmas_colors | Christmas reds/greens | `sensor.holiday_lighting_mode` = `christmas` |
|
||||
| month_new_years_day_colors | New Year's bright/celebratory | `sensor.holiday_lighting_mode` = `new_years_day` |
|
||||
|
||||
### Tips
|
||||
- Adjust scenes once and let all dependent automations inherit the change.
|
||||
- Pair with `script/monthly_color_scene.yaml` for dynamic monthly palettes.
|
||||
- Pair with `script/monthly_color_scene.yaml`; the active mode and scene are exposed by `sensor.holiday_lighting_mode` and `sensor.holiday_lighting_scene`.
|
||||
|
||||
**All of my configuration files are tested against the most stable version of home-assistant.**
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ Reusable scripts that other automations call for notifications, lighting, safety
|
||||
| [send_to_logbook.yaml](send_to_logbook.yaml) | Generic `logbook.log` helper for Activity feed entries (Issue #1550). |
|
||||
| [joanna_dispatch.yaml](joanna_dispatch.yaml) | Shared AGENT engineer dispatch contract that routes HA-detected issues into Joanna/BearClaw remediation. |
|
||||
| [speech_engine.yaml](speech_engine.yaml) | TTS/announcement orchestration with templated speech; speech processing can bypass LLM rewriting for exact messages and also routes garage/office Echo announcements. |
|
||||
| [monthly_color_scene.yaml](monthly_color_scene.yaml) | Seasonal lighting scenes used across automations. |
|
||||
| [monthly_color_scene.yaml](monthly_color_scene.yaml) | Seasonal lighting dispatcher that follows `sensor.holiday_lighting_scene`. |
|
||||
| [interior_off.yaml](interior_off.yaml) | One-call "all interior lights off" helper. |
|
||||
|
||||
### Joanna + BearClaw AGENT engineer handoff
|
||||
|
||||
@@ -3,8 +3,9 @@
|
||||
# For more info visit https://www.vcloudinfo.com/click-here
|
||||
# Original Repo : https://github.com/CCOSTAN/Home-AssistantConfig
|
||||
# -------------------------------------------------------------------
|
||||
# Youtube Video description of how I use this script - Example action call was documented in the legacy header.
|
||||
# Defines the Youtube Video description of how I use this script sequence for reuse by automations and dashboards.
|
||||
# Monthly Color Scene - Exterior seasonal lighting scene dispatcher.
|
||||
# Related Issue: 1774
|
||||
# Turns on the scene exposed by sensor.holiday_lighting_scene after dark.
|
||||
# -------------------------------------------------------------------
|
||||
# Notes: https://www.vcloudinfo.com/2018/10/easy-smart-home-gadgets-i-use-for-my.html
|
||||
# Notes: https://www.vcloudinfo.com/2017/08/diy-outdoor-smart-home-led-strips.html
|
||||
@@ -13,6 +14,7 @@
|
||||
# Notes: - service: script.monthly_color_scene
|
||||
# Notes: scenes should be named month_[01-12]_colors (month_06_colors)
|
||||
# Notes: Color help - http://www.esbnyc.com/explore/tower-lights/calendar
|
||||
# Notes: Active mode is inspectable at sensor.holiday_lighting_mode.
|
||||
######################################################################
|
||||
monthly_color_scene:
|
||||
sequence:
|
||||
@@ -22,51 +24,6 @@ monthly_color_scene:
|
||||
|
||||
- service: scene.turn_on
|
||||
data:
|
||||
entity_id: >
|
||||
scene.month_
|
||||
{%- if states.sensor.flag.state == "True" -%}
|
||||
RWB
|
||||
{%- elif now().strftime("%m%d")|int == 101 -%}
|
||||
new_years_day
|
||||
{%- elif now().strftime("%m%d")|int >= 210
|
||||
and now().strftime("%m%d")|int <= 214-%}
|
||||
valentine
|
||||
{%- elif now().strftime("%m%d")|int == 305 -%}
|
||||
mardi_gras
|
||||
{%- elif now().strftime("%m%d")|int == 314 -%}
|
||||
pi
|
||||
{%- elif now().strftime("%m%d")|int >= 315
|
||||
and now().strftime("%m%d")|int <= 317-%}
|
||||
st_patty
|
||||
{%- elif states('sensor.easter_countdown') | int < 4 -%}
|
||||
easter
|
||||
{%- elif now().strftime("%m%d")|int == 504 -%}
|
||||
starwars
|
||||
{%- elif now().strftime("%m%d")|int == 505 -%}
|
||||
cinco_de_mayo
|
||||
{%- elif states('sensor.mothers_countdown') | int < 4 -%}
|
||||
mothers_day
|
||||
{%- elif states('sensor.fathers_countdown') | int < 4 -%}
|
||||
fathers_day
|
||||
{%- elif states('sensor.memorial_day_countdown') | int < 3 -%}
|
||||
RWB
|
||||
{%- elif now().strftime("%m%d")|int == 704 -%}
|
||||
RWB
|
||||
{%- elif states('sensor.labor_day_countdown') | int < 3 -%}
|
||||
RWB
|
||||
{%- elif now().strftime("%m%d")|int >= 1001
|
||||
and now().strftime("%m%d")|int <= 1031-%}
|
||||
halloween
|
||||
{%- elif now().strftime("%m%d")|int == 1111 -%}
|
||||
veterans
|
||||
{%- elif states('sensor.thanksgiving_day_countdown') | int < 4 -%}
|
||||
thanksgiving
|
||||
{%- elif states('sensor.chanukkah_countdown') | int <= 1 -%}
|
||||
hanukkah
|
||||
{%- elif states('sensor.christmas_countdown') | int <= 25 -%}
|
||||
christmas
|
||||
{%- elif now().strftime("%m%d")|int == 1231 -%}
|
||||
new_years_day
|
||||
{%- else -%}
|
||||
standard
|
||||
{%- endif -%}_colors
|
||||
entity_id: >-
|
||||
{%- set scene = states('sensor.holiday_lighting_scene') | trim -%}
|
||||
{{ scene if scene[:12] == 'scene.month_' else 'scene.month_standard_colors' }}
|
||||
|
||||
@@ -0,0 +1,161 @@
|
||||
param(
|
||||
[int]$Months = 24,
|
||||
[datetime]$StartDate = (Get-Date).Date
|
||||
)
|
||||
|
||||
Set-StrictMode -Version Latest
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
$repoRoot = Split-Path -Parent $PSScriptRoot
|
||||
$holidayPath = Join-Path $repoRoot 'config/www/json_data/holidays.json'
|
||||
$flagPath = Join-Path $repoRoot 'config/www/json_data/flag_days.json'
|
||||
$scenePath = Join-Path $repoRoot 'config/scene/monthly_colors.yaml'
|
||||
$endDate = $StartDate.Date.AddMonths($Months)
|
||||
$errors = @()
|
||||
|
||||
function Read-JsonFile {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$Path
|
||||
)
|
||||
|
||||
if (-not (Test-Path -LiteralPath $Path)) {
|
||||
throw "Missing JSON file: $Path"
|
||||
}
|
||||
|
||||
try {
|
||||
return Get-Content -Raw -LiteralPath $Path | ConvertFrom-Json -AsHashtable
|
||||
} catch {
|
||||
throw "Invalid JSON in $Path`: $($_.Exception.Message)"
|
||||
}
|
||||
}
|
||||
|
||||
function Parse-DateKey {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$DateText
|
||||
)
|
||||
|
||||
return [datetime]::Parse(
|
||||
$DateText,
|
||||
[Globalization.CultureInfo]::InvariantCulture
|
||||
).Date
|
||||
}
|
||||
|
||||
function Get-DynamicEvents {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[object]$DynamicMap,
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string[]]$Names
|
||||
)
|
||||
|
||||
$events = @()
|
||||
foreach ($dateText in $DynamicMap.Keys) {
|
||||
$eventDate = Parse-DateKey -DateText ([string]$dateText)
|
||||
$eventName = [string]$DynamicMap[$dateText]
|
||||
if ($eventDate -ge $StartDate.Date -and $eventDate -lt $endDate -and $Names -contains $eventName) {
|
||||
$events += [pscustomobject]@{
|
||||
Name = $eventName
|
||||
Date = $eventDate
|
||||
}
|
||||
}
|
||||
}
|
||||
return $events | Sort-Object Date, Name
|
||||
}
|
||||
|
||||
$holidayJson = Read-JsonFile -Path $holidayPath
|
||||
$flagJson = Read-JsonFile -Path $flagPath
|
||||
$holidayData = $holidayJson['MAJOR_US']
|
||||
$flagData = $flagJson['Flag_Days_US']
|
||||
$holidayStatic = $holidayData['static']
|
||||
$holidayDynamic = $holidayData['dynamic']
|
||||
$flagDynamic = $flagData['dynamic']
|
||||
|
||||
$sceneText = Get-Content -Raw -LiteralPath $scenePath
|
||||
$sceneNames = [System.Collections.Generic.HashSet[string]]::new([StringComparer]::Ordinal)
|
||||
foreach ($match in [regex]::Matches($sceneText, '(?m)^\s*-\s+name:\s+(month_[A-Za-z0-9_]+_colors)\s*$')) {
|
||||
[void]$sceneNames.Add($match.Groups[1].Value)
|
||||
}
|
||||
|
||||
$lightingModes = @(
|
||||
'standard',
|
||||
'RWB',
|
||||
'new_years_day',
|
||||
'valentine',
|
||||
'mardi_gras',
|
||||
'pi',
|
||||
'st_patty',
|
||||
'easter',
|
||||
'starwars',
|
||||
'cinco_de_mayo',
|
||||
'mothers_day',
|
||||
'fathers_day',
|
||||
'halloween',
|
||||
'veterans',
|
||||
'thanksgiving',
|
||||
'hanukkah',
|
||||
'christmas'
|
||||
)
|
||||
|
||||
foreach ($mode in $lightingModes) {
|
||||
$sceneName = "month_${mode}_colors"
|
||||
if (-not $sceneNames.Contains($sceneName)) {
|
||||
$errors += "Missing scene for lighting mode '$mode': expected $sceneName in $scenePath"
|
||||
}
|
||||
}
|
||||
|
||||
$staticRequirements = @(
|
||||
@{ Key = '1/1'; Name = 'New Years Day' },
|
||||
@{ Key = '2/14'; Name = 'Valentines Day' },
|
||||
@{ Key = '3/14'; Name = 'Pi Day' },
|
||||
@{ Key = '3/17'; Name = 'St. Patricks Day' },
|
||||
@{ Key = '5/4'; Name = 'Star Wars Day' },
|
||||
@{ Key = '5/5'; Name = 'Cinco de Mayo' },
|
||||
@{ Key = '7/4'; Name = 'Independence Day' },
|
||||
@{ Key = '10/31'; Name = 'Halloween' },
|
||||
@{ Key = '11/11'; Name = 'Veterans Day' },
|
||||
@{ Key = '12/25'; Name = 'Christmas Day' },
|
||||
@{ Key = '12/31'; Name = 'New Years Eve' }
|
||||
)
|
||||
|
||||
foreach ($requirement in $staticRequirements) {
|
||||
if (-not $holidayStatic.ContainsKey($requirement.Key)) {
|
||||
$errors += "Missing static holiday key $($requirement.Key) for $($requirement.Name)"
|
||||
}
|
||||
}
|
||||
|
||||
$dynamicRequirements = @(
|
||||
@{ Name = 'Easter Sunday'; Source = 'holidays'; Map = $holidayDynamic },
|
||||
@{ Name = 'Mothers Day'; Source = 'holidays'; Map = $holidayDynamic },
|
||||
@{ Name = 'Fathers Day'; Source = 'holidays'; Map = $holidayDynamic },
|
||||
@{ Name = 'Thanksgiving Day'; Source = 'holidays'; Map = $holidayDynamic },
|
||||
@{ Name = 'Memorial Day'; Source = 'flag_days'; Map = $flagDynamic },
|
||||
@{ Name = 'Labor Day'; Source = 'flag_days'; Map = $flagDynamic }
|
||||
)
|
||||
|
||||
foreach ($requirement in $dynamicRequirements) {
|
||||
$events = @(Get-DynamicEvents -DynamicMap $requirement.Map -Names @($requirement.Name))
|
||||
if ($events.Count -eq 0) {
|
||||
$errors += "No $($requirement.Source) calendar coverage for '$($requirement.Name)' between $($StartDate.ToString('yyyy-MM-dd')) and $($endDate.ToString('yyyy-MM-dd'))"
|
||||
}
|
||||
}
|
||||
|
||||
Write-Host "Holiday lighting coverage window: $($StartDate.ToString('yyyy-MM-dd')) to $($endDate.ToString('yyyy-MM-dd'))"
|
||||
Write-Host "JSON: OK - parsed holidays.json and flag_days.json"
|
||||
Write-Host "Scenes: $($sceneNames.Count) monthly scenes found"
|
||||
|
||||
if ($errors.Count -gt 0) {
|
||||
foreach ($err in $errors) {
|
||||
Write-Host "ERROR: $err"
|
||||
}
|
||||
exit 1
|
||||
}
|
||||
|
||||
foreach ($requirement in $dynamicRequirements) {
|
||||
$events = @(Get-DynamicEvents -DynamicMap $requirement.Map -Names @($requirement.Name))
|
||||
$dates = ($events | ForEach-Object { $_.Date.ToString('yyyy-MM-dd') }) -join ', '
|
||||
Write-Host "$($requirement.Name): $dates"
|
||||
}
|
||||
|
||||
Write-Host "Coverage OK: required lighting scenes and dynamic holiday dates are present for the requested window."
|
||||
Reference in New Issue
Block a user