Skip to content

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): the 0x31 read-alias for AC and HYBRID_GEN1 is retired. Live validation on both families showed 0x11 serves 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 old 0x31 keep working (the hardware facade still answers there) and self-heal to 0x11 on the next detect(); 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_async fork 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 authoritative Plant.inverter_serial accessor 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() and Plant.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_number has been removed (#191) โ€” it was unreliable on real hardware. Use the typed-device enumeration or Plant.serial_index.
  • For inverter identity, prefer the new Plant.inverter_serial; Plant.inverter_serial_number remains 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

  • 106 Phase 3 โ€” serial reconciliation + Plant.serial_index (#216) (e8da546)

๐Ÿ”„ 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:

  • ThreePhaseInverter for three-phase models (HR/IR 1000โ€“1420), including grid protection limits, derating curves, per-phase measurements, EPS/backup mode, energy counters, slot registers, and force_charge_enable / battery_maintenance_mode (09e37f0, 95aba95, @dewet22)
  • Bcu and Bmu model classes for HV battery stacks (AIO / HV Gen3 / AIO-Hybrid systems) (ee15702, @dewet22)
  • Meter and MeterProduct model classes for external meter slaves at addresses 0x01โ€“0x08, read via FC 0x04 and FC 0x16 respectively (c6d3a0a, @dewet22)
  • Ems model for EMS plant status and configuration registers (3621938, @dewet22)
  • Gateway and Gateway2 models with select_gateway() for automatic firmware-version-based dispatch (752160a, @dewet22)
  • select_inverter(model, register_cache) returns SinglePhaseInverter | ThreePhaseInverter based on device type (071c755, @dewet22)

Plant and client lifecycle:

  • PlantCapabilities dataclass captures full device topology from detect(): 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 old refresh_plant_data() monolith (ffa78fc, 9936e98, @dewet22)
  • Typed plant accessors โ€” plant.inverter, plant.batteries, plant.hv_stacks, plant.meters, plant.ems, plant.gateway โ€” all dispatch via capabilities rather than hardcoded device-address arithmetic (9936e98, @dewet22)

Commands:

  • SlotMap frozen dataclass for model-driven slot register routing; all slot setters now accept slot_map: SlotMap = SINGLE_PHASE_SLOTS โ€” three-phase callers pass plant.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:

  • MR register namespace and ReadMeterProductRegisters PDU (FC 0x16) for meter product identification registers (ef89324, @dewet22)
  • resolve_model(dtc, arm_fw) for firmware-version-aware device model resolution (09dea7b, @dewet22)
  • Extended Model enum: HYBRID_GEN1/2/3/4, HYBRID_HV_GEN3, ALL_IN_ONE_HYBRID, POLAR, EMS_COMMERCIAL, AIO_COMMERCIAL; existing single-character Model(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_messages field on SinglePhaseInverter decodes the HR(223/224) bitmask into a list of active fault name strings (5b43be4, @dewet22)
  • min/max physical bounds on RegisterDefinition for 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

  • Inverter renamed to SinglePhaseInverter; select_inverter() is now the recommended constructor โ€” it returns the right concrete type based on device model. Inverter remains importable as a deprecated alias (071c755, @dewet22)
  • Renamed slave_address โ†’ device_address across PDUs, and the matching capability fields on PlantCapabilities (inverter_slave โ†’ inverter_address, meter_slaves โ†’ meter_addresses, lv_battery_slaves โ†’ lv_battery_addresses, bcu_slaves โ†’ bcu_stacks) and HvStack (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.timeslot now returns None for raw register value 60 โ€” a hardware sentinel for an unset slot that previously caused ValueError: minute must be in 0..59 (f93f872, @dewet22)
  • Client.one_shot_command() no longer calls connect() 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 same StreamReader, 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 โ€” use SinglePhaseInverter directly, or select_inverter() to get the correct type for a given device
  • commands.enable_charge() / disable_charge() โ€” use set_enable_charge(bool) instead
  • commands.enable_discharge() / disable_discharge() โ€” use set_enable_discharge(bool) instead
  • slave_address kwarg/attribute on PDUs and slave_address on HvStack โ€” use device_address. PlantCapabilities.inverter_slave / meter_slaves / lv_battery_slaves / bcu_slaves โ€” use inverter_address / meter_addresses / lv_battery_addresses / bcu_stacks. All emit DeprecationWarning on 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 Client with long-lived connections, producer/consumer task pair, and Future-based response tracking โ€” replaces the old synchronous GivEnergyClient.
  • Passive monitoring mode: listen-only client that observes traffic without sending commands.
  • Plant.update(pdu) for incremental state updates from incoming PDU responses; Plant now 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; Framer refactored to pass raw frames to the callback and invoke it on decode failure as well as success.
  • RegisterCache bound 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 Inverter and Battery models.
  • Coordinator renamed to Client; network layer folded into the same class.
  • TimeSlot moved 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

  • pymodbus runtime 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 Plant serializable.

[0.10.0] - 2022-03-02

โœจ Added

  • ๐Ÿ’ช Reintroduced the battery energy totals on the Battery model. On some firmware versions that is populated instead of the values from the inverter. (#7, via @britkat1980)

๐Ÿ”„ Changed

  • โš ๏ธ Breaking change: rejigged the Plant model to abstract away RegisterCaches and remove some of the toil around managing state. README.md updated 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 _time fault 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 60 minutes. Guard by taking the modulo-60 instead.

[0.9.0] - 2022-01-13

โœจ Added

  • ๐Ÿ’ช Create RegisterCache and RegisterGetter to contain the custom register data structures in one place. Also started a Plant model 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 ErrorResponse PDU so we can try and cope better when the inverter throws error responses.
  • ๐Ÿงฝ Added absolufy-imports and autoflake to 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/InputRegister identity 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() and enable_discharge()
    • set_battery_discharge_mode_max_power() and set_battery_discharge_mode_demand()
    • set_charge_slot_n((start_time: datetime.time, end_time: datetime.time)) and reset_charge_slot_n() for slots 1 & 2. Also matching set_discharge_slot_n((start_time, end_time)) and reset_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 WriteHoldingRegisterRequest PDU 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 from model.register_banks.HoldingRegister.
  • A bunch of convenience methods to write data to the inverter without needing any knowledge of registers. See client.GivEnergyClient which has a number of set_* 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_soc to battery_target_soc instead.

[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 Inverter object is shaping up nicely as a user-friendly representation of the inverter dataset โ€“ TODO is likely splitting out a Battery representation 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