sonos_api/lib.rs
1//! High-level Sonos API for device control
2//!
3//! This crate provides a type-safe, trait-based API for controlling Sonos devices.
4//! It uses the private `soap-client` crate for low-level SOAP communication.
5//!
6//! # UPnP Architecture
7//!
8//! This API correctly implements the UPnP architecture used by Sonos devices:
9//!
10//! - **Control Operations**: Commands like Play, Pause, SetVolume are sent to `/Control` endpoints
11//! - **Event Subscriptions**: Subscriptions for state change notifications are sent to `/Event` endpoints
12//!
13//! These are completely separate and independent concepts that work together to provide
14//! both control and monitoring capabilities.
15//!
16//! # Enhanced Operation Framework
17//!
18//! The operation framework provides composable, declarative UPnP operations with validation,
19//! retry policies, and timeouts:
20//!
21//! ```rust,ignore
22//! use sonos_api::{SonosClient, services::av_transport, services::rendering_control};
23//! use sonos_api::operation::ValidationLevel;
24//!
25//! let client = SonosClient::new();
26//!
27//! // Simple operation execution
28//! let play_op = av_transport::play("1".to_string())
29//! .with_validation(ValidationLevel::Comprehensive)
30//! .build()?;
31//!
32//! client.execute_enhanced("192.168.1.100", play_op)?;
33//!
34//! // Composed operations
35//! let sequence = av_transport::play("1".to_string())
36//! .build()?
37//! .and_then(rendering_control::set_volume("Master".to_string(), 75).build()?);
38//!
39//! client.execute_sequence("192.168.1.100", sequence)?;
40//! ```
41//!
42//! # Event Subscription Management
43//!
44//! UPnP event subscriptions allow you to receive real-time state change notifications
45//! from Sonos devices. Subscriptions are managed separately from control operations:
46//!
47//! ## Client-Level Subscriptions
48//!
49//! Subscribe directly to any service using the client:
50//!
51//! ```rust,ignore
52//! use sonos_api::{SonosClient, Service};
53//!
54//! let client = SonosClient::new();
55//!
56//! // Subscribe to AVTransport events (play/pause state changes)
57//! let av_subscription = client.subscribe(
58//! "192.168.1.100",
59//! Service::AVTransport,
60//! "http://192.168.1.50:8080/callback"
61//! )?;
62//!
63//! // Subscribe to RenderingControl events (volume changes)
64//! let rc_subscription = client.subscribe(
65//! "192.168.1.100",
66//! Service::RenderingControl,
67//! "http://192.168.1.50:8080/callback"
68//! )?;
69//!
70//! // Custom timeout (default is 1800 seconds)
71//! let long_subscription = client.subscribe_with_timeout(
72//! "192.168.1.100",
73//! Service::AVTransport,
74//! "http://192.168.1.50:8080/callback",
75//! 7200 // 2 hours
76//! )?;
77//! ```
78//!
79//! ## Service-Level Subscription Helpers
80//!
81//! Each service module provides convenient subscription helpers:
82//!
83//! ```rust,ignore
84//! use sonos_api::{SonosClient, services::av_transport, services::rendering_control};
85//!
86//! let client = SonosClient::new();
87//!
88//! // Subscribe to specific services using module helpers
89//! let av_subscription = av_transport::subscribe(
90//! &client,
91//! "192.168.1.100",
92//! "http://192.168.1.50:8080/callback"
93//! )?;
94//!
95//! let rc_subscription = rendering_control::subscribe(
96//! &client,
97//! "192.168.1.100",
98//! "http://192.168.1.50:8080/callback"
99//! )?;
100//!
101//! // With custom timeout
102//! let long_av_subscription = av_transport::subscribe_with_timeout(
103//! &client,
104//! "192.168.1.100",
105//! "http://192.168.1.50:8080/callback",
106//! 3600
107//! )?;
108//! ```
109//!
110//! ## Subscription Lifecycle Management
111//!
112//! All subscriptions return a `ManagedSubscription` that handles lifecycle management:
113//!
114//! ```rust,ignore
115//! let subscription = client.subscribe(
116//! "192.168.1.100",
117//! Service::AVTransport,
118//! "http://192.168.1.50:8080/callback"
119//! )?;
120//!
121//! // Check if renewal is needed and renew if so
122//! if subscription.needs_renewal() {
123//! subscription.renew()?;
124//! }
125//!
126//! // Clean up when done
127//! subscription.unsubscribe()?;
128//! ```
129//!
130//! The `ManagedSubscription` handles all lifecycle management including expiration tracking,
131//! renewal logic, and proper cleanup.
132//!
133//! ## Control Operations and Subscriptions Together
134//!
135//! Control operations and subscriptions work independently but can be used together:
136//!
137//! ```rust,ignore
138//! use sonos_api::{SonosClient, Service, services::av_transport};
139//!
140//! let client = SonosClient::new();
141//!
142//! // Set up event subscription first
143//! let subscription = client.subscribe(
144//! "192.168.1.100",
145//! Service::AVTransport,
146//! "http://192.168.1.50:8080/callback"
147//! )?;
148//!
149//! // Execute control operations
150//! let play_op = av_transport::play("1".to_string()).build()?;
151//! client.execute_enhanced("192.168.1.100", play_op)?;
152//!
153//! // The subscription will receive events about state changes
154//! // caused by the control operations
155//! ```
156
157pub mod client;
158pub mod error;
159pub mod events;
160pub mod operation; // Enhanced operation framework
161pub mod service;
162pub mod services; // Enhanced services
163pub mod subscription; // New event handling framework
164pub mod types;
165
166// Common types shared across the workspace
167pub use types::{GroupId, SpeakerId};
168
169// Legacy exports for backward compatibility
170pub use client::SonosClient;
171pub use error::{ApiError, Result};
172pub use operation::SonosOperation; // Legacy trait
173pub use service::{Service, ServiceInfo, ServiceScope};
174pub use subscription::ManagedSubscription;
175
176// New enhanced operation framework exports
177pub use operation::{
178 OperationBuilder, OperationMetadata, UPnPOperation, Validate, ValidationError, ValidationLevel,
179};
180
181// New event handling framework exports
182pub use events::{
183 extract_xml_value, EnrichedEvent, EventParser, EventParserRegistry, EventProcessor, EventSource,
184};
185
186// Enhanced services are available through the services module