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>
This commit is contained in:
Aaron D. Lee 2026-04-03 13:45:15 -04:00
parent 713d16d445
commit c77f732ac7
2 changed files with 58 additions and 1 deletions

View File

@ -340,12 +340,60 @@ class TestPetEventClassification:
processor = EventProcessor.__new__(EventProcessor) processor = EventProcessor.__new__(EventProcessor)
etype, sev, source = processor._classify_event( etype, sev, source = processor._classify_event(
"vigilar/camera/kitchen/pet/detected", "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 etype == EventType.PET_DETECTED
assert sev == Severity.INFO assert sev == Severity.INFO
assert source == "kitchen" 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): def test_wildlife_predator_event(self):
from vigilar.events.processor import EventProcessor from vigilar.events.processor import EventProcessor
from vigilar.constants import EventType, Severity from vigilar.constants import EventType, Severity

View File

@ -129,6 +129,15 @@ class EventProcessor:
# Pet detection # Pet detection
if suffix == "pet/detected": 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 return EventType.PET_DETECTED, Severity.INFO, camera_id
# Wildlife detection — severity depends on threat_level in payload # Wildlife detection — severity depends on threat_level in payload