Skip to main content

sonos_api/services/rendering_control/
state.rs

1//! Canonical RenderingControl service state type.
2//!
3//! Used by both UPnP event streaming (via `into_state()`) and polling (via `poll()`).
4
5use serde::{Deserialize, Serialize};
6use std::collections::HashMap;
7
8use crate::SonosClient;
9
10/// Complete RenderingControl service state.
11///
12/// Canonical type used by both UPnP event streaming and polling.
13/// Fields match the UPnP RenderingControl event data 1:1.
14#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
15pub struct RenderingControlState {
16    /// Current volume level (0-100) for Master channel
17    pub master_volume: Option<String>,
18
19    /// Current mute state for Master channel
20    pub master_mute: Option<String>,
21
22    /// Current volume level (0-100) for Left Front channel
23    pub lf_volume: Option<String>,
24
25    /// Current volume level (0-100) for Right Front channel
26    pub rf_volume: Option<String>,
27
28    /// Current mute state for Left Front channel
29    pub lf_mute: Option<String>,
30
31    /// Current mute state for Right Front channel
32    pub rf_mute: Option<String>,
33
34    /// Current bass level
35    pub bass: Option<String>,
36
37    /// Current treble level
38    pub treble: Option<String>,
39
40    /// Current loudness setting
41    pub loudness: Option<String>,
42
43    /// Balance setting (-100 to +100)
44    pub balance: Option<String>,
45
46    /// Additional channel configurations (can be extended)
47    pub other_channels: HashMap<String, String>,
48}
49
50/// Poll a speaker for complete RenderingControl state.
51///
52/// Calls GetVolume (required), GetMute, GetBass, GetTreble, GetLoudness
53/// (optional — fall back to None on failure).
54pub fn poll(client: &SonosClient, ip: &str) -> crate::Result<RenderingControlState> {
55    let volume = client.execute_enhanced(
56        ip,
57        super::get_volume_operation("Master".to_string())
58            .build()
59            .map_err(|e| crate::ApiError::ParseError(e.to_string()))?,
60    )?;
61
62    let mute = super::get_mute_operation("Master".to_string())
63        .build()
64        .ok()
65        .and_then(|op| client.execute_enhanced(ip, op).ok());
66    let bass = super::get_bass_operation()
67        .build()
68        .ok()
69        .and_then(|op| client.execute_enhanced(ip, op).ok());
70    let treble = super::get_treble_operation()
71        .build()
72        .ok()
73        .and_then(|op| client.execute_enhanced(ip, op).ok());
74    let loudness = super::get_loudness_operation("Master".to_string())
75        .build()
76        .ok()
77        .and_then(|op| client.execute_enhanced(ip, op).ok());
78
79    Ok(RenderingControlState {
80        master_volume: Some(volume.current_volume.to_string()),
81        master_mute: mute.map(|m| if m.current_mute { "1" } else { "0" }.to_string()),
82        bass: bass.map(|b| b.current_bass.to_string()),
83        treble: treble.map(|t| t.current_treble.to_string()),
84        loudness: loudness.map(|l| if l.current_loudness { "1" } else { "0" }.to_string()),
85        lf_volume: None,
86        rf_volume: None,
87        lf_mute: None,
88        rf_mute: None,
89        balance: None,
90        other_channels: HashMap::new(),
91    })
92}