Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions crates/hue/src/api/grouped_light.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,27 @@ impl GroupedLight {
}
}

#[derive(Debug, Default, Serialize, Deserialize, Clone)]
pub struct GroupedLightDynamicsUpdate {
#[serde(skip_serializing_if = "Option::is_none")]
pub duration: Option<u32>,
}

impl GroupedLightDynamicsUpdate {
#[must_use]
pub fn new() -> Self {
Self::default()
}

#[must_use]
pub fn with_duration(self, duration: Option<impl Into<u32>>) -> Self {
Self {
duration: duration.map(Into::into),
..self
}
}
}

#[derive(Debug, Serialize, Deserialize, Clone, Default)]
pub struct GroupedLightUpdate {
#[serde(skip_serializing_if = "Option::is_none")]
Expand All @@ -59,6 +80,8 @@ pub struct GroupedLightUpdate {
pub color_temperature: Option<ColorTemperatureUpdate>,
#[serde(skip_serializing_if = "Option::is_none")]
pub owner: Option<ResourceLink>,
#[serde(skip_serializing_if = "Option::is_none")]
pub dynamics: Option<GroupedLightDynamicsUpdate>,
}

impl GroupedLightUpdate {
Expand Down Expand Up @@ -103,6 +126,11 @@ impl GroupedLightUpdate {
..self
}
}

#[must_use]
pub const fn with_dynamics(self, dynamics: Option<GroupedLightDynamicsUpdate>) -> Self {
Self { dynamics, ..self }
}
}

/* conversion from v1 api */
Expand All @@ -113,5 +141,9 @@ impl From<&ApiLightStateUpdate> for GroupedLightUpdate {
.with_brightness(upd.bri.map(|b| f64::from(b) / 2.54))
.with_color_xy(upd.xy.map(XY::from))
.with_color_temperature(upd.ct)
.with_dynamics(
upd.transitiontime
.map(|t| GroupedLightDynamicsUpdate::new().with_duration(Some(t * 100))),
)
}
}
32 changes: 26 additions & 6 deletions crates/hue/src/api/light.rs
Original file line number Diff line number Diff line change
Expand Up @@ -449,16 +449,27 @@ pub struct LightDynamics {
pub speed_valid: bool,
}

#[derive(Debug, Serialize, Deserialize, Clone)]
#[derive(Debug, Serialize, Deserialize, Clone, Default)]
pub struct LightDynamicsUpdate {
#[serde(skip_serializing_if = "Option::is_none")]
pub status: Option<LightDynamicsStatus>,
#[serde(skip_serializing_if = "Option::is_none")]
pub status_values: Option<Vec<LightDynamicsStatus>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub speed: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub speed_valid: Option<bool>,
pub duration: Option<u32>,
}

impl LightDynamicsUpdate {
#[must_use]
pub fn new() -> Self {
Self::default()
}

#[must_use]
pub fn with_duration(self, duration: Option<impl Into<u32>>) -> Self {
Self {
duration: duration.map(Into::into),
..self
}
}
}

impl Default for LightDynamics {
Expand Down Expand Up @@ -688,6 +699,11 @@ impl LightUpdate {
pub fn with_gradient(self, gradient: Option<LightGradientUpdate>) -> Self {
Self { gradient, ..self }
}

#[must_use]
pub fn with_dynamics(self, dynamics: Option<LightDynamicsUpdate>) -> Self {
Self { dynamics, ..self }
}
}

impl From<&ApiLightStateUpdate> for LightUpdate {
Expand All @@ -698,6 +714,10 @@ impl From<&ApiLightStateUpdate> for LightUpdate {
.with_color_temperature(upd.ct)
.with_color_hs(upd.hs.map(Into::into))
.with_color_xy(upd.xy.map(Into::into))
.with_dynamics(
upd.transitiontime
.map(|t| LightDynamicsUpdate::new().with_duration(Some(t * 100))),
)
}
}

Expand Down
3 changes: 2 additions & 1 deletion crates/hue/src/api/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -382,7 +382,8 @@ impl<'a> V1Reply<'a> {
self.add_option("on", upd.on)?
.add_option("bri", upd.bri)?
.add_option("xy", upd.xy)?
.add_option("ct", upd.ct)
.add_option("ct", upd.ct)?
.add_option("transitiontime", upd.transitiontime)
}

pub fn add<T: Serialize>(mut self, name: &'a str, value: T) -> HueResult<Self> {
Expand Down
3 changes: 3 additions & 0 deletions crates/hue/src/legacy_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -484,6 +484,8 @@ pub struct ApiLightStateUpdate {
pub ct: Option<u16>,
#[serde(skip_serializing_if = "Option::is_none", flatten)]
pub hs: Option<RawHS>,
#[serde(skip_serializing_if = "Option::is_none")]
pub transitiontime: Option<u16>,
}

#[derive(Debug, Serialize, Deserialize)]
Expand Down Expand Up @@ -519,6 +521,7 @@ impl From<api::SceneAction> for ApiLightStateUpdate {
xy: action.color.map(|col| col.xy.into()),
ct: action.color_temperature.and_then(|ct| ct.mirek),
hs: None,
transitiontime: None,
}
}
}
Expand Down
5 changes: 5 additions & 0 deletions crates/z2m/src/convert.rs
Original file line number Diff line number Diff line change
Expand Up @@ -190,5 +190,10 @@ impl From<&GroupedLightUpdate> for DeviceUpdate {
.with_brightness(upd.dimming.map(|dim| dim.brightness / 100.0 * 254.0))
.with_color_temp(upd.color_temperature.and_then(|ct| ct.mirek))
.with_color_xy(upd.color.map(|col| col.xy))
.with_transition(
upd.dynamics
.as_ref()
.and_then(|d| d.duration.map(|duration| f64::from(duration) / 1000.0)),
)
}
}
5 changes: 5 additions & 0 deletions crates/z2m/src/update.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,11 @@ impl DeviceUpdate {
..self
}
}

#[must_use]
pub fn with_transition(self, transition: Option<f64>) -> Self {
Self { transition, ..self }
}
}

#[derive(Copy, Debug, Serialize, Deserialize, Clone)]
Expand Down
14 changes: 13 additions & 1 deletion src/backend/z2m/backend_event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,11 +92,23 @@ impl Z2mBackend {
drop(lock);

/* step 1: send generic light update */
let transition = upd
.dynamics
.as_ref()
.and_then(|d| d.duration.map(|duration| f64::from(duration) / 1000.0))
.or_else(|| {
if upd.dimming.is_some() || upd.color_temperature.is_some() || upd.color.is_some() {
Some(0.4)
} else {
None
}
});
let mut payload = DeviceUpdate::default()
.with_state(upd.on.map(|on| on.on))
.with_brightness(upd.dimming.map(|dim| dim.brightness / 100.0 * 254.0))
.with_color_temp(upd.color_temperature.and_then(|ct| ct.mirek))
.with_color_xy(upd.color.map(|col| col.xy));
.with_color_xy(upd.color.map(|col| col.xy))
.with_transition(transition);

// We don't want to send gradient updates twice, but if hue
// effects are not supported for this light, this is the best
Expand Down