Proxy EKM Metering devices onto the Electrification Bus (eBus / Homie 5)
Project description
ekm-proxy
Publishes EKM Metering devices (Omnimeter meters, and ioStack IO/sensor modules) onto the Electrification Bus (eBus) as Homie 5 devices.
It is an eBus proxier (see the eBus proxy.md pattern): EKM meters do not speak eBus natively, so this service reads their data and republishes each discrete device onto eBus/Homie 5. It is not an "adapter" in the vague sense; it is a proxy with a specific, first-class role in the eBus model.
Status: scaffold / MVP. The cloud-MQTT source is the first target; other sources are stubbed with a common interface (see Variants).
What it emits
- Each Omnimeter → one
energy.ebus.device.circuitdevice bearing the eBusmeter(anddemand) capability plusinfo. Behind-the-meter sub-metering is modeled as a capability on a host circuit, not a standalone device type; per-conductor legs are-a/-b/-cproperty suffixes on the one device. - Each ioStack → an IO/sensor device. Its non-electrical sensors (e.g. 1-Wire temperature) are deferred pending the eBus
homie/5domain-partitioning decision (spec issue SPEC-611) and are not emitted yet. - Device IDs follow the proxier convention
{proxier-id}-{proxied-id}(the meter/ioStack serial is the proxied id).
Full context, the field-mapping rationale, and the "why MVP-first is safe" spec-dependency analysis are maintained in the eBus specification project's design notes.
Architecture: shared core + pluggable sources
All variants share the same core (EKM→eBus field mapping + Homie 5 emitter) and differ only in the source (how EKM data is acquired). Ports-and-adapters:
source (acquire EKM data) -> core.mapping (EKM fields -> eBus props) -> core.emitter (Homie 5 -> eBus MQTT)
ekm_proxy/mapping.py— the EKM-field → eBus-property mapping (source-agnostic).ekm_proxy/emitter.py— Homie 5 device/description + value publisher to the eBus broker.ekm_proxy/sources/— pluggable data sources; each yields normalizedreadMeter-style readings.
Variants (sources)
Recall there will be several deployment variants of this proxy; they are all just different sources/ behind the same core:
| Source | Path | Cloud? | Status | Notes |
|---|---|---|---|---|
| cloud-mqtt (MVP) | sources/cloud_mqtt.py |
yes | done (MVP) | Subscribe the EKM Push3 Home Assistant MQTT discovery stream (homeassistant/device/+/config + ekmdata/+/state). Curated field subset (only what the operator exposed as HA entities). |
| rs485 | sources/rs485.py |
no | planned (full-detail path) | Talk RS-485 directly to Omnimeters (via EKM's ekmmeters.py). Full field detail, fully offline, and no Push3 required (just a low-cost USB-RS485 adapter). |
| cloud-api | sources/cloud_api.py |
yes | TBD if needed | Poll the EKM cloud readMeter API (full field set). See the note below on why this is deferred. |
| local-api | sources/local_api.py |
no | TBD if needed | Poll a Push3's onboard LAN readMeter API (http://<push3-ip>/readMeter?...), fully offline. Requires a Push3. See the note below. |
| push3-native | (separate) | no | future | Run on the Push3 itself. Platform/language TBD; likely not this Python package. Placeholder documented in sources/push3_native.py. |
cloud-mqtt and the readMeter-based sources (cloud-api, local-api) share this Python package. The two readMeter variants are TBD if needed: their advantages over cloud-mqtt are full field fidelity and offline (LAN) operation, but rs485 already delivers both without a Push3 (just a low-cost USB-RS485 adapter), so it is the preferred path to full detail. The readMeter sources earn their place mainly where a Push3 is already deployed and you want to reuse its aggregation without wiring to the RS-485 bus. push3-native is a separate consideration because it depends on the Push3's on-device runtime.
eBus spec status
This proxy composes existing eBus capabilities (info / meter / demand) under the proxy.md pattern. It emits energy.ebus.device.circuit bearing the meter capability, conforming to the eBus specification's capability-on-host model for behind-the-meter sub-metering (data-models/circuit.md, capabilities/meter.md). Instrument nameplate (vendor-name / serial-number / model / firmware-version) rides on the circuit's info per capabilities/info.md §"Nameplate versus conductor identity", with the EKM serial mirrored into info/external-ids as ekm:<serial>.
How to run
Step-by-step for the MVP cloud_mqtt variant. Anyone with MQTT broker credentials can run it; no EKM hardware is required. It subscribes to the Home Assistant MQTT discovery stream an EKM Push3 publishes, translates each meter, and republishes it as an eBus (Homie 5) device.
1. What you need
- Python 3.10 or newer, with
pip(a virtual environment is recommended). - The Mosquitto client tools (
mosquitto_sub/mosquitto_pub) for watching the output. Optional, but handy for step 5. - Access to two MQTT brokers, which may be the same broker:
- a source broker carrying an EKM Push3's Home Assistant discovery topics (
homeassistant/device/+/config,ekmdata/+/state,ekmdata/+/availability). This is any broker an EKM Push3 publishes to (your own Push3, or a shared/demo broker). You supply the MQTT credentials. - a target eBus broker to publish the translated Homie 5 devices to. Can be the same broker, or a separate/local one. To stand up a local eBus broker on your own machine, see broker-quickstart.
- a source broker carrying an EKM Push3's Home Assistant discovery topics (
2. Install
ekm-proxy is published on PyPI, so the quickest install is:
python3 -m venv .venv && source .venv/bin/activate
pip install ekm-proxy
That installs the ekm-proxy command plus its dependencies: the eBus library ebus-sdk and paho-mqtt. For the RS-485 source, install the extra with pip install "ekm-proxy[rs485]".
To hack on the proxy itself, install from a source checkout instead:
git clone https://github.com/electrification-bus/ekm-proxy.git
cd ekm-proxy
python3 -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"
If your Python package index does not carry ebus-sdk, install it from the electrification-bus GitHub organization.
3. Configure
cp config/config.example.toml config/config.toml
Edit config/config.toml. The two sections you set are the source ([source.cloud_mqtt], the broker with the EKM discovery stream) and the target ([ebus], where translated devices are published):
[source]
kind = "cloud_mqtt"
[source.cloud_mqtt]
host = "${EKM_MQTT_HOST}" # broker carrying the Push3 HA discovery stream
port = 1883
username = "${EKM_MQTT_USER}" # password comes from EKM_MQTT_PASS
[ebus]
host = "${EBUS_MQTT_HOST}" # where translated eBus devices are published
port = 1883
username = "${EBUS_MQTT_USER}" # password comes from EBUS_MQTT_PASS
proxier_id = "ekm-proxy-1" # your bridge id; devices are {proxier-id}-{serial}
vendor_name = "ekm-proxy"
name = "EKM Proxy"
Provide the credentials via environment variables (never commit secrets):
# source broker (the EKM Push3 / demo broker):
export EKM_MQTT_HOST=<source-broker-host>
export EKM_MQTT_USER=<source-username>
export EKM_MQTT_PASS=<source-password>
# target eBus broker (may be the same host):
export EBUS_MQTT_HOST=<target-broker-host>
export EBUS_MQTT_USER=<target-username>
export EBUS_MQTT_PASS=<target-password>
To publish back to the same broker you read from (the simplest setup), point the EBUS_MQTT_* values at the same host and credentials as the EKM_MQTT_* ones.
4. Run
ekm-proxy --config config/config.toml
It connects, discovers each EKM meter from the retained HA discovery messages, and publishes one eBus device per meter. Leave it running; it updates property values as new readings arrive.
5. Verify
Watch the eBus tree on the target broker:
mosquitto_sub -h <target-broker-host> -p 1883 -u <user> -P <pass> -t 'ebus/5/#' -v
You should see:
ebus/5/<proxier-id>/...: the bridge (energy.ebus.device.bridge) root device.ebus/5/<proxier-id>-<serial>/...: one circuit (energy.ebus.device.circuit) per EKM meter, each with a typed$description, a$state, aninfocapability (nameplate: vendor, serial, plusexternal-ids = ekm:<serial>), and ametercapability (imported-energy,active-power,current-a, ...) in eBus-canonical units (energy in Wh, power in W, current in A).
Contributing
See CONTRIBUTING.md for how to file issues, propose changes, and the lint/test expectations (ruff + pytest).
Releasing
Published to PyPI via Trusted Publishing (GitHub Actions OIDC, no stored token): the publish.yml workflow builds and uploads on any v* tag. To cut a release:
- Bump the version in both
pyproject.toml([project].version) andsrc/ekm_proxy/__init__.py(__version__); thesetup.pyshim reads the latter for the Yocto/kirkstone build path, so they must agree. Update theCHANGELOG.md[Unreleased]section. - Commit, then tag and push:
git tag v0.1.0 && git push origin v0.1.0
- The workflow runs the tests, builds the sdist + wheel, and publishes. Confirm the new version on PyPI and that the badge updates.
The setup.py shim exists so the older setuptools pinned in Yocto kirkstone builds a correct wheel (all modules plus the ekm-proxy entry point) from the sdist; the modern build path uses the PEP 621 metadata in pyproject.toml.
License
MIT (c) 2026 Clark Communications Corporation.
References (EKM public docs)
- EKM Push Open API / MQTT / RS-485: https://documents.ekmmetering.com/api-docs/
- Push3 LAN UI + local
readMeterAPI: https://help.ekmmetering.com/support/solutions/articles/6000236439 - Omnimeter Pulse v.4, ioStack, Push3: https://www.ekmmetering.com/
Captured copies of these docs and live data captures are kept private (EKM's docs are copyrighted and are not redistributed here).
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 ekm_proxy-0.1.0.tar.gz.
File metadata
- Download URL: ekm_proxy-0.1.0.tar.gz
- Upload date:
- Size: 25.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2b2b3c5f8a5974ce032495ec86d45c560546a60b2df8898c8ace3464b773af88
|
|
| MD5 |
379b45506abd434cd2b4b28e1af67fbc
|
|
| BLAKE2b-256 |
7244673a6072087934869617dab76da3017c220e6b1f01efa2ad1de6e31e5b04
|
Provenance
The following attestation bundles were made for ekm_proxy-0.1.0.tar.gz:
Publisher:
publish.yml on electrification-bus/ekm-proxy
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ekm_proxy-0.1.0.tar.gz -
Subject digest:
2b2b3c5f8a5974ce032495ec86d45c560546a60b2df8898c8ace3464b773af88 - Sigstore transparency entry: 2146720028
- Sigstore integration time:
-
Permalink:
electrification-bus/ekm-proxy@9880a53153a3f035d8eaa3a1dad067ef9e0088a9 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/electrification-bus
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@9880a53153a3f035d8eaa3a1dad067ef9e0088a9 -
Trigger Event:
push
-
Statement type:
File details
Details for the file ekm_proxy-0.1.0-py3-none-any.whl.
File metadata
- Download URL: ekm_proxy-0.1.0-py3-none-any.whl
- Upload date:
- Size: 20.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2dfbe94e3f7b8e8efc3719fc06ed0beaf591056512268e2a879072ae8f035419
|
|
| MD5 |
9fc82ce0b9a6ab88f326eb3b4b8fb8b0
|
|
| BLAKE2b-256 |
425072636b9d573812fdba49f01c20ab28e6332168f038c08ffa5c32052e70d0
|
Provenance
The following attestation bundles were made for ekm_proxy-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on electrification-bus/ekm-proxy
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ekm_proxy-0.1.0-py3-none-any.whl -
Subject digest:
2dfbe94e3f7b8e8efc3719fc06ed0beaf591056512268e2a879072ae8f035419 - Sigstore transparency entry: 2146720058
- Sigstore integration time:
-
Permalink:
electrification-bus/ekm-proxy@9880a53153a3f035d8eaa3a1dad067ef9e0088a9 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/electrification-bus
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@9880a53153a3f035d8eaa3a1dad067ef9e0088a9 -
Trigger Event:
push
-
Statement type: