Catalog PyPI
PACKAGE EXPLORERView in registry

aiocometwifi

PyPI

Async library for Eurotronic Comet WiFi thermostats over a caller-supplied MQTT transport.

Versions collected1
Direct dependencies9From release 0.1.0
Last observed21 Oct, 11:44 UTC

README

Version 0.1.0 documentation

aiocometwifi

Async Python library for Eurotronic Comet WiFi radiator thermostats, talking to them over MQTT through a transport the caller owns. The library never opens a connection itself: hand it the publish, subscribe and unsubscribe functions of your MQTT client, and drive one Thermostat instance per device.

It was written for the Home Assistant integration, whose MQTT integration already owns the broker connection, but works with any client (the quick start below uses paho-mqtt).

  • Zero runtime dependencies, fully typed (py.typed), Python ≥ 3.14.
  • Reads setpoint, ambient temperature, offset, battery level and configuration flags.
  • Sets the setpoint, turns heating off or fully on, toggles key lock, key lock plus, mirrored display and DST.
  • Answers the device's connection test so it stays online.

✅ Prerequisites

  1. An MQTT broker that accepts anonymous clients on your LAN (the thermostats do not support authentication). The official Home Assistant Mosquitto add-on does not qualify. See Broker.
  2. Each thermostat must be reconfigured for LAN-only operation, pointing at that broker, using the setup-thermostat command of comet-wifi-communicator. See Device setup.
  3. Python ≥ 3.14 and an MQTT client of your own (paho-mqtt, aiomqtt, Home Assistant's MQTT integration, …), the library only adapts to it.

📡 Broker

The thermostats connect to an MQTT broker anonymously and cannot be given credentials. Run a broker with allow_anonymous true. Restrict what anonymous clients may access with an ACL (the devices only need 01/#) and keep the broker off the public internet. The official Home Assistant Mosquitto add-on refuses anonymous clients by design, so a separate broker, or a bridge to it, is required.

🔧 Device setup

Out of the box a Comet WiFi talks to the manufacturer's cloud. For this library it has to be reconfigured for LAN-only operation, pointed at your own broker. This has to be done once and with the setup-thermostat command of comet-wifi-communicator:

pip install comet-wifi-communicator
setup-thermostat --wifi-ssid <ssid> --wifi-password <password> --mqtt-server-ip <broker ip>

aiocometwifi takes over once the device is on the broker.

🚀 Quick start

import asyncio

import paho.mqtt.client as mqtt
from aiocometwifi import MqttClient, SubscribeCallback, SubState, Thermostat


class PahoTransport:
    """Adapt a running paho client to the callables MqttClient expects."""

    def __init__(self, client: mqtt.Client, loop: asyncio.AbstractEventLoop) -> None:
        self._client = client
        self._loop = loop

    async def publish(self, topic: str, payload: str, qos: int, retain: bool) -> None:
        self._client.publish(topic, payload, qos, retain)

    async def subscribe(
        self, sub_state: SubState | None, topic: str, callback: SubscribeCallback
    ) -> SubState:
        def on_message(
            _client: mqtt.Client, _userdata: object, msg: mqtt.MQTTMessage
        ) -> None:
            # paho calls this on its network thread; the library runs on the event loop.
            self._loop.call_soon_threadsafe(callback, msg.topic, msg.payload.decode())

        self._client.message_callback_add(topic, on_message)
        self._client.subscribe(topic)
        return {"topic": topic}

    async def unsubscribe(self, sub_state: SubState) -> None:
        self._client.message_callback_remove(sub_state["topic"])
        self._client.unsubscribe(sub_state["topic"])


async def main() -> None:
    client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2)
    client.connect("broker.local")
    client.loop_start()

    transport = PahoTransport(client, asyncio.get_running_loop())
    mqtt_client = MqttClient(
        transport.publish, transport.subscribe, transport.unsubscribe
    )
    thermostat = Thermostat(mqtt_client, "AA:BB:CC:DD:EE:FF")

    await thermostat.connect()  # subscribes; nothing is sent yet
    await thermostat.update_heating_values()
    await asyncio.sleep(5)  # replies arrive asynchronously, see below
    print(thermostat.temperature_ambient, thermostat.setpoint, thermostat.is_heating)

    await thermostat.set_setpoint_temperature(21.0)

    await thermostat.disconnect()
    client.loop_stop()


asyncio.run(main())

Requests and replies are separate MQTT messages. update_*() publishes a request and returns immediately; the reply updates the thermostat's properties when it arrives, typically within a few seconds. Awaiting the reply itself is planned for a later release.

Commands (set_setpoint_temperature, turn_off, turn_fully_on, enable_*/disable_*) raise CometWifiConnectionError until the device has been heard from. A reply or one of its periodic connection tests sets connected. The device's last will clears it again.

set_setpoint_temperature clamps to 8.0–28.0 °C (TEMPERATURE_SETPOINT_MIN/_MAX) and truncates to the device's 0.5 °C steps.

⚠️ Errors

Everything raised across the API derives from CometWifiError:

ExceptionRaised when
CometWifiConnectionErrorA command is issued before the device has answered, or after its last will.
CometWifiValueErrorAn argument is rejected (malformed MAC, negative temperature, bad hex). Also a ValueError.
CometWifiTimeoutErrorReserved for awaited replies (later release).
CometWifiInvalidResponseErrorReserved. Malformed replies are currently logged, not raised.

Errors from the transport itself (publish failing, broker gone) propagate unchanged from the call that triggered them. One exception is the automatic ping reply, where they are logged.

📖 Communication Protocol

Topics are 01/<MAC>/<group>/<code> with the MAC as twelve uppercase hex digits and the group S for commands (host → device) or V for replies (device → host). Payloads are ASCII hex with a # prefix; temperatures are half-degrees (#2A = 21.0 °C).

TopicDirectionContent
S/AFHost → DeviceValue request, 32-bit mask of what to report
S/A0Host → DeviceWrite setpoint (#0F = off, #39 = fully on)
S/A3Host → DeviceWrite configuration flags
S/XXHost ↔ DeviceConnection test: the device publishes a ping, the host must answer #COMM-TEST on the same topic
V/A0, V/A1, V/A2Host ← DeviceSetpoint, ambient temperature, offset
V/A3Host ← DeviceConfiguration (key lock, key lock plus, mirrored display, DST)
V/A6Host ← DeviceBattery level
V/XXHost ← DeviceLast will, the device disconnected

Because the pong goes out on the very topic the library is subscribed to, the broker echoes it back. The library recognizes its own echoes and rate-limits pongs, so it never loops.

🔗 Relation to comet-wifi-communicator

comet-wifi-communicator is the same protocol with a bundled paho client, for scripts and standalone use, and it provides the setup-thermostat command above. aiocometwifi is for hosts that already have an MQTT connection and an event loop, Home Assistant first of all.

🛠️ Development

python -m venv .venv && source .venv/bin/activate
pip install -e . -r requirements_dev.txt
pre-commit install
pytest --cov --cov-fail-under=90 --cov-report=term-missing
ruff check . && ruff format --check . && mypy

Tests run against a fake transport (tests/conftest.py) so no broker is needed. Commits follow Conventional Commits. Releases are cut from main by python-semantic-release.

⚖️ Disclaimer

This is an independent, community-developed project. It is not affiliated with, endorsed by or supported by Eurotronic. Eurotronic and Comet WiFi are trademarks or trade names of their respective owners and are used here only to identify the devices this library communicates with. Use at your own risk. See the license for the warranty disclaimer.

While I am somewhat skeptical towards blindly allowing AI for coding and do not support the way major companies are selling and training their models, the usefulness for reviewing and improving software cannot be denied. Therefore, for enhancing the quality of parts of this library and finding issues, AI (mostly Claude Opus 5) was utilized.

📜 License

GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007, see the license file.

Downloads & popularity

Awaiting observations

No metric observations yet

Download counts and popularity will appear here as the collector receives them.

Versions 1

Collected releases
VersionPublishedDirect dependenciesOpen version
0.1.011 Sept 20269

Package advisories

0
No matching advisories collected

This describes our collected data; it is not a security assessment.

Repository

ACCESSIBLE
zerog981/aiocometwifi

Shared repository data · GitHub

License detected in repository
GPL-3.0
License branch / ref
main
Last push
1 Oct 2026
Archived
No
Contributor identities
— · partial collection

Public sponsor counts have not been collected. These signals belong to the repository owner.

Downloads & popularity

0count
Gauge · observed snapshots21 Oct 2026
10.50
2026-10-21: 0 · observed 2026-10-21T21:14:09.4708314Z
21 Oct21 Oct

github · 1 observations shown. Missing days are unknown.

View observed values
Date / source periodValueObserved at
2026-10-2102026-10-21T21:14:09.4708314Z

Metadata history

Meaningful changes
Current collected state Current

Observed since 21 Oct, 11:44 UTC

No superseded metadata states collected. Unchanged observations do not create new revisions.

Record identity

package:679de1a80680fb9cc8ceb09d103aa79b16aec2d8307d850b9c46d8619ac735ce