unifi-protect

repository·main·Indexed 19 days ago

https://github.com/hjdhjd/unifi-protect

A modern TypeScript implementation of the Ubiquiti UniFi Protect API (version 5.2.0). It provides real-time event streams, state observation via a projection model, and direct access to encoded camera video datastreams (H.264, HEVC, or AV1). The library includes tools for managing cameras, chimes, and relays, as well as connection monitoring and diagnostic channels for observability.

Tokens
144.7K
Snippets
426
Records
574
Agent score
61%

What's inside unifi-protect

  1. Understand the StateModelKey type alias

    main

    The StateModelKey type represents the specific subset of model keys that the UniFi Protect controller emits realtime deltas for. While many models are reduced into the canonical ProtectState, StateModelKey is restricted to those that the reducer's realtime upsert, patch, or remove paths actually touch.

    Key characteristics:

    • It is a superset of DeviceModelKey (including user and liveview).
    • It is a subset of the full ModelKey vocabulary.
    • It includes the devices, the NVR, the user roster, and the liveview collection.
    • It excludes models that are only available via bootstrap (e.g., ringtones), because the controller does not broadcast realtime packets for them.
    type StateModelKey = typeof STATE_MODEL_KEYS[number];
  2. Understand the LivestreamSubscriptionState lifecycle

    main

    The LivestreamSubscriptionState represents the coarse, stable lifecycle of a pooled stream as observed by a subscriber. You can use this type to monitor the health and status of a livestream connection.

    • connecting: The initial state where the connection is being established, but no data segments have flowed yet.
    • live: A healthy, active stream.
    • recovering: An active recovery episode is in progress (e.g., a recoverable stall). During this state, the stream is attempting to resume without ending the iteration.
    • closed: A terminal state indicating the stream has been disposed, a codec change has occurred, or the connection policy has given up.
    type LivestreamSubscriptionState = "closed" | "connecting" | "live" | "recovering";
  3. Understand the ProtectCameraConfigInterface structure

    main

    The ProtectCameraConfigInterface represents a semi-complete description of the UniFi Protect camera configuration JSON. It extends ProtectDeviceBaseInterface, meaning it inherits all base device properties. Because the camera configuration JSON can contain arbitrary or evolving fields, the interface is indexable, allowing you to access unknown properties using string keys that return a ProtectKnownJsonValue type.

    // The interface is indexable with string keys
    const config: ProtectCameraConfigInterface = {
      // ... known properties from ProtectDeviceBaseInterface
      some_new_field: "value"
    };
    const value = config["some_new_field"]; // Returns ProtectKnownJsonValue
  4. Understand the ProtectWebSocket interface

    main

    The ProtectWebSocket interface defines a minimal, read-only WebSocket surface used by the library's inbound transports, such as the realtime events stream and livestream sessions.

    It is a deliberate subset of the standard WHATWG/undici WebSocket API, designed to be lightweight and easy to mock for testing. While the library's default factories use undici's WebSocket (which satisfies this interface structurally), the interface itself is decoupled from any specific transport to ensure a consistent definition across the library.

    Note on directionality:

    • ProtectWebSocket is for receive-direction (read-only).
    • For send-direction (write-only) tasks like TalkbackSession, the library uses a separate ProtectWritableWebSocket interface.
  5. Use the Fob projection to manage security fobs

    main

    The Fob interface is a projection of a UniFi Protect security fob. It inherits core device behaviors from DeviceProjection, such as read-through getters, live observation, and write-through updates.

    Because a fob is an input device, its primary interaction with the system is through events (like button presses) arriving on the event firehose. Its state (such as awayState) is read through the device record. The Fob interface does not add new commands, but provides access to existing device management capabilities like reboot() and update().

  6. How the urgency option affects stream behavior

    main

    The urgency property is a function returning a number (milliseconds) that dictates two behaviors:

    1. Resilient Recovery Tuning: The library's recovery budget self-tunes based on the value provided.

      • A low value (e.g., for a live view) triggers aggressive recovery.
      • A higher value (e.g., for a paced recording) allows for more cushion.
      • A large value (e.g., for a passive buffer) allows the system to wait patiently.
    2. Media-Stall Detection: It sets the deadline for the pool's media watchdog.

      • Default: If omitted, the watchdog uses a 10s window.
      • Tighter values: Tightens detection (clamped against jitter).
      • Infinity: Opts out of media-stall detection (the session's heartbeat still watches the socket).

    Note: The value is pulled fresh at each decision and aggregated across a shared stream by the minimum value provided by all subscribers.

  7. Understand the AuthMethod type for doorbell events

    main

    The AuthMethod type defines the specific way a doorbell authDetected event was triggered. Unlike some other detection types in the library that allow for arbitrary strings to support new firmware, AuthMethod is a closed union. This means the library only produces values it explicitly knows how to map from the underlying hardware data. If new authentication methods are added to UniFi Protect firmware, they will not appear in this type until the library is updated to include them in the internal mapping logic.

    type AuthMethod = "fingerprint" | "nfc";
  8. Use the Clock interface for time-based operations

    main

    The Clock interface is the central source of time for the unifi-protect library. Production code uses this interface instead of Date.now() or node:timers/promises to ensure time is a visible, injectable dependency. This design allows for deterministic testing by substituting a fake clock that can drive time forward without real-time waits.

    When writing code that requires wall-clock time, a one-shot delay, or a repeating interval, you should depend on the Clock interface rather than global runtime methods.

    // Example of the mental model: injecting Clock into a consumer
    class ConnectionManager {
      constructor(private clock: Clock) {}
    
      async recover() {
        // Uses the injected clock instead of global setTimeout
        await this.clock.wait(1000, { signal: this.abortController.signal });
      }
    }
  9. Understand sensor feature flags in ProtectSensorConfigInterface

    main

    The featureFlags property in ProtectSensorConfigInterface identifies the capabilities of a specific sensor. It uses ProtectSensorChannelInterface for most sensor types (like acceleration, button, glassBreak, humidity, led, light, motion, open, tamper, temperature) and includes specific metadata for others:

    • alarmTypes: An array of strings representing supported alarm types.
    • isBidirectional: A boolean indicating if the sensor supports bidirectional communication.
    • temperature: Includes a range object with max and min numbers.
    • waterLeak: Includes channelNames (an array of strings).
  10. Select Livestream sources

    main

    Livestreams are selected using the LivestreamSource type, which ensures you cannot illegally combine a non-zero channel with a secondary lens. A livestream is captured from either:

    1. A quality channel on the primary sensor.
    2. A secondary lens (always addressed on channel 0).
  11. Understand the ProtectDeviceConfigMap structure

    main

    The ProtectDeviceConfigMap is a central mapping used by the library to link DeviceCollectionKey values to their specific configuration record types. It ensures that the collection vocabulary and device configuration types remain synchronized.

    Consumers typically do not interact with this map directly; instead, they use the ProtectDeviceConfig union type, which is derived from this map. This map defines how different device types (like cameras, sensors, or lights) are identified and configured within the library.