Integrations
- UNFR0ZEN menu
- MQTT
- Matter
- Matter over Thread
- Zigbee
- External temperature
- Other options
- Backup & restore
- Doom
Integrations are chosen at install: tick them in the app, or pass them to the unfr0zen command-line tool. To change them, run the install again.
MQTT
Wi-Fi, to your MQTT broker. Home Assistant discovers the thermostat there.
Matter
Wi-Fi or Thread, to a Matter controller: Apple Home, Google Home, Alexa or Home Assistant.
Zigbee
The thermostat's own 802.15.4 radio, to your coordinator: Zigbee2MQTT or ZHA.
External temperature
A thermometer of yours regulates the heating in place of the thermostat's own sensor.
nestlocal replaces the thermostat's connection to Nest's cloud. Nest's own nlclient keeps running the screen and the heating, pointed at nestlocal on the unit instead of Nest's servers. The Nest and Google Home apps no longer control the thermostat; Home Assistant does, over your network. Remaining traffic to Nest's and Google's servers, NTP for example, stops with Block Nest and Google servers.
- nestlocal. MQTT, Matter, Zigbee and Doom are served by nestlocal, which any of them installs on the thermostat.
- Wi-Fi. MQTT and Matter use Wi-Fi, joined from the thermostat's Settings → Network. The network doesn't need internet access.
- Generations. 1st, 2nd and 3rd generation. The 1st has MQTT, Matter over Wi-Fi and the external temperature: no Zigbee or Matter over Thread (its radio is on a UART), and no USB shell. The Nest Thermostat E is detected and refused.
- Command line. On Linux and macOS, the app's executable run with arguments; Windows has the app only.
- Linux. Run the app and CLI as your normal user, after the one-time USB permission setup. Run logs:
~/.local/share/unfr0zen/logs/, or$XDG_DATA_HOME/unfr0zen/logs/when set. - Your own integration. The MQTT topics, Zigbee clusters and Matter endpoints are documented under Protocols.
UNFR0ZEN menu
The install replaces the thermostat's NEST APP settings entry with UNFR0ZEN. It shows Matter pairing, Zigbee pairing with a 30-second countdown, and Doom. An integration's entry appears only when it was installed.
The menu is linked into the unit's own nlclient; the original is kept at /etc/unfr0zen/nlclient.original across installs. Supported versions and undo: platform/common/nlclient/README.md.
MQTT
| App | CLI |
|---|---|
MQTT, under Patch → Integrations; Broker host[:port] | --mqtt host[:port] |
| User, Password | --mqtt-user USER, --mqtt-password PASSWORD |
| Encrypt the connection (TLS), CA certificate | --mqtt-tls, --mqtt-ca FILE |
| Device name, Entity ID root, under Home Assistant | --device-name NAME, --ha-entity-root ROOT |
The thermostat connects over Wi-Fi to your MQTT broker (the Mosquitto add-on, for example); Home Assistant's MQTT integration discovers and controls it there. The broker is the only intermediary. Port 1883, or 8883 with TLS; a password needs a user; a CA certificate turns on TLS.
Names in Home Assistant
Home Assistant shows the thermostat as a device named Nest Thermostat, with entity IDs like climate.nestlocal_<serial>_thermostat. A device name and an entity ID root replace them:
- Device name.
Nest FamRmfor example: one line of at most 32 bytes, the same as Matter's. - Entity ID root.
nest_famrmfor example: lowercase letters and digits, words joined by_, at most 64 characters. It givesclimate.nest_famrm_thermostat,sensor.nest_famrm_current_temperature. <serial>in either stands for the thermostat's 16-character serial, which counts towards the limit (lowercase in the root,kikoo_<serial>_lolfor example).
Left empty, the thermostat keeps the names it has, so installing again for an update changes nothing. With a new root, Home Assistant (2025.10 or later) moves the thermostat's entities to the new IDs, with their history and what was set on them there (names, areas, icons); automations and dashboards need the new IDs. If Home Assistant isn't running when the thermostat connects after the install, the IDs change the next time it starts.
Entities
A climate entity with off, heat, cool, heat-cool, eco and fan modes, setpoints and range, and action, and beside it:
- Eco and the eco temperatures
- Humidity
- Target humidity
- Occupancy
- °C/°F
- Backplate temperature
- Battery
- Fan and its timer
- Fan schedule: on/off, minutes per hour, start and end hours
- Auto-Schedule
- Home/Away Assist
- Sunblock
- Early-On
- Eco heat and cool limits
- Leaf, Early-On, Sunblock, safety shutoff and filter indicators
- Heating, cooling and fan running
- Each wired HVAC stage running
- Time to temperature
- Filter runtime
- IP address
- Wi-Fi signal
- Clear schedule
With the equipment for them:
- Filter reminder and its reset
- Hot water state, schedule and boost
- Humidifier and dehumidifier state
- Humidity control
Updates
An unfr0zen update entity shows the version the thermostat runs and, when it can reach GitHub, the latest release, checked every 6 hours. On a 3rd generation unit Install downloads that release's bundle, checks it against its manifest and unfr0zen's release key, installs it and restarts the thermostat. Only official releases install, and never one older than the installed version, unless Allow unsigned updates was on when the unit was patched. 1st and 2nd generation units show the versions; update them with the app. An install that fails says why in the thermostat's log and leaves it running; one that does not come back after the restart is fixed by patching it again with the app over USB.
A thermostat that can't reach GitHub installs a release's bundle (unfr0zen-diamond3-<version>.tgz, from the release's assets) from your own network: put it on any web server the thermostat reaches and publish its URL, for example from a Home Assistant script:
action: mqtt.publish
data:
topic: nestlocal/<serial>/set/update
payload: http://nas.lan/unfr0zen-diamond3-v0.5.0.tgz
Weather and clock
Two Home Assistant blueprints replace what Google supplied. Both need MQTT; on Zigbee, see Zigbee.
Weather
Sends outdoor temperature, humidity, conditions and forecast every 15 minutes, when Home Assistant starts, and when the weather entity comes back from unavailable or unknown.
Import it, create an automation, pick the thermostat and a weather entity. Outdoor sensors are optional; °F is converted to °C.
Matter
| App | CLI |
|---|---|
| Matter, under Patch → Integrations | --matter |
| Pairing code | --matter-code CODE |
| Device name, under Home Assistant | --device-name NAME |
Works alone or beside MQTT; needs Wi-Fi. The device name, shared with MQTT, is the name controllers show: Matter's node label, at most 32 bytes, <serial> standing for the serial. An install that gives a new one sets it; a name changed in the controller stays until then.
Pairing
Pairing code 0347-100-5534, or one of your own with a valid check digit; --matter-code turns on Matter. The first pairing window opens by itself: UNFR0ZEN → Matter shows Ready to pair with the QR and code, then Paired.
- Home Assistant: Settings → Devices & services → Add integration → Matter.
- Add the device with the pairing code, or scan the QR shown on the thermostat.
Enable pairing → Forget and pair forgets every Matter controller before opening a new window. This cannot be undone, even if the new pairing fails; Wi-Fi and Zigbee settings are kept.
Entities
Home Assistant gets the thermostat (modes, setpoints and range, action, eco as a preset, humidity, outdoor temperature), occupancy, °C/°F, backplate temperature, battery, fan and its timer, the fan schedule's run time, start and end, and switches for Auto-Schedule, Home/Away Assist, Sunblock, Early-On, the eco limits and, with a humidifier or dehumidifier, humidity control, and with hot water control, Hot water boost (30 minutes when on) and Hot water schedule, and with an air filter, the filter reminder and a Filter reset switch that resets the filter when turned on, and with a fan, the fan schedule. The other MQTT entities aren't offered over Matter.
Bluetooth commissioning
3rd generation units with the supported DA14580 Bluetooth firmware also pair over Bluetooth; nestlocal loads the unit's own radio firmware into RAM, the stock image unchanged. Bluetooth advertises only while the pairing window is open; traffic goes over Wi-Fi or Thread. The thermostat's own Bluetooth is turned off for it: real Nest Temperature Sensors stop working, and the Nest app cannot pair with it over Bluetooth. Other units pair over IP.
Sleep
Experimental. The thermostat sleeps between Matter messages and wakes for a controller's: unicast UDP to port 5540, and on the 3rd generation also a DNS-SD lookup of the unit and an IPv6 neighbour solicitation for its addresses. nestlocal adds these to the wake patterns Nest's sleep daemon gives the Wi-Fi chip. The 2nd generation's chip lets no multicast through while the unit sleeps, so it answers lookups when it wakes on its own. Each wake reports to the controllers' subscriptions, which they keep through its sleep. It stays awake while commissioning. With more fabrics than the chip has room for, lookups of other Matter devices wake it too.
Matter over Thread
| App | CLI |
|---|---|
| Thread support, under Matter | --matter-thread |
The thermostat joins a Thread mesh as a router and serves Matter over it, beside Wi-Fi; the commissioner supplies the credentials. Needs Matter. Takes the radio from Nest's Thread firmware (Heat Link), so not with Zigbee. On the 2nd generation nestlocal flashes a Thread image and keeps the unit awake while Thread runs. Design and device details: platform/common/radio/thread/README.md.
Zigbee
| App | CLI |
|---|---|
| Zigbee, under Patch → Integrations | --zigbee |
A Zigbee router on the thermostat's own 802.15.4 radio, alone or beside MQTT and Matter. On first start nestlocal replaces Nest's Thread firmware (Heat Link) with its own; installing again without Zigbee puts Nest's back. Transmit power is Nest's -15 dBm.
Heat Link models
A 3rd generation Heat Link model (Display-3.x, Display-5.x) on firmware 6.4-8 can stop on H71 or H74 once the Heat Link can't connect. With Zigbee or Thread, Works With Nest boiler (--wwn-boiler) sets the unit up as heating a Works With Nest boiler: it keeps heating through W1 with its safety temperature and real backplate sensors, and Zigbee, Matter and MQTT offer heat. Off unless chosen; installing again without it takes it back. While set: Safety Temp shows a notice (its values stay in force), Heating Equipment does nothing, Pro Setup's boiler pages go nowhere, and with nothing on W1 the boiler has to be driven from the setpoint, with a relay from Home Assistant for example.
Coordinators
Any Zigbee 3.0 coordinator works: standard clusters, so ZHA needs no quirk (its switches need zha 0.0.61, June 2025, or later). Zigbee2MQTT with the converter is recommended: it names every entity and carries the weather.
- Zigbee2MQTT 2.12.1 or later: copy
nest-learning-thermostat.mjstodata/external_converters/and restart Zigbee2MQTT. - Permit join on the coordinator. The thermostat joins the strongest network permitting joins, on any channel, and keeps it across restarts.
- After updating the converter, re-interview the device to refresh its generation-specific model, then Reconfigure it so Home Assistant gets the new entities.
The switches are Binary Output clusters, so ZHA names them. A thermostat paired on an older release has to be paired again in ZHA, or re-interviewed in Zigbee2MQTT with the updated converter; delete the old switch entities.
Entities, clock and weather
With the converter, Home Assistant gets the MQTT entities under the same names, minus IP address and Wi-Fi signal; temperature and HVAC action sit on the climate entity. The clock comes from the coordinator's Time cluster, no blueprint needed. The weather has a Zigbee2MQTT blueprint: import it, create an automation, pick the thermostat, the base topic (default zigbee2mqtt) and a weather entity. Outdoor sensors are optional; °F is converted to °C. Nothing needs Wi-Fi.
Joining and moving networks
On a coordinator with per-device keys, a router needs its own trust-centre link key to help others join. If the thermostat still has only the global key, enable pairing again.
To move networks: UNFR0ZEN → Zigbee → Enable pairing. The thermostat leaves its network, forgets its credentials and looks for one permitting joins for 30 seconds; on timeout it stays off any network until pairing is enabled again or nestlocal restarts.
Sleep
The radio routes while the thermostat sleeps and wakes it for a message, on the 2nd generation's kernel and the 3rd generation's 5.8 to 6.1 kernels; on others, messages wait until the unit wakes. The 2nd generation doesn't sleep with a USB cable connected.
External temperature
| App | CLI |
|---|---|
| External temperature, under Patch → Integrations | --external-temperature |
A thermometer of yours regulates the heating in place of the thermostat's own sensor. Every generation, with MQTT, Matter or Zigbee. nestlocal presents a virtual Nest Temperature Sensor (gen 1 and 2: local sensor input, on Nest firmware 5.9.4-5; gen 3: Bluetooth). Send readings and the thermostat regulates from them; stop, and it goes back to its own sensor.
- One sensor. Only one external temperature is supported for now.
- Named after the thermostat's room. The sensor takes the thermostat's room, so the Sensors menu lists it under that room's name, Living room for example. It cannot be renamed on its own yet.
- On 3rd generation, Bluetooth is turned off. The thermostat no longer hears real Nest Temperature Sensors, and the Nest app cannot pair with it over Bluetooth.
Setting it up in Home Assistant
Install with the option and your integration, then import that integration's blueprint and create an automation. Each sends the reading on change, every 10 minutes and when Home Assistant starts; °F is converted to °C; nothing is sent while the thermometer is unavailable.
MQTT. Import external-temperature-mqtt.yaml, create an automation from it, and pick the thermostat (its MQTT device) and your room temperature sensor. It publishes each reading to nestlocal/<serial>/set/external_temperature.
Zigbee2MQTT. Use the converter from Zigbee. After updating it, re-interview the thermostat, then Reconfigure it, so Home Assistant gets Temperature source. Import external-temperature-zigbee.yaml, create an automation from it, and pick the thermostat, the Zigbee2MQTT base topic (default zigbee2mqtt) and your room temperature sensor. It publishes {"external_temperature": 21.5} to <base topic>/<IEEE address>/set. There is no blueprint for ZHA: the reading is attribute 0x0005 of the manufacturer-specific cluster 0xFC00 (see Protocols).
Matter. The thermostat's Matter device gets an External temperature select offering 5 to 35 °C in half degrees. Import external-temperature-matter.yaml, create an automation from it, and pick that select and your room temperature sensor. Readings are rounded to the nearest half degree. Choosing the same temperature again counts as a new reading.
How it behaves
- The sensor appears in the Sensors menu when the thermostat starts. The thermostat uses its own sensor until the first reading arrives.
- A reading counts for 30 minutes, repeated to the thermostat every minute. About 11.5 minutes after it lapses, the thermostat goes back to its own sensor until the next reading.
- Choosing the backplate in the Sensors menu lasts until the thermostat restarts. After a restart, the next reading switches it back to the external temperature.
- Home Assistant shows External temperature, the reading while it counts, and Temperature source,
externalorbackplate. Zigbee2MQTT shows Temperature source only. On Matter both are selects: Temperature source shows the source, and choosing the other one is refused. - Installing again without the option removes the sensor from the thermostat, its Sensors menu entry included, and undoes the gen 1 and 2 input patch or restores the gen 3 Bluetooth setting.
Other options
The app's Patch page takes you through Connect, Integrations, Features and Review. Works With Nest boiler (with Zigbee or Thread), Block Nest and Google servers, the time server, SSH, the USB console, Doom and Dark theme are on Features; Device, Entry point, Backplate simulator and Allow unsigned updates under Developer options on Review, which also offers a NAND backup before the patch.
| App | CLI | What it does |
|---|---|---|
| Block Nest and Google servers | --block-cloud | Resolves Nest's and Google's servers to nowhere, and checks for the internet at Cloudflare. Turns on the custom NTP server. |
| Custom NTP server; Time server under Block Nest and Google servers | --ntp-server [HOST] | Sets the firmware's time server to HOST instead of time.nest.com (default pool.ntp.org). nestlocal's own clock check stays on pool.ntp.org. |
| SSH | --ssh | Runs an SSH server on the unit (dropbear on the 1st and 2nd generation, host keys kept in system-config). |
| Your key, under SSH | --ssh-public-key FILE | Requires --ssh. Adds one OpenSSH public key to root's authorized keys without replacing existing keys. Use an ECDSA key with the 3rd generation's stock OpenSSH 6.0; Ed25519 is supported by the 2nd generation's dropbear. Supply a public .pub file, never a private key. |
| USB console | --usb-shell | Root shell on the unit's USB serial port. On the 2nd generation it installs a kernel with a USB serial port, booted ahead of the stock one, which stays as the fallback. |
| Allow password login, turned off | --ssh-key-only | Requires --ssh-public-key. SSH takes the key only: root's password is refused over SSH. |
| Root password | --root-password PASSWORD | For SSH and the USB shell (default unfr0zen). |
| Backplate simulator (1H1C) | --backplate-sim | 1H1C with a simulated temperature, for testing off the wall. |
| Allow unsigned updates | --allow-unsigned-updates | The unit installs updates that unfr0zen's release key does not sign, such as your own builds, and older versions than the one it runs. Off, it installs only official releases, never going back. |
| Not available | --nand-image FILE | 1st or 2nd generation: patches a raw NAND image (2112-byte pages) in place instead of a unit. |
| Entry point | --entry-point ENTRY_POINT | 3rd generation: how its boot ROM is entered for recovery: auto (default) tries the official loader, then CVE-2017-7932, then CVE-2017-7936; or official-loader, cve-2017-7932, cve-2017-7936. It applies to a patch, backup, restore and booting the application, in the app and the CLI. |
| Device | --device diamond|j49|diamond3 | The generation (1st, 2nd, 3rd) to expect instead of the one identified on USB. The app applies it to a patch only. |
| Dark theme | --dark-theme | 2nd generation, Nest firmware 5.9.4-5: shows the settings screens in the 3rd generation's dark colours, and the boot logo on black. The app offers it when a 2nd generation is connected. A run without it brings back the light colours. |
| Works With Nest boiler | --wwn-boiler | Needs Zigbee or Thread. See Zigbee. |
| Rescue | unfr0zen boot | Boots the application firmware from any mode: a 3rd generation through recovery U-Boot, a 1st or 2nd generation by restarting the flasher an interrupted run left on USB. |
| Not available | unfr0zen uboot | 3rd generation: loads recovery U-Boot and leaves it at its prompt. |
| Serial console | Not available | A terminal on a patched unit's USB root shell. |
| Offered at start | unfr0zen update | Installs the latest release. |
Every CLI option: unfr0zen --help.
Backup & restore
| App | CLI |
|---|---|
| Back up unit | unfr0zen backup [OUT] |
| Not available | unfr0zen verify IMAGE |
| Restore to unit, Restore from file | unfr0zen restore IMAGE |
- Backup. The whole NAND as an image with a
.sha256beside it. On the 3rd generation: 512 MiB, ECC-corrected, with a.badlist of its bad and unreadable blocks. On the 1st and 2nd: raw pages with OOB, unclean blocks read until two reads agree. Nothing is written during a backup. - Resume.
unfr0zen backup OUTwith the same OUT resumes an interrupted backup; the app, and a run without OUT, start a new file. - Restore. Verifies the image against the
.sha256, asks, then writes it back. On the 1st and 2nd generation the factory-bad blocks must match the chip's, only differing blocks are rewritten, and an interrupted restore is run again.
Doom
| App | CLI |
|---|---|
| Doom, under Patch → Features | --doom |
Installs Doom with the shareware episode (DOOM1.WAD v1.9, included). Start it from UNFR0ZEN → Doom → Play.
- You walk forward all along. Turn the ring to steer, press it to fire. Doors and switches open as you walk into them.
- In menus, the ring moves and a press selects.
- Hold the ring for 3 seconds to quit back to the thermostat.
- Sound comes from the piezo: the PC speaker effects and each song's highest note.