Usage¶
Assuming that you've followed the installation steps,
you're now ready to use this package. uiprotect is an async library, so the
examples below drive an event loop with asyncio.run.
Command line¶
Most day-to-day tasks are available through the bundled CLI:
| Bash | |
|---|---|
See the Command Line reference for the full list of subcommands.
Connecting from Python¶
Instantiate ProtectApiClient with your console's address and a local-access
user, call update() once to fetch the bootstrap snapshot, then read devices
off the cached bootstrap:
About Ubiquiti SSO accounts
Ubiquiti SSO (cloud) accounts are not supported. Use a local-access user. Driving the cloud owner account over the public Internet is a security risk and there is no MFA support.
Subscribing to events¶
The typed event stream delivers (ProtectEvent, EventChange) pairs. It is
backed by the Public Integration API, so the client must be configured with
an api_key and update_public() must have run at least once before calling
subscribe_events:
The lower-level subscribe_websocket continues to deliver raw
WSSubscriptionMessage frames for advanced callers over the private API and
does not require an API key. The parallel subscribe_events_websocket drives
the Public Integration API WebSocket and does require an API key. See the
project README for the full notes on the typed event contract.
Camera RTSPS streams¶
RTSPS stream URLs live on the camera as PublicCamera.rtsps_streams. The
library owns their entire lifecycle: update_public() primes them, so a
consumer reads the field synchronously and carries no fetch/cache code:
| Python | |
|---|---|
Priming is always run by update_public(): connected cameras without
streams are fetched under a bounded-concurrency semaphore, best-effort (one
slow/offline camera is skipped, never aborting the rest), and disconnected
cameras are skipped. get_camera_rtsps_streams(camera_id) is a flag-free fetch
primitive used internally — it issues a plain GET with no cache side-effects.
The field stays correct from the sources the client controls — stream create/delete is not signalled over the WebSocket, so passive observation is impossible:
- Write-through.
create_camera_rtsps_streamswrites its result onto the camera'srtsps_streams;delete_camera_rtsps_streamsdrops the deleted qualities (and clears the field toNoneonce no streams remain). - Prime / refresh on connect. When a public devices-WS frame moves a camera
to
CONNECTED(a reconnect or firmware change can rotate thertsp_alias), a background fetch is scheduled. A camera that was offline atupdate_public()time — so skipped by the connected-only prime — is primed when it comes online mid-session, not left streamless until the next reload. An already-populated camera is refreshed in place instead. Either way a WebSocket reconnect resync also refreshes every populated camera. The field is never emptied — the old URLs stay readable until the fresh ones land, so synchronous consumers readingcamera.rtsps_streamsnever observe a spuriousNone. It is only ever cleared by a cameraremoveframe (which drops the whole camera) or the client's owndelete_camera_rtsps_streams.
Public vs. private API¶
uiprotect can talk to UniFi Protect two ways, and is actively migrating
from the second to the first:
- Public Integration API — Ubiquiti's officially documented REST API
under
/integration/v1/…. It authenticates with an API key (create one withuiprotect create-api-key NAME), is stable across firmware releases, and is the forward-looking path. The typedsubscribe_eventsstream and the public-API CLI groups (viewers-public,users-public,liveviews, …) are driven by this API. Requests on this path are auto-paced to stay under the server's per-API-key rate budget — the client seeds its rate from theRateLimit-Policyheader on the first response (with safety margin for the shared-budget public WebSocket) and falls back to a conservative default until that header is seen, so a bootstrap fan-out no longer trips a429storm. Rotating the key viaset_api_key()resets the pacing. - Private API — the reverse-engineered, undocumented endpoints under
/api/…plus the binary WebSocket update stream. It authenticates with username/password and powers most of the historicalbootstrap-based surface. It is not documented by Ubiquiti, can shift between firmware versions, and is being deprecated capability-by-capability as the public API gains coverage.
Prefer the public API for new code. Reach for the private API only for capabilities the public API does not yet expose; treat that as a temporary escape hatch rather than the default path.