Skip to main content

hidpp/feature/wireless_device_status/
mod.rs

1//! Implements the `WirelessDeviceStatus` feature (ID `0x1d4b`) that notifies
2//! the host about device reconnections.
3
4use std::sync::Arc;
5
6use num_enum::{IntoPrimitive, TryFromPrimitive};
7
8use crate::{
9    channel::HidppChannel,
10    event::EventEmitter,
11    feature::{CreatableFeature, EmittingFeature, Feature},
12    nibble,
13    protocol::v20,
14};
15
16/// Implements the `WirelessDeviceStatus` / `0x1d4b` feature.
17pub struct WirelessDeviceStatusFeature {
18    /// The underlying HID++ channel.
19    chan: Arc<HidppChannel>,
20
21    /// The emitter used to emit events.
22    emitter: Arc<EventEmitter<WirelessDeviceStatusEvent>>,
23
24    /// The handle assigned to the message listener registered via
25    /// [`HidppChannel::add_msg_listener`].
26    /// This is used to remove the listener when the feature is dropped.
27    msg_listener_hdl: u32,
28}
29
30impl CreatableFeature for WirelessDeviceStatusFeature {
31    const ID: u16 = 0x1d4b;
32    const STARTING_VERSION: u8 = 0;
33
34    fn new(chan: Arc<HidppChannel>, device_index: u8, feature_index: u8) -> Self {
35        let emitter = Arc::new(EventEmitter::new());
36
37        let hdl = chan.add_msg_listener({
38            let emitter = Arc::clone(&emitter);
39
40            move |raw, matched| {
41                if matched {
42                    return;
43                }
44
45                let msg = v20::Message::from(raw);
46
47                let header = msg.header();
48                if header.device_index != device_index
49                    || header.feature_index != feature_index
50                    || nibble::combine(header.software_id, header.function_id) != 0
51                {
52                    return;
53                }
54
55                let payload = msg.extend_payload();
56                let Ok(status) = WirelessDeviceStatus::try_from(payload[0]) else {
57                    return;
58                };
59                let Ok(request) = WirelessDeviceStatusRequest::try_from(payload[1]) else {
60                    return;
61                };
62                let Ok(reason) = WirelessDeviceStatusReason::try_from(payload[2]) else {
63                    return;
64                };
65
66                emitter.emit(WirelessDeviceStatusEvent::StatusBroadcast(
67                    WirelessDeviceStatusBroadcast {
68                        status,
69                        request,
70                        reason,
71                    },
72                ));
73            }
74        });
75
76        Self {
77            chan,
78            emitter,
79            msg_listener_hdl: hdl,
80        }
81    }
82}
83
84impl Feature for WirelessDeviceStatusFeature {}
85
86impl EmittingFeature<WirelessDeviceStatusEvent> for WirelessDeviceStatusFeature {
87    fn listen(&self) -> async_channel::Receiver<WirelessDeviceStatusEvent> {
88        self.emitter.create_receiver()
89    }
90}
91
92impl Drop for WirelessDeviceStatusFeature {
93    fn drop(&mut self) {
94        self.chan.remove_msg_listener(self.msg_listener_hdl);
95    }
96}
97
98/// Represents an event emitted by the [`WirelessDeviceStatusFeature`]
99/// feature.
100#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
101#[cfg_attr(feature = "serde", derive(serde::Serialize))]
102#[non_exhaustive]
103pub enum WirelessDeviceStatusEvent {
104    /// Is emitted whenever a device (re)connects to the host.
105    ///
106    /// This event is always enabled.
107    StatusBroadcast(WirelessDeviceStatusBroadcast),
108}
109
110/// Represents the data of the [`WirelessDeviceStatusEvent::StatusBroadcast`]
111/// event.
112#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
113#[cfg_attr(feature = "serde", derive(serde::Serialize))]
114#[non_exhaustive]
115pub struct WirelessDeviceStatusBroadcast {
116    /// The status the device reports to be in.
117    pub status: WirelessDeviceStatus,
118
119    /// The request the devices expresses towards the host.
120    pub request: WirelessDeviceStatusRequest,
121
122    /// The reason for the status broadcast.
123    pub reason: WirelessDeviceStatusReason,
124}
125
126/// Represents a device status as reported in
127/// [`WirelessDeviceStatusBroadcast::status`].
128#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, IntoPrimitive, TryFromPrimitive)]
129#[cfg_attr(feature = "serde", derive(serde::Serialize))]
130#[non_exhaustive]
131#[repr(u8)]
132pub enum WirelessDeviceStatus {
133    Unknown = 0x00,
134    Reconnection = 0x01,
135}
136
137/// Represents a request as reported in
138/// [`WirelessDeviceStatusBroadcast::request`].
139#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, IntoPrimitive, TryFromPrimitive)]
140#[cfg_attr(feature = "serde", derive(serde::Serialize))]
141#[non_exhaustive]
142#[repr(u8)]
143pub enum WirelessDeviceStatusRequest {
144    NoRequest = 0x00,
145    SoftwareReconfigurationNeeded = 0x01,
146}
147
148/// Represents a broadcast reason as reported in
149/// [`WirelessDeviceStatusBroadcast::reason`].
150#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, IntoPrimitive, TryFromPrimitive)]
151#[cfg_attr(feature = "serde", derive(serde::Serialize))]
152#[non_exhaustive]
153#[repr(u8)]
154pub enum WirelessDeviceStatusReason {
155    Unknown = 0x00,
156    PowerSwitchActivated = 0x01,
157}