[ Home Automation — Snippets ]

Smaller standalone automations and templates that don't warrant
their own page — one-liners, helper sensors, and YAML patterns
extracted from larger setups. Each snippet is shown inline below; copy
what you need into your own configs.

These pair naturally with the longer guides on the
parent page
(alarm system,
ESP32 Bluetooth proxy,
Volcano + HA) and the external
references in
Links & Resources.

[ Low-battery aggregator (any device under N%) ]

Low-battery aggregator (any device under N%) — 2026
A template binary_sensor that fires on when any sensor with
device_class: battery drops below your threshold — without
having to enumerate every device individually. Drop new battery-powered
devices into HA and they get monitored automatically.

Pattern: iterate states.sensor with a Jinja namespace to
short-circuit on the first match. Cheaper than building a list and
checking length when you have many sensors. The devices
attribute gives you a friendly name list for the notification body.
# configuration.yaml — drop in alongside your existing template: blocks

template:
  - binary_sensor:
      - name: "Any low battery"
        unique_id: any_low_battery
        state: >-
          {% set threshold = 20 %}
          {% set ns = namespace(found=false) %}
          {% for s in states.sensor
              if s.attributes.device_class == 'battery'
              and s.state not in ['unknown', 'unavailable'] %}
            {% if s.state | int(100) < threshold %}
              {% set ns.found = true %}
            {% endif %}
          {% endfor %}
          {{ ns.found }}
        attributes:
          # Friendly list for the notification body
          devices: >-
            {% set threshold = 20 %}
            {% set out = namespace(names=[]) %}
            {% for s in states.sensor
                if s.attributes.device_class == 'battery'
                and s.state not in ['unknown', 'unavailable']
                and s.state | int(100) < threshold %}
              {% set out.names = out.names + [s.name + ' (' + s.state + '%)'] %}
            {% endfor %}
            {{ out.names | join(', ') }}

# automations.yaml — notify once per device when threshold tripped
- alias: "Low battery: notify"
  trigger:
    - platform: state
      entity_id: binary_sensor.any_low_battery
      to: "on"
  action:
    - action: notify.mobile_app_magikh0e_phone
      data:
        title: "🔋 Low battery"
        message: "{{ state_attr('binary_sensor.any_low_battery', 'devices') }}"

[ Smart-charging cutoff (stop at 80%) ]

Smart-charging cutoff (stop at 80%) — 2026
Battery longevity hack — lithium cells age fastest when held
between 80–100% charge, so cutting the charger at 80% extends
the cell's useful life by 2–3×. Pairs the HA companion
app's battery_level + battery_state sensors with a smart
plug or zigbee outlet that the charger is plugged into.

Pattern: numeric-state trigger + state condition gates the
action. The condition is what makes this safe to leave armed —
without it, the automation would fire every time the phone passed 80%
even if it wasn't currently charging (e.g., during normal discharge).
# automations.yaml

- alias: "Phone: stop charging at 80%"
  description: "Cut the charger plug when phone hits 80% to preserve the cell"
  mode: single
  trigger:
    - platform: numeric_state
      entity_id: sensor.magikh0e_phone_battery_level
      above: 79
  condition:
    # Only act if actually charging — prevents firing on normal discharge
    - condition: state
      entity_id: sensor.magikh0e_phone_battery_state
      state: "charging"
  action:
    - action: switch.turn_off
      target:
        entity_id: switch.bedroom_charger_outlet
    - action: notify.mobile_app_magikh0e_phone
      data:
        title: "🔋 80% — charger off"
        message: "Battery longevity preserved. Plug in again to override."

[ Door "left open" alert + closed announcement ]

Door "left open" alert + closed announcement — 2026
Two paired automations for "the front door has been open for a
while" situations.

  - Open alert — after the door's been open 30 seconds,
    fires a phone push + Nest TTS announcement, then repeats every
    ~10 minutes while the door stays open.
  - Closed announcement — plays a 'door closed' TTS when
    the door is finally shut, but only if it had been open more than
    30 seconds. Suppresses the closed-TTS for quick in/out trips so
    the house doesn't narrate every time you grab the mail.

Pattern: state + for: as the trigger (30-second debounce),
then a while: loop that re-checks the contact sensor every
~10 min and only continues while it's still open. The companion
closed-announcement uses a template condition on
trigger.from_state.last_changed to gate on how long the door
was open before closing.

Retarget at any contact-sensor + notify-target + speaker by swapping
three entity_ids (front_door_door, mobile_app_magikh0e_phone,
nest_display).
# automations.yaml

# 1. Alert when door is left open
- alias: "Front Door Left Open Alert"
  description: "Notifies + announces when front door has been open for 30s, repeating every ~10 min"
  trigger:
    - platform: state
      entity_id: binary_sensor.front_door_door
      to: "on"
      for: "00:00:30"
  action:
    # Loop while the door remains open
    - repeat:
        while:
          - condition: state
            entity_id: binary_sensor.front_door_door
            state: "on"
        sequence:
          - action: notify.mobile_app_magikh0e_phone
            data:
              title: "Front Door"
              message: "The front door has been left open"
          - action: media_player.play_media
            target:
              entity_id: media_player.nest_display
            data:
              media_content_id: >-
                media-source://tts/tts.piper?message=The+front+door+has+been+left+open&language=en_US
              media_content_type: audio/mp3
          # Wait before the next reminder (only fires if door is still open)
          - delay: "00:10:45"


# 2. Announce when the door is closed (only if it had been open a while)
- alias: "Front Door Closed Announcement"
  description: "Plays a 'door closed' TTS when the front door is closed after being open more than 30s"
  trigger:
    - platform: state
      entity_id: binary_sensor.front_door_door
      from: "on"
      to: "off"
  condition:
    # Only fire if the door had been open at least 30 seconds — skips quick in/out
    - condition: template
      value_template: >-
        {{ (now() - trigger.from_state.last_changed).total_seconds() > 30 }}
  action:
    - action: media_player.play_media
      target:
        entity_id: media_player.nest_display
      data:
        media_content_id: >-
          media-source://tts/tts.piper?message=The+front+door+has+been+closed&language=en_US
        media_content_type: audio/mp3

[ Door auto-lock with open-door safety ]

Door auto-lock with open-door safety — 2026
Re-lock the front door if it's been left unlocked for 5 minutes,
unless the door itself is currently open (locking with the door open
jams most smart-lock bolts against the strike plate). Pairs a smart
lock entity with the contact sensor on the same door — both
common Z-Wave / Zigbee setups.

Pattern: state + for: duration as the trigger
debounces brief unlocks. condition on the contact sensor adds
the safety interlock — a deceptively important detail that
prevents your auto-lock from grinding the bolt against the frame
every time you walk groceries inside.
# automations.yaml

- alias: "Front Door: auto-lock after 5 min"
  description: "Re-lock if left unlocked for 5 min, only if door is closed"
  mode: single
  trigger:
    - platform: state
      entity_id: lock.front_door
      to: "unlocked"
      for: "00:05:00"
  condition:
    # Don't lock against an open door — bolt vs strike plate is bad news
    - condition: state
      entity_id: binary_sensor.front_door_door
      state: "off"
  action:
    - action: lock.lock
      target:
        entity_id: lock.front_door
    - action: notify.mobile_app_magikh0e_phone
      data:
        title: "🔒 Auto-locked"
        message: "Front door re-locked after 5 min unlocked."

[ Outdoor lights — sunset fade-in / overnight fade-out ]

Outdoor lights — sunset fade-in / overnight fade-out — 2026
Also available as a one-instantiate-per-light HA blueprint
(writeup) — the same three-stage cycle wrapped as a single automation with
configurable color, brightness, durations, sunset offset, and a sun-elevation
gate. Use the blueprint if you want a UI install + per-light instances; use the
snippet below if you'd rather hand-roll three plain automations.

For a longer write-up with a step-fade alternative (three sunset-
anchored stages instead of one long ramp), a color reference, and a
troubleshooting table, see the full
Outdoor lights guide.
This snippet is the simple-fade variant only.

Three coordinated automations driving an outdoor RGB light through a
gentle daily cycle: lights grow in over 30 minutes starting at sunset,
fade back down over 30 minutes starting at 23:30, and switch off at
midnight.  The colour stays at a warm 2200K-ish amber the whole time
(RGB 255,147,41 — close to a candle / old-school sodium
streetlight, easy on the eyes after dark).

Pattern — the two-step fade-on.  Home Assistant
can't transition: from the off state directly; light.turn_on
... transition: 1800 against a bulb that's currently off just
snaps to the target brightness instantly.  Workaround: issue two
back-to-back calls.  The first turns the bulb on at 1% with the
desired colour and NO transition (establishes the starting state).
After a 5-second settle, the second call requests the target
brightness WITH the long transition, and the bulb actually animates
from 1% → 50% over the full 30 minutes.

The fade-out side doesn't need the trick because the light is already
on -- a single light.turn_on ... transition: 1800 works
from any non-off state.  The midnight automation then does the actual
light.turn_off after the fade has run its course.

To adapt: change light.lanai_light to your entity_id,
swap the rgb_color if you want a different tint (cooler
white = 255,180,107; pure-amber = 255,140,30), and shift the
'23:30:00' / '00:00:00' times to taste.
# automations.yaml

- id: 'lanai_lights_sunset_on'
  alias: 'Lanai: Evening Lights Fade In at Sunset'
  description: 'Gradually fades lanai lights on at sunset over 30 minutes'
  triggers:
    - trigger: sun
      event: sunset
  conditions: []
  actions:
    # Step 1 -- establish starting state at 1% with the target colour.
    # No `transition:` here on purpose; HA can't transition from off.
    - action: light.turn_on
      target:
        entity_id: light.lanai_light
      data:
        brightness_pct: 1
        rgb_color: [255, 147, 41]
    - delay:
        seconds: 5
    # Step 2 -- now that the bulb is on, request the real fade.
    - action: light.turn_on
      target:
        entity_id: light.lanai_light
      data:
        brightness_pct: 50
        rgb_color: [255, 147, 41]
        transition: 1800           # 30 minutes
  mode: single

- id: 'lanai_lights_fade_out'
  alias: 'Lanai: Evening Lights Fade Out at 11:30pm'
  description: 'Gradually fades lanai lights out from 11:30pm over 30 minutes'
  triggers:
    - trigger: time
      at: '23:30:00'
  conditions:
    # Don't try to fade out a light that's already off (e.g. someone
    # turned it off manually earlier).
    - condition: state
      entity_id: light.lanai_light
      state: 'on'
  actions:
    - action: light.turn_on
      target:
        entity_id: light.lanai_light
      data:
        brightness_pct: 1
        transition: 1800           # 30 minutes
  mode: single

- id: 'lanai_lights_midnight_off'
  alias: 'Lanai: Evening Lights Off at Midnight'
  description: 'Turns off lanai lights at midnight after fade out'
  triggers:
    - trigger: time
      at: '00:00:00'
  conditions: []
  actions:
    - action: light.turn_off
      target:
        entity_id: light.lanai_light
  mode: single

[ See Also ]