Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
bac76ac
chore: exclude pre-release givenergy-modbus v2 from main dependency
dewet22 May 13, 2026
79110b7
fix: reset client after timeout tolerance is exceeded
dewet22 May 14, 2026
ab25413
chore: bump givenergy-modbus to >=2.0.0,<3.0.0 for v1.0.0 development
dewet22 May 13, 2026
1c466f6
chore: point givenergy-modbus source at GitHub HEAD (main branch)
dewet22 May 14, 2026
309f75d
fix: replace deprecated Inverter alias with SinglePhaseInverter
dewet22 May 14, 2026
de7eef8
feat: expose reboot and calibrate battery SOC as HA services
dewet22 May 14, 2026
ff2429e
fix: require explicit device_id on reboot and calibrate services
dewet22 May 14, 2026
2856303
refactor: adopt givenergy-modbus 2.x APIs; drop max_batteries setting
dewet22 May 14, 2026
6860717
fix: raise HomeAssistantError when service target is unavailable
dewet22 May 14, 2026
d4ce23f
fix(deps): bump givenergy-modbus to 2.0.0a1
github-actions[bot] May 14, 2026
3786fb1
chore: drop [tool.uv.sources] override; pull givenergy-modbus from PyPI
dewet22 May 14, 2026
18dd83b
feat(sensor): expand diagnostic and enum sensor coverage
dewet22 May 15, 2026
3bfa0a4
ci: trigger release workflow on tag push
dewet22 May 15, 2026
6a415a8
fix(dashboard): sections battery view, fix apexcharts bar charts
dewet22 May 15, 2026
d5d8db6
feat(lovelace): add dashboard generator script
dewet22 May 15, 2026
c6833d8
fix(lovelace): switch to power-flow-card-plus (flixlix)
dewet22 May 15, 2026
6a81707
feat: add generate_dashboard service
dewet22 May 15, 2026
5202a47
refactor: rename lovelace → dashboard throughout
dewet22 May 15, 2026
d4a3029
fix: log connection close and reconnect events in coordinator
dewet22 May 15, 2026
0e9ca43
fix: reset on Nth consecutive timeout, not after N tolerated failures
dewet22 May 15, 2026
8a7383f
build: pin uv_build as the build backend
dewet22 May 15, 2026
48cfebb
feat: configurable per-request retries (default 1)
dewet22 May 15, 2026
4306f5e
chore(deps): bump givenergy-modbus to 2.0.0a2 in pyproject
dewet22 May 15, 2026
081314e
docs: capture coordinator concurrency invariant in class docstring
dewet22 May 15, 2026
d4ecb31
ci(bump): route bump PR to the branch matching the modbus major version
dewet22 May 15, 2026
f68346f
chore: add idempotent script to apply branch-protection ruleset
dewet22 May 15, 2026
8a54b06
ci(bump): use BUMP_PAT so PRs trigger downstream workflow checks
dewet22 May 15, 2026
477072a
fix(deps): bump givenergy-modbus to 2.0.0a3
github-actions[bot] May 15, 2026
1d64321
feat!: drop user-tunable retries and timeout_tolerance from config form
dewet22 May 15, 2026
3b6cd41
fix(deps): bump givenergy-modbus to 2.0.0a4
github-actions[bot] May 15, 2026
4145109
feat: make battery pause writable; add pause-slot time entities
dewet22 May 15, 2026
69528e5
fix(deps): bump givenergy-modbus to 2.0.0a5
github-actions[bot] May 15, 2026
c183164
refactor(time): switch slot entities to single-endpoint writes
dewet22 May 15, 2026
229407a
fix: address PR #41 review feedback on battery-pause platform migration
dewet22 May 15, 2026
35f9c87
chore: capture www_dir in lambda default arg to avoid B023 late-binding
dewet22 May 15, 2026
3cc8159
fix(deps): bump givenergy-modbus to 2.0.0a6
github-actions[bot] May 15, 2026
bbf411f
fix(dashboard): clamp Overview power chart y-axis to ±max_power_kw
dewet22 May 19, 2026
cbf7980
fix(dashboard): fix Energy view column charts returning N/A
dewet22 May 19, 2026
ac0c0a8
fix(dashboard): plural Battery tab title; uppercase serial numbers
dewet22 May 19, 2026
06ef7bc
feat(dashboard): version dashboard schema; surface update prompt via …
dewet22 May 19, 2026
d1e25bd
feat: bump givenergy-modbus to rc1, rename _2 sensors to _alt
dewet22 May 19, 2026
1f91f3b
feat: add capture_frames service (#45)
dewet22 May 19, 2026
e596f8c
fix(sensor): skip alt battery sensors when inverter doesn't populate …
dewet22 May 19, 2026
926a048
build: bump version to 1.0.0rc1
dewet22 May 19, 2026
415e91a
fix: address review feedback — hassfest, slot guard, ruff targets
dewet22 May 20, 2026
754af8a
docs: update README and hacs.json for v1.0
dewet22 May 21, 2026
fdb2b68
fix(deps): bump givenergy-modbus to 2.0.0 (#49)
dewet22 May 22, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 30 additions & 9 deletions .github/workflows/bump-givenergy-modbus.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,7 @@ jobs:
contents: write
pull-requests: write
steps:
- uses: actions/checkout@v6

- uses: astral-sh/setup-uv@v7
with:
enable-cache: true

- name: Determine target version
- name: Determine target version and base branch
id: ver
env:
DISPATCH_VERSION: ${{ github.event.client_payload.version }}
Expand All @@ -35,7 +29,33 @@ jobs:
echo "::error::no version supplied via client_payload.version or workflow inputs"
exit 1
fi
# Route the PR to the branch whose dependency range that major
# version belongs to. Update the case statement when a new release
# branch (and/or new modbus major) is introduced.
major="${NEW%%.*}"
case "$major" in
1) base=main ;;
2) base=v1.0 ;;
*)
echo "::error::no target branch configured for givenergy-modbus major $major (version $NEW)"
exit 1
;;
esac
echo "new=$NEW" >> "$GITHUB_OUTPUT"
echo "base=$base" >> "$GITHUB_OUTPUT"

- uses: actions/checkout@v6
with:
ref: ${{ steps.ver.outputs.base }}
# PAT (not GITHUB_TOKEN) so the resulting push + PR-creation events
# fire downstream workflows — GITHUB_TOKEN-authored actions don't
# trigger other workflows by design, which would otherwise leave the
# bump PR without validate.yml checks running.
token: ${{ secrets.BUMP_PAT }}

- uses: astral-sh/setup-uv@v7
with:
enable-cache: true

- name: Bump pin in pyproject.toml and manifest.json
env:
Expand Down Expand Up @@ -69,8 +89,9 @@ jobs:

- name: Commit, push, open PR
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GH_TOKEN: ${{ secrets.BUMP_PAT }}
NEW: ${{ steps.ver.outputs.new }}
BASE: ${{ steps.ver.outputs.base }}
run: |
if git diff --quiet; then
echo "nothing to bump (pin already at ${NEW}?)"
Expand All @@ -91,6 +112,6 @@ jobs:
gh pr create \
--title "fix(deps): bump givenergy-modbus to ${NEW}" \
--body "Automated bump triggered by [givenergy-modbus@${NEW}](https://github.com/dewet22/givenergy-modbus/releases/tag/v${NEW})." \
--base main \
--base "$BASE" \
--head "$branch"
fi
29 changes: 24 additions & 5 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
name: Release

on:
release:
types: [published]
push:
tags:
- "v*"

jobs:
release:
Expand All @@ -12,13 +13,13 @@ jobs:
steps:
- uses: actions/checkout@v6
with:
ref: ${{ github.event.release.tag_name }}
ref: ${{ github.ref_name }}
fetch-depth: 0 # need full tag history for the changelog step

- name: Generate release notes from git log
id: notes
env:
TAG: ${{ github.event.release.tag_name }}
TAG: ${{ github.ref_name }}
REPO: ${{ github.repository }}
run: |
# Require an annotated tag — the message becomes the release blurb.
Expand Down Expand Up @@ -49,9 +50,27 @@ jobs:
echo "NOTES_EOF"
} >> "$GITHUB_OUTPUT"

# Mark as pre-release if the tag contains a pre-release identifier.
if echo "$TAG" | grep -qE '(a|b|rc)[0-9]+$'; then
echo "prerelease=true" >> "$GITHUB_OUTPUT"
else
echo "prerelease=false" >> "$GITHUB_OUTPUT"
fi

- name: Create GitHub release
env:
GH_TOKEN: ${{ github.token }}
TAG: ${{ github.ref_name }}
NOTES: ${{ steps.notes.outputs.body }}
PRERELEASE: ${{ steps.notes.outputs.prerelease }}
run: |
flags=""
[ "$PRERELEASE" = "true" ] && flags="--prerelease"
gh release create "$TAG" --title "$TAG" --notes "$NOTES" $flags

- name: Patch manifest.json with release tag
env:
TAG: ${{ github.event.release.tag_name }}
TAG: ${{ github.ref_name }}
run: |
version="${TAG#v}"
jq --arg v "$version" '.version = $v' \
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ dist/
.env
.DS_Store

.coverage

# Claude Code — local-only, not shared
.claude/settings.local.json
.claude/worktrees/
66 changes: 54 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,24 +15,39 @@ Uses [`givenergy-modbus`](https://github.com/dewet22/givenergy-modbus) for all i
## Requirements

- A [supported GivEnergy inverter](#supported-inverters) connected to your local network (wifi or ethernet), with the Modbus TCP port reachable from your Home Assistant server (default port **8899**)
- Home Assistant 2025.4 or later
- Home Assistant 2026.5 or later (requires Python 3.14, which HA 2026.5+ ships)

## Supported inverters

The underlying [`givenergy-modbus`](https://github.com/dewet22/givenergy-modbus) library knows the following inverter families: Hybrid (1ph/3ph), AC, EMS, Gateway, and All-in-One. The following systems have been reported working:
The integration uses [`givenergy-modbus`](https://github.com/dewet22/givenergy-modbus) v2.0, which models the following device families: single-phase hybrid, three-phase hybrid, AC-coupled, EMS, Gateway, and All-in-One. Register maps for all of these shipped in v2.0, but empirical verification is still in progress for most — the mappings were brought in from the GivTCP fork, which ran across a wide range of hardware, so the coverage is broad but not all of it has been confirmed against wire data.

- Hybrid (Gen 1)
Confirmed working:

- Hybrid single-phase (Gen 1)
- AC-coupled (Gen 1)

If you have a different model and would like to help validate the integration against it, please [open an issue](https://github.com/dewet22/givenergy-hass/issues). A register dump is the most useful thing you can include — it captures every raw value the inverter exposes and lets me map new sensors without needing physical access to your hardware. To produce one, clone [givenergy-cli](https://github.com/dewet22/givenergy-cli), run `uv sync`, then:
The following have modelled register maps and are expected to work, but would benefit from owner validation — if yours is one of these and you run into sensor values that look wrong, please [open an issue](https://github.com/dewet22/givenergy-hass/issues):

```bash
uv run givenergy-cli --host <inverter-ip> export -o plant.json
- Hybrid three-phase
- All-in-One (AIO)
- EMS controller
- Gateway (V1 / V2)
- HV battery stacks (BCU/BMU)

If you'd like to help validate, a wire-frame capture is the most useful thing you can include. If you already have the integration running, use the built-in service from **Developer Tools → Services**:

```
Service: givenergy_local.capture_frames
Duration: 60
```

This writes a portable JSON file covering all discovered registers. Attach it to the issue along with your inverter model and serial prefix.
This records a redacted copy of the raw Modbus traffic (serial numbers zeroed), saves it to `/local/`, and sends a notification with a download link. Attach the file to the issue along with your inverter model and serial prefix.

Older Gen 2 units with the `EA` serial prefix are untested — any owners willing to help validate are very welcome!
If you don't yet have the integration installed, [givenergy-cli](https://github.com/dewet22/givenergy-cli) can produce a structured register dump instead:

```bash
uv run givenergy-cli --host <inverter-ip> export -o plant.json
```

## Installation

Expand All @@ -56,14 +71,14 @@ Or if that doesn't work:

Add the integration via **Settings → Devices & Services → Add Integration → GivEnergy Local**.

![Config flow — add integration dialog](docs/config-flow.png)

| Field | Default | Description |
|---|---|---|
| Inverter IP Address | — | Local IP of the inverter's data adapter |
| Modbus Port | `8899` | Modbus TCP port |
| Scan Interval | `30` s | How often HA polls for updated values |
| Number of Batteries | `1` | Number of battery units connected |
| [Passive mode](#passive-mode) | off | Listen only — use when another Modbus client (e.g. the GivEnergy app) is already polling and this integration should just observe |
| Timeout Tolerance | `5` | Consecutive refresh failures before the integration marks entities unavailable |

To change any of these later, open the integration's **⋮** menu in **Settings → Devices & Services → GivEnergy Local** and choose **Reconfigure**. The integration reloads automatically when you save.

Expand All @@ -73,6 +88,8 @@ When enabled, the integration connects to the inverter but does not send any Mod

## Entities

![Inverter device page in Home Assistant](docs/device-page.png)

### Inverter device

#### Sensors
Expand Down Expand Up @@ -107,7 +124,6 @@ When enabled, the integration connects to the inverter but does not send any Mod
| Charger Warning Code | — | Diagnostic |
| Charge Status | — | Diagnostic; raw int (BMS state code, mapping TBD) |
| System Mode | — | Diagnostic; raw int (operating mode, mapping TBD) |
| Battery Pause Mode | — | Diagnostic; raw int (pause-charging state) |
| AC Output Voltage / Frequency / Current | V / Hz / A | Diagnostic; inverter output (post-conversion) |
| Grid Apparent Power | VA | Diagnostic |
| Inverter Power Factor | — | Diagnostic |
Expand Down Expand Up @@ -142,8 +158,12 @@ When enabled, the integration connects to the inverter but does not send any Mod
| Battery Discharge Limit | Number | 0–50 % |
| Battery Discharge Min Power Reserve | Number | 4–100 % |
| Battery Power Mode | Select | Export / Self Consumption |
| Battery Pause Mode | Select | Disabled / Pause Charge / Pause Discharge / Pause Both |
| Charge Slot 1 & 2 Start / End | Time | |
| Discharge Slot 1 & 2 Start / End | Time | |
| Battery Pause Slot Start / End | Time | Active window for the pause mode above |

![Battery pause mode select control](docs/battery-pause-select.png)

### Battery device(s)

Expand All @@ -165,6 +185,27 @@ Each battery appears as a separate device linked to the inverter.

Cell-level entities are tagged as diagnostic, so they're hidden from the default device view but available for dashboards and pack-health monitoring (cell voltage spread, temperature deltas, etc.).

### Services

The integration registers the following services under the `givenergy_local` domain. All are accessible from **Developer Tools → Services** or from automations.

| Service | Description |
|---|---|
| `givenergy_local.generate_dashboard` | Generates a topology-aware Lovelace dashboard YAML for your inverter and battery configuration, saves it to `/local/`, and sends a persistent notification with a download link. Import via **Settings → Dashboards → Add Dashboard**. Accepts an optional `max_power_kw` parameter (default 10). |
| `givenergy_local.capture_frames` | Captures raw Modbus wire frames for a configurable duration (10–300 s, default 60 s), writes a redacted copy to `/local/`, and sends a download link via persistent notification. Serial numbers are zeroed before the file is written. Attach the file to a GitHub issue when reporting connectivity problems. |
| `givenergy_local.reboot_inverter` | Sends the inverter reboot command. Requires a `device_id`. |
| `givenergy_local.calibrate_battery_soc` | Triggers a BMS SOC calibration cycle. Requires a `device_id`. |

![Generated GivEnergy Lovelace dashboard](docs/dashboard.png)

After running `generate_dashboard`, a notification appears with a download link:

![Dashboard generated notification](docs/dashboard-notification.png)

If the dashboard schema is updated in a future release, the integration raises a fixable HA Repairs issue — click **Fix** to regenerate automatically with your settings preserved.

![Dashboard outdated repair issue](docs/repairs-fix.png)

### Not exposed by default

The upstream library makes ~180 inverter fields available; this integration intentionally exposes the subset that's useful for end users without being unsafe or noisy. Deliberately skipped for now:
Expand All @@ -176,7 +217,7 @@ The upstream library makes ~180 inverter fields available; this integration inte
- Raw debug fields (internal bus voltages, countdown timers, `debug_inverter`)
- Per-phase three-phase data beyond `Grid Power Phase 1` and the three-phase balance registers

If any of these would genuinely help your setup, [open an issue](https://github.com/dewet22/givenergy-hass/issues) describing the use case — the field probably can be exposed with a single description entry, but it's nicer to have a concrete reason to do it. The same applies if a sensor we *do* expose looks wrong on your inverter — **real-world testing on non-Hybrid Gen 1 hardware (AC, AC3, EMS, Gateway, All-in-One) is especially appreciated**, and a register dump from your unit goes a long way (see [Supported inverters](#supported-inverters) for how to produce one).
If any of these would genuinely help your setup, [open an issue](https://github.com/dewet22/givenergy-hass/issues) describing the use case — the field probably can be exposed with a single description entry, but it's nicer to have a concrete reason to do it. The same applies if a sensor we *do* expose looks wrong on your inverter — **real-world testing on non-Hybrid Gen 1 hardware (AC, AC3, EMS, Gateway, All-in-One) is especially appreciated**, and a frame capture from your unit goes a long way (see [Supported inverters](#supported-inverters) for how to produce one).

## Energy dashboard

Expand Down Expand Up @@ -212,6 +253,7 @@ The daily counters reset at midnight; Home Assistant's recorder detects the rese
- **Transient connection drops are normal.** TCP-level timeouts and the occasional connection reset get logged at WARNING level and the next scan tick re-establishes the connection. The `Last Successful Refresh` and `Consecutive Refresh Failures` diagnostic sensors will tell you if something more persistent is going on.
- **"Register cache unchanged" failures in passive mode** mean no peer client is refreshing the inverter. Switch back to active mode, or start the other client that's supposed to be driving the bus.
- **Conflicts with another Modbus client** (GivTCP, the GivEnergy app, etc.) — the inverter doesn't always cope well with two clients issuing large reads concurrently. Use [passive mode](#passive-mode).
- **Wrong number of battery devices appearing** — battery count is auto-discovered at startup by probing the Modbus bus; there is no manual override. If detection misfires (e.g. a battery was slow to respond), reloading the integration usually fixes it. If the count is consistently wrong, [open an issue](https://github.com/dewet22/givenergy-hass/issues/48) and attach a frame capture (see [Supported inverters](#supported-inverters)).

For anything else, please [open an issue](https://github.com/dewet22/givenergy-hass/issues) with the relevant HA log lines and your inverter model.

Expand Down
Loading
Loading