You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

443 lines
17KB

  1. use futures::Future;
  2. use huey::connection::{Light, LightGroup};
  3. use huey::HueError;
  4. use qmetaobject::*;
  5. use std::collections::{BTreeMap, HashMap};
  6. use std::sync::{Arc, Mutex, RwLock};
  7. use crate::adapters::*;
  8. cpp!{{
  9. #include <QtQuick/QtQuick>
  10. }}
  11. struct BridgeState {
  12. connection: huey::connection::BridgeConnection,
  13. groups: Option<BTreeMap<String, LightGroup>>,
  14. lights: Option<BTreeMap<String, Light>>,
  15. }
  16. impl BridgeState {
  17. fn new(connection: huey::connection::BridgeConnection) -> Self {
  18. Self {
  19. connection,
  20. groups: None,
  21. lights: None,
  22. }
  23. }
  24. }
  25. lazy_static! {
  26. static ref STATE: RwLock<Option<BridgeState>> = RwLock::new(None);
  27. }
  28. #[derive(QObject, Default)]
  29. pub struct BridgeConnectionConnector {
  30. base: qt_base_class!(trait QObject),
  31. init: qt_method!(fn(&self, address: String, username: String)),
  32. refresh: qt_method!(fn(&self)),
  33. get_lights: qt_method!(fn(&self) -> QVariant),
  34. get_light: qt_method!(fn(&self, id: String) -> QVariant),
  35. start_set_light_on: qt_method!(fn(&self, id: String, on: bool)),
  36. start_set_light_brightness: qt_method!(fn(&self, id: String, new_value: u8)),
  37. start_set_light_hue: qt_method!(fn(&self, id: String, new_value: u16)),
  38. start_set_light_saturation: qt_method!(fn(&self, id: String, new_value: u8)),
  39. start_set_light_ct: qt_method!(fn(&self, id: String, new_value: u16)),
  40. lights_changed: qt_signal!(),
  41. light_changed: qt_signal!(id: String),
  42. get_groups: qt_method!(fn(&self) -> QVariant),
  43. get_group: qt_method!(fn(&self, id: String) -> QVariant),
  44. start_set_group_on: qt_method!(fn(&self, id: String, on: bool)),
  45. groups_changed: qt_signal!(),
  46. group_changed: qt_signal!(id: String),
  47. brightness_queue: Arc<Mutex<HashMap<String, u8>>>,
  48. hue_queue: Arc<Mutex<HashMap<String, u16>>>,
  49. saturation_queue: Arc<Mutex<HashMap<String, u8>>>,
  50. ct_queue: Arc<Mutex<HashMap<String, u16>>>,
  51. }
  52. fn new_qobject(parent: &QObject, object: impl QObject) -> QJSValue {
  53. let parent = parent.get_cpp_object();
  54. let ptr = into_leaked_cpp_ptr(object);
  55. unsafe { cpp!([parent as "QObject*", ptr as "QObject*"] -> QJSValue as "QJSValue" {
  56. return QQmlEngine::contextForObject(parent)->engine()->newQObject(ptr);
  57. })}
  58. }
  59. fn set_prop_loop<T: PartialEq + Copy + Send + 'static>(
  60. make_request: impl Fn(&str, T) -> Box<Future<Item=(),Error=HueError> + Send> + Send + 'static,
  61. update_model: impl Fn(&str, T) + Send + 'static,
  62. queue: Arc<Mutex<HashMap<String, T>>>,
  63. id: String,
  64. new_value: T,
  65. callback: Box<Fn() + Send>
  66. ) -> Box<Future<Item=(), Error=()> + Send> {
  67. if let Some(ref state) = *STATE.read().unwrap() {
  68. let future = make_request(&id, new_value);
  69. std::mem::drop(state);
  70. Box::new(future
  71. .then(move |res| {
  72. match res {
  73. Ok(_) => {
  74. update_model(&id, new_value);
  75. },
  76. Err(err) => {
  77. eprintln!("{:?}", err);
  78. }
  79. }
  80. let mut queue_map = queue.lock().unwrap();
  81. let target_value = queue_map.get(&id);
  82. match target_value {
  83. Some(target_value) if *target_value != new_value => {
  84. println!("looping");
  85. set_prop_loop(make_request, update_model, queue.clone(), id, *target_value, callback)
  86. },
  87. _ => {
  88. queue_map.remove(&id);
  89. callback();
  90. println!("ending");
  91. Box::new(futures::future::ok(()))
  92. },
  93. }
  94. }))
  95. } else {
  96. panic!("STATE missing");
  97. }
  98. }
  99. 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> {
  100. set_prop_loop(|id, new_value| {
  101. if let Some(ref state) = *STATE.read().unwrap() {
  102. Box::new(state.connection.set_light_brightness(id, new_value))
  103. } else {
  104. panic!("STATE missing");
  105. }
  106. }, |id, new_value| {
  107. if let Some(ref mut state) = *STATE.write().unwrap() {
  108. if let Some(ref mut lights) = state.lights {
  109. if let Some(ref mut light) = lights.get_mut(id) {
  110. light.state.bri = new_value;
  111. }
  112. }
  113. }
  114. }, brightness_queue, id, new_value, callback)
  115. }
  116. 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> {
  117. set_prop_loop(|id, new_value| {
  118. if let Some(ref state) = *STATE.read().unwrap() {
  119. Box::new(state.connection.set_light_hue(id, new_value))
  120. } else {
  121. panic!("STATE missing");
  122. }
  123. }, |id, new_value| {
  124. if let Some(ref mut state) = *STATE.write().unwrap() {
  125. if let Some(ref mut lights) = state.lights {
  126. if let Some(ref mut light) = lights.get_mut(id) {
  127. if let Some(ref mut color) = light.state.color {
  128. color.hue = new_value;
  129. }
  130. }
  131. }
  132. }
  133. }, hue_queue, id, new_value, callback)
  134. }
  135. 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> {
  136. set_prop_loop(|id, new_value| {
  137. if let Some(ref state) = *STATE.read().unwrap() {
  138. Box::new(state.connection.set_light_saturation(id, new_value))
  139. } else {
  140. panic!("STATE missing");
  141. }
  142. }, |id, new_value| {
  143. if let Some(ref mut state) = *STATE.write().unwrap() {
  144. if let Some(ref mut lights) = state.lights {
  145. if let Some(ref mut light) = lights.get_mut(id) {
  146. if let Some(ref mut color) = light.state.color {
  147. color.sat = new_value;
  148. }
  149. }
  150. }
  151. }
  152. }, saturation_queue, id, new_value, callback)
  153. }
  154. 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> {
  155. set_prop_loop(|id, new_value| {
  156. if let Some(ref state) = *STATE.read().unwrap() {
  157. Box::new(state.connection.set_light_color_temperature(id, new_value))
  158. } else {
  159. panic!("STATE missing");
  160. }
  161. }, |id, new_value| {
  162. if let Some(ref mut state) = *STATE.write().unwrap() {
  163. if let Some(ref mut lights) = state.lights {
  164. if let Some(ref mut light) = lights.get_mut(id) {
  165. light.state.ct = Some(new_value);
  166. }
  167. }
  168. }
  169. }, ct_queue, id, new_value, callback)
  170. }
  171. impl BridgeConnectionConnector {
  172. fn init(&mut self, address: String, username: String) {
  173. {
  174. let connection = huey::connection::BridgeConnection::new(&address, username).expect("Failed to construct BridgeConnection");
  175. let state = BridgeState::new(connection);
  176. *STATE.write().unwrap() = Some(state);
  177. }
  178. self.refresh();
  179. }
  180. fn refresh(&self) {
  181. {
  182. let callback = {
  183. let ptr = QPointer::from(&*self);
  184. queued_callback(move |_| {
  185. ptr.as_ref().map(|x| x.lights_changed());
  186. })
  187. };
  188. if let Some(ref state) = *STATE.read().unwrap() {
  189. let future = state.connection.get_all_lights();
  190. std::mem::drop(state);
  191. tokio::spawn(future
  192. .then(move |res| {
  193. match res {
  194. Ok(lights) => {
  195. if let Some(ref mut state) = *STATE.write().unwrap() {
  196. state.lights = Some(lights.into_iter().collect());
  197. callback(());
  198. }
  199. },
  200. Err(err) => {
  201. eprintln!("{:?}", err);
  202. },
  203. }
  204. Ok(())
  205. }));
  206. }
  207. }
  208. {
  209. let callback = {
  210. let ptr = QPointer::from(&*self);
  211. queued_callback(move |_| {
  212. ptr.as_ref().map(|x| x.groups_changed());
  213. })
  214. };
  215. if let Some(ref state) = *STATE.read().unwrap() {
  216. let future = state.connection.get_all_groups();
  217. std::mem::drop(state);
  218. tokio::spawn(future
  219. .then(move |res| {
  220. match res {
  221. Ok(groups) => {
  222. if let Some(ref mut state) = *STATE.write().unwrap() {
  223. state.groups = Some(groups.into_iter().collect());
  224. callback(());
  225. }
  226. },
  227. Err(err) => {
  228. eprintln!("{:?}", err);
  229. },
  230. }
  231. Ok(())
  232. }));
  233. }
  234. }
  235. }
  236. fn get_lights(&self) -> QVariant {
  237. if let Some(ref state) = *STATE.read().unwrap() {
  238. if let Some(ref lights) = state.lights {
  239. let mut list = qttypes::QVariantList::default();
  240. for key in lights.keys() {
  241. list.push(QString::from(key.as_ref()).into());
  242. }
  243. return list.into();
  244. }
  245. }
  246. QVariant::default()
  247. }
  248. fn get_light(&self, id: String) -> QVariant {
  249. if let Some(ref state) = *STATE.read().unwrap() {
  250. if let Some(ref lights) = state.lights {
  251. if let Some(ref light) = lights.get(&id) {
  252. let object = RadiateLight::from(*light);
  253. let object = new_qobject(self, object);
  254. return object.to_variant();
  255. }
  256. }
  257. }
  258. Default::default()
  259. }
  260. fn start_set_light_on(&self, id: String, on: bool) {
  261. let callback = {
  262. let ptr = QPointer::from(&*self);
  263. let id = id.clone();
  264. queued_callback(move |_| {
  265. ptr.as_ref().map(|x| x.light_changed(id.clone()));
  266. })
  267. };
  268. if let Some(ref state) = *STATE.read().unwrap() {
  269. let future = state.connection.set_light_on(&id, on);
  270. std::mem::drop(state);
  271. tokio::spawn(future
  272. .then(move |res| {
  273. match res {
  274. Ok(_) => {
  275. if let Some(ref mut state) = *STATE.write().unwrap() {
  276. if let Some(ref mut lights) = state.lights {
  277. if let Some(ref mut light) = lights.get_mut(&id) {
  278. light.state.on = on;
  279. }
  280. }
  281. }
  282. },
  283. Err(err) => {
  284. eprintln!("{:?}", err);
  285. }
  286. }
  287. callback(());
  288. Ok(())
  289. }));
  290. }
  291. }
  292. fn start_set_light_brightness(&self, id: String, new_value: u8) {
  293. let mut queue = self.brightness_queue.lock().unwrap();
  294. let old_value = queue.insert(id.clone(), new_value);
  295. if old_value == None {
  296. let callback = {
  297. let ptr = QPointer::from(&*self);
  298. let id = id.clone();
  299. queued_callback(move |_| {
  300. ptr.as_ref().map(|x| x.light_changed(id.clone()));
  301. })
  302. };
  303. tokio::spawn(set_brightness_loop(self.brightness_queue.clone(), id, new_value, Box::new(move || callback(()))));
  304. }
  305. }
  306. fn start_set_light_hue(&self, id: String, new_value: u16) {
  307. let mut queue = self.hue_queue.lock().unwrap();
  308. let old_value = queue.insert(id.clone(), new_value);
  309. if old_value == None {
  310. let callback = {
  311. let ptr = QPointer::from(&*self);
  312. let id = id.clone();
  313. queued_callback(move |_| {
  314. ptr.as_ref().map(|x| x.light_changed(id.clone()));
  315. })
  316. };
  317. tokio::spawn(set_hue_loop(self.hue_queue.clone(), id, new_value, Box::new(move || callback(()))));
  318. }
  319. }
  320. fn start_set_light_saturation(&self, id: String, new_value: u8) {
  321. let mut queue = self.saturation_queue.lock().unwrap();
  322. let old_value = queue.insert(id.clone(), new_value);
  323. if old_value == None {
  324. let callback = {
  325. let ptr = QPointer::from(&*self);
  326. let id = id.clone();
  327. queued_callback(move |_| {
  328. ptr.as_ref().map(|x| x.light_changed(id.clone()));
  329. })
  330. };
  331. tokio::spawn(set_saturation_loop(self.saturation_queue.clone(), id, new_value, Box::new(move || callback(()))));
  332. }
  333. }
  334. fn start_set_light_ct(&self, id: String, new_value: u16) {
  335. let mut queue = self.ct_queue.lock().unwrap();
  336. let old_value = queue.insert(id.clone(), new_value);
  337. if old_value == None {
  338. let callback = {
  339. let ptr = QPointer::from(&*self);
  340. let id = id.clone();
  341. queued_callback(move |_| {
  342. ptr.as_ref().map(|x| x.light_changed(id.clone()));
  343. })
  344. };
  345. tokio::spawn(set_ct_loop(self.ct_queue.clone(), id, new_value, Box::new(move || callback(()))));
  346. }
  347. }
  348. fn get_groups(&self) -> QVariant {
  349. if let Some(ref state) = *STATE.read().unwrap() {
  350. if let Some(ref groups) = state.groups {
  351. let mut list = qttypes::QVariantList::default();
  352. for key in groups.keys() {
  353. list.push(QString::from(key.as_ref()).into());
  354. }
  355. return list.into();
  356. }
  357. }
  358. Default::default()
  359. }
  360. fn get_group(&self, id: String) -> QVariant {
  361. if let Some(ref state) = *STATE.read().unwrap() {
  362. if let Some(ref groups) = state.groups {
  363. if let Some(ref group) = groups.get(&id) {
  364. let object = RadiateGroup::from(*group);
  365. let object = new_qobject(self, object);
  366. return object.to_variant();
  367. }
  368. }
  369. }
  370. Default::default()
  371. }
  372. fn start_set_group_on(&self, id: String, on: bool) {
  373. let callback = {
  374. let ptr = QPointer::from(&*self);
  375. let id = id.clone();
  376. queued_callback(move |_| {
  377. ptr.as_ref().map(|x| x.group_changed(id.clone()));
  378. })
  379. };
  380. if let Some(ref state) = *STATE.read().unwrap() {
  381. let future = state.connection.set_group_on(&id, on);
  382. std::mem::drop(state);
  383. tokio::spawn(future
  384. .then(move |res| {
  385. match res {
  386. Ok(_) => {
  387. if let Some(ref mut state) = *STATE.write().unwrap() {
  388. if let Some(ref mut groups) = state.groups {
  389. if let Some(ref mut group) = groups.get_mut(&id) {
  390. group.action.on = on;
  391. }
  392. }
  393. }
  394. },
  395. Err(err) => {
  396. eprintln!("{:?}", err);
  397. }
  398. }
  399. callback(());
  400. Ok(())
  401. }));
  402. }
  403. }
  404. }