pub mod event;
pub mod types;
#[cfg(test)]
mod tests;
use std::sync::Arc;
pub use event::IlluminationEvent;
pub use types::{
BrightnessClampedSource, ControlCapabilities, ControlInfo, IlluminationState, LevelConfig,
SetLevels,
};
use self::types::{be16, illumination_state};
use crate::{
channel::{HidppChannel, MessageListenerGuard},
event::EventEmitter,
feature::{CreatableFeature, EmittingFeature, Feature, FeatureEndpoint, event_payload},
protocol::v20::{ErrorType, Hidpp20Error},
};
const FN_GET_ILLUMINATION: u8 = 0;
const FN_SET_ILLUMINATION: u8 = 1;
const FN_GET_BRIGHTNESS_INFO: u8 = 2;
const FN_GET_BRIGHTNESS: u8 = 3;
const FN_SET_BRIGHTNESS: u8 = 4;
const FN_GET_BRIGHTNESS_LEVELS: u8 = 5;
const FN_SET_BRIGHTNESS_LEVELS: u8 = 6;
const FN_GET_COLOR_TEMPERATURE_INFO: u8 = 7;
const FN_GET_COLOR_TEMPERATURE: u8 = 8;
const FN_SET_COLOR_TEMPERATURE: u8 = 9;
const FN_GET_COLOR_TEMPERATURE_LEVELS: u8 = 10;
const FN_SET_COLOR_TEMPERATURE_LEVELS: u8 = 11;
const FN_GET_BRIGHTNESS_EFFECTIVE_MAX: u8 = 12;
pub struct IlluminationFeature {
endpoint: FeatureEndpoint,
emitter: Arc<EventEmitter<IlluminationEvent>>,
_msg_listener: MessageListenerGuard,
}
impl CreatableFeature for IlluminationFeature {
const ID: u16 = 0x1990;
const STARTING_VERSION: u8 = 0;
fn new(chan: Arc<HidppChannel>, device_index: u8, feature_index: u8) -> Self {
let emitter = Arc::new(EventEmitter::new());
let listener = chan.add_msg_listener_guarded({
let emitter = Arc::clone(&emitter);
move |raw, matched| {
let Some((func, payload)) =
event_payload(raw, matched, device_index, feature_index)
else {
return;
};
if let Some(event) = event::decode_event(func.to_lo(), &payload) {
emitter.emit(event);
}
}
});
Self {
endpoint: FeatureEndpoint::new(chan, device_index, feature_index),
emitter,
_msg_listener: listener,
}
}
}
impl Feature for IlluminationFeature {}
impl EmittingFeature<IlluminationEvent> for IlluminationFeature {
fn listen(&self) -> async_channel::Receiver<IlluminationEvent> {
self.emitter.create_receiver()
}
}
impl IlluminationFeature {
pub async fn get_illumination(&self) -> Result<IlluminationState, Hidpp20Error> {
let payload = self
.endpoint
.call(FN_GET_ILLUMINATION, [0; 3])
.await?
.extend_payload();
illumination_state(payload[0])
}
pub async fn set_illumination(&self, state: IlluminationState) -> Result<(), Hidpp20Error> {
self.endpoint
.call(FN_SET_ILLUMINATION, [u8::from(state), 0, 0])
.await?;
Ok(())
}
pub async fn get_brightness_info(&self) -> Result<ControlInfo, Hidpp20Error> {
self.read_info(FN_GET_BRIGHTNESS_INFO).await
}
pub async fn get_brightness(&self) -> Result<u16, Hidpp20Error> {
self.read_value(FN_GET_BRIGHTNESS).await
}
pub async fn set_brightness(&self, brightness: u16) -> Result<(), Hidpp20Error> {
self.write_value(FN_SET_BRIGHTNESS, brightness).await
}
pub async fn get_brightness_levels(
&self,
start_index: u8,
) -> Result<LevelConfig, Hidpp20Error> {
self.read_levels(FN_GET_BRIGHTNESS_LEVELS, start_index)
.await
}
pub async fn set_brightness_levels(&self, levels: &SetLevels) -> Result<(), Hidpp20Error> {
self.write_levels(FN_SET_BRIGHTNESS_LEVELS, levels).await
}
pub async fn get_brightness_effective_max(&self) -> Result<u16, Hidpp20Error> {
self.read_value(FN_GET_BRIGHTNESS_EFFECTIVE_MAX).await
}
pub async fn get_color_temperature_info(&self) -> Result<ControlInfo, Hidpp20Error> {
self.read_info(FN_GET_COLOR_TEMPERATURE_INFO).await
}
pub async fn get_color_temperature(&self) -> Result<u16, Hidpp20Error> {
self.read_value(FN_GET_COLOR_TEMPERATURE).await
}
pub async fn set_color_temperature(&self, color_temperature: u16) -> Result<(), Hidpp20Error> {
self.write_value(FN_SET_COLOR_TEMPERATURE, color_temperature)
.await
}
pub async fn get_color_temperature_levels(
&self,
start_index: u8,
) -> Result<LevelConfig, Hidpp20Error> {
self.read_levels(FN_GET_COLOR_TEMPERATURE_LEVELS, start_index)
.await
}
pub async fn set_color_temperature_levels(
&self,
levels: &SetLevels,
) -> Result<(), Hidpp20Error> {
self.write_levels(FN_SET_COLOR_TEMPERATURE_LEVELS, levels)
.await
}
async fn read_info(&self, function: u8) -> Result<ControlInfo, Hidpp20Error> {
let payload = self.endpoint.call(function, [0; 3]).await?.extend_payload();
Ok(ControlInfo::from_payload(&payload))
}
async fn read_value(&self, function: u8) -> Result<u16, Hidpp20Error> {
let payload = self.endpoint.call(function, [0; 3]).await?.extend_payload();
Ok(be16(&payload, 0))
}
async fn write_value(&self, function: u8, value: u16) -> Result<(), Hidpp20Error> {
let [hi, lo] = value.to_be_bytes();
self.endpoint.call(function, [hi, lo, 0]).await?;
Ok(())
}
async fn read_levels(
&self,
function: u8,
start_index: u8,
) -> Result<LevelConfig, Hidpp20Error> {
if start_index > 0x0f {
return Err(Hidpp20Error::Feature(ErrorType::InvalidArgument));
}
let payload = self
.endpoint
.call(function, [start_index << 4, 0, 0])
.await?
.extend_payload();
Ok(LevelConfig::from_payload(&payload))
}
async fn write_levels(&self, function: u8, levels: &SetLevels) -> Result<(), Hidpp20Error> {
self.endpoint
.call_long(function, levels.to_payload()?)
.await?;
Ok(())
}
}