A from-scratch UI and integration stack for the Eneco Toon 1 / Toon 2 smart thermostat that runs entirely on the device — no cloud, no Quby login, no subscription. The stock Qt UI is replaced with an LVGL app; a small constellation of side processes brings the hardware back to life (boiler, P1 meter, vent, weather, packages) and exposes everything over a tiny progressive web app so you can also drive the house from your phone.
9-frame loop pulled live from the device framebuffer at v0.7.4 — home (live CV pressure, moon-phase widget) → dim ambient → boot picker → buienradar radar → heater detail → schedule editor → graphs → settings → lights.
The 10-second picker every boot — pick freetoon-lvgl or Stock qt-gui and the device dispatches inittab accordingly. Same modal lives in Settings → UI mode for in-session switching.
Eneco discontinued cloud support for the original Toon: no more app, no more weather, no more package tracking, no more software updates. The device itself is a perfectly capable iMX28 box with a 1024×600 touchscreen, an OpenTherm adapter, an i.MX P1-meter UART and a humidity/temperature sensor — too good to land on a shelf.
freetoon-lvgl keeps the thermostat doing what it's good at (running OpenTherm,
modulating the boiler, reading the room) while replacing the parts that
required a server somewhere:
| Lost piece | Replacement |
|---|---|
| Eneco cloud UI | LVGL app on the framebuffer + LVGL "dim" ambient screen |
| Mobile app | Self-hosted PWA served from the Toon itself |
| Weather forecast | Direct Buienradar pull |
| Energy graphs | P1 bridge → MQTT → Home Assistant (or local view) |
| Package tracking | IMAP-driven HA automation → MQTT → on-device banner |
| Ventilation control | Itho Wifi addon REST API integration |
| Schedule editor | Native LVGL screen + PWA editor |
lvgl_ui_recovered/ The LVGL replacement UI
src/
main.c entry point + tick/timer setup
boxtalk.c BoxTalk client — speaks to happ_thermstat over TCP 1337
quby_bridge ↗ see quby_bridge/ — Quby-protocol bridge to keteladapter
homeassistant.c HA REST client (curtains, notify, generic)
homewizard.c HWE-P1 + HWE-WTR pollers
ventilation.c Itho Wifi addon REST client
mqtt_client.c Hand-rolled MQTT 3.1.1 subscriber (no libmosquitto)
packages.c Tap-to-dismiss banner queue, MQTT-fed
pwa_server.c HTTP + SSE server for the PWA on :10081
screen_home.c Main tile grid (heater / vent / energy / water / waste)
screen_dim.c Ambient screen (clock + temp + flame + forecast)
screen_thermostat.c "Heater detail" page (OT health, boiler flow/return)
screen_settings.c On-device config (MQTT, weather, brightness, …)
screen_schedule.c Weekly Comfort/Home/Sleep/Away scheduler
schedule.c Read/write hcb_config schedule JSON
healthcheck.c /healthz endpoint + watchdog + daily restart
settings.c Persistent /mnt/data/toonui.cfg
gen_icons.py Bitmap-icon generator (flame, drop, faucet, fan,
radiator, weather, waste) — runs at build time
pwa_server.c :10081 — serves ONLY the WASM slave UI (/ui/) + /api/*
quby_bridge/ Userspace bridge for keteladapter (replaces stock BA)
p1bridge/ Pushes HWE-P1 readings into hcb_rrd
ha_packages/ HA-side package-tracking automation (deploy.py)
web/ WASM slave UI (shell.html + build.sh) — the only
frontend pwa_server serves on :10081, at /ui/
scripts/ Helpers (ot_mode_switch.sh: proxy/wireless/off)
install.sh One-shot deploy from a Linux host to a Toon
toonshot.sh Pull /dev/fb0 over SSH → PNG (debug helper)
toontap.sh Inject a touch event into /dev/input/event1
The default LVGL screen. Big tiles for the main signals — indoor temperature and setpoint, boiler state (with the original-Toon-style radiator+flame icon when CH is firing), waste-collection reminders, live energy + gas, ventilation preset + RPM, water flow, curtains. A 5-day weather forecast lives at the bottom. Tap the heater tile for OT detail; tap the gear-corner for settings.
After the configured idle timeout the screen drops to a near-black ambient view: clock, indoor temperature with a small radiator+flame indicator when the boiler is firing, vent fan spinner on the left mirroring the flame on the right, top trash hint, top-right outdoor temperature, package banners at the top, weather strip at the bottom.
Tap the heater tile from home to see boiler-side telemetry — flow + return
water temps, modulation level, CH setpoint from the stooklijn, water
pressure, eCO₂ / TVOC / humidity. The Advanced button opens a full dump
of every OpenTherm DataId currently tracked by OTGW.
Served at http://<toon>:10081/ from the device itself (bare / redirects to
/ui/). This is the full LVGL UI compiled to WebAssembly — the exact same
screens as the device, running in any LAN browser as a same-origin slave of
the master Toon's /api/state/stream. It is the only frontend on :10081;
there is no separate simple HTML app or settings page. Optionally gated behind
the PWA login (pwa_login_* in toonui.cfg).
Every boot opens a 10-second picker letting the user choose between freetoon-lvgl and the original Eneco qt-gui. Default selection (the highlighted tile) is whichever was used last; the countdown falls through to that default. Same modal is reachable mid-session from Settings → UI mode. This means the device is never "trapped" on freetoon — anyone can switch back to stock qt-gui in seconds without SSH access.
The picker writes /mnt/data/ui_choice; a small ui_launcher.sh is
the inittab respawn: target and dispatches to either
/mnt/data/toonui or /qmf/sbin/qt-gui based on that file. Companion
sidecars (p1bridge, quby_bridge, …) are wrapped by
companion_gate.sh which polls the same file so they idle quietly
when qt-gui is the active UI.
Browse + one-tap install of third-party integrations from freetoon-integrations. Each integration is a small daemon that publishes data on BoxTalk; toonui picks it up via its existing subscribe path, and Settings → Tiles binds any installed integration to any home-tile slot.
Want to build one? See docs/INTEGRATIONS.md — the developer guide: manifest format, the BoxTalk publish protocol, a minimal C daemon, cross-compiling, and how to publish to the catalog.
┌───────────────────┐
│ happ_thermstat │ ← stock Toon process, untouched
│ (OpenTherm logic)│
└────────┬──────────┘
│ BoxTalk (XML/NUL-framed over 127.0.0.1:1337)
│ HTTP (127.0.0.1:10080/happ_thermstat?…)
▼
┌────────────┐ Quby ┌───────────────────────────┐ LVGL ┌────────────┐
│keteladapter│◀──ttymxc0──▶│ toonui │──────▶│ /dev/fb0 │
│ (boiler) │ bridged │ (this repo, on /mnt/ │ │ 1024×600 │
└────────────┘ via │ data/toonui) │ └────────────┘
quby_bridge└──┬─────┬─────┬─────┬───┘
│ │ │ │
│ │ │ └──── PWA HTTP/SSE on :10081
│ │ │
│ │ └──────── MQTT subscriber → home/packages/*
│ │
│ └──── Itho Wifi REST (vent), HWE-P1 (power/gas),
│ HA REST (curtains, notify), Buienradar
│
└──── Boxtalk client: setpoint, program, schedule
toonui is the only new long-running process on the device. The stock
qt-gui is removed from inittab so it can't fight for the framebuffer;
toonui is added with respawn so init brings it back on crash. Config
lives at /mnt/data/toonui.cfg; logs go to /var/volatile/tmp/toonui.log.
Two paths: grab a pre-built release (no toolchain required), or build
from source if you want to hack on the code. Both use the same
install.sh script — it auto-detects which layout it's running in.
- A Linux host with
bash,ssh,scp, andsshpassonPATH(apt-get install sshpass). - SSH enabled on the Toon — toggle from the stock
hcb_configmenu (Internals → SSH server). Default credentials areroot/toon. /mnt/data/is writable on stock firmware; nothing to do there.- A reachable MQTT broker is recommended (powers the packages banner and the energy/water logging path).
- Home Assistant is optional (drives curtains, Life360 location, push
notifications) — set the LLAT in
/mnt/data/ha.cfgafter install.
-
Grab the latest tarball from Releases — pick
freetoon-lvgl-vX.Y.Z.tar.gz. -
Extract somewhere on your Linux host:
tar xzf freetoon-lvgl-v0.5.0.tar.gz cd freetoon-lvgl-v0.5.0The tarball contains:
toonui # LVGL UI binary (ARMv7 hardfloat) p1bridge # HomeWizard → hcb_rrd bridge quby_bridge # (optional) Quby-protocol bridge to keteladapter ot_mode_switch.sh # helper called by the on-device Settings UI pwa/ # PWA static assets served on :10081 install.sh # the same script as in the repo -
Run the installer against your Toon:
TOON_HOST=192.168.3.212 \ P1_TOKEN=<HomeWizard v2 bearer token> \ VENT_USER=<Itho-Wifi user> VENT_PASS=<…> \ ./install.sh
The script scps every binary to
/mnt/data/, writes/mnt/data/p1bridge.conf+/mnt/data/vent.conf, upserts thetoon/p1br/qbrirows in/etc/inittab, thenkill -HUP 1s init. After ~4 seconds it pgreps the new processes so you can see them running. Idempotent — re-run to upgrade. -
(Optional) edit
/mnt/data/toonui.cfgon-device — or open the Settings tile on the LVGL UI — to point at your MQTT broker, set the weather location, dial brightness/idle timeout, etc.
To uninstall: ./install.sh --uninstall (drops inittab rows, kills
processes, removes binaries + PWA + configs).
Use this if you want to modify the UI or the bridges.
-
Clone the repo and grab the LVGL submodule:
git clone https://github.com/Ierlandfan/freetoon-lvgl.git cd freetoon-lvgl git submodule update --init --recursive -
Cross-compile for ARMv7 hardfloat. The Makefile expects a Linaro toolchain at
/tmp/qt_rebuild/linaro/(mirror ofgcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf.tar.xz). Adjust the path in the Makefiles if yours lives elsewhere.make -C lvgl_ui_recovered/src # produces lvgl_ui_recovered/build/toonui make -C p1bridge # produces p1bridge/p1bridge make -C quby_bridge # produces quby_bridge/quby_bridge (optional)
Native desktop build with the SDL2 driver is also wired up — useful for previewing UI changes without a Toon on the desk. See
lvgl_ui_recovered/src/Makefilefor thesdltarget. -
Deploy with the same script — it picks the in-tree build paths automatically:
TOON_HOST=192.168.3.212 P1_TOKEN=… ./install.sh
Edit lvgl_ui_recovered/src/settings.c defaults or /mnt/data/toonui.cfg
on-device to point at your own IPs.
The thermostat path is the path on this install — happ_thermstat →
keteladapter over the Quby protocol, with quby_bridge patched to mimic
the discontinued BoilerAdapter bit-for-bit. OTGW runs in GW=1 (relay) mode
and forwards the same OpenTherm frames the original BA produced, so the
boiler's CH/DHW state machines see no change from when Eneco's hardware was
in the loop. Verified by warming a 17.9 °C room to 20.0 °C unattended.
The repo is opinionated to one physical install (the author's), and parts of the integration glue (HA entities, MQTT topic names, Itho user/password location) are tuned to that environment. The LVGL UI and the core BoxTalk / Quby / OTGW plumbing are portable; the per-integration screens are easy to disable.
freetoon-lvgl is an independent alternative UI by Ierlandfan. Special
thanks to Quby / Eneco for the underlying Toon platform and the BoxTalk /
Quby protocol structure this UI builds on.
- OTGW — Robert van den Breemen's HTTP-firmware fork of the OpenTherm Gateway is the boiler-side workhorse.
- Itho Wifi — Arjen Hiemstra's add-on board + REST API made vent control trivial.
- HomeWizard — open
/api/v1/dataon the HWE-P1 / HWE-WTR. - LVGL — the embedded UI library this app is built on (MIT, © LVGL Kft).
- QR-Code-generator — © Project Nayuki (MIT); LodePNG — © Lode Vandevenne (zlib). Both bundled via LVGL.
- Toon community — the reverse-engineering work scattered across hacktoon and various Tweakers threads that mapped out happ_thermstat, hcb_config and the Quby protocol.
freetoon-lvgl (the UI and integration glue authored in this repository) is released under the MIT License — see LICENSE.
This repository contains no Eneco / Quby proprietary code. The stock Toon
binaries (happ_thermstat, hcb_config, keteladapter firmware, …) remain
the property of Eneco / Quby; nothing here redistributes or modifies them.
Bundled third-party components (LVGL, QR-Code-generator, LodePNG) retain their
own licenses and copyrights as noted in LICENSE.


