vishen / go-chromecast

cli for Google Chromecast, Home devices and Cast Groups
Apache License 2.0
833 stars 80 forks source link

Is it possible to mimic the ad hoc media group function from the Google home android app #83

Open LukeDefeo opened 3 years ago

LukeDefeo commented 3 years ago

If you go into the google home app then tap media at the top you get something like this:

From here I can add and remove any of my cast speakers to the current playing group by using the tick buttons. Its completely ad hoc and i can add remove speakers that aren't in a pre-baked speaker group.

What I'd like to is write an automation so that when i enter a room it queries all the speakers in the house and if there is some media playing on a different speaker it will add the current rooms speaker to the 'current playing group' (As if i ticked it on the screen shot) Similarly Id like to remove the speaker from the 'current playback group' if i leave the room

Can go-chomecast achieve this? It looks like no is the answer... I tried running a packet sniffer on my android phone and listening to the traffic going to the google home speakers but i had a hard time decoding the protobuf message in wireshark. Any help with the reverse engineering effort would be appreciated.

vishen commented 3 years ago

That would be very awesome if possible to achieve this. go-chromecast can't add to a group at the moment, but it can query all the devices on the network to see what is playing. Can you share the protobuf message from Wireshark and I'll take a look?

You can try running go-chromecast watch on the network to see if there is anything useful being broadcasted when a device joins a group. If you could share those logs as well that would be useful.

edit: Dumping some maybe useful links

LukeDefeo commented 3 years ago

Thanks @vishen

I ran watch on a my kitchen speaker and added playback to it from the app. Music was already playing on living room speaker.

This is what i got.

Idle, volume=0.40 muted=false
CHROMECAST BROADCAST MESSAGE: type=RECEIVER_STATUS proto=CASTV2_1_0 (namespace=urn:x-cast:com.google.cast.receiver) receiver-0 -> * | 
{
    "requestId": 0,
    "status": {
        "applications": [
            {
                "appId": "705D30C6",
                "appType": "WEB",
                "displayName": "Spotify",
                "iconUrl": "",
                "isIdleScreen": false,
                "launchedFromCloud": false,
                "namespaces": [
                    {
                        "name": "urn:x-cast:com.google.cast.media"
                    }
                ],
                "sessionId": "4f24660e-5271-47e0-9bbb-488f23f96fa7",
                "statusText": "Spotify",
                "transportId": "4f24660e-5271-47e0-9bbb-488f23f96fa7",
                "universalAppId": "705D30C6"
            }
        ],
        "userEq": {
            "high_shelf": {
                "frequency": 4500.0,
                "gain_db": 0.0,
                "quality": 0.707
            },
            "low_shelf": {
                "frequency": 150.0,
                "gain_db": 2,
                "quality": 0.707
            },
            "max_peaking_eqs": 0,
            "peaking_eqs": []
        },
        "volume": {
            "controlType": "master",
            "level": 0.4000000059604645,
            "muted": false,
            "stepInterval": 0.019999999552965164
        }
    },
    "type": "RECEIVER_STATUS"
}
CHROMECAST BROADCAST MESSAGE: type=RECEIVER_STATUS proto=CASTV2_1_0 (namespace=urn:x-cast:com.google.cast.receiver) receiver-0 -> * | 
{
    "requestId": 0,
    "status": {
        "applications": [
            {
                "appId": "705D30C6",
                "appType": "WEB",
                "displayName": "Spotify",
                "iconUrl": "",
                "isIdleScreen": false,
                "launchedFromCloud": false,
                "namespaces": [
                    {
                        "name": "urn:x-cast:com.google.cast.media"
                    }
                ],
                "sessionId": "4f24660e-5271-47e0-9bbb-488f23f96fa7",
                "statusText": "Casting: Ticking Hands - Mix Cut",
                "transportId": "4f24660e-5271-47e0-9bbb-488f23f96fa7",
                "universalAppId": "705D30C6"
            }
        ],
        "userEq": {
            "high_shelf": {
                "frequency": 4500.0,
                "gain_db": 0.0,
                "quality": 0.707
            },
            "low_shelf": {
                "frequency": 150.0,
                "gain_db": 2,
                "quality": 0.707
            },
            "max_peaking_eqs": 0,
            "peaking_eqs": []
        },
        "volume": {
            "controlType": "master",
            "level": 0.4000000059604645,
            "muted": false,
            "stepInterval": 0.019999999552965164
        }
    },
    "type": "RECEIVER_STATUS"
}
CHROMECAST BROADCAST MESSAGE: type=MULTIZONE_STATUS proto=CASTV2_1_0 (namespace=urn:x-cast:com.google.cast.multizone) receiver-0 -> * | 
{
    "requestId": 0,
    "status": {
        "devices": [
            {
                "capabilities": 196612,
                "deviceId": "50dcd7aa-53df-1ad6-305e-86681dfcfd77",
                "name": "Kitchen speaker",
                "volume": {
                    "level": 0.4000000059604645,
                    "muted": false
                }
            }
        ],
        "isMultichannel": false
    },
    "type": "MULTIZONE_STATUS"
}
CHROMECAST BROADCAST MESSAGE: type=MULTIZONE_STATUS proto=CASTV2_1_0 (namespace=urn:x-cast:com.google.cast.multizone) receiver-0 -> * | 
{
    "requestId": 0,
    "status": {
        "devices": [
            {
                "capabilities": 196612,
                "deviceId": "50dcd7aa-53df-1ad6-305e-86681dfcfd77",
                "name": "Kitchen speaker",
                "volume": {
                    "level": 0.4000000059604645,
                    "muted": false
                }
            }
        ],
        "isMultichannel": false
    },
    "type": "MULTIZONE_STATUS"
}
CHROMECAST BROADCAST MESSAGE: type=MULTIZONE_STATUS proto=CASTV2_1_0 (namespace=urn:x-cast:com.google.cast.multizone) receiver-0 -> * | 
{
    "requestId": 0,
    "status": {
        "devices": [
            {
                "capabilities": 196612,
                "deviceId": "50dcd7aa-53df-1ad6-305e-86681dfcfd77",
                "name": "Kitchen speaker",
                "volume": {
                    "level": 0.4000000059604645,
                    "muted": false
                }
            }
        ],
        "isMultichannel": false
    },
    "type": "MULTIZONE_STATUS"
}
Idle (Spotify), volume=0.40 muted=false
Idle (Spotify), volume=0.40 muted=false
CHROMECAST BROADCAST MESSAGE: type=MULTIZONE_STATUS proto=CASTV2_1_0 (namespace=urn:x-cast:com.google.cast.multizone) receiver-0 -> * | 
{
    "requestId": 0,
    "status": {
        "devices": [
            {
                "capabilities": 196612,
                "deviceId": "50dcd7aa-53df-1ad6-305e-86681dfcfd77",
                "name": "Kitchen speaker",
                "volume": {
                    "level": 0.4000000059604645,
                    "muted": false
                }
            }
        ],
        "isMultichannel": false
    },
    "type": "MULTIZONE_STATUS"
}
Idle (Spotify), volume=0.40 muted=false

next i removed playback and got this


Idle (Spotify), volume=0.40 muted=false
CHROMECAST BROADCAST MESSAGE: type=RECEIVER_STATUS proto=CASTV2_1_0 (namespace=urn:x-cast:com.google.cast.receiver) receiver-0 -> * | 
{
    "requestId": 0,
    "status": {
        "userEq": {
            "high_shelf": {
                "frequency": 4500.0,
                "gain_db": 0.0,
                "quality": 0.707
            },
            "low_shelf": {
                "frequency": 150.0,
                "gain_db": 2,
                "quality": 0.707
            },
            "max_peaking_eqs": 0,
            "peaking_eqs": []
        },
        "volume": {
            "controlType": "master",
            "level": 0.4000000059604645,
            "muted": false,
            "stepInterval": 0.019999999552965164
        }
    },
    "type": "RECEIVER_STATUS"
}
CHROMECAST BROADCAST MESSAGE: type=CLOSE proto=CASTV2_1_0 (namespace=urn:x-cast:com.google.cast.tp.connection) 4f24660e-5271-47e0-9bbb-488f23f96fa7 -> sender-0 | {
    "type": "CLOSE"
}
CHROMECAST BROADCAST MESSAGE: type=MULTIZONE_STATUS proto=CASTV2_1_0 (namespace=urn:x-cast:com.google.cast.multizone) receiver-0 -> * | 
{
    "requestId": 0,
    "status": {
        "devices": [
            {
                "capabilities": 196612,
                "deviceId": "50dcd7aa-53df-1ad6-305e-86681dfcfd77",
                "name": "Kitchen speaker",
                "volume": {
                    "level": 0.4000000059604645,
                    "muted": false
                }
            }
        ],
        "isMultichannel": false
    },
    "type": "MULTIZONE_STATUS"
}

Not sure if this is useful or not. The close message when i turned it off looks interesting. Are these the actual messages the device is sending and receiving to my phone (intercepted somehow) or are these just broadcasts the chromecast sends out so other apps can synchronise their state to what the device is doing?

LukeDefeo commented 3 years ago

As for the protobuf decode, It was just binary and the hex showed no readable characters, I tried reading it with the cli tool but it kept saying it was invalid.

Ill have another go at decoding it over the holidays but Im a bit hesitant to post it online incase theres any personal data or credentials in any of the messages. If you have any tips at how to decode protobuf from the wire that would be really helpful

LukeDefeo commented 3 years ago

I had another go and its failing still, ive tried all sorts. Would a random message in the stream be encrypted? is that why i cant pass it into protoc --decode_raw ?