Skip to main content

sonos_api/services/
events.rs

1//! UPnP event subscription operations
2//!
3//! This module provides operations for managing UPnP event subscriptions
4//! across all Sonos services. These operations handle the HTTP-based
5//! subscription protocol rather than SOAP.
6//!
7//! Note: This module is being deprecated in favor of the new event framework
8//! in `crate::events`. Service-specific event handling is now done in
9//! individual service modules.
10
11use crate::{Result, Service};
12use serde::{Deserialize, Serialize};
13
14/// Subscribe operation for UPnP event subscriptions
15///
16/// This operation handles creating new UPnP event subscriptions for any service.
17/// Unlike regular SOAP operations, this uses HTTP SUBSCRIBE method instead of POST.
18pub struct SubscribeOperation;
19
20/// Request for Subscribe operation
21#[derive(Debug, Clone, Serialize)]
22pub struct SubscribeRequest {
23    /// The callback URL where events should be sent
24    pub callback_url: String,
25    /// Requested subscription timeout in seconds
26    pub timeout_seconds: u32,
27}
28
29/// Response for Subscribe operation
30#[derive(Debug, Clone, Deserialize)]
31pub struct SubscribeResponse {
32    /// Subscription ID returned by the device
33    pub sid: String,
34    /// Actual timeout granted by the device (in seconds)
35    pub timeout_seconds: u32,
36}
37
38impl SubscribeOperation {
39    /// Execute a subscription request for a specific service
40    ///
41    /// This method uses the soap-client's subscribe functionality to create
42    /// a UPnP event subscription for the specified service.
43    ///
44    /// # Arguments
45    /// * `soap_client` - The SOAP client to use for the request
46    /// * `ip` - Device IP address
47    /// * `service` - The service to subscribe to
48    /// * `request` - The subscription request parameters
49    ///
50    /// # Returns
51    /// The subscription response containing SID and timeout
52    pub fn execute(
53        soap_client: &soap_client::SoapClient,
54        ip: &str,
55        service: Service,
56        request: &SubscribeRequest,
57    ) -> Result<SubscribeResponse> {
58        let service_info = service.info();
59
60        let subscription_response = soap_client.subscribe(
61            ip,
62            1400, // Standard Sonos port
63            service_info.event_endpoint,
64            &request.callback_url,
65            request.timeout_seconds,
66        )?;
67
68        Ok(SubscribeResponse {
69            sid: subscription_response.sid,
70            timeout_seconds: subscription_response.timeout_seconds,
71        })
72    }
73}
74
75/// Unsubscribe operation for UPnP event subscriptions
76///
77/// This operation handles canceling existing UPnP event subscriptions for any service.
78/// Unlike regular SOAP operations, this uses HTTP UNSUBSCRIBE method instead of POST.
79pub struct UnsubscribeOperation;
80
81/// Request for Unsubscribe operation
82#[derive(Debug, Clone, Serialize)]
83pub struct UnsubscribeRequest {
84    /// The subscription ID to cancel
85    pub sid: String,
86}
87
88/// Response for Unsubscribe operation (empty - success is indicated by no error)
89#[derive(Debug, Clone)]
90pub struct UnsubscribeResponse;
91
92impl UnsubscribeOperation {
93    /// Execute an unsubscribe request for a specific service
94    ///
95    /// This method uses the soap-client's unsubscribe functionality to cancel
96    /// an existing UPnP event subscription for the specified service.
97    ///
98    /// # Arguments
99    /// * `soap_client` - The SOAP client to use for the request
100    /// * `ip` - Device IP address
101    /// * `service` - The service to unsubscribe from
102    /// * `request` - The unsubscribe request parameters
103    ///
104    /// # Returns
105    /// An empty response on success, or an error if the operation failed
106    pub fn execute(
107        soap_client: &soap_client::SoapClient,
108        ip: &str,
109        service: Service,
110        request: &UnsubscribeRequest,
111    ) -> Result<UnsubscribeResponse> {
112        let service_info = service.info();
113
114        soap_client.unsubscribe(
115            ip,
116            1400, // Standard Sonos port
117            service_info.event_endpoint,
118            &request.sid,
119        )?;
120
121        Ok(UnsubscribeResponse)
122    }
123}
124
125/// Renew operation for UPnP event subscriptions
126///
127/// This operation handles renewing existing UPnP event subscriptions for any service.
128/// Unlike regular SOAP operations, this uses HTTP SUBSCRIBE method with SID header.
129pub struct RenewOperation;
130
131/// Request for Renew operation
132#[derive(Debug, Clone, Serialize)]
133pub struct RenewRequest {
134    /// The subscription ID to renew
135    pub sid: String,
136    /// Requested renewal timeout in seconds
137    pub timeout_seconds: u32,
138}
139
140/// Response for Renew operation
141#[derive(Debug, Clone, Deserialize)]
142pub struct RenewResponse {
143    /// The actual timeout granted by the device (in seconds)
144    pub timeout_seconds: u32,
145}
146
147impl RenewOperation {
148    /// Execute a subscription renewal request for a specific service
149    ///
150    /// This method uses the soap-client's renew_subscription functionality to
151    /// extend an existing UPnP event subscription for the specified service.
152    ///
153    /// # Arguments
154    /// * `soap_client` - The SOAP client to use for the request
155    /// * `ip` - Device IP address
156    /// * `service` - The service to renew subscription for
157    /// * `request` - The renewal request parameters
158    ///
159    /// # Returns
160    /// The renewal response containing the actual timeout granted
161    pub fn execute(
162        soap_client: &soap_client::SoapClient,
163        ip: &str,
164        service: Service,
165        request: &RenewRequest,
166    ) -> Result<RenewResponse> {
167        let service_info = service.info();
168
169        let actual_timeout_seconds = soap_client.renew_subscription(
170            ip,
171            1400, // Standard Sonos port
172            service_info.event_endpoint,
173            &request.sid,
174            request.timeout_seconds,
175        )?;
176
177        Ok(RenewResponse {
178            timeout_seconds: actual_timeout_seconds,
179        })
180    }
181}
182
183#[cfg(test)]
184mod tests {
185    use super::*;
186
187    #[test]
188    fn test_subscribe_request_creation() {
189        let request = SubscribeRequest {
190            callback_url: "http://192.168.1.50:8080/callback".to_string(),
191            timeout_seconds: 1800,
192        };
193
194        assert_eq!(request.callback_url, "http://192.168.1.50:8080/callback");
195        assert_eq!(request.timeout_seconds, 1800);
196    }
197
198    #[test]
199    fn test_subscribe_response_creation() {
200        let response = SubscribeResponse {
201            sid: "uuid:12345678-1234-1234-1234-123456789012".to_string(),
202            timeout_seconds: 1800,
203        };
204
205        assert_eq!(response.sid, "uuid:12345678-1234-1234-1234-123456789012");
206        assert_eq!(response.timeout_seconds, 1800);
207    }
208
209    #[test]
210    fn test_unsubscribe_request_creation() {
211        let request = UnsubscribeRequest {
212            sid: "uuid:12345678-1234-1234-1234-123456789012".to_string(),
213        };
214
215        assert_eq!(request.sid, "uuid:12345678-1234-1234-1234-123456789012");
216    }
217
218    #[test]
219    fn test_unsubscribe_response_creation() {
220        let _response = UnsubscribeResponse;
221        // Just verify it can be created
222    }
223
224    #[test]
225    fn test_renew_request_creation() {
226        let request = RenewRequest {
227            sid: "uuid:12345678-1234-1234-1234-123456789012".to_string(),
228            timeout_seconds: 1800,
229        };
230
231        assert_eq!(request.sid, "uuid:12345678-1234-1234-1234-123456789012");
232        assert_eq!(request.timeout_seconds, 1800);
233    }
234
235    #[test]
236    fn test_renew_response_creation() {
237        let response = RenewResponse {
238            timeout_seconds: 1800,
239        };
240
241        assert_eq!(response.timeout_seconds, 1800);
242    }
243}