The camera API can serve frozen images for hours while everything looks healthy.
Seen on 2026-09-16 at chateau-eau-douadic (Pi 192.168.255.170, two Reolink cameras):
latest_image returned the exact same JPEG for all 8 camera/pose pairs over 12 minutes
- camera 192.168.1.11 served a night IR frame at 12:05, while a live
capture on the same camera at the same second returned a sharp daylight image
- the engine kept scoring those dead frames and logging
No wildfire, so the site was blind with no error anywhere
Cause
The patrol thread was alive but blocked in a PTZ call. In camera/adapters/reolink.py the 9 requests.post calls have no timeout, so move_camera, called by patrol_loop, can wait forever. The stop flag was set meanwhile and the frozen thread never saw it.
From there the API cannot recover:
start_patrol only checks thread.is_alive(), so it answers already_running and never starts a new thread
patrol_status also checks the flag, so it answers patrol_running: false
last_images is never updated again, and latest_image carries no timestamp, so nobody can tell the frame is old
- the stuck detector skips when the patrol is not running, which is exactly this case
What to change
- Add a default timeout to every request in the Reolink adapter, with one helper instead of 9 call sites.
- Share one "is patrol running" check, and let
start_patrol replace a thread that is alive but has its flag set.
- Store the capture time with each image in
last_images, expose it, and let the engine refuse a frame that is too old.
- Do not disable the stuck detector when the patrol is not running.
- Engine side:
core.py stops the patrol only when it is already stopped (inverted condition), and is_day is overwritten at each pose, so the last camera of the loop decides day or night for the whole site.
Workaround
Restart the camera API container. The engine starts a clean patrol on the next cycle.
The camera API can serve frozen images for hours while everything looks healthy.
Seen on 2026-09-16 at chateau-eau-douadic (Pi 192.168.255.170, two Reolink cameras):
latest_imagereturned the exact same JPEG for all 8 camera/pose pairs over 12 minutescaptureon the same camera at the same second returned a sharp daylight imageNo wildfire, so the site was blind with no error anywhereCause
The patrol thread was alive but blocked in a PTZ call. In
camera/adapters/reolink.pythe 9requests.postcalls have no timeout, somove_camera, called bypatrol_loop, can wait forever. The stop flag was set meanwhile and the frozen thread never saw it.From there the API cannot recover:
start_patrolonly checksthread.is_alive(), so it answersalready_runningand never starts a new threadpatrol_statusalso checks the flag, so it answerspatrol_running: falselast_imagesis never updated again, andlatest_imagecarries no timestamp, so nobody can tell the frame is oldWhat to change
start_patrolreplace a thread that is alive but has its flag set.last_images, expose it, and let the engine refuse a frame that is too old.core.pystops the patrol only when it is already stopped (inverted condition), andis_dayis overwritten at each pose, so the last camera of the loop decides day or night for the whole site.Workaround
Restart the camera API container. The engine starts a clean patrol on the next cycle.