1use crate::upnp::DecodeXml;
2use instant_xml::FromXml;
3
4#[derive(Debug, PartialEq, Clone)]
5pub struct ZoneGroupState {
6 pub groups: Vec<ZoneGroup>,
7}
8
9impl DecodeXml for ZoneGroupState {
10 fn decode_xml(xml: &str) -> crate::Result<Self> {
11 let mut parsed: ZoneGroupStateHelper = instant_xml::from_str(xml)?;
12
13 for group in &mut parsed.group_list.groups {
14 group.members.sort_by(|a, b| a.uuid.cmp(&b.uuid));
15 }
16
17 Ok(Self {
18 groups: parsed.group_list.groups,
19 })
20 }
21}
22
23#[derive(Debug, FromXml)]
24#[xml(rename = "ZoneGroupState")]
25struct ZoneGroupStateHelper {
26 group_list: ZoneGroups,
27 }
30
31#[derive(Debug, FromXml)]
32struct ZoneGroups {
33 pub groups: Vec<ZoneGroup>,
34}
35
36#[derive(Debug, FromXml, PartialEq, Eq, Clone)]
37pub struct ZoneGroup {
38 #[xml(rename = "Coordinator", attribute)]
39 pub coordinator: String,
40 #[xml(rename = "ID", attribute)]
41 pub id: String,
42
43 pub members: Vec<ZoneGroupMember>,
44}
45
46macro_rules! machine_info {
50 (pub struct $ty:ident { $($inner:tt)* }) => {
51#[derive(Debug, FromXml, PartialEq, Eq, Clone)]
52pub struct $ty {
53 $($inner)*
54
55 #[xml(rename = "UUID", attribute)]
56 pub uuid: String,
57 #[xml(rename = "Location", attribute)]
59 pub location: String,
60 #[xml(rename = "ZoneName", attribute)]
61 pub zone_name: String,
62 #[xml(rename = "Icon", attribute)]
63 pub icon: String,
64 #[xml(rename = "Configuration", attribute)]
65 pub configuration: String,
66 #[xml(rename = "SoftwareVersion", attribute)]
67 pub software_version: String,
68 #[xml(rename = "SWGen", attribute)]
69 pub sw_gen: String,
70 #[xml(rename = "MinCompatibleVersion", attribute)]
71 pub min_compatible_version: String,
72 #[xml(rename = "LegacyCompatibleVersion", attribute)]
73 pub legacy_compatible_version: String,
74 #[xml(rename = "BootSeq", attribute)]
75 pub boot_seq: String,
76 #[xml(rename = "TVConfigurationError", attribute)]
77 pub tv_configuration_error: String,
78 #[xml(rename = "HdmiCecAvailable", attribute)]
79 pub hdmi_cec_available: u8,
80 #[xml(rename = "WirelessMode", attribute)]
81 pub wireless_mode: u8,
82 #[xml(rename = "WirelessLeafOnly", attribute)]
83 pub wireless_leaf_only: u8,
84 #[xml(rename = "ChannelFreq", attribute)]
85 pub channel_freq: u32,
86 #[xml(rename = "BehindWifiExtender", attribute)]
87 pub behind_wifi_extender: u8,
88 #[xml(rename = "WifiEnabled", attribute)]
89 pub wifi_enabled: u8,
90 #[xml(rename = "EthLink", attribute)]
91 pub eth_link: u8,
92 #[xml(rename = "Orientation", attribute)]
93 pub orientation: u8,
94 #[xml(rename = "RoomCalibrationState", attribute)]
95 pub room_calibration_state: u32,
96 #[xml(rename = "SecureRegState", attribute)]
97 pub secure_reg_state: u32,
98 #[xml(rename = "VoiceConfigState", attribute)]
99 pub voice_config_state: u32,
100 #[xml(rename = "MicEnabled", attribute)]
101 pub mic_enabled: u8,
102 #[xml(rename = "AirPlayEnabled", attribute)]
103 pub airplay_enabled: u8,
104 #[xml(rename = "IdleState", attribute)]
105 pub idle_state: u8,
106 #[xml(rename = "MoreInfo", attribute)]
107 pub more_info: String,
108 #[xml(rename = "SSLPort", attribute)]
109 pub ssl_port: u16,
110 #[xml(rename = "HHSSLPort", attribute)]
111 pub hhssl_port: u16,
112}
113 };
114}
115
116machine_info! {
117 pub struct ZoneGroupMember {
118 pub satellites: Vec<Satellite>,
119 }
120}
121
122machine_info! {
123 pub struct Satellite {
124 }
125}
126
127#[cfg(test)]
128mod test {
129 use super::*;
130
131 #[test]
132 fn test_parse_group_state() {
133 let group_state = include_str!("../data/zone_group_state.xml");
134 let parsed = ZoneGroupState::decode_xml(&group_state).unwrap();
135 k9::snapshot!(
136 parsed,
137 r#"
138ZoneGroupState {
139 groups: [
140 ZoneGroup {
141 coordinator: "RINCON_XXX",
142 id: "RINCON_XXX:3435548679",
143 members: [
144 ZoneGroupMember {
145 satellites: [],
146 uuid: "RINCON_XXX",
147 location: "http://10.10.10.161:1400/xml/device_description.xml",
148 zone_name: "Primary Bath",
149 icon: "",
150 configuration: "1",
151 software_version: "78.1-52020",
152 sw_gen: "2",
153 min_compatible_version: "77.0-00000",
154 legacy_compatible_version: "58.0-00000",
155 boot_seq: "145",
156 tv_configuration_error: "0",
157 hdmi_cec_available: 0,
158 wireless_mode: 1,
159 wireless_leaf_only: 0,
160 channel_freq: 5220,
161 behind_wifi_extender: 0,
162 wifi_enabled: 1,
163 eth_link: 0,
164 orientation: 0,
165 room_calibration_state: 4,
166 secure_reg_state: 3,
167 voice_config_state: 0,
168 mic_enabled: 0,
169 airplay_enabled: 1,
170 idle_state: 1,
171 more_info: "RawBattPct:99,BattPct:100,BattChg:CHARGING,BattTmp:33",
172 ssl_port: 1443,
173 hhssl_port: 1843,
174 },
175 ],
176 },
177 ZoneGroup {
178 coordinator: "RINCON_XXX",
179 id: "RINCON_XXX:3326086195",
180 members: [
181 ZoneGroupMember {
182 satellites: [
183 Satellite {
184 uuid: "RINCON_XXX",
185 location: "http://10.10.10.131:1400/xml/device_description.xml",
186 zone_name: "Some Room",
187 icon: "",
188 configuration: "1",
189 software_version: "78.1-52020",
190 sw_gen: "2",
191 min_compatible_version: "77.0-00000",
192 legacy_compatible_version: "58.0-00000",
193 boot_seq: "237",
194 tv_configuration_error: "0",
195 hdmi_cec_available: 0,
196 wireless_mode: 0,
197 wireless_leaf_only: 0,
198 channel_freq: 2437,
199 behind_wifi_extender: 0,
200 wifi_enabled: 1,
201 eth_link: 0,
202 orientation: 0,
203 room_calibration_state: 5,
204 secure_reg_state: 3,
205 voice_config_state: 0,
206 mic_enabled: 0,
207 airplay_enabled: 0,
208 idle_state: 1,
209 more_info: "",
210 ssl_port: 1443,
211 hhssl_port: 1843,
212 },
213 Satellite {
214 uuid: "RINCON_XXX",
215 location: "http://10.10.10.226:1400/xml/device_description.xml",
216 zone_name: "Some Room",
217 icon: "",
218 configuration: "1",
219 software_version: "78.1-52020",
220 sw_gen: "2",
221 min_compatible_version: "77.0-00000",
222 legacy_compatible_version: "58.0-00000",
223 boot_seq: "274",
224 tv_configuration_error: "0",
225 hdmi_cec_available: 0,
226 wireless_mode: 0,
227 wireless_leaf_only: 0,
228 channel_freq: 2437,
229 behind_wifi_extender: 0,
230 wifi_enabled: 1,
231 eth_link: 0,
232 orientation: 0,
233 room_calibration_state: 5,
234 secure_reg_state: 3,
235 voice_config_state: 0,
236 mic_enabled: 0,
237 airplay_enabled: 0,
238 idle_state: 1,
239 more_info: "",
240 ssl_port: 1443,
241 hhssl_port: 1843,
242 },
243 ],
244 uuid: "RINCON_XXX",
245 location: "http://10.10.10.196:1400/xml/device_description.xml",
246 zone_name: "Some Room",
247 icon: "",
248 configuration: "1",
249 software_version: "78.1-52020",
250 sw_gen: "2",
251 min_compatible_version: "77.0-00000",
252 legacy_compatible_version: "58.0-00000",
253 boot_seq: "123",
254 tv_configuration_error: "0",
255 hdmi_cec_available: 1,
256 wireless_mode: 0,
257 wireless_leaf_only: 0,
258 channel_freq: 2437,
259 behind_wifi_extender: 0,
260 wifi_enabled: 1,
261 eth_link: 0,
262 orientation: 0,
263 room_calibration_state: 1,
264 secure_reg_state: 3,
265 voice_config_state: 0,
266 mic_enabled: 0,
267 airplay_enabled: 1,
268 idle_state: 1,
269 more_info: "",
270 ssl_port: 1443,
271 hhssl_port: 1843,
272 },
273 ],
274 },
275 ZoneGroup {
276 coordinator: "RINCON_XXX",
277 id: "RINCON_XXX:2302873263",
278 members: [
279 ZoneGroupMember {
280 satellites: [],
281 uuid: "RINCON_XXX",
282 location: "http://10.10.10.166:1400/xml/device_description.xml",
283 zone_name: "Study",
284 icon: "",
285 configuration: "1",
286 software_version: "78.1-52020",
287 sw_gen: "2",
288 min_compatible_version: "77.0-00000",
289 legacy_compatible_version: "58.0-00000",
290 boot_seq: "73",
291 tv_configuration_error: "0",
292 hdmi_cec_available: 0,
293 wireless_mode: 0,
294 wireless_leaf_only: 0,
295 channel_freq: 2437,
296 behind_wifi_extender: 0,
297 wifi_enabled: 1,
298 eth_link: 1,
299 orientation: 0,
300 room_calibration_state: 4,
301 secure_reg_state: 3,
302 voice_config_state: 0,
303 mic_enabled: 0,
304 airplay_enabled: 1,
305 idle_state: 0,
306 more_info: "TargetRoomName:Study",
307 ssl_port: 1443,
308 hhssl_port: 1843,
309 },
310 ],
311 },
312 ZoneGroup {
313 coordinator: "RINCON_XXX",
314 id: "RINCON_XXX:4111376911",
315 members: [
316 ZoneGroupMember {
317 satellites: [],
318 uuid: "RINCON_XXX",
319 location: "http://10.10.10.138:1400/xml/device_description.xml",
320 zone_name: "Beam",
321 icon: "x-rincon-roomicon:masterbedroom",
322 configuration: "1",
323 software_version: "78.1-52020",
324 sw_gen: "2",
325 min_compatible_version: "77.0-00000",
326 legacy_compatible_version: "58.0-00000",
327 boot_seq: "158",
328 tv_configuration_error: "0",
329 hdmi_cec_available: 1,
330 wireless_mode: 0,
331 wireless_leaf_only: 0,
332 channel_freq: 2437,
333 behind_wifi_extender: 0,
334 wifi_enabled: 1,
335 eth_link: 0,
336 orientation: 0,
337 room_calibration_state: 3,
338 secure_reg_state: 3,
339 voice_config_state: 0,
340 mic_enabled: 0,
341 airplay_enabled: 1,
342 idle_state: 1,
343 more_info: "",
344 ssl_port: 1443,
345 hhssl_port: 1843,
346 },
347 ],
348 },
349 ZoneGroup {
350 coordinator: "RINCON_XXX",
351 id: "RINCON_XXX:2134456247",
352 members: [
353 ZoneGroupMember {
354 satellites: [],
355 uuid: "RINCON_XXX",
356 location: "http://10.10.10.165:1400/xml/device_description.xml",
357 zone_name: "Kitchen (Move)",
358 icon: "",
359 configuration: "1",
360 software_version: "78.1-52020",
361 sw_gen: "2",
362 min_compatible_version: "77.0-00000",
363 legacy_compatible_version: "58.0-00000",
364 boot_seq: "112",
365 tv_configuration_error: "0",
366 hdmi_cec_available: 0,
367 wireless_mode: 1,
368 wireless_leaf_only: 0,
369 channel_freq: 5785,
370 behind_wifi_extender: 0,
371 wifi_enabled: 1,
372 eth_link: 0,
373 orientation: 0,
374 room_calibration_state: 4,
375 secure_reg_state: 3,
376 voice_config_state: 0,
377 mic_enabled: 0,
378 airplay_enabled: 1,
379 idle_state: 1,
380 more_info: "RawBattPct:100,BattPct:100,BattChg:CHARGING,BattTmp:27",
381 ssl_port: 1443,
382 hhssl_port: 1843,
383 },
384 ],
385 },
386 ZoneGroup {
387 coordinator: "RINCON_XXX",
388 id: "RINCON_XXX:2884078592",
389 members: [
390 ZoneGroupMember {
391 satellites: [
392 Satellite {
393 uuid: "RINCON_XXX",
394 location: "http://10.10.10.190:1400/xml/device_description.xml",
395 zone_name: "Primary Bedroom",
396 icon: "x-rincon-roomicon:masterbedroom",
397 configuration: "1",
398 software_version: "78.1-52020",
399 sw_gen: "2",
400 min_compatible_version: "77.0-00000",
401 legacy_compatible_version: "58.0-00000",
402 boot_seq: "286",
403 tv_configuration_error: "0",
404 hdmi_cec_available: 0,
405 wireless_mode: 0,
406 wireless_leaf_only: 0,
407 channel_freq: 2437,
408 behind_wifi_extender: 0,
409 wifi_enabled: 1,
410 eth_link: 0,
411 orientation: 0,
412 room_calibration_state: 5,
413 secure_reg_state: 3,
414 voice_config_state: 0,
415 mic_enabled: 0,
416 airplay_enabled: 0,
417 idle_state: 1,
418 more_info: "",
419 ssl_port: 1443,
420 hhssl_port: 1843,
421 },
422 Satellite {
423 uuid: "RINCON_XXX",
424 location: "http://10.10.10.198:1400/xml/device_description.xml",
425 zone_name: "Primary Bedroom",
426 icon: "x-rincon-roomicon:masterbedroom",
427 configuration: "1",
428 software_version: "78.1-52020",
429 sw_gen: "2",
430 min_compatible_version: "77.0-00000",
431 legacy_compatible_version: "58.0-00000",
432 boot_seq: "278",
433 tv_configuration_error: "0",
434 hdmi_cec_available: 0,
435 wireless_mode: 0,
436 wireless_leaf_only: 0,
437 channel_freq: 2437,
438 behind_wifi_extender: 0,
439 wifi_enabled: 1,
440 eth_link: 0,
441 orientation: 0,
442 room_calibration_state: 5,
443 secure_reg_state: 3,
444 voice_config_state: 0,
445 mic_enabled: 0,
446 airplay_enabled: 0,
447 idle_state: 1,
448 more_info: "",
449 ssl_port: 1443,
450 hhssl_port: 1843,
451 },
452 Satellite {
453 uuid: "RINCON_XXX",
454 location: "http://10.10.10.116:1400/xml/device_description.xml",
455 zone_name: "Sub",
456 icon: "",
457 configuration: "1",
458 software_version: "78.1-52020",
459 sw_gen: "2",
460 min_compatible_version: "77.0-00000",
461 legacy_compatible_version: "58.0-00000",
462 boot_seq: "90",
463 tv_configuration_error: "0",
464 hdmi_cec_available: 0,
465 wireless_mode: 0,
466 wireless_leaf_only: 0,
467 channel_freq: 2437,
468 behind_wifi_extender: 0,
469 wifi_enabled: 1,
470 eth_link: 0,
471 orientation: 0,
472 room_calibration_state: 5,
473 secure_reg_state: 3,
474 voice_config_state: 0,
475 mic_enabled: 0,
476 airplay_enabled: 0,
477 idle_state: 1,
478 more_info: "",
479 ssl_port: 1443,
480 hhssl_port: 1843,
481 },
482 ],
483 uuid: "RINCON_XXX",
484 location: "http://10.10.10.231:1400/xml/device_description.xml",
485 zone_name: "Primary Bedroom",
486 icon: "",
487 configuration: "1",
488 software_version: "78.1-52020",
489 sw_gen: "2",
490 min_compatible_version: "77.0-00000",
491 legacy_compatible_version: "58.0-00000",
492 boot_seq: "91",
493 tv_configuration_error: "0",
494 hdmi_cec_available: 1,
495 wireless_mode: 0,
496 wireless_leaf_only: 0,
497 channel_freq: 2437,
498 behind_wifi_extender: 0,
499 wifi_enabled: 1,
500 eth_link: 0,
501 orientation: 0,
502 room_calibration_state: 1,
503 secure_reg_state: 3,
504 voice_config_state: 0,
505 mic_enabled: 0,
506 airplay_enabled: 1,
507 idle_state: 1,
508 more_info: "",
509 ssl_port: 1443,
510 hhssl_port: 1843,
511 },
512 ],
513 },
514 ZoneGroup {
515 coordinator: "RINCON_XXX",
516 id: "RINCON_XXX:1940091512",
517 members: [
518 ZoneGroupMember {
519 satellites: [],
520 uuid: "RINCON_XXX",
521 location: "http://10.10.10.157:1400/xml/device_description.xml",
522 zone_name: "Great Room",
523 icon: "",
524 configuration: "1",
525 software_version: "78.1-52020",
526 sw_gen: "2",
527 min_compatible_version: "77.0-00000",
528 legacy_compatible_version: "58.0-00000",
529 boot_seq: "89",
530 tv_configuration_error: "0",
531 hdmi_cec_available: 0,
532 wireless_mode: 0,
533 wireless_leaf_only: 0,
534 channel_freq: 2437,
535 behind_wifi_extender: 0,
536 wifi_enabled: 1,
537 eth_link: 1,
538 orientation: 0,
539 room_calibration_state: 4,
540 secure_reg_state: 3,
541 voice_config_state: 0,
542 mic_enabled: 0,
543 airplay_enabled: 1,
544 idle_state: 1,
545 more_info: "",
546 ssl_port: 1443,
547 hhssl_port: 1843,
548 },
549 ],
550 },
551 ZoneGroup {
552 coordinator: "RINCON_XXX",
553 id: "RINCON_XXX:2667033389",
554 members: [
555 ZoneGroupMember {
556 satellites: [],
557 uuid: "RINCON_XXX",
558 location: "http://10.10.10.120:1400/xml/device_description.xml",
559 zone_name: "Other Room",
560 icon: "x-rincon-roomicon:living",
561 configuration: "1",
562 software_version: "78.1-52020",
563 sw_gen: "2",
564 min_compatible_version: "77.0-00000",
565 legacy_compatible_version: "58.0-00000",
566 boot_seq: "320",
567 tv_configuration_error: "0",
568 hdmi_cec_available: 0,
569 wireless_mode: 0,
570 wireless_leaf_only: 0,
571 channel_freq: 2437,
572 behind_wifi_extender: 0,
573 wifi_enabled: 1,
574 eth_link: 0,
575 orientation: 0,
576 room_calibration_state: 5,
577 secure_reg_state: 3,
578 voice_config_state: 0,
579 mic_enabled: 0,
580 airplay_enabled: 0,
581 idle_state: 1,
582 more_info: "",
583 ssl_port: 1443,
584 hhssl_port: 1843,
585 },
586 ZoneGroupMember {
587 satellites: [],
588 uuid: "RINCON_XXX",
589 location: "http://10.10.10.158:1400/xml/device_description.xml",
590 zone_name: "Other Room",
591 icon: "x-rincon-roomicon:living",
592 configuration: "1",
593 software_version: "78.1-52020",
594 sw_gen: "2",
595 min_compatible_version: "77.0-00000",
596 legacy_compatible_version: "58.0-00000",
597 boot_seq: "273",
598 tv_configuration_error: "0",
599 hdmi_cec_available: 0,
600 wireless_mode: 0,
601 wireless_leaf_only: 0,
602 channel_freq: 2437,
603 behind_wifi_extender: 0,
604 wifi_enabled: 1,
605 eth_link: 0,
606 orientation: 4,
607 room_calibration_state: 3,
608 secure_reg_state: 3,
609 voice_config_state: 0,
610 mic_enabled: 0,
611 airplay_enabled: 1,
612 idle_state: 1,
613 more_info: "",
614 ssl_port: 1443,
615 hhssl_port: 1843,
616 },
617 ZoneGroupMember {
618 satellites: [],
619 uuid: "RINCON_XXX",
620 location: "http://10.10.10.217:1400/xml/device_description.xml",
621 zone_name: "Other Room",
622 icon: "x-rincon-roomicon:living",
623 configuration: "1",
624 software_version: "78.1-52020",
625 sw_gen: "2",
626 min_compatible_version: "77.0-00000",
627 legacy_compatible_version: "58.0-00000",
628 boot_seq: "253",
629 tv_configuration_error: "0",
630 hdmi_cec_available: 0,
631 wireless_mode: 0,
632 wireless_leaf_only: 0,
633 channel_freq: 2437,
634 behind_wifi_extender: 0,
635 wifi_enabled: 1,
636 eth_link: 0,
637 orientation: 3,
638 room_calibration_state: 5,
639 secure_reg_state: 3,
640 voice_config_state: 0,
641 mic_enabled: 0,
642 airplay_enabled: 0,
643 idle_state: 1,
644 more_info: "",
645 ssl_port: 1443,
646 hhssl_port: 1843,
647 },
648 ],
649 },
650 ZoneGroup {
651 coordinator: "RINCON_XXX",
652 id: "RINCON_XXX:97",
653 members: [
654 ZoneGroupMember {
655 satellites: [],
656 uuid: "RINCON_XXX",
657 location: "http://10.10.10.236:1400/xml/device_description.xml",
658 zone_name: "Kitchen",
659 icon: "x-rincon-roomicon:masterbedroom",
660 configuration: "1",
661 software_version: "78.1-52020",
662 sw_gen: "2",
663 min_compatible_version: "77.0-00000",
664 legacy_compatible_version: "58.0-00000",
665 boot_seq: "367",
666 tv_configuration_error: "0",
667 hdmi_cec_available: 0,
668 wireless_mode: 0,
669 wireless_leaf_only: 0,
670 channel_freq: 2437,
671 behind_wifi_extender: 0,
672 wifi_enabled: 1,
673 eth_link: 0,
674 orientation: 3,
675 room_calibration_state: 4,
676 secure_reg_state: 3,
677 voice_config_state: 0,
678 mic_enabled: 0,
679 airplay_enabled: 0,
680 idle_state: 1,
681 more_info: "",
682 ssl_port: 1443,
683 hhssl_port: 1843,
684 },
685 ],
686 },
687 ],
688}
689"#
690 );
691 }
692}