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}