No description
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Simon Richter db518dbd82 Accept a host name as device address
DHCP can give the device a new address. The program looks the name up
again after each failed query, so it follows the device without a
restart.
2026-10-13 20:45:32 +00:00
.cargo Poll the device and print the values 2026-10-03 16:14:48 +00:00
src Accept a host name as device address 2026-10-13 20:45:32 +00:00
.gitignore Add empty marstek-mqtt crate 2026-10-02 19:01:29 +00:00
Cargo.lock Build rumqttc without TLS 2026-10-03 16:30:06 +00:00
Cargo.toml Accept a host name as device address 2026-10-13 20:45:32 +00:00
clippy.toml Add empty marstek-mqtt crate 2026-10-02 19:01:29 +00:00
LICENSE-APACHE Add empty marstek-mqtt crate 2026-10-02 19:01:29 +00:00
LICENSE-MIT Add empty marstek-mqtt crate 2026-10-02 19:01:29 +00:00
README.md Accept a host name as device address 2026-10-13 20:45:32 +00:00

marstek-mqtt

Reads a Marstek device over the local Open API and publishes its values to MQTT, one value per topic.

Topics

<base>/<ble_mac>/status            online | offline   (retained, last will)
<base>/<ble_mac>/meter/<field>     EM.GetStatus       CT meter
<base>/<ble_mac>/mode/<field>      ES.GetMode         operating mode, battery grid power
<base>/<ble_mac>/energy/<field>    ES.GetStatus       energy counters
<base>/<ble_mac>/battery/<field>   Bat.GetStatus      battery state
<base>/<ble_mac>/device/<field>    Marstek.GetDevice  identity (retained)
<base>/<ble_mac>/bluetooth/state   BLE.GetStatus

<base> is marstek unless set otherwise. <ble_mac> is the Bluetooth MAC of the device, which stays the same when its IP address changes. A field name ends with its unit, for example total_power_w or input_energy_wh. The payload is the plain value: -62, true, auto.

Signs: ongrid_power_w is negative while the battery charges. The meter powers are negative while the house feeds into the grid.

mosquitto_sub -h mqtt -v -t 'marstek/+/meter/total_power_w'

Timing

The device answers one request at a time and drops many of them. The program sends one query after the other with 1 s between two requests. With the default intervals, the meter values and the mode values arrive about every 2.5 s each. A query that fails publishes nothing. While the broker is away, values are dropped.

Options

Every option can also come from the environment variable in brackets.

--device             (MARSTEK_DEVICE)            host or host:port of the device
--mqtt-host          (MQTT_HOST)                 MQTT broker
--mqtt-port          (MQTT_PORT)                 default 1883
--mqtt-user          (MQTT_USER)                 with --mqtt-pass
--mqtt-pass          (MQTT_PASS)                 with --mqtt-user
--base-topic         (MARSTEK_BASE_TOPIC)        default marstek
--timeout            (MARSTEK_TIMEOUT)           seconds, default 5
--meter-interval     (MARSTEK_METER_INTERVAL)    seconds, default 0 (as often as possible)
--mode-interval      (MARSTEK_MODE_INTERVAL)     seconds, default 0 (as often as possible)
--energy-interval    (MARSTEK_ENERGY_INTERVAL)   seconds, default 10
--battery-interval   (MARSTEK_BATTERY_INTERVAL)  seconds, default 30
--device-interval    (MARSTEK_DEVICE_INTERVAL)   seconds, default 300

The device host is a name or an IPv4 address. After a failed query, the program looks the name up again, so it follows the device when DHCP gives it a new address.

The connection to the broker uses no TLS.

Build

The marstek library comes from an SSH remote, so cargo fetches it with the git CLI (see .cargo/config.toml).

A static binary that runs on Alpine Linux:

rustup target add x86_64-unknown-linux-musl
cargo build --release --target x86_64-unknown-linux-musl