Controlling a TOLO Steam Bath with Home Assistant

This one started, as many of my side projects do, with a piece of hardware sitting in front of me and the nagging question: why can't I control this from Home Assistant?

A while ago I built a steam bath together with a member of my family. The steam generator we used came from TOLO (a brand of Steamtec), and it shipped with a so-called App Control Box — a little network module that lets you operate the steam bath from a smartphone app instead of just the wall panel. Temperature, humidity, the lamp, the fan, aroma therapy, the salt bath nebulizer: all of it controllable from your phone. Nice. But of course I didn't want yet another app on my phone — I wanted it in Home Assistant, next to everything else in the house. And later, when I built my own house and put a steam bath into it for my own family, having proper home automation control was no longer a nice-to-have, it was the whole point.

There was just one problem: there is no public API, no documentation, and no official integration. So I had to build one.

Reverse Engineering the Protocol

The App Control Box talks to the smartphone app over the local network, so the obvious first step was to find out how. I put the box and my phone on the same network, captured the traffic while clicking through the app, and started staring at packets.

A few things became clear quickly:

  • The communication is based on UDP datagrams, not TCP. The app sends a small request packet, the device answers with a response packet.
  • Everything happens on a fixed port (51500).
  • The protocol is refreshingly simple: each message is a short, fixed-structure byte sequence — a command byte, a couple of arguments, and (for status/settings responses) a payload that encodes the current state of the device.
  • There is a tiny keep-alive mechanism (a single byte) to let the device know someone is still listening.

From there it was the usual reverse-engineering grind: toggle one setting in the app, watch which byte in the packet changes, write it down, repeat. Bit by bit the meaning of every field fell into place — target temperature (35–60 °C), target humidity (60–99 %), the various timers (power, fan, salt bath), lamp mode, aroma therapy slot, and a whole set of read-only status values like the current water tank temperature, water level and valve state.

tololib

Once I understood the protocol, I wrapped it in a proper Python library so I'd never have to think about raw bytes again: tololib.

tololib gives you a clean client object that hides all the datagram juggling. Reading the current state of the device is as simple as:

from tololib import ToloClient

client = ToloClient("192.168.1.100")

status = client.get_status()
print(status.power_on, status.current_temperature, status.current_humidity)

settings = client.get_settings()
print(settings.target_temperature, settings.target_humidity)

# turn the steam bath on
client.set_power_on(True)

The library also ships with a command line interface, so you can poke at a device straight from the terminal without writing a single line of Python — handy for debugging and for figuring out whether a problem is in the hardware, the network, or the software on top.

One detail I'm particularly happy with: tololib includes a device simulator. Since the protocol is just UDP request/response, I could implement a fake TOLO device that speaks the same protocol. That meant I could write and run the entire test suite — and later develop the Home Assistant integration — without needing the actual steam bath powered up and reachable. Continuous integration doesn't have a steam bath in the rack, after all.

The library is published on PyPI under the MIT license, and the API documentation lives here.

Legal note: this is a community project. Neither tololib nor I have any professional affiliation with the companies behind TOLO. I just wanted to control my own steam bath.

The Home Assistant Integration

With a stable library doing the heavy lifting, building the TOLO integration for Home Assistant became the fun part. tololib is the core; the integration is a relatively thin layer mapping the device's capabilities onto Home Assistant entities:

  • a climate entity for the steam bath itself, with the heat and dry operating modes, target temperature and target humidity;
  • a light entity for the RGB lamp, including its automatic colour-fading mode;
  • a fan entity for the ventilation, with its timer;
  • switches for aroma therapy and the salt bath nebulizer;
  • and a set of sensors and binary sensors exposing the diagnostics — tank temperature, water level, timers and valve state.

Because the integration communicates with the device entirely over the local network, it runs as a local polling integration: no cloud, no account, no external dependency. Discovery makes setup painless — in most cases Home Assistant finds the App Control Box on the network and you just confirm.

I contributed the integration to Home Assistant core, and after the usual (very thorough, and very helpful) review process, it shipped with the Home Assistant 2021.12 release. Seeing your own little reverse-engineering hobby project become a one-click integration that anyone with the same hardware can use is a genuinely good feeling.

Wrapping Up

If you happen to own a TOLO / Steamtec steam bath with an App Control Box, you can add it straight from the Home Assistant integrations UI — no extra setup required. And if you just want to talk to one of these devices from your own Python code or from the command line, grab tololib from PyPI.

Found a bug, or missing a feature? Open an issue — contributions are very welcome.