sonor/
discovery.rs

1use crate::{
2    speaker::{Speaker, SONOS_URN},
3    Error, Result,
4};
5use futures_util::stream::{FuturesUnordered, Stream, TryStreamExt};
6use rupnp::Device;
7use std::time::Duration;
8
9// 1,408ms +/- 169ms for two devices in network
10/*pub(crate) async fn discover_simple(
11    timeout: Duration,
12) -> Result<impl Stream<Item = Result<Speaker>>> {
13    let stream = rupnp::discover(&SONOS_URN.into(), timeout)
14        .await?
15        .map_ok(Speaker::from_device)
16        .map_ok(|device| device.expect("searched for sonos urn but got something else"));
17
18    Ok(stream)
19}*/
20
21// 292ms +/- 191ms for two devices in network
22/// Discover sonos players on the network.
23///
24/// # Example Usage
25///
26/// ```rust,no_run
27/// # use futures::prelude::*;
28/// # use std::time::Duration;
29/// # async fn f() -> Result<(), sonor::Error> {
30/// let mut devices = sonor::discover(Duration::from_secs(2)).await?;
31///
32/// while let Some(device) = devices.try_next().await? {
33///     let name = device.name().await?;
34///     println!("- {}", name);
35/// }
36/// # Ok(())
37/// # };
38pub async fn discover(timeout: Duration) -> Result<impl Stream<Item = Result<Speaker>>> {
39    // this method searches for devices, but when it finds the first one it
40    // uses its `.zone_group_state` to find the other devices in the network.
41
42    let devices = rupnp::discover(&SONOS_URN.into(), timeout)
43        .await?
44        .try_filter_map(|dev| async move { Ok(Speaker::from_device(dev)) });
45    futures_util::pin_mut!(devices);
46
47    let mut devices_iter = None;
48
49    if let Some(device) = devices.try_next().await? {
50        let iter = device
51            ._zone_group_state()
52            .await?
53            .into_iter()
54            .flat_map(|(_, speakers)| speakers)
55            .map(|speaker_info| {
56                let url = speaker_info.location().parse();
57                async {
58                    let device = Device::from_url(url?).await?;
59                    let speaker = Speaker::from_device(device);
60                    speaker.ok_or(Error::GetZoneGroupStateReturnedNonSonos)
61                }
62            });
63        devices_iter = Some(iter);
64    };
65
66    Ok(devices_iter
67        .into_iter()
68        .flatten()
69        .collect::<FuturesUnordered<_>>())
70}
71
72/// Search for a sonos speaker by its name.
73///
74/// # Example Usage
75///
76/// ```rust,no_run
77/// # use futures::prelude::*;
78/// # use std::time::Duration;
79/// # async fn f() -> Result<(), sonor::Error> {
80/// let speaker = sonor::find("your room name", Duration::from_secs(1)).await?
81///     .expect("player exists");
82/// assert_eq!(speaker.name().await?, "yoor room name");
83/// # Ok(())
84/// # };
85pub async fn find(roomname: &str, timeout: Duration) -> Result<Option<Speaker>> {
86    let mut devices = discover(timeout).await?;
87
88    while let Some(device) = devices.try_next().await? {
89        if device.name().await?.eq_ignore_ascii_case(roomname) {
90            return Ok(Some(device));
91        }
92    }
93
94    Ok(None)
95}