Skip to main content

Python client for the SAJ Elekeeper API

Project description

PySaj

CI PyPI Python License: MIT Docs

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 httpx client.
  • 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 GET and POST helpers 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:

  • authenticate
  • login
  • refresh_access_token
  • logout
  • get_login_info

Plants and home statistics:

  • list_plants
  • get_primary_plant
  • get_plant_by_uid
  • get_plant_by_name
  • get_primary_device_sn
  • get_plant_overview
  • get_end_user_plant_list
  • get_plant_list
  • get_plant_info
  • get_plant_parallel_info
  • get_device_energy_flow
  • get_home_energy_statistics
  • get_home_power_statistics
  • get_home_plant_statistics
  • get_plant_statistics_data
  • get_home_battery_statistics
  • get_home_device_statistics

Devices and battery:

  • get_device_list
  • get_battery_list
  • get_one_device_info
  • get_one_device_battery_info
  • get_order_status_summary_report_info

Weather and charts:

  • get_current_weather
  • get_forecast_weather
  • get_self_use_energy_data
  • get_store_power_analysis_data
  • get_store_energy_compare_data
  • get_store_energy_balance_data
  • get_grid_curve_analysis_data
  • get_grid_energy_statistics_data

Escape hatches:

  • get_raw
  • post_raw

Known Limitations

  • getPlantSankeyDiagram returned upstream errCode=10001 for the tested account and is intentionally not called by the sample.
  • Some endpoints require deviceSn; for the tested plant this came from get_plant_info()["deviceSnList"][0].
  • Some dashboard endpoints use searchOfficeIdArr, available from get_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:

  • CI validates lint, tests, and package build on pushes and pull requests.
  • Release builds GitHub Release artifacts when pushing a SemVer tag such as v0.1.0.
  • Publish to PyPI is 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

pysaj_elekeeper-0.0.6.tar.gz (17.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pysaj_elekeeper-0.0.6-py3-none-any.whl (14.9 kB view details)

Uploaded Python 3

File details

Details for the file pysaj_elekeeper-0.0.6.tar.gz.

File metadata

  • Download URL: pysaj_elekeeper-0.0.6.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

Hashes for pysaj_elekeeper-0.0.6.tar.gz
Algorithm Hash digest
SHA256 0d9a9413b39c34bb2f90b7651c88a427f50a98bc939dd0f66700695acb83eb2e
MD5 3d7816bfecab4eb115657ec9b55f175c
BLAKE2b-256 7ba25fc0fbed9c8f916de04fc88af21390ed996b1ca7ee5b18971498eca6d915

See more details on using hashes here.

Provenance

The following attestation bundles were made for pysaj_elekeeper-0.0.6.tar.gz:

Publisher: pypi.yml on giovadroid/pysaj-elekeeper

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pysaj_elekeeper-0.0.6-py3-none-any.whl.

File metadata

  • Download URL: pysaj_elekeeper-0.0.6-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

Hashes for pysaj_elekeeper-0.0.6-py3-none-any.whl
Algorithm Hash digest
SHA256 6797ca81d6f1cc3520fae35a03bd0fb0762d1f384b3c8fdf6ed8781124dbcfd5
MD5 818c32e4d1dd6893fe3033ec66cd106f
BLAKE2b-256 088f9d53f9465a6e8d8a4d7e7ef8172fb449b10133180af63b2d1c761e4be5f4

See more details on using hashes here.

Provenance

The following attestation bundles were made for pysaj_elekeeper-0.0.6-py3-none-any.whl:

Publisher: pypi.yml on giovadroid/pysaj-elekeeper

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page