Conversation
onvif-zeep hasn't been updated since 2018. Switch to the actively maintained onvif-zeep-async (v4.0.4) and make the client fully async, aligning with the project's async-first architecture. Also fix the WS-Discovery logging suppression to target the correct logger. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… discovery Many cameras (TP-Link, Hikvision, budget models) reuse the probe's MessageID in responses, causing WSDiscovery to silently drop them as duplicates. Enable relates_to=True to accept these replies. Also probe for both Device and NetworkVideoTransmitter ONVIF types (some cameras only advertise one), and set TTL=4 so multicast probes can cross VLANs. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Two bugs were causing cameras to be missed: 1. WS-Discovery Probe type matching is AND, not OR. Passing [Device, NVT] in a single probe only found cameras advertising both types. Now we send separate probes for each type and merge results, catching cameras that only advertise one. 2. The retry loop broke early as soon as any camera was found on the first attempt, missing slow-responding cameras. All attempt rounds now always run; results are deduplicated. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Make the password parameter optional in info/streams CLI commands. When omitted, securely prompts via getpass so credentials don't leak into shell history or process listings. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ssword Three fixes: - Resolve WSDL directory from inside the onvif package (onvif/wsdl/) instead of relying on data_files placement (site-packages/wsdl/) which is unreliable across installers and platforms. - Add close() to OnvifCameraClient and call it in CLI finally blocks to prevent "Unclosed client session" errors from aiohttp. - Make password optional in info/streams commands; prompt securely via getpass when omitted so credentials don't leak to shell history. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Parse 'host:port' syntax in the ip argument for info/streams commands so '192.168.0.183:2020' works without needing --port. Previously the port was appended again, producing an invalid address like '192.168.0.183:2020:80'. - Call ONVIFCamera.close() which properly closes all service transports and the snapshot client, fixing "Unclosed client session" errors from aiohttp. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
These methods are coroutines in onvif-zeep-async (unlike the sync onvif-zeep) and must be awaited. Without await they returned a coroutine object instead of the service, causing 'coroutine object has no attribute GetDeviceInformation'. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #28 +/- ##
==========================================
+ Coverage 82.01% 82.49% +0.48%
==========================================
Files 94 101 +7
Lines 8177 8707 +530
==========================================
+ Hits 6706 7183 +477
- Misses 1471 1524 +53 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
lan17
added a commit
that referenced
this pull request
Feb 23, 2026
Add end-to-end ONVIF support: automatic camera discovery via WS-Discovery, credential-authenticated probing for device info and media profiles, and a guided UI wizard that turns discovered cameras into RTSP-backed camera configs with a few clicks. - **Discovery** (`discovery.py`): WS-Discovery client that probes for both `Device` and `NetworkVideoTransmitter` ONVIF types separately (WS-Discovery AND-matches within a single probe), merges and deduplicates results. Handles quirky cameras (TP-Link, Hikvision, budget models) that reuse the probe MessageID by enabling `relates_to=True`. Configurable TTL (default 4) for cross-VLAN multicast. - **Client** (`client.py`): Async ONVIF camera client wrapping `onvif-zeep-async` — fetches device info, media profiles, and RTSP stream URIs. Resolves WSDL path from the installed package rather than fragile `data_files` placement. - **Service** (`service.py`): Shared orchestration layer used by both the API and CLI. Manages client lifecycle (including best-effort close with bounded timeout), normalizes credentials, and maps exceptions into typed errors (`OnvifDiscoverError`, `OnvifProbeError`, `OnvifProbeTimeoutError`). - **CLI** (`cli.py` / `__main__.py`): Standalone `python -m homesec.onvif` CLI with `discover`, `info`, and `streams` subcommands. Passwords are prompted via `getpass` when omitted, and `host:port` syntax is parsed automatically. - `POST /api/v1/onvif/discover` — triggers a WS-Discovery scan, returns discovered cameras. - `POST /api/v1/onvif/probe` — probes a specific camera with credentials, returns device info, media profiles, and per-profile RTSP stream URIs. Timeout and auth errors mapped to appropriate HTTP status codes (504/502). Three-step wizard integrated into the Cameras page: 1. **Discover** — scans the network, lists found cameras with IP/scope info. 2. **Probe** — user enters ONVIF credentials + port (inferred from discovery xaddr), fetches device info and available media profiles. 3. **Select Stream** — pick a profile, review the RTSP URI, name the camera, and create it as an RTSP-backed camera config. State managed via `useReducer` with typed actions. Presentation helpers handle camera name derivation, port inference from xaddrs, and credential injection into RTSP URIs. - **Backend**: 1,383 lines of test coverage across discovery, client, service, CLI, and API routes. Tests mock WS-Discovery and ONVIF internals to validate probe flows, error handling, timeout behavior, credential normalization, and CLI argument parsing. - **Frontend**: Wizard integration tests (330 lines) covering the full discover → probe → create flow, plus unit tests for the API client, parsing utilities, and presentation helpers. - [ ] Run `pytest` — all new and existing backend tests pass - [ ] Run `npm test` in `ui/` — all new and existing frontend tests pass - [ ] Manual: run `python -m homesec.onvif discover` on a network with ONVIF cameras and verify cameras are found - [ ] Manual: open the UI, click "ONVIF Discovery", complete the wizard end-to-end to create a camera ---------
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Add end-to-end ONVIF support: automatic camera discovery via WS-Discovery, credential-authenticated probing for device info and media profiles, and a guided UI wizard that turns discovered cameras into RTSP-backed camera configs with a few clicks.
Backend —
homesec.onvifpackagediscovery.py): WS-Discovery client that probes for bothDeviceandNetworkVideoTransmitterONVIF types separately (WS-Discovery AND-matches within a single probe), merges and deduplicates results. Handles quirky cameras (TP-Link, Hikvision, budget models) that reuse the probe MessageID by enablingrelates_to=True. Configurable TTL (default 4) for cross-VLAN multicast.client.py): Async ONVIF camera client wrappingonvif-zeep-async— fetches device info, media profiles, and RTSP stream URIs. Resolves WSDL path from the installed package rather than fragiledata_filesplacement.service.py): Shared orchestration layer used by both the API and CLI. Manages client lifecycle (including best-effort close with bounded timeout), normalizes credentials, and maps exceptions into typed errors (OnvifDiscoverError,OnvifProbeError,OnvifProbeTimeoutError).cli.py/__main__.py): Standalonepython -m homesec.onvifCLI withdiscover,info, andstreamssubcommands. Passwords are prompted viagetpasswhen omitted, andhost:portsyntax is parsed automatically.API — FastAPI routes
POST /api/v1/onvif/discover— triggers a WS-Discovery scan, returns discovered cameras.POST /api/v1/onvif/probe— probes a specific camera with credentials, returns device info, media profiles, and per-profile RTSP stream URIs. Timeout and auth errors mapped to appropriate HTTP status codes (504/502).UI — ONVIF Discovery Wizard
Three-step wizard integrated into the Cameras page:
State managed via
useReducerwith typed actions. Presentation helpers handle camera name derivation, port inference from xaddrs, and credential injection into RTSP URIs.Tests
Test plan
pytest— all new and existing backend tests passnpm testinui/— all new and existing frontend tests passpython -m homesec.onvif discoveron a network with ONVIF cameras and verify cameras are found