Skip to content

Instantly share code, notes, and snippets.

@interfect
Last active November 17, 2024 15:19
Show Gist options
  • Save interfect/5f68381d55658d334e2bc4619d796476 to your computer and use it in GitHub Desktop.
Save interfect/5f68381d55658d334e2bc4619d796476 to your computer and use it in GitHub Desktop.
Set up a Chromecast from a Linux PC, without an Android or iOS mobile device and without Google Home
#!/usr/bin/env bash
# castanet.sh: Script to connect a chromecast to a WiFi network.
#
# Allows you to put your Chromecast on WiFi and do Chromecast initial setup
# without using the Google Home app at all, just using a normal Linux computer.
#
# You do need your Chromecast to be on Ethernet, or (untested) to join its setup WiFi
# network with your PC, and you also need to find out its IP yourself with e.g.
# Wireshark.
set -e
if [[ -z "${CHROMECAST_IP}" || -z "${WIFI_SSID}" || -z "${WIFI_PASSWORD}" ]] ; then
echo 1>&2 "Usage: CHROMECAST_IP=\"XXX\" WIFI_SSID=\"XXX\" WIFI_PASSWORD=\"XXX\" ${0}"
exit 1
fi
if ! which curl >/dev/null 2>/dev/null ; then
echo 1>&2 "Install curl to use this script!"
exit 1
fi
if ! which jq >/dev/null 2>/dev/null ; then
echo 1>&2 "Install jq to use this script!"
exit 1
fi
if ! which nodejs >/dev/null 2>/dev/null ; then
echo 1>&2 "Install nodejs to use this script!"
exit 1
fi
# Set VERBOSITY=-vvv to see Curl traffic happening
if [[ -z "${VERBOSITY}" ]] ; then
VERBOSITY=-s
fi
echo "Connecting ${CHROMECAST_IP} to ${WIFI_SSID} with password ${WIFI_PASSWORD}"
# Get the device's public key
INFO_JSON="$(curl ${VERBOSITY} --insecure --tlsv1.2 --tls-max 1.2 https://${CHROMECAST_IP}:8443/setup/eureka_info)"
CHROMECAST_PUBKEY="$(echo "${INFO_JSON}" | jq -r '.public_key')"
# Scan for and find the network we want to get the encryption parameters
curl ${VERBOSITY} --insecure --tlsv1.2 --tls-max 1.2 -X POST https://${CHROMECAST_IP}:8443/setup/scan_wifi
sleep 20
WIFI_JSON="$(curl ${VERBOSITY} --insecure --tlsv1.2 --tls-max 1.2 https://${CHROMECAST_IP}:8443/setup/scan_results)"
WIFI_NETWORK_JSON="$(echo "${WIFI_JSON}" | jq ".[] | select(.ssid == \"${WIFI_SSID}\")")"
WIFI_AUTH_NUMBER="$(echo "${WIFI_NETWORK_JSON}" | jq -r '.wpa_auth')"
WIFI_CIPHER_NUMBER="$(echo "${WIFI_NETWORK_JSON}" | jq -r '.wpa_cipher')"
echo "${WIFI_NETWORK_JSON}"
# Encrypt the password to the device
# Encryption kernel by @thorleifjaocbsen
# See <https://github.com/rithvikvibhu/GHLocalApi/issues/68#issue-766300901>
ENCRYPTED_KEY="$(nodejs <<EOF
let crypto = require('crypto');
let cleartext = "${WIFI_PASSWORD}";
let publicKey = "${CHROMECAST_PUBKEY}";
publicKey = "-----BEGIN RSA PUBLIC KEY-----\n"+publicKey+"\n-----END RSA PUBLIC KEY-----"
const encryptedData = crypto.publicEncrypt({
key: publicKey,
padding: crypto.constants.RSA_PKCS1_PADDING,
// This was in the original thorleifjaocbsen code but seems nonsensical/unneeded and upsest some Nodes
//oaepHash: "sha256",
}, Buffer.from(cleartext));
console.log(encryptedData.toString("base64"));
EOF
)"
# Generate the command to connect.
CONNECT_COMMAND="{\"ssid\": \"${WIFI_SSID}\", \"wpa_auth\": ${WIFI_AUTH_NUMBER}, \"wpa_cipher\": ${WIFI_CIPHER_NUMBER}, \"enc_passwd\": \"${ENCRYPTED_KEY}\"}"
# And the command to save the connection.
# Include keep_hotspot_until_connected in case we are on the Chromecast's setup hotspot and not Ethernet.
# See <https://github.com/rithvikvibhu/GHLocalApi/issues/88#issuecomment-860538447>
SAVE_COMMAND="{\"keep_hotspot_until_connected\": true}"
# Send the commands
curl ${VERBOSITY} --insecure --tlsv1.2 --tls-max 1.2 -H "content-type: application/json" -d "${CONNECT_COMMAND}" https://${CHROMECAST_IP}:8443/setup/connect_wifi
# Hope this one gets there before it can actually disconnect if we're using the setup hotspot?
# Otherwise we have to use Ethernet or jump over to the target network and find the device again.
# See <http://blog.brokennetwork.ca/2019/05/setting-up-google-chromecast-without.html?m=1> for a script that knows how to swap wifi networks but needs to be ported to use the current API.
curl ${VERBOSITY} --insecure --tlsv1.2 --tls-max 1.2 -H "content-type: application/json" -d "${SAVE_COMMAND}" https://${CHROMECAST_IP}:8443/setup/save_wifi
# To see it working, if you aren't kicked off the hotspot (or if you set the new CHROMECAST_IP in your shell):
#
# curl --insecure --tlsv1.2 --tls-max 1.2 https://${CHROMECAST_IP}:8443/setup/eureka_info | jq .
#
# To list known networks:
#
# curl --insecure --tlsv1.2 --tls-max 1.2 https://${CHROMECAST_IP}:8443/setup/configured_networks | jq .
#
# To forget a newtwork:
#
# curl --insecure --tlsv1.2 --tls-max 1.2 -H "content-type: application/json" -d '{"wpa_id": 0}' https://${CHROMECAST_IP}:8443/setup/forget_wifi
#
# If you leave Ethernet plugged in, the Chromecast will ARP for its WiFi IP on
# Etherenet and drop the WiFi connection! Unplug the Chromecast, and plug it in
# again with no Ethernet, to get it to keep the WiFi connection up!
#
# Set Name and opt out of things:
#
# curl --insecure --tlsv1.2 --tls-max 1.2 -H "content-type: application/json" -d '{"name": "NovakCast5000", "opt_in": {"crash": false, "stats": false, "opencast": false}}' https://${CHROMECAST_IP}:8443/setup/set_eureka_info
@yann1420
Copy link

yann1420 commented Aug 7, 2024

This works. Thank you.
One remark though, line 19 says ' echo 1>&2 "Install jq to use this script!"' but should say ' echo 1>&2 "Install curl to use this script!"'

@interfect
Copy link
Author

@yann1420 Fixed!

@1000283
Copy link

1000283 commented Sep 15, 2024

I have a factory-reset chromecast, all it shows is a message telling me to download the app to setup.

I don't know which IP does it have, it doesn't show up on my router (which makes sense since it doesn't know the SSID/password). What am i missing? Is this because uPnP is off in the router?

@interfect
Copy link
Author

interfect commented Sep 16, 2024 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment