e3dc-mqtt-ruby

A Ruby bridge between E3DC solar battery storage systems and MQTT, using the RSCP protocol. Ruby port of e3dc-mqtt-go / e3dc-mqtt-rs with feature parity for the read side.

Repository: https://github.com/isnogudus/e3dc-mqtt-ruby

Features

  • Real-time power values (PV, battery, grid, home, wallbox)
  • Battery state of charge, cycle count, cell voltages/temperatures
  • Daily statistics (production, consumption, autarky)
  • Delta-based publishing (only changed values hit the broker)
  • Aligned polling intervals (ticks on clock boundaries)
  • TLS with verification toggle, LWT
  • Pure-Ruby RSCP implementation — no C extensions, no FFI
  • Complete RSCP tag registry (3564 tags, auto-generated from the Go reference)

Requirements

  • Ruby ≥ 3.4 (uses Data.define)
  • An E3DC system with the RSCP interface enabled
  • An MQTT broker

Installation

bundle install
cp config.toml.example config.toml
$EDITOR config.toml            # fill in RSCP host/user/password/key + MQTT

Configuration

log_level = "INFO"             # DEBUG, INFO, WARN, ERROR

[e3dc]
host = "192.168.1.100"
port = 5033                    # default
username = "your_username"
password = "your_password"
key = "your_rscp_key"
interval = "5s"                # polling interval (min 5s)
statistic_update_interval = "5m"

[mqtt]
url = "ssl://broker:8883"      # or tcp://broker:1883
root = "e3dc"                  # topic prefix
client_id = "e3dc-mqtt-ruby"
username = ""                  # optional
password = ""                  # optional
tls_verify = true              # set false for self-signed certs

Running

bundle exec bin/e3dc-mqtt -c config.toml

For a one-shot liveness probe without touching MQTT:

bundle exec bin/probe -c config.toml

The bridge follows a "let it crash" model — on errors it exits with a specific code and a supervisor (systemd, runit, Docker) should restart it.

Exit codes

Code Meaning
0 Clean shutdown
1 Configuration error
4 E3DC connection error
5 MQTT connection error
6 System-info error
10 Status publish error
11 Statistics publish error
12 Battery publish error

MQTT topics

All topics are prefixed with {root}/{model}-{serial}/.

  • online — retained LWT (true/false)
  • info — retained JSON system info
  • status/… — 5-second cadence (pv, grid, battery, autarky, …)
  • status_sums/…statistic_update_interval cadence (daily totals)
  • status/battery:N/… and status/battery:N/dcb:M/… — battery detail

Full topic list: see e3dc-mqtt-go README.

Architecture

lib/e3dc_mqtt/
├── config.rb            TOML loader + validation
├── types.rb             Data.define value types (Status, BatteryData, …)
├── app.rb               Main loop, aligned tickers, signals
├── e3dc.rb              High-level E3DC client (status, info, stats, batteries)
├── mqtt_client.rb       Delta-publish, JSON, LWT, TLS verify toggle
└── rscp/                Pure-Ruby RSCP protocol
    ├── constants.rb     Frame layout, control-field masks
    ├── data_type.rb     18 RSCP types (pack/unpack)
    ├── message.rb       Message value + codec (with container recursion)
    ├── frame.rb         Frame writer/reader + CRC32
    ├── rijndael256.rb   Rijndael-256 CBC (32-byte block, 14 rounds)
    ├── tags.rb          Generated: 3564 tags, 712 declared data types
    └── client.rb        TCP + auth + send/receive (stateful CBC)

Regenerating the tag registry

The tag registry in lib/e3dc_mqtt/rscp/tags.rb is generated from the Go reference's tag.go and tag_datatype.go. To pick up new tags when the Go library updates:

ruby tools/generate_tags.rb             # uses default Go module cache path
ruby tools/generate_tags.rb /path/to/go-rscp/rscp

Tests

bundle exec rake test

Covers the config loader, the RSCP framing round-trip, Rijndael-256 against known Go-reference vectors, the tag registry, and an end-to-end loopback test of the RSCP client (real TCP server, full auth handshake, stateful CBC across messages).

Credits

Ported from and verified against these upstream projects:

  • spali/go-rscp — the Go RSCP library. Structural reference for framing, tag registry, data-type marshalling, and the client/auth flow. tools/generate_tags.rb parses its sources verbatim.
  • azihsoyn/rijndael256 — the Go Rijndael-256 implementation the rscp/rijndael256.rb port is based on. The single-block and CBC outputs match byte-for-byte. The original ancestor is agl/pond.
  • isnogudus/e3dc-mqtt-go — the Go MQTT bridge. Blueprint for the high-level E3DC::Client structure, MQTT topic naming, delta publishing, and the daily-statistics boundary logic.
  • isnogudus/e3dc-mqtt-rs — the Rust MQTT bridge. Cross-checked for protocol details and tag coverage.

License

MIT — see LICENSE. Third-party attributions (for the Rijndael-256 port and the go-rscp-derived tag registry) are preserved in NOTICE.