Skip to content

Commit

Permalink
fix: one put call per connection, simplify types
Browse files Browse the repository at this point in the history
Sending multiple PUT requests to the same coap connection does not work
with the gateway where multiple GET requests works. Also reduces the type
complexity in the library.
  • Loading branch information
tirithen committed Nov 16, 2023
1 parent 1f8d1c6 commit 5923e7c
Show file tree
Hide file tree
Showing 7 changed files with 419 additions and 364 deletions.
59 changes: 33 additions & 26 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,44 +20,51 @@ until the best API has been found.
Example of a simple planing program (from `examples/light.rs`):

```rust
use tradfri_gateway::{Device, TradfriGatewayConnector};
use tradfri_gateway::{Device, TradfriGateway};

fn main() {
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Connect with gateway code
let gateway_code = "enter gateway code from the underside of your TRÅDFRI gateway";
let mut gateway = TradfriGatewayConnector::from_gateway_code(gateway_code)
.unwrap()
.connect()
.unwrap();
let mut gateway = TradfriGateway::from_gateway_code(gateway_code)?;
println!("{:#?}", gateway);

// Connect with identifier and session key
// Connect with identifier and session key once created
// let session_key = "enter pre shared key generated from gateway code";
// let identifier = "enter identifier generated along with the pre shared key";
// let mut gateway =
// TradfriGatewayConnector::from_identifier_and_session_key(identifier, session_key)
// .unwrap()
// .connect()
// .unwrap();
// TradfriGateway::from_identifier_and_session_key(identifier, session_key)?;
// println!("{:#?}", gateway);

// Get all device ids
let ids = gateway.device_ids().unwrap();
println!("ids {:#?}", ids);

// Turn on all your lights
for id in ids {
let device = gateway.device(id).unwrap();
println!("device {:#?}", device);

match device {
Device::RemoteControl => (),
Device::Light(mut light) => {
light.on().unwrap();
println!("light {:#?}", light);
},
// Toggle all your lights
for i in 0..10 {
for device in gateway.devices()? {
match device {
Ok(Device::RemoteControl) => (),
Ok(Device::Light(mut light)) => {
if i % 2 == 0 {
light.on()?;
} else {
light.off()?;
}
println!("light {:#?}", light);
}
Err(error) => panic!("{}", error),
}
}
}

// Turn on one specific light, if id exists
for i in 0..10 {
if let Ok(Device::Light(mut light)) = gateway.device(65568) {
if i % 2 == 0 {
light.on()?;
} else {
light.off()?;
}
}
}

Ok(())
}
```

Expand Down
55 changes: 31 additions & 24 deletions examples/light.rs
Original file line number Diff line number Diff line change
@@ -1,39 +1,46 @@
use tradfri_gateway::{Device, TradfriGatewayConnector};
use tradfri_gateway::{Device, TradfriGateway};

fn main() {
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Connect with gateway code
let gateway_code = "enter gateway code from the underside of your TRÅDFRI gateway";
let mut gateway = TradfriGatewayConnector::from_gateway_code(gateway_code)
.unwrap()
.connect()
.unwrap();
let mut gateway = TradfriGateway::from_gateway_code(gateway_code)?;
println!("{:#?}", gateway);

// Connect with identifier and session key
// Connect with identifier and session key once created
// let session_key = "enter pre shared key generated from gateway code";
// let identifier = "enter identifier generated along with the pre shared key";
// let mut gateway =
// TradfriGatewayConnector::from_identifier_and_session_key(identifier, session_key)
// .unwrap()
// .connect()
// .unwrap();
// TradfriGateway::from_identifier_and_session_key(identifier, session_key)?;
// println!("{:#?}", gateway);

// Get all device ids
let ids = gateway.device_ids().unwrap();
println!("ids {:#?}", ids);

// Turn on all your lights
for id in ids {
let device = gateway.device(id).unwrap();
println!("device {:#?}", device);
// Toggle all your lights
for i in 0..10 {
for device in gateway.devices()? {
match device {
Ok(Device::RemoteControl) => (),
Ok(Device::Light(mut light)) => {
if i % 2 == 0 {
light.on()?;
} else {
light.off()?;
}
println!("light {:#?}", light);
}
Err(error) => panic!("{}", error),
}
}
}

match device {
Device::RemoteControl => (),
Device::Light(mut light) => {
light.on().unwrap();
println!("light {:#?}", light);
// Turn on one specific light, if id exists
for i in 0..10 {
if let Ok(Device::Light(mut light)) = gateway.device(65568) {
if i % 2 == 0 {
light.on()?;
} else {
light.off()?;
}
}
}

Ok(())
}
145 changes: 145 additions & 0 deletions src/device/light.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
use chrono::{DateTime, NaiveDateTime, TimeZone, Utc};

use crate::{
tradfri_coap::TradfriConnection, BulbColdWarmHexUpdate, BulbParsed, BulbRgbXYUpdate,
BulbUpdate, Device, DeviceError, DeviceInfoParsed, DriverUpdate, LightDeviceParsed,
TradfriGateway, Update,
};

#[derive(Debug)]
pub struct Light {
gateway: TradfriGateway,
info: DeviceInfoParsed,
id: u32,
name: String,
creation_date: DateTime<Utc>,
last_seen: DateTime<Utc>,
reachable: bool,
bulbs: Vec<BulbParsed>,
}

impl Light {
pub fn new(gateway: TradfriGateway, bytes: &[u8]) -> Result<Self, DeviceError> {
let parsed: LightDeviceParsed = match serde_json::from_slice(bytes) {
Ok(p) => p,
Err(error) => {
return Err(DeviceError::SerdeError(
error.to_string(),
String::from_utf8_lossy(bytes).to_string(),
))
}
};

Ok(Self {
gateway,
info: parsed.info,
id: parsed.id,
name: parsed.name,
creation_date: Utc.from_utc_datetime(
&NaiveDateTime::from_timestamp_opt(parsed.creation_date.into(), 0).unwrap(),
),
last_seen: Utc.from_utc_datetime(
&NaiveDateTime::from_timestamp_opt(parsed.last_seen.into(), 0).unwrap(),
),
reachable: parsed.reachable,
bulbs: parsed.bulbs,
})
}

pub fn on(&mut self) -> Result<(), DeviceError> {
let update = Update::BulbUpdate {
bulbs: self
.bulbs
.iter()
.map(|bulb| match bulb {
BulbParsed::LedDriver(_) => BulbUpdate::DriverUpdate(DriverUpdate {
on: Some(true),
..Default::default()
}),
BulbParsed::BulbColdWarmHex(_) => {
BulbUpdate::BulbColdWarmHexUpdate(BulbColdWarmHexUpdate {
on: Some(true),
..Default::default()
})
}
BulbParsed::BulbRgbXY(_) => BulbUpdate::BulbRgbXYUpdate(BulbRgbXYUpdate {
on: Some(true),
..Default::default()
}),
})
.collect(),
};

let mut connection = self.gateway.create_connection()?;
self.gateway
.update_device(self.id, &update, Some(&mut connection))?;
self.update_with_connection(&mut connection)?;

Ok(())
}

pub fn off(&mut self) -> Result<(), DeviceError> {
let update = Update::BulbUpdate {
bulbs: self
.bulbs
.iter()
.map(|bulb| match bulb {
BulbParsed::LedDriver(_) => BulbUpdate::DriverUpdate(DriverUpdate {
on: Some(false),
..Default::default()
}),
BulbParsed::BulbColdWarmHex(_) => {
BulbUpdate::BulbColdWarmHexUpdate(BulbColdWarmHexUpdate {
on: Some(false),
..Default::default()
})
}
BulbParsed::BulbRgbXY(_) => BulbUpdate::BulbRgbXYUpdate(BulbRgbXYUpdate {
on: Some(false),
..Default::default()
}),
})
.collect(),
};

let mut connection = self.gateway.create_connection()?;
self.gateway
.update_device(self.id, &update, Some(&mut connection))?;
self.update_with_connection(&mut connection)?;

Ok(())
}

pub fn is_on(&self) -> bool {
self.bulbs
.iter()
.fold(0, |acc, b| acc + if b.is_on() { 1 } else { 0 })
> 0
}

pub fn update(&mut self) -> Result<(), DeviceError> {
let mut connection = self.gateway.create_connection()?;
self.update_with_connection(&mut connection)
}

fn update_with_connection(
&mut self,
connection: &mut TradfriConnection,
) -> Result<(), DeviceError> {
let device = self.gateway.device_with_connection(self.id, connection)?;

if let Device::Light(light) = device {
self.info = light.info;
self.id = light.id;
self.name = light.name;
self.creation_date = light.creation_date;
self.last_seen = light.last_seen;
self.reachable = light.reachable;
self.bulbs = light.bulbs;
} else {
return Err(DeviceError::ExpectedDeviceType("Light".to_string()));
}

Ok(())
}
}

0 comments on commit 5923e7c

Please sign in to comment.