Metadata-Version: 2.4
Name: pychargefox
Version: 0.1.1
Summary: Async Python client for the Chargefox GraphQL API
Author-email: brendann993 <brendann993@icloud.com>
License-Expression: MIT
Project-URL: Documentation, https://github.com/brendann993/pychargefox#readme
Project-URL: Issues, https://github.com/brendann993/pychargefox/issues
Project-URL: Source, https://github.com/brendann993/pychargefox
Project-URL: Download, https://github.com/brendann993/pychargefox/releases
Keywords: asyncio,chargefox,electric-vehicle,ev-charging,graphql
Classifier: Development Status :: 4 - Beta
Classifier: Framework :: AsyncIO
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Topic :: Internet :: WWW/HTTP
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.12
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: aiohttp>=3.9
Provides-Extra: dev
Requires-Dist: build>=1.2; extra == "dev"
Requires-Dist: coverage>=7.6; extra == "dev"
Requires-Dist: ruff>=0.15.1; extra == "dev"
Requires-Dist: twine>=6.0; extra == "dev"
Dynamic: license-file

# pychargefox

Async Python client for the Chargefox GraphQL API.

> [!WARNING]
> This is an unofficial project and is not affiliated with, endorsed by, or connected to Chargefox. It uses an undocumented GraphQL API that may change or stop working without notice.

> [!NOTE]
> This project was developed with AI-assisted coding. Its behavior has been reviewed and tested, but users should independently evaluate it before relying on it.

## Install

```bash
pip install pychargefox
```

For local development, install the project into its virtual environment in editable mode:

```bash
python -m pip install --editable ".[dev]"
```

## Example

```python
import asyncio

from chargefox import Bounds, ChargefoxClient


async def main() -> None:
    async with ChargefoxClient() as client:
        stations = await client.get_charge_stations_by_bounds(
            Bounds(south=-32.5, west=115.0, north=-31.0, east=116.5)
        )

        for station in stations:
            print(station.name, station.status, station.online)
            for connector in station.connectors:
                plug_name = connector.plug.short_name if connector.plug else "Unknown"
                print(plug_name, connector.status)

                if connector.active_charge_session:
                    session = connector.active_charge_session
                    charge_rate_kw = (
                        session.charge_rate.value_kw if session.charge_rate else None
                    )
                    consumption_kwh = (
                        session.total_consumption / 1000
                        if session.total_consumption is not None
                        else None
                    )
                    print(session.current_state, charge_rate_kw, consumption_kwh)


asyncio.run(main())
```

The current public queries do not require authentication. A bearer token can still be supplied for endpoints that require one:

```python
client = ChargefoxClient(bearer_token="YOUR_TOKEN")
```

## Behaviour

- `get_locations_by_bounds()` returns lightweight map summaries.
- `get_charge_stations_by_bounds()` hydrates those summaries through `location(id)` and returns fully modelled stations, connectors, plugs, and active sessions.
- Bounds hydration uses at most 5 concurrent requests by default. Successful locations are returned when isolated locations fail; if every location fails, the first error is raised.
- Bulk location hydration intentionally omits record-sensitive fields such as `startMeterValue`, `chargeBoxIdentity`, and `firmwareVersion` because Chargefox can return HTTP 500 for otherwise valid records.
- HTTP 429 responses are retried three times by default. The client honors a numeric `Retry-After` header and otherwise uses 2/4/8-second exponential backoff.
- Requests are paced through a client-wide gate at a minimum interval of 0.25 seconds. A 429 pauses all workers, preventing concurrent retries from extending the limit.
- `get_location()`, `get_charge_station()`, and `get_connector()` support targeted polling.
- Active-session consumption is returned by the API in Wh; divide it by 1000 for kWh.

The concurrency limit can be configured with `ChargefoxClient(max_concurrent_requests=5)`.
Rate-limit behavior can be configured with `ChargefoxClient(max_rate_limit_retries=3, rate_limit_retry_delay=2)`.
Request pacing can be configured with `ChargefoxClient(min_request_interval=0.5)`.

## Development checks

```bash
python -m ruff format --check .
python -m ruff check .
python -m unittest discover -s tests -v
python -m coverage run -m unittest discover -s tests
python -m coverage report
```

Build a wheel into `dist/` with:

```bash
python -m pip wheel . --no-deps --wheel-dir dist
```

## Live integration test

Set `CHARGEFOX_RUN_LIVE_TESTS=1`, then run the normal test suite. The live tests use the public API without authentication and are skipped unless explicitly enabled.

```bash
py -m unittest discover -s tests
```

If you need to override the endpoint, set `CHARGEFOX_GRAPHQL_URL` as well.

The live tests cover lookup collections, lightweight map discovery, and resolving a full location and its stations from a map result. Connector status and active-session data are included with each full station and can be refreshed by polling the location, station, or connector methods.
