aiosaj
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:
- https://forum.domoticz.com/viewtopic.php?t=30154
- https://community.openhab.org/t/read-networkbinding-xml-file-from-saj-suntrino-inverter/105690
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)
| File | Size | Uploaded | |
|---|---|---|---|
| aiosaj-0.2.0.tar.gz | 21.4 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| 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 logRelease 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