Skip to main content

edge_core/
device_control.rs

1//! Device-control abstraction surfaced over `/ws/edge`.
2//!
3//! `weave-server` may send `ServerToEdge::DisplayGlyph` /
4//! `ServerToEdge::DeviceConnect` / `ServerToEdge::DeviceDisconnect` frames
5//! to drive a specific device on the edge — used by the weave-web UI
6//! buttons (Connect / Disconnect / Test LED). The native edge-agent owns
7//! per-device handles (BLE for Nuimo, etc.) that are foreign to this crate,
8//! so the WS handler dispatches via this trait. The agent registers an
9//! impl backed by its device map; targets the trait doesn't recognise
10//! return `Ok(())` after logging.
11//!
12//! Errors propagate as `anyhow::Error` for symmetry with the rest of the
13//! WS client surface and to keep the trait dyn-friendly.
14
15use async_trait::async_trait;
16
17/// Trait the WS client uses to dispatch device-control frames. The
18/// concrete impl lives in the binary that owns the device handles
19/// (`edge-agent` for Nuimo today; future targets register their own).
20#[async_trait]
21pub trait DeviceControlHook: Send + Sync {
22    /// Render `pattern` on the device's display.
23    /// `pattern` is a 9-line ASCII grid (`*` = on).
24    /// `brightness` is on the closed interval `0.0..=1.0`; `None` =
25    /// implementation default.
26    /// `timeout_ms` is the auto-clear timeout; `None` = implementation
27    /// default.
28    /// `transition` is `"immediate"` or `"cross_fade"`; `None` =
29    /// implementation default.
30    async fn display_glyph(
31        &self,
32        device_type: &str,
33        device_id: &str,
34        pattern: &str,
35        brightness: Option<f32>,
36        timeout_ms: Option<u32>,
37        transition: Option<&str>,
38    ) -> anyhow::Result<()>;
39
40    /// Re-attempt a connection to the device. Implementations should clear
41    /// any "paused" flag that suppresses auto-reconnect.
42    async fn connect_device(&self, device_type: &str, device_id: &str) -> anyhow::Result<()>;
43
44    /// Drop the active connection and set "paused" so the auto-reconnect
45    /// loop does not immediately re-establish the link.
46    async fn disconnect_device(&self, device_type: &str, device_id: &str) -> anyhow::Result<()>;
47}
48
49/// No-op implementation used when the host hasn't registered a device-
50/// control backend. Keeps the WS frames from crashing a stripped-down
51/// build that doesn't supervise BLE devices.
52pub struct NoopDeviceControl;
53
54#[async_trait]
55impl DeviceControlHook for NoopDeviceControl {
56    async fn display_glyph(
57        &self,
58        device_type: &str,
59        device_id: &str,
60        _pattern: &str,
61        _brightness: Option<f32>,
62        _timeout_ms: Option<u32>,
63        _transition: Option<&str>,
64    ) -> anyhow::Result<()> {
65        tracing::warn!(
66            device_type,
67            device_id,
68            "DisplayGlyph received but no device-control hook is registered"
69        );
70        Ok(())
71    }
72
73    async fn connect_device(&self, device_type: &str, device_id: &str) -> anyhow::Result<()> {
74        tracing::warn!(
75            device_type,
76            device_id,
77            "DeviceConnect received but no device-control hook is registered"
78        );
79        Ok(())
80    }
81
82    async fn disconnect_device(&self, device_type: &str, device_id: &str) -> anyhow::Result<()> {
83        tracing::warn!(
84            device_type,
85            device_id,
86            "DeviceDisconnect received but no device-control hook is registered"
87        );
88        Ok(())
89    }
90}