Skip to content

Commit 37d65fc

Browse files
authored
Merge pull request #106 from chrivers/chrivers/hue-crate-refactor
Refactor code to move all hue-related code entirely into the hue crate (`crates/hue/`) This improves rebuild times, and makes it easier to reuse parts of the Bifrost code in other projects.
2 parents 20f930b + 5fd709b commit 37d65fc

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

55 files changed

+395
-332
lines changed

crates/hue/Cargo.toml

+6-2
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,15 @@ workspace = true
1818
[dependencies]
1919
bitflags = "2.8.0"
2020
byteorder = "1.5.0"
21-
chrono = { version = "0.4.39", default-features = false }
21+
chrono = { version = "0.4.39", default-features = false, features = ["clock", "std"] }
22+
hex = "0.4.3"
23+
iana-time-zone = "0.1.61"
24+
mac_address = { version = "1.1.8", features = ["serde"] }
2225
packed_struct = "0.10.1"
2326
serde = { version = "1.0.217", features = ["derive"] }
27+
serde_json = "1.0.140"
2428
thiserror = "2.0.11"
25-
uuid = "1.13.1"
29+
uuid = { version = "1.13.1", features = ["serde", "v5"] }
2630

2731
[dev-dependencies]
2832
hex = "0.4.3"

src/hue/api/device.rs crates/hue/src/api/device.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ use std::ops::{AddAssign, Sub};
33

44
use serde::{Deserialize, Serialize};
55

6-
use crate::hue::api::{Metadata, MetadataUpdate, RType, ResourceLink, Stub};
7-
use crate::hue::version::SwVersion;
8-
use crate::hue::HUE_BRIDGE_V2_MODEL_ID;
6+
use crate::api::{Metadata, MetadataUpdate, RType, ResourceLink, Stub};
7+
use crate::version::SwVersion;
8+
use crate::HUE_BRIDGE_V2_MODEL_ID;
99

1010
#[derive(Debug, Serialize, Deserialize, Clone)]
1111
pub struct Device {

src/hue/api/entertainment.rs crates/hue/src/api/entertainment.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use serde::{Deserialize, Serialize};
22

3-
use crate::hue::api::ResourceLink;
3+
use crate::api::ResourceLink;
44

55
#[derive(Debug, Serialize, Deserialize, Clone)]
66
pub struct Entertainment {

src/hue/api/entertainment_config.rs crates/hue/src/api/entertainment_config.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use serde::{Deserialize, Serialize};
22

3-
use crate::hue::api::ResourceLink;
3+
use crate::api::ResourceLink;
44

55
#[derive(Debug, Serialize, Deserialize, Clone)]
66
pub struct EntertainmentConfiguration {

src/hue/api/grouped_light.rs crates/hue/src/api/grouped_light.rs

+2-3
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
use serde::{Deserialize, Serialize};
22
use serde_json::Value;
33

4-
use hue::xy::XY;
5-
6-
use crate::hue::api::{ColorTemperatureUpdate, ColorUpdate, DimmingUpdate, On, ResourceLink, Stub};
4+
use crate::api::{ColorTemperatureUpdate, ColorUpdate, DimmingUpdate, On, ResourceLink, Stub};
5+
use crate::xy::XY;
76

87
#[derive(Debug, Serialize, Deserialize, Clone)]
98
pub struct GroupedLight {

src/hue/api/light.rs crates/hue/src/api/light.rs

+2-3
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,8 @@ use std::ops::{AddAssign, Sub};
44
use serde::{Deserialize, Serialize};
55
use serde_json::{json, Value};
66

7-
use hue::xy::XY;
8-
9-
use crate::hue::api::{DeviceArchetype, Identify, Metadata, MetadataUpdate, ResourceLink, Stub};
7+
use crate::api::{DeviceArchetype, Identify, Metadata, MetadataUpdate, ResourceLink, Stub};
8+
use crate::xy::XY;
109

1110
#[derive(Debug, Serialize, Deserialize, Clone)]
1211
pub struct Light {

src/hue/api/mod.rs crates/hue/src/api/mod.rs

+12-18
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,8 @@ use std::fmt::Debug;
5757
use serde::{Deserialize, Serialize};
5858
use serde_json::{from_value, json, Value};
5959

60-
use crate::error::{ApiError, ApiResult};
61-
use crate::hue::legacy_api::ApiLightStateUpdate;
60+
use crate::error::{HueError, HueResult};
61+
use crate::legacy_api::ApiLightStateUpdate;
6262

6363
#[derive(Debug, Deserialize, Clone, Default)]
6464
#[serde(deny_unknown_fields)]
@@ -189,7 +189,7 @@ impl Resource {
189189
}
190190
}
191191

192-
pub fn from_value(rtype: RType, obj: Value) -> ApiResult<Self> {
192+
pub fn from_value(rtype: RType, obj: Value) -> HueResult<Self> {
193193
let res = match rtype {
194194
RType::AuthV1 => Self::AuthV1(from_value(obj)?),
195195
RType::BehaviorInstance => Self::BehaviorInstance(from_value(obj)?),
@@ -232,37 +232,37 @@ impl Resource {
232232
macro_rules! resource_conversion_impl {
233233
( $name:ident ) => {
234234
impl<'a> TryFrom<&'a mut Resource> for &'a mut $name {
235-
type Error = ApiError;
235+
type Error = HueError;
236236

237237
fn try_from(value: &'a mut Resource) -> Result<Self, Self::Error> {
238238
if let Resource::$name(obj) = value {
239239
Ok(obj)
240240
} else {
241-
Err(ApiError::WrongType(RType::Light, value.rtype()))
241+
Err(HueError::WrongType(RType::Light, value.rtype()))
242242
}
243243
}
244244
}
245245

246246
impl<'a> TryFrom<&'a Resource> for &'a $name {
247-
type Error = ApiError;
247+
type Error = HueError;
248248

249249
fn try_from(value: &'a Resource) -> Result<Self, Self::Error> {
250250
if let Resource::$name(obj) = value {
251251
Ok(obj)
252252
} else {
253-
Err(ApiError::WrongType(RType::Light, value.rtype()))
253+
Err(HueError::WrongType(RType::Light, value.rtype()))
254254
}
255255
}
256256
}
257257

258258
impl TryFrom<Resource> for $name {
259-
type Error = ApiError;
259+
type Error = HueError;
260260

261261
fn try_from(value: Resource) -> Result<Self, Self::Error> {
262262
if let Resource::$name(obj) = value {
263263
Ok(obj)
264264
} else {
265-
Err(ApiError::WrongType(RType::Light, value.rtype()))
265+
Err(HueError::WrongType(RType::Light, value.rtype()))
266266
}
267267
}
268268
}
@@ -303,12 +303,6 @@ resource_conversion_impl!(ZigbeeConnectivity);
303303
resource_conversion_impl!(ZigbeeDeviceDiscovery);
304304
resource_conversion_impl!(Zone);
305305

306-
#[derive(Debug, Serialize, Deserialize)]
307-
pub struct V2Reply<T> {
308-
pub data: Vec<T>,
309-
pub errors: Vec<String>,
310-
}
311-
312306
#[derive(Clone, Debug, Serialize)]
313307
pub struct V1Reply<'a> {
314308
prefix: String,
@@ -339,19 +333,19 @@ impl<'a> V1Reply<'a> {
339333
Self::new(format!("/groups/{id}"))
340334
}
341335

342-
pub fn with_light_state_update(self, upd: &ApiLightStateUpdate) -> ApiResult<Self> {
336+
pub fn with_light_state_update(self, upd: &ApiLightStateUpdate) -> HueResult<Self> {
343337
self.add_option("on", upd.on)?
344338
.add_option("bri", upd.bri)?
345339
.add_option("xy", upd.xy)?
346340
.add_option("ct", upd.ct)
347341
}
348342

349-
pub fn add<T: Serialize>(mut self, name: &'a str, value: T) -> ApiResult<Self> {
343+
pub fn add<T: Serialize>(mut self, name: &'a str, value: T) -> HueResult<Self> {
350344
self.success.push((name, serde_json::to_value(value)?));
351345
Ok(self)
352346
}
353347

354-
pub fn add_option<T: Serialize>(mut self, name: &'a str, value: Option<T>) -> ApiResult<Self> {
348+
pub fn add_option<T: Serialize>(mut self, name: &'a str, value: Option<T>) -> HueResult<Self> {
355349
if let Some(val) = value {
356350
self.success.push((name, serde_json::to_value(val)?));
357351
}

src/hue/api/resource.rs crates/hue/src/api/resource.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use std::hash::{DefaultHasher, Hash, Hasher};
44
use serde::{Deserialize, Serialize};
55
use uuid::Uuid;
66

7-
use crate::hue::api::Resource;
7+
use crate::api::Resource;
88

99
#[derive(Copy, Debug, Serialize, Deserialize, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
1010
#[serde(rename_all = "snake_case")]

src/hue/api/room.rs crates/hue/src/api/room.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use std::ops::{AddAssign, Sub};
33

44
use serde::{Deserialize, Serialize};
55

6-
use crate::hue::api::{RType, ResourceLink};
6+
use crate::api::{RType, ResourceLink};
77

88
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
99
pub struct RoomMetadata {

src/hue/api/scene.rs crates/hue/src/api/scene.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ use chrono::{DateTime, Utc};
44
use serde::{Deserialize, Serialize};
55
use serde_json::Value;
66

7-
use crate::hue::api::{ColorTemperatureUpdate, ColorUpdate, DimmingUpdate, On, ResourceLink};
8-
use crate::hue::date_format;
7+
use crate::api::{ColorTemperatureUpdate, ColorUpdate, DimmingUpdate, On, ResourceLink};
8+
use crate::date_format;
99

1010
#[derive(Copy, Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
1111
#[serde(rename_all = "snake_case")]

src/hue/api/stream.rs crates/hue/src/api/stream.rs

+7-7
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use hex::FromHexError;
22

3-
use crate::error::{ApiError, ApiResult};
3+
use crate::error::{HueError, HueResult};
44

55
pub struct HueStreamKey {
66
key: [u8; Self::BYTE_SIZE],
@@ -15,9 +15,9 @@ impl HueStreamKey {
1515
Self { key }
1616
}
1717

18-
pub fn write_to_slice(&self, out: &mut [u8]) -> ApiResult<()> {
18+
pub fn write_to_slice(&self, out: &mut [u8]) -> HueResult<()> {
1919
if out.len() < Self::BYTE_SIZE {
20-
return Err(ApiError::FromHexError(FromHexError::InvalidStringLength));
20+
return Err(HueError::FromHexError(FromHexError::InvalidStringLength));
2121
}
2222
out[..Self::BYTE_SIZE].copy_from_slice(&self.key);
2323
Ok(())
@@ -28,9 +28,9 @@ impl HueStreamKey {
2828
hex::encode(self.key)
2929
}
3030

31-
pub fn to_hex_slice(&self, out: &mut [u8]) -> ApiResult<()> {
31+
pub fn to_hex_slice(&self, out: &mut [u8]) -> HueResult<()> {
3232
if out.len() < Self::HEX_SIZE {
33-
return Err(ApiError::FromHexError(FromHexError::InvalidStringLength));
33+
return Err(HueError::FromHexError(FromHexError::InvalidStringLength));
3434
}
3535
Ok(hex::encode_to_slice(self.key, &mut out[..Self::HEX_SIZE])?)
3636
}
@@ -43,12 +43,12 @@ impl AsRef<[u8]> for HueStreamKey {
4343
}
4444

4545
impl TryFrom<&str> for HueStreamKey {
46-
type Error = ApiError;
46+
type Error = HueError;
4747

4848
fn try_from(value: &str) -> Result<Self, Self::Error> {
4949
let mut key = [0u8; 16];
5050
if value.len() < Self::HEX_SIZE {
51-
return Err(ApiError::FromHexError(FromHexError::InvalidStringLength));
51+
return Err(HueError::FromHexError(FromHexError::InvalidStringLength));
5252
}
5353

5454
hex::decode_to_slice(value, &mut key)?;

src/hue/api/stubs.rs crates/hue/src/api/stubs.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ use serde::{Deserialize, Deserializer, Serialize};
55
use serde_json::Value;
66
use uuid::Uuid;
77

8-
use crate::hue::api::{DeviceArchetype, ResourceLink, SceneMetadata};
9-
use crate::hue::{best_guess_timezone, date_format};
8+
use crate::api::{DeviceArchetype, ResourceLink, SceneMetadata};
9+
use crate::{best_guess_timezone, date_format};
1010

1111
#[derive(Debug, Serialize, Deserialize, Clone)]
1212
pub struct Bridge {

src/hue/api/update.rs crates/hue/src/api/update.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use serde::{Deserialize, Serialize};
22
use uuid::Uuid;
33

4-
use crate::hue::api::{
4+
use crate::api::{
55
DeviceUpdate, EntertainmentConfigurationUpdate, GroupedLightUpdate, LightUpdate, RType,
66
RoomUpdate, SceneUpdate,
77
};

src/hue/date_format.rs crates/hue/src/date_format.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -164,15 +164,15 @@ pub mod legacy_utc_opt {
164164
mod tests {
165165
use chrono::{DateTime, TimeZone, Utc};
166166

167-
use crate::error::ApiResult;
167+
use crate::error::HueResult;
168168

169169
fn date() -> (&'static str, DateTime<Utc>) {
170170
let dt = Utc.with_ymd_and_hms(2014, 7, 8, 9, 10, 11).unwrap();
171171
("\"2014-07-08T09:10:11Z\"", dt)
172172
}
173173

174174
#[test]
175-
fn utc_de() -> ApiResult<()> {
175+
fn utc_de() -> HueResult<()> {
176176
let (ds, d1) = date();
177177

178178
let mut deser = serde_json::Deserializer::from_str(ds);
@@ -183,7 +183,7 @@ mod tests {
183183
}
184184

185185
#[test]
186-
fn utc_se() -> ApiResult<()> {
186+
fn utc_se() -> HueResult<()> {
187187
let (s1, dt) = date();
188188

189189
let mut s2 = vec![];

src/hue/devicedb.rs crates/hue/src/devicedb.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::hue::api::{DeviceArchetype, DeviceProductData};
1+
use crate::api::{DeviceArchetype, DeviceProductData};
22

33
// This file contains discovered product data from multiple sources,
44
// including data samples from the community, and various open source or public

crates/hue/src/error.rs

+26
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
use thiserror::Error;
22

3+
use crate::api::{RType, ResourceLink};
4+
35
#[derive(Error, Debug)]
46
pub enum HueError {
57
/* mapped errors */
@@ -9,9 +11,15 @@ pub enum HueError {
911
#[error(transparent)]
1012
IOError(#[from] std::io::Error),
1113

14+
#[error(transparent)]
15+
SerdeJson(#[from] serde_json::Error),
16+
1217
#[error(transparent)]
1318
TryFromIntError(#[from] std::num::TryFromIntError),
1419

20+
#[error(transparent)]
21+
FromHexError(#[from] hex::FromHexError),
22+
1523
#[error(transparent)]
1624
PackedStructError(#[from] packed_struct::PackingError),
1725

@@ -26,6 +34,24 @@ pub enum HueError {
2634

2735
#[error("Failed to decode Hue Zigbee Update: Unknown flags {0:04x}")]
2836
HueZigbeeUnknownFlags(u16),
37+
38+
#[error("State changes not supported for: {0:?}")]
39+
UpdateUnsupported(RType),
40+
41+
#[error("Resource {0} not found")]
42+
NotFound(uuid::Uuid),
43+
44+
#[error("Resource {0} not found")]
45+
V1NotFound(u32),
46+
47+
#[error("Missing auxiliary data resource {0:?}")]
48+
AuxNotFound(ResourceLink),
49+
50+
#[error("Cannot allocate any more {0:?}")]
51+
Full(RType),
52+
53+
#[error("Resource type wrong: expected {0:?} but found {1:?}")]
54+
WrongType(RType, RType),
2955
}
3056

3157
pub type HueResult<T> = Result<T, HueError>;

src/hue/event.rs crates/hue/src/event.rs

+5-8
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,9 @@ use serde::{Deserialize, Serialize};
33
use serde_json::{json, Value};
44
use uuid::Uuid;
55

6-
use crate::{
7-
error::ApiResult,
8-
hue::api::{self, ResourceLink},
9-
};
10-
11-
use super::date_format;
6+
use crate::api::{self, ResourceLink};
7+
use crate::date_format;
8+
use crate::error::HueResult;
129

1310
#[derive(Debug, Serialize, Deserialize, Clone)]
1411
#[serde(rename_all = "lowercase", tag = "type")]
@@ -38,7 +35,7 @@ impl EventBlock {
3835
}
3936
}
4037

41-
pub fn update(id: &Uuid, id_v1: Option<u32>, data: api::Update) -> ApiResult<Self> {
38+
pub fn update(id: &Uuid, id_v1: Option<u32>, data: api::Update) -> HueResult<Self> {
4239
Ok(Self {
4340
creationtime: Utc::now(),
4441
id: Uuid::new_v4(),
@@ -50,7 +47,7 @@ impl EventBlock {
5047
})
5148
}
5249

53-
pub fn delete(link: &ResourceLink) -> ApiResult<Self> {
50+
pub fn delete(link: &ResourceLink) -> HueResult<Self> {
5451
Ok(Self {
5552
creationtime: Utc::now(),
5653
id: Uuid::new_v4(),

0 commit comments

Comments
 (0)