sonos_discovery/lib.rs
1//! Internal implementation detail of [`sonos-sdk`](https://crates.io/crates/sonos-sdk). Not intended for direct use.
2//!
3//! Sonos device discovery library
4//!
5//! This crate provides a simple API for discovering Sonos devices on a local network
6//! using SSDP (Simple Service Discovery Protocol) and UPnP device descriptions.
7//!
8//! # Quick Start
9//!
10//! ```no_run
11//! use sonos_discovery::get;
12//!
13//! // Discover all Sonos devices on the network
14//! let devices = get();
15//! for device in devices {
16//! println!("Found {} at {}", device.name, device.ip_address);
17//! }
18//! ```
19//!
20//! # Iterator-based Discovery
21//!
22//! For more control, use the iterator API:
23//!
24//! ```no_run
25//! use sonos_discovery::{get_iter, DeviceEvent};
26//!
27//! for event in get_iter() {
28//! match event {
29//! DeviceEvent::Found(device) => {
30//! println!("Found: {}", device.name);
31//! // Can break early if needed
32//! }
33//! }
34//! }
35//! ```
36
37pub mod device;
38mod discovery;
39mod error;
40mod ssdp;
41
42pub use discovery::DiscoveryIterator;
43pub use error::{DiscoveryError, Result};
44
45/// Information about a discovered Sonos device.
46///
47/// Contains all relevant metadata needed to identify and connect to a Sonos speaker.
48#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
49pub struct Device {
50 /// Unique device identifier (UDN), e.g., "uuid:RINCON_000E58A0123456"
51 pub id: String,
52 /// Friendly name of the device
53 pub name: String,
54 /// Room name where the device is located
55 pub room_name: String,
56 /// IP address of the device
57 pub ip_address: String,
58 /// Port number (typically 1400)
59 pub port: u16,
60 /// Model name (e.g., "Sonos One", "Sonos Play:1")
61 pub model_name: String,
62}
63
64/// Events emitted during device discovery.
65///
66/// Currently only supports device found events. Future versions may add additional
67/// event types (e.g., device lost, device updated).
68#[derive(Debug, Clone)]
69pub enum DeviceEvent {
70 /// A Sonos device was found on the network
71 Found(Device),
72}
73
74use std::time::Duration;
75
76/// Discover all Sonos devices on the local network with a default 3-second timeout.
77///
78/// This is a convenience function that collects all discovered devices into a Vec.
79/// For more control over the discovery process, use `get_iter()` instead.
80///
81/// # Examples
82///
83/// ```no_run
84/// use sonos_discovery::get;
85///
86/// let devices = get();
87/// for device in devices {
88/// println!("Found: {} at {}", device.name, device.ip_address);
89/// }
90/// ```
91pub fn get() -> Vec<Device> {
92 get_with_timeout(Duration::from_secs(3))
93}
94
95/// Discover all Sonos devices on the local network with a custom timeout.
96///
97/// This function collects all discovered devices into a Vec. The timeout parameter
98/// controls how long to wait for SSDP responses and HTTP requests.
99///
100/// # Arguments
101///
102/// * `timeout` - Maximum duration to wait for network operations
103///
104/// # Examples
105///
106/// ```no_run
107/// use sonos_discovery::get_with_timeout;
108/// use std::time::Duration;
109///
110/// let devices = get_with_timeout(Duration::from_secs(5));
111/// for device in devices {
112/// println!("Found: {} at {}", device.name, device.ip_address);
113/// }
114/// ```
115pub fn get_with_timeout(timeout: Duration) -> Vec<Device> {
116 get_iter_with_timeout(timeout)
117 .map(|event| match event {
118 DeviceEvent::Found(device) => device,
119 })
120 .collect()
121}
122
123/// Get an iterator for discovering Sonos devices with a default 3-second timeout.
124///
125/// This function returns an iterator that yields `DeviceEvent::Found` for each
126/// discovered device. This allows for streaming processing and early termination.
127///
128/// # Examples
129///
130/// ```no_run
131/// use sonos_discovery::{get_iter, DeviceEvent};
132///
133/// for event in get_iter() {
134/// match event {
135/// DeviceEvent::Found(device) => {
136/// println!("Found: {} at {}", device.name, device.ip_address);
137/// // Can break early if needed
138/// break;
139/// }
140/// }
141/// }
142/// ```
143pub fn get_iter() -> DiscoveryIterator {
144 get_iter_with_timeout(Duration::from_secs(3))
145}
146
147/// Get an iterator for discovering Sonos devices with a custom timeout.
148///
149/// This function returns an iterator that yields `DeviceEvent::Found` for each
150/// discovered device. The timeout parameter controls how long to wait for
151/// SSDP responses and HTTP requests.
152///
153/// # Arguments
154///
155/// * `timeout` - Maximum duration to wait for network operations
156///
157/// # Examples
158///
159/// ```no_run
160/// use sonos_discovery::{get_iter_with_timeout, DeviceEvent};
161/// use std::time::Duration;
162///
163/// for event in get_iter_with_timeout(Duration::from_secs(5)) {
164/// match event {
165/// DeviceEvent::Found(device) => {
166/// println!("Found: {} at {}", device.name, device.ip_address);
167/// }
168/// }
169/// }
170/// ```
171pub fn get_iter_with_timeout(timeout: Duration) -> DiscoveryIterator {
172 DiscoveryIterator::new(timeout).unwrap_or_else(|_| {
173 // If we fail to create the iterator, return an empty one
174 // This is better than panicking
175 DiscoveryIterator::empty()
176 })
177}