huesmith_core/light/mod.rs
1pub mod command;
2pub mod state;
3
4/// Trait for any LED output backend.
5/// Implementations handle the actual hardware (GPIO PWM, WS2812B, etc.).
6///
7/// # Call contract
8///
9/// [`state::LightState`] drives implementations under these rules:
10///
11/// - `set_on(false)` must drive the output dark immediately.
12/// - `set_on(true)` must restore the most recently set color/brightness on its
13/// own. The identify/effect blink calls *only* `set_on` in alternation, so an
14/// implementation that treats `set_on(true)` as a bare flag renders identify
15/// invisible.
16/// - Hardware errors are not propagated: the state machine has no recovery
17/// action for a failed LED write, so implementations log and continue.
18pub trait LightOutput: Send {
19 /// Turn the light on or off. On `true`, re-apply the last output state.
20 fn set_on(&mut self, on: bool);
21
22 /// Set brightness level (0-254, where 254 is full brightness).
23 fn set_brightness(&mut self, level: u8);
24
25 /// Set color using CIE 1931 XY coordinates (0-65535 range for each axis).
26 ///
27 /// Defaults to a no-op. [`state::LightState`] never calls this — it converts
28 /// XY to RGB and calls [`set_rgb`](LightOutput::set_rgb) instead — so
29 /// override it only for a backend you drive with raw XY yourself.
30 fn set_color_xy(&mut self, _x: u16, _y: u16) {}
31
32 /// Set color temperature in mireds (153 = 6500K, 500 = 2000K).
33 ///
34 /// Defaults to a no-op, so an on/off or plain-dimmable backend with no
35 /// tunable white needs to implement only [`set_on`](LightOutput::set_on) and
36 /// [`set_brightness`](LightOutput::set_brightness).
37 fn set_color_temp(&mut self, _mireds: u16) {}
38
39 /// Set color using direct RGB values.
40 ///
41 /// Required — the state machine renders the XY and hue/saturation color
42 /// modes (and dimmed color temperature) through this method, so a silently
43 /// inherited no-op would mean a light that pairs, powers, and dims but
44 /// never changes color. Backends with no color channel implement it as a
45 /// brightness mapping (see `SimpleLed`'s BT.709 luminance) or an explicit
46 /// empty body — the choice must be visible in the impl, not an invisible
47 /// default.
48 fn set_rgb(&mut self, r: u8, g: u8, b: u8);
49
50 /// Observe the full raw light state after every render pass.
51 ///
52 /// Defaults to a no-op; the built-in backends ignore it. **Pick one lane:**
53 /// simple hardware implements the `set_*` methods and ignores this hook;
54 /// an effects/full-control backend treats this hook as its single source
55 /// of truth and leaves the `set_*` bodies empty — handling both would
56 /// double-apply every change.
57 ///
58 /// The [`state::LightSnapshot`] carries every raw ZCL value with no
59 /// `Option`s: `on`, `brightness` (logical target, 0-254),
60 /// `rendered_brightness` (what is being driven *right now* — ramps during
61 /// fades; key visual effects off this one), `color_mode`,
62 /// `hue`/`saturation` (0-254), `enhanced_hue` (0-65535),
63 /// `color_x`/`color_y` (0-65535 CIE), `color_temp_mireds` (153-500).
64 /// During a smooth transition it fires on every animation frame
65 /// (~20-33 ms).
66 ///
67 /// Note: the identify blink signals through
68 /// [`set_on`](LightOutput::set_on) alone and does not re-render.
69 fn state_update(&mut self, _state: &state::LightSnapshot) {}
70}