184 lines
5.5 KiB
Rust
184 lines
5.5 KiB
Rust
use crate::light::{Command, Light, Status, Update, Value};
|
|
use crossbeam_channel::RecvTimeoutError;
|
|
use lifxi::http::prelude::*;
|
|
use log::{debug, info, trace, warn};
|
|
use std::time::Duration;
|
|
|
|
pub struct Lifx {
|
|
client: Client,
|
|
updates: crossbeam_channel::Sender<Status>,
|
|
commands: crossbeam_channel::Receiver<Command>,
|
|
lights: Vec<Light>,
|
|
}
|
|
|
|
impl Lifx {
|
|
pub fn new<S: ToString>(
|
|
secret: S,
|
|
updates: crossbeam_channel::Sender<Status>,
|
|
commands: crossbeam_channel::Receiver<Command>,
|
|
) -> Self {
|
|
Lifx {
|
|
client: Client::new(secret),
|
|
updates,
|
|
commands,
|
|
lights: vec![],
|
|
}
|
|
}
|
|
|
|
pub fn get_lights(&self) -> Option<Vec<Light>> {
|
|
let response = self.client.select(Selector::All).list().send();
|
|
match response {
|
|
Ok(mut json_response) => match json_response.json() {
|
|
Ok(light) => {
|
|
trace!("{:#?}", light);
|
|
light
|
|
}
|
|
Err(err) => {
|
|
warn!("{}", err);
|
|
None
|
|
}
|
|
},
|
|
Err(err) => {
|
|
warn!("{}", err);
|
|
None
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn listen(&mut self) {
|
|
info!("Listening for lifx commands and updates");
|
|
loop {
|
|
match self.commands.recv_timeout(Duration::from_secs(5)) {
|
|
Ok(command) => self.handle_command(command),
|
|
Err(RecvTimeoutError::Disconnected) => return,
|
|
Err(RecvTimeoutError::Timeout) => {}
|
|
}
|
|
|
|
debug!("Updating lights");
|
|
self.update_lights();
|
|
}
|
|
}
|
|
|
|
fn update_lights(&mut self) {
|
|
if let Some(new_lights) = self.get_lights() {
|
|
// find changes
|
|
for new_light in &new_lights {
|
|
if let Some(old_light) = self.lights.iter().find(|x| new_light.id == x.id) {
|
|
self.find_diffs(old_light, new_light);
|
|
} else {
|
|
if let Err(err) = self.updates.send(Status::New(new_light.clone())) {
|
|
warn!("{}", err);
|
|
}
|
|
}
|
|
}
|
|
|
|
// find removed lights
|
|
self.lights
|
|
.iter()
|
|
.filter(|o| new_lights.iter().find(|n| n.id == o.id).is_none())
|
|
.for_each(|l| {
|
|
if let Err(err) = self.updates.send(Status::Remove(l.label.clone())) {
|
|
warn!("{}", err);
|
|
}
|
|
});
|
|
|
|
self.lights = new_lights;
|
|
}
|
|
}
|
|
|
|
fn find_diffs(&self, old_light: &Light, new_light: &Light) {
|
|
if old_light.power != new_light.power {
|
|
if let Err(err) = self.updates.send(Status::Update(Update::new(
|
|
&new_light.label,
|
|
Value::Power(new_light.power.clone()),
|
|
))) {
|
|
warn!("{}", err);
|
|
}
|
|
}
|
|
|
|
if (old_light.brightness - new_light.brightness).abs() < 0.01 {
|
|
if let Err(err) = self.updates.send(Status::Update(Update::new(
|
|
&new_light.label,
|
|
Value::Brightness(new_light.brightness),
|
|
))) {
|
|
warn!("{}", err);
|
|
}
|
|
}
|
|
}
|
|
|
|
fn handle_command(&self, command: Command) {
|
|
match command.command {
|
|
Value::Power(val) => {
|
|
if let Err(err) = self.set_power(command.lightname, val == "on") {
|
|
warn!("{}", err);
|
|
}
|
|
}
|
|
Value::Brightness(val) => {
|
|
if let Err(err) = self.set_brightness(command.lightname, val) {
|
|
warn!("{}", err);
|
|
}
|
|
}
|
|
Value::Hue(val) => {
|
|
if let Err(err) = self.set_hue(command.lightname, val) {
|
|
warn!("{}", err);
|
|
}
|
|
}
|
|
Value::Saturation(val) => {
|
|
if let Err(err) = self.set_saturation(command.lightname, val) {
|
|
warn!("{}", err);
|
|
}
|
|
}
|
|
Value::Kelvin(val) => {
|
|
if let Err(err) = self.set_kelvin(command.lightname, val) {
|
|
warn!("{}", err);
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
fn set_power(&self, name: String, value: bool) -> Result<(), lifxi::http::Error> {
|
|
self.client
|
|
.select(Selector::Label(name))
|
|
.change_state()
|
|
.power(value)
|
|
.send()
|
|
.and(Ok(()))
|
|
}
|
|
|
|
fn set_brightness(&self, name: String, value: f32) -> Result<(), lifxi::http::Error> {
|
|
self.client
|
|
.select(Selector::Label(name))
|
|
.change_state()
|
|
.brightness(value)
|
|
.send()
|
|
.and(Ok(()))
|
|
}
|
|
|
|
fn set_hue(&self, name: String, value: f32) -> Result<(), lifxi::http::Error> {
|
|
self.client
|
|
.select(Selector::Label(name))
|
|
.change_state()
|
|
.hue(value as i16)
|
|
.send()
|
|
.and(Ok(()))
|
|
}
|
|
|
|
fn set_saturation(&self, name: String, value: f32) -> Result<(), lifxi::http::Error> {
|
|
self.client
|
|
.select(Selector::Label(name))
|
|
.change_state()
|
|
.saturation(value)
|
|
.send()
|
|
.and(Ok(()))
|
|
}
|
|
|
|
fn set_kelvin(&self, name: String, value: i16) -> Result<(), lifxi::http::Error> {
|
|
self.client
|
|
.select(Selector::Label(name))
|
|
.change_state()
|
|
.kelvin(value)
|
|
.send()
|
|
.and(Ok(()))
|
|
}
|
|
}
|