dahua-camera-server 0.3.1

axum HTTP, SSE and WebSocket API for the dahua-camera stack
Documentation
//! `GET /api/cameras/{id}/capabilities` — what a device reports it can do.
//!
//! The point of exposing this is that a UI can grey out a control the camera
//! does not have, instead of showing a button that returns
//! `CapabilityUnsupported` when pressed.
//!
//! A capability reported `false` means one of two things, and the response
//! says which: either a probe ran and the device did not advertise it, or no
//! probe has run yet. Both refuse the call; only one is worth investigating.

use crate::AppState;
use axum::extract::{Path, State};
use axum::http::StatusCode;
use axum::Json;
use dahua_camera_core::Capability;
use serde::Serialize;

/// One capability and what is known about it.
#[derive(Debug, Serialize)]
pub struct CapabilityView {
    /// Stable lowercase name, matching the error text and OPC UA node ids.
    pub name: &'static str,
    /// Whether the device confirmed it.
    pub supported: bool,
    /// Whether this capability has ever been verified on this hardware.
    ///
    /// `false` marks the analytics that are modelled but unconfirmed — face
    /// recognition, ANPR, people counting, PPE, heat map, object
    /// left/removed. They report `supported: false` and are never called.
    pub confirmed_on_this_model: bool,
}

/// Body of the capabilities endpoint.
#[derive(Debug, Serialize)]
pub struct CapabilitiesView {
    /// Which camera.
    pub camera_id: String,
    /// Whether a probe has run at all.
    ///
    /// When `false`, everything below is refused because nothing was asked —
    /// not because the device said no.
    pub probed: bool,
    /// Every capability the workspace models.
    pub capabilities: Vec<CapabilityView>,
}

/// `GET /api/cameras/{id}/capabilities`.
pub async fn get_capabilities(
    State(state): State<AppState>,
    Path(id): Path<String>,
) -> Result<Json<CapabilitiesView>, StatusCode> {
    let camera = state.get(&id).ok_or(StatusCode::NOT_FOUND)?;
    let profile = camera.capabilities();

    Ok(Json(CapabilitiesView {
        camera_id: camera.id().to_owned(),
        probed: profile.is_probed(),
        capabilities: Capability::all()
            .iter()
            .map(|capability| CapabilityView {
                name: capability.name(),
                supported: profile.supports(*capability),
                confirmed_on_this_model: capability.is_confirmed_on_this_model(),
            })
            .collect(),
    }))
}