Skip to main content

dahua_camera_server/handlers/
capabilities.rs

1//! `GET /api/cameras/{id}/capabilities` — what a device reports it can do.
2//!
3//! The point of exposing this is that a UI can grey out a control the camera
4//! does not have, instead of showing a button that returns
5//! `CapabilityUnsupported` when pressed.
6//!
7//! A capability reported `false` means one of two things, and the response
8//! says which: either a probe ran and the device did not advertise it, or no
9//! probe has run yet. Both refuse the call; only one is worth investigating.
10
11use crate::AppState;
12use axum::extract::{Path, State};
13use axum::http::StatusCode;
14use axum::Json;
15use dahua_camera_core::Capability;
16use serde::Serialize;
17
18/// One capability and what is known about it.
19#[derive(Debug, Serialize)]
20pub struct CapabilityView {
21    /// Stable lowercase name, matching the error text and OPC UA node ids.
22    pub name: &'static str,
23    /// Whether the device confirmed it.
24    pub supported: bool,
25    /// Whether this capability has ever been verified on this hardware.
26    ///
27    /// `false` marks the analytics that are modelled but unconfirmed — face
28    /// recognition, ANPR, people counting, PPE, heat map, object
29    /// left/removed. They report `supported: false` and are never called.
30    pub confirmed_on_this_model: bool,
31}
32
33/// Body of the capabilities endpoint.
34#[derive(Debug, Serialize)]
35pub struct CapabilitiesView {
36    /// Which camera.
37    pub camera_id: String,
38    /// Whether a probe has run at all.
39    ///
40    /// When `false`, everything below is refused because nothing was asked —
41    /// not because the device said no.
42    pub probed: bool,
43    /// Every capability the workspace models.
44    pub capabilities: Vec<CapabilityView>,
45}
46
47/// `GET /api/cameras/{id}/capabilities`.
48pub async fn get_capabilities(
49    State(state): State<AppState>,
50    Path(id): Path<String>,
51) -> Result<Json<CapabilitiesView>, StatusCode> {
52    let camera = state.get(&id).ok_or(StatusCode::NOT_FOUND)?;
53    let profile = camera.capabilities();
54
55    Ok(Json(CapabilitiesView {
56        camera_id: camera.id().to_owned(),
57        probed: profile.is_probed(),
58        capabilities: Capability::all()
59            .iter()
60            .map(|capability| CapabilityView {
61                name: capability.name(),
62                supported: profile.supports(*capability),
63                confirmed_on_this_model: capability.is_confirmed_on_this_model(),
64            })
65            .collect(),
66    }))
67}