Highlights
- Sentinel now audits your home network. With the router integration you already have (FRITZ!Box, UniFi, eero, ASUSWRT, Nmap, …), you get a push when an unknown device joins, with a Trust device button. It also reports UPnP on the router, new UPnP port mappings, a public IP change, and, with eero, guest devices connected while nobody is home, per-device data-usage spikes and weak router settings. Nothing scans the network: the audit reads what Home Assistant already knows. See the network security plan.
- The model never sees your addresses. Everything Sentinel shows a language model passes a redaction gate first: MAC, IP, Bluetooth and Zigbee addresses are removed and hostnames are replaced by a manufacturer and a per-install pseudonym. A new option goes further and gives a cloud chat model only a fixed digest of the audit.
- Voice replies start sooner. HGA's text-to-speech now streams, so a satellite starts speaking after the first sentence instead of waiting for the whole reply, and an optional spoken acknowledgement ("Let me check.") covers the wait while the agent works.
- Cameras repeat themselves less. A per-camera notification cooldown turns repeat alerts for a lingering subject into quiet updates of the same card, caption deduplication got five fixes, and Ring Protect event recordings can now be analyzed frame by frame.
- Everyday questions that used to fail now work: "is the garage door lock locked?", "is the front door open?" when it is closed, and answering "Yes" to the agent's own offer.
Thanks to @andymcmanus, who suggested the spoken acknowledgement and field-tested the voice and camera work on his own hardware across a dozen betas.
If HGA is useful to you, a ⭐ on the repo helps other Home Assistant users find it.
Added
- New option Camera notification cooldown (
video_analyzer_notification_cooldown_s, 0 = off, up to 600 seconds, in Global Options) for the video analyzer (#672). A subject that lingers in view, or a camera whose motion sensor re-triggers, could produce several near-identical notifications within a minute, because the vision model words the same scene differently each time and caption deduplication compares wording. With the cooldown set, the first notification from a camera sounds and later ones from that camera within the window replace the same notification card quietly with the latest description and image. With face recognition on, the first Unknown Person reported inside the window still sounds, once; nothing else does, and without face recognition everything inside the window is a quiet update. After an Unknown Person notification, a later one that does not show an unknown person goes to a second quiet card instead of replacing it. Each camera has its own fixed-length window; analysis, sensors, events and stored results are unchanged. It needs a single companion-app notify service (notify.mobile_app_*), is verified on iOS and unverified on Android; see Notification cooldown for the limits. - New option Spoken acknowledgement (voice only) (
voice_tool_acknowledgement, empty = off, in Global Options): on a voice turn, the text you set (for example "Let me check.") is spoken as soon as the agent starts working on a request, so a question that takes the agent a few seconds is not met with silence (#671, suggested by @andymcmanus). A second option, When to speak the acknowledgement, picks the moment: As soon as the agent starts (the default) is heard about a second after the request reaches the agent, ahead of the whole wait, including a local model's seconds of reading its input before its first word (@andymcmanus measured 6 to 8 s on qwen3:8b, which the later moment left uncovered), but it is also said before a reply that needs no tools, so neutral wording such as "One moment." suits it; Before the first tool call is said only on turns that use tools, and only when the model has not already said something itself. Either way only on turns from an Assist satellite (a typed chat, or a typed pipeline run that names a device, gets none), and at most once per reply. Home Assistant's pipeline starts speaking a reply only once it passes 60 characters or meets a tool call, so the early phrase is padded with trailing spaces the speech engine drops; a phrase without a closing sentence mark gets a full stop, because a streaming TTS engine holds text back until a sentence ends. It is shown in the chat log with the tool call and never sent back to the model. Needs a streaming text-to-speech engine, such as HGA's own. Known behavior: on a Home Assistant Voice PE the early moment can produce a brief click during the wait, from the device rather than the audio (clicks on 4 of 8 turns in a trial, none with Before the first tool call); choose that timing to avoid it. - HGA's text-to-speech engine now streams: in an Assist pipeline it receives the agent's reply while the agent is still writing it and speaks it a sentence at a time, so a voice satellite starts talking after the first sentence instead of staying silent until the whole reply is done (#671). The first sentence is synthesized on its own, and a finished sentence followed by a pause of 0.4 s — the agent stopping to call a tool — is spoken during the pause rather than held until the answer; later sentences are batched into one request per gap, and the pieces are sent as one WAV stream, which Home Assistant converts for the satellite as before. The first piece is padded with a moment of silence: Home Assistant's converter passes nothing on until 64 KB of audio has arrived, which a short first sentence in mp3 took about 8 seconds of speech to reach, so a Voice PE heard nothing and gave up after 2 s. While the agent is still working between sentences, the stream is kept flowing with silence paced to real time (never more than 0.3 s ahead): and the first sentence always ends in at least a quarter second of silence, because Home Assistant's converter holds back the last ~50 ms of audio while it waits for more: without the silence, the tail of a long acknowledgement ("…se" of "please") played seconds later, just before the answer. A message that arrives whole (
tts.speak, announcements, a reply the pipeline already has in full) is still one request in the preferred format. If a turn fails and the pipeline never ends its text, synthesis gives up after five minutes without text. Home Assistant's pipeline starts streaming once a reply passes about 60 characters or when the agent writes text before a tool call; shorter replies are spoken whole, as before. New dependency:sentence-stream(the sentence splitter Home Assistant's own ElevenLabs integration uses). - New option Analyze Ring event recordings (
video_analyzer_event_recording_enabled, off by default, in Global Options): when a ring-mqttselect.*_event_selectentity publishes a neweventIdtogether with a signedrecordingUrl(Ring Protect), the video analyzer downloads the event's MP4, extracts one frame per second with ffmpeg, keeps up to 8 spread across the clip, and analyzes those in place of the retained interval snapshot — which on battery Ring cameras can be up to 10 minutes older than the event, so a capture window there previously could not show the event at all (#491; availability measured by @andymcmanus at 0.4–1.1 s after theeventIdchange). Clip frames are stamped before the trigger, so they order ahead of the snapshots the window captures afterwards, which still join the batch unless they repeat a frame the clip displaced (a battery camera's frozen snapshot must not ride along as if current — @andymcmanus's field report of a stale person blended into a passing car's clip). A window whose recording is still downloading holds its flush for up to 20 s, so a battery camera's event is analyzed and notified once, from the clip, rather than first from the stale snapshot; a clip that lands later still is analyzed on its own. Cameras without arecordingUrlkeep the snapshot behavior. Needs the ffmpeg binary the official Home Assistant image ships; without it the analyzer logs one warning and keeps using snapshots. The URL and event id come over MQTT and are not trusted: https-only, public hostnames only, redirects followed only to a subdomain of the same host (Ring's signed URL answers 302 to one), a 64 MB download cap, a three-minute decode cap, and no attribute ever names a path. Retries for about 15 s when the recording is not yet fetchable, bounds each ingest at three minutes and three in flight per camera, warns at most hourly per camera on failure, and reportsrecording_frames,recording_failures, andreplaced_by_recordingin the hourly metrics. See Ring cameras via ring-mqtt. - Sentinel rule discovery can now propose rules over the network (network security plan, step 10): a device connected when it should not be (
network_client_present_when, for example a device on the network while nobody is home), a device missing when it should be there (network_client_absent_when), and a router setting at a value you do not want (network_posture_equals, such as UPnP or the guest Wi-Fi turned on). The discovery model sees only a pseudonymized network section: each device's key, the name Home Assistant shows, whether it is connected, the guest flag, and the router's boolean settings. Proposals appear on the proposals card like any other, and an approved rule addresses the device by its pseudonymized key, so it survives a rename and stores no address. network_client_usage_anomaly: the Home Assistant security audit reports a connected device that has moved far more data today than it usually has by this hour, upload at medium (a device sending a lot is the shape of a compromise), download at low (network security plan, step 10). "Usual" is the device's own hourly baseline, so it needs baseline collection on and a few days of samples, plus per-device traffic from the router: with the eero integration, select Data Usage (Day) for clients under Activity in its options; the flow shows that dropdown only when at least one client is named in the clients picker of the previous step, and that client then loses its eero entities but stays visible to Sentinel, which reads eero's client list directly; the docs' Setting up eero note walks through it (until then the audit lists the check as not run and says so). A device is reported when its traffic exceeds its usual by 300% and by at least 250 MB, so an idle device's few megabytes never read as a multiple. Each device and direction is its own finding with a one-day cooldown on that device, so one device's alert never hides another's. The per-device traffic counters (keyed by the pseudonymized device key, never an address) are stored as baselines next to the entities without the per-entity establishment notices or weekly rows, expire 30 days after a device was last seen, stay out of the health sensor's baseline counts, and can be reset one device at a time withsentinel_reset_baseline. The sensors a router integration creates per client (eero's rate, signal, and traffic sensors) are no longer baselined as entities, so enabling the Activity option does not produce a "Baseline established" notice per device; their rows are removed once.- New Sentinel option Enable LLM triage of findings (
sentinel_triage_enabled, off by default) and its Triage timeout setting, both in Sentinel → Configure → Advanced. Triage has been in the engine since v3.7 but had no switch anywhere in the UI, so it was unreachable on every install; anyone who found the option name in the docs had no way to turn it on. Turned on, each finding gets one extra call to your chat model that may suppress a low-value alert before it is sent. The model can only suppress or pass a finding — it never alters, creates, or acts on one — it is shown no entity states, area names, or evidence text, and any error or timeout sends the finding anyway. Needs a configured chat model and autonomy level 1 or higher. (#262) - New Sentinel option Share security audit details with the chat model (
sentinel_network_audit_share_details, on by default, so nothing changes until you turn it off). Off, theaudit_home_securitytool gives the conversation model only a deterministic digest: how many findings at each severity, each finding's fixed title ("UPnP enabled on router"), which checks ran, and why others could not. Nothing in the digest is copied from your home and no model writes any of it, so from then on an audit hands the chat provider no device, add-on, token, or automation names (a conversation that already received details keeps them in its memory, so start a new one after turning the option off). When a signed-in administrator asks, the full report is posted as a persistent notification, "Security audit report", replaced on each audit, and the agent points you to it; when anyone else asks (a voice satellite, a non-admin account) nothing is posted and the agent says an administrator can get the details. If checks ran on incomplete data, the digest says how many such notes the full report holds rather than claiming a clean bill. Meant for a cloud conversation model, or a voice satellite guests can reach. This replaces the local-model override the network security plan described for step 9: that design passed a model-written narrative of the full audit to the chat model, which could not guarantee what stayed local. Therun_network_auditservice is unchanged and always returns the full report. network_guest_client_present(medium): the Home Assistant security audit reports a device on your guest Wi-Fi that you never trusted when it is connected while nobody is home (network security plan, step 7). Guests on the guest network while someone is home are what the network is for, day or night, so nothing is reported then; a home with nopersonentities is never treated as empty, and neither is one whose presence trackers areunknownorunavailable. Devices you parked on the guest network on purpose were trusted when the device inventory was established, and a visitor's phone stops being reported once you tap Trust device on the push (offered when the alert names a single device, so one tap never trusts a device the push did not show), so what is left is a device that was announced when it joined, was never vouched for, and keeps coming back. A device's first day belongs tonetwork_unknown_device_joined, so one arrival never pushes twice; at most one alert a day. Needs a router integration that says which clients are on the guest network (the eero integration today); connected clients that come from a source that does not say are counted in the audit's notes as not checked.network_unknown_device_joinednow also says when a new device joined over the guest Wi-Fi.- Four router settings checks for the Home Assistant security audit, read from the eero integration today (network security plan, step 7). Each is a standing condition reported at most once a day:
network_guest_network_idle(low): the guest Wi-Fi is on and no guest has connected forsentinel_network_guest_idle_days(default 7) days. The idle clock is remembered across restarts and restarts whenever a guest connects or the network is turned off.network_wpa3_disabled(low, advisory): WPA3 is off at the router.network_protection_disabled(medium): the router's malware and threat blocking is off. Ad blocking alone being off is not reported.network_ddns_enabled(low, informational): dynamic DNS gives the home a fixed public name.
The last two are eero Plus features and are audited only when Plus is active, so nobody is told to turn on something they cannot.
- The security audit now reads the eero integration's own client list and settings directly, so a device that joins your network is seen within one eero poll (about two minutes) instead of only after the integration is reloaded, and the client filters in eero's setup no longer matter. The read touches an explicit allowlist of the integration's data and never its Wi-Fi or guest passwords or Thread keys; if the integration's objects change, the audit falls back to eero's device trackers and says so in its notes. (network security plan, step 7)
- The Home Assistant security audit now watches the devices on your network when a router integration is set up (network security plan, step 7). Any
device_trackerthat reportssource_type: routercounts (FRITZ!Box, UniFi, eero, ASUSWRT, Nmap, …), so no new integration is needed beyond the one you already have. New Sentinel rulenetwork_unknown_device_joined(medium; high while nobody is home or at night) reports a client the device inventory had not seen, once it has been known for a grace period (sentinel_network_unknown_device_grace_min, default 5 minutes); a device that has since left is still reported, marked "not connected now", because a visitor that came and went is exactly what you want to hear about. The push carries a Trust device button; a client whose MAC belongs to a Home Assistant device that a non-router integration set up (a Shelly plug, a printer) is trusted automatically, and a phone's rotated random address on a device whose name is already trusted is reported once at low severity. The first run after the router is seen records its current clients as trusted with one notification, like the radio inventory. Clients are recorded and reported by a per-install pseudonym of their MAC; the inventory, the findings, and anything a language model sees carry no MAC or IP address (a client a router names by its address is shown as "device 3fa2c1b0") and never a client's DHCP hostname, a client the router did not report is kept for 30 days so an integration reload cannot turn your devices into new ones, and the trust services accept inventory keys (device_key) for clients that have no Home Assistant device. - The eero integration's network settings are read as router posture: its UPnP switch now decides
network_upnp_enabled(outranking the UPnP/IGD inference, and able to say UPnP is off), its public-IP sensor feedsnetwork_public_ip_changed, and its WPA3, guest-network, IPv6, dynamic-DNS, advanced-security, and ad-blocking switches plus guest-client count and daily threat count enter the audit's capabilities for the guest, WPA3, protection, and DDNS checks that follow. eero Plus features simply show as not run on other accounts. One eero fact worth knowing: it registers a device tracker for a client only when it is set up or reloaded, which is why the audit reads its client list directly (see above) and mentions the tracker limit only when that read fails. - Sentinel's model calls no longer see network addresses. Everything the explainer, triage, and rule discovery are shown, and everything the
audit_home_securitytool returns to the chat model, now passes a redaction gate first (network security plan, step 8): fields holding a MAC, IP, Bluetooth, or Zigbee address are dropped, a network client's hostname is replaced by its manufacturer and pseudonymized key, and an address embedded in a sentence becomes[mac],[lan ip], or[public ip](a bare one inside a name advertised on the LAN, such asSonos-7828CA123456, included), while a name under a local suffix such asnas.localbecomes[hostname]. Device names that carry no address (discovery titles, gateway names) still reach the model, labelled as data, as before. Notifications and therun_network_auditservice response keep the real values, since they are for you rather than a model; entities you expose to Assist are shown to the conversation model as Home Assistant shows them. This lands ahead of the router adapters that will fill the client list, so none of them can leak an address. - The Home Assistant security audit now reads the router's UPnP posture, the first router-class adapter of the network security plan (step 7). No router integration is needed: Home Assistant already listens for UPnP gateway announcements on every install, and that announcement is the proof. Three new Sentinel rules:
network_upnp_enabled(medium, once a day) reports a router that advertises UPnP, which lets any device on the LAN open ports to the internet unasked unless the router restricts it. Evidence is the SSDP discovery cache, a set-up UPnP/IGD integration with live entities, or (only when the cache cannot be read) a pending UPnP/IGD discovery; when none is seen the check is listed as not run rather than read as "UPnP is off", since Home Assistant may be on another network segment.network_public_ip_changed(low) notes when the home's public IP address changed since the previous run, for anyone who relies on dynamic DNS, a VPN endpoint, or a port forward by address. Needs the UPnP/IGD integration. The address is pseudonymized before it enters the snapshot.network_upnp_port_mapping_added(medium) reports that the router has more UPnP port mappings open than on the previous run, meaning a device asked to be reachable from the internet. Needs the UPnP/IGD integration's port-mapping count sensor, which is disabled by default; the audit names the sensor to enable.
- The two change checks compare against the previous run's values, which are kept in the Sentinel device inventory file together with the sensor they were read from, so a change across a Home Assistant restart is still seen and a value from one gateway is never compared with another's. A change whose alert a cooldown or quiet hours held back is reported once the alert can go out.
- Sentinel persistent notifications for the Home Assistant & network security family now escape Markdown in their text, so a device name advertised on the LAN that is shaped like a link or HTML renders as plain text in the Home Assistant UI. Mobile pushes were already plain text.
Changed
- Caption deduplication in
notify_on_anomalymode now counts the 30-minute window from the last caption that was sent as a notification, not from the last caption stored. Each withheld caption used to restart the window for the next, so a subject that stayed in view could produce one notification and then none for as long as similar captions kept arriving; such a scene now notifies again once 30 minutes have passed since its last notification (reason=renotifyin the debug log). A caption whose notification had no notify service to go to does not count, and neither does a quiet card update sent during a notification cooldown. A caption from a batch in which face recognition saw an Unknown Person is withheld only by an earlier notification that was also for an Unknown Person, so a resident's caption cannot silence a stranger's (before, the closest match decided, whoever it showed). New captions are stored withnotifiedandunknown_personflags; captions stored before the upgrade count as notified (the recent-caption comparison does not read them, so for the first 30 minutes after the upgrade a repeat can notify once more). Static and empty-scene captions are unaffected and stay withheld. See Caption deduplication. - Four runtime requirements are now short version ranges instead of exact pins:
aiofiles>=25.1.0,ollama>=0.6.1,<0.7,anthropic>=0.125.0,<0.126andsentence-stream>=1.3.0,<1.4. Home Assistant's validator (Hassfest) began refusing exact pins on packages Home Assistant itself pins, and anollamapin that excludes the version Home Assistant's own Ollama integration uses (0.6.2). The caps keep a fresh install from drifting to a new minor release, which is what theanthropicpin was for. Nothing changes for an existing install: the versions already installed satisfy the ranges. - Releases now bundle several changes instead of following every merge, so updates arrive on a
monthly cadence rather than most days. Fixes for anything broken in the wild — regressions,
Home Assistant compatibility breaks, security and safety failures — still ship the moment
they're ready. If you want to test a fix before it reaches a stable release, enable beta
versions for this repository in HACS. See
RELEASING.md. - Documentation on speech-to-text phrase biasing now says it steers recognition rather than
guaranteeing a spelling, since what matters for a voice assistant is landing close enough for
the conversation agent to match the entity. Field testing on OpenRouter's
microsoft/mai-transcribe-2returned an approximated spelling that Assist resolved correctly
every time. (#610)
Fixed
- In
notify_on_anomalymode, a camera caption that closely matched one sent a minute earlier could still notify when an older caption happened to score slightly higher (#704). Caption deduplication judged only the single best-scoring stored caption by its age, so on a camera that sees the same scenes repeatedly (the same people, the same cars) the best match was usually hours or days old and the notification went out as a new event. A caption of a real subject in action is now withheld when any matching caption was notified in the last 30 minutes, whatever older captions score; when old near-duplicates fill the search results, the camera's recently notified captions are compared with the new one directly. The debug log reports these asreason=recent_match. - Caption deduplication could compare a camera's caption with another camera's when one camera's entity name begins with the other's (
camera.sideandcamera.side_gate): the caption store matches names by prefix, sosidealso readside_gate's captions and a scene on one could suppress a notification from the other. Only the camera's own captions are compared now. - Two analyses of one camera that overlapped (a snapshot batch and an event recording, for example) could both pass caption deduplication and both notify for the same scene, because each checked the stored captions before the other had stored its own. The check, the notification and the caption store are now one step per camera.
- A database or embedding-model error during the caption deduplication check (anything other than a timeout) dropped the analysis and its notification. The notification is now sent (
reason=store_errorin the debug log). sensor.*_recognized_people,image.*_last_eventand the caption deduplication check could be given the names from a different analysis of the same camera when two analyses overlapped (a snapshot batch and an event recording, for example): they read a per-camera "last recognized" slot after the summary model call, by which time a second batch could have overwritten it. Each analysis now carries its own names through to those consumers.- When the agent gave up after several tool-use attempts, its "I wasn't able to complete this request…" reply replaced the turn's last tool call in the chat log (Show Details lost the call), and with the spoken acknowledgement on, a voice satellite said nothing at all. The reply is now added after the tool call and spoken.
- Asking whether a lock is locked works again. The extra arguments HGA adds to lock and unlock commands were also added to state questions about a lock, and Home Assistant rejected them ("not a valid option"), so every "is the garage door lock locked?" made the model retry until it gave up with "I wasn't able to complete this request after several tool-use attempts". Broken since v2.8.0. State questions now pass Home Assistant only the filters it accepts (name, domain, area).
- "Is the front door open?" gets an answer again when the door is closed. For questions about whether something is open, HGA widened the agent's live-state lookup to every door and window sensor and kept only the open ones; that helps "list all open windows", but for one named device it removed a closed front door from the result, and the agent said it could not find it. A lookup that names a specific device is now passed through as the agent asked; category lookups ("Window", "doors") are still widened. Since v3.14.9.
- Speech from a local server that answers in WAV (Speaches with piper voices does) is no longer cut off after its first sentence. Such a server returns one WAV file per sentence, back to back, and each header gives only its own sentence's length, so only the first sentence was played: a player that asked for WAV heard the start of an announcement (since v3.38.0), and a streamed reply lost the rest of any batch after its first sentence, a fraction of a second when that sentence was "Yes." (reported on the 3.43.0 betas). Every sentence's audio is now read, and a whole message is merged into one file.
- Answering "Yes" to an offer from the agent ("Want me to turn it off?") now gets the tools to do it. A short reply that accepts an offer names no device or action, so the tool search ran on "Yes" alone, offered media-player controls and no light tool, and the model checked the light's state until the tool-loop limit gave up with "I wasn't able to complete this request after several tool-use attempts" (seen 2026-10-01). Such a reply (yes, sure, OK, go ahead, please, do it, and the same in Czech, Russian and Turkish, up to four words) is now searched together with your previous request and the end of the agent's offer; that also lets the on/off safety net add the device-control tools whenever the offer names an action. The model still sees only what you said.
- On a shared Assist pipeline with Prefer handling commands locally, a request Home Assistant answered itself (the time, turning on a light) reached the agent as a bare question with its answer removed, so a small model answered those questions again in its next reply and claimed actions it never took: "what time is it", "turn on hallway light", then "say hello" got "It's 9:06 PM. I've turned on the hallway light. Hello!" (6 of 6 runs on qwen3:8b; heard aloud once replies streamed, #671, reported by @andymcmanus). Such a request now reaches the agent marked as already handled, with Home Assistant's answer, inside the user's message (0 of 6). The answer is still never shown to the model as an assistant reply, which is what taught it to answer device commands without acting (#588); with the note, a follow-up such as "turn it off" still finds the right device and calls the tool (6 of 6 on qwen3:8b and qwen3.8). Present since v3.33.3.
- Reasoning a model writes inline as
<think>…</think>(for example qwen3 or deepseek-r1 behind an OpenAI-compatible server that does not separate it) no longer appears in the streamed chat reply or its Show Details, and is never spoken: it was removed only from the final copy, and with the streaming TTS engine above a voice satellite would have read it aloud as it arrived. A reply that is all reasoning now gets the "unable to respond" apology instead of the raw reasoning. Ollama models configured through HGA were not affected; their reasoning arrives separately. - A refused automation no longer eats the retry it asks for. The agent allows three tool rounds per turn before it gives up with "I wasn't able to complete this request after several tool-use attempts"; on the live box the leak-alert request spent two rounds hunting for the phone's notify service, one on the refused guess, and the corrected
add_automationcall arrived in a fourth round that the guard discarded unexecuted, so the user got the apology and no automation. A round in which every tool call was a refused automation now counts as a correction round (up to two per turn) rather than an action round, since nothing ran and the tool handed back exactly what to change; the give-up message repeats the last refusal so the user sees what was wrong instead of a bare apology; and the system prompt now names the configured mobile push service, so the model writesnotify.mobile_app_<phone>on the first try instead of looking for it with tools that cannot show it. add_automationno longer installs an automation that can never run. Home Assistant shows the agent device names, never entity IDs, so the model derives them: asked to "always notify me if there is a leak", it wrote a trigger onbinary_sensor.sink_moisture_sensor(the friendly name "Sink Moisture Sensor", slugified) and an action callingnotify.mobile_app(a guess at the mobile push service). Both pass Home Assistant's automation validator, which checks schema only, so the automation registered, the tool answered "Added automation", and nothing was logged; the trigger could never fire, and had it fired the notify call would have failed. The tool now checks every entity reference (entity_idin triggers, conditions, action conditions, targets and waits;zone,scene, a time trigger'sat, a time condition'sbefore/afterand anabove/belowthreshold that names an entity; the entities a scene snapshot, scene apply or group set names) and every service call in the validated config against Home Assistant and refuses the automation with each missing name and the closest real one: the entity whose friendly name slugifies to the guess (all of them when several share the name, and in any domain, sosensor.for abinary_sensor.is corrected too), the mobile push service configured for this integration for a missingnotify.*alongside any othernotify.mobile_app_*service, and a domain's real services otherwise (on thescriptandautomationdomains only Home Assistant's ownturn_on/turn_off/toggle/reload/trigger; a user's own script, shell command, REST command or pyscript name is neither confirmed nor denied, since listing the missing ones would reveal the real ones before the PIN gate that guards every such call). Suggestions only ever name entities exposed to Assist, so a typo never reveals an entity you chose to hide; a real entity ID that is not exposed still installs. Anotify.Xwritten as an entity is told it is a service to call. A state trigger or condition on a value the entity can never report is refused the same way: on the live box the leak alert installed withto: wet, the moisture sensor's display label, and never fired, because Home Assistant compares the raw state, which for a binary sensor, switch, light, fan, humidifier, siren, input boolean or automation is onlyonoroff; the refusal says so and names the raw states (andawayon a person or device tracker is corrected tonot_home). A registered entity that is merely unloaded passes; a disabled one is refused and says so. Templates, entity registry IDs, areas,entity_id: all, a device action'sdevice_idandtype, event payloads, variables, a script's own arguments and anything underenabled: falseare left to Home Assistant, and entities the automation creates for itself (scene.create,group.set, its ownautomation.<alias>) count as existing for its later actions, though not for its triggers or conditions. The camera blueprint's configured push service is checked before the blueprint is built, since its call is templated (a value stored without itsnotify.prefix counts as the notify service it names, as elsewhere in the integration), and that refusal says it is a configuration problem rather than something to retry. The refusal is logged at info with the automation's alias and the missing names.- The Trust device button on a new-device push no longer trusts devices the push did not show.
radio_new_device_joinedandnetwork_unknown_device_joinedbundle every device that joined into one alert, and one tap trusted all of them, while the push lists at most ten names and is cut at 220 characters (a field test trusted an unidentified client together with the laptop the owner meant). The button now appears only when the alert names one device, as it already did fornetwork_guest_client_present; an alert naming several has no primary button and ends with a line saying to trust the ones you recognize with thesentinel_trust_network_deviceservice. A tap from an older notification that would trust several devices is refused and logged. - Changing any option no longer logs
Global tool index background task failedwith aPoolClosedtraceback. The tool index is written by a background task, and a reload closed the database pool under it; the unload now stops that write (the task and the store's batch worker) before the pool closes, an embedding-provider switch cancels a build that would have declared the old provider's vectors ready, and a turn that was in tool discovery across an unload no longer schedules a write onto the closed pool. - Sentinel now tells you when its unknown-person rules cannot fire.
unknown_person_camera_no_home,unknown_person_camera_night_home,alarm_disarmed_during_external_threatand the approvedunknown_person_camera_when_homediscovery rule all trigger on one thing only: a face-recognition result saying a person was seen but not recognised. Nothing else produces that, so on an install without face recognition all of them had been silently inert — no error, nothing in the log, and still counted as active on thesentinel_healthsensor, because from Sentinel's side they simply never matched. A repair issue under Settings → System → Repairs now names the affected rules and points at themotion_detected_*discovery rules, which give camera coverage with no face service. It also appears in the two states that look healthy from the options page but are just as inert: face recognition switched on with no database for the person gallery, and face recognition switched on with the video analyzer disabled (faces are only recognised while the analyzer is processing snapshots). The notice names which of the three you are in and gives that state's fix, rather than listing all three and leaving you to work it out. It clears by itself once the pipeline works, when Sentinel is turned off, and when the entry is unloaded. A face service that is configured but unreachable is not covered — that one already logs a warning. Sentinel's other rules were never affected. - Re-running Sentinel Basic setup no longer wipes your entity exclusions or camera-to-entry links. Basic setup restores recommended defaults for everything else, but those two are lists you picked by hand and no default can reconstruct them; losing the exclusions brought back exactly the phantom alerts they were added to silence, with nothing in the UI to say why. The overwrite warning now says the two are kept.
- The integration will keep loading on Home Assistant 2026.12. It imported a helper class that Home Assistant deprecated and removes in that release, at the top of the module — so on 2026.12 the import itself would have failed and the whole integration would not have set up. Until then the only visible sign was a deprecation warning, logged each time the
save_and_analyze_snapshotservice ran and asking you to report it against this repository. Older Home Assistant versions are unaffected. - Sentinel rule discovery no longer offers a statistical (baseline or time-of-day) proposal on a cumulative energy counter (a kWh sensor such as
sensor.washing_machine_switch_0_energy). The counter only ever grows, so such a proposal could never become a rule; it reached the proposals card as "unsupported" with a Request New Template button, and had to be rejected by hand. It is now dropped before it is stored and listed under Filtered Discovery Candidates with the reasoncumulative_energy_sensor. Deviation on the same appliance's power sensor, and a threshold on the energy counter ("above 5 kWh"), are still proposed as before.
Documentation
- The caption deduplication section of the camera docs said notifications for scenes with a person, vehicle, package or animal are always preserved. They are not: a matching caption from the last 30 minutes is suppressed whatever it shows, and only after 30 minutes does a matching caption with an active subject notify again. The text now says so.
- The seven Home Assistant & network security options in Sentinel → Configure → Advanced now have help text under each field (English and Czech): what the master switch covers and that nothing scans the network, what each threshold waits for and which need a router integration, what leaves the audit with details sharing on or off, and why token age is the only fact the stale-token check has.
- The network security plan opens with a status table per implementation step, the architecture guide describes the whole network audit path (adapters and the eero runtime tier, the auth and device inventories, counter baselines, discovery templates, the on-demand audit, and the privacy gate), and the constants reference lists the network family's remaining code-only constants. This closes step 11 of the plan.
- The Enable LLM explanations for sentinel findings switch now has help text, which it never had. It sits in the same form as the new LLM triage switch and the two names read alike, so the text says which is which: triage decides whether an alert reaches you, explanations decide how it is worded. It also notes that explanations run only for alerts that are actually sent, and that the Home Assistant & network security and alarm-disarm alerts always keep their exact wording.
- Documented how to give the agent internet search: the Control Home Assistant picker lists every LLM API Home Assistant knows about, not only Assist and MCP servers, so a search tool from Tools for Assist (Brave or self-hosted SearXNG) or a search MCP server can be selected there and pinned with Always-included tools. See Internet search. (#642)