sonos/
zone.rs

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    // There's a <VanishedDevices> element but I don't
28    // know what it contains
29}
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
46/// Helper for DRY; Satellite and ZoneGroupMember are almost
47/// identical structs but have to be separate in order for
48/// instant_xml to generate appropriate serde logic
49macro_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    /// URL of the device_description.xml
58    #[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}