go-chromecast icon indicating copy to clipboard operation
go-chromecast copied to clipboard

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

Open LukeDefeo opened this issue 5 years ago • 4 comments

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.

LukeDefeo avatar Dec 06 '20 15:12 LukeDefeo

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

  • https://developers.google.com/cast/docs/reference/chrome
  • https://developers.google.com/cast/docs/reference/chrome/chrome.cast
  • https://blog.oakbits.com/google-cast-protocol-discovery-and-connection.html
  • https://blog.oakbits.com/google-cast-protocol-receiver-authentication.html
  • https://chromium.googlesource.com/chromium/src/+/66.0.3359.158/components/cast_channel/proto/cast_channel.proto

vishen avatar Dec 06 '20 23:12 vishen

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 avatar Dec 17 '20 12:12 LukeDefeo

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 avatar Dec 17 '20 16:12 LukeDefeo

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 ?

LukeDefeo avatar Dec 17 '20 17:12 LukeDefeo