I have three Eurotronic Comet WiFi thermostats. They work fine. The app works fine. But every morning I’d watch my Home Assistant logs fill up with connection attempts going out to some mqtt3.eurotronic.io endpoint and think: this is not how I want my heating to work. My radiators should not depend on a server somewhere in Germany staying online. So I fixed it.
This post is the writeup I wish existed when I started. It took a weekend of packet sniffing, failed HACS integrations, and one very satisfying Pi-hole rule to get here.
What’s Actually Inside These Things
The Comet WiFi runs on a Dialog Semiconductor DA16200 WiFi chip. That’s important because it immediately rules out a few popular options: no Tuya compatibility, no custom firmware path, nothing in ESPHome. The DA16200 is not an ESP8266. You’re not flashing this thing.
What the thermostat does do is speak MQTT — and only MQTT — to a fixed set of cloud hostnames: mqtt.eurotronic.io, mqtt1.eurotronic.io through mqtt5.eurotronic.io. No local API. No mDNS. No REST endpoint. Just MQTT, pointed permanently at the cloud.
The moment I confirmed this with Wireshark I knew exactly what to do.
The Interception: Pi-hole + Local Mosquitto
The plan is elegant: intercept the DNS queries for those cloud hostnames and point them at your own Mosquitto broker. The thermostats never know the difference. They connect, authenticate, and start chattering away — just to your broker instead of Eurotronic’s.
In Pi-hole, add custom DNS records for every variant:
mqtt.eurotronic.io → 192.168.0.10
mqtt1.eurotronic.io → 192.168.0.10
mqtt2.eurotronic.io → 192.168.0.10
mqtt3.eurotronic.io → 192.168.0.10
mqtt4.eurotronic.io → 192.168.0.10
mqtt5.eurotronic.io → 192.168.0.10
Replace 192.168.0.10 with whatever IP your Mosquitto instance is running on. Don’t skip any of the numbered variants — the firmware will try several of them before giving up, and you want all paths leading home.
On the Mosquitto side, since all devices in a single Eurotronic installation share the same MQTT username and password (yes, really — one credential set for all your thermostats), and since you now own the broker, you can simply enable anonymous access:
allow_anonymous true
listener 1883
Counterintuitively, this is actually more secure than the cloud setup. Before, your thermostat data was transiting someone else’s infrastructure. Now it never leaves your LAN.
The MQTT Protocol
Once the thermostats are talking to your broker, subscribe to # and watch the traffic. You’ll see topics structured like this:
02/PREFIX/MAC/V/A0 ← values coming FROM the device
02/PREFIX/MAC/S/A0 ← commands going TO the device
The PREFIX and MAC are device-specific — just watch the broker traffic after your thermostats connect and you’ll spot them immediately. The registers you actually care about:
- A0 — target temperature (setpoint)
- A1 — current measured temperature
- A5 — window open detection
- A6 — battery level
Temperature values are encoded as hex, prefixed with #, where the hex value equals temperature × 2. So 21.0°C becomes #2a (42 in decimal, 0x2a in hex). 18.5°C is #25. Weird encoding, but consistent.
For polling, publish to the S/AF topic. Two useful payloads:
#01000000— returns the current active setpoint cleanly#02000000— triggers an immediate current temperature report
You might find documentation elsewhere suggesting #0b on S/A0 for polling. I did too. It’s slow, unreliable, and sometimes returns schedule data mixed in with the current value. Avoid it. The AF approach is much cleaner.
One critical rule: never use the retain flag on any messages you publish. Retained messages get replayed to the thermostat every time it reconnects — which means your retained setpoint command will constantly override whatever the device’s internal heating schedule is trying to do. It’s a subtle bug that’ll have you wondering why your thermostat is ignoring its schedule.
Home Assistant Integration — Skip the HACS Plugin
There’s a HACS integration called comet_wifi_integration. I tried it. It has bugs, and more critically it uses QoS 2 for MQTT delivery, which HA’s MQTT client handles poorly. Messages get dropped, entities get stuck, it’s frustrating.
The better approach: use Home Assistant’s built-in MQTT climate entity via YAML. It’s rock solid and gives you full control.
In your mqtt.yaml:
climate:
- name: Wohnzimmer
temperature_command_topic: "02/PREFIX/MAC/S/A0"
temperature_command_template: >-
{{ "#%02x" % ((value | float * 2) | int) }}
temperature_state_topic: "02/PREFIX/MAC/V/A0"
temperature_state_template: "{{ int(value[1:3],base=16)/2 }}"
current_temperature_topic: "02/PREFIX/MAC/V/A1"
current_temperature_template: "{{ int(value[1:3],base=16)/2 }}"
The templates handle the hex encoding automatically. The command template converts a float like 21.0 into #2a. The state templates do the reverse. Swap PREFIX and MAC with your actual device values, repeat for each thermostat.
For polling, add an automation that fires every 15 minutes:
alias: Poll Comet WiFi Thermostats
trigger:
- platform: time_pattern
minutes: "/15"
action:
- service: mqtt.publish
data:
topic: "02/PREFIX/MAC/S/AF"
payload: "#01000000"
- service: mqtt.publish
data:
topic: "02/PREFIX/MAC/S/AF"
payload: "#02000000"
Battery and window sensors follow the same pattern using MQTT sensor and binary_sensor entities — same topic structure, same hex decoding.
One Thing to Keep in Mind
The Comet WiFi has an internal heating schedule that runs independently of anything you do via MQTT. If you set a temperature through Home Assistant, it’ll hold — until the thermostat’s next scheduled time slot kicks in and overrides it. This is the same behavior you’d get with the official app. It’s not a bug in your setup; it’s just how the device works. Plan your automations around it, or accept that the schedule has the final word.
The Result
Three thermostats, fully local. Real-time temperature readings in Home Assistant. Target temperature control. Battery monitoring. Window-open detection. Zero cloud dependency. The whole setup survives internet outages without a hiccup.
How We Got Here
Full disclosure: I built this integration in a pair-programming session with Claude Code, Anthropic’s CLI coding assistant. Claude handled the broker setup, DNS configuration, and initial HA integration code — but the existing community documentation and the HACS plugin both had gaps that only showed up during real-world testing. The #0b polling command that everyone recommends? Unreliable. The QoS 2 MQTT subscriptions in the custom integration? Silently broken in HA. The retain flag? A landmine waiting to blow up your heating schedule.
Each of these issues required hands-on debugging — me watching thermostat displays, checking if temperatures actually changed, opening windows to test sensors — while Claude analyzed the MQTT broker logs and iterated on the configuration. The S/AF polling commands and the distinction between #01000000 and #02000000 came from sniffing what the official Eurotronic app actually sends, which turned out to be completely different from what the community had documented.
It took more reverse engineering than it should have — Eurotronic publishes nothing about this protocol — but once you have the DNS intercept in place and understand the hex encoding, the rest falls into place quickly. If you’re sitting on a pile of Comet WiFi thermostats wondering why there’s no clean local integration, this is your path forward.
P.S. — The Jinja2 Trap
If your battery sensors show suspiciously low values, check your template. In Python, int("3C", 16) means „parse as base 16“ and returns 60. In Jinja2, the same syntax means „use 16 as the default if parsing fails.“ The correct Jinja2 for hex conversion is {{ value[1:] | int(base=16) }}, not {{ int(value[1:], 16) }}. This applies to battery values but not temperatures — the temperature registers happen to use only digits 0-9 in their hex encoding at typical room temperatures, so the bug is invisible until a value contains A-F.
