lifx_api_server/
lib.rs

1// TODO - Impliment authentication header (DONE)
2// TODO - Wrap as a rust library with configurable ports + authentication (DONE)
3// TODO - Impliment LIFX Effects, Scenes, Clean, Cycle
4// TODO - Impliment an extended API for changing device labels, wifi-config, etc.
5
6
7use get_if_addrs::{get_if_addrs, IfAddr, Ifv4Addr};
8use lifx_rs::lan::{get_product_info, BuildOptions, Message, PowerLevel, ProductInfo, RawMessage, HSBK};
9use std::collections::HashMap;
10use std::net::{IpAddr, SocketAddr, UdpSocket};
11use std::sync::{Arc, Mutex};
12use std::thread::{spawn};
13use std::time::{Duration, Instant};
14use rouille::try_or_400;
15use rand::{thread_rng, Rng};
16use rand::distributions::Alphanumeric;
17use std::thread;
18
19use rouille::Response;
20use rouille::post_input;
21
22
23use serde::{Serialize, Deserialize};
24
25use palette::FromColor;
26
27use colors_transform::{Rgb, Color};
28
29
30
31const HOUR: Duration = Duration::from_secs(60 * 60);
32
33
34#[derive(Debug)]
35struct RefreshableData<T> {
36    data: Option<T>,
37    max_age: Duration,
38    last_updated: Instant,
39    refresh_msg: Message,
40}
41
42impl<T> RefreshableData<T> {
43    fn empty(max_age: Duration, refresh_msg: Message) -> RefreshableData<T> {
44        RefreshableData {
45            data: None,
46            max_age,
47            last_updated: Instant::now(),
48            refresh_msg,
49        }
50    }
51    fn update(&mut self, data: T) {
52        self.data = Some(data);
53        self.last_updated = Instant::now();
54
55
56    }
57    fn needs_refresh(&self) -> bool {
58        self.data.is_none() || self.last_updated.elapsed() > self.max_age
59    }
60    fn as_ref(&self) -> Option<&T> {
61        self.data.as_ref()
62    }
63}
64
65#[derive(Debug, Serialize)]
66struct BulbInfo {
67    pub id: String,
68    pub uuid: String,
69    pub label: String,
70    pub connected: bool,
71    pub power: String,
72    #[serde(rename = "color")]
73    pub lifx_color: Option<LifxColor>,
74    pub brightness: f64,
75    #[serde(rename = "group")]
76    pub lifx_group: Option<LifxGroup>,
77    #[serde(rename = "location")]
78    pub lifx_location: Option<LifxLocation>,
79    pub product: Option<ProductInfo>,
80    #[serde(rename = "last_seen")]
81    pub lifx_last_seen: String,
82    #[serde(rename = "seconds_since_seen")]
83    pub seconds_since_seen: i64,
84    // pub error: Option<String>,
85    // pub errors: Option<Vec<Error>>,
86
87    #[serde(skip_serializing)]
88    last_seen: Instant,
89
90    source: u32,
91
92    target: u64,
93
94    addr: SocketAddr,
95
96    #[serde(skip_serializing)]
97    group: RefreshableData<LifxGroup>,
98
99
100    #[serde(skip_serializing)]
101    name: RefreshableData<String>,
102    #[serde(skip_serializing)]
103    model: RefreshableData<(u32, u32)>,
104    #[serde(skip_serializing)]
105    location: RefreshableData<String>,
106    #[serde(skip_serializing)]
107    host_firmware: RefreshableData<u32>,
108    #[serde(skip_serializing)]
109    wifi_firmware: RefreshableData<u32>,
110    #[serde(skip_serializing)]
111    power_level: RefreshableData<PowerLevel>,
112    #[serde(skip_serializing)]
113    color: LiColor,
114}
115
116#[derive(Debug)]
117enum LiColor {
118    Unknown,
119    Single(RefreshableData<HSBK>),
120    Multi(RefreshableData<Vec<Option<HSBK>>>),
121}
122
123#[derive(Debug, Serialize)]
124#[serde(rename_all = "camelCase")]
125#[doc(hidden)]
126pub struct LifxLocation {
127    pub id: String,
128    pub name: String,
129}
130
131/// Represents an LIFX Color
132#[derive(Debug, Serialize)]
133#[serde(rename_all = "camelCase")]
134pub struct LifxColor {
135    pub hue: u16,
136    pub saturation: u16,
137    pub kelvin: u16,
138    pub brightness: u16,
139}
140
141#[derive(Debug, Serialize)]
142#[serde(rename_all = "camelCase")]
143#[doc(hidden)]
144pub struct LifxGroup {
145    pub id: String,
146    pub name: String,
147}
148
149impl BulbInfo {
150    fn new(source: u32, target: u64, addr: SocketAddr) -> BulbInfo {
151        let id: String = thread_rng().sample_iter(&Alphanumeric).take(12).map(char::from).collect();
152        let uuid: String = thread_rng().sample_iter(&Alphanumeric).take(30).map(char::from).collect();
153        BulbInfo {
154            id: id.to_string(),
155            uuid: uuid.to_string(),
156            label: format!(""),
157            connected: true,
158            power: format!("off"),
159            lifx_color: None,
160            brightness: 0.0,
161            lifx_group: None,
162            lifx_location: None,
163            product: None,
164            lifx_last_seen: format!(""),
165            seconds_since_seen: 0,
166            last_seen: Instant::now(),
167            source,
168            target,
169            addr,
170            group: RefreshableData::empty(HOUR, Message::GetGroup),
171            location: RefreshableData::empty(HOUR, Message::GetLocation),
172            name: RefreshableData::empty(HOUR, Message::GetLabel),
173            model: RefreshableData::empty(HOUR, Message::GetVersion),
174            host_firmware: RefreshableData::empty(HOUR, Message::GetHostFirmware),
175            wifi_firmware: RefreshableData::empty(HOUR, Message::GetWifiFirmware),
176            power_level: RefreshableData::empty(Duration::from_millis(500), Message::GetPower),
177            color: LiColor::Unknown,
178        }
179    }
180
181    fn update(&mut self, addr: SocketAddr) {
182        self.last_seen = Instant::now();
183        self.addr = addr;
184    }
185
186    fn refresh_if_needed<T>(
187        &self,
188        sock: &UdpSocket,
189        data: &RefreshableData<T>,
190    ) -> Result<(), failure::Error> {
191        if data.needs_refresh() {
192            let options = BuildOptions {
193                target: Some(self.target),
194                res_required: true,
195                source: self.source,
196                ..Default::default()
197            };
198            let message = RawMessage::build(&options, data.refresh_msg.clone())?;
199            sock.send_to(&message.pack()?, self.addr)?;
200        }
201        Ok(())
202    }
203
204    fn set_power(
205        &self,
206        sock: &UdpSocket,
207        power_level: PowerLevel,
208    ) -> Result<(), failure::Error> {
209        
210        let options = BuildOptions {
211            target: Some(self.target),
212            res_required: true,
213            source: self.source,
214            ..Default::default()
215        };
216        let message = RawMessage::build(&options, Message::SetPower{level: power_level})?;
217        sock.send_to(&message.pack()?, self.addr)?;
218  
219        Ok(())
220    }
221
222    fn set_infrared(
223        &self,
224        sock: &UdpSocket,
225        brightness: u16,
226    ) -> Result<(), failure::Error> {
227        
228        let options = BuildOptions {
229            target: Some(self.target),
230            res_required: true,
231            source: self.source,
232            ..Default::default()
233        };
234        let message = RawMessage::build(&options, Message::LightSetInfrared{brightness: brightness})?;
235        sock.send_to(&message.pack()?, self.addr)?;
236  
237        Ok(())
238    }
239
240
241    fn set_color(
242        &self,
243        sock: &UdpSocket,
244        color: HSBK,
245        duration: u32
246    ) -> Result<(), failure::Error> {
247        
248        let options = BuildOptions {
249            target: Some(self.target),
250            res_required: true,
251            source: self.source,
252            ..Default::default()
253        };
254        let message = RawMessage::build(&options, Message::LightSetColor{reserved: 0, color: color, duration: duration})?;
255        sock.send_to(&message.pack()?, self.addr)?;
256  
257        Ok(())
258    }
259
260
261
262
263    fn query_for_missing_info(&self, sock: &UdpSocket) -> Result<(), failure::Error> {
264        self.refresh_if_needed(sock, &self.name)?;
265        self.refresh_if_needed(sock, &self.model)?;
266        self.refresh_if_needed(sock, &self.location)?;
267        self.refresh_if_needed(sock, &self.host_firmware)?;
268        self.refresh_if_needed(sock, &self.wifi_firmware)?;
269        self.refresh_if_needed(sock, &self.power_level)?;
270        self.refresh_if_needed(sock, &self.group)?;
271        match &self.color {
272            LiColor::Unknown => (), // we'll need to wait to get info about this bulb's model, so we'll know if it's multizone or not
273            LiColor::Single(d) => self.refresh_if_needed(sock, d)?,
274            LiColor::Multi(d) => self.refresh_if_needed(sock, d)?,
275        }
276
277    
278
279        Ok(())
280    }
281}
282
283// impl std::fmt::Debug for BulbInfo {
284//     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
285//         write!(f, "BulbInfo({:0>16X} - {}  ", self.target, self.addr)?;
286
287//         if let Some(name) = self.name.as_ref() {
288//             write!(f, "{}", name)?;
289//         }
290//         if let Some(location) = self.location.as_ref() {
291//             write!(f, "/{}", location)?;
292//         }
293//         if let Some((vendor, product)) = self.model.as_ref() {
294//             if let Some(info) = get_product_info(*vendor, *product) {
295//                 write!(f, " - {} ", info.name)?;
296//             } else {
297//                 write!(
298//                     f,
299//                     " - Unknown model (vendor={}, product={}) ",
300//                     vendor, product
301//                 )?;
302//             }
303//         }
304//         if let Some(fw_version) = self.host_firmware.as_ref() {
305//             write!(f, " McuFW:{:x}", fw_version)?;
306//         }
307//         if let Some(fw_version) = self.wifi_firmware.as_ref() {
308//             write!(f, " WifiFW:{:x}", fw_version)?;
309//         }
310//         if let Some(level) = self.power_level.as_ref() {
311//             if *level == PowerLevel::Enabled {
312//                 write!(f, "  Powered On(")?;
313//                 match self.color {
314//                     Color::Unknown => write!(f, "??")?,
315//                     Color::Single(ref color) => {
316//                         f.write_str(
317//                             &color
318//                                 .as_ref()
319//                                 .map(|c| c.describe(false))
320//                                 .unwrap_or_else(|| "??".to_owned()),
321//                         )?;
322//                     }
323//                     Color::Multi(ref color) => {
324//                         if let Some(vec) = color.as_ref() {
325//                             write!(f, "Zones: ")?;
326//                             for zone in vec {
327//                                 if let Some(color) = zone {
328//                                     write!(f, "{} ", color.describe(true))?;
329//                                 } else {
330//                                     write!(f, "?? ")?;
331//                                 }
332//                             }
333//                         }
334//                     }
335//                 }
336//                 write!(f, ")")?;
337//             } else {
338//                 write!(f, "  Powered Off")?;
339//             }
340//         }
341//         write!(f, ")")
342//     }
343// }
344
345struct Manager {
346    bulbs: Arc<Mutex<HashMap<u64, BulbInfo>>>,
347    last_discovery: Instant,
348    sock: UdpSocket,
349    source: u32,
350}
351
352impl Manager {
353    fn new() -> Result<Manager, failure::Error> {
354        let sock = UdpSocket::bind("0.0.0.0:56700")?;
355        sock.set_broadcast(true)?;
356
357        // spawn a thread that can send to our socket
358        let recv_sock = sock.try_clone()?;
359
360        let bulbs = Arc::new(Mutex::new(HashMap::new()));
361        let receiver_bulbs = bulbs.clone();
362        let source = 0x72757374;
363
364        // spawn a thread that will receive data from our socket and update our internal data structures
365        spawn(move || Self::worker(recv_sock, source, receiver_bulbs));
366
367        let mut mgr = Manager {
368            bulbs,
369            last_discovery: Instant::now(),
370            sock,
371            source,
372        };
373        mgr.discover()?;
374        Ok(mgr)
375    }
376
377    fn handle_message(raw: RawMessage, bulb: &mut BulbInfo) -> Result<(), lifx_rs::lan::Error> {
378        match Message::from_raw(&raw)? {
379            Message::StateService { port: _, service: _ } => {
380                // if port != bulb.addr.port() as u32 || service != Service::UDP {
381                //     println!("Unsupported service: {:?}/{}", service, port);
382                // }
383            }
384            Message::StateLabel { label } => {
385                bulb.name.update(label.0);
386                bulb.label = bulb.name.data.as_ref().unwrap().to_string();
387
388            },
389
390  
391            Message::StateLocation { location, label, updated_at: _ } => {
392
393                let lab = label.0;
394
395                bulb.location.update(lab.clone());
396
397
398          
399                let group_two = LifxLocation{id: format!("{:?}", location.0).replace(", ", "").replace("[", "").replace("]", ""), name: lab};
400                bulb.lifx_location = Some(group_two);
401
402            },
403            Message::StateVersion {
404                vendor, product, ..
405            } => {
406                bulb.model.update((vendor, product));
407                if let Some(info) = get_product_info(vendor, product) {
408                    // println!("{:?}", info.clone());
409
410                    bulb.product = Some(info.clone());
411
412                    if info.capabilities.has_multizone {
413                        bulb.color = LiColor::Multi(RefreshableData::empty(
414                            Duration::from_secs(15),
415                            Message::GetColorZones {
416                                start_index: 0,
417                                end_index: 255,
418                            },
419                        ))
420                    } else {
421                        bulb.color = LiColor::Single(RefreshableData::empty(
422                            Duration::from_secs(15),
423                            Message::LightGet,
424                        ))
425                    }
426                }
427            }
428            Message::StatePower { level } => {
429                bulb.power_level.update(level);
430
431                if bulb.power_level.data.as_ref().unwrap() ==  &PowerLevel::Enabled{
432                    bulb.power = format!("on");
433                } else {
434                    bulb.power = format!("off");
435                }
436
437               
438            },
439
440            Message::StateGroup { group, label, updated_at: _ } => {
441
442                let group_one = LifxGroup{id: format!("{:?}", group.0), name: label.to_string()};
443                
444                let group_two = LifxGroup{id: format!("{:?}", group.0).replace(", ", "").replace("[", "").replace("]", ""), name: label.to_string()};
445                bulb.group.update(group_one);
446                bulb.lifx_group = Some(group_two);
447            },
448
449
450
451            Message::StateHostFirmware { version, .. } => bulb.host_firmware.update(version),
452            Message::StateWifiFirmware { version, .. } => bulb.wifi_firmware.update(version),
453            Message::LightState {
454                color,
455                power,
456                label,
457                ..
458            } => {
459                if let LiColor::Single(ref mut d) = bulb.color {
460                    d.update(color);
461
462                    let bc = color;
463
464
465                    bulb.lifx_color = Some(LifxColor{
466                        hue: bc.hue,
467                        saturation: bc.saturation,
468                        kelvin: bc.kelvin,
469                        brightness: bc.brightness,
470                    });
471
472                    bulb.brightness = (bc.brightness / 65535) as f64;
473
474
475                    bulb.power_level.update(power);
476                }
477                bulb.name.update(label.0);
478            }
479            Message::StateZone {
480                count,
481                index,
482                color,
483            } => {
484                if let LiColor::Multi(ref mut d) = bulb.color {
485                    d.data.get_or_insert_with(|| {
486                        let mut v = Vec::with_capacity(count as usize);
487                        v.resize(count as usize, None);
488                        assert!(index <= count);
489                        v
490                    })[index as usize] = Some(color);
491                }
492            }
493            Message::StateMultiZone {
494                count,
495                index,
496                color0,
497                color1,
498                color2,
499                color3,
500                color4,
501                color5,
502                color6,
503                color7,
504            } => {
505                if let LiColor::Multi(ref mut d) = bulb.color {
506                    let v = d.data.get_or_insert_with(|| {
507                        let mut v = Vec::with_capacity(count as usize);
508                        v.resize(count as usize, None);
509                        assert!(index + 7 <= count);
510                        v
511                    });
512
513                    v[index as usize + 0] = Some(color0);
514                    v[index as usize + 1] = Some(color1);
515                    v[index as usize + 2] = Some(color2);
516                    v[index as usize + 3] = Some(color3);
517                    v[index as usize + 4] = Some(color4);
518                    v[index as usize + 5] = Some(color5);
519                    v[index as usize + 6] = Some(color6);
520                    v[index as usize + 7] = Some(color7);
521                }
522            }
523            unknown => {
524                println!("Received, but ignored {:?}", unknown);
525            }
526        }
527        Ok(())
528    }
529
530    fn worker(
531        recv_sock: UdpSocket,
532        source: u32,
533        receiver_bulbs: Arc<Mutex<HashMap<u64, BulbInfo>>>,
534    ) {
535        let mut buf = [0; 1024];
536        loop {
537            match recv_sock.recv_from(&mut buf) {
538                Ok((0, addr)) => println!("Received a zero-byte datagram from {:?}", addr),
539                Ok((nbytes, addr)) => match RawMessage::unpack(&buf[0..nbytes]) {
540                    Ok(raw) => {
541                        if raw.frame_addr.target == 0 {
542                            continue;
543                        }
544                        if let Ok(mut bulbs) = receiver_bulbs.lock() {
545                            let bulb = bulbs
546                                .entry(raw.frame_addr.target)
547                                .and_modify(|bulb| bulb.update(addr))
548                                .or_insert_with(|| {
549                                    BulbInfo::new(source, raw.frame_addr.target, addr)
550                                });
551                            if let Err(e) = Self::handle_message(raw, bulb) {
552                                println!("Error handling message from {}: {}", addr, e)
553                            }
554                        }
555                    }
556                    Err(e) => println!("Error unpacking raw message from {}: {}", addr, e),
557                },
558                Err(e) => panic!("recv_from err {:?}", e),
559            }
560        }
561    }
562
563    fn discover(&mut self) -> Result<(), failure::Error> {
564        println!("Doing discovery");
565
566        let opts = BuildOptions {
567            source: self.source,
568            ..Default::default()
569        };
570        let rawmsg = RawMessage::build(&opts, Message::GetService).unwrap();
571        let bytes = rawmsg.pack().unwrap();
572
573        for addr in get_if_addrs().unwrap() {
574            match addr.addr {
575                IfAddr::V4(Ifv4Addr {
576                    broadcast: Some(bcast),
577                    ..
578                }) => {
579                    if addr.ip().is_loopback() {
580                        continue;
581                    }
582                    let addr = SocketAddr::new(IpAddr::V4(bcast), 56700);
583                    println!("Discovering bulbs on LAN {:?}", addr);
584                    self.sock.send_to(&bytes, &addr)?;
585                }
586                _ => {}
587            }
588        }
589
590        self.last_discovery = Instant::now();
591
592        Ok(())
593    }
594
595    fn refresh(&self) {
596        if let Ok(bulbs) = self.bulbs.lock() {
597            for bulb in bulbs.values() {
598                match bulb.query_for_missing_info(&self.sock){
599                    Ok(_missing_info) => {
600                    },
601                    Err(e) => {
602                        println!("Error querying for missing info: {:?}", e);
603                    }
604                }
605            }
606        }
607    }
608}
609
610/// Used to set the params when posting a FlameEffect event
611#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
612#[serde(rename_all = "camelCase")]
613pub struct Config {
614    pub secret_key: String,
615    pub port: u16,
616}
617
618pub fn start(config: Config) {
619
620
621    sudo::with_env(&["SECRET_KEY"]).unwrap();
622    sudo::escalate_if_needed().unwrap();
623
624
625    let mgr = Manager::new();
626
627    match mgr {
628        Ok(mgr) => {
629            let mgr_arc = Arc::new(Mutex::new(mgr));
630
631            let th_arc_mgr = Arc::clone(&mgr_arc);
632
633            thread::spawn(move || {
634                loop{
635                    let mut lock = th_arc_mgr.lock().unwrap();
636                    let mgr = &mut *lock;  
637                
638                    // if Instant::now() - mgr.last_discovery > Duration::from_secs(300) {
639                    //     mgr.discover().unwrap();
640                    // }
641            
642                    mgr.refresh();
643                    std::mem::drop(mgr);
644                    std::mem::drop(lock);
645                    thread::sleep(Duration::from_millis(1000));
646                }
647        
648            });
649        
650        
651            let th2_arc_mgr = Arc::clone(&mgr_arc);
652        
653            thread::spawn(move || {
654                rouille::start_server(format!("0.0.0.0:{}", config.port).as_str(), move |request| {
655        
656        
657                    let auth_header = request.header("Authorization");
658        
659                    if auth_header.is_none(){
660                        return Response::empty_404();
661                    } else {
662                        if auth_header.unwrap().to_string() != format!("Bearer {}", config.secret_key){
663                            return Response::empty_404();
664                        }
665                    }
666        
667        
668        
669        
670                    let mut response = Response::text("hello world");
671        
672                    let mut lock = th2_arc_mgr.lock().unwrap();
673                    let mgr = &mut *lock;  
674        
675                
676                    mgr.refresh();
677        
678        
679                    let urls = request.url().to_string();
680                    let split = urls.split("/");
681                    let vec: Vec<&str> = split.collect();
682        
683                    let mut selector = "";
684        
685                    if vec.len() >= 3 {
686                        selector = vec[3];
687                    }
688            
689        
690        
691                    let mut bulbs_vec: Vec<&BulbInfo> = Vec::new();
692        
693                    let bulbs = mgr.bulbs.lock().unwrap();
694                    
695                        
696                    for bulb in bulbs.values() {
697                        println!("{:?}", *bulb);
698                        bulbs_vec.push(bulb);
699                    }
700        
701                    if selector == "all"{
702                    
703                    }
704        
705                    if selector.contains("group_id:"){
706                        bulbs_vec = bulbs_vec
707                        .into_iter()
708                        .filter(|b| b.lifx_group.as_ref().unwrap().id.contains(&selector.replace("group_id:", "")))
709                        .collect();
710                    }
711        
712                    if selector.contains("location_id:"){
713                        bulbs_vec = bulbs_vec
714                        .into_iter()
715                        .filter(|b| b.lifx_location.as_ref().unwrap().id.contains(&selector.replace("location_id:", "")))
716                        .collect();
717                    }
718        
719                    if selector.contains("id:"){
720                        bulbs_vec = bulbs_vec
721                        .into_iter()
722                        .filter(|b| b.id.contains(&selector.replace("id:", "")))
723                        .collect();
724                    }
725        
726        
727                    // (PUT) SetStates
728                    // TODO - Implement
729                    // https://api.lifx.com/v1/lights/states
730                    if request.url().contains("/lights/states"){
731                        // std::mem::drop(bulbs);
732                        // std::mem::drop(mgr);
733                        // std::mem::drop(lock);
734                    }
735        
736                    // (PUT) SetState
737                    // https://api.lifx.com/v1/lights/:selector/state
738                    if request.url().contains("/state"){
739        
740                        let input = try_or_400!(post_input!(request, {
741                            power: Option<String>,
742                            color: Option<String>,
743                            brightness: Option<f64>,
744                            duration: Option<f64>,
745                            infrared: Option<f64>,
746                            fast: Option<bool>
747                        }));
748        
749        
750                        // Power
751                        if input.power.is_some() {
752                            let power = input.power.unwrap();
753                            if power == format!("on"){
754                                for bulb in &bulbs_vec {
755                                    bulb.set_power(&mgr.sock, PowerLevel::Enabled);
756                                }
757                            } 
758                
759                            if power == format!("off"){
760                                for bulb in &bulbs_vec {
761                                    bulb.set_power(&mgr.sock, PowerLevel::Standby);
762                                }
763                            } 
764                        }
765        
766                        // Color
767                        if input.color.is_some() {
768                            let cc = input.color.unwrap();
769        
770        
771        
772                            for bulb in &bulbs_vec {
773        
774        
775                                let mut kelvin = 6500;
776                                let mut brightness = 65535;
777                                let mut saturation = 0;
778                                let mut hue = 0;
779        
780                                let mut duration = 0;
781                                if input.duration.is_some(){
782                                    duration = input.duration.unwrap() as u32;
783                                }
784        
785                                if bulb.lifx_color.is_some() {
786                                    let lifxc = bulb.lifx_color.as_ref().unwrap();
787                                    kelvin = lifxc.kelvin;
788                                    brightness = lifxc.brightness;
789                                    saturation = lifxc.saturation;
790                                    hue = lifxc.hue;
791                                }
792                            
793                                if cc.contains("white"){
794                                    let hbsk_set = HSBK {
795                                        hue: 0,
796                                        saturation: 0,
797                                        brightness: brightness,
798                                        kelvin: kelvin,
799                                    };
800                                    bulb.set_color(&mgr.sock, hbsk_set, duration);
801                                }
802        
803                                if cc.contains("red"){
804                                    let hbsk_set = HSBK {
805                                        hue: 0,
806                                        saturation: 65535,
807                                        brightness: brightness,
808                                        kelvin: kelvin,
809                                    };
810                                    bulb.set_color(&mgr.sock, hbsk_set, duration);
811                                }
812        
813                                if cc.contains("orange"){
814                                    let hbsk_set = HSBK {
815                                        hue: 7098,
816                                        saturation: 65535,
817                                        brightness: brightness,
818                                        kelvin: kelvin,
819                                    };
820                                    bulb.set_color(&mgr.sock, hbsk_set, duration);
821                                }
822        
823                                if cc.contains("yellow"){
824                                    let hbsk_set = HSBK {
825                                        hue: 10920,
826                                        saturation: 65535,
827                                        brightness: brightness,
828                                        kelvin: kelvin,
829                                    };
830                                    bulb.set_color(&mgr.sock, hbsk_set, duration);
831                                }
832        
833                                if cc.contains("cyan"){
834                                    let hbsk_set = HSBK {
835                                        hue: 32760,
836                                        saturation: 65535,
837                                        brightness: brightness,
838                                        kelvin: kelvin,
839                                    };
840                                    bulb.set_color(&mgr.sock, hbsk_set, duration);
841                                }
842        
843                                if cc.contains("green"){
844                                    let hbsk_set = HSBK {
845                                        hue: 21840,
846                                        saturation: 65535,
847                                        brightness: brightness,
848                                        kelvin: kelvin,
849                                    };
850                                    bulb.set_color(&mgr.sock, hbsk_set, duration);
851                                }
852        
853                                if cc.contains("blue"){
854                                    let hbsk_set = HSBK {
855                                        hue: 43680,
856                                        saturation: 65535,
857                                        brightness: brightness,
858                                        kelvin: kelvin,
859                                    };
860                                    bulb.set_color(&mgr.sock, hbsk_set, duration);
861                                }
862        
863                                if cc.contains("purple"){
864                                    let hbsk_set = HSBK {
865                                        hue: 50050,
866                                        saturation: 65535,
867                                        brightness: brightness,
868                                        kelvin: kelvin,
869                                    };
870                                    bulb.set_color(&mgr.sock, hbsk_set, duration);
871                                }
872        
873                                if cc.contains("pink"){
874                                    let hbsk_set = HSBK {
875                                        hue: 63700,
876                                        saturation: 25000,
877                                        brightness: brightness,
878                                        kelvin: kelvin,
879                                    };
880                                    bulb.set_color(&mgr.sock, hbsk_set, duration);
881                                }
882        
883        
884                                if cc.contains("hue:"){
885        
886                                    let hue_split = cc.split("hue:");
887                                    let hue_vec: Vec<&str> = hue_split.collect();
888                                    let new_hue = hue_vec[1].to_string().parse::<u16>().unwrap(); 
889                                    let hbsk_set = HSBK {
890                                        hue: new_hue,
891                                        saturation: saturation,
892                                        brightness: brightness,
893                                        kelvin: kelvin,
894                                    };
895                                    bulb.set_color(&mgr.sock, hbsk_set, duration);
896                                }
897        
898                                if cc.contains("saturation:"){
899                                    let saturation_split = cc.split("saturation:");
900                                    let saturation_vec: Vec<&str> = saturation_split.collect();
901                                    let new_saturation_float = saturation_vec[1].to_string().parse::<f64>().unwrap(); 
902                                    let new_saturation: u16 = (f64::from(100) * new_saturation_float) as u16;
903                                    let hbsk_set = HSBK {
904                                        hue: hue,
905                                        saturation: new_saturation,
906                                        brightness: brightness,
907                                        kelvin: kelvin,
908                                    };
909                                    bulb.set_color(&mgr.sock, hbsk_set, duration);
910                                }
911        
912                                if cc.contains("brightness:"){
913                                    let brightness_split = cc.split("brightness:");
914                                    let brightness_vec: Vec<&str> = brightness_split.collect();
915                                    let new_brightness_float = brightness_vec[1].to_string().parse::<f64>().unwrap(); 
916                                    let new_brightness: u16 = (f64::from(65535) * new_brightness_float) as u16;
917                                    let hbsk_set = HSBK {
918                                        hue: hue,
919                                        saturation: saturation,
920                                        brightness: new_brightness,
921                                        kelvin: kelvin,
922                                    };
923                                    bulb.set_color(&mgr.sock, hbsk_set, duration);
924                                }
925        
926                                if cc.contains("kelvin:"){
927                                    let kelvin_split = cc.split("kelvin:");
928                                    let kelvin_vec: Vec<&str> = kelvin_split.collect();
929                                    let new_kelvin = kelvin_vec[1].to_string().parse::<u16>().unwrap(); 
930                                    let hbsk_set = HSBK {
931                                        hue: hue,
932                                        saturation: 0,
933                                        brightness: brightness,
934                                        kelvin: new_kelvin,
935                                    };
936                                    bulb.set_color(&mgr.sock, hbsk_set, duration);
937                                }
938        
939                                if cc.contains("rgb:"){
940        
941        
942                                    let rgb_split = cc.split("rgb:");
943                                    let rgb_vec: Vec<&str> = rgb_split.collect();
944                                    let rgb_parts = rgb_vec[1].to_string();
945        
946                                    let rgb_part_split = rgb_parts.split(",");
947                                    let rgb_parts_vec: Vec<&str> = rgb_part_split.collect();
948        
949                                    let red_int = rgb_parts_vec[0].to_string().parse::<i64>().unwrap(); 
950                                    let red_float: f32 = (red_int) as f32;
951        
952                                    let green_int = rgb_parts_vec[1].to_string().parse::<i64>().unwrap(); 
953                                    let green_float: f32 = (green_int) as f32;
954        
955                                    let blue_int = rgb_parts_vec[2].to_string().parse::<i64>().unwrap(); 
956                                    let blue_float: f32 = (blue_int) as f32;
957        
958                                    let hcc = palette::Hsv::from_rgb(palette::Rgb{
959                                        red: red_float,
960                                        green: green_float,
961                                        blue: blue_float,
962                                    });
963        
964                                    // TODO: Why does this ugly hack work? Why is lifx api so differ
965                                    let hbsk_set = HSBK {
966                                        hue: (hcc.hue.to_positive_degrees() * 182.0) as u16,
967                                        saturation: (hcc.saturation.to_degrees() * 1000.0) as u16,
968                                        brightness: brightness,
969                                        kelvin: kelvin,
970                                    };
971
972        
973                                    bulb.set_color(&mgr.sock, hbsk_set, duration);
974        
975                                }
976        
977                                if cc.contains("#"){
978                                    println!("!CC!");
979                                    let hex_split = cc.split("#");
980                                    let hex_vec: Vec<&str> = hex_split.collect();
981                                    let hex = hex_vec[1].to_string();
982        
983                                    let rgb2 = Rgb::from_hex_str(format!("#{}", hex).as_str()).unwrap();
984                                    // Rgb { r: 255.0, g: 204.0, b: 0.0 }
985        
986                                    println!("{:?}", rgb2);
987        
988                                    let red_int = rgb2.get_red().to_string().parse::<i64>().unwrap(); 
989                                    let red_float: f32 = (red_int) as f32;
990        
991                                    let green_int = rgb2.get_green().to_string().parse::<i64>().unwrap(); 
992                                    let green_float: f32 = (green_int) as f32;
993        
994                                    let blue_int = rgb2.get_blue().to_string().parse::<i64>().unwrap(); 
995                                    let blue_float: f32 = (blue_int) as f32;
996        
997        
998                                    println!("red_float: {:?}", red_float);
999                                    println!("green_float: {:?}", green_float);
1000                                    println!("blue_float: {:?}", blue_float);
1001        
1002                    
1003                                    let hcc = palette::Hsv::from_rgb(palette::Rgb{
1004                                        red: red_float,
1005                                        green: green_float,
1006                                        blue: blue_float,
1007                                    });
1008
1009                                    println!("hcc: {:?}", hcc);
1010        
1011                                    // TODO: Why does this ugly hack work? Why is lifx api so differ
1012                                    let hbsk_set = HSBK {
1013                                        hue: (hcc.hue.to_positive_degrees() * 182.0) as u16,
1014                                        saturation: (hcc.saturation.to_degrees() * 1000.0) as u16,
1015                                        brightness: brightness,
1016                                        kelvin: kelvin,
1017                                    };
1018
1019                                    println!("hbsk_set: {:?}", hbsk_set);
1020        
1021        
1022        
1023                                    bulb.set_color(&mgr.sock, hbsk_set, duration);
1024        
1025                                }
1026        
1027                            }
1028                        }
1029        
1030        
1031                        // Brightness
1032                        if input.brightness.is_some() {
1033                            let brightness = input.brightness.unwrap();
1034        
1035                            for bulb in &bulbs_vec {
1036        
1037        
1038                                let mut kelvin = 6500;
1039                                let mut saturation = 0;
1040                                let mut hue = 0;
1041        
1042                                let mut duration = 0;
1043                                if input.duration.is_some(){
1044                                    duration = input.duration.unwrap() as u32;
1045                                }
1046        
1047                                if bulb.lifx_color.is_some() {
1048                                    let lifxc = bulb.lifx_color.as_ref().unwrap();
1049                                    kelvin = lifxc.kelvin;
1050                                    saturation = lifxc.saturation;
1051                                    hue = lifxc.hue;
1052                                }
1053                                
1054                                let new_brightness_float = brightness.to_string().parse::<f64>().unwrap(); 
1055                                let new_brightness: u16 = (f64::from(65535) * new_brightness_float) as u16;
1056                                let hbsk_set = HSBK {
1057                                    hue: hue,
1058                                    saturation: saturation,
1059                                    brightness: new_brightness,
1060                                    kelvin: kelvin,
1061                                };
1062                                bulb.set_color(&mgr.sock, hbsk_set, duration);
1063        
1064                            }
1065        
1066                        }
1067        
1068                        // Infrared
1069                        if input.infrared.is_some() {
1070                            let new_brightness: u16 = (f64::from(65535) * input.infrared.unwrap()) as u16;
1071        
1072                            for bulb in &bulbs_vec {
1073                                bulb.set_infrared(&mgr.sock, new_brightness);
1074                            }
1075                        }
1076        
1077        
1078                        // std::mem::drop(mgr);
1079                        // std::mem::drop(lock);
1080                        // TODO - Send Results
1081                        // {
1082                        //     "results": [
1083                        //       {
1084                        //         "id": "d3b2f2d97452",
1085                        //         "label": "Left Lamp",
1086                        //         "status": "ok" // timeout or error
1087                        //       }
1088                        //     ]
1089                        //   }
1090        
1091        
1092                        response = Response::text("done");
1093        
1094                    }
1095        
1096        
1097                    // ListLights
1098                    // https://api.lifx.com/v1/lights/:selector
1099                    if request.url().contains("/v1/lights/") && !request.url().contains("/state"){
1100                        response = Response::json(&bulbs_vec.clone());
1101                    }
1102        
1103        
1104                    // Drop mutex locks
1105                    std::mem::drop(bulbs);
1106                    std::mem::drop(mgr);
1107                    std::mem::drop(lock);
1108        
1109                    return response;
1110                });
1111            });
1112
1113
1114        },
1115        Err(e) => {
1116            println!("{:?}", e);
1117        }
1118    }
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129}