import type { DeviceContext } from "./context.ts"; import { DeviceProjection } from "./device.ts"; import type { LivestreamSpec } from "../transport/livestream-session.ts"; import type { LivestreamSubscription } from "../client/livestream-pool.ts"; import type { ProtectCameraConfig } from "../types/index.ts"; import type { TalkbackSession } from "../transport/talkback-session.ts"; /** * Options for {@link Camera.snapshot}. Width and height request a specific output size; omit both for the controller's default. Set `packageCamera` to capture from the * secondary package sensor on dual-camera devices (the G4/G5 Doorbell Pro) instead of the primary lens. * * @category Devices */ export interface SnapshotOptions { height?: number; /** * Capture from the camera's secondary package sensor (the downward-facing lens on dual-camera doorbells such as the G4/G5 Doorbell Pro) rather than the primary lens. * This is an endpoint distinction - it swaps the request path to `/package-snapshot` - not a multi-lens selection, and it is a hard precondition: {@link * Camera.snapshot} throws {@link ProtectUnsupportedError} before issuing any request when the camera's `featureFlags.hasPackageCamera` is not set. Named * `packageCamera` rather than `package` because `package` is a reserved word in strict-mode ES modules and so could not be destructured. */ packageCamera?: boolean; signal?: AbortSignal; width?: number; } /** * Options for {@link Camera.livestream} - the stream-affecting `source` selector (a quality channel or a secondary lens) and tuning (segment length, chunk size, opt-in * timestamps) without `cameraId` (the camera supplies its own id), plus an optional abort signal that disposes the returned subscription and an optional `urgency` * closure feeding the pool's resilient recovery. * * @category Devices */ export interface LivestreamOptions extends Omit { signal?: AbortSignal; /** * How many milliseconds this consumer can tolerate receiving no segment, pulled fresh at each decision and aggregated across a shared stream by the minimum. It serves * consumer-owned policies at once. First, the resilient recovery's await budget self-tunes from the consumer's real buffer headroom: a live view reports ~0 * (recover aggressively), a paced recording its cushion, a passive buffer a large value (wait patiently). Second, it is the consumer's MEDIA-stall detection deadline * for the pool's always-on (while live) media watchdog: declaring nothing leaves the default 10 s window (every stream is media-watched by default), a tighter value * tightens detection (clamped up to a small floor against jitter), and Infinity opts out of media-stall detection entirely (the session's any-byte heartbeat still * watches the socket). Omit it to leave the stream maximally patient on recovery and media-watched at the 10 s default. */ urgency?: () => number; } /** * A camera projection. Inherits the read-through getters, live `observe`, and the write-through `update` and `reboot` commands from {@link DeviceProjection}, and adds * the commands specific to cameras: a JPEG snapshot and a pooled livestream subscription. Camera-specific *configuration* (RTSP enablement, recording settings, and so * on) is expressed through `update` payloads rather than bespoke methods. * * @category Devices */ export declare class Camera extends DeviceProjection { readonly modelKey = "camera"; constructor(ctx: DeviceContext, id: string); /** * Fetch a current JPEG snapshot from the camera. * * @param opts - Snapshot sizing and an optional abort signal. * * @returns The snapshot image bytes. * * @throws {ProtectUnsupportedError} When `opts.packageCamera` is set but the camera has no package sensor (checked before any request is issued). * @throws The classified `FatalError` on a non-2xx, or a transport-level `ProtectError` on failure. */ snapshot(opts?: SnapshotOptions): Promise; /** * Read the camera's current ambient-light level. This is a live *query* of the camera's lux sensor - the instantaneous illuminance reading - and is distinct from the * config's `isDark` boolean (a derived day/night flag the reducer already holds). Because the reading is telemetry the controller does not broadcast as state, there is * nothing for the reducer to fold it into, so it is a direct request rather than an observable field. * * @param opts - An optional abort signal. * * @returns The live illuminance reading, in lux. * * @throws {ProtectUnsupportedError} When the camera has no ambient-light sensor (`featureFlags.hasLuxCheck` is unset), checked before any request is issued. * @throws {ProtectProtocolError} When the controller's 2xx response carries no numeric `illuminance` reading. * @throws The classified `FatalError` on a non-2xx, or a transport-level `ProtectError` on failure. */ lux(opts?: { signal?: AbortSignal; }): Promise; /** * Release a paired UniFi Access lock on this camera. Currently the controller supports unlocking only Access readers attached to a camera on the same controller as * Protect. Write-through: the command is issued and the method returns; it does not fold any response into the store. Unlike a config command, an unlock has no * lock-state result to observe - the Access bridge is one-way. `accessDeviceMetadata.doorInfo.lockState` is static capability metadata that does *not* change on an * unlock (confirmed live against an Access reader, via both the realtime stream and a REST refresh), and the controller emits no firehose occurrence a consumer can act * on: a software unlock arrives as a device-less, bare `type: "access"` packet byte-identical to the user-session audit event the controller emits on every * authentication, so the library deliberately surfaces no unlock-confirmation event. Treat `unlock()` as fire-and-forget - it issues the command and resolves; * there is no realtime acknowledgement to await. * * @param opts - An optional abort signal. * * @throws {ProtectUnsupportedError} When the camera has no paired Access lock (`accessDeviceMetadata.featureFlags.supportUnlock` is unset), checked before any * request is issued. * @throws The classified `FatalError` on a non-2xx, or a transport-level `ProtectError` on failure. */ unlock(opts?: { signal?: AbortSignal; }): Promise; /** * Activate the package-camera downlight (the flashlight on dual-camera doorbells such as the G4/G5 Doorbell Pro). This is a **momentary** operation: the controller * exposes no off endpoint, and the light self-extinguishes roughly 25 seconds after the last activation. A consumer that wants the light to stay lit re-issues this * call on its own cadence (Protect's own apps pulse it every ~20 seconds); turning it off is simply ceasing to re-issue. The keepalive cadence is therefore consumer * policy - the library exposes only the single primitive operation. Write-through: the request is issued and the method returns. * * @param opts - An optional abort signal. * * @throws {ProtectUnsupportedError} When the camera has no package sensor (`featureFlags.hasPackageCamera` is unset), checked before any request is issued. * @throws The classified `FatalError` on a non-2xx, or a transport-level `ProtectError` on failure. */ turnOnFlashlight(opts?: { signal?: AbortSignal; }): Promise; /** * Subscribe to a live fMP4 stream from this camera. Delegates to the library-owned pool, which shares one underlying WebSocket across every consumer of an identical * stream and replays the cached init segment to late joiners. The returned subscription is an `AsyncIterable` and `AsyncDisposable`; iterate it for segments, * dispose it (or abort its signal) to leave - the underlying session closes once the last subscriber departs. * * @param opts - The stream source and tuning, plus an optional abort signal that disposes the subscription. * * @returns A live subscription. */ livestream(opts: LivestreamOptions): LivestreamSubscription; /** * Open a send-direction two-way-audio (talkback) channel to this camera's speaker. Negotiates the WebSocket and connects atomically: the returned promise resolves with * a live {@link TalkbackSession} or throws. The session is an `AsyncDisposable` - feed it audio with `session.send(source)` (a consumer-supplied * `AsyncIterable`, e.g. an ffmpeg `stdout` producing the format `config.talkbackSettings` describes) and dispose it (or abort its signal) to close. The * bytes are opaque; the library neither inspects nor transcodes them. * * A client-side precondition is checked before any negotiation: a camera without a speaker cannot receive talkback, so this throws {@link * ProtectUnsupportedError} up front rather than issuing a request the controller would reject - the package-snapshot precedent applied to the send channel. The * projection holds no talkback state (a set-once "talkback active" flag on a live view would be the stale-flag failure mode); each call mints an independent session. * * @param opts - An optional abort signal that cancels the connect and, once live, tears the session down. * * @returns A connected talkback session. * * @throws {ProtectUnsupportedError} When the camera has no speaker (`featureFlags.hasSpeaker` is unset), checked before any negotiation. * @throws The classified `FatalError` on a non-2xx negotiation, or a transport-level `ProtectError` when the socket fails to open. */ talkback(opts?: { signal?: AbortSignal; }): Promise; } //# sourceMappingURL=camera.d.ts.map