A command-line tool for monitoring and exporting data from GivEnergy inverters over the local network, built on givenergy-modbus.
- Python 3.14+
- A GivEnergy inverter accessible on the local network
uv syncAfter uv sync the givenergy-cli command is available at .venv/bin/givenergy-cli, or via uv run givenergy-cli.
host and port are shared options on the root command. They can also be supplied via the GIVENERGY_HOST / GIVENERGY_PORT environment variables.
uv run givenergy-cli --host 192.168.x.x tuiFour views, mirroring the layout of the companion Home Assistant dashboards, plus a modbus log panel and a status bar showing connection state and the time of the last refresh:
- Glance (default) — headline summary: a flow-status sentence, Solar today / Battery SOC / Home now figures, and a chip row with per-battery SOC and today's import/export.
- Flow — three "now" tiles above the animated power-flow topology.
- Analyst — the data-heavy widgets: energy balance ledger, Inverter panel (with collapsible Charge/Discharge Slot sections), and Battery panel.
- Controls — send commands to the inverter: enable/disable charge & discharge, charge target SOC, SOC reserve, and charge/discharge time slots. Everyday changes apply immediately (mirroring the app); the disruptive operations (reboot, SOC recalibration) require typing a confirmation token. Read-only unless the TUI is launched with
--allow-writes. Single-phase inverters only for now — three-phase and EMS use different write paths that haven't been implemented or hardware-tested, so those models get a notice instead of controls.
The status indicator cycles through Connecting… / Probing… / Reconnecting… / ● Connected / ● Disconnected to show the live transport state; an automatic reconnect is attempted whenever the connection drops.
The plant topology is detected once and cached per host, so subsequent launches skip the slow probe and paint near-immediately (a cheap re-check runs in the background to catch hardware changes). Pass --redetect to force a full detection and refresh the cache after changing hardware.
| Key | Action |
|---|---|
1–4 |
Switch view: Glance / Flow / Analyst / Controls |
r |
Refresh now (re-reads instantaneous measurements) |
Shift+R |
Full refresh — also re-reads the holding-register config blocks |
l |
Toggle the modbus log panel |
q |
Quit |
uv run givenergy-cli --host 192.168.x.x capture --output frames.log --duration 60Connects and records raw redacted Modbus frames for the given duration (default 60 s). Writes one line per frame: a UTC timestamp and the hex payload. Serials are redacted in every frame the library can decode; a frame it can't decode is passed through untouched, so it's worth a quick skim before attaching the file to a public issue.
--duration is wall-clock coverage: the capture reconnects automatically across dropped connections and quiet stretches (no register updates for --reconnect-after seconds, default 60; 0 disables the quiet check), writing every segment to the one file with a # … reconnected after Ns gap comment marker on each resumption.
uv run givenergy-cli --host 192.168.x.x probe --type hr --base 4080 --count 60 --device 0x31Issues raw Modbus read requests, bypassing the normal capability-driven polling. Useful for exploring undocumented register blocks — e.g. checking whether HR(4080+) holds battery energy totals on AC-coupled models. Requests are split into 60-register chunks automatically; timeouts are reported per-chunk rather than aborting the whole probe.
| Option | Description | Default |
|---|---|---|
--type / -t |
Register bank: hr (holding) or ir (input) |
required |
--base / -b |
First register address (decimal or 0x-hex) |
required |
--count / -n |
Number of registers to read | 60 |
--device / -d |
Modbus device address, decimal or 0x-hex (e.g. 0x11 inverter, 0x31 AC) |
0x11 |
--compact / --terse |
Plain hex dump instead of a table — easy to copy-paste, and loadable by shell / inspect |
off |
The compact form is the modbus library's canonical register-cache text serialisation (to_compact), one device-inline row per 60-register block:
# givenergy-cli probe of device 0x31 (HR 0..119) on 192.168.1.5:8899
0x31:HR(0,60) 20010003…
0x31:HR(60,60) …
The leading # line is informational only (host/port provenance) — strip it freely (e.g. to censor your IP) and the dump still parses, since each row carries its own device address. Redirect to a file (probe … --compact > dump.txt) and you have a register dump you can reconstruct a plant from — handy for new hardware that export can't yet detect.
uv run givenergy-cli --host 192.168.x.x export -o plant.jsonConnects, runs detect to discover the plant topology, loads the holding-register config, fetches the input registers, and writes every register from every discovered device address. Partial captures are still written on timeout, with a warning.
uv run givenergy-cli inspect plant.json
uv run givenergy-cli inspect dump.txt # a probe --compact dump works tooReconstructs the Plant from an export JSON or a probe --compact dump, then prints the Inverter and Battery model fields plus per-device raw register dumps (decimal + hex). No network required. (Probe-sourced plants carry raw registers but no detected capabilities, so the typed model views may be limited.)
uv run givenergy-cli shell plant.json # offline, from an export or probe dump
uv run givenergy-cli --host 192.168.x.x shell # one-shot live snapshotDrops you into an interactive Python REPL with the reconstructed plant ready to poke at. With a file argument it loads an export JSON or probe --compact dump offline; with no file it takes a single live snapshot via --host and closes the socket. The namespace exposes plant, caches (the raw register caches), batteries, show() (the full inspect-style dump) and console.
Install the shell extra (pip install 'givenergy-cli[shell]') for an IPython shell with tab-completion and history; without it, the standard-library REPL is used.
uv run givenergy-cli mock-server --capture plant.log
# then, in another terminal:
uv run givenergy-cli --host 127.0.0.1 tuiReplays one or more capture logs as a faithful in-memory plant, answering a real client's detect / load_config / refresh sequence with synthesized, correct-CRC responses — so you can drive tui, export, or probe against it with no hardware. Reads of an absent register bank return the same error shape real hardware uses. Seed files come from the capture command.
| Option | Description | Default |
|---|---|---|
--capture / -c |
Capture .log file(s) to seed from (repeatable) |
required |
--bind |
Bind address (use 0.0.0.0 to expose on the LAN) |
127.0.0.1 |
--port |
Bind port | 8899 |
| Variable | Subcommand | Default |
|---|---|---|
GIVENERGY_HOST |
all | — (required) |
GIVENERGY_PORT |
all | 8899 |
GIVENERGY_REFRESH_INTERVAL |
tui |
15.0 |
GIVENERGY_LOG_LEVEL |
tui |
INFO |
givenergy_cli/
__init__.py
__main__.py — Typer entry point (tui / capture / probe / export / inspect / mock-server subcommands)
app.py — Textual TUI app
capture.py — frame-capture logic for bug reports
mock.py — mock-plant server for offline testing
registers.py — export, load, and rich-formatted display of register dumps
tests/
fixtures/ — anonymised plant JSON fixtures (good + bad-enum cases)
A few deliberate properties worth knowing:
- Read-only by default. Every command only ever reads registers, with one deliberate exception: the TUI's Controls view can issue writes, and only when launched with
--allow-writes. Without that flag, no code path sends a Modbus write to the inverter. - The local Modbus-TCP interface is unauthenticated and unencrypted. That's a property of the GivEnergy hardware, not something this tool can change: anyone who can reach port 8899 can read from the inverter. Keeping the inverter on a segmented network (VLAN/firewall) is the control that matters, and it's yours to apply.
- Sharing is redaction-aware.
exportredacts serial numbers by default (pass--no-redactfor raw data);captureredacts on a best-effort, per-frame basis (see the note above).
To report a vulnerability, see SECURITY.md. A point-in-time audit of the codebase lives in SECURITY_AUDIT.md.
| Package | Purpose |
|---|---|
givenergy-modbus |
Modbus TCP client and data model for GivEnergy inverters |
textual |
Terminal UI framework |
typer |
CLI argument parsing |
rich |
Console formatting for export / capture / inspect output |
