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}