|
- use futures::Future;
- use huey::connection::{Light, LightGroup};
- use huey::HueError;
- use qmetaobject::*;
- use std::collections::{BTreeMap, HashMap};
- use std::sync::{Arc, Mutex, RwLock};
-
- use crate::adapters::*;
-
- cpp!{{
- #include <QtQuick/QtQuick>
- }}
-
- struct BridgeState {
- connection: huey::connection::BridgeConnection,
- groups: Option<BTreeMap<String, LightGroup>>,
- lights: Option<BTreeMap<String, Light>>,
- }
-
- impl BridgeState {
- fn new(connection: huey::connection::BridgeConnection) -> Self {
- Self {
- connection,
- groups: None,
- lights: None,
- }
- }
- }
-
- lazy_static! {
- static ref STATE: RwLock<Option<BridgeState>> = RwLock::new(None);
- }
-
- #[derive(QObject, Default)]
- pub struct BridgeConnectionConnector {
- base: qt_base_class!(trait QObject),
- init: qt_method!(fn(&self, address: String, username: String)),
- refresh: qt_method!(fn(&self)),
-
- get_lights: qt_method!(fn(&self) -> QVariant),
- get_light: qt_method!(fn(&self, id: String) -> QVariant),
- start_set_light_on: qt_method!(fn(&self, id: String, on: bool)),
- start_set_light_brightness: qt_method!(fn(&self, id: String, new_value: u8)),
- start_set_light_hue: qt_method!(fn(&self, id: String, new_value: u16)),
- start_set_light_saturation: qt_method!(fn(&self, id: String, new_value: u8)),
- start_set_light_ct: qt_method!(fn(&self, id: String, new_value: u16)),
- lights_changed: qt_signal!(),
- light_changed: qt_signal!(id: String),
-
- get_groups: qt_method!(fn(&self) -> QVariant),
- get_group: qt_method!(fn(&self, id: String) -> QVariant),
- start_set_group_on: qt_method!(fn(&self, id: String, on: bool)),
- groups_changed: qt_signal!(),
- group_changed: qt_signal!(id: String),
-
- brightness_queue: Arc<Mutex<HashMap<String, u8>>>,
- hue_queue: Arc<Mutex<HashMap<String, u16>>>,
- saturation_queue: Arc<Mutex<HashMap<String, u8>>>,
- ct_queue: Arc<Mutex<HashMap<String, u16>>>,
- }
-
- fn new_qobject(parent: &QObject, object: impl QObject) -> QJSValue {
- let parent = parent.get_cpp_object();
- let ptr = into_leaked_cpp_ptr(object);
-
- unsafe { cpp!([parent as "QObject*", ptr as "QObject*"] -> QJSValue as "QJSValue" {
- return QQmlEngine::contextForObject(parent)->engine()->newQObject(ptr);
- })}
- }
-
- fn set_prop_loop<T: PartialEq + Copy + Send + 'static>(
- make_request: impl Fn(&str, T) -> Box<Future<Item=(),Error=HueError> + Send> + Send + 'static,
- update_model: impl Fn(&str, T) + Send + 'static,
- queue: Arc<Mutex<HashMap<String, T>>>,
- id: String,
- new_value: T,
- callback: Box<Fn() + Send>
- ) -> Box<Future<Item=(), Error=()> + Send> {
- if let Some(ref state) = *STATE.read().unwrap() {
- let future = make_request(&id, new_value);
- std::mem::drop(state);
- Box::new(future
- .then(move |res| {
- match res {
- Ok(_) => {
- update_model(&id, new_value);
- },
- Err(err) => {
- eprintln!("{:?}", err);
- }
- }
-
- let mut queue_map = queue.lock().unwrap();
- let target_value = queue_map.get(&id);
- match target_value {
- Some(target_value) if *target_value != new_value => {
- println!("looping");
- set_prop_loop(make_request, update_model, queue.clone(), id, *target_value, callback)
- },
- _ => {
- queue_map.remove(&id);
- callback();
- println!("ending");
- Box::new(futures::future::ok(()))
- },
- }
- }))
- } else {
- panic!("STATE missing");
- }
- }
-
- fn set_brightness_loop(brightness_queue: Arc<Mutex<HashMap<String, u8>>>, id: String, new_value: u8, callback: Box<Fn() + Send>) -> Box<Future<Item=(), Error=()> + Send> {
- set_prop_loop(|id, new_value| {
- if let Some(ref state) = *STATE.read().unwrap() {
- Box::new(state.connection.set_light_brightness(id, new_value))
- } else {
- panic!("STATE missing");
- }
- }, |id, new_value| {
- if let Some(ref mut state) = *STATE.write().unwrap() {
- if let Some(ref mut lights) = state.lights {
- if let Some(ref mut light) = lights.get_mut(id) {
- light.state.bri = new_value;
- }
- }
- }
- }, brightness_queue, id, new_value, callback)
- }
-
- fn set_hue_loop(hue_queue: Arc<Mutex<HashMap<String, u16>>>, id: String, new_value: u16, callback: Box<Fn() + Send>) -> Box<Future<Item=(), Error=()> + Send> {
- set_prop_loop(|id, new_value| {
- if let Some(ref state) = *STATE.read().unwrap() {
- Box::new(state.connection.set_light_hue(id, new_value))
- } else {
- panic!("STATE missing");
- }
- }, |id, new_value| {
- if let Some(ref mut state) = *STATE.write().unwrap() {
- if let Some(ref mut lights) = state.lights {
- if let Some(ref mut light) = lights.get_mut(id) {
- if let Some(ref mut color) = light.state.color {
- color.hue = new_value;
- }
- }
- }
- }
- }, hue_queue, id, new_value, callback)
- }
-
- fn set_saturation_loop(saturation_queue: Arc<Mutex<HashMap<String, u8>>>, id: String, new_value: u8, callback: Box<Fn() + Send>) -> Box<Future<Item=(), Error=()> + Send> {
- set_prop_loop(|id, new_value| {
- if let Some(ref state) = *STATE.read().unwrap() {
- Box::new(state.connection.set_light_saturation(id, new_value))
- } else {
- panic!("STATE missing");
- }
- }, |id, new_value| {
- if let Some(ref mut state) = *STATE.write().unwrap() {
- if let Some(ref mut lights) = state.lights {
- if let Some(ref mut light) = lights.get_mut(id) {
- if let Some(ref mut color) = light.state.color {
- color.sat = new_value;
- }
- }
- }
- }
- }, saturation_queue, id, new_value, callback)
- }
-
- fn set_ct_loop(ct_queue: Arc<Mutex<HashMap<String, u16>>>, id: String, new_value: u16, callback: Box<Fn() + Send>) -> Box<Future<Item=(), Error=()> + Send> {
- set_prop_loop(|id, new_value| {
- if let Some(ref state) = *STATE.read().unwrap() {
- Box::new(state.connection.set_light_color_temperature(id, new_value))
- } else {
- panic!("STATE missing");
- }
- }, |id, new_value| {
- if let Some(ref mut state) = *STATE.write().unwrap() {
- if let Some(ref mut lights) = state.lights {
- if let Some(ref mut light) = lights.get_mut(id) {
- light.state.ct = Some(new_value);
- }
- }
- }
- }, ct_queue, id, new_value, callback)
- }
-
- impl BridgeConnectionConnector {
- fn init(&mut self, address: String, username: String) {
- {
- let connection = huey::connection::BridgeConnection::new(&address, username).expect("Failed to construct BridgeConnection");
- let state = BridgeState::new(connection);
- *STATE.write().unwrap() = Some(state);
- }
- self.refresh();
- }
-
- fn refresh(&self) {
- {
- let callback = {
- let ptr = QPointer::from(&*self);
- queued_callback(move |_| {
- ptr.as_ref().map(|x| x.lights_changed());
- })
- };
- if let Some(ref state) = *STATE.read().unwrap() {
- let future = state.connection.get_all_lights();
- std::mem::drop(state);
- tokio::spawn(future
- .then(move |res| {
- match res {
- Ok(lights) => {
- if let Some(ref mut state) = *STATE.write().unwrap() {
- state.lights = Some(lights.into_iter().collect());
- callback(());
- }
- },
- Err(err) => {
- eprintln!("{:?}", err);
- },
- }
- Ok(())
- }));
- }
- }
- {
- let callback = {
- let ptr = QPointer::from(&*self);
- queued_callback(move |_| {
- ptr.as_ref().map(|x| x.groups_changed());
- })
- };
- if let Some(ref state) = *STATE.read().unwrap() {
- let future = state.connection.get_all_groups();
- std::mem::drop(state);
- tokio::spawn(future
- .then(move |res| {
- match res {
- Ok(groups) => {
- if let Some(ref mut state) = *STATE.write().unwrap() {
- state.groups = Some(groups.into_iter().collect());
- callback(());
- }
- },
- Err(err) => {
- eprintln!("{:?}", err);
- },
- }
- Ok(())
- }));
- }
- }
- }
-
- fn get_lights(&self) -> QVariant {
- if let Some(ref state) = *STATE.read().unwrap() {
- if let Some(ref lights) = state.lights {
- let mut list = qttypes::QVariantList::default();
- for key in lights.keys() {
- list.push(QString::from(key.as_ref()).into());
- }
- return list.into();
- }
- }
-
- QVariant::default()
- }
-
- fn get_light(&self, id: String) -> QVariant {
- if let Some(ref state) = *STATE.read().unwrap() {
- if let Some(ref lights) = state.lights {
- if let Some(ref light) = lights.get(&id) {
- let object = RadiateLight::from(*light);
- let object = new_qobject(self, object);
- return object.to_variant();
- }
- }
- }
-
- Default::default()
- }
-
- fn start_set_light_on(&self, id: String, on: bool) {
- let callback = {
- let ptr = QPointer::from(&*self);
- let id = id.clone();
- queued_callback(move |_| {
- ptr.as_ref().map(|x| x.light_changed(id.clone()));
- })
- };
- if let Some(ref state) = *STATE.read().unwrap() {
- let future = state.connection.set_light_on(&id, on);
- std::mem::drop(state);
- tokio::spawn(future
- .then(move |res| {
- match res {
- Ok(_) => {
- if let Some(ref mut state) = *STATE.write().unwrap() {
- if let Some(ref mut lights) = state.lights {
- if let Some(ref mut light) = lights.get_mut(&id) {
- light.state.on = on;
- }
- }
- }
- },
- Err(err) => {
- eprintln!("{:?}", err);
- }
- }
- callback(());
- Ok(())
- }));
- }
- }
-
- fn start_set_light_brightness(&self, id: String, new_value: u8) {
- let mut queue = self.brightness_queue.lock().unwrap();
- let old_value = queue.insert(id.clone(), new_value);
- if old_value == None {
- let callback = {
- let ptr = QPointer::from(&*self);
- let id = id.clone();
- queued_callback(move |_| {
- ptr.as_ref().map(|x| x.light_changed(id.clone()));
- })
- };
-
- tokio::spawn(set_brightness_loop(self.brightness_queue.clone(), id, new_value, Box::new(move || callback(()))));
- }
- }
-
- fn start_set_light_hue(&self, id: String, new_value: u16) {
- let mut queue = self.hue_queue.lock().unwrap();
- let old_value = queue.insert(id.clone(), new_value);
- if old_value == None {
- let callback = {
- let ptr = QPointer::from(&*self);
- let id = id.clone();
- queued_callback(move |_| {
- ptr.as_ref().map(|x| x.light_changed(id.clone()));
- })
- };
-
- tokio::spawn(set_hue_loop(self.hue_queue.clone(), id, new_value, Box::new(move || callback(()))));
- }
- }
-
- fn start_set_light_saturation(&self, id: String, new_value: u8) {
- let mut queue = self.saturation_queue.lock().unwrap();
- let old_value = queue.insert(id.clone(), new_value);
- if old_value == None {
- let callback = {
- let ptr = QPointer::from(&*self);
- let id = id.clone();
- queued_callback(move |_| {
- ptr.as_ref().map(|x| x.light_changed(id.clone()));
- })
- };
-
- tokio::spawn(set_saturation_loop(self.saturation_queue.clone(), id, new_value, Box::new(move || callback(()))));
- }
- }
-
- fn start_set_light_ct(&self, id: String, new_value: u16) {
- let mut queue = self.ct_queue.lock().unwrap();
- let old_value = queue.insert(id.clone(), new_value);
- if old_value == None {
- let callback = {
- let ptr = QPointer::from(&*self);
- let id = id.clone();
- queued_callback(move |_| {
- ptr.as_ref().map(|x| x.light_changed(id.clone()));
- })
- };
-
- tokio::spawn(set_ct_loop(self.ct_queue.clone(), id, new_value, Box::new(move || callback(()))));
- }
- }
-
- fn get_groups(&self) -> QVariant {
- if let Some(ref state) = *STATE.read().unwrap() {
- if let Some(ref groups) = state.groups {
- let mut list = qttypes::QVariantList::default();
- for key in groups.keys() {
- list.push(QString::from(key.as_ref()).into());
- }
- return list.into();
- }
- }
-
- Default::default()
- }
-
- fn get_group(&self, id: String) -> QVariant {
- if let Some(ref state) = *STATE.read().unwrap() {
- if let Some(ref groups) = state.groups {
- if let Some(ref group) = groups.get(&id) {
- let object = RadiateGroup::from(*group);
- let object = new_qobject(self, object);
-
- return object.to_variant();
- }
- }
- }
-
- Default::default()
- }
-
- fn start_set_group_on(&self, id: String, on: bool) {
- let callback = {
- let ptr = QPointer::from(&*self);
- let id = id.clone();
- queued_callback(move |_| {
- ptr.as_ref().map(|x| x.group_changed(id.clone()));
- })
- };
- if let Some(ref state) = *STATE.read().unwrap() {
- let future = state.connection.set_group_on(&id, on);
- std::mem::drop(state);
- tokio::spawn(future
- .then(move |res| {
- match res {
- Ok(_) => {
- if let Some(ref mut state) = *STATE.write().unwrap() {
- if let Some(ref mut groups) = state.groups {
- if let Some(ref mut group) = groups.get_mut(&id) {
- group.action.on = on;
- }
- }
- }
- },
- Err(err) => {
- eprintln!("{:?}", err);
- }
- }
- callback(());
- Ok(())
- }));
- }
- }
- }
|