lifx-mqtt-bridge/src/lifx.rs

184 lines
5.5 KiB
Rust
Raw Permalink Normal View History

2019-01-20 09:02:44 +00:00
use crate::light::{Command, Light, Status, Update, Value};
use crossbeam_channel::RecvTimeoutError;
2019-01-15 20:41:43 +00:00
use lifxi::http::prelude::*;
2019-02-09 17:32:23 +00:00
use log::{debug, info, trace, warn};
2019-01-20 09:02:44 +00:00
use std::time::Duration;
2019-01-15 20:41:43 +00:00
pub struct Lifx {
client: Client,
2019-01-16 21:46:12 +00:00
updates: crossbeam_channel::Sender<Status>,
commands: crossbeam_channel::Receiver<Command>,
2019-01-20 09:02:44 +00:00
lights: Vec<Light>,
2019-01-15 20:41:43 +00:00
}
impl Lifx {
2019-01-16 21:46:12 +00:00
pub fn new<S: ToString>(
secret: S,
updates: crossbeam_channel::Sender<Status>,
commands: crossbeam_channel::Receiver<Command>,
) -> Self {
2019-01-15 20:41:43 +00:00
Lifx {
client: Client::new(secret),
2019-01-16 21:46:12 +00:00
updates,
commands,
2019-01-20 09:02:44 +00:00
lights: vec![],
2019-01-15 20:41:43 +00:00
}
}
2019-01-20 12:02:39 +00:00
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() {
2019-02-09 17:32:23 +00:00
Ok(light) => {
trace!("{:#?}", light);
light
}
2019-01-20 12:02:39 +00:00
Err(err) => {
warn!("{}", err);
None
}
},
Err(err) => {
warn!("{}", err);
None
}
}
2019-01-15 20:41:43 +00:00
}
2019-01-20 09:02:44 +00:00
pub fn listen(&mut self) {
2019-02-09 17:32:23 +00:00
info!("Listening for lifx commands and updates");
2019-01-20 09:02:44 +00:00
loop {
2019-02-09 17:32:23 +00:00
match self.commands.recv_timeout(Duration::from_secs(5)) {
2019-01-20 09:02:44 +00:00
Ok(command) => self.handle_command(command),
Err(RecvTimeoutError::Disconnected) => return,
Err(RecvTimeoutError::Timeout) => {}
}
2019-02-09 17:32:23 +00:00
debug!("Updating lights");
2019-01-20 09:02:44 +00:00
self.update_lights();
}
}
fn update_lights(&mut self) {
2019-01-20 12:02:39 +00:00
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);
}
}
2019-01-20 09:02:44 +00:00
}
2019-02-09 17:32:23 +00:00
// find removed lights
2019-01-20 12:02:39 +00:00
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);
}
});
2019-01-20 09:02:44 +00:00
2019-01-20 12:02:39 +00:00
self.lights = new_lights;
}
2019-01-20 09:02:44 +00:00
}
fn find_diffs(&self, old_light: &Light, new_light: &Light) {
if old_light.power != new_light.power {
2019-01-20 12:02:39 +00:00
if let Err(err) = self.updates.send(Status::Update(Update::new(
&new_light.label,
Value::Power(new_light.power.clone()),
))) {
warn!("{}", err);
}
2019-01-20 09:02:44 +00:00
}
if (old_light.brightness - new_light.brightness).abs() < 0.01 {
2019-01-20 12:02:39 +00:00
if let Err(err) = self.updates.send(Status::Update(Update::new(
&new_light.label,
Value::Brightness(new_light.brightness),
))) {
warn!("{}", err);
}
2019-01-20 09:02:44 +00:00
}
}
fn handle_command(&self, command: Command) {
match command.command {
2019-01-20 12:02:39 +00:00
Value::Power(val) => {
2019-02-09 17:32:23 +00:00
if let Err(err) = self.set_power(command.lightname, val == "on") {
2019-01-20 12:02:39 +00:00
warn!("{}", err);
}
}
Value::Brightness(val) => {
2019-02-09 17:32:23 +00:00
if let Err(err) = self.set_brightness(command.lightname, val) {
2019-01-20 12:02:39 +00:00
warn!("{}", err);
}
}
Value::Hue(val) => {
2019-02-09 17:32:23 +00:00
if let Err(err) = self.set_hue(command.lightname, val) {
2019-01-20 12:02:39 +00:00
warn!("{}", err);
}
}
Value::Saturation(val) => {
2019-02-09 17:32:23 +00:00
if let Err(err) = self.set_saturation(command.lightname, val) {
2019-01-20 12:02:39 +00:00
warn!("{}", err);
}
}
Value::Kelvin(val) => {
2019-02-09 17:32:23 +00:00
if let Err(err) = self.set_kelvin(command.lightname, val) {
2019-01-20 12:02:39 +00:00
warn!("{}", err);
}
}
2019-01-20 09:02:44 +00:00
};
}
2019-02-09 17:32:23 +00:00
fn set_power(&self, name: String, value: bool) -> Result<(), lifxi::http::Error> {
2019-01-20 12:02:39 +00:00
self.client
2019-02-09 17:32:23 +00:00
.select(Selector::Label(name))
2019-01-20 12:02:39 +00:00
.change_state()
.power(value)
.send()
.and(Ok(()))
}
2019-02-09 17:32:23 +00:00
fn set_brightness(&self, name: String, value: f32) -> Result<(), lifxi::http::Error> {
2019-01-20 12:02:39 +00:00
self.client
2019-02-09 17:32:23 +00:00
.select(Selector::Label(name))
2019-01-20 12:02:39 +00:00
.change_state()
.brightness(value)
.send()
.and(Ok(()))
}
2019-02-09 17:32:23 +00:00
fn set_hue(&self, name: String, value: f32) -> Result<(), lifxi::http::Error> {
2019-01-20 12:02:39 +00:00
self.client
2019-02-09 17:32:23 +00:00
.select(Selector::Label(name))
2019-01-20 12:02:39 +00:00
.change_state()
2019-02-09 17:32:23 +00:00
.hue(value as i16)
2019-01-20 12:02:39 +00:00
.send()
.and(Ok(()))
}
2019-02-09 17:32:23 +00:00
fn set_saturation(&self, name: String, value: f32) -> Result<(), lifxi::http::Error> {
2019-01-15 20:41:43 +00:00
self.client
2019-02-09 17:32:23 +00:00
.select(Selector::Label(name))
2019-01-15 20:41:43 +00:00
.change_state()
2019-01-20 12:02:39 +00:00
.saturation(value)
2019-01-15 20:41:43 +00:00
.send()
.and(Ok(()))
}
2019-02-09 17:32:23 +00:00
fn set_kelvin(&self, name: String, value: i16) -> Result<(), lifxi::http::Error> {
2019-01-15 20:41:43 +00:00
self.client
2019-02-09 17:32:23 +00:00
.select(Selector::Label(name))
2019-01-15 20:41:43 +00:00
.change_state()
2019-01-20 12:02:39 +00:00
.kelvin(value)
2019-01-15 20:41:43 +00:00
.send()
.and(Ok(()))
}
}