sonos_sdk/lib.rs
1//! # Sonos SDK - Sync-First API for Sonos Control
2//!
3//! Provides a clean, property-centric API for controlling Sonos devices.
4//! All operations are **synchronous** - no async/await required.
5//!
6//! ## Quick Start
7//!
8//! ```rust,ignore
9//! use sonos_sdk::prelude::*;
10//!
11//! fn main() -> Result<(), SdkError> {
12//! let sonos = SonosSystem::new()?;
13//!
14//! // Direct SOAP calls — no event infrastructure created
15//! let kitchen = sonos.speaker("Kitchen").unwrap();
16//! kitchen.play()?;
17//! let vol = kitchen.volume.fetch()?;
18//!
19//! // Fluent navigation
20//! let group = kitchen.group().unwrap();
21//! println!("Kitchen is in group {}", group.id);
22//!
23//! // ONLY NOW does the event manager lazily initialize
24//! let _vol = kitchen.volume.watch()?;
25//! for event in sonos.iter() {
26//! // The new value arrives with the event — no cache re-read needed
27//! match &event.change {
28//! PropertyChange::Volume(v) => println!("volume -> {}%", v.value()),
29//! other => println!("{} changed on {}", other.key(), event.speaker_id),
30//! }
31//! }
32//!
33//! Ok(())
34//! }
35//! ```
36//!
37//! ## Key Features
38//!
39//! - **Sync-First API**: All methods are synchronous - no async/await required
40//! - **Cheap constructor**: `SonosSystem::new()` does discovery only — event infrastructure is lazy
41//! - **DOM-like API**: Access properties directly on speaker objects
42//! - **Three access patterns**: `get()` for cached, `fetch()` for fresh, `watch()` for reactive
43//! - **Fluent navigation**: `speaker.group()`, `group.speaker("name")`
44//! - **Type safety**: All properties are strongly typed
45//! - **Resource efficiency**: Shared state management and HTTP connections
46//!
47//! ## Available Properties
48//!
49//! Currently implemented:
50//! - `volume` - Speaker volume (0-100)
51//! - `playback_state` - Current playback state (Playing/Paused/Stopped/Transitioning)
52//! - `mute` - Mute state
53//! - `bass`, `treble`, `loudness` - EQ settings
54//! - `position` - Current track position
55//! - `current_track` - Track metadata
56//!
57//! ## Architecture
58//!
59//! ```text
60//! sonos-sdk (Sync-First DOM-like API)
61//! ↓
62//! sonos-state (State Management) ←→ sonos-event-manager (Event Subscriptions)
63//! ↓ ↓
64//! sonos-api (UPnP Operations) sonos-stream (Event Processing)
65//! ```
66
67// Main exports
68pub use error::SdkError;
69pub use group::{Group, GroupChangeResult};
70pub use speaker::{PlayMode, SeekTarget, Speaker};
71pub use system::SonosSystem;
72
73// Re-export the generic PropertyHandle, SpeakerContext, and watch types
74pub use property::{PropertyHandle, SpeakerContext, WatchHandle, WatchMode};
75
76// Re-export group property handle types
77pub use property::{
78 GroupContext, GroupFetchable, GroupMuteHandle, GroupPropertyHandle,
79 GroupVolumeChangeableHandle, GroupVolumeHandle,
80};
81
82// Re-export response types for action methods
83pub use sonos_api::services::av_transport::{
84 AddURIToQueueResponse, BecomeCoordinatorOfStandaloneGroupResponse, CreateSavedQueueResponse,
85 GetCrossfadeModeResponse, GetCurrentTransportActionsResponse, GetDeviceCapabilitiesResponse,
86 GetMediaInfoResponse, GetRemainingSleepTimerDurationResponse,
87 GetRunningAlarmPropertiesResponse, GetTransportSettingsResponse,
88 RemoveTrackRangeFromQueueResponse, SaveQueueResponse,
89};
90pub use sonos_api::services::group_rendering_control::SetRelativeGroupVolumeResponse;
91pub use sonos_api::services::rendering_control::SetRelativeVolumeResponse;
92
93// sonos_discovery is internal — consumers use SonosSystem::new()
94// Re-exported under test-support for integration tests that need Device
95#[cfg(feature = "test-support")]
96pub use sonos_discovery;
97
98// Re-export commonly used types from sonos-state
99pub use sonos_state::{
100 ChangeEvent, ChangeIterator, ChangeSource, CurrentTrack, GroupId, GroupMute, GroupVolume,
101 GroupVolumeChangeable, PlaybackState, PropertyChange, SpeakerId, Volume, WriteOutcome,
102 WriteStamp,
103};
104
105/// The property trait, re-exported so downstream crates can write code that is
106/// generic over properties.
107///
108/// Without this a consumer needing `P::KEY` — a UI caching watch handles by
109/// `(speaker, property)`, say — has to depend on `sonos-sdk-state` directly and
110/// reach past this facade into a crate documented as an implementation detail.
111/// That also puts the trait's breaking changes outside this crate's version
112/// contract, since `sonos-sdk-state` is not covered by its semver gate.
113pub use sonos_state::{Property, SonosProperty};
114
115// Public modules
116pub mod prelude;
117
118// Internal modules
119mod cache;
120mod error;
121mod group;
122pub mod property;
123mod speaker;
124mod system;