Changelog
Changelog¶
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
[2.5.1] - 2026-06-22¶
๐ Fixed¶
- de-noise #256 splice-guard singleton path (thresholds + logging) (e116b65)
[2.5.0] - 2026-06-17¶
โจ Added¶
- model-aware write rejection + dry_run in one_shot_command (Pillar A) (485c95d)
- AioBatteryModule.REGISTER_GETTER for staleness gating (#273) (fb24d79)
๐ Fixed¶
- tear down connection when detect() fails (atomic connect+detect, #274) (ed2985a)
[2.4.0] - 2026-06-17¶
โจ Added¶
- add compact probe-dump serialisation (to_compact/parse_compact) (4ec5f1e)
๐ง Maintenance¶
- add first real three-phase capture fixture + wire-level regression test (4c7e55e)
[2.3.3] - 2026-06-17¶
โจ Added¶
- derive three-phase p_battery and e_battery_throughput (#262) (52737cf)
- add set_charge_target_soc + clarify set_charge_target naming (#243) (1cb20c5)
๐ Fixed¶
- correct three-phase i_battery scale (deci โ centi) (#264) (fd64bb9)
๐ง Maintenance¶
- allowlist read-only dev commands in project settings (1d4564d)
- bump astral-sh/setup-uv from 7.6.0 to 8.2.0 (#259) (2c9b0dd)
[2.3.2] - 2026-06-14¶
๐ Fixed¶
- guard LV battery banks against sub-bus splice corruption (#256) (#258) (44d6444)
๐ง Maintenance¶
- add battery sub-bus splice corpus tools (#147, #256) (#257) (29b6889)
[2.3.1] - 2026-06-12¶
โจ Added¶
- map IR(105-107) โ LV energy totals + force-discharge flag (#251) (4150f11)
- map LV BCU block at 0x31 IR(60-63) (#241) (#253) (e9cf91b)
- non-negative directional power sensors (#205) (#254) (7cef175)
๐ Fixed¶
- pf formula + ChargeStatus enum (#209, #222) (#252) (7245382)
- CRC-failed frames must not commit over good cache (#255) (6edbc38)
[2.3.0] - 2026-06-11¶
2.3.0 is a register-correctness and addressing release, shaped by field evidence rather than the roadmap's planned theme โ the write-safety pillar moves to 2.4.x. Two behaviour changes lead it, both capture-proven.
Highlights¶
- Inverter addressing unified on
0x11(#189): the0x31read-alias for AC and HYBRID_GEN1 is retired. Live validation on both families showed0x11serves the full register file โ the old "identity-only" claim was an artefact of passive dongle captures โ and it is where the official app reads and writes. Capabilities persisted with the old0x31keep working (the hardware facade still answers there) and self-heal to0x11on the nextdetect(); no consumer action needed. - Meter power-factor decode fixed (#246):
pf_*(IR 80โ83) was decoded as unsigned milli, producing impossible values like 9.998; the correct encoding is signed int16 ร1e-4 in the EE [โ1, +1] convention. Apparent power gains its deci scaling (0.1 VA, unsigned), and reactive power is confirmed correct at 1 var โ all three scales settled against capture evidence via the displacement-PF identity, and pinned by a golden-master test so they can't silently regress. - Migration guide for fork consumers
(migrating-from-the-async-fork, #244):
the import map, polling-lifecycle change, command mapping and attribute
rename table for moving from the community
givenergy_modbus_asyncfork to this library โ written for GivTCP's port, useful to any fork descendant. - Per-register provenance accessors (#248): public access to a field's backing registers and their per-register freshness โ early groundwork for the provenance pillar.
i_batteryโ signed per-pack battery current (IR 95, #239) โ plus an audit extension that diffs device sections against their getters, closing the blind spot that had hidden it (#240).- Redaction hardening (#245): identifier registers are auto-discovered across all model modules, so a future device type can't silently miss share-safe redaction coverage.
โจ Added¶
- map IR(95) Im_Avg as i_battery โ signed per-pack current (#239) (cc494d1)
- diff device sections against their getters โ close the IR95 blind spot (#240) (d8dd183)
- auto-discover identifier registers for redaction across all model modules (#245) (61a0a09)
- public accessors for a field's backing registers + per-register freshness (#248) (106f4be)
- retire 0x31 read-alias โ inverter addressing unified on 0x11 (#249) (5bada9a)
๐ Fixed¶
- pf_ decode as signed int16 ร1e-4; p_apparent_ deci-scaled (#250) (b02b57a)
๐ง Maintenance¶
- add migration guide for givenergy_modbus_async fork consumers (#244) (a4c970d)
- release notes + roadmap sequencing for 2.3.0 (2b6b622)
[2.2.0] - 2026-06-10¶
2.2 reshapes how a system is modelled and hardens the library end to end. The
headline is the graph-shaped Plant (#106): instead of a flat model interrogated
through scattered is_*/has_* predicates, a Plant now enumerates its hardware
as typed devices โ inverter, batteries, EMS, gateway, AIO battery modules โ each
addressable in its own right. The same release line absorbed a full security
audit and a series of device-identity fixes, so serial numbers are now reliable
across the whole detectโrefresh lifecycle.
Highlights¶
- Typed-device enumeration โ
Plant.devices(#106): walk a system's hardware as typed devices rather than reading raw register caches or model predicates. - Reliable device identity: serial reconciliation via
Plant.serial_index(#106), a single authoritativePlant.inverter_serialaccessor that stays correct from first detect through every refresh (#227), and a fix for peripheral responses clobbering the inverter serial (givenergy-hass#95). - Per-module AIO battery data exposed as enumerable devices (#192).
- Share-safe diagnostics:
RegisterCache.redact_serials()andPlant.redact()produce exports with serial numbers redacted, for attaching to issues (#212). - Model-aware write commands for three-phase inverters and EMS plants (#75, #203): model-specific command mixins route writes to the correct registers for the hardware, with write safety enforced by the global allowlist at the PDU layer. Per-model allowlists are laid out as groundwork but not yet enforced.
- Frozen-BMS detection via a content-staleness primitive, so consumers can tell live data from a stuck cache (#91).
Security¶
This release carries the library's share of a coordinated security audit that
swept all three GivEnergy repositories โ the modbus library, the Home Assistant
integration, and the CLI โ conducted end-to-end by Anthropic's Mythos/Fable
model, with every finding either fixed or closed with documented analysis
(#223, #224). On the library side it delivered: a published SECURITY.md and
threat model, fail-closed serial redaction, write-path input validation (bools
and non-integral values are rejected before they reach a register), hardening
of the decode layer against malformed frames and tampered cache files, an
opt-in strict CRC mode (ReadRegistersResponse.strict_crc), and a hardened CI
supply chain (actions pinned to commit SHAs, least-privilege workflow
permissions).
Breaking changes & migration from 2.1¶
first_battery_serial_numberhas been removed (#191) โ it was unreliable on real hardware. Use the typed-device enumeration orPlant.serial_index.- For inverter identity, prefer the new
Plant.inverter_serial;Plant.inverter_serial_numberremains but is now a fallback surface and a candidate for deprecation in a future release. - Topology checks built on flat predicates or raw register caches keep working, but the typed-device API is the supported path going forward โ new device types will appear there first.
Home Assistant¶
The Home Assistant integration (givenergy-hass) ships its 1.2.0 line on 2.2, and it is the most visible beneficiary of the typed device model. All-in-One systems now surface each removable battery module as its own HA device โ parented to the inverter, identified by the module's serial, with its 24 per-cell voltages and temperatures as diagnostic sensors โ so the cell-balance heatmap and pack-health views work at module granularity, validated end-to-end on a real four-module unit. The 2.2 identity fixes also resolve a long-standing All-in-One collision where the inverter device could merge into one of its own battery modules.
Beyond the device model, 1.2.0 adds always-positive Grid Power Import/Export sensors that suit the Energy Dashboard's two-sensor grid option directly, hardens debug-capture access behind admin tokens and single-capture signed links (following a security review of the integration that found no exploitable vulnerabilities), and inherits the library's rejection of the dongle's transient all-zero register blocks โ last-good values are kept rather than zeros reaching your sensors. There's no migration on upgrade; AIO owners who ran an early 1.2.0 pre-release should remove and re-add the integration once to clear stale entities.
givenergy-cli¶
The CLI (givenergy-cli) tracks 2.2
in its 1.5 line: the TUI gains Glance/Flow/Analyst views mirroring the Home
Assistant dashboards, warm starts become near-instant by caching the detected
topology, and a new Controls view sends inverter writes โ the CLI's first step
beyond read-only, gated behind an explicit --allow-writes flag. Diagnostic
exports are share-safe by default, with serial numbers redacted. Nothing
changes for users beyond a normal upgrade โ the graph-shaped Plant and the
register renames were absorbed inside the CLI.
โจ Added¶
- hand-authored release-notes file leads the generated section (#236) (cba2c59)
๐ง Maintenance¶
- backfill the 2.1.6 section onto v2.2 (#237) (f04d436)
[2.2.0rc6] - 2026-06-10¶
โจ Added¶
- add unified inverter_serial accessor (#227) (#234) (5e7bbb0)
๐ Fixed¶
- close redact_serials() gaps โ meter serial + Plant header (#224 H2) (2433494)
- security-audit batch 3 โ write-path assurance (#224) (9dbdf00)
- security-audit batch 4 โ adversarial-input & decode-boundary hardening (#224) (#230) (8dc4903)
๐ง Maintenance¶
- confirm IR(42) p_load_demand includes the EPS branch (IR31) (#231) (fd2f08c)
- shared power-flow state classification spec (draft for cli/hass sign-off) (#232) (848d706)
- security-audit batch 5 โ supply-chain hardening + inbox de-advertise (#224) (#233) (d74d3df)
[2.2.0rc5] - 2026-06-10¶
๐ Fixed¶
- inverter serial overwritten by peripheral responses (hass#95) (24da545)
[2.2.0rc4] - 2026-06-10¶
๐ Fixed¶
- security-audit batch 1 quick wins (#224) (07b6b67)
๐ง Maintenance¶
- post-v2.2 unified roadmap (#221) (d2d7a98)
- security policy + committed audit, 2026-06-10 (#223) (4ef9d66)
[2.2.0rc3] - 2026-06-09¶
โจ Added¶
- per-module AIO battery data as enumerable devices (#192) (#219) (0bdeffe)
- content-staleness primitive for frozen-BMS detection (#91) (667cd0d)
[2.2.0rc2] - 2026-06-09¶
โจ Added¶
๐ Changed¶
- โ ๏ธ Breaking: remove unreliable first_battery_serial_number (#191) (#218) (560754f)
๐ง Maintenance¶
- mark Phase 4 as shipped in plant-device-graph.md (18fa6fe)
- bump codecov/codecov-action from 6 to 7 (#217) (d453cf6)
[2.2.0rc1] - 2026-06-07¶
โจ Added¶
- typed-device enumeration API โ Plant.devices (#106 Phase 1) (#210) (8fb5941)
- RegisterCache.redact_serials() โ share-safe cache export (#212) (#214) (b41022a)
- model-aware write commands for ThreePhaseInverter (#203) (#215) (e1ff30d)
๐ง Maintenance¶
- ghbot convention, permission allowlist, and inbox hooks (5224099)
[2.1.6] - 2026-06-09¶
โจ Added¶
- RegisterCache.redact_serials() โ share-safe cache export (#212, #214) (7ce7c66)
๐ Changed¶
- โ ๏ธ Breaking: remove unreliable first_battery_serial_number (#191) (#218) (f08af87)
๐ง Maintenance¶
- ghbot convention, permission allowlist, and inbox hooks (9dde868)
- bump codecov/codecov-action from 6 to 7 (#217) (9014003)
[2.1.5] - 2026-06-07¶
โจ Added¶
- support Python 3.11+ (lower floor from 3.14) (e74786e)
- model-specific command mixins โ _ThreePhaseCommands + _EmsCommands (#75) (c28e063)
- ingestion timestamps (#65), IR(0,60) skip-if-fresh (#196), all-zero bank rejection (#206) (#208) (f47bd43)
๐ง Maintenance¶
- fix PostToolUse hook matcher format in .claude/settings.json (29d11e4)
- stabilise capture-replay tests under load (#188) (786bc11)
- add soak harness for IR(0,60) skip-if-fresh (#196) (bb47e15)
[2.1.4] - 2026-06-05¶
โจ Added¶
- add p_pv, e_pv_day, battery_capacity_kwh to ThreePhaseInverter (6924f63)
๐ Fixed¶
- rename battery_power_cutoff โ battery_reserve_soc, add set_battery_reserve_soc (#48) (41425ce)
- write-register echo routed to inverter_address; AC/HYBRID_GEN1 writes now visible after refresh() (#187) (5ae3b66)
๐ง Maintenance¶
- add .deepsource.toml with Python 3.x runtime to reduce false positives (cbb8ba2)
- add register-doc audit tooling + hardware/firmware quirks reference (e43b1a3)
- correct inverter_address_for docstring โ 0x11 is not identity-only on AC/HYBRID_GEN1 (6730b12)
[2.1.3] - 2026-06-03¶
๐ Fixed¶
- tolerate unmapped enum values + gate Smart Load poll (#179, #180) (#181) (b92c04b)
๐ง Maintenance¶
- make main prek-clean (hook-coverage reconcile, not a ruff version skew) (#177) (ad3e1b6)
- autoupdate hook revs (ruff, mypy, uv-pre-commit) (#178) (c81780b)
[2.1.2] - 2026-06-03¶
๐ Fixed¶
- rename e_inverter_out_total โ e_pv_generation_total + battery_max_power None for unmapped DTC (#176) (0aab325)
๐ง Maintenance¶
- add v2.1 ecosystem release notes (39c7480)
[2.1.1] - 2026-06-02¶
๐ Fixed¶
- surface per-slot inverter status as raw hex code (#108) (#173) (a96d091)
- correct e_load_day mislabel; add e_consumption_today (#174) (#175) (c2a33ee)
[2.1.0] - 2026-06-02¶
๐ Fixed¶
- correct EMS per-slot status bitfield layout (#108) (61eb0f0)
[2.1.0rc1] - 2026-06-02¶
[2.1.0b15] - 2026-06-02¶
โจ Added¶
- add set_smart_load_slot_start/end/slot helpers (d0c8caa)
- register cross-correlation helpers (sentinel_devices + identify) (6631d43)
[2.1.0b14] - 2026-06-02¶
โจ Added¶
- rename HR(199) + add smart_load_slot_1..10 decode Defs (3f86f84)
[2.1.0b13] - 2026-06-02¶
โจ Added¶
- extend WRITE_SAFE_REGISTERS with app-confirmed registers (#48) (6d5d964)
๐ Fixed¶
- drop spurious base-register alignment warning (#163) (880ba13)
[2.1.0b12] - 2026-06-02¶
โจ Added¶
- add DEBUG-level PDU logging to MockPlant (c912de5)
[2.1.0b11] - 2026-06-02¶
๐ Fixed¶
- probe all LV battery slots; continue past absent/invalid slots (a4ba870)
[2.1.0b10] - 2026-06-02¶
[2.1.0b9] - 2026-06-02¶
โจ Added¶
- MockPlant capture-seeded mock inverter + encode error-bit fix (#158 B-2) (f28617a)
[2.1.0b8] - 2026-06-02¶
๐ Fixed¶
- gate HR(300-359) load_config poll on AC-config-block models (#162) (1d85d60)
[2.1.0b7] - 2026-06-01¶
๐ Fixed¶
- response CRC integrity + frame-aware capture redactor (#158) (a06f97a)
[2.1.0b6] - 2026-06-01¶
๐ Fixed¶
- treat residential All-in-One as single-phase (#105) (0bb591b)
[2.1.0b5] - 2026-06-01¶
๐ Fixed¶
- include device address + byte-swap in request CRC (#105) (b85b33a)
๐ง Maintenance¶
- drop now-redundant FC 0x16 CRC override (421f92b)
[2.1.0b4] - 2026-06-01¶
๐ Fixed¶
- refuse to poll without capabilities instead of guessing 0x32 (#105) (ae0502c)
- warn when refresh_plant max_batteries is passed (now ignored) (d1d6cf8)
- keep refresh_plant_data as a deprecated raising stub (e044541)
๐ง Maintenance¶
- consolidate CLAUDE.md into AGENTS.md as single source of truth (#155) (55c411c)
- pin detect-before-poll order in refresh_plant no-caps test (16c2768)
[2.1.0b3] - 2026-06-01¶
โจ Added¶
- add export_priority (HR311) and enable_eps (HR317) for AC inverters (732643c)
๐ Fixed¶
- clamp zero month/day in to_datetime to prevent crash (#153) (30f4a5f)
- add HR311/317 to PDU write allowlist; validate AC setters (d1c3634)
[2.1.0b2] - 2026-05-31¶
โจ Added¶
- add is_ac_coupled topology discriminator (376e87b)
๐ง Maintenance¶
- pin AC-limit register consistency, document 3ph gap (#75) (9a8ee7b)
[2.1.0b1] - 2026-05-31¶
โจ Added¶
- route battery-energy registers by model, not live values (#76) (85f49ce)
๐ Fixed¶
- re-apply low-risk model-layer fixes reverted from v2.0 (#74) (a83f314)
- redact serials split across capture-frame boundaries (#117) (1cecc03)
๐ง Maintenance¶
- clear actionable in-code TODOs (a92818f)
- reconcile v2.1 roadmap with shipped reality (a3โa11) (d3458e1)
[2.1.0a11] - 2026-05-31¶
โจ Added¶
- add plant_enabled boolean read-back for Flexi EMS control (a4386d5)
[2.1.0a10] - 2026-05-30¶
โจ Added¶
- add EMS-named export slot setters for API parity (341a595)
[2.1.0a9] - 2026-05-30¶
๐ Fixed¶
- raise refresh default budget to 2.0s/1 retry for contended buses (7318590)
๐ง Maintenance¶
- bump uv to 0.11.17 to clear file-write advisory (83ffa2d)
- clarify physical measurement points for single-phase grid power registers (961af80)
[2.1.0a8] - 2026-05-30¶
โจ Added¶
- add per-endpoint EMS slot setters for API parity (eea1e78)
[2.1.0a7] - 2026-05-30¶
โจ Added¶
- EMS plant-level charge/discharge/export write support (925cdc2)
๐ Fixed¶
- recognise EMS serial format in is_valid_serial (014095d)
๐ง Maintenance¶
- golden-master classification/topology checks over captures (8d55372)
[2.1.0a6] - 2026-05-30¶
โจ Added¶
- expose per-attribute display precision derived from register scaling (#129) (2dda112)
[2.1.0a5] - 2026-05-29¶
โจ Added¶
- read EMS rollup at detect time + sanity-check it (#109) (918a33a)
- preserve manufacture-date digits in serial redaction (#116) (4661483)
๐ Changed¶
- โ ๏ธ Breaking: raise RefreshPartiallySucceeded/RefreshFailed on partial/total poll failure (#125) (3493dd1)
๐ Fixed¶
- detect missing EMS rollup registers, not just missing cache (#111) (9fb8786)
- address inverter at its model-specific device address, not 0x32 (#119) (0200292)
๐ง Maintenance¶
- add EMS/hybrid/AIO topology captures + per-plant READMEs (#120) (1476beb)
- run per-PR checks on Linux only, cross-OS on a weekly schedule (c2bd2e1)
- add live device-address probe diagnostics (#119/#124) (fb001cb)
[2.1.0a4] - 2026-05-28¶
โจ Added¶
- unified Inverter facade for EMS-managed plant topology (#98) (4cb40b0)
- seed tests/fixtures/captures with real EMS-plant wire data (#103) (5376582)
๐ Fixed¶
- skip inverter-style HR/IR reads on EMS plant controllers (#93) (cecd06d)
- drop empty meter slots in detect() via Meter.is_valid() (#96) (1c21bfa)
- exercise strip path with partial-padding serial in EMS rollup test (#102) (57a6dcc)
- extend redact() for EMS serials and IPv4 addresses (#99) (ae4f84d)
๐ง Maintenance¶
- add ยง2 back-reference on a5 to mirror the a4 stretch note (721d904)
[2.1.0a3] - 2026-05-27¶
๐ Changed¶
- enable mypy check_untyped_defs for production code (ace9701)
๐ง Maintenance¶
- fetch full git history in release workflow so CHANGELOG generator works (2f446b7)
- sketch v2.1 release roadmap as a chain of small alphas (43ec1c4)
- use absolute LICENSE URL so include-markdown resolves it under docs/ (e11b298)
- build into envtmpdir so twine checks only the current run's artifacts (f6ae7a7)
- escalate unawaited-coroutine and unraisable warnings to errors (c169c02)
- update roadmap to reflect what shipped in 2.1.0a3 (afc8526)
[2.1.0a2] - 2026-05-27¶
First 2.1 alpha. Spans all work on the v2.1 branch since it diverged
from main at the v2.0.0 release, so includes equivalents of every
patch shipped in the v2.0.1โv2.0.4 maintenance line as well as the v2.1
feature work.
โจ Added¶
- Client.detect(prior=) for fast restarts; serialisable PlantCapabilities (811a143)
- add tx_jitter knob to disperse producer bursts (#71) (d5f4337)
๐ Changed¶
- โ ๏ธ Breaking: rename work_time_total to work_time_total_hours (9b737d9)
- โ ๏ธ Breaking: migrate PlantCapabilities to Pydantic v2 (#72) (52caaf0)
- โ ๏ธ Breaking: migrate RegisterDefinition to Pydantic v2 (#73) (f83928e)
๐ Fixed¶
- address #81 review feedback (BCU drift, address coercion, docs) (46ddfae)
- document and bound work_time_total unit (#84) (757c2c9)
- suppress out-of-bounds register values instead of passing through (#82) (151d7ff)
- discard Pattern A IR(0,60) responses before they reach the cache (#78) (9b3c490)
- accept v2.0.0 PlantCapabilities payloads in from_dict() (2c8327d)
- coerce bcu_stacks tuple entries to int in from_dict() (00152b6)
- harden from_dict against int device_type and null list fields (3cb7cf9)
- include error flag in WriteHoldingRegister equality (with unskipped tests) (e8f289c)
- harden against malformed-frame DoS cases (#88) (6555bea)
- refine review feedback on #88 hardening (a82f432)
- guard p_pv()/e_pv_day() against None inputs (#85) (#92) (962951a)
- address gemini-code-assist feedback on PR #94 (aee7990)
๐ง Maintenance¶
- add post-v2.0 improvement plan + correct stale #66 reference (39c14d4)
- reframe section 1 of plan as model-aware vs PDU-level (198a86f)
- add offline wire-capture replay harness (#82) (7f72c97)
- cover Model-instance passthrough in from_dict() _device_type (1719521)
- clarify write PDU hashability scope (6c0f808)
- bump the uv group across 1 directory with 2 updates (#77) (79c46ad)
- cross-reference open-giv/bms-analysis and document TCP-cache layering (08b0195)
- close leaked Queue.put coroutine in tx-queue-full timeout test (cba5f7a)
- pin actions-gh-pages to v4.1.0 for Node 24 runtime (b3c0fd5)
- hoist ValidationError import to module top per coderabbit nit (58f1504)
[2.0.0] - 2026-05-22¶
๐ง Maintenance¶
- enrich cli section of v2.0 release notes with v1.0 ecosystem story (e87c8fe)
[2.0.0rc1] - 2026-05-19¶
โจ Added¶
- support prerelease stage transitions in release.py bump (00ce5c3)
[2.0.0a6] - 2026-05-15¶
๐ง Maintenance¶
- address Gemini review on Converter.int32 and Converter.bitfield (67af9c8)
[2.0.0a5] - 2026-05-15¶
๐ง Maintenance¶
- bring Codacy-equivalent coverage in-house; suppress noise (945641a)
[2.0.0a4] - 2026-05-15¶
โจ Added¶
- add retry_delay knob and complete late-arrival wire-skip optimisation (522795d)
[2.0.0a3] - 2026-05-15¶
๐ Fixed¶
- drop incoherent-bank discard log from WARNING to DEBUG (1238975, @dewet22)
[2.0.0a2] - 2026-05-15¶
๐ Fixed¶
- quieten bounds-violation logs and exempt all-zero raw banks (a4501d7, @dewet22)
- thread timeout/retries through refresh_plant() post-detect (260af42, @dewet22)
- use address-prefixed byte-swapped CRC for FC 0x16 requests (a5b0b2c, @dewet22)
- make Client.connect() idempotent and reset _shutting_down (6c56889, @dewet22)
๐ง Maintenance¶
- switch PyPI publishing to OIDC; add publish-tag.yml recovery workflow (27a1dc4, @dewet22)
- fold republish-tag mode into release.yml (759a04e, @dewet22)
[2.0.0a1] - 2026-05-14¶
A chunky update, incorporating a lot of the differences that GivTCP developed and introduced during the extended time being forked away. Kudos and credit to @britkat1980 for all the effort that I could crib from.
โจ Added¶
New device models โ all modelled as siblings of SinglePhaseInverter / Battery, with from_register_cache() constructors and typed fields:
ThreePhaseInverterfor three-phase models (HR/IR 1000โ1420), including grid protection limits, derating curves, per-phase measurements, EPS/backup mode, energy counters, slot registers, andforce_charge_enable/battery_maintenance_mode(09e37f0, 95aba95, @dewet22)BcuandBmumodel classes for HV battery stacks (AIO / HV Gen3 / AIO-Hybrid systems) (ee15702, @dewet22)MeterandMeterProductmodel classes for external meter slaves at addresses0x01โ0x08, read via FC 0x04 and FC 0x16 respectively (c6d3a0a, @dewet22)Emsmodel for EMS plant status and configuration registers (3621938, @dewet22)GatewayandGateway2models withselect_gateway()for automatic firmware-version-based dispatch (752160a, @dewet22)select_inverter(model, register_cache)returnsSinglePhaseInverter | ThreePhaseInverterbased on device type (071c755, @dewet22)
Plant and client lifecycle:
PlantCapabilitiesdataclass captures full device topology fromdetect(): device type, inverter address, LV battery / meter / HV BCU device addresses (ffa78fc, @dewet22)Client.detect()for one-time device and peripheral discovery;Client.refresh()for fast IR measurement polling;Client.load_config()for HR configuration reads โ replacing the oldrefresh_plant_data()monolith (ffa78fc, 9936e98, @dewet22)- Typed plant accessors โ
plant.inverter,plant.batteries,plant.hv_stacks,plant.meters,plant.ems,plant.gatewayโ all dispatch viacapabilitiesrather than hardcoded device-address arithmetic (9936e98, @dewet22)
Commands:
SlotMapfrozen dataclass for model-driven slot register routing; all slot setters now acceptslot_map: SlotMap = SINGLE_PHASE_SLOTSโ three-phase callers passplant.inverter.slot_map(a0c50e3, @dewet22)- New command helpers:
set_battery_pause_mode,set_pause_slot,set_ac_charge,set_force_charge,set_force_discharge,set_enable_rtc,set_active_power_rate,set_battery_charge_limit_ac,set_battery_discharge_limit_ac,set_ems_plant,set_export_slot(8ca0b75, @dewet22)
Register coverage and converters:
MRregister namespace andReadMeterProductRegistersPDU (FC 0x16) for meter product identification registers (ef89324, @dewet22)resolve_model(dtc, arm_fw)for firmware-version-aware device model resolution (09dea7b, @dewet22)- Extended
Modelenum:HYBRID_GEN1/2/3/4,HYBRID_HV_GEN3,ALL_IN_ONE_HYBRID,POLAR,EMS_COMMERCIAL,AIO_COMMERCIAL; existing single-characterModel(dtc)lookups are unchanged (09dea7b, @dewet22) - New converters:
C.int32,C.bitfield,C.hexfield,C.gateway_version,C.nominal_voltage,C.nominal_frequency,C.inverter_fault_code(80a457d, 5b43be4, @dewet22) - New enums:
WorkMode,Certification,InverterType,Generation,Phase,MeterStatus,BatteryMaintenance(80a457d, 95aba95, @dewet22) inverter_fault_messagesfield onSinglePhaseInverterdecodes the HR(223/224) bitmask into a list of active fault name strings (5b43be4, @dewet22)min/maxphysical bounds onRegisterDefinitionfor out-of-range detection across all register LUTs (5e55be0, @dewet22)- accept mode parameter in set_calibrate_battery_soc (8139304, @dewet22)
- add slots 3-10 write commands and model-aware SlotMap dispatch (e7a88d0, @dewet22)
- per-model register block dispatch in load_config() and refresh() (1568038, @dewet22)
- per-commit and per-push overrides for the changelog bot (2eb890a, @dewet22)
- support prerelease and finalize bumps in release workflow (743f0e5, @dewet22)
๐ Changed¶
Inverterrenamed toSinglePhaseInverter;select_inverter()is now the recommended constructor โ it returns the right concrete type based on device model.Inverterremains importable as a deprecated alias (071c755, @dewet22)- Renamed
slave_addressโdevice_addressacross PDUs, and the matching capability fields onPlantCapabilities(inverter_slaveโinverter_address,meter_slavesโmeter_addresses,lv_battery_slavesโlv_battery_addresses,bcu_slavesโbcu_stacks) andHvStack(slave_addressโdevice_address), aligning with Modbus.org's 2020 terminology update. Legacy names remain as deprecation-warning aliases (#61, @dewet22) Plant.update()validates each incoming register bank before committing; banks with an invalid serial number (e.g. all-zero padding from an absent battery slot) are silently discarded rather than written into the cache (82ba7fc, @dewet22)- Bounds violations on physical measurements are logged at ERROR level and currently still committed โ enforcement (discard-on-violation) follows in a future release once the bounds have been validated in production (see #57) (5e55be0, @dewet22)
๐ Fixed¶
Converter.timeslotnow returnsNonefor raw register value60โ a hardware sentinel for an unset slot that previously causedValueError: minute must be in 0..59(f93f872, @dewet22)Client.one_shot_command()no longer callsconnect()internally โ calling it on an already-connected client was opening a second TCP connection, spawning duplicate consumer/producer tasks, and causing both tasks to race for reads on the sameStreamReader, permanently breaking the connection (2b33e61, @dewet22)- use dict.get() in nominal_voltage/nominal_frequency to avoid IndexError on unknown option (b5446a2, @dewet22)
- remove implicit connect() from one_shot_command (87d42bf, @dewet22)
- whitelist battery pause registers 318-320 in WRITE_SAFE_REGISTERS (9824e3b, @dewet22)
- add docstrings to deprecated slot wrappers; narrow types in slot tests (0038349, @dewet22)
- split three-phase HR 1060-1124 load into two โค60-register reads (31bce62, @dewet22)
- read/write CHANGELOG.md as UTF-8 explicitly (e4296d0, @dewet22)
- tighten loose ends spotted by Codacy + CodeRabbit + Gemini (f2e4013, @dewet22)
โ ๏ธ Deprecated¶
Inverterโ useSinglePhaseInverterdirectly, orselect_inverter()to get the correct type for a given devicecommands.enable_charge()/disable_charge()โ useset_enable_charge(bool)insteadcommands.enable_discharge()/disable_discharge()โ useset_enable_discharge(bool)insteadslave_addresskwarg/attribute on PDUs andslave_addressonHvStackโ usedevice_address.PlantCapabilities.inverter_slave/meter_slaves/lv_battery_slaves/bcu_slavesโ useinverter_address/meter_addresses/lv_battery_addresses/bcu_stacks. All emitDeprecationWarningon access
๐ง Maintenance¶
- add AGENTS.md with accurate architecture and dependency info (d7bc6a2, @dewet22)
- add logo; rationalise badges; fix Python capitalisation in blurb (6c4ef4d, @dewet22)
- add coverage for deprecation alias, slot maps, getter branches, and BatteryMaintenance (f3c3842, @dewet22)
- add .bandit INI file to exclude tests/ from bandit scan (f153a30, @dewet22)
- update usage guide, README, CONTRIBUTING, and add CLAUDE.md (f4f0ee4, @dewet22)
- purify Converter class and backfill three-phase/EMS register fields (851d4a9, @dewet22)
- add architecture overview with topology and component diagrams (7a7eca4, @dewet22)
- add load_config/refresh dispatch tests; drop email from CLAUDE.md (da4d218, @dewet22)
[1.3.0] - 2026-05-13¶
๐ง Maintenance¶
- notify downstream consumers on release via repository_dispatch (#53) (2a03623, @dewet22)
๐ Fixed¶
- prevent deadlock after inverter maintenance disconnect (#54) (4469b7a, @dewet22)
[1.2.0] - 2026-05-11¶
๐ Changed¶
- rewrite with commit attribution and full history backfill (1fbf3c2, @dewet22)
- Update givenergy_modbus/model/battery.py (128e3ba, @dewet22)
- fix off-by-one and replace assert in Plant.number_batteries (75f8425, @dewet22)
๐ Fixed¶
- stop ValueError on battery decode aborting number_batteries (#49, #51) (27e97b3, @dewet22)
- downgrade CRITICAL shutdown logs on intentional client.close() (#50) (d21cf66, @dewet22)
- drop battery_soc_reserve=100 from set_mode_storage (#27) (a6e5a1f, @dewet22)
- keep parens around multi-exception except for py313 compatibility (cd40d2f, @dewet22)
- append every commit on push to [Unreleased], not just head_commit (5ea934d, @dewet22)
๐ง Maintenance¶
- align with usb_device_inserted field name retained by Codacy revert (a471c9a, @dewet22)
[1.1.2] - 2026-05-11¶
๐ง Maintenance¶
- replace tag-triggered release with workflow_dispatch (dc78952, @dewet22)
- add Dependabot GitHub Actions version tracking (e1680f9, @dewet22)
[1.1.1] - 2026-05-11¶
๐ง Maintenance¶
- downgrade codecov upload failure to warning (12a3085, @dewet22)
๐ Changed¶
- migrate from Poetry to uv for dependency management (c8c6156, @dewet22)
๐ Fixed¶
- handle ConnectionResetError when closing client connection (b7109f1, @dewet22)
[1.1.0] - 2026-05-09¶
๐ Changed¶
- modernise type annotations and fix bandit/prek hooks (6d8c50f, @dewet22)
- bump inverter model to 1.1.0: new registers, computed battery_capacity_kwh, enum fix (23b9cb8, @dewet22)
[1.0.2] - 2026-05-09¶
๐ Changed¶
- remove unnecessary fail-fast: false from release workflow (2b65001, @dewet22)
- use Python 3.14 as default; fix version when package not installed (f46a4a4, @dewet22)
- update ruff target-version to py314 (867b90a, @dewet22)
- update docs for v1.0.0 API (bd21ab5, @dewet22)
- update root-level docs and config (3935f9d, @dewet22)
[1.0.1] - 2026-05-09¶
๐ Changed¶
- move docs publish to after pypi release in release workflow (ffc0b13, @dewet22)
- Set package-ecosystem to 'pip' in dependabot config (93228e6, @dewet22)
- update poetry.lock to resolve 32 dependabot alerts (d396420, @dewet22)
- bump all GitHub Actions to current versions (e83b8b1, @dewet22)
- fix release workflow: remove invalid fail_ci_if_error, use skip-existing (075a7dd, @dewet22)
- update README for v1.0.0 API (f54fbcb, @dewet22)
- credit pymodbus as inspiration in README (14103c8, @dewet22)
- migrate to prek, remove stale tooling, clean up post-1.0 (adcfe87, @dewet22)
- fix release workflow: Linux-only publish steps, fail-fast false (db01aaa, @dewet22)
[1.0.0] - 2026-05-09¶
A completely different approach to this library, handling comms in an asynchronous thread to avoid the old synchronous blocking and polling strategy. That started a rabbit hole of largely re-doing the entire architecture.
โจ Added¶
- New asyncio
Clientwith long-lived connections, producer/consumer task pair, andFuture-based response tracking โ replaces the old synchronousGivEnergyClient. - Passive monitoring mode: listen-only client that observes traffic without sending commands.
Plant.update(pdu)for incremental state updates from incoming PDU responses;Plantnow dynamically discovers the number of attached batteries.- Comprehensive holding register coverage: all HR(0โ300+) mapped and typed, including
charge_slot_2,battery_discharge_mode,battery_calibration_stage,modbus_address,modbus_version,system_time,enable_60hz_freq_mode,enable_drm_rj45_port,inverter_reboot, and ~30 additional fields. - Virtual aggregates:
p_pv_power,e_pv_total, and related computed fields. - Custom exception hierarchy for richer, typed error handling.
- Community contributions: inverter register additions from @holdestmade and @dominic.
๐ Changed¶
- PDUs now encode and decode themselves;
Framerrefactored to pass raw frames to the callback and invoke it on decode failure as well as success. RegisterCachebound to an explicit slave address; sanity-check gates stale or malformed PDU updates.- Registers refactored to plain data containers; behaviour (validation, scaling, conversion) moved into
InverterandBatterymodels. Coordinatorrenamed toClient; network layer folded into the same class.TimeSlotmoved to the model package.- Python 3.9+ minimum (Python 3.7 and 3.8 dropped).
- modernisation phases 1 & 2: Python 3.13+, ruff, drop legacy tooling (18ca4dc, @dewet22 ๐)
- modernisation phase 3: pydantic v2 migration (98b89fd, @dewet22)
- modernisation phase 4: remove arrow and bump2version deps (ee6b5c0, @dewet22)
- modernisation phase 5: type annotation modernisation (568a722, @dewet22)
- modernisation phase 6: CI/CD update (142be88, @dewet22)
- note missing Gen 2 (EA prefix) inverter model support (cd131bb, @dewet22)
- fix CI failures on Python 3.13/3.14 (450e2dc, @dewet22)
- bump outdated dev/test dependencies (8cce166, @dewet22)
- bump mypy to ^2.0.0 and fix newly surfaced type errors (24c64ad, @dewet22)
- fix mkdocs deprecation warnings (dd4f546, @dewet22)
- add security fix tests and tidy changelog (2a22379, @dewet22)
๐๏ธ Removed¶
pymodbusruntime dependency โ the Modbus protocol is now implemented entirely in-library.- Old synchronous CLI (spun out to a separate package).
๐ Security¶
- fix five issues identified in audit (2e38d5e, @dewet22)
[0.10.1] - 2022-03-03¶
๐ Fixed¶
- ๐ Make
Plantserializable.
[0.10.0] - 2022-03-02¶
โจ Added¶
- ๐ช Reintroduced the battery energy totals on the
Batterymodel. On some firmware versions that is populated instead of the values from the inverter. (#7, via @britkat1980)
๐ Changed¶
- โ ๏ธ Breaking change: rejigged the
Plantmodel to abstract awayRegisterCaches and remove some of the toil around managing state.README.mdupdated with example implementation.
[0.9.4] - 2022-02-15¶
โจ Added¶
- ๐ Enable CodeQL GitHub workflow for automated code quality scans
๐ Fixed¶
- ๐ Allow multiple serial number prefixes to map to the same inverter model name (#6, by @zaheerm)
[0.9.3] - 2022-02-01¶
๐ Fixed¶
- ๐งฝ Update total energy registers (by @britkat1980)
- ๐ Re-enable builds back to python v3.7 to support e.g. Raspberry Pi current version
- ๐งน Update python and pre-commit deps, including security fix for loguru
[0.9.2] - 2022-01-24¶
๐ Fixed¶
- ๐ Scaled registers to use division instead of multiplication โ prevents rounding errors.
- ๐ Update README.md to match reality better
- ๐งฝ Update deps
- ๐ Try to re-enable GH pages
[0.9.1] - 2022-01-13¶
๐ Fixed¶
- ๐ The
_timefault registers don't denote a BCD-encoded timestamp, but seems to be a counter of #cycles the fault lasted. - Sometimes a time slot timestamp is returned as
60minutes. Guard by taking the modulo-60 instead.
[0.9.0] - 2022-01-13¶
โจ Added¶
- ๐ช Create
RegisterCacheandRegisterGetterto contain the custom register data structures in one place. Also started aPlantmodel to be a container for all devices in a given system. - ๐ Add JSON processing for the RegisterCache โ mostly to help with testing but also expecting debugging other plants to benefit from it.
- ๐ท Add some more test cases with actual register data.
- ๐จ Added some recovery logic to the framer โ try to scan ahead for other messages instead of truncating the entire buffer when there's unexpected data incoming. Hopefully this helps when the communication stream seems to get out of sync a bit.
- ๐
Add an
ErrorResponsePDU so we can try and cope better when the inverter throws error responses. - ๐งฝ Added
absolufy-importsandautoflaketo pre-commit checks.
๐ Changed¶
- โ ๏ธ Ensure we check charge and discharge limits: current hardware cannot support >50% (i.e. >2.6kW) rates.
- โ Make sure we query the 180+ block of input registers too, since it contains (amongst others) battery energy counters.
- ๐ค Split out querying the battery/BMS registers since this will vary depending on how many batteries the user has. The
slave address of the request determines which battery unit is targeted.
- Also start modeling the Battery as separate from the Inverter.
- ๐ Collapse the register cache to a single dict since we can use the
HoldingRegister/InputRegisteridentity to discern between the types. It makes the data structures a lot simpler. - ๐ Improve the CLI โ it is already a useful tool to dump registers for debugging right now.
- ๐ณ Changed to target slave id 0x11 by default instead of 0x32. 0x32 shadows 0x11 but seems to be the first battery,
with subsequent batteries living at the following slave addresses.
- โ๏ธ reverted that change because it seems to affect the cloud metrics quite badly when you query frequently.
- ๐คซ Squelch flake8 warnings about missing constructor and magic method docstrings.
- ๐ฉน Update README to show usage properly.
- ๐งน Update python deps.
[0.8.0] - 2022-01-09¶
โจ Added¶
- A large number of convenience methods in the client to alter the state of the inverter:
enable_charge_target(target_soc: int)&disable_charge_target()enable_charge()&disable_charge()enable_discharge()andenable_discharge()set_battery_discharge_mode_max_power()andset_battery_discharge_mode_demand()set_charge_slot_n((start_time: datetime.time, end_time: datetime.time))andreset_charge_slot_n()for slots 1 & 2. Also matchingset_discharge_slot_n((start_time, end_time))andreset_discharge_slot_n().set_mode_dynamic()to maximise storage of generation and discharge during demand. This mirrors "mode 1" operation in the portal.set_mode_storage(slot_1, [slot_2], [export])which keeps the battery charge topped-up and discharges during specified periods. This mirrors modes 2-4 in the portal.set_datetime(datetime)to set the inverter date & time.
- Ensure we always close the network socket after every request. Sometimes the inverter turns orange/grey in the portal after executing queries via this library, and this seems to mitigate against it โ possible the inverter TCP stack isn't closing half-closed sockets aggressively enough?
๐ Changed¶
- Potentially breaking: Once again, pretty wholesale renaming of registers to more official designations, and standardising naming somewhat. Part of the motivation for adding more convenience functions is so clients never have to deal with register names directly, so this should hopefully make future renaming easier.
[0.7.0] - 2022-01-05¶
โจ Added¶
- Another register whitelist and check in the
WriteHoldingRegisterRequestPDU as another layer of checks to not inadvertently write to unsafe registers. Add a test to ensure the allow list stays in sync with the register definitions frommodel.register_banks.HoldingRegister. - A bunch of convenience methods to write data to the inverter without needing any knowledge of registers.
See
client.GivEnergyClientwhich has a number ofset_*methods.
๐ Changed¶
- Split out the end-user client functionality from the Modbus client - they were getting too entangled unnecessarily. Updated example code in README for reference.
- Renamed
target_soctobattery_target_socinstead.
[0.6.2] - 2022-01-04¶
๐ Fixed¶
- Will this fix
mindsers/changelog-reader-action@v2?
[0.6.1] - 2022-01-04¶
๐ Fixed¶
- Fix stupid pypi classifier strictness
[0.6.0] - 2022-01-04¶
๐ Changed¶
- [BREAKING CHANGE] registers have been widely renamed for consistency and clarity. The joys of a pre-release API.
- Checked all the registers and their values to make sense. Added units for most that are self-evident. The
Inverterobject is shaping up nicely as a user-friendly representation of the inverter dataset โ TODO is likely splitting out aBatteryrepresentation too, to account for systems with multiple battery units (and those without batteries at all!). The same might make sense for the PV aspect as well.
๐ Fixed¶
- Avoid loading a whole batch of input registers that seem completely unused and save a network call.
- Match prod release workflow to preview to use py3.9
- Update PyPI classifiers to specify Alpha quality :)
0.5.0 (2022-01-04)¶
- Simplify the client contract so you only work with structured data instead of register banks.
- Add example use to README
0.4.0 (2022-01-03)¶
- Implement writing values to single holding registers
0.3.0 (2022-01-03)¶
- Make register definitions a bit more flexible to cater for units and descriptions in future
0.2.0 (2022-01-03)¶
- Fix GitHub actions & codecov
0.1.1 (2022-01-02)¶
- Update deps
- Rename a few class names for consistency
- Add a few more attributes to export
0.1.0 (2022-01-02)¶
- First release on PyPI