Python client for Kospel C.MI electric heater HTTP API
Project description
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
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
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file kospel_cmi_lib-1.0.0b1.tar.gz.
File metadata
- Download URL: kospel_cmi_lib-1.0.0b1.tar.gz
- Upload date:
- Size: 35.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
da068d711aae1959300ef8667403b3b9ceee95363d4d5caf4db84c293f3fe134
|
|
| MD5 |
8c1cfe74b253d4e8f027a36f71946605
|
|
| BLAKE2b-256 |
0533250af745c7ce4a79b9aa7eb51dea1063a92fa222f472c447d1b0e0deaa86
|
Provenance
The following attestation bundles were made for kospel_cmi_lib-1.0.0b1.tar.gz:
Publisher:
release-pypi.yml on JanKrl/kospel-cmi-lib
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
kospel_cmi_lib-1.0.0b1.tar.gz -
Subject digest:
da068d711aae1959300ef8667403b3b9ceee95363d4d5caf4db84c293f3fe134 - Sigstore transparency entry: 1280956127
- Sigstore integration time:
-
Permalink:
JanKrl/kospel-cmi-lib@df45d7796d64bd0812294e881c266d125a97e603 -
Branch / Tag:
refs/tags/v1.0.0-beta.1 - Owner: https://github.com/JanKrl
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release-pypi.yml@df45d7796d64bd0812294e881c266d125a97e603 -
Trigger Event:
push
-
Statement type:
File details
Details for the file kospel_cmi_lib-1.0.0b1-py3-none-any.whl.
File metadata
- Download URL: kospel_cmi_lib-1.0.0b1-py3-none-any.whl
- Upload date:
- Size: 47.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b813954c2e58e7deb61ae06cf103d1a7fe25255d80eff1027704ea9b3493363a
|
|
| MD5 |
7b34bda4cee872e3ded6966742e693e1
|
|
| BLAKE2b-256 |
04f7ea4c8ecf1380dba072265a7b405a9393e1677f1febbb9c7252376e0e71d3
|
Provenance
The following attestation bundles were made for kospel_cmi_lib-1.0.0b1-py3-none-any.whl:
Publisher:
release-pypi.yml on JanKrl/kospel-cmi-lib
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
kospel_cmi_lib-1.0.0b1-py3-none-any.whl -
Subject digest:
b813954c2e58e7deb61ae06cf103d1a7fe25255d80eff1027704ea9b3493363a - Sigstore transparency entry: 1280956130
- Sigstore integration time:
-
Permalink:
JanKrl/kospel-cmi-lib@df45d7796d64bd0812294e881c266d125a97e603 -
Branch / Tag:
refs/tags/v1.0.0-beta.1 - Owner: https://github.com/JanKrl
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release-pypi.yml@df45d7796d64bd0812294e881c266d125a97e603 -
Trigger Event:
push
-
Statement type: