Metadata-Version: 2.4
Name: aiolauntel
Version: 0.1.0
Summary: Asynchronous client for the My Launtel mobile API
License-Expression: MIT
Project-URL: Homepage, https://github.com/Poshy163/aiolauntel
Project-URL: Source, https://github.com/Poshy163/aiolauntel
Project-URL: Issues, https://github.com/Poshy163/aiolauntel/issues
Classifier: Development Status :: 3 - Alpha
Classifier: Framework :: AsyncIO
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Typing :: Typed
Requires-Python: >=3.13
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: aiohttp>=3.11.0
Provides-Extra: test
Requires-Dist: pytest>=8; extra == "test"
Requires-Dist: pytest-asyncio>=0.24; extra == "test"
Requires-Dist: aioresponses<0.8,>=0.7.8; extra == "test"
Provides-Extra: release
Requires-Dist: build>=1.2.2; extra == "release"
Requires-Dist: twine>=6; extra == "release"
Requires-Dist: ruff>=0.9; extra == "release"
Dynamic: license-file

# aiolauntel

An asynchronous Python client for the API used by the My Launtel mobile app. It
has no Home Assistant dependency and supports all nine recovered API operations.

Source and issue tracking are maintained at
[Poshy163/aiolauntel](https://github.com/Poshy163/aiolauntel). This is an unofficial
client and is not endorsed by Launtel.

## Local installation and tests

Python 3.13 or newer is required. From this directory:

```console
python -m pip install -e ".[test]"
python -m pytest
```

The tests intercept HTTP requests and use synthetic values. They do not contact
Launtel or require Home Assistant. Build distributions locally with
`python -m build` after installing the `build` package.

Maintainers can run `python scripts/release.py` to test and build a release
locally. See [RELEASING.md](https://github.com/Poshy163/aiolauntel/blob/main/RELEASING.md)
for the PyPI trusted publisher setup,
version tags, and retries. The helper never commits, pushes, tags, or uploads.

## Usage

```python
import aiohttp

from aiolauntel import LauntelApi


async def read_account(token: str) -> dict:
    async with aiohttp.ClientSession() as session:
        api = LauntelApi(session, token=token)
        return await api.async_get_user()
```

The caller supplies and closes the `aiohttp.ClientSession`. For initial sign-in,
construct the client without a token and call
`await api.async_login(username, password)`. This returns a bearer token and sets
`api.token`; the caller decides whether and how to persist it. The login request
retains the existing `device_name` value, `Home Assistant`, for compatibility. The
client does not persist credentials or provide automatic sign-in/token refresh.

## Operations

| Method | Result |
| --- | --- |
| `async_login(username, password)` | Token string; also updates `api.token` |
| `async_get_user()` | Raw account dictionary, including `resi_user_id` |
| `async_get_services()` | List of raw service dictionaries, each containing `avcid` |
| `async_get_plans(avc_id)` | Dictionary containing raw `plans` and `has_custom_shaper` |
| `async_modify_service(avc_id, ps_id, disc_code=None)` | Applies the selected catalog entry; returns `None` |
| `async_create_health_check(avc_id)` | Creates a diagnostic job; returns its string or integer ID |
| `async_get_health_checks(avc_id)` | List of raw health records in server order |
| `async_get_health_check(job_id)` | One raw health record |
| `async_get_portal_token()` | Residential portal sign-in token string |

Responses retain their wire field names and optional fields. The client validates
the response envelope, object/list shape, and essential identifiers; it does not
coerce optional values or invent missing service data. Missing
`has_custom_shaper` defaults to `False`. Service modification uses the selected
catalog entry's `id` as `ps_id`, not its `speed_id` or `plan_id`, and accepts the
app's exact successful HTTP statuses, 200 and 202.

The client makes a request only when an operation is called. It does not poll,
create background tasks, retry failed requests, or repeat POST operations.
Applications own polling schedules, ownership checks, action confirmation, and
authentication policy. Health polling should distinguish `is_finished` from
`is_complete`; both raw flags are preserved.

## Errors and transport behavior

All errors derive from `LauntelApiError`:

- `LauntelAuthError`: missing/invalid bearer credentials or HTTP 401.
- `LauntelConnectionError`: request timeout, transport failure, or unreadable
  transport response.
- `LauntelRateLimitError`: HTTP 429. Its `retry_after` contains the server's
  numeric delay in seconds when supplied, capped at 86,400; otherwise it is
  `None`. HTTP-date forms are not interpreted.
- `LauntelApiError`: other rejected HTTP responses or malformed response data.

Requests use the fixed HTTPS mobile API origin, a 30-second total timeout,
encoded path identifiers, and disabled redirects. Exceptions omit raw server
bodies and credentials. Raw account/service results and portal tokens can still
contain private information; applications should avoid logging them.

The API was recovered from the mobile app. Server token expiration, revocation,
and the backend's complete schema are not specified by this client.

## License

MIT; see [LICENSE](https://github.com/Poshy163/aiolauntel/blob/main/LICENSE).
