Releases: SkyTechNerds/faceid
Releases · SkyTechNerds/faceid
Release list
v0.24.0
- The folder input no longer remembers every file forever.
data/folder_ingest.json
keeps one fingerprint per processed file so a restart does not re-read the whole folder,
but nothing ever pruned it — and the whole file is rewritten on every recording, so the
per-file cost grew with everything before it. Measured: 453 bytes per entry, 160 ms per
save at 50,000 entries, 660 ms at 200,000. At a thousand recordings a day that is over a
second of pure bookkeeping per file after a year. Reported on the HA forum. - New
folder.max_indexed_files, default 5000,0disables the cap. Oldest entries go
first; a file still being processed is never evicted. Dropping an entry costs nothing
beyond reading that file once more if it is still there. - Settable at runtime as Remember at most in the settings tab, which shows how many
files are currently indexed against the limit. - Enrolled faces are not affected. The gallery lives in
data/personsand never
expires — this cap only bounds the "already seen this file" notebook. - Eviction happens once when a scan finishes, never in the middle of one: trimming between
files would throw away entries the same scan had just written and re-process them.
A folder that permanently holds more files than the cap now logs a warning, since every
scan re-reads whatever fell out. - Entries left in
processingby a crash or a restart are reset for retry when the index
is loaded. They are exempt from eviction, so without that they would accumulate until the
cap could never be met again. - The settings tab applies the limit through a locked, non-blocking path — it runs on the
HTTP thread while the folder worker may be writing the same state. - Documented that the folder input reads still images (
.jpg,.jpeg,.png,.webp),
not just video. It always could; the defaultextensionslist is video-only and nothing
said so, so a folder of images was skipped in silence.
v0.23.0
Contributed by @thethereza, who brought the idea and
built the input; the review follow-ups below were finished on his branch.
- Add a no-Frigate mode that watches a directory for completed video/image files.
- Require stable file size and mtime before processing, persist fingerprints across
restarts, and bound retries for unreadable media. - Sample video frames, collapse repeated views of the same person within each file, and
reuse the existing gallery, unknown clustering, history, MQTT, and review UI. - Add folder-aware health and manual-scan UI plus a low-priority hardened systemd unit.
- Fixed: the unknown review broke when
url:was left empty.url:written with no
value is YAMLnull, not a missing key, so the.get("url", "")default never applied
andNone.rstrip("/")took out/api/unknownswith a 500. Reproduced before and after. - Fixed: a history scan with no input configured failed with
KeyError: 'url'. The
branch was chosen byfolder_moderather than by whether an input is usable, so turning
Frigate off before setting up the folder landed in the Frigate path without Frigate. The
scan is now rejected up front with an explanation instead of dying in the worker. - The folder scan reports progress while it runs instead of only at the end, so the bar no
longer sits at 0/0 for a long directory and reads as a hang. - A manual scan started while the poller is already scanning returns immediately instead of
waiting and then walking the same files a second time. The lock now covers the whole run,
not just the check-and-set. DisabledFrigateAPIinherits fromFrigateAPI, sofrigate_client(...) -> FrigateAPI
is honest and a method added later cannot silently be missing in folder mode.
v0.22.3
- Frigate credentials work in the app at last.
frigate_userandfrigate_password
have been offered as options for a long time, butrun.shnever wrote them into the
generated config — so anyone running Frigate behind its authenticated port 8971 could not
connect FaceID as a Home Assistant app at all. The options looked usable and did nothing.
Reported in #24, where the visible symptom was a different one. - Supervisor stops warning about them. Both keys were listed under
optionsbut missing
fromschema, which producedOption 'frigate_user' does not exist in the schema for FaceIDon every start. That is what the report was about; the dead pass-through was the
larger half underneath. - Nothing changes for existing installs: an empty value and a missing key both end up as
user=None, exactly as before, so the open API on port 5000 stays the default. - Free-text options no longer break the generated config. Credentials, prefixes and the
URL were interpolated raw into quoted YAML. A password containing a double quote produced
an unparseable file — and a crafted value could write further keys into the config. They
now go throughjq, which emits a properly escaped JSON string, and JSON is valid YAML.
This applied to the MQTT credentials as well, which predates this release.
The same applied to the four camera lists, which were joined into a string and wrapped in
brackets by the template — a camera name containing a comma or a quote broke the list.
They are emitted as JSON arrays now. - Fixed alongside it: an emptied
frigate_topic_prefixormqtt_prefixbecame YAMLnull,
andstr(None).strip("/") or "frigate"yields the string"None"— FaceID would have
subscribed toNone/eventsand never seen an event. They now degrade to the default.
v0.22.2 — One history row per person, and a script-injection fix
- Security: a person's name could execute JavaScript in the web UI. Captions were
spliced into a single-quoted JS string inside anonclickattribute. The HTML escaper
does not escape apostrophes — and even if it did, the browser decodes them before the JS
parser runs — so a name likeO'Brien, or a camera name from Frigate containing one,
terminated the string and ran arbitrary script. It affected four places: the history
card, the unknown queue (twice, fed by Frigate camera names) and the person cards. All of
them now pass the value through adata-attribute, where escaping is correct for the
context. Found while reviewing this release; it predates it. - The history shows one row per event and named person again (unknown faces keep one row
each — several strangers in a single frame all carry that label, and merging them would
swap their pictures against each other). It is titled "What FaceID
reported", but only the first match per person and event is ever reported — the
announcedset suppresses the rest. Every further row therefore claimed a notification
that never happened, and pushed genuine older entries out: 27 such rows out of 200 slots
on the reference instance, so the history reached 13% less far back than it should. - Later matches are not discarded, they improve the row. Checking the stored crops
first showed why that matters: all ten sampled duplicate pairs held different pictures,
sometimes drastically so — 8 KB against 94 KB of the same person, a distant crop against
a close one. Those later pictures are exactly what makes a wrong recognition checkable.
So a later match now improves the existing row instead of adding one: it replaces the
picture when its score is higher, and the card shows "best view 0.687" beside the
reported score. Independently of that, the first match that is actually published takes
over the row's score and timestamp even when it scores lower — it is the one somebody
received, and the card is headed "What FaceID reported". - Picture and embedding are always replaced together. Keeping them apart would let the
"was wrong" analysis run on a different face than the row displays.
v0.22.1 — Live frame when there is no snapshot
live_hires_fallbacknow also covers the case its name promises: no snapshot at all.
It said "on a failed snapshot, ask go2rtc for a full frame", but only handled one of the
two ways a snapshot can fail — one that exists and holds no face. When Frigate produced
no snapshot, FaceID did nothing: the event never entered the queue (nohas_snapshot),
so evenlive_hires_mode: alwaysnever ran. Reported in #14.- This matters for setups with
snapshots.required_zones, where Frigate withholds the
snapshot while the person stands plainly in the picture. It could not surface here —
our Frigate has norequired_zones, andno snapshot from Frigateappears zero times
in 14 days of logs. - A recognition from a frame fetched without a snapshot binds nothing to the event: no
sub_label, nobest_score. Without the snapshot nothing says who the event is about,
which is the same reasonlive_hires_mode: alwaysalready behaved that way. It still
counts toward presence and firesfaceid/event. - The poll path still requires a snapshot on purpose: it picks up stragglers up to five
minutes old, and a frame from now no longer shows that person.
v0.22.0 — Measurements without a terminal
- A Tools tab with the measurements that used to need a terminal. Off by default;
switch it on under Settings → Does it work?. It runs three analyses that so far only
existed as scripts: how fast a recognition is, how well each person is covered, and why
events yield no usable face. - This is not a convenience wrapper around the scripts — it could not be.
scripts/is not
in the app image at all (the Dockerfile shipsapp/andstatic/), and the delay
measurement readjournalctl, which does not exist in a container without systemd. For
anyone running FaceID as a Home Assistant app, those analyses were unreachable, not just
inconvenient. - The delay measurement therefore uses a different source than the script: the history
instead of the log. The event's start time is in the Frigate event id anyway, and the
moment the name went out is in the history entry — so it needs neither the log nor camera
access, and answers instantly. - Recognitions from a history scan are excluded from the delay figures rather than silently
averaged in; they lag their event by weeks and would wreck every median. The count of
what was dropped is shown. - Corrected: there is no 3-second floor. README and the notification blueprint said the
name takes 3 seconds at the very best, "because that is how long Frigate takes to produce
the first snapshot". The new tool made it checkable, and 18% of recognitions land below
that, the fastest in 0.8s. The old figure was the median of first attempts in an older
sample, written up as a physical limit. Both measurement paths — the log-based script and
the history — now agree: median 8-9s, fastest around 1s, and the floor varies per event.
Clocks were compared between Frigate and the container first; they match to the second. - The history no longer prints a similarity of 1.0 as if it meant something. Every such
entry was a face that had since been assigned, so it is one of that person's reference
photos and matches itself. It now reads "now assigned: ", without a number. retry_seconds: 1.2(down from 2.5) is confirmed to have worked: attempt 2 went from a
median of 6.5s to 4.0s.- Two long-standing bugs in the background jobs, found while reviewing the new one and
fixed for the history scan and the calibration analysis as well:- A failing import inside a worker killed the thread before the
finallythat clears the
running flag. The job then stayed "running" forever and every later start answered409 already runninguntil a service restart — with that message as the only clue, pointing
at everything except the real cause. Verified by breaking the import on purpose. - Starting a job checked the running flag and set it in two separate steps, so two
simultaneous starts could both get past the guard. Now held under one lock.
- A failing import inside a worker killed the thread before the
v0.21.0
- Camera filter on the review queue and the history, and a choice of how many history
entries to load — suggested and prototyped by @WiredLife in #14, rebuilt here against
the current UI. - The filter only appears once there is more than one camera to choose between. With a
single camera it would be a control that cannot change anything, and an empty dropdown
invites the question of what it is for. - On the review queue it filters clusters: a cluster survives when at least one face in it
came from the selected camera, so a group is never silently cut in half.
v0.20.0
- The notification image now comes from Home Assistant by default, as the relative path
/api/frigate/notifications/<event>/snapshot.jpg. The phone resolves that against its own
Home Assistant connection, so the picture arrives at home and away without any Frigate
address being involved. Needs the Frigate integration with its notification proxy enabled. - This replaces a footgun reported in #13: the user had entered
http://ccab4aaf-frigate:5000
— the add-on hostname, which only resolves inside Home Assistant. The phone fetches the
image itself, so it got nothing, on the local network and off it. That is not an obvious
failure, because the same address works perfectly in every other setting. - The old behaviour is still available (A Frigate address I enter below), now with the
requirement stated where it is needed: the address must be reachable from the phone. - Verified against a real event: the proxy path returns HTTP 200, 45 KB,
image/jpeg,
without a token.
v0.19.5
⚠️ Correcting something I published. 0.19.0 stated that Frigate never sends an MQTT
update when asub_labelis set, and that a blueprint watchingfrigate/eventscould
therefore never see the name. That is wrong. The test behind it was run against an
event that had already ended — where Frigate sends nothing more, regardless of sub_labels.- On a running event, Frigate 0.17.2 forwards the name in the same second FaceID writes
it, asafter.sub_label=["Eli", 0.514]— an array of name and score. Verified over
several real arrivals, log and MQTT capture side by side. - Why a Frigate blueprint can still come up empty: at least one popular one reads
after.data.sub_labels, which isnullon 0.17.2. The name is in the payload, just not
where it is being looked for. If yours shows no name, check that field before assuming
nothing was written. - The blueprint here is unaffected and still useful: it listens to
faceid/event, so it
does not depend on which Frigate version puts the name where, and it carries the score
and the zones as well. Only its explanation was wrong, not its behaviour.
v0.19.4
- Fix: the snapshot only reached Android, not iOS. The two platforms expect the image in
different places — Android readsdata.image, iOS readsdata.attachment.url. Only the
Android field was set, so iPhone users got the name without a picture. Both are sent now;
each platform ignores the other's field. - When no Frigate URL is configured, neither field is emitted at all. An empty
attachment
makes the notification fail outright on iOS, so leaving the key out matters more than it
looks.