Skip to main content

Proxy a CTA-2045 (EcoPort) Smart Grid Device onto the Electrification Bus (eBus / Homie 5), via a pluggable UCM backend

Project description

cta2045-proxy

PyPI CI Ruff License: MIT

Proxy a CTA-2045 (EcoPort) Smart Grid Device onto the Electrification Bus (eBus / Homie 5), through a pluggable UCM backend.

Status: pre-alpha. The SkyCentrics UCM backend is functional and validated end to end against a live SkyCentrics Ethernet UCM: telemetry decode, the full Homie device tree, and bidirectional demand-response control. The public API may still change before 1.0. Published to PyPI as cta2045-proxy (pip install cta2045-proxy). It has not been certified under any conformance program and carries no warranty of interoperability.

What it does

A CTA-2045 appliance (an SGD, e.g. a water heater) speaks demand-response through a UCM. This service bridges that appliance onto eBus so any eBus/Homie 5 controller can read its telemetry and command demand-response events, published per the eBus proxy and water-heater data models:

ebus/5/{MAC}/       energy.ebus.device.bridge        (the UCM)
ebus/5/{MAC}-wh01/  energy.ebus.device.water-heater  (the proxied appliance)

The UCM is a pluggable backend, not a fixed assumption. CTA-2045 standardizes only the SGD-to-UCM link; how a UCM reaches the network is vendor-specific, so each UCM is a separate backend behind the vendor-neutral cta2045.ucm.Ucm interface. A SkyCentrics Ethernet UCM (over MQTT) ships today; the same CTA-2045 decode and eBus device model serve any future UCM without change:

  • other vendors' CTA-2045 UCMs (add a backend for that dongle's transport),
  • a native own-UCM that drives the CTA-2045 RS485 link layer directly, with no vendor dongle at all (reserved; see below).

Adding support for a new UCM means writing one backend; the appliance-side mapping and Homie publishing are shared across all of them.

Architecture

Three layers, cleanly separated:

  • cta2045 (upstream lib) - the CTA-2045 protocol codec and the abstract, vendor-neutral cta2045.ucm.Ucm interface. Pure, no I/O.
  • cta2045_proxy.ucm - concrete UCM backends binding Ucm to a real transport. Variants differ ONLY here:
    • skycentrics - a SkyCentrics Ethernet UCM (US08C) reachable over MQTT. Ships today.
    • native - an own-UCM driving the CTA-2045 RS485 link layer directly, no vendor dongle. Reserved; blocked on cta2045.link.
  • cta2045_proxy.mapping + .emitter - the CTA-2045 -> eBus water-heater data-model translation and the Homie adapter, shared across all backends. Built on ebus-sdk's declarative proxy layer (PropertySpec + build_from_declarations); see the SDK's doc/building-a-proxy.md.

Install

pip install cta2045-proxy

Requires Python 3.10+. For local development, clone the repo and install editable with the dev extras instead: pip install -e ".[dev]".

Run

cp config/config.example.toml config/config.toml   # then edit
export EBUS_MQTT_PASS=...                           # secrets via env, never the file
cta2045-proxy --config config/config.toml

See config/config.example.toml for the [ucm] backend selector and the [ebus] output broker. A downstream deployment customizes only the config; the code is deployment-neutral.

Bring up a SkyCentrics UCM

tools/ has helper scripts to discover a SkyCentrics Ethernet UCM on your LAN, point it at a broker, and confirm it is publishing (ucm-discover, ucm-configure, ucm-watch), plus a how-to for standing up a local test broker (via the sibling broker-quickstart or a bare Mosquitto). See tools/README.md.

Example: the published Homie tree

A live capture of what the proxy publishes for one SkyCentrics UCM (84FEDC538C72) feeding one water heater. The bridge is the UCM; its child is the proxied appliance:

ebus/5/
├── 84FEDC538C72/                     # bridge device (the UCM)
│   ├── $state = ready
│   ├── info/
│   │   ├── vendor-name = SkyCentrics
│   │   └── firmware-version = v2.2 (26 June 2026, 9:0:0)
│   └── connection/
│       ├── feeds-device-id = 84FEDC538C72-wh01
│       ├── feeds-device-type = energy.ebus.device.water-heater
│       ├── feeds-device-status = OK
│       └── last-seen = 2026-07-05T20:24:49Z
└── 84FEDC538C72-wh01/                # proxied device (the water heater / SGD)
    ├── $state = ready
    ├── info/
    │   └── fuel-type = (unset until the SGD reports it)
    ├── meter/
    │   ├── active-power = 0.0 W
    │   └── imported-energy = 733.0 Wh
    ├── soc/
    │   ├── soe = 0.0 Wh
    │   ├── total-energy-storage = 4500.0 Wh
    │   └── loadup-headroom = (unset)
    ├── dr/
    │   ├── dr-response = NONE
    │   ├── opted-out = false
    │   ├── event = (settable — publish a DR command to .../dr/event/set)
    │   └── active-event = {"mode": "NORMAL"}
    └── status/
        └── fault-state = OK

Send a demand-response command by publishing to the settable event property, e.g. a 10-minute shed:

mosquitto_pub -t 'ebus/5/84FEDC538C72-wh01/dr/event/set' -m '{"mode":"SHED","duration":600}'

The proxy encodes it to CTA-2045 and forwards it to the UCM (devices/84FEDC538C72/ctl/shed); the SGD's resulting state flows back to dr/dr-response (e.g. CURTAILED).

Development

ruff check . && ruff format --check .
pytest tests/ -v

Optionally install the pre-commit hooks (ruff format + lint on every commit, mirroring CI):

pip install pre-commit && pre-commit install

CI (.github/workflows/) runs the same ruff checks and pytest on 3.10 / 3.12 for every push and PR.

References

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

cta2045_proxy-0.1.2.tar.gz (27.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

cta2045_proxy-0.1.2-py3-none-any.whl (20.9 kB view details)

Uploaded Python 3

File details

Details for the file cta2045_proxy-0.1.2.tar.gz.

File metadata

  • Download URL: cta2045_proxy-0.1.2.tar.gz
  • Upload date:
  • Size: 27.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for cta2045_proxy-0.1.2.tar.gz
Algorithm Hash digest
SHA256 9f8dfa07fc41f886f083b5bb6c6924096090845b49f0629f187796501922013d
MD5 3f6efdacf8f5702a2b36cae4e97700e7
BLAKE2b-256 6343e963fd29fb9993ca712d8941e036891b5b7d6c854ac60e01736e725d4d17

See more details on using hashes here.

Provenance

The following attestation bundles were made for cta2045_proxy-0.1.2.tar.gz:

Publisher: publish.yml on electrification-bus/cta2045-proxy

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file cta2045_proxy-0.1.2-py3-none-any.whl.

File metadata

  • Download URL: cta2045_proxy-0.1.2-py3-none-any.whl
  • Upload date:
  • Size: 20.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for cta2045_proxy-0.1.2-py3-none-any.whl
Algorithm Hash digest
SHA256 9fa90b2e685cf4f2d3e1d974e1a02a0796351bae5ac586657379d2e64f9df628
MD5 56b89b9f7182ac07e025e234a60492ec
BLAKE2b-256 b5eed2ca87a50b3b653a93bced2134bfd3da428325083949dc779852667e00a8

See more details on using hashes here.

Provenance

The following attestation bundles were made for cta2045_proxy-0.1.2-py3-none-any.whl:

Publisher: publish.yml on electrification-bus/cta2045-proxy

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page