From a Nest thermostat to your own Elixir app

Copy Markdown View Source

This guide takes a Nest Learning Thermostat (2nd generation) from its box to running an Elixir app you can change and redeploy: first the example app, foxbus, then your own.

It takes about an hour, most of it waiting for the Nest's flash memory. Each step says what you should see; if you don't see it, check Troubleshooting before going on.

Status: written from the first unit and being checked step by step on a second, factory-fresh one. Steps marked (to confirm) haven't been through that check yet.

What you need

  • A Nest Learning Thermostat, 2nd generation. In the Nest's own menu, Settings → Technical Info shows the display model: it must start with 2 (e.g. 2.14). Gen 1 isn't supported by this SDK, and Gen 3 can't be flashed yet (see No Longer Evil's compatibility list). A Gen 2 has a yellow/green spirit level in the base; a Gen 3's is blue.
  • Power for it. The display unit runs from its own battery for a while, but needs its base: a wall plate, the Nest desk stand, or a Heat Link base. The SDK talks to the base, and without it the Nest can't stay up.
  • A USB data cable (micro-USB) for flashing. Plug it into the socket on the back of the display, not the one on the desk stand or base: the base's socket only supplies power, so the computer never sees the Nest through it. Many micro-USB cables are charge-only; to check yours, plug the display in directly while it is running Nest's own software. A data cable makes it show up as a small USB drive (2464:0002 Nest in lsusb, about 40 MB). Don't write to that drive.
  • A computer running Linux or macOS, on the same network as the Nest.
  • A 2.4 GHz Wi-Fi network. The Nest's radio doesn't do 5 GHz. (A phone hotspot works; on an iPhone turn on Maximize Compatibility.)

Throughout, <nest-address> means the Nest's IP address on your network (step 1 shows where to find it).

1. Put the Nest on your Wi-Fi and note its address

Do this first, with the Nest's own (stock) software: after rooting you need the Nest on your network to reach it, and it keeps the network it joined.

  1. Power the Nest (on its base or stand) and go through its on-screen setup, or later Settings → Network. Choose your 2.4 GHz network and type its password with the ring.
  2. If it insists on pairing with a Heat Link you don't have, hold the display down for 10 seconds to restart it; it then goes straight to Wi-Fi setup.
  3. Note its address: Settings → Technical Info → Network shows the IP address (e.g. 192.0.2.50). That's <nest-address> in the rest of this guide. Your router's list of devices shows it too.
  4. Recommended: give it a fixed address in your router (a "DHCP reservation" for the Nest), so the address stays the same through flashing and restarts.

While you're in Technical Info, check the display model starts with 2.

2. Root it with No Longer Evil

No Longer Evil (NLE) replaces the Nest's bootloader and kernel so it works without Google's servers, and gives you root over SSH. This SDK runs on top of it.

Follow NLE's own instructions: the installation guide (start with their compatibility check). In short, you connect the display to your computer by USB, hold the display down for 10–15 seconds to put it into DFU mode, and run their installer.

When it's done the Nest restarts on its own and rejoins your Wi-Fi, usually at the same address. Check you can log in as root; the password is nolongerevil:

ssh -o PreferredAuthentications=password root@<nest-address>

The first time, SSH asks you to confirm the Nest's host key: answer yes. You should get a # prompt. Type exit to leave.

(to confirm on a fresh unit: whether NLE's hosted installer enables SSH like this by default, or whether you need their firmware builder's --enable-root-access option.)

3. Set up SSH without a password

The scripts in this guide log in to the Nest with an SSH key, never a password. The Nest's SSH server is old: it accepts ECDSA or RSA keys, not ed25519, so make an ECDSA key just for it:

ssh-keygen -t ecdsa -b 521 -f ~/.ssh/nest_gen2 -C nest_gen2

Press Enter when asked for a passphrase (or set one and use ssh-agent). Then copy the public key to the Nest, typing nolongerevil once more:

ssh -o PreferredAuthentications=password root@<nest-address> \
  'mkdir -p /root/.ssh && chmod 700 /root/.ssh && cat >> /root/.ssh/authorized_keys && chmod 600 /root/.ssh/authorized_keys' \
  < ~/.ssh/nest_gen2.pub

Check it logs in without asking for a password:

ssh -i ~/.ssh/nest_gen2 root@<nest-address> uname -a

You should see a line starting Linux and mentioning 2.6.37. To shorten commands, add this to ~/.ssh/config, after which ssh nest is enough:

Host nest
  HostName <nest-address>
  User root
  IdentityFile ~/.ssh/nest_gen2
  IdentitiesOnly yes

The installer in step 5 turns off root password logins, so from then on only your key gets in. (If you skip this step, the installer does it for you and asks for the password once.)

4. Set up your computer

Apps for the Nest are built on your computer with Erlang/OTP 26 and Elixir 1.17, the versions the Nest runs. Newer versions build code the Nest can't load. With mise (or asdf):

mise use --global erlang@26.2.5 elixir@1.17.3-otp-26

Then get the SDK, which holds the install and deploy scripts:

git clone https://github.com/integration-technology/nest_gen2_sdk.git
cd nest_gen2_sdk

Check the toolchain:

sh -c '. platform/host_env.sh && nest_toolchain_ok && echo OK'

You should see OK. Otherwise it says what it found and what to install.

5. Install the platform on the Nest

platform/install.sh <nest-address>

It explains each step as it goes:

  1. SSH key. It logs in with ~/.ssh/nest_gen2 from step 3. (If that key doesn't exist or isn't on the Nest yet, it creates and installs it, asking once for the password nolongerevil.)

  2. Checks it's a 2nd-gen Nest with room in its flash.

  3. Downloads the runtime, Erlang/OTP 26 and Elixir 1.17 built for the Nest (about 45 MB), and checks its checksum.

  4. Copies it to the Nest. This takes several minutes; the flash is slow.

  5. Wi-Fi. It asks for your network's name (press Enter to keep the one the Nest is on) and then, on the Nest, its password. The SDK runs its own Wi-Fi from now on, because Nest's connection manager goes with the rest of Nest's software.

  6. Boot script. It changes /etc/init.d/rcS so the Nest starts the SDK instead of Nest's thermostat software, keeping the original as /etc/init.d/rcS.stock.

  7. Restarts the Nest and reports what's running:

    entropyd                     running
    Wi-Fi (SDK)                  running
    app watchdog (foxbus)        running
    foxbus                       not deployed yet
    Nest's own software          stopped
    runtime                      nest_gen2-runtime-...
    address                      <nest-address>

After the restart the Nest shows its boot logo until an app draws something. You can run platform/install.sh <nest-address> --check at any time for the same report.

Everything lives in /media/scratch/.nest_gen2_sdk on the Nest. To go back to No Longer Evil's own software, see Undoing it.

6. Deploy the example app: foxbus

foxbus shows live bus departures, the room temperature, disruption warnings and an arrival alarm. Get it next to the SDK and set it up for your own stops as described in its README, under Run foxbus on your own Nest (it shows Carousel Buses stops only, for now):

cd ..
git clone https://github.com/integration-technology/foxbus.git
cd nest_gen2_sdk
platform/deploy_app.sh ../foxbus

deploy_app.sh runs the app's tests, builds it, copies it and its dependencies to the Nest (the first time, the SDK as well), and restarts it. It ends with the first lines of the app's log, and within half a minute the Nest shows foxbus.

install.sh saved the Nest's address and key in ~/.config/nest_gen2/target, which is where deploy_app.sh finds them.

7. Your own app

examples/hello_nest is the smallest useful app: a number you change with the ring, reset with a press, and the room temperature. Copy it, make it yours, and switch the Nest over to it:

cp -r examples/hello_nest ../my_app        # then rename the app inside
platform/install.sh <nest-address> --app my_app
platform/deploy_app.sh ../my_app

install.sh --app only changes which app starts at boot; it skips everything that's already done. One app runs at a time: the Nest has 58 MB of memory, too little for two Erlang VMs.

An app is an ordinary Mix project with {:nest_gen2, "~> 0.2.1"} in its dependencies. docs/api.md describes what the SDK provides: drawing on the round screen, text, the ring and its press, the piezo, the screen's sleep and wake, the motion, light and climate sensors, and Wi-Fi.

Everyday use

All of these run on your computer, from the SDK folder.

ToRun
See what's runningplatform/install.sh <nest-address> --check
Update the appplatform/deploy_app.sh ../foxbus
Read the app's logssh -i ~/.ssh/nest_gen2 root@<nest-address> tail -50 /media/scratch/.nest_gen2_sdk/foxbus.log
The log from before the last restartthe same with foxbus.log.1
Read or change an app's saved settingsplatform/storage.sh foxbus list, then get <key> or put <key> '<value>'
Save another Wi-Fi networkssh -t -i ~/.ssh/nest_gen2 root@<nest-address> sh /media/scratch/.nest_gen2_sdk/wifi.sh add "Network name"
Move to a newer SDKupdate the app's dependency, then platform/deploy_app.sh ../foxbus --sdk
Restart the Nesthold the display down for 10 seconds

The Nest joins whichever saved network is in range, so a phone hotspot saved at home lets you take it somewhere else.

Undoing it

platform/uninstall.sh <nest-address>           # back to No Longer Evil's software
platform/uninstall.sh <nest-address> --purge   # ... and delete the runtime and apps

This restores the original /etc/init.d/rcS and restarts the Nest. Your SSH key stays installed.

Troubleshooting

install.sh can't log in with the password. Check the address and that you can log in by hand as in steps 2 and 3.

The download fails or the checksum doesn't match. Delete ~/.cache/nest_gen2/ and run it again.

After the restart the Nest isn't on the network. Give it three minutes: if the router doesn't answer, the SDK restarts Wi-Fi every 3 minutes. If it still isn't back, the network name or password was probably wrong. Hold the display down for 10 s to restart; if that doesn't help, connect it by USB, re-flash No Longer Evil (step 2), and run install.sh again.

The screen stays on the boot logo. The app isn't deployed or doesn't start. Run --check, then read the app's log.

The light above the screen flashes. It's the base's status light, not the SDK's:

  • Blinking green: the Nest is starting, restarting or updating. If it goes on for more than about 5 minutes and nothing reacts to motion, the base has probably gone quiet after losing power; the SDK wakes it within about a minute, and otherwise restart the Nest.
  • Blinking red: the display's battery is too low to start, and it's charging. Leave it on its base; it starts by itself once there's enough charge (this can take an hour or more). Holding the display down does nothing until then.

The Nest ran flat. The base charges the display with only about 120 mA, less than it uses awake, so it drains while "plugged in" unless it deep-sleeps: set the screen to sleep (foxbus: press on the home screen, then Screen) rather than Always on. See power.md. Seat the display firmly on its base, and use a decent 5 V supply.

The screen doesn't wake when you walk up to it. The motion sensor is better at movement across the room than straight towards it. Pressing the display always wakes it, and foxbus's settings (press on the home screen, then Screen) can keep it on.

deploy_app.sh says the SDK version doesn't match. Another app on the Nest was deployed with a different SDK version. Build against that version, or add --sdk to replace it.

Anything else. Open an issue at https://github.com/integration-technology/nest_gen2_sdk/issues with the output of --check and the last lines of the app's log.

What install.sh changes

So you can check its work, or do it by hand:

  • adds /media/scratch/.nest_gen2_sdk/ with otp/, elixir/, udhcpc, entropyd, wifi.sh, udhcpc.script, app_watchdog.sh, RUNTIME, and wpa_supplicant.conf (your Wi-Fi network as a WPA key, not the password); apps keep their settings in data/<app>/ there (NestGen2.Storage);
  • appends your public key to /root/.ssh/authorized_keys;
  • copies /etc/init.d/rcS to /etc/init.d/rcS.stock, then in rcS: comments out nlmetrics, heartbeat, nlsleep, nlshutdown, nestlabs and monit; starts entropyd before networking and wifi.sh takeover after it; adds -g to dropbear (no root password logins); and starts app_watchdog.sh <app> at the end (platform/rcs_patch.awk does this);
  • writes ~/.config/nest_gen2/target on your computer.

platform/README.md explains why each part is there.