Skip to main content

aiosaj

PyPI Python tests License: MIT

Async library to read SAJ solar inverters over their Ethernet (real_time_data.xml) and WiFi (status/status.php) interfaces, written for Home Assistant.

aiosaj continues fredericvl/pysaj, which was based on the pysma component written by @kellerza. It is published under a new name and imported as aiosaj, so it can be installed next to the original pysaj.

pip install aiosaj
import asyncio

from aiosaj import SAJ, Sensors


async def main():
    sensors = Sensors(wifi=True)
    saj = SAJ("192.168.1.100", wifi=True)
    if await saj.read(sensors):
        print(sensors["current_power"].value, sensors["total_yield"].value)


asyncio.run(main())

Migrating from pysaj: install aiosaj and change import pysaj / from pysaj import ... to aiosaj. The API is otherwise the same as pysaj 0.1.x; see CHANGELOG.md for what differs from upstream 0.0.16.

Sensors

Sensors(wifi=True) reads the WiFi module's status/status.php; Sensors(wifi=False) reads the ethernet interface's real_time_data.xml. A sensor is only reported as enabled once the inverter has actually returned a value for it, so channels a particular inverter does not have (a third PV string, unused string currents) never show up.

Sensor Unit Interface
current_power W both
today_yield, total_yield kWh both
today_time, total_time h both
total_co2_reduced kg both
temperature °C both
state both
today_max_current W ethernet
pv1_voltage … pv3_voltage V WiFi ✓ · LAN pending¹
pv1_current … pv3_current A WiFi ✓ · LAN pending¹
pv1_string1_current … pv3_string4_current A WiFi ✓ · LAN pending¹
grid_frequency Hz WiFi ✓ · LAN pending¹
line1_voltage … line3_voltage V WiFi ✓ · LAN pending¹
line1_current … line3_current A WiFi ✓ · LAN pending¹
bus_voltage V WiFi ✓ · LAN pending¹

¹ These values are not WiFi-exclusive — the inverter measures them regardless of how you connect. They are currently wired up and verified only for the WiFi interface, where field positions, scaling and units come from the module's own status.html (its cf array) and /i18n/en/status.xml (its u<N> entries). The ethernet/LAN interface exposes the same values in real_time_data.xml, under different tag names.

Two independently posted real_time_data.xml dumps (see Sources) agree on these, so they are the tags LAN support should use:

Sensor LAN tag
pv1_voltage … pv3_voltage v-pv1, v-pv2, v-pv3
pv1_string1_current … pv3_string4_current i-pv11 … i-pv34
bus_voltage v-bus
line1_voltage … line3_voltage Vac_l1, Vac_l2, Vac_l3
line1_current … line3_current Iac_l1, Iac_l2, Iac_l3
grid_frequency Freq1, and Freq2 / Freq3 per further phase

pv1_current … pv3_current have no LAN counterpart: the XML reports string currents only. The LAN interface also carries per-phase power (pac1, pac2, pac3) that the WiFi record does not.

Those tag names come from public dumps rather than from an inverter this library has been run against, so they are deliberately not wired up yet - a sensor reading a guessed tag that turns out to be wrong would be worse than one that stays disabled. If you have a LAN-connected SAJ inverter, curl http://<inverter>/real_time_data.xml and open an issue with the output; confirming the tags is all that is left to do.

Sources

The LAN tag names in ¹ above are taken from these publicly posted real_time_data.xml dumps, which agree with each other:

Validation

The WiFi module returns malformed records - truncated bodies, wait markers, zeroed registers while it is starting up. Its own web UI throws these away (if(35!=s.length){return;}); this library does the same, because a partial record otherwise yields plausible looking numbers read from the wrong field positions.

A record is only applied when:

  • it is a single line whose field count matches a known layout (35, or 23 for older firmware),
  • the daily counters do not exceed their lifetime counters,
  • each reading falls inside that sensor's plausible range.

65535 is the module's not-available sentinel and never becomes a reading.

Readings taken while the inverter's run state is not Normal (waking up, waiting, error) are not trusted: live values become None and the counters keep their last known value.

Lifetime counters (total_yield, total_time, total_co2_reduced) may never decrease. Home Assistant reads them as total_increasing, so a single low sample is taken as a meter reset and the whole counter is then booked as fresh production. Implausibly large forward jumps are held until several consecutive readings agree, so a corrupt high value cannot become the new baseline either.

Metadata

Release files for aiosaj 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for aiosaj 0.2.0
File Size Uploaded
aiosaj-0.2.0.tar.gz 21.4 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for aiosaj 0.2.0
File Interpreter ABI Platform
aiosaj-0.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 34.6 kB

Release files / aiosaj-0.2.0.tar.gz

Download URL aiosaj-0.2.0.tar.gz
Size 21.4 kB
Tags Source
SHA-256 checksum
How to use checksums
2718b1ad09e439d94fa986ecc78d167bc2d66ba1f3ac1471083e5b6a9c11dbdd
BLAKE2b-256 checksum
How to use checksums
94f5b183267630b76434ccce974dee6fd2a4d5bc780a5aa66dac6acc75786de7
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 19, 2026.

Transparency log

Release files / aiosaj-0.2.0-py3-none-any.whl

Download URL aiosaj-0.2.0-py3-none-any.whl
Size 13.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
d53bf8da851f2832c140421472f0c9d96b6bd51f5bcec3f838a831ce39166a63
BLAKE2b-256 checksum
How to use checksums
a8f4c6ed934f0d6f5e001f2acf02badd70b4a1d13246fad6765fdc17cd93fbbb
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 19, 2026.

Transparency log

Release history Release notifications | RSS feed

0.2.1

2 release files

This release

0.2.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page