Command-line interface to control GivEnergy systems via the local network
Project description
GivEnergy CLI
A command-line tool for monitoring and exporting data from GivEnergy inverters over the local network, built on givenergy-modbus.
Requirements
- Python 3.14+
- A GivEnergy inverter accessible on the local network
Supported hardware
The CLI works with whatever givenergy-modbus can decode — the authoritative,
evidence-based support matrix (validated vs modelled-but-unverified hardware) lives in
the givenergy-modbus documentation.
Two CLI-specific caveats on top of it:
- Controls (writes) are single-phase inverters only. Three-phase, EMS, and gateways use different write paths that aren't implemented or hardware-tested, so they get a read-only notice.
- The live
tuiis oriented around a single inverter's power flow. Multi-inverter gateway and EMS topologies decode fine but are best explored viainspect/shell, which render every device the plant exposes.
If you run hardware the matrix lists as unverified (single-phase Gen 2, Polar, Commercial
AIO/EMS, three-phase AC), a capture from it would help confirm the decode.
Installation
uv sync
After uv sync the givenergy-cli command is available at .venv/bin/givenergy-cli, or via uv run givenergy-cli.
Usage
host and port are shared options on the root command. They can also be supplied via the GIVENERGY_HOST / GIVENERGY_PORT environment variables.
tui — interactive terminal UI
uv run givenergy-cli --host 192.168.x.x tui
Four 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 |
capture — record wire frames for a bug report
uv run givenergy-cli --host 192.168.x.x capture --output frames.log --duration 60
Connects 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.
probe — read an arbitrary register range
uv run givenergy-cli --host 192.168.x.x probe --type hr --base 4080 --count 60 --device 0x31
Issues 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.
export — dump registers to a portable JSON file
uv run givenergy-cli --host 192.168.x.x export -o plant.json
Connects, 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.
inspect — render an exported plant
uv run givenergy-cli inspect plant.json
uv run givenergy-cli inspect dump.txt # a probe --compact dump works too
Reconstructs 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.)
shell — interactive Python shell with a reconstructed plant
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 snapshot
Drops 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.
mock-server — serve a fake plant from recorded captures
uv run givenergy-cli mock-server --capture plant.log
# then, in another terminal:
uv run givenergy-cli --host 127.0.0.1 tui
Replays 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.
Two further seeding modes exist for register-interrogation work: --spec <file> builds a mock from a pure JSON/YAML register spec with no capture, and --sentinel <device>:<bank>:<start>-<end> overlays sentinel values on a --capture base. Exactly one of --capture or --spec is required.
| Option | Description | Default |
|---|---|---|
--capture / -c |
Capture .log file(s) to seed from (repeatable) |
— |
--spec |
JSON/YAML register spec to build a mock directly (no capture) | — |
--sentinel |
Overlay sentinel values on the --capture base (repeatable): <device>:<bank>:<start>-<end> |
— |
--offset |
Sentinel value offset, used with --sentinel |
0 |
--bind |
Bind address (use 0.0.0.0 to expose on the LAN) |
127.0.0.1 |
--port |
Bind port | 8899 |
Environment variables
| Variable | Subcommand | Default |
|---|---|---|
GIVENERGY_HOST |
all | — (required) |
GIVENERGY_PORT |
all | 8899 |
GIVENERGY_REFRESH_INTERVAL |
tui |
15.0 |
GIVENERGY_LOG_LEVEL |
tui |
INFO |
Project structure
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)
Security
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.
Dependencies
| 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 |
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file givenergy_cli-1.8.0.tar.gz.
File metadata
- Download URL: givenergy_cli-1.8.0.tar.gz
- Upload date:
- Size: 166.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f4839bfee1cfce1ddba8d172b6bb5c6569c01d470a719b49b4303b143bdf0644
|
|
| MD5 |
8b8566791fafd243833179f9ec7d5f21
|
|
| BLAKE2b-256 |
a41955f4233e792b81c68339d632b054f737da6d74558b4f0a646541b372c737
|
Provenance
The following attestation bundles were made for givenergy_cli-1.8.0.tar.gz:
Publisher:
release.yml on dewet22/givenergy-cli
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
givenergy_cli-1.8.0.tar.gz -
Subject digest:
f4839bfee1cfce1ddba8d172b6bb5c6569c01d470a719b49b4303b143bdf0644 - Sigstore transparency entry: 2138617466
- Sigstore integration time:
-
Permalink:
dewet22/givenergy-cli@5ed2643825ac2aca9528f497f0af687834af75b6 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/dewet22
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@5ed2643825ac2aca9528f497f0af687834af75b6 -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file givenergy_cli-1.8.0-py3-none-any.whl.
File metadata
- Download URL: givenergy_cli-1.8.0-py3-none-any.whl
- Upload date:
- Size: 62.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c06bd6d46afe8410d9358745a9b5569029a83e7833b98b157de543811babc2fa
|
|
| MD5 |
a618026eb5293fd526bad999b7c403d4
|
|
| BLAKE2b-256 |
f3372f10bbcf69e0da817a2edfd3a2a035a466715d4b1026eb7c06297f491954
|
Provenance
The following attestation bundles were made for givenergy_cli-1.8.0-py3-none-any.whl:
Publisher:
release.yml on dewet22/givenergy-cli
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
givenergy_cli-1.8.0-py3-none-any.whl -
Subject digest:
c06bd6d46afe8410d9358745a9b5569029a83e7833b98b157de543811babc2fa - Sigstore transparency entry: 2138617505
- Sigstore integration time:
-
Permalink:
dewet22/givenergy-cli@5ed2643825ac2aca9528f497f0af687834af75b6 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/dewet22
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@5ed2643825ac2aca9528f497f0af687834af75b6 -
Trigger Event:
workflow_dispatch
-
Statement type: