Python client for the SAJ Elekeeper API
Project description
PySaj
Async Python client for the current SAJ Elekeeper API at
https://eop.saj-electric.com/.
Looking for testers! PySaj has been validated against SAJ inverters with serial prefixes R5X (on-grid) and ASS (AC-coupling with battery). If you have a different SAJ device and are willing to test, please open an issue — your feedback helps expand coverage.
PySaj targets the newer Elekeeper web API (/dev-api/api/v1/...) used by the
current SAJ portal. It does not use the legacy /saj/login cookie flow.
Status
This package is in alpha. The implemented endpoints have been validated against a real Elekeeper account, but the upstream API is private and can change without notice.
Features
- Async
httpxclient. - Elekeeper-compatible AES password encryption.
- Elekeeper-compatible request signing.
- Login, refresh token, and logout helpers.
- Plant list and plant detail helpers.
- Power flow, energy statistics, battery, device, weather, and chart helpers.
- Raw authenticated
GETandPOSThelpers for endpoints not wrapped yet. - CLI discovery sample with JSONL output.
- CLI plant summary focused on relevant daily values.
Installation
From GitHub:
python -m pip install pysaj-elekeeper
From a local checkout:
git clone https://github.com/giovadroid/pysaj.git
cd pysaj
uv sync --extra dev
Editable install without uv:
python -m venv .venv
. .venv/bin/activate
python -m pip install -e ".[dev]"
Credentials
Create a local .env file from the example:
cp .env.example .env
Then fill:
SAJ_USER=your-email@example.com
SAJ_PASS=your-password
SAJ_BASE_URL=https://eop.saj-electric.com
Library Usage
High-level API for normal integrations:
import asyncio
import os
from pysaj import SajClient
async def main() -> None:
async with SajClient() as client:
await client.authenticate(os.environ["SAJ_USER"], os.environ["SAJ_PASS"])
overview = await client.get_plant_overview()
# Or select explicitly:
# overview = await client.get_plant_overview(plant_name="Home")
# overview = await client.get_plant_overview(plant_uid="...")
print(overview.name)
print(overview.pv_power_w)
print(overview.battery_soc_percent)
asyncio.run(main())
Lower-level endpoint wrappers are also available when you need raw Elekeeper payloads:
import asyncio
import os
from pysaj import SajClient
async def main() -> None:
async with SajClient() as client:
await client.login(os.environ["SAJ_USER"], os.environ["SAJ_PASS"])
first = await client.get_primary_plant()
if first is None:
return
plant_uid = first["plantUid"]
device_sn = await client.get_primary_device_sn(plant_uid)
flow = await client.get_device_energy_flow(plant_uid, device_sn=device_sn)
battery = await client.get_one_device_battery_info(device_sn) if device_sn else {}
print(flow.get("totalPvPower"), battery.get("batEnergyPercent"))
asyncio.run(main())
CLI Examples
Both examples read credentials from a .env file in the current directory:
cp .env.example .env
# edit SAJ_USER and SAJ_PASS
Discovery sample
python examples/sample.py
python examples/sample.py --jsonl pysaj-sample.jsonl
Logs in, lists plants, fetches the first plant's detail, device, battery, weather, and chart endpoints. Prints a compact discovery summary and writes full endpoint payloads to JSONL.
Plant summary
python examples/plant_summary.py
python examples/plant_summary.py --plant-uid <plant-uid>
python examples/plant_summary.py --plant-name <plant-name>
Prints relevant daily plant values:
- Current PV, load, grid, and battery power flow.
- Today's production, load, import, export, battery charge, and battery discharge.
- Lifetime plant totals.
- Battery SOC, SOH, voltage, current, temperature, and work time.
- Device rows with current power, daily production, and SOC where available.
Wrapped Endpoints
Auth and account:
authenticateloginrefresh_access_tokenlogoutget_login_info
Plants and home statistics:
list_plantsget_primary_plantget_plant_by_uidget_plant_by_nameget_primary_device_snget_plant_overviewget_end_user_plant_listget_plant_listget_plant_infoget_plant_parallel_infoget_device_energy_flowget_home_energy_statisticsget_home_power_statisticsget_home_plant_statisticsget_plant_statistics_dataget_home_battery_statisticsget_home_device_statistics
Devices and battery:
get_device_listget_battery_listget_one_device_infoget_one_device_battery_infoget_order_status_summary_report_info
Weather and charts:
get_current_weatherget_forecast_weatherget_self_use_energy_dataget_store_power_analysis_dataget_store_energy_compare_dataget_store_energy_balance_dataget_grid_curve_analysis_dataget_grid_energy_statistics_data
Escape hatches:
get_rawpost_raw
Known Limitations
getPlantSankeyDiagramreturned upstreamerrCode=10001for the tested account and is intentionally not called by the sample.- Some endpoints require
deviceSn; for the tested plant this came fromget_plant_info()["deviceSnList"][0]. - Some dashboard endpoints use
searchOfficeIdArr, available fromget_login_info()["officeId"].
API Documentation
Generated from docstrings with pdoc. Hosted at https://giovadroid.github.io/pysaj.
To generate locally:
make docs # generate HTML → docs/api/
make docs-serve # live preview at http://localhost:8080
Or directly:
uv run pdoc -o docs/api src/pysaj
Development
uv sync --extra dev
uv run ruff check .
uv run python -m pytest -q
uv build
uv run twine check dist/*
GitHub Actions:
CIvalidates lint, tests, and package build on pushes and pull requests.Releasebuilds GitHub Release artifacts when pushing a SemVer tag such asv0.1.0.Publish to PyPIis manual and uses PyPI Trusted Publishing. It is included for later use, but GitHub releases are the default publishing path for now.
License
MIT
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pysaj_elekeeper-0.0.7.tar.gz.
File metadata
- Download URL: pysaj_elekeeper-0.0.7.tar.gz
- Upload date:
- Size: 17.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2bbee2cd58a7743d8aef5518b08150dea3a8286f8450ba7cf7d90e683a2648df
|
|
| MD5 |
fd3e4f3c697e07da12a750941ca7bbf6
|
|
| BLAKE2b-256 |
23ea70912374e09e858d3bba649f029bb19d1ec44555ce2d1290cb0ad3e74070
|
Provenance
The following attestation bundles were made for pysaj_elekeeper-0.0.7.tar.gz:
Publisher:
pypi.yml on giovadroid/pysaj-elekeeper
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pysaj_elekeeper-0.0.7.tar.gz -
Subject digest:
2bbee2cd58a7743d8aef5518b08150dea3a8286f8450ba7cf7d90e683a2648df - Sigstore transparency entry: 1525375635
- Sigstore integration time:
-
Permalink:
giovadroid/pysaj-elekeeper@c48c3ff8a0e640892b14495453d391bc7a848f4d -
Branch / Tag:
refs/heads/main - Owner: https://github.com/giovadroid
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
pypi.yml@c48c3ff8a0e640892b14495453d391bc7a848f4d -
Trigger Event:
workflow_run
-
Statement type:
File details
Details for the file pysaj_elekeeper-0.0.7-py3-none-any.whl.
File metadata
- Download URL: pysaj_elekeeper-0.0.7-py3-none-any.whl
- Upload date:
- Size: 14.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
214ee7ef9071863376439b8ff929d53eb8308927092569add2c2cd4c219de6a3
|
|
| MD5 |
92cfffb93a25712a3e66c03f08d4bcef
|
|
| BLAKE2b-256 |
7ad43b062992ba7f9ef866b33fa752e67ebc1bda4cbfc2a8f0a2bd044461c22d
|
Provenance
The following attestation bundles were made for pysaj_elekeeper-0.0.7-py3-none-any.whl:
Publisher:
pypi.yml on giovadroid/pysaj-elekeeper
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pysaj_elekeeper-0.0.7-py3-none-any.whl -
Subject digest:
214ee7ef9071863376439b8ff929d53eb8308927092569add2c2cd4c219de6a3 - Sigstore transparency entry: 1525375651
- Sigstore integration time:
-
Permalink:
giovadroid/pysaj-elekeeper@c48c3ff8a0e640892b14495453d391bc7a848f4d -
Branch / Tag:
refs/heads/main - Owner: https://github.com/giovadroid
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
pypi.yml@c48c3ff8a0e640892b14495453d391bc7a848f4d -
Trigger Event:
workflow_run
-
Statement type: