Skip to content

feat(reolink): detect a motorised lens on static cameras - #431

Open
MateoLostanlen wants to merge 9 commits into
developfrom
feat/motorised-lens-detection
Open

MateoLostanlen wants to merge 9 commits into
developfrom
feat/motorised-lens-detection

Conversation

@MateoLostanlen

Copy link
Copy Markdown
Member

Supersedes #396 by @joseg20 (their commits are kept), rebased on develop with the last review points fixed.

  • Static Reolink cameras are probed with GetZoomFocus instead of being assumed fixed-lens, so bullets with a varifocal lens (RLC-811A, P430) can be zoomed. PTZ cameras are not probed and behave as before.
  • The probe never raises, warns once per camera, and zoom/focus requests have a 5 s timeout.
  • stop_stream restores the zoom the camera had when the stream started instead of 0, and is serialized with start_stream.
  • /control/zoom returns 400 for a fixed-lens camera instead of a silent 200.

joseg20 and others added 8 commits October 8, 2026 17:55
…eras have none

cam_type describes how a camera is mounted, not what optics it carries. A
"static" camera is one that does not pan or tilt, which says nothing about
zoom: Reolink bullets such as the RLC-811A or the P430 sit fixed on their
mast and still ship a motorised varifocal lens.

Ask the camera instead of inferring. GetZoomFocus reports a zoom position
only on models that can drive the lens, so the answer comes from the device
and holds for any model. The probe is cached, since a lens cannot grow a
motor at runtime and zoom commands are frequent. A camera that cannot be
reached is treated as fixed-lens rather than raising, so a network blip
degrades the feature instead of failing the call.

Fixed-lens cameras keep their current behaviour: they report no zoom
position, so the commands stay unsent exactly as before. PTZ cameras are
unaffected.
…route

Two problems raised in review, both real.

A failed GetZoomFocus request was being cached as "no motorised lens". One
transient error would have stranded a PTZ camera without zoom or manual
focus for the rest of the process, long after the camera recovered. An
inconclusive probe now skips the command without caching, so the next call
tries again.

The autofocus route rejected static cameras before ever reaching the
adapter, so the capability check could not take effect through the camera
service. It now asks the camera for its lens the same way, and falls back to
allowing the call on adapters that cannot answer.
… routes

Three points from review.

The probe conflated a camera that could not be reached with one that
answered by rejecting the command: both returned None from
get_focus_level() and neither was cached. A fixed-lens model that rejects
GetZoomFocus outright would have paid an HTTP request and a warning on
every zoom and focus call for the life of the process. The probe now reads
the reply itself: a well-formed non-zero code is the camera settling the
question and is cached, while transport failures and HTTP errors stay
uncached and are retried.

The capability guard moves from the autofocus route to the zoom route.
Lifting it on /focus/focus_finder would let the search run on a static
varifocal camera whose focus_position is unset, and that path opens with
start_zoom_focus(0) — wiping zoom levels set by hand in the field, which is
exactly the state this change exists to respect. routes_focus.py is left
untouched, both for that reason and because #391 rewrites it.

/control/zoom keeps the guard: hasattr only proves the adapter has the
method, so without it the route answered 200 for a command the adapter
dropped, which is the silent failure this PR set out to remove.
…ns probe

- stop_stream restores the zoom the camera had when the stream started
  (0 if unknown) instead of always resetting to 0, so static varifocal
  cameras keep their hand-set detection framing.
- The lens probe never raises: parsing a non-JSON body or a reply without
  ZoomFocus is now inconclusive instead of propagating a 500.
- A failing probe warns once per camera, then logs at debug.
- Only static cameras are probed; PTZ cameras keep sending zoom and focus
  commands unconditionally, as before.
- The probe uses the shared session (legacy TLS adapter) like every other
  Reolink call.
- Drop the unreachable focus_finder guard change.
@codecov

codecov Bot commented Oct 8, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 79.46%. Comparing base (f2dd3b4) to head (a746776).
⚠️ Report is 1 commits behind head on develop.

Additional details and impacted files
@@           Coverage Diff            @@
##           develop     #431   +/-   ##
========================================
  Coverage    79.46%   79.46%           
========================================
  Files            6        6           
  Lines          852      852           
========================================
  Hits           677      677           
  Misses         175      175           
Flag Coverage Δ
unittests 79.46% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@MateoLostanlen

Copy link
Copy Markdown
Member Author

Hi @joseg20

I closed your PR #396 and finished it here in #431 (your commits are kept).

To move forward, could you test it on your camera before we merge? On a static varifocal camera (RLC-811A or P430):

  1. POST /control/zoom/{ip}/{level} moves the lens.
  2. Start a stream, change the zoom, stop the stream: the camera goes back to the zoom it had before the stream.
  3. On a fixed-lens camera, /control/zoom returns 400.

Thanks!

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants