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).
Most visitors want a single flow, not this whole setup. Nothing below assumes you run anything the way I do.
- Grab the tab you want from the
flows/directory. Each file is an importable JSON for one tab, regenerated automatically on every nightly backup. - 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.
- 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. - Set the environment variables the flow needs (next section).
- Replace the entity ids that are still mine (the table after that).
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).
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-housestate_changedevent 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 oldPERSON_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.
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 |
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.
- 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_modeexists as a helper but is intentionally ignored by the engine β it is consumed directly by other automations.
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.
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.
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.
- All Away evaluations use a fresh live HA snapshot via
ha-get-entitiesβ no stale flow context. server-state-changednodes always useoutputInitially: 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_modeviahm-set-mode, logs to/data/house_mode.logwith a local timestamp + reason, pushes a notification, and publishes the same reason toinput_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 publisheschanged by hand, so a consumer can't attribute it to whatever the engine last decided. - Self-heals a corrupted
input_select.house_modeentity (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 readingHomefor 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-memoryflow.modesurvives 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.
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.
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".
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.
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.
- House Mode β Home β automatic on resident arrival (handled by House Mode tab).
- Front-door NFC tag β instant and deterministic.
- Manual β the Alarmo app or panel.
- Touchpad (planned) β a physical keypad for the one case software can't cover: a fully-dead phone on arrival.
- Listens for the HA
tag_scannedevent. - 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.
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.
server-state-changedtriggers use an explicit entity list β thesubstring/regexfilter throwsa?.some is not a functionon this palette version.- All
api-call-servicenodes use theactionproperty β the old separatedomain/servicefields are deprecated in v1.0 of the palette. Static calls: setaction: 'domain.service'. Dynamic calls (action from message): setaction: '{{payload.action}}'(mustache). Do not useactionType/dataTypeβ the palette ignores them; the only wayisDynamicValue()returns true is mustache or a Node-RED env var. - Notify dispatch uses the
actionform ({action:'notify.x', data:β¦}); thepeople confignode publishes toglobalcontext 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.
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.
eco 19 Β· night 19.5 Β· comfort 20 Β· hot 20.5 Β· frost 12
- 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
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.
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 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.
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.
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.
- Tab
Heating Control; controllerheat-fn, fed byheat-getβ anha-get-entitiesversion 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_modechange + 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 viaoutputPropertiesvalueTyperesults) βheat-fc-fnβ notify (sub-zero only). The pre-heat decision lives inflow.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_textlimit, and Home Assistant rejects an over-long value, which would silently stop the ticker updating. - Ignores
house_modewhen its reason reads the literalseededsentinel (23-09-2026).input_text.house_mode_reason's own configuredinitialvalue is the stringseededβ it only ever appears when an HA Core restart's restore_state failed to bring back the real reason, which is the same momenthouse_modeitself can be wrong (see the House Mode section above). When it readsseeded, the controller holds the last house_mode value it trusted instead of acting on the fresh (possibly bogus) one.
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.
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-websocketpalette, with its server config node pointed at your HA and holding a Long-Lived Access Token. NABU_URLset 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.
- Primary: MQTT topic
frigate/reviews(broker10.0.0.229). Frigate only publishes a review atseverity: alertwhen 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.
For each frigate/reviews message the flow decides three things:
- Relevant? Only
severity: alert. Rear cameras are dropped entirely while the patio-door NFC switch is on (input_boolean.patio_door_open). - Kind (first match, in order): Person β Vehicle (car/truck) β Motorcycle β Bicycle β Package β Umbrella β the six Frigate alert labels. Each kind is tracked separately.
- 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.
All stages share the same notification tag, so they update in place rather than stacking:
- Immediate text β fires the instant the review starts, no image, so the alert lands without waiting on a download (e.g. "π· Person β Front").
- Snapshot β a still frame attached as soon as Frigate has it (fast).
- 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.
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.
The notification's tap action (clickAction/url) opens the event clip (clip.mp4) of that first-detecting camera β straight to the footage.
- 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 = Awayoverrides 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.
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.
- 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 = Awayoverrides 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.
- Every action is logged to
data/camera_concierge.log(tagspush-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.
- Real Frigate camera names are title-case:
Doorbell,Front_Van,Front_Car,Rear_Door,Rear_Shed. - Notify
datamust be built with explicit JSONata ({"title":β¦, "message":β¦, "data":β¦}) β otherwise the nesteddata(image/clickAction) gets flattened. house_modeis mirrored into flow context (flow.house_mode) so the courier/postman TTS suppression and the person-at-the-car deterrent can read it cheaply. Aserver-state-changedwatcher updates it on every change (withfor:0, forType:numβ a missing/emptyforthrowsConfigError: Invalid config value for 'for'on every change), and a startup inject βha-get-entitiesβ function seeds the current mode on restart. The watcher usesoutputInitially:false, so the seed is what keeps Sleeping-based behaviour correct after a Node-RED restart mid-Sleeping/Away β the seed'sha-get-entitiesmust output tomsg.payload(outputLocationType:msg, notnone) or it silently falls back to a default.- The front door is mirrored into flow context the same way (
flow.front_door={state, at}), becausecam-fn-reviewfires 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, samefor:0andmsg.payloadrequirements as above. The seed takes its timestamp from the entity'slast_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).
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_modeis 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.
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-changedwatcher 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.
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.
Built by Colm Finn β MIT licensed.



