Add color temperature light
This commit is contained in:
@@ -15,7 +15,7 @@ use std::ops::Deref;
|
|||||||
|
|
||||||
use automation_cast::Cast;
|
use automation_cast::Cast;
|
||||||
use automation_lib::device::{Device, LuaDeviceCreate};
|
use automation_lib::device::{Device, LuaDeviceCreate};
|
||||||
use zigbee::light::{LightBrightness, LightOnOff};
|
use zigbee::light::{LightBrightness, LightColorTemperature, LightOnOff};
|
||||||
use zigbee::outlet::{OutletOnOff, OutletPower};
|
use zigbee::outlet::{OutletOnOff, OutletPower};
|
||||||
|
|
||||||
pub use self::air_filter::AirFilter;
|
pub use self::air_filter::AirFilter;
|
||||||
@@ -144,6 +144,7 @@ macro_rules! impl_device {
|
|||||||
|
|
||||||
impl_device!(LightOnOff);
|
impl_device!(LightOnOff);
|
||||||
impl_device!(LightBrightness);
|
impl_device!(LightBrightness);
|
||||||
|
impl_device!(LightColorTemperature);
|
||||||
impl_device!(OutletOnOff);
|
impl_device!(OutletOnOff);
|
||||||
impl_device!(OutletPower);
|
impl_device!(OutletPower);
|
||||||
impl_device!(AirFilter);
|
impl_device!(AirFilter);
|
||||||
@@ -161,6 +162,7 @@ impl_device!(Washer);
|
|||||||
pub fn register_with_lua(lua: &mlua::Lua) -> mlua::Result<()> {
|
pub fn register_with_lua(lua: &mlua::Lua) -> mlua::Result<()> {
|
||||||
register_device!(lua, LightOnOff);
|
register_device!(lua, LightOnOff);
|
||||||
register_device!(lua, LightBrightness);
|
register_device!(lua, LightBrightness);
|
||||||
|
register_device!(lua, LightColorTemperature);
|
||||||
register_device!(lua, OutletOnOff);
|
register_device!(lua, OutletOnOff);
|
||||||
register_device!(lua, OutletPower);
|
register_device!(lua, OutletPower);
|
||||||
register_device!(lua, AirFilter);
|
register_device!(lua, AirFilter);
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ use automation_lib::mqtt::WrappedAsyncClient;
|
|||||||
use automation_macro::LuaDeviceConfig;
|
use automation_macro::LuaDeviceConfig;
|
||||||
use google_home::device;
|
use google_home::device;
|
||||||
use google_home::errors::ErrorCode;
|
use google_home::errors::ErrorCode;
|
||||||
use google_home::traits::{Brightness, OnOff};
|
use google_home::traits::{Brightness, Color, ColorSetting, ColorTemperatureRange, OnOff};
|
||||||
use google_home::types::Type;
|
use google_home::types::Type;
|
||||||
use rumqttc::{matches, Publish};
|
use rumqttc::{matches, Publish};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
@@ -63,6 +63,31 @@ impl From<StateBrightness> for StateOnOff {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
||||||
|
pub struct StateColorTemperature {
|
||||||
|
#[serde(deserialize_with = "state_deserializer")]
|
||||||
|
state: bool,
|
||||||
|
brightness: f32,
|
||||||
|
color_temp: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LightState for StateColorTemperature {}
|
||||||
|
|
||||||
|
impl From<StateColorTemperature> for StateOnOff {
|
||||||
|
fn from(state: StateColorTemperature) -> Self {
|
||||||
|
StateOnOff { state: state.state }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<StateColorTemperature> for StateBrightness {
|
||||||
|
fn from(state: StateColorTemperature) -> Self {
|
||||||
|
StateBrightness {
|
||||||
|
state: state.state,
|
||||||
|
brightness: state.brightness,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Light<T: LightState> {
|
pub struct Light<T: LightState> {
|
||||||
config: Config<T>,
|
config: Config<T>,
|
||||||
@@ -72,6 +97,7 @@ pub struct Light<T: LightState> {
|
|||||||
|
|
||||||
pub type LightOnOff = Light<StateOnOff>;
|
pub type LightOnOff = Light<StateOnOff>;
|
||||||
pub type LightBrightness = Light<StateBrightness>;
|
pub type LightBrightness = Light<StateBrightness>;
|
||||||
|
pub type LightColorTemperature = Light<StateColorTemperature>;
|
||||||
|
|
||||||
impl<T: LightState> Light<T> {
|
impl<T: LightState> Light<T> {
|
||||||
async fn state(&self) -> RwLockReadGuard<T> {
|
async fn state(&self) -> RwLockReadGuard<T> {
|
||||||
@@ -181,6 +207,47 @@ impl OnMqtt for Light<StateBrightness> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl OnMqtt for Light<StateColorTemperature> {
|
||||||
|
async fn on_mqtt(&self, message: Publish) {
|
||||||
|
// Check if the message is from the deviec itself or from a remote
|
||||||
|
if matches(&message.topic, &self.config.mqtt.topic) {
|
||||||
|
let state = match serde_json::from_slice::<StateColorTemperature>(&message.payload) {
|
||||||
|
Ok(state) => state,
|
||||||
|
Err(err) => {
|
||||||
|
warn!(id = Device::get_id(self), "Failed to parse message: {err}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
{
|
||||||
|
let current_state = self.state().await;
|
||||||
|
// No need to do anything if the state has not changed
|
||||||
|
if state.state == current_state.state
|
||||||
|
&& state.brightness == current_state.brightness
|
||||||
|
&& state.color_temp == current_state.color_temp
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.state_mut().await.state = state.state;
|
||||||
|
self.state_mut().await.brightness = state.brightness;
|
||||||
|
self.state_mut().await.color_temp = state.color_temp;
|
||||||
|
debug!(
|
||||||
|
id = Device::get_id(self),
|
||||||
|
"Updating state to {:?}",
|
||||||
|
self.state().await
|
||||||
|
);
|
||||||
|
|
||||||
|
self.config
|
||||||
|
.callback
|
||||||
|
.call(self, self.state().await.deref())
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl<T: LightState> OnPresence for Light<T> {
|
impl<T: LightState> OnPresence for Light<T> {
|
||||||
async fn on_presence(&self, presence: bool) {
|
async fn on_presence(&self, presence: bool) {
|
||||||
@@ -297,3 +364,50 @@ where
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl<T> ColorSetting for Light<T>
|
||||||
|
where
|
||||||
|
T: LightState,
|
||||||
|
T: Into<StateColorTemperature>,
|
||||||
|
{
|
||||||
|
fn color_temperature_range(&self) -> ColorTemperatureRange {
|
||||||
|
ColorTemperatureRange {
|
||||||
|
temperature_min_k: 2200,
|
||||||
|
temperature_max_k: 4000,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn color(&self) -> Color {
|
||||||
|
let state = self.state().await;
|
||||||
|
let state: StateColorTemperature = state.deref().clone().into();
|
||||||
|
|
||||||
|
let temperature = 1_000_000 / state.color_temp;
|
||||||
|
|
||||||
|
Color { temperature }
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn set_color(&self, color: Color) -> Result<(), ErrorCode> {
|
||||||
|
let temperature = 1_000_000 / color.temperature;
|
||||||
|
|
||||||
|
let message = json!({
|
||||||
|
"color_temp": temperature,
|
||||||
|
});
|
||||||
|
|
||||||
|
let topic = format!("{}/set", self.config.mqtt.topic);
|
||||||
|
// TODO: Handle potential errors here
|
||||||
|
self.config
|
||||||
|
.client
|
||||||
|
.publish(
|
||||||
|
&topic,
|
||||||
|
rumqttc::QoS::AtLeastOnce,
|
||||||
|
false,
|
||||||
|
serde_json::to_string(&message).unwrap(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.map_err(|err| warn!("Failed to update state on {topic}: {err}"))
|
||||||
|
.ok();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user