pub struct SonosSystem { /* private fields */ }Expand description
Main system entry point - provides DOM-like API
SonosSystem is fully synchronous - no async/await required.
§Example
use sonos_sdk::SonosSystem;
fn main() -> Result<(), sonos_sdk::SdkError> {
let system = SonosSystem::new()?;
// Get speaker by name
let speaker = system.speaker("Living Room")
.ok_or_else(|| sonos_sdk::SdkError::SpeakerNotFound("Living Room".to_string()))?;
// Three methods on each property:
let volume = speaker.volume.get(); // Get cached value
let fresh_volume = speaker.volume.fetch()?; // API call + update cache
let current = speaker.volume.watch()?; // Start watching for changes
// Iterate over changes
for event in system.iter() {
println!("Property changed: {:?}", event);
}
Ok(())
}Implementations§
Source§impl SonosSystem
impl SonosSystem
Sourcepub fn new() -> Result<Self, SdkError>
pub fn new() -> Result<Self, SdkError>
Create a new SonosSystem with cache-first device discovery (sync)
Discovery strategy:
- Try loading cached devices from disk (~/.cache/sonos/cache.json)
- If cache is fresh (< 24h), use cached devices
- If cache is stale, run SSDP; fall back to stale cache if SSDP finds nothing
- If no cache exists, run SSDP discovery
- If no devices found anywhere, return
Err(SdkError::DiscoveryFailed)
Sourcepub fn get_speaker_by_name(&self, name: &str) -> Option<Speaker>
👎Deprecated since 0.2.0: renamed to speaker()
pub fn get_speaker_by_name(&self, name: &str) -> Option<Speaker>
renamed to speaker()
Get speaker by name (sync)
Sourcepub fn speaker_by_id(&self, speaker_id: &SpeakerId) -> Option<Speaker>
pub fn speaker_by_id(&self, speaker_id: &SpeakerId) -> Option<Speaker>
Get speaker by ID (sync)
Sourcepub fn get_speaker_by_id(&self, speaker_id: &SpeakerId) -> Option<Speaker>
👎Deprecated since 0.2.0: renamed to speaker_by_id()
pub fn get_speaker_by_id(&self, speaker_id: &SpeakerId) -> Option<Speaker>
renamed to speaker_by_id()
Get speaker by ID (sync)
Sourcepub fn speaker_names(&self) -> Vec<String>
pub fn speaker_names(&self) -> Vec<String>
Get all speaker names (sync)
Sourcepub fn state_manager(&self) -> &Arc<StateManager>
pub fn state_manager(&self) -> &Arc<StateManager>
Get the state manager for advanced usage
Sourcepub fn iter(&self) -> ChangeIterator ⓘ
pub fn iter(&self) -> ChangeIterator ⓘ
Get a blocking iterator over property change events
Only emits events for properties that have been watch()ed.
§Example
// First, watch some properties
speaker.volume.watch()?;
speaker.playback_state.watch()?;
// Then iterate over changes (blocking)
for event in system.iter() {
println!("Changed: {} on {}", event.property_key, event.speaker_id);
}Sourcepub fn groups(&self) -> Vec<Group>
pub fn groups(&self) -> Vec<Group>
Get all current groups (sync)
Returns all groups in the system. Every speaker is always in a group, so a single speaker forms a group of one.
§Example
for group in system.groups() {
println!("Group: {} ({} members)", group.id, group.member_count());
if let Some(coordinator) = group.coordinator() {
println!(" Coordinator: {}", coordinator.name);
}
}Sourcepub fn group_by_id(&self, group_id: &GroupId) -> Option<Group>
pub fn group_by_id(&self, group_id: &GroupId) -> Option<Group>
Sourcepub fn get_group_by_id(&self, group_id: &GroupId) -> Option<Group>
👎Deprecated since 0.2.0: renamed to group_by_id()
pub fn get_group_by_id(&self, group_id: &GroupId) -> Option<Group>
renamed to group_by_id()
Get a specific group by ID (sync)
Sourcepub fn group_for_speaker(&self, speaker_id: &SpeakerId) -> Option<Group>
pub fn group_for_speaker(&self, speaker_id: &SpeakerId) -> Option<Group>
Get the group a speaker belongs to (sync)
Returns None if the speaker is not found or has no group.
Since all speakers are always in a group, this typically only returns
None if the speaker ID is invalid.
§Example
if let Some(speaker) = system.speaker("Living Room") {
if let Some(group) = system.group_for_speaker(&speaker.id) {
println!("{} is in a group with {} speakers",
speaker.name, group.member_count());
}
}Sourcepub fn get_group_for_speaker(&self, speaker_id: &SpeakerId) -> Option<Group>
👎Deprecated since 0.2.0: use speaker.group() or group_for_speaker() instead
pub fn get_group_for_speaker(&self, speaker_id: &SpeakerId) -> Option<Group>
use speaker.group() or group_for_speaker() instead
Get the group a speaker belongs to (sync)
Sourcepub fn group(&self, name: &str) -> Option<Group>
pub fn group(&self, name: &str) -> Option<Group>
Get a group by its coordinator speaker name (sync)
Sonos groups don’t have independent names — they are identified by the coordinator speaker’s friendly name. This method matches groups by looking up the coordinator’s name in the state manager.
Returns None if no group’s coordinator matches the given name.
§Example
if let Some(group) = system.group("Living Room") {
println!("Found group with {} members", group.member_count());
}Sourcepub fn get_group_by_name(&self, name: &str) -> Option<Group>
👎Deprecated since 0.2.0: renamed to group()
pub fn get_group_by_name(&self, name: &str) -> Option<Group>
renamed to group()
Get a group by its coordinator speaker name (sync)
Sourcepub fn create_group(
&self,
coordinator: &Speaker,
members: &[&Speaker],
) -> Result<GroupChangeResult, SdkError>
pub fn create_group( &self, coordinator: &Speaker, members: &[&Speaker], ) -> Result<GroupChangeResult, SdkError>
Create a new group with the specified coordinator and members
Adds each member speaker to the coordinator’s current group.
Attempts every speaker even if some fail, returning per-speaker results.
After calling this, re-fetch groups via groups() to see the updated topology.
§Example
let living_room = system.speaker("Living Room").unwrap();
let kitchen = system.speaker("Kitchen").unwrap();
let bedroom = system.speaker("Bedroom").unwrap();
let result = system.create_group(&living_room, &[&kitchen, &bedroom])?;
if !result.is_success() {
for (id, err) in &result.failed {
eprintln!("Failed to add {}: {}", id, err);
}
}