A writable control driver for the Ferroamp Energy Hub that implements the
EMS inverter contract so it can be
orchestrated by ha-ems (EMS Steward). It exposes the mode selector and battery
power setpoint and actuates the hub over its local MQTT ExtAPI.
The driver is implemented against Ferroamp's local MQTT ExtAPI. It publishes
charge / discharge / auto commands to <base_topic>/control/request
(requires the ExtAPI / EMS option to be enabled on your hub).
Safety first — nothing reaches the battery until you opt in:
- A per-entry
switch.ferroamp_control_enableddefaults OFF. While off, the driver builds no command and publishes nothing. - EMS ships in read-only mode, so it won't even write to these entities until you disable its read-only switch.
So you can install, configure and wire everything up, watch what EMS would send in the logs, and only flip control on once you trust it.
Telemetry (SoC, powers, capacity) still comes from the existing read-only
ferroamp MQTT integration — install
this driver alongside it. This driver provides only the writable surface.
- Base topic — default
extapi; commands go toextapi/control/request. Confirm your hub's ExtAPI topic (some setups namespace it differently). - Command format —
{"transId": "<uuid>", "cmd": {"name": "charge", "arg": "<watts>"}},dischargelikewise,autoto hand back. Confirm against your ExtAPI documentation. - With control still OFF, set
number.ferroamp_battery_power_setpointmanually and check the log lineFerroamp control → …shows the payload you expect.
| Entity | Purpose |
|---|---|
switch.ferroamp_control_enabled |
Master gate — OFF by default |
select.ferroamp_mode |
self_consumption, peak_shaving, forced_charge, forced_discharge, idle |
number.ferroamp_battery_power_setpoint |
W, ± (charge / discharge → ExtAPI command) |
number.ferroamp_grid_power_limit |
W, advisory peak cap (peak shaving realised via setpoint) |
sensor.ferroamp_last_command |
idle / pending / ack / nak: the hub's answer to the latest command, with last_command, last_ack, last_nak, last_result as attributes |
binary_sensor.ferroamp_following |
on = the hub acknowledged the latest command, off = it refused it (NAK); EMS raises a Repairs issue after two ticks of NAK |
Telemetry sensors (sensor.ferroamp_soc, _battery_power, _grid_power,
_solar_power, _battery_capacity) are consumed by EMS directly from the
ferroamp telemetry integration and are not re-published here.
See custom_components/ems/inverter_contract.py in the EMS repo for the
authoritative contract definition.
Every command carries a transId; the hub answers on
<base_topic>/control/response (receipt: ack "sending cmd to ESOs", or
nak, typically "transaction in progress") and on
<base_topic>/control/result (outcome: "all ESOs have changed setting").
The driver subscribes to both, pairs the answers with what it sent and
shows the verdict on sensor.ferroamp_last_command and
binary_sensor.ferroamp_following. A NAK is logged as a warning. Answers to
transactions the driver did not send (another app or integration commanding
the same hub) are ignored and counted in the unmatched attribute, which is
the quickest way to see that something else is talking to the hub. A command
the hub refused is published again on the next write even when the setpoint
is unchanged (0.2.1); EMS Steward 0.30's recovery from a NAK depends on it.
- A test that holds the report to stats.py's key list.
stats.pypasses only the keys in itsEXTRA_KEYSon; anything elsestats_extrabuilds is dropped without a word. ReadEXTRA_KEYSfrom stats.py and check that every key the report builds is in it, so a dropped key fails locally. (The shared file itself was synced from ha-ctc in 0.3.3.) - Modbus TCP transport as an alternative to MQTT, once a verified Ferroamp register map is available (the config flow is structured to add it).
Brand-specific control logic doesn't belong in the brand-agnostic EMS. Each inverter family gets its own driver repo; users install the driver matching their hardware. Future siblings:
ha-solaredge-controlha-huawei-controlha-victron-control
Not affiliated with Ferroamp AB.
The driver sends one report per day to https://stats.rnet.se: which version you run, your Home Assistant version and installation type, the country you have set in Home Assistant, an approximate position rounded to about 11 km, the battery power it is allowed to command, whether control is enabled and has actually been used, whether the grid limit is in use, and how many commands the hub refused since the last report.
No power readings, no energy, no time series: this driver commands a hub, it does not meter a house. It never sends a name, an address, an exact position, a serial number, an MQTT topic or an entity name, and your IP address is not stored. The refusal count is the one number that says whether the driver actually works in the field, which is exactly what a maintainer cannot see from one installation.
To opt out: Settings, Devices and services, Ferroamp Control, Configure, Send anonymous usage statistics. Switching it off also erases what has already been sent. The full list of fields and the reasoning: https://stats.rnet.se/integritet.
Apache License 2.0, see LICENSE.