Skip to content

Instantly share code, notes, and snippets.

@interfect
Last active October 5, 2025 13:50
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
@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

@wtt604
Copy link

wtt604 commented Mar 9, 2025

After installing jq and nodejs, it worked great on a factory reset NC2-6A5 after Google killed their android apps talking to it.

Thanks for getting it back!

@DavidePrincipi
Copy link

DavidePrincipi commented Mar 10, 2025

Thank you for this script. It seems really important nowadays!
I tried to remove the nodejs dependency here: https://gist.github.com/DavidePrincipi/e2007b998bb66ea69a1ff7832af6c417/revisions#diff-f602019a80ebff228e17d6515aa93d17d299f6c0dc3610962565f7d637091e33

@AndroidNecunoscut
Copy link

It seems that 5GHz WiFi channels above 149 are not supported by some firmware versions, so disconnecting the device after calling setup/connect_wifi is not caused by an issue on this script

https://www.googlenestcommunity.com/t5/Streaming/firmware-version-1-56-275994-cannot-connect-to-5GHz-WiFi/m-p/70018

@1mm0rt41PC
Copy link

Good job! !!! Your research is the only way to configure a Chromecast since Google's CA is expired (205-03-09) for *cast (chromecast,nest,autiocast,...)
I rewrote the script in python3 :

https://gist.github.com/1mm0rt41PC/78e134d43e5badf3cc999bdf33e1fdd5

@per-review
Copy link

Thank you for this script. It seems really important nowadays! I tried to remove the nodejs dependency here: https://gist.github.com/DavidePrincipi/e2007b998bb66ea69a1ff7832af6c417/revisions#diff-f602019a80ebff228e17d6515aa93d17d299f6c0dc3610962565f7d637091e33

And it worked! :) Tested sucessfully with a wifi-connected 2nd generation Chromecast.

Here are commands to execute the script while printing out the lines that get executed (with bash -x) and capturing standard out and standard error, which makes it a bit easier to debug stuff.

Having a space before the first command prevents it from being written to history – provided that HISTCONTROL has been set accordingly.

192.168.255.249 seems to be Chromecast's default IP address on its own wifi network.

 WIFI_PASSWORD=abc12345
VERBOSITY=--verbose CHROMECAST_IP=192.168.255.249 WIFI_SSID="whatever" WIFI_PASSWORD="$WIFI_PASSWORD" bash -x castanet.sh > >(tee -a stdout.log) 2> >(tee -a stderr.log >&2)

@ElbertCode
Copy link

ElbertCode commented Sep 27, 2025

Hi, I'm having some trouble getting this to work with my "Chromecast with Google TV" and a Linux PC. So I connected the PC with the Chromecast hotspot and provided the correct SSID and password to the script but it kept complaining about the public key format, so I've debugged it a little bit, and figured that the response I get from https://CHROMECAST_IP:8443/setup/eureka_info is always:

{"name":"Chromecast8462","net":{"ethernet_connected":false,"ip_address":"","online":false},"setup":{"setup_state":0,"ssid_suffix":"ytb","tos_accepted":true},"version":0}

As you can see, it's missing the public key. Did this script stopped working or did I do something wrong?

I've also tried this python version, but as it calls the same URL, it didn't work either...

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