Releases: sphings79/marstek-modbus-suite
Release list
2.0.0 — the panel
The integration now brings its own page, and it writes as well as reads.
The panel
A sidebar entry appears as soon as a battery is set up. Seven tabs, no custom cards to install — every element ships with the integration.
| Overview | state of charge as a double ring, the gap between the rings being the reserve; four cells that follow the direction of flow |
| Cells | every pack's cell range on one shared axis, 112 cells on a Venus D |
| Packs | state of charge per pack, with the discharge floor and the backup limit drawn in |
| Solar | the MPPT inputs, and whether anything is actually connected to them |
| Energy | day, month and lifetime side by side |
| Control | writes: power, limits, modes, all six schedules |
| System | device, firmware, connection, faults, limits |
Behind the gear: seven colour schemes with their own light and dark versions, a light/dark override, which tab to open on, which tabs to hide, an extra decimal place, and import/export.
The panel finds its values by the register behind an entity rather than by its name, so renaming a device, moving it to another area or switching language leaves it working.
Control
Charge and discharge power, the power and SoC limits, work and forced mode, the backup and RS485 switches, all six schedules, and a confirmed restart.
Every control reads its own bounds from the entity, so the same editor is correct on a 1500 W Venus A and a 2500 W Venus D. Schedule times are converted between the HHMM the registers hold and a clock field.
A factory reset is deliberately not offered on the tab; it stays in the entity list.
New sensors
usable_energy, energy_to_full |
measured against a discharge floor you configure per battery, default 12 % |
runtime_to_empty, runtime_to_full |
hours at the current power, counting only in the direction of flow |
remaining_cycles, battery_health |
wear against the cell rating |
backup_reserve_energy |
what sits below the floor, reachable only through the off-grid socket — Venus A and D |
battery_1_voltage, battery_1_current, battery_1_cycle_count |
pack 1 was missing these |
Fixed
- Venus A was offered up to 2500 W for charge and discharge power and in every schedule slot. Its real limit is 1500 W.
- A healthy battery reported a protection fault. Any non-zero MOSFET status was treated as an alarm, but the device closes one pack's MOSFETs at a time and that reading simply says which pack is working. It is now reported as exactly that.
- Normal operation read as a fault in three more places. The pack spread warned above 5 percentage points, the pack table flagged rows more than 5 points from the median, and the overview judged the spread across the whole stack against the 100 mV limit for cells inside one pack. The device works one pack at a time, so the packs sit around ten points apart by design. Thresholds now sit above what normal working produces, and the overview reports the widest spread within a pack, which is the figure that finds a weak cell.
- The phone scrolled sideways. The tab strip, the schedule editor and the pack columns each pushed the page instead of scrolling in their own box. Below 640 px the cell matrix writes each pack's range out instead of drawing a two-centimetre axis.
- Three sensors had no name at all, and one registered as
sensor.<device>with no suffix.
Upgrading from 1.x
Two things change underneath you.
An entity keeps the id it was registered with. If your install already has the unnamed pack 1 sensor, its display name will be right after this update but its entity id will not. Rename it, or delete it from the entity registry and restart so it is created again.
grid_standard reports lower-case states. Venus E v1/v2 only: Germany becomes germany, and so on for the rest. Home Assistant does not accept the old spelling as a translation key. An automation comparing against the old value needs adjusting; the displayed name is unchanged.
Also
- Renamed to Marstek Modbus Suite, repository renamed to
marstek-modbus-suite. Old links redirect and HACS follows the repository by id, so nothing needs reinstalling. - An icon of the project's own, in the sidebar and in HACS.
- README rewritten in English and German, with per-model capability tables and a languages table. The register table moved to
docs/. scripts/check_translations.pyasserts every entity a register map defines has a name in every catalogue, and runs in CI — a missing translation produces no error and no log line at runtime, so nothing else would catch it.- hassfest and the HACS validation now run on every push.
2.0.0-beta.9 — spread, judged correctly
The battery charges and discharges one pack at a time rather than all of them together. During either, the packs sit around ten points of state of charge apart, and their cells therefore sit at different voltages. That is the design working.
Three readings treated it as a fault.
Pack spread
Warned above 5 percentage points and scaled its bar to 10, so normal operation was permanently amber with the bar at its end. A warning now starts at 12 points, critical at 20, and the bar is scaled to 25.
Overview tile
Reported the spread across the whole stack — highest cell anywhere minus lowest cell anywhere — and judged it against 100 mV, which is the limit for cells inside one pack. On a stack whose packs are staggered that is guaranteed to read critical.
It now reports the widest spread within a single pack and names that pack. Packs staggered 160 mV apart but each internally tight read 8 mV and green; one pack drifting 120 mV internally reads red and says which one.
Stack spread in the cells view
Keeps its number, loses its verdict and its bar, and says why a spread is expected. The number that finds a weak cell is the delta within a pack, which the tile beside it and the table below already report.
2.0.0-beta.8 — names and direction
Fixed
Three pack 1 sensors had no name. battery_1_voltage, battery_1_current and battery_1_cycle_count were added to the register maps in beta.7 but never to the translation files. Home Assistant found no name for their translation key and fell back to the device name alone — one of them registered as sensor.<device> with no suffix at all.
Every definition in every register map is now checked against all three catalogues; these three were the only gap.
An entity keeps the id it was registered with. If your install already has the unnamed sensor, the display name will be right after this update but the entity id will not. Rename it, or delete it from the entity registry and restart so it is created again.
Overview
Capacity moves into the stored cell as 10,93 / 17,92 kWh, and the space it leaves goes to two cells that follow the direction of flow.
Only one runtime sensor ever counts — each returns 0 while the power flows the other way — so the fixed "until empty" slot was showing a stopped countdown half the time.
| while charging | while discharging | at rest | |
|---|---|---|---|
| energy | room to full | usable | usable |
| runtime | until full | until empty | — |
A fourth cell counts the installed packs, and is left out on a battery that reports nothing per pack.
Added
scripts/check_translations.py asserts every entity a register map defines has a name in every catalogue, and runs as its own job in the validate workflow. A missing translation produces no error and no log line at runtime, so nothing else would catch it.
2.0.0-beta.7 — control and settings
Two new tabs in the panel.
Control
Charge and discharge power, the power and SoC limits, work and force mode, the backup and RS485 switches, all six schedule slots, and a confirmed device restart.
Every control reads its own bounds from the entity rather than hard-coding them, so the same editor is correct on a 1500 W Venus A and a 2500 W Venus D. Schedule times are converted between the HHMM the registers hold and a clock field.
Anything that regulates the battery from outside writes the same registers and will win within seconds. When that happens right after the panel wrote, the tab says so instead of looking broken.
Factory reset is deliberately not offered here; it stays in the entity list.
Settings
Behind a gear in the header. Seven colour schemes — Reactor, Cockpit, Verdant, Plasma, Ember, Glacier, and one that takes its colours from your Home Assistant theme — each with its own light and dark version. Plus a light/dark override, which tab the panel opens on, which tabs to hide, and one extra decimal place.
Settings live in the browser, so each browser keeps its own.
Fixed
- Venus A was offered up to 2500 W for charge and discharge power and in every schedule slot. Its real limit is 1500 W.
2.0.0-beta.6 — Marstek Modbus Suite
The integration is now called Marstek Modbus Suite. It carries four register maps, control, schedules and a panel of its own, and the name says so rather than describing a Modbus bridge.
Renamed in the manifest, hacs.json, both READMEs, the config flow, all three translation catalogues, the panel's strings, the issue template and the artwork.
The domain stays marstek_modbus. Changing it would invalidate every entity id and every config entry — that is a migration, not a rename, and it would land on people who never asked for it. Your entities and automations are untouched.
Existing config entries keep the title they were created with. Only newly added batteries show the new name.
A full Home Assistant restart, not a reload.
2.0.0-beta.5 — the brand assets, this time actually included
beta.4 announced the brand icons and shipped none of them: the blanket icon* and logo* rules in .gitignore swallowed assets/icon.svg and both PNGs before they ever reached a commit.
The sidebar icon was never affected — it lives in its own bundle, which is not called icon-anything. Only HACS was left without artwork.
Nothing else changed. If you are already on beta.4 and only care about the sidebar, you can skip this one.
2.0.0-beta.4 — an icon of its own
Five bars on one baseline, high-low-lowest-low-high. As a chart it reads as cell voltages, as a silhouette the tops trace an M — no borrowed logo.
The sidebar icon comes from a custom icon set in its own 0.24 kB bundle, loaded on every page because the sidebar draws its entry before anyone opens the panel. Missing file falls back to mdi:home-battery.
assets/make_brand_icons.py renders the HACS brand icons from the same geometry — icon.png at 256 and icon@2x.png at 512 — so the sidebar path and the PNGs cannot drift apart. It needs nothing installed.
A full Home Assistant restart, not a reload. Browsers cache the icon bundle, so a hard refresh may be needed for the sidebar to pick it up.
2.0.0-beta.3 — the panel stays put
Fixes the sidebar item disappearing after an update. The panel is now called Marstek Modbus and lives at /marstek-modbus.
Why it vanished
Removal was wired to async_unload_entry. Home Assistant calls that on every reload and every options change, not only when a battery is deleted — so the panel was torn down as a matter of routine and only reappeared if the setup that followed put it back. Anything going wrong in between left no sidebar item at all.
It now goes away in async_remove_entry, where removal means removal.
Registering it has also moved out of the block whose except rolls the entire setup back, and it logs at info rather than debug. A sidebar item is not worth refusing to load a battery over, and the silence was what made this hard to find.
Names carry the domain now
| before | now | |
|---|---|---|
| sidebar | Marstek Venus | Marstek Modbus |
| path | /marstek-venus |
/marstek-modbus |
| element | marstek-panel |
marstek-modbus-panel |
| bundle | marstek-panel.js |
marstek-modbus-panel.js |
A custom element name is global to the page. Another integration serving a file of the same name would define the same element, the second definition throws, and that panel stays blank — a failure with no error anywhere near the cause. The prefix removes the possibility.
If you had the old panel pinned or bookmarked, the URL changed.
Upgrading
A full Home Assistant restart, not a reload.
2.0.0-beta.2 — all six tabs
All six tabs are implemented. Core is now Overview.
Two views that answer what a list of entities cannot
Cells puts every pack's cell range on one shared voltage axis. The device reports a delta per pack but never one across the stack, so a pack sitting at a different level than its neighbours does not show up in any number — it is simply the bar that has drifted sideways. A narrow bar is a balanced pack, a wide one is drift inside it.
Packs shows state of charge per pack as columns against a common scale, with the discharge floor drawn across them, and flags any pack more than five points from the group. The reference is the median, not the mean, so a single outlier cannot drag the average towards itself and hide the fact that it is an outlier.
Solar covers the four MPPT inputs and tells a floating input apart from a working one. Energy puts today, this month and lifetime side by side, and explains the gap between the monthly and lifetime efficiency instead of just showing both. System leads with a single line saying whether any fault register is raised, then device, firmware, connection, limits and thermals.
More than one battery
The picker in the status bar remembers its choice, and every view reads only the selected device. The panel itself registers once no matter how many batteries are configured, and disappears from the sidebar only when the last one is removed.
There is no combined view across several batteries yet — each is looked at on its own.
Fixes found by rendering against live readings
- Identifiers were run through the number formatter. Firmware build 202409090159 came out as 202.409.090.159, and BMS version 1177 as 1.177. Rows can now be marked as identifiers, and every version, address and serial is.
- Four-digit firmware versions are now shown as major.minor — the device encodes the last digit that way, so 1177 is version 117.7.
- The discharge floor line sat underneath the column fill, which made it invisible whenever a pack was charged above it. That is most of the time.
Under the hood
Every view shares one base class holding the device reader, the number formatter and the label/value row, so a view is now mostly layout. Both catalogues carry 110 keys, checked for drift and for placeholders that differ between languages.
Upgrading
A full Home Assistant restart, not a reload.
2.0.0-beta.10 — the backup reserve
The energy only the backup socket can reach
The device stops at the configured discharge floor in normal operation, but keeps discharging to 8 % while it is running the off-grid output. That band was invisible: usable_energy counts down to the floor and stops there.
backup_reserve_energy reports it, on Venus A and D, where the figure is confirmed. Other models get no sensor, and the panel rows that show it disappear with it.
reserve = capacity × (min(SOC, floor) − 8) / 100
| SOC | reserve on a 17.9 kWh pack |
|---|---|
| 61 % — normal working | 0.72 kWh |
| 12 % — at the floor | 0.72 kWh |
| 10 % — outage in progress | 0.36 kWh |
| 8 % | 0.00 kWh |
Reported whether or not the socket is armed: a figure that drops to zero exactly when someone is planning for an outage answers nothing. It counts down once the state of charge is inside the band.
It appears in "Reserve · lifetime" and as a dotted line below the discharge floor in the pack columns, drawn quieter because it is the exception. The sensor carries both percentages as attributes, so the panel holds no copy of the number.
Settings: import and export
One text box, filled with what is set now, and a button that applies what was pasted into it — the same in every browser, no file permission, and you can see what you are moving. Text that is not a settings object is refused rather than silently resetting everything.
Fixed
The pack table highlighted any pack more than 5 points from the median. Since the device works one pack at a time, and the packs therefore sit around ten points apart, half the rows were marked at any moment. A row now stands out at the same distance the spread tile warns at, and both read that distance from one definition.