Skip to content

Latest commit

Β 

History

97 Commits

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

πŸ”΄ Node-RED β€” Smart Home Flows

Node-RED Home Assistant Flows License Repo

The automation brain behind a family smart home in Ireland.

Buy Me a Coffee


The Node-RED flows + config behind the automations in colfin22/ha-config. Home Assistant handles the simple, single-trigger stuff; the multi-input, stateful logic lives here β€” deciding the house's mode, arming the alarm, making heating calls from presence + forecast + tariff, and turning camera detections into one smart notification. Runs in Docker alongside Home Assistant; this repo adds flow versioning and quick recovery.

Eight flows: House Mode Β· House & Shed Alarm Β· Alarm NFC Tags Β· Camera Concierge Β· Heating Control Β· Infra Watchdog Β· Infra Health & Alerts Β· Person Config (dashboard, no screenshot yet).

Start here if you only want one flow

Most visitors want a single flow, not this whole setup. Nothing below assumes you run anything the way I do.

  1. Grab the tab you want from the flows/ directory. Each file is an importable JSON for one tab, regenerated automatically on every nightly backup.
  2. Import it into your own Node-RED via Menu β†’ Import. Each file already includes the config nodes its flow references (a Home Assistant server node, and the MQTT broker for the camera flow). They are exported without secrets.
  3. Point the config nodes at your own setup. Double-click any Home Assistant node, edit the server, and fill in your Base URL (with the scheme and port, e.g. http://192.168.1.10:8123) and a Long-Lived Access Token. Same for the MQTT broker on the camera flow.
  4. Set the environment variables the flow needs (next section).
  5. Replace the entity ids that are still mine (the table after that).

Configuration lives in environment variables

Anything specific to a household β€” who lives there, which phones to notify, which server to poll β€” is read from environment variables rather than written into the flows. Copy .env.example, which documents every variable, and fill in your own values.

Setting them when you imported a single tab: there is no docker-compose.yml in play, so .env does not apply. Set them in the editor instead: double-click the flow tab's name, then the Environment Variables tab. That needs Node-RED 2.1 or newer.

People are numbered slots, and the same slot is used by every flow:

Only two of these are still read from the environment β€” everything else about a person now lives on the Household Config dashboard instead:

Variable Example Meaning
PERSON_1_NAME Alice Still read directly. Display name, used in spoken messages and NFC/heating attribution
PERSON_1_HA_USER_ID da119… Still read directly. Attributes "who changed the heating" and who scanned an NFC tag

PERSON_n_ENTITY / PERSON_n_PING / PERSON_n_NOTIFY / PERSON_n_ROLE / PERSON_n_ONLY_WHEN_HOME / PERSON_n_BATTERY are no longer read anywhere β€” a fresh clone of this repo with no person.* entities in its own HA and no dashboard toggles set will simply show nobody as a resident, which degrades loudly (nothing arms/sleeps) rather than silently. Set people up from the dashboard once Node-RED is running against your own HA instead.

Add people by filling PERSON_2_NAME, PERSON_3_NAME and so on β€” up to eight slots are read, and empty slots are skipped.

PERSON_n_ROLE, PERSON_n_NOTIFY and PERSON_n_ONLY_WHEN_HOME are superseded by the Household Config dashboard page (below) for House Mode role and every notification channel it covers β€” those three variables are no longer read anywhere. PERSON_n_ENTITY, PERSON_n_PING, PERSON_n_NAME, PERSON_n_BATTERY and PERSON_n_HA_USER_ID are still read directly from the environment (NFC scan-attribution naming, the alarm's spoken possessive names, and the battery watchdog don't go through the dashboard).

Household Config dashboard β€” who gets what, without editing a flow

A Node-RED Dashboard 2.0 page (@flowfuse/node-red-dashboard, install with npm install @flowfuse/node-red-dashboard inside the container) at /dashboard/household lists every HA person.* entity β€” fetched live, so a person added in HA just appears on next refresh with safe defaults (House Mode role none, every toggle off) rather than needing a flow edit. Per person:

  • House Mode role β€” none / guest / resident (feeds the House Mode tab's residents/guests list directly). Presence for whoever has a role is watched via a whole-house state_changed event filter, not a fixed entity list β€” so promoting a person on the page gets them real-time Home/Away/Sleeping reactions immediately, no flow edit.
  • Security β€” alarm arm/disarm/trigger pushes + the door-gated welcome naming.
  • Only when home β€” only push Security notifications to this person while they're physically present (the old PERSON_n_ONLY_WHEN_HOME, now per-person and independent of role).
  • Camera / Doorbell β€” Camera Concierge pushes.
  • House-mode-change β€” the mode-change push itself.
  • Heating changes / Freeze warning β€” the two heating notification types.
  • Infra alerts β€” Infra Watchdog + Infra Health & Alerts phone pushes, and (dynamically, for whoever currently has it on and is home) the Infra Watchdog TTS announcement.
  • Battery sensor β€” free-text entity id (e.g. sensor.xxx_battery_level); low-battery monitoring only runs for Residents with this set (the old PERSON_n_BATTERY, now per-person and dashboard-editable). Notified to whoever currently has Security on.

A toggle with no resolvable notify target (no linked device_tracker/notify entity) renders disabled with a reason, rather than silently doing nothing. Backing store is data/person_config.json (tracked in this repo, entity ids + booleans only, no secrets), loaded into global context on deploy/tick so every consumer flow reads it live β€” no redeploy needed after a toggle change. Every consumer that reads it (House Mode, House Alarm, Camera Concierge, Heating Control, both infra flows, battery monitoring) builds its notify/watch list dynamically from whoever currently has that channel/role on, so it scales to any number of people without a flow edit. PERSON_n_NAME and PERSON_n_HA_USER_ID are still read directly from the environment β€” they're only used for spoken possessive naming and "who did it" attribution (NFC/heating), not migrated to the dashboard.

What to search and replace

Every id below is mine and will not exist on your system. Nothing errors when a name does not match, the branch just silently never fires, so work through the list rather than waiting for an error to tell you.

What Mine Where
Camera names Doorbell, Front_Car, Front_Van, Rear_Door, Rear_Shed Camera Concierge. Must match your Frigate config exactly, capitals and all
House mode input_select.house_mode Every flow. This is the orchestrator, it drives most of the logic
Maintenance switch input_boolean.maintenance_mode Both infra flows β€” suppresses alerts during planned work
Patio door switch input_boolean.patio_door_open Camera Concierge (silences the rear cameras)
Front door contact binary_sensor.front_door_contact Camera Concierge (silences the doorbell popup and the doorbell pushes on the way out)
Speakers and displays media_player.kitchen_display_cast, media_player.sitting_room_speaker_cast, notify.nvidia_shield Camera Concierge TTS, alarm voice
Bedroom speaker media_player.*_bedroom_speaker_cast Person-at-the-car deterrent
Lights light.hallway, light.sitting_room, and the per-room lights in the House Mode activity watcher Camera Concierge deterrent, alarm strobe, House Mode activity
Motion and presence binary_sensor.hallway_motion_sensor_occupancy and the other three indoor sensors House Mode β€” these are what "activity" means
Courier counters sensor.front_car_<courier>_count, sensor.front_van_<courier>_count Camera Concierge courier TTS
TVs binary_sensor.lg_tv_online, binary_sensor.samsung_tv_online House Mode quiet detection
Weather warnings sensor.met_eireann_warning_level House Mode storm overlay (Irish-specific)
Infrastructure sensors the sensor.proxmox_*, sensor.proxmox2_* and sensor.truenas_* families Infra Health & Alerts rule engine

House Mode β€” how it works

The House Mode flow in the Node-RED editor

The House Mode tab (tab-hm) is the household state orchestrator. It computes and maintains input_select.house_mode β€” the single source of truth that other flows (alarm, heating and infrastructure alerts) consume so each doesn't have to do its own presence tracking.

Three states + overlays

  • Home β€” at least one resident is in (or just arrived).
  • Away β€” all residents out, confirmed by a 20-minute quiet period (see below).
  • Sleeping β€” everyone in bed; auto-detected overnight.
  • Overlays (independent of the above): storm_mode (auto from Met Γ‰ireann warnings), maintenance_mode (blocks Away, silences all infra alerting while planned maintenance is under way, auto-expires after 4 hours). guest_mode exists as a helper but is intentionally ignored by the engine β€” it is consumed directly by other automations.

Away detection

All residents out β†’ 20-minute timer β†’ Away. At the fire point the engine checks the indoor sensors (sitting-room mmWave, office, landing, hallway) for motion in the last 15 minutes β€” if anyone is still moving around, it holds off and retries every 20 minutes. maintenance_mode also blocks the transition. Anyone arriving during the countdown cancels it immediately.

Dead/low-phone safeguards live here: if a resident's phone is off Wi-Fi and its battery sensor is unavailable (genuinely dead/off), the engine treats that person as unconfirmed-away and won't set Away β€” it pushes a notification instead. A low-but-alive phone still reports location and is trusted. A nudge fires when a phone drops below 15 % so presence keeps working. These alerts go to the residents only.

Sleeping detection (auto, 22:00–07:00)

Requires: mode is Home, a resident is home, maintenance_mode off, and no activity for 20 minutes β€” where activity means any of the 25 tracked lights, either TV, any dimmer press, or any indoor sensor. A 2-hour re-entry block prevents flipping back to Sleeping straight after waking. Nothing wakes before 06:00; first activity from 06:00 returns to Home.

People config (hm-cfg node)

Residents (PERSON_n_ROLE=resident) enable Sleeping and trigger Away. Guests (PERSON_n_ROLE=guest) only contribute to anyoneHome() β€” they prevent Away when physically present but don't enable Sleeping. Anyone in the house with no tracked phone is excluded from both, and the indoor sensors are their safety net.

Implementation notes

  • All Away evaluations use a fresh live HA snapshot via ha-get-entities β€” no stale flow context.
  • server-state-changed nodes always use outputInitially: false; startup state is seeded via an inject β†’ ha-get-entities β†’ function chain (global context is not populated reliably at 1 s after start).
  • On every state change: sets input_select.house_mode via hm-set-mode, logs to /data/house_mode.log with a local timestamp + reason, pushes a notification, and publishes the same reason to input_text.house_mode_reason (hm-reason).
  • Why the reason is published, not just notified: the engine always knew why it changed mode β€” morning activity (light.sitting_room), resident returned, all residents left (20m), quiet 20m, no activity β€” but that string only ever reached a push notification. Other flows (the heating controller, below) can now name the actual cause instead of guessing at it. A manual mode change publishes changed by hand, so a consumer can't attribute it to whatever the engine last decided.
  • Self-heals a corrupted input_select.house_mode entity (23-09-2026). An HA Core restart can fail to restore this helper's real prior state and fall back to its configured default instead β€” this happened at 02:42 one night and left the entity reading Home for hours while the house was actually still asleep, which fed a wrong "morning boost" into the heating controller and a stale reading into Camera Concierge. The engine's own in-memory flow.mode survives an HA-only restart (Node-RED itself didn't restart), so on every 60 s tick it now compares the live entity against its own remembered mode and writes the entity back if they've drifted, logging [house-mode] RECONCILE: entity=X internal=Y. A non-tick state seed also now prefers its own remembered mode over the raw entity, so a single bad snapshot can't poison it going forward either.

House & Shed Alarm β€” how it works

The House Alarm flow in the Node-RED editor

Two tabs automate the Alarmo alarm β€” alarm_control_panel.house and .shed (two independent panels); none needs a code. The house alarm is driven by House Mode state; the shed is NFC-driven. All notifications and voice announcements are state-driven, so they fire however the alarm changes β€” phone, Alarmo card, NFC, or automatic.

Node-RED signs in to Home Assistant as its own dedicated "Node-RED" user, so Alarmo's activity log attributes automatic arm/disarm to Node-RED instead of a person. Manual arm/disarm still shows whoever did it.

House β€” auto arm/disarm via House Mode (tab House Alarm)

The alarm follows input_select.house_mode directly:

  • Away β†’ alarm_arm_away (house)
  • Sleeping β†’ alarm_arm_night (house) β€” silent, no announcement; people are in bed
  • Home β†’ alarm_disarm (house). When returning from Away the spoken welcome is not fired on arrival β€” it is held until the front door opens (then a short delay), so you are inside to hear it, then a single merged line names whoever is back: "Welcome home, [name]. The house has been disarmed." on the home audio group. It stays silent if the door never opens within a few minutes, and says nothing when Home comes from Sleeping. (Someone whose phone never leaves the house can't flip Awayβ†’Home, so is never named.)

All presence tracking, timing, dead-phone safeguards, and battery nudges live in the House Mode tab β€” the alarm tab just reacts to the resulting state. On Node-RED restart, a startup inject reads the current house_mode and guest_mode via ha-get-entities and syncs both into flow context before the first evaluation.

Two robustness details: a 60-second reconcile compares the live house_mode against the last mode the flow processed and re-applies any change missed during a websocket drop (a manual disarm is respected β€” it only reacts to missed mode changes, never to alarm state). And every arm/disarm call is idempotent β€” if the panel is already in the target state the call is skipped, so nothing spams the Alarmo log with "cannot go to state X from X".

Guest mode

When input_boolean.guest_mode is on, only Away arming is suspended (guests moving around would trip an away-armed alarm):

  • House mode going Away β†’ alarm stays as-is (arm away skipped silently)
  • House mode going Sleeping β†’ still arms night β€” guest mode does NOT block night arming (changed 02-07-2026), so the perimeter stays armed overnight with guests in
  • Home always disarms regardless of guest mode

When guest mode is turned off, the flow immediately re-evaluates the current house mode and arms accordingly β€” if the house is already Away it arms away, if Sleeping it arms night, if Home it does nothing.

Alarm notifications + voice (same tab)

Derived from the state of .house + .shed (Alarmo's own events are internal, not on the HA bus). Five events β†’ push to all people + a spoken announcement on the home audio group: armed Β· disarmed Β· triggered Β· no-longer-triggered Β· failed-to-arm. Trigger/failure messages carry the cause (open sensors); a failure is recognised both as an instant refusal and as an exit-delay arm that aborts back to disarmed with sensors open (a cancelled exit delay with nothing open stays a plain disarm). A visitor is only pushed while at home. On a return from Away the spoken disarmed line is suppressed in favour of the door-gated welcome above (the phone push still fires); the shed's disarm announcement is unaffected.

Ways to disarm the house

  1. House Mode β†’ Home β€” automatic on resident arrival (handled by House Mode tab).
  2. Front-door NFC tag β€” instant and deterministic.
  3. Manual β€” the Alarmo app or panel.
  4. Touchpad (planned) β€” a physical keypad for the one case software can't cover: a fully-dead phone on arrival.

Shed + NFC tags (tab Alarm NFC Tags)

  • Listens for the HA tag_scanned event.
  • Front-door tag β†’ disarm the house (no-op if already disarmed).
  • Back-door tag β†’ disarm the shed for up to 2 hours. It re-arms when either the shed door has been closed for 15 minutes (after being opened) or the 2-hour cap is reached with the door closed (watching binary_sensor.shed_door_contact).
  • Shed left open at the 2-hour cap β†’ it does not arm; instead it notifies everyone + announces that the shed has been left open and unarmed, then waits to re-arm when the door is finally closed for 15 minutes.
  • Nightly 22:00 auto-arm β†’ arms the shed only if it is currently disarmed and the door is closed β€” a catch-all for a shed left disarmed during the day.

Strobe (HA, not Node-RED)

Two separate HA automations β€” one on alarm_control_panel.house, one on .shed β€” trigger on each panel's triggered state and run script.strobe_lights on light.downstairs until that panel is disarmed. A siren is planned.

Implementation notes

  • server-state-changed triggers use an explicit entity list β€” the substring/regex filter throws a?.some is not a function on this palette version.
  • All api-call-service nodes use the action property β€” the old separate domain/service fields are deprecated in v1.0 of the palette. Static calls: set action: 'domain.service'. Dynamic calls (action from message): set action: '{{payload.action}}' (mustache). Do not use actionType/dataType β€” the palette ignores them; the only way isDynamicValue() returns true is mustache or a Node-RED env var.
  • Notify dispatch uses the action form ({action:'notify.x', data:…}); the people config node publishes to global context so both alarm tabs share one source of truth.
  • TTS for alarm state changes is centralised in the notification engine (one announce per event, any source); the NFC shed-open alert announces from the NFC tab as it is not an alarm state change.

Heating Control β€” how it works

The Heating Control flow in the Node-RED editor

Runs off House Mode + time of day, driving the local HomeKit thermostat (climate.netatmo_smart_thermostat). The Netatmo holds a flat eco 19Β°C baseline; this flow only ever raises above it and re-asserts the target every 30 minutes so a manual override never lapses back to the baseline.

Temperatures

eco 19 Β· night 19.5 Β· comfort 20 Β· hot 20.5 Β· frost 12

Schedule (House Mode + time)

  • Home β†’ comfort 20; hot 20.5 between 19:00–22:00
  • Sleeping β†’ night 19.5 overnight; comfort 20 from a wake time that varies by season: 06:30 weekdays / 07:30 weekends (Sep–May), 07:00 every day in June–August
  • Away β†’ eco 19; drops to frost 12 after 24 h empty (gated by the Away 24h+ dashboard toggle, input_boolean.heating_extended_away)
  • The same 24 h-empty state also stops the hot-water solar diverter (no point heating water for an empty house); it goes back to normal when anyone returns, when a pre-warm boost is started, or when someone is heading home β€” and the flow only ever writes on those transitions, so a manually-stopped diverter is left alone

Forecast pre-heat

Nightly at 21:30 it reads the Met Γ‰ireann hourly forecast for tomorrow's 05:00–07:00 low and starts that day's warm-up earlier β€” the colder it is, the earlier: 4–8Β°C β†’ 15 min, 0–4Β°C β†’ 30 min, βˆ’3–0Β°C β†’ 45 min, below βˆ’3Β°C β†’ 60 min. A phone push to the residents the night before, only when the low is sub-zero.

Proximity pre-heat

When the house is empty and someone is driving home (within 10 km and getting closer, via the Proximity integration), it warms toward comfort so it's ready on arrival. The pre-heat latches once triggered β€” a GPS wobble flipping "towards" to "away from" for a moment can't bounce the setpoint mid-approach; it releases only when they arrive (house leaves Away) or genuinely leave the area again (beyond 12 km).

Boost

Boost from the dashboard (pick a temperature, tap Boost) or by nudging the thermostat above the scheduled target β€” either way it holds for 2 hours before the schedule resumes, and re-boosting restarts the clock. Turning the thermostat down to or below the schedule (or tapping Cancel on the dashboard) cancels the boost β€” a turn-down is never treated as a "boost" (this also absorbs the Netatmo app's boost-delete, which reverts the device to its 19 baseline). A boost still cancels the moment everyone leaves β€” but you can start one from the dashboard while the house is Away to pre-warm it before arriving home; it holds the usual 2 hours, so if nobody makes it back it lapses to the Away setback.

"Why did the heating change?"

The controller writes a plain-English status to input_text.heating_status β€” used both by the heating card and by a Recent activity logbook card β€” and it names what triggered the change, not just what the heating is doing:

Status line What happened
Home β€” comfort 20Β° Β· house woke up (sitting room light) someone turned a light on in the morning; the house switched out of Sleeping
Evening warm-up 20.5Β° Β· evening schedule (19:00) the clock, not you
Sleeping β€” overnight 19.5Β° Β· quiet for 20 min β€” bed the house settled
Away β€” setback 19Β° Β· everyone left the last person left
Boost 22Β° until 15:30 Β· you asked dashboard or thermostat dial
Morning warm-up 20Β° Β· cold morning β€” started early forecast pre-heat
Home β€” comfort 20Β° Β· heading home proximity pre-heat

The cause is taken from input_text.house_mode_reason when a mode change drove it (entity ids resolved to friendly names), from the boost/pre-heat state when one of those did, and otherwise from the clock.

Guest mode

While input_boolean.guest_mode is on the heating never drops to the Away setback (visitors stay warm); the normal overnight and morning behaviour still applies.

Implementation notes

  • Tab Heating Control; controller heat-fn, fed by heat-get β€” an ha-get-entities version 3 node. A version-1 node returns an empty list, which silently broke this flow (it fell back to "Home" and never saw Away) until fixed 02-07-2026. When adding a get-entities node, clone a v3 one.
  • Triggers: input_select.house_mode change + a 60 s heartbeat; output de-duped with a 30-min re-assert.
  • Forecast sub-flow: heat-fc-cron (21:30) β†’ heat-fc-get (weather.get_forecasts, hourly, weather.forecast_home; response via outputProperties valueType results) β†’ heat-fc-fn β†’ notify (sub-zero only). The pre-heat decision lives in flow.preHeat (in-memory β†’ lost on a restart between 21:30 and morning, fails safe to that day's normal wake time).
  • Boost detection is poll-lag-proof: a manual setpoint is only treated as a boost once the flow's own last write has been confirmed by the thermostat.
  • The status line is capped at 100 characters β€” that is the input_text limit, and Home Assistant rejects an over-long value, which would silently stop the ticker updating.
  • Ignores house_mode when its reason reads the literal seeded sentinel (23-09-2026). input_text.house_mode_reason's own configured initial value is the string seeded β€” it only ever appears when an HA Core restart's restore_state failed to bring back the real reason, which is the same moment house_mode itself can be wrong (see the House Mode section above). When it reads seeded, the controller holds the last house_mode value it trusted instead of acting on the fresh (possibly bogus) one.

Camera Concierge β€” how it works

The Camera Concierge flow in the Node-RED editor

The Camera Concierge (tab Camera Concierge) is the sole handler of Frigate camera notifications. It turns Frigate detections into smart, consolidated phone alerts. It replaced six separate HA automations and the old package concierge.

Prerequisites

Four things have to be in place before this flow does anything. Most "nothing happens" reports come down to one of them.

  • The Frigate integration from HACS, installed in Home Assistant. It is what serves the /api/frigate/notifications/… paths that every snapshot, GIF and tap-link in this flow is built from. Without it the links have nothing on the other end. Note this is not the Frigate Proxy add-on, which only puts Frigate in the HA sidebar and plays no part here.
  • An MQTT broker that both Frigate and Node-RED can reach, with Frigate publishing to it. The flow subscribes to frigate/reviews.
  • The node-red-contrib-home-assistant-websocket palette, with its server config node pointed at your HA and holding a Long-Lived Access Token.
  • NABU_URL set as an environment variable (see Start here if you only want one flow). It is just the base URL your phone can reach HA on, with no trailing slash. Never put a token in it: those notification paths are unauthenticated on purpose so the phone can fetch the image without logging in. If it is unset, the URLs start at /api/ with no host and iOS reports "Failed to load attachment, unsupported URL".

Alert severity is Frigate's call, not this flow's. The flow only acts on severity: alert and drops everything else, so if nothing arrives, check the review section of your Frigate config first. That is where you list which labels count as alerts, and where required_zones promotes an object to alert only once it enters a zone you care about.

Signal it listens to

  • Primary: MQTT topic frigate/reviews (broker 10.0.0.229). Frigate only publishes a review at severity: alert when an object enters an alert zone β€” so masks/zones (e.g. the driveway car-mask) are honoured upstream, and the concierge only acts on real, zone-qualified events.
  • Secondary branches: HA websocket for the courier count sensors + the postman sensor, and the patio-door switch state.

Turning a detection into a notification

For each frigate/reviews message the flow decides three things:

  1. Relevant? Only severity: alert. Rear cameras are dropped entirely while the patio-door NFC switch is on (input_boolean.patio_door_open).
  2. Kind (first match, in order): Person β†’ Vehicle (car/truck) β†’ Motorcycle β†’ Bicycle β†’ Package β†’ Umbrella β€” the six Frigate alert labels. Each kind is tracked separately.
  3. Zone: Front (Front_Car + Front_Van), Doorbell (on its own), or Rear (Rear_Door + Rear_Shed).

Each unique (zone + kind) opens an "incident" (120-second window). So a person out front and at the doorbell = two notifications ("Person β€” Front", "Person β€” Doorbell"); a person and a vehicle out front = two more.

The notification β€” three stages, one notification

All stages share the same notification tag, so they update in place rather than stacking:

  1. Immediate text β€” fires the instant the review starts, no image, so the alert lands without waiting on a download (e.g. "πŸ“· Person β€” Front").
  2. Snapshot β€” a still frame attached as soon as Frigate has it (fast).
  3. GIF β€” the animated preview replaces the snapshot when the review ends.

If more cameras in the same zone+kind see it, the notification updates its camera list instead of firing new ones.

Which camera's view you get

Within a zone the snapshot, GIF and tap-link all come from the camera that detected first β€” the one that opened the 120-second incident (shed-first β†’ shed's view, door-first β†’ door's view). The notification text still lists every camera that saw it.

Tapping the alert

The notification's tap action (clickAction/url) opens the event clip (clip.mp4) of that first-detecting camera β€” straight to the footage.

Who gets what

  • Phone push (text β†’ snapshot β†’ GIF) β†’ everyone with the Camera / Doorbell toggle on in the Household Config dashboard.
  • Doorbell + person also casts a snapshot to the kitchen display (20 s) and an overlay on the Shield TV. This only fires for an arrival. If the front door is open, or closed less than 60 seconds ago, both popups are skipped, because whoever is on camera has already come through it.
  • Doorbell pushes are held back on an exit too β€” all three kinds, latched for the whole incident so the late GIF cannot slip through alone. house_mode = Away overrides it.
  • Couriers (DPD/GLS/Amazon/UPS/FedEx/DHL/An Post on the front cameras) β†’ spoken "<courier> has landed" on the home audio group β€” suppressed when house_mode = Sleeping.
  • Postman at the doorbell β†’ spoken "the postman has been detected" β€” suppressed when house_mode = Sleeping.
  • The two TTS branches share a 2-minute cooldown so one An Post delivery never announces twice.
  • Phone pushes are unaffected by Sleeping β€” that suppression is speaker-only. When house_mode = Away, camera pushes are escalated: delivered immediately at high priority on a dedicated high-importance β€œCameras Away” channel with a ⚠️ title, so a person at the house while everyone is out cuts through.

Person-at-the-car deterrent

When house_mode = Sleeping, a person detected in the front car or van focus zone triggers a deterrent: sitting-room and hallway lights strobe and a warning is announced on the bedroom and sitting-room speakers. A 120-second cooldown prevents repeat triggers from the same event. Tied to Sleeping mode rather than a fixed clock window so it responds to when the household actually goes to bed. Every candidate event (fired or not) now logs the live house_mode value it saw ([car-deterrent] …), so a mismatch between this tab's cached mode and reality shows up immediately instead of needing to be reconstructed from logs afterwards.

Anti-spam / suppression

  • Rear silenced while the patio-door switch is on.
  • Doorbell silenced on an exit: while the front door is open, or for 60 seconds after it closes, the Shield overlay, the kitchen display and all three doorbell pushes are held back. house_mode = Away overrides it. The verdict is latched per incident so the late GIF push cannot arrive on its own.
  • Courier/postman TTS silenced when house_mode = Sleeping (push still goes to phones).
  • Per-incident grouping (multiple cameras = one notification).
  • 120 s incident window + courier/postman cooldowns to avoid repeats.

Behind the scenes

  • Every action is logged to data/camera_concierge.log (tags push-grp / push-grp-snap / push-grp-gif:<cam>, etc.).
  • Whole flow version-controlled in this repo (colfin22/node-red-config, nightly git backup); the LXC is also in the PBS nightly backup.

Implementation notes

  • Real Frigate camera names are title-case: Doorbell, Front_Van, Front_Car, Rear_Door, Rear_Shed.
  • Notify data must be built with explicit JSONata ({"title":…, "message":…, "data":…}) β€” otherwise the nested data (image/clickAction) gets flattened.
  • house_mode is mirrored into flow context (flow.house_mode) so the courier/postman TTS suppression and the person-at-the-car deterrent can read it cheaply. A server-state-changed watcher updates it on every change (with for:0, forType:num β€” a missing/empty for throws ConfigError: Invalid config value for 'for' on every change), and a startup inject β†’ ha-get-entities β†’ function seeds the current mode on restart. The watcher uses outputInitially:false, so the seed is what keeps Sleeping-based behaviour correct after a Node-RED restart mid-Sleeping/Away β€” the seed's ha-get-entities must output to msg.payload (outputLocationType:msg, not none) or it silently falls back to a default.
  • The front door is mirrored into flow context the same way (flow.front_door = {state, at}), because cam-fn-review fires the instant the MQTT review lands and fetches no entity states, so it cannot look the door up itself. Same watcher plus 120 second seed shape, same for:0 and msg.payload requirements as above. The seed takes its timestamp from the entity's last_changed, the watcher from the moment the event arrives.
  • The doorbell exit gate latches its verdict per incident rather than re-checking each push. Measured over ten real incidents the GIF push landed 44 to 159 seconds after the first one, always past the 60 second window, so a per-push check would silence the openers and then let a lone GIF through. The latch expires after 10 minutes.
  • The doorbell exit gate keys off the incident tag prefix (frig_doorbell_) rather than the camera name, because all three push families carry that same tag and none of them carries the camera in a single common field. That is what lets one gate cover all three without editing any of the three source nodes.
  • Context keys derived from review ids are sanitised (dots β†’ _) because Frigate review ids contain a dot (which Node-RED would otherwise treat as a nested-context path).

Infra Watchdog β€” how it works

Uptime Kuma posts every monitor up/down event to a webhook in this tab (/uk-event). The watchdog turns those raw events into escalating alerts rather than one-ping-per-flap:

  • Tier 1 β€” phone push on first confirmed down.
  • Tier 2 β€” still down after the escalation window β†’ repeat push + a spoken announcement (voice only while someone is home).
  • Tier 3 β€” long outages re-alert on a slow repeat so a dead service can't be silently forgotten.
  • Recovery sends an "up again" push and resets the monitor's state machine.
  • Quiet hours (22:00–07:00) hold non-urgent noise; anything still outstanding is delivered in an 07:00 overnight summary.
  • While maintenance_mode is on, notifications are suppressed but the state machines keep running β€” anything still down when maintenance ends re-alerts on its next repeat. When maintenance switches off, the watchdog also re-polls the uptime monitor's status page and injects a synthetic up-event for every monitor currently up β€” this clears any down-state whose recovery happened during the window (the monitor's own maintenance window pauses its webhooks, so those recoveries would otherwise be missed and cause false "still down" alerts).

Uptime Kuma stays the source of truth for reachability; the flow below handles health.

Infra Health & Alerts β€” how it works

One tab consolidates what used to be eleven separate HA infrastructure-alert automations. Every alert is titled [Category] Subsystem: detail (categories: Health / Backup / Monitoring / Service) and lands on a phone with a matching Android notification channel + group, plus a line in a log file.

Inputs:

  • Sensor-driven rules β€” a server-state-changed watcher over ~40 entities feeds a rule engine (disk usage, pool health, service states, rsync failures…) with per-rule thresholds, sustain times and de-duplication.
  • Webhooks β€” backup scripts (TrueNAS config, MikroTik config, restic) and Zabbix post their outcomes straight to HTTP-in endpoints here.
  • Backup watchdog β€” each morning it queries the Proxmox API on both nodes for last night's vzdump task list and alerts only on failures (the read-only API token comes from the gitignored .env).
  • Data-freshness checks β€” e.g. an hourly check that the ESB Networks smart-meter add-on has polled recently; a stale poll timestamp means the add-on is wedged even though its sensors still show plausible values.

Delivery: shared quiet-hours gate β€” alerts between 22:00 and 07:00 are held and flushed at 07:00 with their original trigger time in the title; maintenance_mode drops alerts entirely (logged, not pushed) while planned work is under way.


Rebuilding my own instance

The restore steps, what is and is not tracked, and how the nightly backup works have moved to RESTORE.md. You do not need any of it to use a flow.

Licence

Built by Colm Finn β€” MIT licensed.

About

πŸ”΄ The Node-RED automation brain behind a family smart home in Ireland β€” house-mode, alarm, heating and camera-notification flows layered on Home Assistant. Local-first, self-hosted on Proxmox, flow-versioned and PBS-backed. MIT.

Topics

Resources

Stars

6 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages