rytilahti / python-songpal

Python library for interfacing with Sony's Songpal devices
GNU General Public License v3.0
66 stars 24 forks source link

[Feature/Discussion] Grouping / Ungrouping Devices #12

Closed lukx closed 5 years ago

lukx commented 6 years ago

Hey, as I would love to be able to group/ungroup my Sony Speakers using Home Assistant, I have done some investigation how the protocol works there. The Audio Control API doesn't support grouping/ungrouping (yet?), but instead it's a SOAP Request.

I can not judge whether or not @rytilahti would like to implement support for grouping or ungrouping, but here's what I know.

Setup

I have two speakers, "Küche" and "Wohnzimmer (WZ)". Küche runs at IP 192.168.178.77.

Getting Group Status

Request:

curl -H 'SOAPACTION: "urn:schemas-sony-com:service:Group:1#X_GetState"' -H 'Content-Type: text/xml; charset="utf-8"' -H 'User-Agent: UPnP/1.0 DLNADOC/1.50' -H 'X-AV-Client-Info: av=5.0; cn="Sony Corporation"; mn="Music Center"; mv="5.6.0-20180205a"' -H 'X-AV-Physical-Unit-Info: pa="Music Center"' -H 'Host: 192.168.178.77:54380' --data-binary "<?xml version='1.0' encoding='UTF-8' standalone='yes' ?><s:Envelope s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\" xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\"><s:Body><u:X_GetState xmlns:u=\"urn:schemas-sony-com:service:Group:1\" /></s:Body></s:Envelope>" --compressed 'http://192.168.178.77:54380/upnp/control/Group'

Response when not grouped

 <?xml version="1.0"?>
    <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"
            s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
        <s:Body>
            <u:X_GetStateResponse xmlns:u="urn:schemas-sony-com:service:Group:1">
                <GroupState>IDLE</GroupState>
                <GroupMode>IDLE</GroupMode>
                <GroupName></GroupName>
                <GroupSong>PUBLIC</GroupSong>
                <SessionID>0</SessionID>
                <NumberOfSlaves>0</NumberOfSlaves>
                <SlaveList></SlaveList>
                <MasterUUID></MasterUUID>
                <MasterSessionID>0</MasterSessionID>
                <PlayingState>STOPPED</PlayingState>
                <PowerState>OFF</PowerState>
                <Discoverable>YES</Discoverable>
                <WiredState>DOWN</WiredState>
                <WiredLinkSpeed>0</WiredLinkSpeed>
                <WirelessState>UP</WirelessState>
                <WirelessLinkSpeed>65</WirelessLinkSpeed>
                <WirelessType>802.11bgn</WirelessType>
                <RSSIValue>-65</RSSIValue>
                <SlaveNetworkState></SlaveNetworkState>
            </u:X_GetStateResponse>
        </s:Body>
    </s:Envelope>

Response when grouped with the other Speaker

<?xml version="1.0"?>
    <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"
                s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
        <s:Body>
            <u:X_GetStateResponse xmlns:u="urn:schemas-sony-com:service:Group:1">
                <GroupState>SINGING</GroupState>
                <GroupMode>GROUP</GroupMode>
                <GroupName>WZ und Kueche</GroupName>
                <GroupSong>PUBLIC</GroupSong>
                <SessionID>792069</SessionID>
                <NumberOfSlaves>1</NumberOfSlaves>
                <SlaveList>uuid:00000000-0000-1010-8000-8c579b4d78da</SlaveList>
                <MasterUUID></MasterUUID>
                <MasterSessionID>0</MasterSessionID>
                <PlayingState>PLAYING</PlayingState>
                <PowerState>ON</PowerState>
                <Discoverable>YES</Discoverable>
                <WiredState>DOWN</WiredState>
                <WiredLinkSpeed>0</WiredLinkSpeed>
                <WirelessState>UP</WirelessState>
                <WirelessLinkSpeed>65</WirelessLinkSpeed>
                <WirelessType>802.11bgn</WirelessType>
                <RSSIValue>-65</RSSIValue>
                <SlaveNetworkState></SlaveNetworkState>
            </u:X_GetStateResponse>
        </s:Body>
    </s:Envelope>

Grouping two speakers

By running the following request to the master Speaker (Küche), telling it to enslave Wohnzimmer.

curl -H 'SOAPACTION: "urn:schemas-sony-com:service:Group:1#X_Start"' -H 'Content-Type: text/xml; charset="utf-8"' -H 'User-Agent: UPnP/1.0 DLNADOC/1.50' -H 'X-AV-Client-Info: av=5.0; cn="Sony Corporation"; mn="Music Center"; mv="5.6.0-20180205a"' -H 'X-AV-Physical-Unit-Info: pa="Music Center"' -H 'Host: 192.168.178.77:54380' --data-binary "<?xml version='1.0' encoding='UTF-8' standalone='yes' ?><s:Envelope s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\" xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\"><s:Body><u:X_Start xmlns:u=\"urn:schemas-sony-com:service:Group:1\"><GroupMode>GROUP</GroupMode><GroupName>WZ und Kueche</GroupName><SlaveList>uuid:00000000-0000-1010-8000-8c579b4d78da</SlaveList></u:X_Start></s:Body></s:Envelope>" --compressed 'http://192.168.178.77:54380/upnp/control/Group'

Response:

<?xml version="1.0"?>
    <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"
                s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
        <s:Body>
            <u:X_StartResponse xmlns:u="urn:schemas-sony-com:service:Group:1">
                <MasterSessionID>792069</MasterSessionID>
            </u:X_StartResponse>
        </s:Body>
    </s:Envelope>

Ungrouping

curl -H 'SOAPACTION: "urn:schemas-sony-com:service:Group:1#X_Abort"' -H 'Content-Type: text/xml; charset="utf-8"' -H 'User-Agent: UPnP/1.0 DLNADOC/1.50' -H 'X-AV-Client-Info: av=5.0; cn="Sony Corporation"; mn="Music Center"; mv="5.6.0-20180205a"' -H 'X-AV-Physical-Unit-Info: pa="Music Center"' -H 'Host: 192.168.178.77:54380' --data-binary "<?xml version='1.0' encoding='UTF-8' standalone='yes' ?><s:Envelope s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\" xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\"><s:Body><u:X_Abort xmlns:u=\"urn:schemas-sony-com:service:Group:1\"><MasterSessionID>792066</MasterSessionID></u:X_Abort></s:Body></s:Envelope>" --compressed 'http://192.168.178.77:54380/upnp/control/Group'

(i forgot to capture the response for this one)

rytilahti commented 6 years ago

Hi!

I would gladly accept a PR to add such functionality, the library used for discovery (https://github.com/flyte/upnpclient) provides a nice interface for SSDP calls, so considering you have already figured out the parameters it should be fairly easy to implement. There are also some features which use those (the library for sony bravia series seems to use the same protocol as this lib, a fact I noticed too late: https://github.com/aparraga/braviarc) and which could be interesting to add at some point, too.

One thing to consider is that finding out the SSDP endpoint requires either a parameter or a separate discovery round (which is currently avoided by having --endpoint as a parameter), so it should be done only on demand. It probably also makes sense to have it in its own class (the device.py is already overblown and will have to be refactored at some point).

rytilahti commented 5 years ago

@lukx I just added some preliminary support to this (songpal group command), please let me know if it's working for you! Unfortunately I don't know the format for adding multiple devices into a group, so more input here would be very welcome.

Same applies also for those other commands (Entry, Leave) which are probably used to add and remove single devices from an existing group.

flyingclimber commented 5 years ago

Thank you. This is very exciting. I have 2x SRS-ZR5's and will try to test in the next week(s)

rytilahti commented 5 years ago

I just merged this in, please open new issues if you encounter any troubles :-)