Skip to content

Commit 56e454b

Browse files
authored
Merge pull request #135 from chrivers/chrivers/apiv1-entertainment-mode
Implement complete support for entertainment zones ("sync mode") for the v1 api, including the obsolete (but apparently still used) streaming v1 api! This fixes support for at least the following: - Philips Ambilight TVs (..ironically) - The iLightShow app for streaming blinkenlights This change also improves logging and error handling related to sync streaming.
2 parents dff6b84 + 2cc406c commit 56e454b

10 files changed

Lines changed: 558 additions & 189 deletions

File tree

crates/bifrost-api/src/backend.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use serde::{Deserialize, Serialize};
22
use uuid::Uuid;
33

44
use hue::api::{GroupedLightUpdate, LightUpdate, ResourceLink, RoomUpdate, Scene, SceneUpdate};
5-
use hue::stream::HueStreamLights;
5+
use hue::stream::HueStreamLightsV2;
66

77
use crate::Client;
88
use crate::config::Z2mServer;
@@ -23,7 +23,7 @@ pub enum BackendRequest {
2323
Delete(ResourceLink),
2424

2525
EntertainmentStart(Uuid),
26-
EntertainmentFrame(HueStreamLights),
26+
EntertainmentFrame(HueStreamLightsV2),
2727
EntertainmentStop(),
2828
}
2929

crates/hue/src/api/device.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,19 @@ pub struct DeviceUpdate {
3030
}
3131

3232
impl Device {
33+
#[must_use]
34+
pub fn service(&self, rtype: RType) -> Option<&ResourceLink> {
35+
self.services.iter().find(|rl| rl.rtype == rtype)
36+
}
37+
3338
#[must_use]
3439
pub fn light_service(&self) -> Option<&ResourceLink> {
35-
self.services.iter().find(|rl| rl.rtype == RType::Light)
40+
self.service(RType::Light)
41+
}
42+
43+
#[must_use]
44+
pub fn entertainment_service(&self) -> Option<&ResourceLink> {
45+
self.service(RType::Entertainment)
3646
}
3747
}
3848

crates/hue/src/api/entertainment_config.rs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ pub enum EntertainmentConfigurationType {
104104
Other,
105105
}
106106

107-
#[derive(Debug, Serialize, Deserialize, Clone)]
107+
#[derive(Debug, Serialize, Deserialize, Clone, Default)]
108108
pub struct EntertainmentConfigurationUpdate {
109109
pub configuration_type: Option<EntertainmentConfigurationType>,
110110
pub metadata: Option<EntertainmentConfigurationMetadata>,
@@ -113,6 +113,13 @@ pub struct EntertainmentConfigurationUpdate {
113113
pub locations: Option<EntertainmentConfigurationLocationsUpdate>,
114114
}
115115

116+
impl EntertainmentConfigurationUpdate {
117+
#[must_use]
118+
pub fn new() -> Self {
119+
Self::default()
120+
}
121+
}
122+
116123
#[derive(Debug, Serialize, Deserialize, Clone)]
117124
#[serde(deny_unknown_fields)]
118125
pub struct EntertainmentConfigurationNew {
@@ -196,6 +203,18 @@ impl From<EntertainmentConfigurationServiceLocationsNew>
196203
}
197204
}
198205

206+
impl From<EntertainmentConfigurationLocationsNew> for EntertainmentConfigurationLocationsUpdate {
207+
fn from(value: EntertainmentConfigurationLocationsNew) -> Self {
208+
Self {
209+
service_locations: value
210+
.service_locations
211+
.into_iter()
212+
.map(Into::into)
213+
.collect(),
214+
}
215+
}
216+
}
217+
199218
#[derive(Debug, Serialize, Deserialize, Clone)]
200219
pub struct EntertainmentConfigurationServiceLocationsUpdate {
201220
pub equalization_factor: Option<f64>,

crates/hue/src/legacy_api.rs

Lines changed: 22 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -122,19 +122,14 @@ pub struct NewUserReply {
122122
}
123123

124124
#[allow(non_camel_case_types)]
125-
#[derive(Debug, Serialize, Deserialize)]
125+
#[derive(Debug, Serialize, Deserialize, Default)]
126126
#[serde(rename_all = "lowercase")]
127127
pub enum ConnectionState {
128128
Connected,
129+
#[default]
129130
Disconnected,
130131
}
131132

132-
impl Default for ConnectionState {
133-
fn default() -> Self {
134-
Self::Disconnected
135-
}
136-
}
137-
138133
#[derive(Debug, Serialize, Deserialize)]
139134
pub struct ApiInternetServices {
140135
pub internet: ConnectionState,
@@ -301,15 +296,16 @@ pub struct ApiGroupAction {
301296
pub colormode: Option<LightColorMode>,
302297
}
303298

304-
#[derive(Debug, Serialize, Deserialize)]
299+
#[derive(Debug, Serialize, Deserialize, Default, PartialEq, Eq)]
305300
pub enum ApiGroupType {
306301
Entertainment,
302+
#[default]
307303
LightGroup,
308304
Room,
309305
Zone,
310306
}
311307

312-
#[derive(Debug, Serialize, Deserialize)]
308+
#[derive(Debug, Serialize, Deserialize, Default, PartialEq, Eq)]
313309
pub enum ApiGroupClass {
314310
#[serde(rename = "Living room")]
315311
LivingRoom,
@@ -332,6 +328,7 @@ pub enum ApiGroupClass {
332328
Garden,
333329
Driveway,
334330
Carport,
331+
#[default]
335332
Other,
336333

337334
Home,
@@ -380,6 +377,16 @@ pub struct ApiGroup {
380377
pub locations: Value,
381378
}
382379

380+
#[derive(Debug, Serialize, Deserialize)]
381+
pub struct ApiGroupNew {
382+
pub name: Option<String>,
383+
#[serde(default, rename = "type")]
384+
pub group_type: ApiGroupType,
385+
#[serde(default)]
386+
pub class: ApiGroupClass,
387+
pub lights: Vec<String>,
388+
}
389+
383390
impl ApiGroup {
384391
#[must_use]
385392
pub fn make_group_0() -> Self {
@@ -388,7 +395,7 @@ impl ApiGroup {
388395
lights: vec![],
389396
action: ApiGroupAction::default(),
390397
group_type: ApiGroupType::LightGroup,
391-
class: ApiGroupClass::Other,
398+
class: ApiGroupClass::default(),
392399
recycle: false,
393400
sensors: vec![],
394401
state: ApiGroupState::default(),
@@ -418,7 +425,7 @@ impl ApiGroup {
418425
alert: ApiAlert::None,
419426
colormode: None,
420427
},
421-
class: ApiGroupClass::Other,
428+
class: ApiGroupClass::default(),
422429
group_type: ApiGroupType::Room,
423430
recycle: false,
424431
sensors: vec![],
@@ -427,31 +434,6 @@ impl ApiGroup {
427434
locations: Value::Null,
428435
}
429436
}
430-
431-
#[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
432-
#[must_use]
433-
pub fn from_entertainment_configuration(ent: &api::EntertainmentConfiguration) -> Self {
434-
Self {
435-
name: ent.metadata.name.clone(),
436-
// FIXME: obviously don't hard-code light numbers here
437-
lights: vec!["9".into()],
438-
locations: json!({
439-
"9": [0, 0.8, 0],
440-
}),
441-
action: ApiGroupAction::default(),
442-
class: ApiGroupClass::Other,
443-
group_type: ApiGroupType::Entertainment,
444-
recycle: false,
445-
sensors: vec![],
446-
state: ApiGroupState::default(),
447-
stream: json!({
448-
"active": false,
449-
"owner": Value::Null,
450-
"proxymode": "auto",
451-
"proxynode": "/bridge"
452-
}),
453-
}
454-
}
455437
}
456438

457439
#[derive(Debug, Serialize, Deserialize, Default)]
@@ -509,14 +491,16 @@ pub struct ApiGroupUpdate {
509491
pub scene: String,
510492
}
511493

512-
#[derive(Debug, Serialize, Deserialize)]
494+
#[derive(Debug, Serialize, Deserialize, Clone, Copy)]
513495
pub struct Active {
514496
pub active: bool,
515497
}
516498

517499
#[derive(Debug, Serialize, Deserialize)]
518500
pub struct ApiGroupUpdate2 {
519-
pub stream: Active,
501+
pub lights: Option<Vec<String>>,
502+
pub name: Option<String>,
503+
pub stream: Option<Active>,
520504
}
521505

522506
#[derive(Debug, Serialize, Deserialize)]

0 commit comments

Comments
 (0)