Skip to main content

Bridge Rainforest RAVEn / EMU-2 serial XML streams to MQTT and Home Assistant discovery

Project description

raven2mqtt

tests python

Bridge a Rainforest Automation RAVEn / EMU-2 serial device to MQTT and Home Assistant MQTT discovery.

The RAVEn exposes a simple CDC ACM serial terminal that streams independent XML fragments. That behavior is awkward inside Home Assistant because every HA restart can reopen the terminal in the middle of an unsolicited XML report. This bridge keeps one long-lived serial session outside Home Assistant and publishes normalized readings over MQTT.

⚠️ Is this for you?

raven2mqtt is a standalone service that runs on a Linux host owning the RAVEn USB stick. It is not a Home Assistant add-on or integration, and it cannot be installed inside Home Assistant OS (HAOS). You need shell access, the RAVEn attached to that host, and an MQTT broker Home Assistant already uses (such as the Mosquitto add-on).

Your setup Recommendation
Home Assistant OS / Supervised, RAVEn plugged into the HA machine The built-in rainforest_raven integration, or the one-click raven2mqtt add-on.
HA in a Docker container, or a separate always-on Linux box / LXC / VM that owns the USB stick raven2mqtt; run the container (see Quickstart) or a systemd service.
RAVEn on a different machine than Home Assistant raven2mqtt on that machine; it reaches HA over MQTT.

If direct serial access from Home Assistant is stable for you, prefer the built-in integration. raven2mqtt exists for the cases where the serial lifecycle around HA restarts, upgrades, and USB passthrough is the unreliable part; see below for why.

⚖️ Relationship to Home Assistant's built-in integration

For many installations, the built-in Home Assistant rainforest_raven integration is the right first choice: it is simple, supported by Home Assistant's normal configuration flow, and does not require a separate service.

raven2mqtt is meant for environments where the RAVEn serial lifecycle is the unreliable part. The device behaves more like a continuously streaming terminal than a request/response API. It can emit asynchronous XML reports at any time, and a process that opens the serial port may start reading in the middle of an XML fragment. That is especially painful when Home Assistant restarts, upgrades, reloads an integration, or runs inside a VM with USB passthrough.

This bridge addresses that class of problem by moving serial ownership out of Home Assistant:

  • A small long-running service owns the USB serial terminal continuously.
  • Home Assistant restarts and upgrades no longer reopen or reset the RAVEn serial session.
  • The parser is designed to recover from partial, malformed, or concatenated XML fragments without requiring Home Assistant setup retries.
  • MQTT retained state lets Home Assistant come back online with the last known meter values while waiting for the next RAVEn report.
  • Home Assistant consumes normal MQTT discovery entities, so the RAVEn device path, Linux permissions, and USB passthrough details stay outside HA Core.

It also runs as a separate piece of infrastructure you maintain (a container or service), unlike the built-in integration which runs entirely inside Home Assistant.

🚀 Quickstart (Docker)

The simplest way to run the bridge is the published container image, which needs no Python, venv, udev, or systemd setup on the host; only Docker on the machine the RAVEn is plugged into.

  1. Create a config from the example and edit it. At minimum set your MQTT broker under [mqtt] and the serial device path as it appears inside the container under [serial] device (commonly /dev/ttyACM0 for a RAVEn):

    curl -O https://raw.githubusercontent.com/lestephen/raven2mqtt/main/raven2mqtt.example.toml
    mv raven2mqtt.example.toml raven2mqtt.toml
    # edit raven2mqtt.toml
    
  2. Run it, passing through the RAVEn USB device (adjust /dev/ttyACM0 to match your host):

    docker run -d --name raven2mqtt \
      --restart unless-stopped \
      --device /dev/ttyACM0 \
      -v "$PWD/raven2mqtt.toml:/config/raven2mqtt.toml:ro" \
      ghcr.io/lestephen/raven2mqtt:latest
    

Or with Docker Compose, see docker-compose.example.yml:

services:
  raven2mqtt:
    image: ghcr.io/lestephen/raven2mqtt:latest
    restart: unless-stopped
    devices:
      - /dev/ttyACM0:/dev/ttyACM0
    volumes:
      - ./raven2mqtt.toml:/config/raven2mqtt.toml:ro

The container reads its config from /config/raven2mqtt.toml. Home Assistant discovers the entities over MQTT automatically. To keep last-known state across container restarts, set [service] state_file = "/data/state.json" and add a -v raven2mqtt-data:/data volume (otherwise retained MQTT state still restores values once Home Assistant reconnects).

📦 Install without Docker

Requires Python 3.11+ on the host. The runtime dependencies are paho-mqtt and pyserial.

Install the released version as an isolated CLI with pipx:

pipx install raven2mqtt

Or from source, for development:

git clone https://github.com/lestephen/raven2mqtt.git
cd raven2mqtt
python3 -m venv .venv
. .venv/bin/activate
pip install -e '.[test]'
pytest

⚙️ Configure

Copy the example config and edit it. With Docker this is the file you mount at /config/raven2mqtt.toml; for a host install, /etc/raven2mqtt.toml is a common location:

cp raven2mqtt.example.toml raven2mqtt.toml
editor raven2mqtt.toml

Key settings:

  • [serial] device: path to the RAVEn serial device. With Docker this is the path inside the container (the one you passed with --device). On a host, /dev/raven is a recommended udev symlink (see below).
  • [mqtt]: broker host, credentials, topic prefix, optional TLS.
  • [service] state_save_interval_seconds: throttle for state.json disk writes. The MQTT state topic is published on every meter report regardless; this only governs how often the on-disk snapshot is rewritten. Default 60 s.
  • [device]: identifiers and default entity prefix used by Home Assistant MQTT discovery.

Render the Home Assistant discovery payload to verify your configuration:

raven2mqtt --config raven2mqtt.toml discovery-json

▶️ Run as a systemd service (advanced: bare-metal or LXC Linux host)

This is the manual path for a standalone Linux host or LXC when you are not using the Docker image. It needs root, a system user in the dialout group, and a udev rule. Skip this section entirely if you used the Docker quickstart.

The unit (systemd/raven2mqtt.service) expects a raven2mqtt user in the dialout group and a venv at /opt/raven2mqtt/.venv:

useradd --system --home /opt/raven2mqtt --shell /usr/sbin/nologin --groups dialout raven2mqtt
install -d -o raven2mqtt -g raven2mqtt /opt/raven2mqtt
python3 -m venv /opt/raven2mqtt/.venv
/opt/raven2mqtt/.venv/bin/pip install /path/to/raven2mqtt
install -m 0644 systemd/raven2mqtt.service /etc/systemd/system/raven2mqtt.service
install -m 0640 -g raven2mqtt raven2mqtt.example.toml /etc/raven2mqtt.toml
editor /etc/raven2mqtt.toml
systemctl daemon-reload
systemctl enable --now raven2mqtt
journalctl -u raven2mqtt -f

A typical udev rule for a stable /dev/raven symlink; adjust the vendor and product IDs for your specific RAVEn:

SUBSYSTEM=="tty", ATTRS{idVendor}=="04b4", ATTRS{idProduct}=="0003", SYMLINK+="raven", GROUP="dialout", MODE="0660"

For USB passthrough into an LXC or VM (so the host above can see the device), see your hypervisor's documentation (for example, Proxmox USB passthrough or an LXC device cgroup rule). Home Assistant OS cannot satisfy this requirement.

🔧 Debugging

These commands require shell access on the host running the bridge (for a container, prefix with docker exec raven2mqtt). Pipe a captured RAVEn stream through the parser without connecting to MQTT:

cat raven-capture.log | raven2mqtt parse-stdin

Each top-level XML frame is printed as a single JSON line containing tag, payload, and the original raw_xml. This is useful for diagnosing which frames the meter is pushing.

📄 License

Apache-2.0. See LICENSE and NOTICE.

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

raven2mqtt-0.1.0.tar.gz (25.1 kB view details)

Uploaded Source

Built Distribution

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

raven2mqtt-0.1.0-py3-none-any.whl (20.9 kB view details)

Uploaded Python 3

File details

Details for the file raven2mqtt-0.1.0.tar.gz.

File metadata

  • Download URL: raven2mqtt-0.1.0.tar.gz
  • Upload date:
  • Size: 25.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for raven2mqtt-0.1.0.tar.gz
Algorithm Hash digest
SHA256 088018ca3b97544a097a798dc08e85661f3a65205c732a801cc1ee6149947902
MD5 02117437f6fb930a4c4758fa525749fc
BLAKE2b-256 32f296dc14d7f6c3c1280f7d08209d4ffe483f0712e437742f56a2d9b2531a36

See more details on using hashes here.

Provenance

The following attestation bundles were made for raven2mqtt-0.1.0.tar.gz:

Publisher: release.yml on lestephen/raven2mqtt

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

File details

Details for the file raven2mqtt-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: raven2mqtt-0.1.0-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.12

File hashes

Hashes for raven2mqtt-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 83750ac88bac4d52df86bba11e0c7b1c096da72512223df55ca8a258ec395b86
MD5 a7ab8647a5563263485d82e94b29bf67
BLAKE2b-256 5dae91f3eacf4e3098971c8574539a268bfb814471ff5c69f6a17b84aca18d06

See more details on using hashes here.

Provenance

The following attestation bundles were made for raven2mqtt-0.1.0-py3-none-any.whl:

Publisher: release.yml on lestephen/raven2mqtt

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