Skip to content

Releases: SkyTechNerds/faceid

v0.24.0

Choose a tag to compare

@NerdyHank NerdyHank released this 15 Sep 09:49
ba5e2b3
  • 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, 0 disables 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/persons and 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 processing by 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 default extensions list is video-only and nothing
    said so, so a folder of images was skipped in silence.

v0.23.0

Choose a tag to compare

@NerdyHank NerdyHank released this 14 Sep 08:54
c913963

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 YAML null, not a missing key, so the .get("url", "") default never applied
    and None.rstrip("/") took out /api/unknowns with a 500. Reproduced before and after.
  • Fixed: a history scan with no input configured failed with KeyError: 'url'. The
    branch was chosen by folder_mode rather 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.
  • DisabledFrigateAPI inherits from FrigateAPI, so frigate_client(...) -> FrigateAPI
    is honest and a method added later cannot silently be missing in folder mode.

v0.22.3

Choose a tag to compare

@NerdyHank NerdyHank released this 13 Sep 13:45
a9dd96f
  • Frigate credentials work in the app at last. frigate_user and frigate_password
    have been offered as options for a long time, but run.sh never 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 options but missing
    from schema, which produced Option 'frigate_user' does not exist in the schema for FaceID on 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 through jq, 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_prefix or mqtt_prefix became YAML null,
    and str(None).strip("/") or "frigate" yields the string "None" — FaceID would have
    subscribed to None/events and never seen an event. They now degrade to the default.

v0.22.2 — One history row per person, and a script-injection fix

Choose a tag to compare

@NerdyHank NerdyHank released this 07 Sep 06:34
  • Security: a person's name could execute JavaScript in the web UI. Captions were
    spliced into a single-quoted JS string inside an onclick attribute. The HTML escaper
    does not escape apostrophes — and even if it did, the browser decodes them before the JS
    parser runs — so a name like O'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 a data- 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
    announced set 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

Choose a tag to compare

@NerdyHank NerdyHank released this 04 Sep 11:23
f867b18
  • live_hires_fallback now 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 (no has_snapshot),
    so even live_hires_mode: always never 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 no required_zones, and no snapshot from Frigate appears zero times
    in 14 days of logs.
  • A recognition from a frame fetched without a snapshot binds nothing to the event: no
    sub_label, no best_score. Without the snapshot nothing says who the event is about,
    which is the same reason live_hires_mode: always already behaved that way. It still
    counts toward presence and fires faceid/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

Choose a tag to compare

@NerdyHank NerdyHank released this 02 Sep 09:30
86db887
  • 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 ships app/ and static/), and the delay
    measurement read journalctl, 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 finally that clears the
      running flag. The job then stayed "running" forever and every later start answered 409 already running until 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.

v0.21.0

Choose a tag to compare

@NerdyHank NerdyHank released this 02 Sep 06:57
  • 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

Choose a tag to compare

@NerdyHank NerdyHank released this 02 Sep 06:06
  • 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

Choose a tag to compare

@NerdyHank NerdyHank released this 31 Aug 08:48
  • ⚠️ Correcting something I published. 0.19.0 stated that Frigate never sends an MQTT
    update when a sub_label is set, and that a blueprint watching frigate/events could
    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, as after.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 is null on 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

Choose a tag to compare

@NerdyHank NerdyHank released this 27 Aug 08:39
  • Fix: the snapshot only reached Android, not iOS. The two platforms expect the image in
    different places — Android reads data.image, iOS reads data.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.