diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 799f3697..c1183a52 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -22,9 +22,9 @@ jobs: uses: actions/checkout@v4 - name: Set up Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: - python-version: "3.11" + python-version: "3.12" - name: Set up uv uses: astral-sh/setup-uv@v3 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5e7115e6..be8b74d5 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -25,7 +25,7 @@ jobs: - name: Set up Python uses: actions/setup-python@v6 with: - python-version: "3.x" + python-version: "3.12" - name: Retrieve version from tag name id: retrieve-version diff --git a/README.md b/README.md index 3bcaa109..e635b6c2 100644 --- a/README.md +++ b/README.md @@ -35,11 +35,10 @@ pip install pyoverkiz ```python import asyncio -import time from pyoverkiz.auth.credentials import UsernamePasswordCredentials from pyoverkiz.client import OverkizClient -from pyoverkiz.models import Action +from pyoverkiz.models import Action, Command from pyoverkiz.enums import Server, OverkizCommand USERNAME = "" @@ -51,16 +50,12 @@ async def main() -> None: server=Server.SOMFY_EUROPE, credentials=UsernamePasswordCredentials(USERNAME, PASSWORD), ) as client: - try: - await client.login() - except Exception as exception: # pylint: disable=broad-except - print(exception) - return + await client.login() devices = await client.get_devices() for device in devices: - print(f"{device.label} ({device.id}) - {device.controllable_name}") + print(f"{device.label} ({device.device_url}) - {device.controllable_name}") print(f"{device.widget} - {device.ui_class}") await client.execute_action_group( @@ -80,7 +75,7 @@ async def main() -> None: events = await client.fetch_events() print(events) - time.sleep(2) + await asyncio.sleep(2) asyncio.run(main()) @@ -90,7 +85,6 @@ asyncio.run(main()) ```python import asyncio -import time from pyoverkiz.auth.credentials import LocalTokenCredentials from pyoverkiz.client import OverkizClient @@ -110,7 +104,7 @@ async def main() -> None: ) as client: await client.login() - print("Local API connection succesfull!") + print("Local API connection successful!") print(await client.get_api_version()) @@ -121,14 +115,14 @@ async def main() -> None: print(devices) for device in devices: - print(f"{device.label} ({device.id}) - {device.controllable_name}") + print(f"{device.label} ({device.device_url}) - {device.controllable_name}") print(f"{device.widget} - {device.ui_class}") while True: events = await client.fetch_events() print(events) - time.sleep(2) + await asyncio.sleep(2) asyncio.run(main()) @@ -157,7 +151,7 @@ If you use Visual Studio Code with Docker or GitHub Codespaces, you can take adv - Install [uv](https://docs.astral.sh/uv/getting-started/installation). - Clone this repository and navigate to it: `cd python-overkiz-api` -- Initialize the project with `uv sync`, then run `uv run pre-commit install` +- Initialize the project with `uv sync`, then run `uv run prek install` #### Tests diff --git a/docs/contribute.md b/docs/contribute.md index b079654b..819ea1a5 100644 --- a/docs/contribute.md +++ b/docs/contribute.md @@ -69,7 +69,7 @@ uv run pytest ## Project guidelines -- Use Python 3.10+ features and type annotations. +- Use Python 3.12+ features and type annotations. - Keep absolute imports and avoid relative imports. - Preserve existing comments and logging unless your change requires updates. - Prefer small, focused changes and add tests when behavior changes. diff --git a/docs/device-control.md b/docs/device-control.md index 85e046fb..f135a23f 100644 --- a/docs/device-control.md +++ b/docs/device-control.md @@ -169,7 +169,7 @@ await client.execute_action_group( - Use a single action group to batch multiple device commands. ```python -from pyoverkiz.enums import OverkizCommand +from pyoverkiz.enums import OverkizCommand, OverkizCommandParam from pyoverkiz.models import Action, Command await client.execute_action_group( @@ -195,6 +195,7 @@ await client.execute_action_group( ], label="Execution: multiple commands", ) +``` ## Limitations and rate limits diff --git a/docs/error-handling.md b/docs/error-handling.md deleted file mode 100644 index 210697a7..00000000 --- a/docs/error-handling.md +++ /dev/null @@ -1,56 +0,0 @@ -# Error handling - -## Common errors - -- `NotAuthenticatedError` -- `TooManyRequestsError` -- `TooManyConcurrentRequestsError` -- `TooManyExecutionsError` -- `MaintenanceError` -- `AccessDeniedToGatewayError` -- `BadCredentialsError` - -## Retry and backoff guidance - -Use short, jittered delays for transient errors and re-authenticate on auth failures. - -```python -import asyncio - -from pyoverkiz.auth.credentials import UsernamePasswordCredentials -from pyoverkiz.client import OverkizClient -from pyoverkiz.enums import Server -from pyoverkiz.exceptions import ( - NotAuthenticatedError, - TooManyConcurrentRequestsError, - TooManyRequestsError, -) - - -async def fetch_devices_with_retry() -> None: - async with OverkizClient( - server=Server.SOMFY_EUROPE, - credentials=UsernamePasswordCredentials("you@example.com", "password"), - ) as client: - await client.login() - for attempt in range(5): - try: - devices = await client.get_devices() - print(devices) - return - except (TooManyRequestsError, TooManyConcurrentRequestsError): - await asyncio.sleep(0.5 * (attempt + 1)) - except NotAuthenticatedError: - await client.login() - - -asyncio.run(fetch_devices_with_retry()) -``` - -## SSL and local certificates - -If you use local gateways with `.local` hostnames, you may need `verify_ssl=False` when a trusted certificate is not available. - -## Timeouts - -For long-running operations, prefer shorter request timeouts with retries rather than a single long timeout. diff --git a/docs/event-handling.md b/docs/event-handling.md index 4a6b0bc3..cc840933 100644 --- a/docs/event-handling.md +++ b/docs/event-handling.md @@ -59,38 +59,6 @@ async def main() -> None: await asyncio.sleep(2) -asyncio.run(main()) -``` - -## Update an in-memory state map - -```python -import asyncio - -from pyoverkiz.auth.credentials import UsernamePasswordCredentials -from pyoverkiz.client import OverkizClient -from pyoverkiz.enums import Server - - -async def main() -> None: - async with OverkizClient( - server=Server.SOMFY_EUROPE, - credentials=UsernamePasswordCredentials("you@example.com", "password"), - ) as client: - await client.login() - await client.register_event_listener() - - state_map: dict[str, dict[str, object]] = {} - - while True: - events = await client.fetch_events() - for event in events: - device_id = event.device_id - state_map.setdefault(device_id, {})[event.state_name] = event.state_value - - await asyncio.sleep(2) - - asyncio.run(main()) ``` diff --git a/docs/getting-started.md b/docs/getting-started.md index e9a2c053..9735d07a 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -6,7 +6,7 @@ This guide will help you install the library, connect to your hub, and perform y ## Prerequisites -- Python 3.10+ +- Python 3.12+ - An OverKiz-compatible hub and account ## Install pyOverkiz from PyPI @@ -97,7 +97,7 @@ Use a cloud server when you want to connect through the vendor’s public API. U async def main() -> None: async with OverkizClient( - server=Server.SOMFY_EUROPE, + server=Server.ATLANTIC_COZYTOUCH, credentials=UsernamePasswordCredentials("you@example.com", "password"), ) as client: await client.login() diff --git a/docs/index.md b/docs/index.md index d07f685f..08cc7bab 100644 --- a/docs/index.md +++ b/docs/index.md @@ -33,5 +33,7 @@ pyOverkiz is an async Python library for interacting with Overkiz-based platform - Somfy TaHoma - Somfy TaHoma Switch - Thermor Cozytouch - - +- Brandt Smart Control **\*** +- Rexel Energeasy Connect **\*** + +\[*] _These servers utilize an authentication method that is currently not supported by this library._ diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md index eaffa066..0d08f7d3 100644 --- a/docs/troubleshooting.md +++ b/docs/troubleshooting.md @@ -1,23 +1,65 @@ # Troubleshooting -## Cloud vs local connectivity - -- Cloud servers require internet access and valid vendor credentials. -- Local servers require direct access to the gateway on your LAN. - -## SSL and .local hostnames - -If the gateway uses a self-signed certificate, pass `verify_ssl=False` when creating `OverkizClient` for local access. - ## Authentication failures -- Confirm the server matches your vendor region. +- Confirm the server matches your vendor and region. - Re-run `login()` and retry the call. +- On `NotAuthenticatedError`, re-authenticate before retrying. ## Rate limits and concurrency - Reduce polling frequency. - Back off on `TooManyRequestsError` or `TooManyConcurrentRequestsError`. +- Use short, jittered delays for transient errors. + +```python +import asyncio + +from pyoverkiz.auth.credentials import UsernamePasswordCredentials +from pyoverkiz.client import OverkizClient +from pyoverkiz.enums import Server +from pyoverkiz.exceptions import ( + NotAuthenticatedError, + TooManyConcurrentRequestsError, + TooManyRequestsError, +) + + +async def fetch_devices_with_retry() -> None: + async with OverkizClient( + server=Server.SOMFY_EUROPE, + credentials=UsernamePasswordCredentials("you@example.com", "password"), + ) as client: + await client.login() + for attempt in range(5): + try: + devices = await client.get_devices() + print(devices) + return + except (TooManyRequestsError, TooManyConcurrentRequestsError): + await asyncio.sleep(0.5 * (attempt + 1)) + except NotAuthenticatedError: + await client.login() + + +asyncio.run(fetch_devices_with_retry()) +``` + +## Common errors + +- `NotAuthenticatedError` +- `BadCredentialsError` +- `TooManyRequestsError` +- `TooManyConcurrentRequestsError` +- `TooManyExecutionsError` +- `MaintenanceError` +- `AccessDeniedToGatewayError` + +## SSL and local certificates + +- Cloud servers require internet access and valid vendor credentials. +- Local servers require direct access to the gateway on your LAN. +- If the gateway uses a self-signed certificate, pass `verify_ssl=False` when creating `OverkizClient` for local access. ## Listener drops @@ -29,7 +71,11 @@ If the gateway uses a self-signed certificate, pass `verify_ssl=False` when crea - Refresh setup with `get_setup()` and re-fetch devices. - Confirm you are using the correct gateway and server. -## Logging tips +## Timeouts + +For long-running operations, prefer shorter request timeouts with retries rather than a single long timeout. + +## Logging Enable debug logging in your application to inspect request/response details. diff --git a/mkdocs.yml b/mkdocs.yml index 2a8508e7..2566aab2 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -48,7 +48,6 @@ nav: - Device control: device-control.md - Action queue: action-queue.md - Event handling: event-handling.md - - Error handling: error-handling.md - Troubleshooting: troubleshooting.md - API Reference: api-reference.md - Contribute: contribute.md