From c77f732ac785987f0b9f7ab29254bb89cb09d8ad Mon Sep 17 00:00:00 2001 From: "Aaron D. Lee" Date: Fri, 3 Apr 2026 13:45:15 -0400 Subject: [PATCH] Differentiate PET_ESCAPE and UNKNOWN_ANIMAL events by zone and identity MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- tests/unit/test_events.py | 50 ++++++++++++++++++++++++++++++++++++- vigilar/events/processor.py | 9 +++++++ 2 files changed, 58 insertions(+), 1 deletion(-) diff --git a/tests/unit/test_events.py b/tests/unit/test_events.py index 689ea30..6e6e023 100644 --- a/tests/unit/test_events.py +++ b/tests/unit/test_events.py @@ -340,12 +340,60 @@ class TestPetEventClassification: processor = EventProcessor.__new__(EventProcessor) etype, sev, source = processor._classify_event( "vigilar/camera/kitchen/pet/detected", - {"pet_name": "Angel", "confidence": 0.92}, + {"pet_id": "p1", "pet_name": "Angel", "confidence": 0.92, "camera_location": "INTERIOR"}, # noqa: E501 ) assert etype == EventType.PET_DETECTED assert sev == Severity.INFO assert source == "kitchen" + def test_pet_escape_exterior(self): + """Known pet in exterior zone → PET_ESCAPE/ALERT.""" + from vigilar.events.processor import EventProcessor + from vigilar.constants import EventType, Severity + processor = EventProcessor.__new__(EventProcessor) + etype, sev, source = processor._classify_event( + "vigilar/camera/front/pet/detected", + {"pet_id": "p1", "pet_name": "Angel", "camera_location": "EXTERIOR"}, + ) + assert etype == EventType.PET_ESCAPE + assert sev == Severity.ALERT + + def test_pet_escape_transition(self): + """Known pet in transition zone → PET_ESCAPE/ALERT.""" + from vigilar.events.processor import EventProcessor + from vigilar.constants import EventType, Severity + processor = EventProcessor.__new__(EventProcessor) + etype, sev, source = processor._classify_event( + "vigilar/camera/garage/pet/detected", + {"pet_id": "p1", "pet_name": "Milo", "camera_location": "TRANSITION"}, + ) + assert etype == EventType.PET_ESCAPE + assert sev == Severity.ALERT + + def test_unknown_animal(self): + """No pet_id → UNKNOWN_ANIMAL/WARNING.""" + from vigilar.events.processor import EventProcessor + from vigilar.constants import EventType, Severity + processor = EventProcessor.__new__(EventProcessor) + etype, sev, source = processor._classify_event( + "vigilar/camera/kitchen/pet/detected", + {"species": "cat", "camera_location": "INTERIOR"}, + ) + assert etype == EventType.UNKNOWN_ANIMAL + assert sev == Severity.WARNING + + def test_known_pet_interior(self): + """Known pet in interior → PET_DETECTED/INFO.""" + from vigilar.events.processor import EventProcessor + from vigilar.constants import EventType, Severity + processor = EventProcessor.__new__(EventProcessor) + etype, sev, source = processor._classify_event( + "vigilar/camera/kitchen/pet/detected", + {"pet_id": "p1", "pet_name": "Angel", "camera_location": "INTERIOR"}, + ) + assert etype == EventType.PET_DETECTED + assert sev == Severity.INFO + def test_wildlife_predator_event(self): from vigilar.events.processor import EventProcessor from vigilar.constants import EventType, Severity diff --git a/vigilar/events/processor.py b/vigilar/events/processor.py index 47c0209..2d75c54 100644 --- a/vigilar/events/processor.py +++ b/vigilar/events/processor.py @@ -129,6 +129,15 @@ class EventProcessor: # Pet detection if suffix == "pet/detected": + pet_id = payload.get("pet_id") + camera_location = payload.get("camera_location", "INTERIOR") + + if not pet_id: + return EventType.UNKNOWN_ANIMAL, Severity.WARNING, camera_id + + if camera_location in ("EXTERIOR", "TRANSITION"): + return EventType.PET_ESCAPE, Severity.ALERT, camera_id + return EventType.PET_DETECTED, Severity.INFO, camera_id # Wildlife detection — severity depends on threat_level in payload