Skip to content

Docs/camera adapter guide - #407

Open
fe51 wants to merge 2 commits into
developfrom
docs/camera-adapter-guide
Open

Docs/camera adapter guide#407
fe51 wants to merge 2 commits into
developfrom
docs/camera-adapter-guide

Conversation

@fe51

@fe51 fe51 commented Aug 18, 2026

Copy link
Copy Markdown
Member

Docs only, no code changes.

Why

The camera API has six adapters and a capability model, none of it written down. Anyone plugging in a new camera today has to reconstruct the design from registry.py, base.py and the route
handlers. Two questions have no answer in the repo: do I even need a new adapter (the rtsp,
url and rest adapters cover a lot with config alone), and what exactly must my class
implement
.

What it adds

One guide at pyro_camera_api/pyro_camera_api/camera/adapters/README.md: what an adapter is for,
a table of the six existing ones to check before writing code, the capture() contract, the
capabilities and how far to go, a commented skeleton, and the pitfalls. The root README.md
links to it and gains the missing rest adapter in its list.

It lives in the adapters directory so GitHub renders it when browsing the folder, which is where
you land when you go look at existing adapters. Same convention as tools/ and setup_presets/.

Worth a look later

Documented as-is, not changed here:

  • An implicit contract sits next to the explicit one. Six methods are resolved by hasattr()
    in the routes and declared nowhere in base.py (set_auto_focus, focus_finder,
    start_zoom_focus, get_ptz_preset, set_ptz_preset, reboot_camera). An adapter can
    implement both mixins fully, pass mypy, and still 400 on six endpoints. Worst case is silent:
    without reboot_camera, main.py never starts the stuck detector and logs nothing.
  • Streaming is outside the adapter layer. Stream URLs are built in core/config.py from the
    IP, assuming RTSP on 554 with a Reolink or Linovision path. A camera off that convention
    captures fine and cannot stream.
  • adapter: "reolink" degrades silently. Speed tables are keyed by exact model, so the
    generic value falls back to the 823S2 calibration with only a warning.

@fe51
fe51 requested a review from MateoLostanlen August 18, 2026 16:46
@MateoLostanlen

Copy link
Copy Markdown
Member

Checked every claim in the guide against the code. Docs only, CI green, nothing risky. A few things are inaccurate enough to mislead someone writing an adapter.

focus_finder() is not hasattr()-guarded. The optional-methods table lists it under the methods routes look up with hasattr() and degrade gracefully. routes_focus.py:122 only checks isinstance(cam, FocusMixin), then calls cam.focus_finder(...) at line 149 unguarded. A FocusMixin adapter without focus_finder raises AttributeError and returns 500, not a clean 400. Same correction applies to "still 400 on six endpoints" in the PR description.

The rest row omits response. The table lists url, headers, json_path, encoding, but response (rest.py:50, default "image") is the switch that enables the JSON path. Left out, json_path and encoding are ignored and the capture silently fails.

The url adapter dispatches on the literal string CGIProxy.fcgi (url.py:161), not on the presence of usr/pwd. "It exposes a snapshot URL returning an image directly, use url" is a trap: a credential free public snapshot URL falls into the digest branch and returns None with "requires inline credentials". Worth one sentence.

focus_position is applied once per patrol cycle, after the return to pose 0 (patrol.py:115-124), not on every pass over a preset.

Nits:

  • /control/reboot returns 501, not 400 (routes_control.py:973). Real paths are /control/reboot/{camera_ip} and /control/zoom/{camera_ip}/{level}.
  • "Required configuration" overstates: ip_address falls back to the registry key, poses/azimuths default to []. Only rtsp_url and url actually make build_camera_object return None.
  • Step 3 "export in __init__.py": the registry imports the modules directly and __init__.py currently exports 4 of 6 (no Linovision, no Mock). Convention rather than wiring.

Verified correct: the base.py contract and mixin semantics, isinstance to 400 on the mixin routes, import time registry build, the reolink speed table fallback with warning (routes_control.py:72), stream URLs built in core/config.py from the IP on 554, the stuck detector silently skipped without reboot_camera (main.py:84), both reboot_camera snippets, the Linovision set_auto_focus stub behaviour, and the patrol move_camera("ToPos", idx=pose, speed=50) loop.

Fix the focus_finder row and the response field, the rest is polish. The "is my camera already supported" table is the part that saves real work.

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