Skip to content

Latest commit

 

History

10 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Terma MOA WiFi — Home Assistant integration

A HACS-installable Home Assistant custom integration that controls a Terma MOA WiFi towel-radiator/heater locally over Bluetooth Low Energy (BLE), using the same BluFi protocol the manufacturer's app uses. It exposes one climate entity per heater: target temperature, on/off, and mode presets (Manual / Schedule / Dryer / Antifreeze).

What it does — and doesn't

  • Fully local, no cloud. This integration talks directly to the heater over BLE. It never contacts Terma's servers, and it doesn't need (or use) the Terma mobile app, an account, or internet access to function day-to-day.
  • It re-implements the heater's BluFi/protobuf command protocol (temperature, power, mode) and telemetry parsing based on real captured traffic. There's no vendor SDK involved.
  • It does not modify or replace the heater's own control panel or app — you can keep using those. HA and the app talk to the same physical device.

Requirements

  • Home Assistant with Bluetooth support (either a local Bluetooth adapter reachable from the HA host, or an ESPHome Bluetooth proxy, or both).
  • The heater's 6-digit pairing PIN (see below).
  • HACS (recommended) or manual installation into config/custom_components/.

Do I need an ESP32?

BLE has limited range, and — unlike Bluetooth proxying for e.g. sensors — controlling this heater requires an actual bonded (paired) connection, because the heater requires passkey authentication.

  • You don't need an ESP32 if the Home Assistant host itself has a Bluetooth adapter that is in range of the heater. In that case the integration can attempt to bond directly against the heater using your PIN (Path A, see below).
  • You do need an ESP32 if the HA host is out of Bluetooth range of the heater (e.g. HA runs on a server/NAS in a different room, or as a VM/container without direct BLE access). In that case, flash a cheap ESP32 board with the bundled esphome/terma-proxy.yaml config, place it near the heater, and let it act as both a Bluetooth proxy and the bonding client (Path B, see below). This is the recommended, most reliable setup.

Finding your PIN

The Terma MOA WiFi heater has a 6-digit pairing PIN, printed on/near the device (a label on the unit) or shown in the Terma mobile app under the device's Bluetooth/pairing settings. This is the same PIN the official app asks for the first time you pair with the heater. You'll enter this PIN into the HA config flow (and, for Path B, into the ESPHome YAML) — it's not a value you invent yourself.

Setup

Path A — HA host in range (no ESP32)

  1. Make sure the HA host's Bluetooth adapter can see the heater (it should show up via discovery).
  2. In Home Assistant, go to Settings → Devices & Services → Add Integration and search for "Terma MOA WiFi" (or wait for automatic discovery — the integration advertises a Bluetooth matcher, so a newly-in-range heater should prompt you to set it up).
  3. Enter the device PIN when asked.
  4. The integration will attempt to pair/bond with the heater directly over the local adapter using the PIN, then verify the connection. If this succeeds, the climate entity appears.

Note: local-adapter bonding (Path A) depends on your Bluetooth stack (BlueZ on Linux) and is best-effort — some hosts/adapters/OS combinations don't expose the D-Bus pairing agent interface this relies on. If bonding via Path A doesn't work for your setup, use Path B (ESP32) instead, which is the more reliable, tested route.

Path B — ESP32 proxy + bonding (recommended)

  1. Copy esphome/terma-proxy.yaml into your ESPHome configs.
  2. Edit the substitutions at the top:
    • terma_pin: your heater's 6-digit PIN.
    • terma_mac: the heater's current BLE MAC address (find it via Bluetooth discovery/scanning, e.g. in HA's Bluetooth integration or a BLE scanner app).
  3. Make sure your secrets.yaml has wifi_ssid / wifi_password defined (or inline them, not recommended).
  4. Flash the config to an ESP32 board and place it within BLE range of the heater.
  5. Put the heater into pairing mode: hold both physical buttons on the heater for about 3 seconds until the LED starts pulsing blue. This opens a pairing window.
  6. While the heater is in pairing mode, the ESP32 (configured with io_capability: keyboard_only and an on_passkey_request handler that replies with terma_pin) will bond with it automatically. Watch the ESPHome logs to confirm the bond succeeded.
  7. Once bonded, the ESP32 acts as a Bluetooth proxy for Home Assistant. In HA, go to Settings → Devices & Services → Add Integration, search for "Terma MOA WiFi" (or accept the automatic discovery prompt), and enter the same PIN. The integration will verify the connection through the proxy — no direct pairing is required from HA's side, since the ESP already bonded.

You only need to redo the pairing-mode step (step 5) if the heater is factory-reset, unpaired, or you're pairing a replacement ESP32.

Usage

Once set up, the heater appears as a single climate entity:

  • Target temperature: adjustable between the heater's supported range, in 0.5 °C steps.
  • HVAC mode: Off / Heat.
  • Presets: manual, schedule, dryer, antifreeze — mirrors the heater's own operating modes. Selecting "dryer" runs the built-in towel-dry timer.

Use the standard HA climate card/dashboard tile, automations, or scripts against this entity like any other climate device.

Troubleshooting

  • not_bonded error in the config flow: the integration could connect to the heater's BLE advertisement but the authenticated connection failed. This almost always means the heater isn't bonded yet with whatever adapter/proxy HA is using. For Path A, retry — local BlueZ bonding can be flaky; if it keeps failing, switch to Path B. For Path B, re-check that the ESP32 actually completed a bond (see its logs) and that terma_pin/terma_mac are correct.
  • Device found but connection times out / entity is "unavailable": check range — BLE has a much shorter effective range than WiFi. Move the ESP32 proxy closer, or add a second proxy.
  • ESP32 proxy loses the connection intermittently: make sure power_save_mode: none is set under wifi: in the ESPHome config. WiFi power-saving on the ESP32 interferes with the timing BLE connections need and is a common cause of dropped or flaky BLE links. This is already set in the bundled terma-proxy.yaml — don't remove it.
  • Heater address changed / integration can't find the device anymore: BLE MAC addresses can change after a factory reset or firmware update. Re-scan and update terma_mac in the ESPHome config (Path B) or re-add the integration (Path A).
  • PIN doesn't work: double check you're using the pairing PIN from the device/app, not a Wi-Fi password or account password — this integration doesn't use the heater's cloud/WiFi credentials at all, only the local BLE pairing PIN.

Protocol notes / credits

The BLE command protocol (BluFi framing + a minimal hand-rolled protobuf encoder/decoder) was reverse-engineered from real captured traffic between the Terma app and a Terma MOA WiFi heater. There's no dependency on any vendor library or protobuf schema file — field numbers and framing were derived empirically and are golden-tested against real captured frames in tests/.

Disclaimer

This is an unofficial, community-developed integration and is not affiliated with, endorsed by, or supported by Terma or any of its subsidiaries. It was built by reverse-engineering observed Bluetooth traffic and may stop working if the manufacturer changes the device firmware or protocol. Use at your own risk — in particular, be careful with temperature/mode automations on a device that heats towels/rails, and always verify behavior manually before relying on unattended automations. No warranty of any kind is provided; see LICENSE (MIT).

About

Home Assistant integration for Terma MOA WiFi towel radiators — local BLE (BluFi) control, no cloud

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages