kospel-cmi-lib
Python client for the Kospel C.MI electric heater HTTP API.
This library provides a Python client for controlling Kospel C.MI electric heaters via their HTTP API. It is designed for integration with Home Assistant and other automation systems, and supports device discovery, register-based control, and offline development with a YAML simulator.
Features
- Async-first: Built on
asyncioandaiohttpfor non-blocking I/O - Type-safe: Strict type hinting throughout
- Device-specific API: Explicit properties and async setters on
EkcoM3 - Simulator-capable: Full simulator for offline development and testing (no hardware required)
- Protocol-based: Decoder/encoder interfaces via Python
Protocoltypes - Device discovery:
probe_device()anddiscover_devices()to find Kospel devices on the network (no device_id required)
Implemented Features
- Heater mode control (OFF, SUMMER, WINTER, PARTY, VACATION, MANUAL)
- CWU (water) mode and temperatures (economy, comfort)
- Manual heating temperature
- Device discovery (CLI
kospel-discover+ Python API) - Register scanner and live scanner tools (
kospel-scan-registers,kospel-scan-live) - YAML backend for offline testing (no hardware required)
Installation
# With uv (recommended)
uv add kospel-cmi-lib
# With pip
pip install kospel-cmi-lib
Quick Start
- Install:
uv add kospel-cmi-liborpip install kospel-cmi-lib - Discover device: Run
kospel-discoveror useprobe_device(session, "192.168.x.x")to getapi_base_url - Connect and read: Create
EkcoM3withHttpRegisterBackend(session, api_base_url)and callrefresh()
Usage
Create a register backend (HTTP or YAML) and pass it to EkcoM3.
When using HttpRegisterBackend, call aclose() or use the controller as an
async context manager to release the HTTP session when done.
Recommended: async context manager (resources released automatically):
import asyncio
import aiohttp
from kospel_cmi.controller.device import EkcoM3
from kospel_cmi.kospel.backend import HttpRegisterBackend, YamlRegisterBackend
from kospel_cmi.registers.enums import HeaterMode
async def main() -> None:
api_base_url = "http://192.168.1.1/api/dev/65" # Replace with your heater URL
async with aiohttp.ClientSession() as session:
backend = HttpRegisterBackend(session, api_base_url)
async with EkcoM3(backend=backend) as controller:
await controller.refresh()
print(controller.heater_mode) # Read property
await controller.set_heater_mode(HeaterMode.MANUAL) # Write immediately
# Session and controller resources released here
asyncio.run(main())
Alternative: explicit aclose() (for long-lived integrations):
controller = EkcoM3(backend=HttpRegisterBackend(session, api_base_url))
try:
await controller.refresh()
# ... use controller ...
finally:
await controller.aclose()
For offline development or tests, use the YAML backend (no HTTP, no close needed):
backend = YamlRegisterBackend(state_file="/path/to/state.yaml")
controller = EkcoM3(backend=backend)
await controller.refresh()
Device Discovery
CLI — scan network and list devices (no config needed):
kospel-discover # Scans common subnets
kospel-discover 192.168.101.0/24 # Scan specific subnet
Python API:
import aiohttp
from kospel_cmi import probe_device, discover_devices
async with aiohttp.ClientSession() as session:
info = await probe_device(session, "192.168.101.49")
if info:
print(f"Found: {info.serial_number}, {info.api_base_url}")
found = await discover_devices(session, "192.168.101.0/24")
for device in found:
print(device.host, device.serial_number, device.api_base_url)
Setting Heater Mode
import asyncio
import aiohttp
from kospel_cmi.controller.device import EkcoM3
from kospel_cmi.kospel.backend import HttpRegisterBackend
from kospel_cmi.registers.enums import CwuMode, HeaterMode
async def main():
async with aiohttp.ClientSession() as session:
backend = HttpRegisterBackend(session, "http://192.168.1.1/api/dev/65")
async with EkcoM3(backend=backend) as controller:
await controller.refresh()
# Manual heating: mode + temperature in one call (recommended)
await controller.set_manual_heating(22.0)
# Or use individual setters (each writes immediately)
await controller.set_heater_mode(HeaterMode.WINTER)
await controller.set_manual_temperature(22.0)
# Water: set mode and temperature separately
await controller.set_water_mode(CwuMode.COMFORT)
await controller.set_water_comfort_temperature(38.0)
await controller.set_water_economy_temperature(35.0)
asyncio.run(main())
Documentation
Module Documentation
Module-specific documentation is co-located with the code (GitHub automatically displays these when browsing directories):
- kospel/ - HTTP API endpoints and protocol
- registers/ - Register encoding, decoding, and mappings
- controller/ - EkcoM3 device class
- tools/ - Register scanner and live scanner for reverse-engineering
Project Documentation
- Documentation Index - Overview of all docs and suggested reading order
- Development Guide - Contributing and extending the library
- Architecture - System design, layers, components, and data flow
- Technical Specs - Implementation details, data formats, protocols, testing, and coding standards
- AI Context (AGENTS.md) - Entry point and cheat-sheet for AI coding assistants (Copilot, Claude, Antigravity)
Known Limitations
- No authentication (assumes local network access)
- HTTP only (no HTTPS)
- Single device config (
kospel_cmi_standard) — more variants planned for v2
Roadmap
v1.0.0 Engine & Explorer
- Local control - basic device functions can be operated using the library
- Robust interface - an interface for 3rd party tools (i.e., Home Assistant integration)
- Reverse-engineering toolset -
kospel-scan-registersandkospel-scan-livefor exploring registers
v2.0.0 Plug & Play for Kospel ecosystem
- Support multiple device types
- Device discovery
- Advanced state management (error and warning flags, fault detection, debug)
References
This library was reverse-engineered from JavaScript code used in the heater's web interface. Key findings:
- Register encoding uses little-endian byte order
- Flag bits are used for boolean settings within registers
- Temperature and pressure values are scaled for precision
- Read-Modify-Write pattern is required for setting flag bits
License
Apache License 2.0
Metadata
Release files for kospel-cmi-lib 2.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| kospel_cmi_lib-2.0.0.tar.gz | 38.1 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| kospel_cmi_lib-2.0.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 88.4 kB
Release files / kospel_cmi_lib-2.0.0.tar.gz
| Download URL | kospel_cmi_lib-2.0.0.tar.gz |
|---|---|
| Size | 38.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
a39a38375265988d18c5623d6cae954b03c4f11e2895b84a745d5f6bf42e428d
|
|
BLAKE2b-256 checksum How to use checksums |
d0dc5634c4717cea13e75ac649737abbe91b7f4e3ccc3a44e9a6e3e4330769b5
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 1, 2026.
Transparency logRelease files / kospel_cmi_lib-2.0.0-py3-none-any.whl
| Download URL | kospel_cmi_lib-2.0.0-py3-none-any.whl |
|---|---|
| Size | 50.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
8562420fd59010baa25544aeebda6e4d568371c4f2d41365aebe8f71f28e1efe
|
|
BLAKE2b-256 checksum How to use checksums |
e587979f539346a93ba945d85c224003b72b28d92c9e289786848e97263bc699
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 1, 2026.
Transparency log