From 4c99abe17114a6811cfd2e5707e4300aecf27cf4 Mon Sep 17 00:00:00 2001 From: Carlo Costanzo <2160436+CCOSTAN@users.noreply.github.com> Date: Wed, 15 Apr 2026 09:34:44 -0400 Subject: [PATCH] Add bedroom wake alarm helpers Normalize Alexa bedroom wake alarm helpers and recorder exclusions. --- config/packages/README.md | 3 +- config/packages/alexa_media_player.yaml | 132 ++++++++++++++++++++++++ config/recorder.yaml | 3 +- 3 files changed, 136 insertions(+), 2 deletions(-) diff --git a/config/packages/README.md b/config/packages/README.md index 68c88839..a8705a37 100755 --- a/config/packages/README.md +++ b/config/packages/README.md @@ -39,6 +39,7 @@ Live collection of plug-and-play Home Assistant packages. Each YAML file in this | Package | What it unlocks | Notable entities / services | | --- | --- | --- | | [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` | | [garadget.yaml](garadget.yaml) | MQTT-based control + status for both garage doors, feeding entry/exit lighting routines. | `cover.large_garage_door`, `cover.small_garage_door`, `sensor.garadget_reflection` | | [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` | @@ -54,7 +55,7 @@ Live collection of plug-and-play Home Assistant packages. Each YAML file in this | [mariadb.yaml](mariadb.yaml) | MariaDB recorder health and capacity SQL sensors. | `sensor.mariadb_status`, `sensor.database_size` | | [tugtainer_updates.yaml](tugtainer_updates.yaml) | Tugtainer container update notifications via webhook + persistent alerts, plus event-based Joanna dispatch when reports include `### Available:` (24h cooldown via `mode: single` + delay, no new helpers). | `persistent_notification.create`, `event: tugtainer_available_detected`, `script.joanna_dispatch`, `input_datetime.tugtainer_last_update` | | [bearclaw.yaml](bearclaw.yaml) | Joanna/BearClaw bridge automations that forward Telegram commands to codex_appliance, include LLM-first routing context for freeform text, relay replies back, ingest `/api/bearclaw/status` telemetry, and expose dispatch plus QMD/memory-index sensors for Infrastructure dashboards. | `rest_command.bearclaw_*`, `sensor.bearclaw_status_telemetry`, `sensor.joanna_*`, `binary_sensor.joanna_*`, `automation.bearclaw_*`, `script.send_to_logbook` | -| [telegram_bot.yaml](telegram_bot.yaml) | Telegram transport package for BearClaw and other ops flows; the shared `joanna_send_telegram` helper now lives under `config/script/`. | `telegram_bot.send_message`, `script.joanna_send_telegram` | +| [telegram_bot.yaml](telegram_bot.yaml) | Telegram transport package for BearClaw and other ops flows; the shared `joanna_send_telegram` helper now lives under `config/script/`. | `telegram_bot.send_message`, `script.joanna_send_telegram` | | [phynplus.yaml](phynplus.yaml) | Phyn shutoff automations with push + Activity feed + Repairs issues for leak events. | `valve.phyn_shutoff_valve`, `binary_sensor.phyn_leak_test_running`, `repairs.create` | | [water_delivery.yaml](water_delivery.yaml) | ReadyRefresh delivery date helper with night-before + garage door Alexa reminders, plus helper-change audit logging and Telegram confirmations. | `input_datetime.water_delivery_date`, `script.send_to_logbook`, `script.joanna_send_telegram`, `notify.alexa_media_garage` | | [vacation_mode.yaml](vacation_mode.yaml) | Auto-enable vacation mode after 24 hours away or no bed use, track sitter analytics/secure-house checks, and deliver Chromecast-first vacation briefings with a garage Alexa welcome. | `input_boolean.vacation_mode`, `input_boolean.house_sitter_present`, `sensor.vacation_house_sitter_*`, `group.garage_doors`, `lock.front_door`, `script.notify_engine`, `script.joanna_send_telegram` | diff --git a/config/packages/alexa_media_player.yaml b/config/packages/alexa_media_player.yaml index 8754d0de..bdef3659 100755 --- a/config/packages/alexa_media_player.yaml +++ b/config/packages/alexa_media_player.yaml @@ -6,6 +6,9 @@ # Alexa Media Player - Sensors and notifications via Alexa Media integration. # Track Alexa media state and expose routine/command triggers. # ------------------------------------------------------------------- +# Related Issue: 858 +# Notes: Wrapper wake-alarm sensors normalize Alexa Media `next_alarm` IDs. +# Notes: Current wake-alarm scope is limited to Carlo Bedroom + Stacey Bedroom. ###################################################################### template: @@ -14,3 +17,132 @@ template: unique_id: last_alexa state: > {{ states.media_player | selectattr('attributes.last_called','eq',True) | map(attribute='entity_id') | first | default('none') }} + - name: "Carlo Bedroom Wake Alarm" + unique_id: carlo_bedroom_wake_alarm + device_class: timestamp + icon: mdi:alarm + state: > + {% set invalid = ['unknown', 'unavailable', 'none', ''] %} + {% set primary = states('sensor.carlo_bedroom_next_alarm') %} + {% set secondary = states('sensor.carlo_bedroom_next_alarm_2') %} + {% set value = primary if primary not in invalid else secondary %} + {% set ts = as_timestamp(value, none) %} + {{ ts | timestamp_local if ts is not none else none }} + - name: "Stacey Bedroom Wake Alarm" + unique_id: stacey_bedroom_wake_alarm + device_class: timestamp + icon: mdi:alarm + state: > + {% set invalid = ['unknown', 'unavailable', 'none', ''] %} + {% set primary = states('sensor.stacey_bedroom_next_alarm') %} + {% set secondary = states('sensor.stacey_bedroom_next_alarm_2') %} + {% set value = primary if primary not in invalid else secondary %} + {% set ts = as_timestamp(value, none) %} + {{ ts | timestamp_local if ts is not none else none }} + - name: "Bedroom Next Wake Alarm" + unique_id: bedroom_next_wake_alarm + device_class: timestamp + icon: mdi:alarm + state: > + {% set now_ts = as_timestamp(now()) %} + {% set max_ts = now_ts + 172800 %} + {% set carlo_raw = states('sensor.carlo_bedroom_next_alarm') %} + {% set carlo_fallback = states('sensor.carlo_bedroom_next_alarm_2') %} + {% set stacey_raw = states('sensor.stacey_bedroom_next_alarm') %} + {% set stacey_fallback = states('sensor.stacey_bedroom_next_alarm_2') %} + {% set carlo_ts = as_timestamp(carlo_raw, none) if carlo_raw not in ['unknown', 'unavailable', 'none', ''] else as_timestamp(carlo_fallback, none) %} + {% set stacey_ts = as_timestamp(stacey_raw, none) if stacey_raw not in ['unknown', 'unavailable', 'none', ''] else as_timestamp(stacey_fallback, none) %} + {% set carlo_ok = carlo_ts is not none and carlo_ts >= (now_ts - 900) and carlo_ts <= max_ts %} + {% set stacey_ok = stacey_ts is not none and stacey_ts >= (now_ts - 900) and stacey_ts <= max_ts %} + {% if carlo_ok and stacey_ok %} + {{ [carlo_ts, stacey_ts] | min | timestamp_local }} + {% elif carlo_ok %} + {{ carlo_ts | timestamp_local }} + {% elif stacey_ok %} + {{ stacey_ts | timestamp_local }} + {% else %} + {{ none }} + {% endif %} + - name: "Bedroom Next Wake Alarm Source" + unique_id: bedroom_next_wake_alarm_source + icon: mdi:account-voice + state: > + {% set now_ts = as_timestamp(now()) %} + {% set max_ts = now_ts + 172800 %} + {% set carlo_raw = states('sensor.carlo_bedroom_next_alarm') %} + {% set carlo_fallback = states('sensor.carlo_bedroom_next_alarm_2') %} + {% set stacey_raw = states('sensor.stacey_bedroom_next_alarm') %} + {% set stacey_fallback = states('sensor.stacey_bedroom_next_alarm_2') %} + {% set carlo_ts = as_timestamp(carlo_raw, none) if carlo_raw not in ['unknown', 'unavailable', 'none', ''] else as_timestamp(carlo_fallback, none) %} + {% set stacey_ts = as_timestamp(stacey_raw, none) if stacey_raw not in ['unknown', 'unavailable', 'none', ''] else as_timestamp(stacey_fallback, none) %} + {% set carlo_ok = carlo_ts is not none and carlo_ts >= (now_ts - 900) and carlo_ts <= max_ts %} + {% set stacey_ok = stacey_ts is not none and stacey_ts >= (now_ts - 900) and stacey_ts <= max_ts %} + {% if carlo_ok and stacey_ok %} + {{ 'Carlo Bedroom' if carlo_ts <= stacey_ts else 'Stacey Bedroom' }} + {% elif carlo_ok %} + Carlo Bedroom + {% elif stacey_ok %} + Stacey Bedroom + {% else %} + none + {% endif %} + - name: "Bedroom Next Wake Alarm Minutes" + unique_id: bedroom_next_wake_alarm_minutes + unit_of_measurement: min + state_class: measurement + icon: mdi:timer-outline + state: > + {% set now_ts = as_timestamp(now()) %} + {% set max_ts = now_ts + 172800 %} + {% set carlo_raw = states('sensor.carlo_bedroom_next_alarm') %} + {% set carlo_fallback = states('sensor.carlo_bedroom_next_alarm_2') %} + {% set stacey_raw = states('sensor.stacey_bedroom_next_alarm') %} + {% set stacey_fallback = states('sensor.stacey_bedroom_next_alarm_2') %} + {% set carlo_ts = as_timestamp(carlo_raw, none) if carlo_raw not in ['unknown', 'unavailable', 'none', ''] else as_timestamp(carlo_fallback, none) %} + {% set stacey_ts = as_timestamp(stacey_raw, none) if stacey_raw not in ['unknown', 'unavailable', 'none', ''] else as_timestamp(stacey_fallback, none) %} + {% set carlo_ok = carlo_ts is not none and carlo_ts >= (now_ts - 900) and carlo_ts <= max_ts %} + {% set stacey_ok = stacey_ts is not none and stacey_ts >= (now_ts - 900) and stacey_ts <= max_ts %} + {% if carlo_ok and stacey_ok %} + {{ ((([carlo_ts, stacey_ts] | min) - now_ts) / 60) | round(0) }} + {% elif carlo_ok %} + {{ ((carlo_ts - now_ts) / 60) | round(0) }} + {% elif stacey_ok %} + {{ ((stacey_ts - now_ts) / 60) | round(0) }} + {% else %} + {{ none }} + {% endif %} + - binary_sensor: + - name: "Carlo Bedroom Wake Alarm Armed" + unique_id: carlo_bedroom_wake_alarm_armed + icon: mdi:alarm-check + state: > + {% set now_ts = as_timestamp(now()) %} + {% set max_ts = now_ts + 172800 %} + {% set primary = states('sensor.carlo_bedroom_next_alarm') %} + {% set secondary = states('sensor.carlo_bedroom_next_alarm_2') %} + {% set value = primary if primary not in ['unknown', 'unavailable', 'none', ''] else secondary %} + {% set ts = as_timestamp(value, none) %} + {{ ts is not none and ts >= now_ts and ts <= max_ts }} + - name: "Stacey Bedroom Wake Alarm Armed" + unique_id: stacey_bedroom_wake_alarm_armed + icon: mdi:alarm-check + state: > + {% set now_ts = as_timestamp(now()) %} + {% set max_ts = now_ts + 172800 %} + {% set primary = states('sensor.stacey_bedroom_next_alarm') %} + {% set secondary = states('sensor.stacey_bedroom_next_alarm_2') %} + {% set value = primary if primary not in ['unknown', 'unavailable', 'none', ''] else secondary %} + {% set ts = as_timestamp(value, none) %} + {{ ts is not none and ts >= now_ts and ts <= max_ts }} + - name: "Bedroom Next Wake Alarm Armed" + unique_id: bedroom_next_wake_alarm_armed + icon: mdi:alarm-check + state: > + {{ states('sensor.bedroom_next_wake_alarm') not in ['unknown', 'unavailable', 'none', ''] }} + - name: "Bedroom Next Wake Alarm Active" + unique_id: bedroom_next_wake_alarm_active + icon: mdi:alarm-light + state: > + {% set next_alarm = as_timestamp(states('sensor.bedroom_next_wake_alarm'), none) %} + {% set now_ts = as_timestamp(now()) %} + {{ next_alarm is not none and now_ts >= next_alarm and now_ts <= (next_alarm + 900) }} diff --git a/config/recorder.yaml b/config/recorder.yaml index f258e4c9..5e9883c0 100755 --- a/config/recorder.yaml +++ b/config/recorder.yaml @@ -6,7 +6,7 @@ # Recorder Configuration - database retention and exclusions # Stores HA history while purging noise and controlling DB size. # ------------------------------------------------------------------- -# Notes: Keeps 180 days (1/2 year); excludes vcloudinfo pings, noisy connectivity telemetry, and other high-churn entities; MariaDB via recorder_db_url. +# Notes: Keeps 180 days (1/2 year); excludes vcloudinfo pings, noisy connectivity telemetry, countdown-style alarm helpers, and other high-churn entities; MariaDB via recorder_db_url. ###################################################################### db_url: !secret recorder_db_url purge_keep_days: 180 @@ -56,6 +56,7 @@ exclude: - sensor.*_wi_fi_signal - sensor.*_wifi_signal - sensor.*_wifi_signal_strength + - sensor.*_wake_alarm_minutes - sensor.*_temperature_state - sensor.*_humidity_state - sensor.*_last_seen*