Skip to main content

bluos_api_rs/
discover.rs

1#![cfg(feature = "discover")]
2use crate::error::Error;
3use std::any::Any;
4use std::sync::Arc;
5use std::time::Duration;
6use tokio::sync::mpsc::{self, Receiver};
7use zeroconf::prelude::*;
8use zeroconf::{MdnsBrowser, ServiceDiscovery, ServiceType};
9
10pub struct DiscoveredBluOSDevice {
11    pub name: String,
12    pub hostname: String,
13    pub port: u16,
14}
15
16pub struct Discovery {
17    cancel: Option<std::sync::mpsc::Sender<bool>>,
18}
19
20impl Discovery {
21    pub fn new() -> Discovery {
22        Discovery { cancel: None }
23    }
24    /// Discover uses mDNS to scan the network for BluOS devices
25    /// Returns a Tokio channel that streams results as they are found
26    ///
27    /// The discovery process is cancelled on drop
28    pub async fn discover(&mut self) -> Result<Receiver<DiscoveredBluOSDevice>, Error> {
29        //Check if we're already doing this
30        if self.cancel.is_some() {
31            return Err(Error::AlreadyDiscovering);
32        }
33
34        let (tx, rx) = mpsc::channel(200);
35        let (ctx, crx): (
36            std::sync::mpsc::Sender<bool>,
37            std::sync::mpsc::Receiver<bool>,
38        ) = std::sync::mpsc::channel();
39        self.cancel = Some(ctx);
40
41        tokio::task::spawn_blocking(move || {
42            let mut browser = MdnsBrowser::new(ServiceType::new("musc", "tcp").unwrap());
43
44            browser.set_service_discovered_callback(Box::new(
45                move |result: zeroconf::Result<ServiceDiscovery>,
46                      _context: Option<Arc<dyn Any>>| {
47                    let res = result.unwrap();
48                    let _ = tx.blocking_send(DiscoveredBluOSDevice {
49                        name: res.name().clone(),
50                        hostname: res.address().clone(),
51                        port: *res.port(),
52                    });
53                },
54            ));
55
56            let event_loop = browser.browse_services().unwrap();
57
58            loop {
59                event_loop.poll(Duration::from_millis(500)).unwrap();
60
61                match crx.try_recv() {
62                    Ok(_) => return,
63                    Err(e) => match e {
64                        std::sync::mpsc::TryRecvError::Empty => {}
65                        std::sync::mpsc::TryRecvError::Disconnected => return,
66                    },
67                }
68            }
69        });
70
71        Ok(rx)
72    }
73    /// Discover one is a helper function that scans the network and returns the FIRST BluOS device it finds.
74    /// This is useful if you only have one BluOS device.
75    pub async fn discover_one() -> Result<DiscoveredBluOSDevice, Error> {
76        let mut d = Discovery::new();
77        let mut c = d.discover().await?;
78
79        let m = c.recv().await.ok_or(Error::NoBluOSError)?;
80
81        Ok(m)
82    }
83}
84
85impl Drop for Discovery {
86    fn drop(&mut self) {
87        match &self.cancel {
88            Some(c) => {
89                let _ = c.send(true);
90            }
91            None => {}
92        }
93    }
94}