pub struct StateManager { /* private fields */ }Expand description
Core state manager with sync-first API
All public methods are synchronous. Background event processing happens in a dedicated thread.
Implementations§
Source§impl StateManager
impl StateManager
Sourcepub fn builder() -> StateManagerBuilder
pub fn builder() -> StateManagerBuilder
Create a StateManager builder for custom configuration
Sourcepub fn speaker_infos(&self) -> Vec<SpeakerInfo> ⓘ
pub fn speaker_infos(&self) -> Vec<SpeakerInfo> ⓘ
Get all speaker info
Sourcepub fn speaker_info(&self, speaker_id: &SpeakerId) -> Option<SpeakerInfo>
pub fn speaker_info(&self, speaker_id: &SpeakerId) -> Option<SpeakerInfo>
Get a specific speaker info by ID
Sourcepub fn get_speaker_ip(&self, speaker_id: &SpeakerId) -> Option<IpAddr>
pub fn get_speaker_ip(&self, speaker_id: &SpeakerId) -> Option<IpAddr>
Get speaker IP by ID
Sourcepub fn get_boot_seq(&self, speaker_id: &SpeakerId) -> Option<u32>
pub fn get_boot_seq(&self, speaker_id: &SpeakerId) -> Option<u32>
Get boot_seq for a speaker (used by GroupManagement AddMember)
Sourcepub fn update_speaker_ip(&self, speaker_id: &SpeakerId, new_ip: IpAddr)
pub fn update_speaker_ip(&self, speaker_id: &SpeakerId, new_ip: IpAddr)
Update a speaker’s IP address in both the store and the reverse map.
Sourcepub fn get_satellite_ids(&self) -> Vec<SpeakerId>
pub fn get_satellite_ids(&self) -> Vec<SpeakerId>
Get all satellite speaker IDs from topology data.
Sourcepub fn set_satellite_ids(&self, ids: Vec<SpeakerId>)
pub fn set_satellite_ids(&self, ids: Vec<SpeakerId>)
Store satellite speaker IDs from topology data.
Sourcepub fn iter(&self) -> ChangeIterator ⓘ
pub fn iter(&self) -> ChangeIterator ⓘ
Create a blocking iterator over change events
Only emits events for properties that have been watched.
Each call returns an independent iterator: every iterator receives every event, so two event loops both see the whole stream instead of splitting it between them. An iterator only receives events emitted after it was created, so take it before the writes you want to observe.
Each iterator owns an unbounded queue, so a slow consumer never loses an event and never blocks a fast one — and never drains means never bounded.
§Example
// First, watch some properties
speaker.volume.watch()?;
// Then iterate over changes — the new value rides along on the event
for event in manager.iter() {
match &event.change {
PropertyChange::Volume(v) => println!("volume -> {}%", v.value()),
other => println!("{} changed", other.key()),
}
}Sourcepub fn get_property<P: SonosProperty>(
&self,
speaker_id: &SpeakerId,
) -> Option<P>
pub fn get_property<P: SonosProperty>( &self, speaker_id: &SpeakerId, ) -> Option<P>
Get current property value (sync, no subscription)
For PerCoordinator speaker-scoped properties, this transparently reads from the coordinator’s store, so group members see the coordinator’s value.
Sourcepub fn get_group_property<P: Property>(&self, group_id: &GroupId) -> Option<P>
pub fn get_group_property<P: Property>(&self, group_id: &GroupId) -> Option<P>
Get current group property value (sync, no subscription)
Sourcepub fn set_property<P: SonosProperty>(&self, speaker_id: &SpeakerId, value: P)
pub fn set_property<P: SonosProperty>(&self, speaker_id: &SpeakerId, value: P)
Set a property value
Updates the property value in the store and emits a change event if the property is being watched.
The write is routed the same way Self::get_property reads: for a
PerCoordinator speaker-scoped property, the value lands in the
coordinator’s bag, because get_resolved reads it from there. Writing
the raw speaker_id instead put the value in a bag nothing ever reads —
so speaker.play() on a grouped member updated a cache entry that
playback_state.get() could not see, and the UI kept showing the old
state until an event arrived.
The notification is still keyed on the requesting speaker, so a member watching the property is woken by its own write. The coordinator’s own watchers are reached by the worker’s group fan-out on the next event.
Stamped ChangeSource::LocalAction as of now. Use
Self::set_property_stamped for a fetch() result, whose observation
predates the write by a full network round trip.
Sourcepub fn set_property_stamped<P: SonosProperty>(
&self,
speaker_id: &SpeakerId,
value: P,
stamp: WriteStamp,
) -> WriteOutcome
pub fn set_property_stamped<P: SonosProperty>( &self, speaker_id: &SpeakerId, value: P, stamp: WriteStamp, ) -> WriteOutcome
Set a property value with explicit write provenance.
Rejected without effect if stamp is older than the observation already
stored — see WriteStamp. Returns the outcome so a caller can tell a
rejected write from an accepted one.
Sourcepub fn set_group_property<P: SonosProperty>(&self, group_id: &GroupId, value: P)
pub fn set_group_property<P: SonosProperty>(&self, group_id: &GroupId, value: P)
Set a group property value
Updates the group property value in the store and emits a change event if the property is being watched (keyed on the coordinator’s speaker ID). Used by the SDK layer to store group-scoped values fetched via API calls.
Stamped ChangeSource::LocalAction; see
Self::set_group_property_stamped for fetch() results.
Sourcepub fn set_group_property_stamped<P: SonosProperty>(
&self,
group_id: &GroupId,
value: P,
stamp: WriteStamp,
) -> WriteOutcome
pub fn set_group_property_stamped<P: SonosProperty>( &self, group_id: &GroupId, value: P, stamp: WriteStamp, ) -> WriteOutcome
Set a group property value with explicit write provenance.
Rejected without effect if stamp is older than the observation already
stored — see WriteStamp.
Sourcepub fn register_watch(&self, speaker_id: &SpeakerId, property_key: &'static str)
pub fn register_watch(&self, speaker_id: &SpeakerId, property_key: &'static str)
Register a property as watched (called by PropertyHandle::watch)
Adds one reference. Balanced by Self::unregister_watch; the property
keeps emitting until every registration has been unregistered.
Sourcepub fn unregister_watch(
&self,
speaker_id: &SpeakerId,
property_key: &'static str,
)
pub fn unregister_watch( &self, speaker_id: &SpeakerId, property_key: &'static str, )
Unregister a property watch
Releases one reference taken by Self::register_watch. The property
stops being watched only when the last reference is released, so one
watcher going away cannot silence its siblings. Unregistering something
that was never registered is a no-op.
Sourcepub fn watch_property_with_subscription<P: SonosProperty>(
&self,
speaker_id: &SpeakerId,
) -> Result<Option<P>>
pub fn watch_property_with_subscription<P: SonosProperty>( &self, speaker_id: &SpeakerId, ) -> Result<Option<P>>
Watch a property with automatic UPnP subscription (recommended API)
This is the preferred method for watching properties as it:
- Registers the property for change notifications
- Subscribes to the UPnP service via the event manager
Returns the current cached value if available.
Sourcepub fn unwatch_property_with_subscription<P: SonosProperty>(
&self,
speaker_id: &SpeakerId,
)
pub fn unwatch_property_with_subscription<P: SonosProperty>( &self, speaker_id: &SpeakerId, )
Unwatch a property and release UPnP subscription
Sourcepub fn is_watched(
&self,
speaker_id: &SpeakerId,
property_key: &'static str,
) -> bool
pub fn is_watched( &self, speaker_id: &SpeakerId, property_key: &'static str, ) -> bool
Check if a property is being watched
Sourcepub fn initialize(&self, topology: Topology)
pub fn initialize(&self, topology: Topology)
Initialize from topology data
Sourcepub fn is_initialized(&self) -> bool
pub fn is_initialized(&self) -> bool
Check if initialized with any speakers
Sourcepub fn speaker_count(&self) -> usize
pub fn speaker_count(&self) -> usize
Get number of speakers
Sourcepub fn group_count(&self) -> usize
pub fn group_count(&self) -> usize
Get number of groups
Sourcepub fn groups(&self) -> Vec<GroupInfo>
pub fn groups(&self) -> Vec<GroupInfo>
Get all current groups
Returns all groups in the system. Every speaker is always in a group, so a single speaker forms a group of one.
Sourcepub fn get_group_for_speaker(&self, speaker_id: &SpeakerId) -> Option<GroupInfo>
pub fn get_group_for_speaker(&self, speaker_id: &SpeakerId) -> Option<GroupInfo>
Get the group a speaker belongs to
Uses the speaker_to_group mapping for quick lookup.
Sourcepub fn resolve_subscription_target(
&self,
speaker_id: &SpeakerId,
speaker_ip: IpAddr,
service: Service,
) -> (SpeakerId, IpAddr)
pub fn resolve_subscription_target( &self, speaker_id: &SpeakerId, speaker_ip: IpAddr, service: Service, ) -> (SpeakerId, IpAddr)
Resolve the subscription target for a PerCoordinator service.
For PerCoordinator services, returns the coordinator’s (SpeakerId, IpAddr)
so the SDK can route UPnP subscriptions to the coordinator speaker.
Falls back to the speaker itself if no group data exists.
For non-PerCoordinator services, returns the speaker’s own identity.
Sourcepub fn event_manager(&self) -> Option<&Arc<SonosEventManager>>
pub fn event_manager(&self) -> Option<&Arc<SonosEventManager>>
Get access to the event manager (if configured)
This allows PropertyHandle::watch() to trigger UPnP subscriptions via the event manager’s ensure_service_subscribed() method.
Sourcepub fn set_event_manager(&self, em: Arc<SonosEventManager>) -> Result<()>
pub fn set_event_manager(&self, em: Arc<SonosEventManager>) -> Result<()>
Wire an event manager into this StateManager after construction.
Spawns the event worker thread and registers all known devices. Can only be called once — subsequent calls are no-ops.
Sourcepub fn set_event_init(&self, f: EventInitFn)
pub fn set_event_init(&self, f: EventInitFn)
Set the lazy event manager initialization closure.
Called once by SonosSystem::from_devices_inner() after construction.
Subsequent calls are no-ops (OnceLock semantics).
Sourcepub fn event_init(&self) -> Option<&EventInitFn>
pub fn event_init(&self) -> Option<&EventInitFn>
Get the event init closure (if set).
Used by PropertyHandle::watch() and GroupPropertyHandle::watch()
to trigger lazy event manager creation on first use.