Tuesday, May 28, 2019

Setting up a Google Chromecast Without the Home App

Let's be clear on one thing: I am not a developer.

 I am dangerous with bash, PERL, and, increasingly, Python. Given the state of the world, gaining skill in writing code is high on my priority list. But all that self-loathing disclaimer and wishful thinking aside...

 I've been doing a pile of work lately with Chromecasts. Ya know, those cute little dongles you connect to you TV and stream stuff to. Yeah, those. Well, as it turns out there's an undocumented API that you can use to do all kinds of things on them. Most of these undocumented APIs are documented in various places around the Web. I used these, in conjunction with a little of my own packet sniffing to figure out how to setup a default Chromecast without having to use the Google Home app. I took this derivative work, and piled it all into a bash script that seems to do the job (read: there's MASSIVE room for improvement, but hey running code rules the world, right?).

 As this is an undocumented API this is totally use at your own risk. I am not liable for any unexpected consequences, or if the API endpoints happen to change at any moment. This worked today, but I make no promises about tomorrow.

**Update**

This information is now outdated. Google has changed the way you access the API on the Chromecast rendering this method obsolete. I'll leave this up for posterity, but nothing here will be useful to you anymore. More info can be found here on the changes https://github.com/rithvikvibhu/GHLocalApi/issues/39

**End Update**

Big thanks goes out to whomever created, and maintains, this documentation:

https://rithvikvibhu.github.io/GHLocalApi/

99% of the API calls used came from that documentation. However, one crucial API call that's not documented there (or anywhere on the Internet that I could find) that's mandatory in setting up an Chromecast I found via a packet capture of the Google Home app provisioning a Chromecast. After you set the SSID and associated parameters you need to tell the Chromecast to save the settings. If you don't do this it will eventually time out and revert to broadcasting its own SSID and waiting to be configured. An example of this endpoint is:

curl -H "Content-Type: application/json" -d '{\"keep_hotspot_until_connected\": true}' http://192.168.255.249:8008/setup/save_wifi

One of these days I'll learn to use Github... But in the meantime this code is released under the "Do whatever the hell you want with it" license. Enjoy!

#!/bin/bash

#############
# Variables #
#############

CC_OPTION=1
WLAN="wlan0"

#############
# Functions #
#############

CC_CONNECT() {

  # Function to connect the wlan adapter to the selected SSID
  # Exits loop when selected CC SSID is detected as the ESSID
  # for the adapter.
  # Needs a better check to verify that we're connected...

  while [ -z $(iwconfig $WLAN | grep -o ${CC_LIST[$CC_CHOICE]}) ]
  do
    ifconfig $WLAN down
    ifconfig $WLAN up
    iwconfig $WLAN essid ${CC_LIST[$CC_CHOICE]}
    sleep 2
  done
}

CC_IP_DETECT() {

  # Function to verify that an IP is assigned to the wlan adapter
  # CC's always seem to hand out an address in 192.168.255.248/29

  while [ -z $(ip addr show dev wlan0 | grep -oE 192.168.255.[0-9]+ | head -n1) ]
  do
    echo -n "."
    sleep 2
  done
}

########
# Main #
########

echo "Searching for Chromecasts... "

# Create a list of all visible CC's on Wifi
# A CC that's not connected will advertise its own SSID
# with a suffix of ".v"

CC_LIST=($(iwlist wlan0 scan | grep -o Chromecast[0-9]*.v))

# It's possible a non-default CC will advertise an SSID of
# "name".v instead of the default. This could find those too...
# CC_LIST=($(iwlist wlan0 scan | grep -o \.v))

if [ -z "$CC_LIST" ]
then
  echo "No Chromecasts found, exiting"
  exit
else
  echo "Chromecasts Found!"
fi

# Display a menu for the user to select a CC to configure

for CC in "${CC_LIST[@]}"
do
  echo "$CC_OPTION ) $CC"
  ((CC_OPTION++))
done

echo "Enter the number of the Chromecast to configure "
read CC_CHOICE
((CC_CHOICE--))

# Instead of asking the user for a name this script could lookup
# the MAC of the CC (after it connects to it) in a file and pull
# the name from there.

echo "Enter the name to assign to the Chromecast "
read CC_NAME

echo "Configuring ${CC_LIST[$CC_CHOICE]} with name \"$CC_NAME\""
sleep 1
echo "Connecting to ${CC_LIST[$CC_CHOICE]}..."

CC_CONNECT

echo "Connected!"
echo -n "Aquiring IP Address."

CC_IP_DETECT

echo "Verifying connectivity to ${CC_LIST[$CC_CHOICE]}"

# Should function this...
# A quick check to verify the CC is reachable

PING=1
while [ $PING -eq 1 ]
do
  ping -nqc 1 192.168.255.249 > /dev/null
  if [ $? == 0 ]
  then
    PING=0
  fi
done

echo "${CC_LIST[$CC_CHOICE]} is reachable!"
echo -n "Configuring ${CC_LIST[$CC_CHOICE]} as $CC_NAME.."

# Loop to set the CC name. Exits loop when $CC_NAME appears in settings output from CC

while [ -z $(curl http://192.168.255.249:8008/setup/eureka_info?options=detail 2>&1 | grep -oE '\"name\":"'$CC_NAME'"') ]
do
  echo -n "."
  curl -H "Content-Type: application/json" -d '{"name": "'"$CC_NAME"'"}' http://192.168.255.249:8008/setup/set_eureka_info
  echo -n "."
  sleep 2
done

echo "Done!"
echo -n "Configuring WiFi network.."

# Loop to set the wifi parameters. Exits when the CASTING SSID appears in settings output from CC

while [ -z $(curl http://192.168.255.249:8008/setup/eureka_info?options=detail 2>&1 | grep -oE '\"ssid\":\"CASTING\"') ]
do
  echo -n "."
  curl -m 5 -H "Content-Type: application/json" -d '{"ssid": "CASTING", "wpa_auth": 1,"wpa_cipher": 1,"wpa_id": 0}' http://192.168.255.249:8008/setup/connect_wifi
  echo -n "."
  sleep 5

  # Usually after setting the SSID the CC resets it's wifi and the connection from the provisioner is lost
  # This calls the functions to reconnect and verify we have an IP again after this drop

  CC_CONNECT
  CC_IP_DETECT
done

echo "Done!"
echo -n "Waiting for Chromecast to detect Internet.."

# Loop to monitor the settings on the CC. When setup_state changes to "61" the CC is on the new SSID
# and has verified Internet access (it's at this point the Google Home app saves settings)

while [ -z $(curl http://192.168.255.249:8008/setup/eureka_info?options=detail 2>&1 | grep -oE '\"setup_state\":61') ]
do
  echo -n "."
  sleep 2
done
echo "Internet Detected!"

echo "Saving config to Chromecast"
curl -H "Content-Type: application/json" -d '{\"keep_hotspot_until_connected\": true}' http://192.168.255.249:8008/setup/save_wifi > /dev/null 2>&1
echo "Configuration complete!"



No comments:

Post a Comment