Skip to content

v0.2.0: Clear skies ☀️

Latest

Choose a tag to compare

@github-actions github-actions released this 15 Jul 07:30
Immutable release. Only release title and notes can be modified.
aa8ce46

☀️ Clear skies

This release is about honest readings. solaredge already decoded a point the device does not implement to None. This round teaches it to distrust a few values that look like data but are not, and to stop assuming a register block is there before it has proof. Each change lines up with what the long-standing community integrations learned the hard way against real hardware.

Probe the grid status extension instead of assuming it

The grid on/off status (40113) and extended vendor status (40119) registers are a SolarEdge extension that not every firmware serves. They were declared on the always-present Inverter, and because the poll pools the identity and model block into one read spanning 40004 through 40120, an inverter that rejects those registers failed the entire poll with a Modbus exception. The probe never touched 40113, so such a device probed fine and was then permanently unreadable.

They now live on InverterExtended, and async_probe detects them the same way it probes the control blocks: an illegal-address answer on 40113 means absent. Firmware that serves the extension sees no change. Firmware that does not now polls cleanly, with on_grid and vendor_status_extended reading None. A consumer that needs to tell "extension absent" from "not reported right now" can check isinstance(inverter, InverterExtended).

This is the one breaking change, and only for consumers that build SolarEdge(unit) through the constructor: grid status is now off by default there. Pass the new grid_status=True flag, or use async_probe, which detects it for you. async_probe consumers see no difference.

Out-of-range battery percentages decode to None

An initializing battery, and the odd communication glitch, reports a state of energy or health outside 0-100: negatives, values just over 100, or plain float garbage. Those are not readings, and a graph cannot tell them from real data. state_of_energy and state_of_health are now properties over raw fields (the same pattern as site_limit): a value outside an inclusive 0-100 reads None, so an empty or a perfectly healthy battery still reports a real value.

A lifetime energy of 0 decodes as not accumulated

SunSpec types the inverter's lifetime energy (40093) as acc32, where 0 means "not accumulated", and some firmware reports 0 transiently around the sleep/wake transition. It was modelled as uint32, so that transient 0 posed as a genuine reading of zero, which is poison for anything tracking a monotonic counter. A factory-new inverter now reads ac_energy as None until its first watt-hour. The meter accumulators deliberately stay uint32, where a freshly commissioned meter's 0 is a genuine reading.

Under the hood

Now built and verified against modbus-connection 3.7.0. The register map gained a second, independent conformance oracle (the official SunSpec model generator) alongside the hand-parsed one, plus a regression test for partial block reads.

pip install solaredged

A note on the version

Still 0.x, still pre-1.0. The decode contract is holding steady (a point the device does not serve, or a value that cannot be a real reading, is None, never a guess), but parts of the API may still shift before 1.0.

Clear skies. ☀️

../Frenck

                       

Blogging my personal ramblings at frenck.dev

What's changed

🚨 Breaking changes

  • Probe the grid status extension instead of assuming it @frenck (#9)

🐛 Bug fixes

  • Probe the grid status extension instead of assuming it @frenck (#9)
  • Decode out-of-range battery percentages to None @frenck (#10)
  • Decode a lifetime energy of 0 as not accumulated @frenck (#11)

🚀 Enhancements

⬆️ Dependency updates