Skip to main content

sonos_api/services/av_transport/
state.rs

1//! Canonical AVTransport service state type.
2//!
3//! Used by both UPnP event streaming (via `into_state()`) and polling (via `poll()`).
4
5use serde::{Deserialize, Serialize};
6
7use crate::SonosClient;
8
9/// Complete AVTransport service state.
10///
11/// Canonical type used by both UPnP event streaming and polling.
12/// Fields match the UPnP AVTransport event data 1:1.
13#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
14pub struct AVTransportState {
15    /// Current transport state (PLAYING, PAUSED_PLAYBACK, STOPPED, etc.)
16    pub transport_state: Option<String>,
17
18    /// Current transport status (OK, ERROR_OCCURRED, etc.)
19    pub transport_status: Option<String>,
20
21    /// Current playback speed
22    pub speed: Option<String>,
23
24    /// Current track URI
25    pub current_track_uri: Option<String>,
26
27    /// Track duration
28    pub track_duration: Option<String>,
29
30    /// Current track metadata (DIDL-Lite XML)
31    pub track_metadata: Option<String>,
32
33    /// Relative time position in current track
34    pub rel_time: Option<String>,
35
36    /// Absolute time position
37    pub abs_time: Option<String>,
38
39    /// Relative track number in queue.
40    /// UPnP returns i32 (-1 means "not implemented"); negative values map to None.
41    pub rel_count: Option<u32>,
42
43    /// Absolute track number.
44    /// UPnP returns i32 (-1 means "not implemented"); negative values map to None.
45    pub abs_count: Option<u32>,
46
47    /// Current play mode (NORMAL, REPEAT_ALL, REPEAT_ONE, SHUFFLE, etc.)
48    pub play_mode: Option<String>,
49
50    /// Next track URI
51    pub next_track_uri: Option<String>,
52
53    /// Next track metadata
54    pub next_track_metadata: Option<String>,
55
56    /// Queue size/length
57    pub queue_length: Option<u32>,
58}
59
60/// Poll a speaker for complete AVTransport state.
61///
62/// Calls GetTransportInfo (required), GetPositionInfo, GetTransportSettings,
63/// and GetMediaInfo (optional — fall back to None on failure).
64pub fn poll(client: &SonosClient, ip: &str) -> crate::Result<AVTransportState> {
65    let transport = client.execute_enhanced(
66        ip,
67        super::get_transport_info_operation()
68            .build()
69            .map_err(|e| crate::ApiError::ParseError(e.to_string()))?,
70    )?;
71
72    let position = super::get_position_info_operation()
73        .build()
74        .ok()
75        .and_then(|op| client.execute_enhanced(ip, op).ok());
76    let settings = super::get_transport_settings_operation()
77        .build()
78        .ok()
79        .and_then(|op| client.execute_enhanced(ip, op).ok());
80    let media = super::get_media_info_operation()
81        .build()
82        .ok()
83        .and_then(|op| client.execute_enhanced(ip, op).ok());
84
85    Ok(AVTransportState {
86        transport_state: Some(transport.current_transport_state),
87        transport_status: Some(transport.current_transport_status),
88        speed: Some(transport.current_speed),
89        current_track_uri: position.as_ref().map(|p| p.track_uri.clone()),
90        track_duration: position.as_ref().map(|p| p.track_duration.clone()),
91        track_metadata: position.as_ref().map(|p| p.track_meta_data.clone()),
92        rel_time: position.as_ref().map(|p| p.rel_time.clone()),
93        abs_time: position.as_ref().map(|p| p.abs_time.clone()),
94        rel_count: position
95            .as_ref()
96            .and_then(|p| u32::try_from(p.rel_count).ok()),
97        abs_count: position
98            .as_ref()
99            .and_then(|p| u32::try_from(p.abs_count).ok()),
100        play_mode: settings.map(|s| s.play_mode),
101        next_track_uri: media.as_ref().map(|m| m.next_uri.clone()),
102        next_track_metadata: media.as_ref().map(|m| m.next_uri_meta_data.clone()),
103        queue_length: media.map(|m| m.nr_tracks),
104    })
105}