riot_coap_handler_demos/led.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94
//! Handler that expose a single or all LEDs of a board
//
// TBD: Generalize like GPIOs/I2C to be independent of RIOT
use coap_handler_implementations::{
new_dispatcher, wkc, Empty, HandlerBuilder, TypeHandler, TypeRenderable,
};
use riot_wrappers::led::{LedNotPresent, LED};
use switch_hal::OutputSwitch;
use switch_hal::ToggleableOutputSwitch;
struct OneLED<const I: u8>(LED<I>);
// FIXME: It doesn't really make sense to have this monomorphized over I, especially as most of the
// time one would have multiple such handlers. This should be trivial to change if riot-wrappers
// LED gains a const-erased version.
impl<const I: u8> TypeRenderable for OneLED<I> {
type Get = ();
type Put = bool;
type Post = Empty;
fn put(&mut self, data: &bool) -> u8 {
if *data {
self.0.on().expect("LEDs are infallible");
} else {
self.0.off().expect("LEDs are infallible");
}
coap_numbers::code::CHANGED
}
fn post(&mut self, _data: &Empty) -> u8 {
self.0.toggle().expect("LEDs are infallible");
coap_numbers::code::CHANGED
}
}
/// Handler for a single LED.
///
/// The handler accepts PUTs of CBOR booleans true or false. It also accepts POSTs, which for
/// simplicity of the implementation right now require a CBOR `null` value in the payload.
pub fn single_led<const I: u8>(
led: LED<I>,
) -> impl coap_handler::Handler + coap_handler::Reporting {
let handler = TypeHandler::new_minicbor_0_24(OneLED(led));
wkc::ConstantSingleRecordReport::new(
handler,
&[coap_handler::Attribute::ResourceType(
"tag:chrysn@fsfe.org,2024-08-24:led",
)],
)
}
/// Handler for a single LED, which is only shown if the LED is present.
///
/// See [`single_led()`] for how it is accessed.
pub fn single_led_maybe<const I: u8>(
led: Result<LED<I>, LedNotPresent>,
) -> impl coap_handler::Handler + coap_handler::Reporting {
// Option<Handler> implements Handler and 4.04s on None, and does suppresses the report.
led.ok().map(|led| {
wkc::ConstantSingleRecordReport::new(
TypeHandler::new_minicbor(OneLED(led)),
&[coap_handler::Attribute::ResourceType(
"tag:chrysn@fsfe.org,2024-08-24:led",
)],
)
})
}
/// A handler that contains sub-resources for all LEDs present on a system.
///
/// It has sub-resources `0` up to `7` (depending on which LEDs are present on the board).
///
/// Use this with `.below()`; if no extra Uri-Path option is present (eg. when placed
/// `.at(&["leds"], ...)` requesting `coap://.../leds`), it does not even implement a resource
/// there.
///
/// This is subtly different from the [`crate::saul`] module where SAUL reports a single resource
/// in .well-known/core. There are no hard rules to follow in which to pick, but generally there
/// are few LEDs and there can be many SAUL resources.
pub fn all_leds() -> impl coap_handler::Handler + coap_handler::Reporting {
new_dispatcher()
.at(&["0"], single_led_maybe(LED::<0>::new_checked()))
.at(&["1"], single_led_maybe(LED::<1>::new_checked()))
.at(&["2"], single_led_maybe(LED::<2>::new_checked()))
.at(&["3"], single_led_maybe(LED::<3>::new_checked()))
.at(&["4"], single_led_maybe(LED::<4>::new_checked()))
.at(&["5"], single_led_maybe(LED::<5>::new_checked()))
.at(&["6"], single_led_maybe(LED::<6>::new_checked()))
.at(&["7"], single_led_maybe(LED::<7>::new_checked()))
}