fix: wire package/visitor events, bbox payloads, reel/timelapse scheduling
- Add `requests` to pyproject.toml dependencies (used by detection/weather.py) - Wire PACKAGE_DELIVERED/REMINDER/COLLECTED and KNOWN/UNKNOWN_VISITOR event types in EventProcessor._classify_event - Add normalized bbox to all detection payloads in camera worker (domestic_animal, wildlife, person, vehicle) - Integrate highlight reel and timelapse scheduling in HealthMonitor.run() Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
3289f874ab
commit
4873d36194
@ -24,6 +24,7 @@ dependencies = [
|
|||||||
"py-vapid>=1.9.0",
|
"py-vapid>=1.9.0",
|
||||||
"ultralytics>=8.2.0",
|
"ultralytics>=8.2.0",
|
||||||
"torchvision>=0.18.0",
|
"torchvision>=0.18.0",
|
||||||
|
"requests>=2.32.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[project.optional-dependencies]
|
[project.optional-dependencies]
|
||||||
|
|||||||
@ -275,6 +275,12 @@ def run_camera_worker(
|
|||||||
detections = yolo_detector.detect(frame)
|
detections = yolo_detector.detect(frame)
|
||||||
for det in detections:
|
for det in detections:
|
||||||
category = YOLODetector.classify(det)
|
category = YOLODetector.classify(det)
|
||||||
|
frame_h, frame_w = frame.shape[:2]
|
||||||
|
bx, by, bw, bh = det.bbox
|
||||||
|
norm_bbox = [
|
||||||
|
round(bx / frame_w, 4), round(by / frame_h, 4),
|
||||||
|
round(bw / frame_w, 4), round(bh / frame_h, 4),
|
||||||
|
]
|
||||||
if category == "domestic_animal":
|
if category == "domestic_animal":
|
||||||
# Crop for pet ID and staging
|
# Crop for pet ID and staging
|
||||||
x, y, w, h = det.bbox
|
x, y, w, h = det.bbox
|
||||||
@ -295,6 +301,7 @@ def run_camera_worker(
|
|||||||
"confidence": round(det.confidence, 3),
|
"confidence": round(det.confidence, 3),
|
||||||
"camera_location": camera_cfg.location,
|
"camera_location": camera_cfg.location,
|
||||||
"crop_path": crop_path,
|
"crop_path": crop_path,
|
||||||
|
"bbox": norm_bbox,
|
||||||
}
|
}
|
||||||
if pet_result and pet_result.is_identified:
|
if pet_result and pet_result.is_identified:
|
||||||
payload["pet_id"] = pet_result.pet_id
|
payload["pet_id"] = pet_result.pet_id
|
||||||
@ -320,6 +327,7 @@ def run_camera_worker(
|
|||||||
confidence=round(det.confidence, 3),
|
confidence=round(det.confidence, 3),
|
||||||
camera_location=camera_cfg.location,
|
camera_location=camera_cfg.location,
|
||||||
crop_path=None,
|
crop_path=None,
|
||||||
|
bbox=norm_bbox,
|
||||||
)
|
)
|
||||||
|
|
||||||
elif category == "person":
|
elif category == "person":
|
||||||
@ -327,6 +335,7 @@ def run_camera_worker(
|
|||||||
Topics.camera_motion_start(camera_id),
|
Topics.camera_motion_start(camera_id),
|
||||||
detection="person",
|
detection="person",
|
||||||
confidence=round(det.confidence, 3),
|
confidence=round(det.confidence, 3),
|
||||||
|
bbox=norm_bbox,
|
||||||
)
|
)
|
||||||
|
|
||||||
elif category == "vehicle":
|
elif category == "vehicle":
|
||||||
@ -334,6 +343,7 @@ def run_camera_worker(
|
|||||||
Topics.camera_motion_start(camera_id),
|
Topics.camera_motion_start(camera_id),
|
||||||
detection="vehicle",
|
detection="vehicle",
|
||||||
confidence=round(det.confidence, 3),
|
confidence=round(det.confidence, 3),
|
||||||
|
bbox=norm_bbox,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Heartbeat every 10 seconds
|
# Heartbeat every 10 seconds
|
||||||
|
|||||||
@ -183,6 +183,20 @@ class EventProcessor:
|
|||||||
else:
|
else:
|
||||||
return EventType.WILDLIFE_PASSIVE, Severity.INFO, camera_id
|
return EventType.WILDLIFE_PASSIVE, Severity.INFO, camera_id
|
||||||
|
|
||||||
|
# Package detection
|
||||||
|
if suffix == "package/delivered":
|
||||||
|
return EventType.PACKAGE_DELIVERED, Severity.INFO, camera_id
|
||||||
|
if suffix == "package/reminder":
|
||||||
|
return EventType.PACKAGE_REMINDER, Severity.WARNING, camera_id
|
||||||
|
if suffix == "package/collected":
|
||||||
|
return EventType.PACKAGE_COLLECTED, Severity.INFO, camera_id
|
||||||
|
|
||||||
|
# Visitor recognition
|
||||||
|
if suffix == "visitor/known":
|
||||||
|
return EventType.KNOWN_VISITOR, Severity.INFO, camera_id
|
||||||
|
if suffix == "visitor/unknown":
|
||||||
|
return EventType.UNKNOWN_VISITOR, Severity.INFO, camera_id
|
||||||
|
|
||||||
# Ignore heartbeats etc.
|
# Ignore heartbeats etc.
|
||||||
return None, None, None
|
return None, None, None
|
||||||
|
|
||||||
|
|||||||
@ -73,6 +73,9 @@ class HealthMonitor:
|
|||||||
log.info("Health monitor started")
|
log.info("Health monitor started")
|
||||||
last_disk_check = 0
|
last_disk_check = 0
|
||||||
last_mqtt_check = 0
|
last_mqtt_check = 0
|
||||||
|
last_highlight_check = 0
|
||||||
|
last_timelapse_check = 0
|
||||||
|
highlight_generated_today = False
|
||||||
|
|
||||||
while not shutdown:
|
while not shutdown:
|
||||||
now = time.monotonic()
|
now = time.monotonic()
|
||||||
@ -91,6 +94,37 @@ class HealthMonitor:
|
|||||||
self._update_check(mqtt)
|
self._update_check(mqtt)
|
||||||
last_mqtt_check = now
|
last_mqtt_check = now
|
||||||
|
|
||||||
|
# Highlight reel generation (daily)
|
||||||
|
if hasattr(self._cfg, 'highlights') and self._cfg.highlights.enabled:
|
||||||
|
if now - last_highlight_check >= 60:
|
||||||
|
last_highlight_check = now
|
||||||
|
import datetime
|
||||||
|
current_time = datetime.datetime.now().strftime("%H:%M")
|
||||||
|
if current_time == self._cfg.highlights.generate_time and not highlight_generated_today:
|
||||||
|
highlight_generated_today = True
|
||||||
|
try:
|
||||||
|
from vigilar.highlights.reel import generate_daily_reel
|
||||||
|
from vigilar.storage.db import get_db_path, get_engine
|
||||||
|
engine = get_engine(get_db_path(self._cfg.system.data_dir))
|
||||||
|
yesterday = datetime.date.today() - datetime.timedelta(days=1)
|
||||||
|
generate_daily_reel(engine, self._cfg.system.recordings_dir,
|
||||||
|
yesterday, self._cfg.highlights)
|
||||||
|
except Exception:
|
||||||
|
log.exception("Highlight reel generation failed")
|
||||||
|
if current_time == "00:00":
|
||||||
|
highlight_generated_today = False
|
||||||
|
|
||||||
|
# Timelapse schedule check (every 60s)
|
||||||
|
if now - last_timelapse_check >= 60:
|
||||||
|
last_timelapse_check = now
|
||||||
|
try:
|
||||||
|
from vigilar.highlights.timelapse import check_schedules
|
||||||
|
from vigilar.storage.db import get_db_path, get_engine
|
||||||
|
engine = get_engine(get_db_path(self._cfg.system.data_dir))
|
||||||
|
check_schedules(engine, self._cfg.system.recordings_dir)
|
||||||
|
except Exception:
|
||||||
|
log.exception("Timelapse schedule check failed")
|
||||||
|
|
||||||
self._publish_status()
|
self._publish_status()
|
||||||
time.sleep(10)
|
time.sleep(10)
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user