From d9c4cf1379939ffc0c19c2fcb047c088227ee6c1 Mon Sep 17 00:00:00 2001 From: Mick Vleeshouwer Date: Sun, 19 Apr 2026 14:19:49 +0000 Subject: [PATCH 01/12] Remove outdated example for updating in-memory state map in event handling documentation --- docs/event-handling.md | 32 -------------------------------- 1 file changed, 32 deletions(-) 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()) ``` From 45f796c0b4a320da4c6e33b578eb1eac0b01b1c4 Mon Sep 17 00:00:00 2001 From: Mick Vleeshouwer Date: Sun, 19 Apr 2026 14:20:47 +0000 Subject: [PATCH 02/12] Fix incorrect server constant in Cozytouch getting started example The Cozytouch cloud tab used Server.SOMFY_EUROPE instead of Server.ATLANTIC_COZYTOUCH, contradicting the surrounding text. --- docs/getting-started.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/getting-started.md b/docs/getting-started.md index e9a2c053..73d95ee7 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -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() From 88fd763ebe45f4674195fc2e740d8e3dfb43176d Mon Sep 17 00:00:00 2001 From: Mick Vleeshouwer Date: Sun, 19 Apr 2026 14:21:17 +0000 Subject: [PATCH 03/12] Fix multiple issues in README examples - Add missing Command import in cloud example - Replace blocking time.sleep with await asyncio.sleep in both examples - Replace nonexistent device.id with device.device_url - Fix typo "succesfull" -> "successful" --- README.md | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 3bcaa109..00613c72 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 = "" @@ -60,7 +59,7 @@ async def main() -> None: 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 +79,7 @@ async def main() -> None: events = await client.fetch_events() print(events) - time.sleep(2) + await asyncio.sleep(2) asyncio.run(main()) @@ -90,7 +89,6 @@ asyncio.run(main()) ```python import asyncio -import time from pyoverkiz.auth.credentials import LocalTokenCredentials from pyoverkiz.client import OverkizClient @@ -110,7 +108,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 +119,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()) From 5ab9eb69f0d5588190cae198927888b828708cc1 Mon Sep 17 00:00:00 2001 From: Mick Vleeshouwer Date: Sun, 19 Apr 2026 14:21:25 +0000 Subject: [PATCH 04/12] Fix pre-commit install command to use prek The project uses prek as its pre-commit runner, not pre-commit directly. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 00613c72..b3b3561a 100644 --- a/README.md +++ b/README.md @@ -155,7 +155,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 From a26b951536cc3210c4d146082adf56aaf61274bc Mon Sep 17 00:00:00 2001 From: Mick Vleeshouwer Date: Sun, 19 Apr 2026 14:21:35 +0000 Subject: [PATCH 05/12] Add Brandt and Rexel to docs supported hubs list These were commented out in the docs but listed in the README. Both servers exist in the codebase, so include them with a caveat about unsupported authentication. --- docs/index.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) 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._ From e22304339903726d0a3f5129b0465e6acf5477ce Mon Sep 17 00:00:00 2001 From: Mick Vleeshouwer Date: Sun, 19 Apr 2026 14:21:48 +0000 Subject: [PATCH 06/12] Add top-level re-exports for commonly used classes Allows importing OverkizClient, Action, Command, Device, Event, Gateway, Setup, and State directly from pyoverkiz instead of requiring knowledge of internal submodule paths. --- pyoverkiz/__init__.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/pyoverkiz/__init__.py b/pyoverkiz/__init__.py index 6cebe968..571813b3 100644 --- a/pyoverkiz/__init__.py +++ b/pyoverkiz/__init__.py @@ -3,3 +3,17 @@ This package provides models, enums and a client to communicate with the Overkiz cloud and local gateway APIs. """ + +from pyoverkiz.client import OverkizClient +from pyoverkiz.models import Action, Command, Device, Event, Gateway, Setup, State + +__all__ = [ + "Action", + "Command", + "Device", + "Event", + "Gateway", + "OverkizClient", + "Setup", + "State", +] From 86bb254ade0747331c77d832d11f6bdd89fee5ce Mon Sep 17 00:00:00 2001 From: Mick Vleeshouwer Date: Sun, 19 Apr 2026 16:27:45 +0200 Subject: [PATCH 07/12] Revert "Add top-level re-exports for commonly used classes" This reverts commit e22304339903726d0a3f5129b0465e6acf5477ce. --- pyoverkiz/__init__.py | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/pyoverkiz/__init__.py b/pyoverkiz/__init__.py index 571813b3..6cebe968 100644 --- a/pyoverkiz/__init__.py +++ b/pyoverkiz/__init__.py @@ -3,17 +3,3 @@ This package provides models, enums and a client to communicate with the Overkiz cloud and local gateway APIs. """ - -from pyoverkiz.client import OverkizClient -from pyoverkiz.models import Action, Command, Device, Event, Gateway, Setup, State - -__all__ = [ - "Action", - "Command", - "Device", - "Event", - "Gateway", - "OverkizClient", - "Setup", - "State", -] From c943fa46ed191cd1b00ac0c429adc2ff9ea30971 Mon Sep 17 00:00:00 2001 From: Mick Vleeshouwer Date: Sun, 19 Apr 2026 14:33:52 +0000 Subject: [PATCH 08/12] Fix missing import and unclosed code fence in device control docs Add missing OverkizCommandParam import used in the action groups example and close the code fence before the Limitations section. --- docs/device-control.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 From 08c1755c2bf2022032ef2bf17b33997b8c2c5e96 Mon Sep 17 00:00:00 2001 From: Mick Vleeshouwer Date: Sun, 19 Apr 2026 14:34:00 +0000 Subject: [PATCH 09/12] Remove bare exception handler from README cloud example A getting-started example should show the happy path, not a catch-all that swallows errors and exits silently. --- README.md | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/README.md b/README.md index b3b3561a..e635b6c2 100644 --- a/README.md +++ b/README.md @@ -50,11 +50,7 @@ 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() From bf0b5457468f34b58819f4514d8054d5e40148c1 Mon Sep 17 00:00:00 2001 From: Mick Vleeshouwer Date: Sun, 19 Apr 2026 14:37:21 +0000 Subject: [PATCH 10/12] Consolidate error handling and troubleshooting into one page Both pages covered SSL, rate limits, and auth failures. Merge all content into troubleshooting.md and remove error-handling.md. --- docs/error-handling.md | 56 --------------------------------- docs/troubleshooting.md | 68 ++++++++++++++++++++++++++++++++++------- mkdocs.yml | 1 - 3 files changed, 57 insertions(+), 68 deletions(-) delete mode 100644 docs/error-handling.md 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/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 From 375c520a32d34de9b41dfa5fa92a65718780fc49 Mon Sep 17 00:00:00 2001 From: Mick Vleeshouwer Date: Sun, 19 Apr 2026 14:39:22 +0000 Subject: [PATCH 11/12] Update minimum Python version to 3.12 in docs The docs said 3.10+ but pyproject.toml requires >=3.12. --- docs/contribute.md | 2 +- docs/getting-started.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) 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/getting-started.md b/docs/getting-started.md index 73d95ee7..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 From dae5e914fc5f16b72aa9dfa7239413059a3a6489 Mon Sep 17 00:00:00 2001 From: Mick Vleeshouwer Date: Sun, 19 Apr 2026 14:40:19 +0000 Subject: [PATCH 12/12] Update Python version in docs and release workflows to 3.12 The docs workflow used 3.11 which is below the 3.12 minimum. Also bumped setup-python to v6 in the docs workflow and pinned the release workflow from 3.x to 3.12 for consistency. --- .github/workflows/docs.yml | 4 ++-- .github/workflows/release.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) 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