Commit Graph

57 Commits

Author SHA1 Message Date
adlee-was-taken
038ab0af12 fix: address final-review items (status endpoint, docs, tests)
Follow-up to the holistic review of the PIN-unification branch:

- /system/status now reads the real arm state from the arm_state_log
  table via get_current_arm_state, instead of returning a hardcoded
  'DISARMED' stub. Without this, polling after the new async 202
  arm/disarm flow was a UX dead-end — clients never saw the state
  change they just requested. DB read failures degrade gracefully.

- Operator guide: correct the claim that 'vigilar config set-pin'
  populates recovery_passphrase_hash. It doesn't. recovery_passphrase
  _hash has no CLI helper today; it must be set manually.

- Tests: add a fail-closed regression for verify_pin on malformed
  stored hashes, and a companion test confirming the deprecation
  warning stays silent on a fully migrated config.

All address specific review comments on the branch; no scope creep.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 12:26:30 -04:00
adlee-was-taken
ac52198b35 test: end-to-end PIN unification regression guard (issue #2) 2026-04-05 12:08:32 -04:00
adlee-was-taken
9f203d8ce5 fix(web): align arm/disarm 202 response shape with {"ok": true} convention
Follow-up to efd5c4a. The plan invented {"accepted": True, ...} for
the new 202 responses, but every other 2xx endpoint in the Flask app
returns {"ok": True, ...} — including cameras.py:108 which is direct
prior art for a 202 with the same convention. The shared JS helper
at static/js/settings.js:54 does 'if (resp.ok && result.ok)' and was
falling into the error branch on our success responses, showing a
bogus "Save failed" toast after every arm/disarm click.

Keep the 202 status. Swap the body key from 'accepted' to 'ok'.
No JS change needed.
2026-04-05 12:03:05 -04:00
adlee-was-taken
efd5c4ad80 fix(web): arm/disarm actually transition the FSM via MQTT (issue #2)
Was: /system/api/arm verified the PIN against [security] pin_hash and
returned {ok: true} without ever calling the FSM. State never changed.
Now: the endpoint publishes a SYSTEM_ARM_REQUEST message to the local
MQTT broker. The event processor (see previous commit) picks it up,
ArmStateFSM verifies the PIN via alerts.pin.verify_pin and performs
the transition. Response is 202 Accepted; clients poll /system/status
for the new state.

Design: PIN travels over localhost-only MQTT, which matches the
existing trust boundary for the internal bus.
2026-04-05 12:00:24 -04:00
adlee-was-taken
c275404e4e refactor(events): drop forward-ref quote and test triggered_by default
Code review follow-up on f4d66dd:
- _handle_arm_request signature used "ArmStateFSM" as a string forward
  reference even though the type is imported at module top.
  _handle_event uses the bare form; match it for consistency.
- Add a test asserting that omitting triggered_by in an arm-request
  payload defaults to "unknown". That value feeds the audit log, so
  it deserves explicit regression coverage.

No behavior change.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 11:57:26 -04:00
adlee-was-taken
f4d66dd5df feat(events): processor handles SYSTEM_ARM_REQUEST over MQTT
Adds _handle_arm_request and a dedicated bus.subscribe on
Topics.SYSTEM_ARM_REQUEST. Payload {mode, pin, triggered_by} is
dispatched to ArmStateFSM.transition, which verifies the PIN via
alerts.pin.verify_pin and performs the state change.

This is the missing link for web /system/api/arm to actually move
the system into an armed state. Part of issue #2.
2026-04-05 11:51:05 -04:00
adlee-was-taken
c9dd348850 feat(config): deprecation warning for [system] arm_pin_hash
If a config still has the legacy [system] arm_pin_hash set but no
[security] pin_hash, load_config logs a WARNING telling the operator
to re-run 'vigilar config set-pin'. The legacy field is still parsed
(so old configs don't fail validation) but ignored at runtime.

Part of issue #2 PIN hashing unification.
2026-04-05 11:46:21 -04:00
adlee-was-taken
8ffe1a38ed fix(cli): set-pin emits PBKDF2 under [security] pin_hash (issue #2)
Was: HMAC-SHA256(random, pin) written to [system] arm_pin_hash —
no verifier in the codebase accepted this output.
Now: PBKDF2-SHA256 via alerts.pin.hash_pin written to [security]
pin_hash, matching what the web and FSM paths verify against.

Also fixes show_cmd to redact the new location.
2026-04-05 11:37:59 -04:00
adlee-was-taken
7fda351c02 fix(events): ArmStateFSM uses PBKDF2 via alerts.pin (issue #2)
Was: unsalted SHA-256 read from [system] arm_pin_hash.
Now: PBKDF2-SHA256 600k iterations read from [security] pin_hash,
matching the web arm/disarm path and the alerts/pin module.

Also drops the redundant pin re-hash on the arm_state_log audit row
(a fresh PBKDF2 salt made the column valueless for traceability).

Part of issue #2 PIN hashing unification.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 11:26:07 -04:00
adlee-was-taken
e657f2bfbc test: isolate VIGILAR_CONFIG via autouse session fixture
Some web endpoint handlers call _save_and_reload(), which resolves the
target path via VIGILAR_CONFIG env var with a fallback to the relative
"config/vigilar.toml". Any test exercising such an endpoint without
setting the env var rewrites the repo's committed config file via a
Pydantic model_dump round-trip, stripping comments and non-default
fields. The culprit discovered was test_reset_pin_correct_passphrase
in tests/unit/test_system_pin.py.

Add an autouse session-scoped fixture in tests/conftest.py that points
VIGILAR_CONFIG at a path inside pytest's session tmp dir so no test
can touch the real file. Restore the previous env var value on teardown.

Fixes #3.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 10:55:42 -04:00
Aaron D. Lee
bdfadbb829 feat(Q6): timelapse generator, schedules, and web routes
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-03 19:08:29 -04:00
Aaron D. Lee
622af22642 feat(Q1): highlight reel event scoring and FFmpeg clip assembly
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-03 19:07:12 -04:00
Aaron D. Lee
b4dbb41624 feat(Q4): kiosk ambient mode with camera rotation, alert takeover, dimming
Add GET /kiosk/ambient route and standalone fullscreen template with
rotating camera snapshots (crossfade), top bar clock/date, pet status
bottom bar, SSE-driven alert takeover with HLS playback, configurable
screen dimming, and 6-hour auto-refresh.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-03 19:06:57 -04:00
Aaron D. Lee
d69bf6d6af feat(Q1,Q4): add HighlightsConfig, KioskConfig, HIGHLIGHT/TIMELAPSE triggers
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-03 19:06:08 -04:00
Aaron D. Lee
23d5bf062a feat(S3): visitors blueprint with profiles, visits, labeling, privacy controls
Add /visitors/ blueprint with REST API endpoints for listing profiles and
visits, labeling (with consent gate), household linking, ignore/unignore,
and cascading forget. Register blueprint in app.py. Add dashboard and profile
detail templates (Bootstrap 5 dark, tab navigation, fetch-based JS). All six
API tests pass (339 total).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 19:01:33 -04:00
Aaron D. Lee
a5ddc53cf0 feat(S3): FaceRecognizer with in-memory embedding matching
Add FaceRecognizer class that loads face encodings from the database,
supports runtime add_encoding(), and matches new encodings by L2 distance.
face_recognition import is deferred so the class works without dlib installed.
FaceResult dataclass carries profile_id, name, confidence, crop, and bbox.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 18:59:07 -04:00
Aaron D. Lee
5a438fdb32 feat(S3): face profile, embedding, and visit CRUD queries
Add create/get/update/delete_cascade for face_profiles, insert/get for
face_embeddings, and insert/get/get_active for visits. All seven query
functions covered by unit tests (327 passing).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 18:58:19 -04:00
Aaron D. Lee
a44187d0f1 feat(S5): pet rule CRUD routes with validation and templates 2026-04-03 18:53:50 -04:00
Aaron D. Lee
931b453ba9 feat(S5): pet rule engine with condition evaluation and cooldown 2026-04-03 18:52:38 -04:00
Aaron D. Lee
fac51a7c8a feat(S5): pet rule CRUD query functions 2026-04-03 18:51:47 -04:00
Aaron D. Lee
cdc13e05f6 feat(Q5): package event queries and tracker state machine
Add insert/get/update queries for package_events table, and
notification content for PACKAGE_DELIVERED and PACKAGE_REMINDER events.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 18:47:40 -04:00
Aaron D. Lee
31757f410a feat(Q5): package delivery state machine with sunset-aware reminders
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 18:46:59 -04:00
Aaron D. Lee
8bf7900324 feat(Q3): wildlife journal blueprint with API routes and template
Add wildlife_bp with sightings, stats, frequency, and CSV export
endpoints; Bootstrap 5 dark journal template with live-updating
summary cards, species bars, time-of-day frequency chart, and
paginated/filterable sightings table.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-03 18:46:32 -04:00
Aaron D. Lee
8c2a8ea1c5 feat(Q3): wildlife journal query functions
Add get_wildlife_sightings_paginated, get_wildlife_stats, and
get_wildlife_frequency to queries.py with full test coverage.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-03 18:46:25 -04:00
Aaron D. Lee
66a53f0cd8 feat(Q2): heatmap generation with bbox accumulation and colormap
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 18:46:00 -04:00
Aaron D. Lee
7ccd818a93 feat(Q3): Open-Meteo weather fetcher with hourly caching 2026-04-03 18:42:52 -04:00
Aaron D. Lee
e75a9a9d71 feat(Q5): NOAA sunset calculator (stdlib only) 2026-04-03 18:42:26 -04:00
Aaron D. Lee
a5dd15d0a1 feat(Q5): add package event types and package_events table 2026-04-03 18:41:31 -04:00
Aaron D. Lee
38ff219364 feat(Q3): add temperature, conditions, bbox columns to wildlife_sightings 2026-04-03 18:40:55 -04:00
Aaron D. Lee
4097ee9dd3 feat(Q3): add LocationConfig for latitude/longitude 2026-04-03 18:40:36 -04:00
Aaron D. Lee
c3c743ec74 feat(F1): configure syslog audit logging for vigilar.alerts
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 18:36:57 -04:00
Aaron D. Lee
f33b82cc83 feat(F1): integrate Web Push notifications into event processor
Import send_alert into processor.py, store engine as self._engine after
init_db(), extend _execute_action() to accept event_type/severity/source_id
and call send_alert for alert_all and push_and_record actions, and pass
those params from _handle_event().

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-03 18:01:11 -04:00
Aaron D. Lee
2c79e0c044 feat(F1): notification content mapping and Web Push sender
Add vigilar/alerts/sender.py with _CONTENT_MAP for human-readable push
notification titles/bodies, build_notification(), and send_alert() which
retrieves VAPID key, iterates push subscriptions, calls pywebpush, and
logs results to alert_log with auto-cleanup of expired (410) endpoints.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-03 18:01:07 -04:00
Aaron D. Lee
602945e99d feat(F2): recording list, download (decrypt), and delete API
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-03 17:42:35 -04:00
Aaron D. Lee
1b7f77b298 feat(F2): integrate AES-256-CTR encryption into AdaptiveRecorder
After FFmpeg finishes writing, _stop_ffmpeg() now reads VIGILAR_ENCRYPTION_KEY
and encrypts the MP4 to .vge format via encrypt_file(), updating the returned
RecordingSegment to reflect the encrypted file path and size.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-03 17:41:39 -04:00
Aaron D. Lee
e630c206b2 feat(F4): PIN verification on arm/disarm + reset-pin endpoint
Adds PIN checking to arm/disarm endpoints using verify_pin() against
cfg.security.pin_hash, and a new POST /system/api/reset-pin endpoint
that verifies the recovery passphrase before updating the PIN hash.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-03 17:40:01 -04:00
Aaron D. Lee
f8d28cf78e feat(F2): AES-256-CTR encryption module for recordings
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-03 17:39:40 -04:00
Aaron D. Lee
0544f7218a feat(F4): add SecurityConfig model to VigilarConfig 2026-04-03 17:38:10 -04:00
Aaron D. Lee
3f2a59c11e feat(F4): add PIN hashing utilities with PBKDF2-SHA256 2026-04-03 17:37:42 -04:00
Aaron D. Lee
c77f732ac7 Differentiate PET_ESCAPE and UNKNOWN_ANIMAL events by zone and identity
Replace the flat pet/detected handler with context-aware classification:
unknown animals (no pet_id) → UNKNOWN_ANIMAL/WARNING, known pets in
exterior/transition zones → PET_ESCAPE/ALERT, known pets indoors →
PET_DETECTED/INFO. Adds four new unit tests covering all three paths.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-03 13:45:15 -04:00
Aaron D. Lee
94c5184f46 Add pets web blueprint with API endpoints
Implements all /pets/* routes (register, status, sightings, wildlife,
unlabeled crops, label, upload, update, delete, train, training-status,
highlights), registers the blueprint in app.py, adds a placeholder
dashboard template, and covers the API with 11 passing unit tests.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-03 13:27:46 -04:00
Aaron D. Lee
2b3a4ba853 Add pet and wildlife counts to daily digest
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-03 13:24:33 -04:00
Aaron D. Lee
45007dcac2 Add crop manager for staging and training image lifecycle
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-03 13:22:26 -04:00
Aaron D. Lee
d0acf7703c Handle pet and wildlife events in event processor 2026-04-03 13:20:46 -04:00
Aaron D. Lee
53daf58c78 Add camera_location filtering to alert profile matching
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-03 13:20:37 -04:00
Aaron D. Lee
e48ba305ea Add pet ID model trainer with MobileNetV3-Small transfer learning
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-03 13:19:12 -04:00
Aaron D. Lee
c7f9304f2a Add pet ID classifier with species-filtered identification
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-03 13:18:42 -04:00
Aaron D. Lee
13b7c2a219 Add wildlife threat classification with size heuristics
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-03 13:17:21 -04:00
Aaron D. Lee
131eed73b1 Add YOLOv8 unified detector with class classification
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-03 13:15:31 -04:00
Aaron D. Lee
d83710b839 Add pet and wildlife database query functions
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-03 13:13:56 -04:00