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}