From ca10ade74e3ec98d6c0f1ad58bc61684c72c2955 Mon Sep 17 00:00:00 2001 From: Mick Vleeshouwer Date: Tue, 9 Jun 2026 15:40:38 +0000 Subject: [PATCH 1/8] Enable local API support for Rexel Rexel gateways support the local API and the paste-a-token UX, so add Server.REXEL to SERVERS_WITH_LOCAL_API. This lets consumers (e.g. the Home Assistant overkiz integration) offer the local-or-cloud choice for Rexel. --- pyoverkiz/const.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pyoverkiz/const.py b/pyoverkiz/const.py index 366b7da6..e2e7e4bb 100644 --- a/pyoverkiz/const.py +++ b/pyoverkiz/const.py @@ -48,6 +48,7 @@ Server.SOMFY_EUROPE, Server.SOMFY_OCEANIA, Server.SOMFY_AMERICA, + Server.REXEL, ] SUPPORTED_SERVERS: MappingProxyType[str, ServerConfig] = MappingProxyType( From b168dcd7b3e46fff8f04cb3683a68c803cdf2e2f Mon Sep 17 00:00:00 2001 From: Mick Vleeshouwer Date: Tue, 9 Jun 2026 15:41:49 +0000 Subject: [PATCH 2/8] Document Rexel local API setup Add a 'Rexel (local)' tab to the getting-started guide with the EConnect app token-generation steps and supported gateways, and note Rexel under Local Token auth in core concepts. --- docs/core-concepts.md | 2 +- docs/getting-started.md | 43 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/docs/core-concepts.md b/docs/core-concepts.md index 3c36439a..1e02a498 100644 --- a/docs/core-concepts.md +++ b/docs/core-concepts.md @@ -85,7 +85,7 @@ The library supports multiple authentication methods depending on the server: - **Username/Password**: Most cloud servers (Somfy, Cozytouch, Hitachi, Nexity) - **Bearer Token**: Cloud servers with pre-issued tokens -- **Local Token**: Somfy Developer Mode (local gateways) +- **Local Token**: local gateways (Somfy Developer Mode, Rexel Energeasy Connect) - **OAuth2 with PKCE**: Rexel (Azure AD B2C) Each server automatically selects the appropriate authentication strategy based on the credentials provided. diff --git a/docs/getting-started.md b/docs/getting-started.md index ff8e2a2b..5d3268d4 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -273,3 +273,46 @@ Use a cloud server when you want to connect through the vendor’s public API. U gateway_id="STORED_GATEWAY_ID", ) ``` + +=== "Rexel (local)" + + Rexel Energeasy Connect gateways expose a local API that third-party + software (such as Home Assistant) can connect to over your local network. + Supported by the following gateways: + + - Energeasy Connect Rail Din (`48`) + - Energeasy Connect V2 (`57`) + - Energeasy Connect V3 (`120`) + - Energeasy Connect V3 Rail Din (`125`) + + To obtain a token, enable the local API of your Energeasy Connect Box from + the EConnect mobile app: + + 1. Open the EConnect app. + 2. Go to **Settings** » **My home** » **Maintenance**. + 3. Select your gateway » **Local API**. + 4. Generate a token to authenticate your API requests. + 5. Use the generated token below, and set the host to your gateway PIN code + (e.g. `gateway-xxxx-xxxx-xxxx.local:8443`) or its IP address. + + Use the helper function `create_local_server_config` to create a `Server` + with `LocalTokenCredentials` to provide your token. + + ```python + import asyncio + + from pyoverkiz.auth.credentials import LocalTokenCredentials + from pyoverkiz.client import OverkizClient + from pyoverkiz.utils import create_local_server_config + + + async def main() -> None: + async with OverkizClient( + server=create_local_server_config(host="gateway-xxxx-xxxx-xxxx.local:8443"), + credentials=LocalTokenCredentials("token-from-the-econnect-app"), + verify_ssl=True, # disable if you connect via IP + ) as client: + await client.login() + + asyncio.run(main()) + ``` From f523494f43ef4ac49fb7b20220ebf0ca2b330c5a Mon Sep 17 00:00:00 2001 From: Mick Vleeshouwer Date: Tue, 9 Jun 2026 16:16:43 +0000 Subject: [PATCH 3/8] Label Rexel local server config and clarify server default Set name/manufacturer for the Rexel local example and note that the default server value must be kept for local token auth routing. --- docs/getting-started.md | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/docs/getting-started.md b/docs/getting-started.md index 5d3268d4..d2bea17e 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -277,8 +277,8 @@ Use a cloud server when you want to connect through the vendor’s public API. U === "Rexel (local)" Rexel Energeasy Connect gateways expose a local API that third-party - software (such as Home Assistant) can connect to over your local network. - Supported by the following gateways: + software can connect to over your local network. Supported by the following + gateways: - Energeasy Connect Rail Din (`48`) - Energeasy Connect V2 (`57`) @@ -308,7 +308,11 @@ Use a cloud server when you want to connect through the vendor’s public API. U async def main() -> None: async with OverkizClient( - server=create_local_server_config(host="gateway-xxxx-xxxx-xxxx.local:8443"), + server=create_local_server_config( + host="gateway-xxxx-xxxx-xxxx.local:8443", + name="Rexel Energeasy Connect (local)", + manufacturer="Rexel", + ), credentials=LocalTokenCredentials("token-from-the-econnect-app"), verify_ssl=True, # disable if you connect via IP ) as client: @@ -316,3 +320,8 @@ Use a cloud server when you want to connect through the vendor’s public API. U asyncio.run(main()) ``` + + !!! note + Keep the default `server` value — the local token auth strategy is only + selected when `server` is *not* a cloud-mapped server like `Server.REXEL`. + Override `name`/`manufacturer` for labeling, but leave `server` as-is. From ba12bad0726ed4ed49ab4cb9038415c129e13333 Mon Sep 17 00:00:00 2001 From: Mick Vleeshouwer Date: Tue, 9 Jun 2026 16:26:28 +0000 Subject: [PATCH 4/8] Route local API by api_type before server-specific strategies Move the APIType.LOCAL check to the top of build_auth_strategy so a local config can carry any server identity (e.g. Server.REXEL) for labelling without misrouting to that server's cloud strategy. No built-in server uses APIType.LOCAL, so this cannot steal cloud routing. Add a regression test and update the Rexel local docs to set server=Server.REXEL. --- docs/getting-started.md | 8 +++++--- pyoverkiz/auth/factory.py | 26 ++++++++++++++------------ tests/test_auth.py | 27 +++++++++++++++++++++++++++ 3 files changed, 46 insertions(+), 15 deletions(-) diff --git a/docs/getting-started.md b/docs/getting-started.md index d2bea17e..305345b4 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -303,6 +303,7 @@ Use a cloud server when you want to connect through the vendor’s public API. U from pyoverkiz.auth.credentials import LocalTokenCredentials from pyoverkiz.client import OverkizClient + from pyoverkiz.enums import Server from pyoverkiz.utils import create_local_server_config @@ -310,6 +311,7 @@ Use a cloud server when you want to connect through the vendor’s public API. U async with OverkizClient( server=create_local_server_config( host="gateway-xxxx-xxxx-xxxx.local:8443", + server=Server.REXEL, name="Rexel Energeasy Connect (local)", manufacturer="Rexel", ), @@ -322,6 +324,6 @@ Use a cloud server when you want to connect through the vendor’s public API. U ``` !!! note - Keep the default `server` value — the local token auth strategy is only - selected when `server` is *not* a cloud-mapped server like `Server.REXEL`. - Override `name`/`manufacturer` for labeling, but leave `server` as-is. + Local API routing keys off `api_type`, so `server`, `name`, and + `manufacturer` are free to label the connection for your gateway — + setting `server=Server.REXEL` keeps the local token strategy. diff --git a/pyoverkiz/auth/factory.py b/pyoverkiz/auth/factory.py index 55f35160..8d39716c 100644 --- a/pyoverkiz/auth/factory.py +++ b/pyoverkiz/auth/factory.py @@ -39,6 +39,20 @@ def build_auth_strategy( """Build the correct auth strategy for the given server and credentials.""" server: Server | None = server_config.server + # Local API routing keys off api_type, not server, so a local config can + # carry any server identity (e.g. Server.REXEL) purely for labelling. + if server_config.api_type == APIType.LOCAL: + if isinstance(credentials, LocalTokenCredentials): + return LocalTokenAuthStrategy( + credentials, session, server_config, ssl_context + ) + return BearerTokenAuthStrategy( + _ensure_credentials(credentials, TokenCredentials), + session, + server_config, + ssl_context, + ) + if server == Server.SOMFY_EUROPE: return SomfyAuthStrategy( _ensure_credentials(credentials, UsernamePasswordCredentials), @@ -79,18 +93,6 @@ def build_auth_strategy( ssl_context, ) - if server_config.api_type == APIType.LOCAL: - if isinstance(credentials, LocalTokenCredentials): - return LocalTokenAuthStrategy( - credentials, session, server_config, ssl_context - ) - return BearerTokenAuthStrategy( - _ensure_credentials(credentials, TokenCredentials), - session, - server_config, - ssl_context, - ) - if isinstance(credentials, TokenCredentials) and not isinstance( credentials, LocalTokenCredentials ): diff --git a/tests/test_auth.py b/tests/test_auth.py index adc7942d..09dc2428 100644 --- a/tests/test_auth.py +++ b/tests/test_auth.py @@ -340,6 +340,33 @@ async def test_build_auth_strategy_local_token(self): assert isinstance(strategy, LocalTokenAuthStrategy) + @pytest.mark.asyncio + async def test_build_auth_strategy_local_token_with_cloud_server(self): + """A local config keeps local routing even when server is a cloud id. + + Local API routing keys off api_type, so a config labelled with a + cloud-mapped server (e.g. Server.REXEL) must still select the local + token strategy rather than the Rexel OAuth strategy. + """ + server_config = ServerConfig( + server=Server.REXEL, + name="Rexel Energeasy Connect (local)", + endpoint="https://gateway.local", + manufacturer="Rexel", + api_type=APIType.LOCAL, + ) + credentials = LocalTokenCredentials("local_token") + session = AsyncMock(spec=ClientSession) + + strategy = build_auth_strategy( + server_config=server_config, + credentials=credentials, + session=session, + ssl_context=True, + ) + + assert isinstance(strategy, LocalTokenAuthStrategy) + @pytest.mark.asyncio async def test_build_auth_strategy_local_bearer(self): """Test building local bearer token auth strategy.""" From 60dee9f7206b3853ccc419deeb462ba86bb63e13 Mon Sep 17 00:00:00 2001 From: Mick Vleeshouwer Date: Tue, 9 Jun 2026 16:29:36 +0000 Subject: [PATCH 5/8] Move local API routing fix to its own PR Revert the factory reorder and its regression test from this branch (now tracked in the dedicated PR), and drop the server=Server.REXEL example and accompanying note that depended on that fix. The Rexel local example keeps name/manufacturer labelling, which works against released code. --- docs/getting-started.md | 7 ------- pyoverkiz/auth/factory.py | 26 ++++++++++++-------------- tests/test_auth.py | 27 --------------------------- 3 files changed, 12 insertions(+), 48 deletions(-) diff --git a/docs/getting-started.md b/docs/getting-started.md index 305345b4..4db92599 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -303,7 +303,6 @@ Use a cloud server when you want to connect through the vendor’s public API. U from pyoverkiz.auth.credentials import LocalTokenCredentials from pyoverkiz.client import OverkizClient - from pyoverkiz.enums import Server from pyoverkiz.utils import create_local_server_config @@ -311,7 +310,6 @@ Use a cloud server when you want to connect through the vendor’s public API. U async with OverkizClient( server=create_local_server_config( host="gateway-xxxx-xxxx-xxxx.local:8443", - server=Server.REXEL, name="Rexel Energeasy Connect (local)", manufacturer="Rexel", ), @@ -322,8 +320,3 @@ Use a cloud server when you want to connect through the vendor’s public API. U asyncio.run(main()) ``` - - !!! note - Local API routing keys off `api_type`, so `server`, `name`, and - `manufacturer` are free to label the connection for your gateway — - setting `server=Server.REXEL` keeps the local token strategy. diff --git a/pyoverkiz/auth/factory.py b/pyoverkiz/auth/factory.py index 8d39716c..55f35160 100644 --- a/pyoverkiz/auth/factory.py +++ b/pyoverkiz/auth/factory.py @@ -39,20 +39,6 @@ def build_auth_strategy( """Build the correct auth strategy for the given server and credentials.""" server: Server | None = server_config.server - # Local API routing keys off api_type, not server, so a local config can - # carry any server identity (e.g. Server.REXEL) purely for labelling. - if server_config.api_type == APIType.LOCAL: - if isinstance(credentials, LocalTokenCredentials): - return LocalTokenAuthStrategy( - credentials, session, server_config, ssl_context - ) - return BearerTokenAuthStrategy( - _ensure_credentials(credentials, TokenCredentials), - session, - server_config, - ssl_context, - ) - if server == Server.SOMFY_EUROPE: return SomfyAuthStrategy( _ensure_credentials(credentials, UsernamePasswordCredentials), @@ -93,6 +79,18 @@ def build_auth_strategy( ssl_context, ) + if server_config.api_type == APIType.LOCAL: + if isinstance(credentials, LocalTokenCredentials): + return LocalTokenAuthStrategy( + credentials, session, server_config, ssl_context + ) + return BearerTokenAuthStrategy( + _ensure_credentials(credentials, TokenCredentials), + session, + server_config, + ssl_context, + ) + if isinstance(credentials, TokenCredentials) and not isinstance( credentials, LocalTokenCredentials ): diff --git a/tests/test_auth.py b/tests/test_auth.py index 09dc2428..adc7942d 100644 --- a/tests/test_auth.py +++ b/tests/test_auth.py @@ -340,33 +340,6 @@ async def test_build_auth_strategy_local_token(self): assert isinstance(strategy, LocalTokenAuthStrategy) - @pytest.mark.asyncio - async def test_build_auth_strategy_local_token_with_cloud_server(self): - """A local config keeps local routing even when server is a cloud id. - - Local API routing keys off api_type, so a config labelled with a - cloud-mapped server (e.g. Server.REXEL) must still select the local - token strategy rather than the Rexel OAuth strategy. - """ - server_config = ServerConfig( - server=Server.REXEL, - name="Rexel Energeasy Connect (local)", - endpoint="https://gateway.local", - manufacturer="Rexel", - api_type=APIType.LOCAL, - ) - credentials = LocalTokenCredentials("local_token") - session = AsyncMock(spec=ClientSession) - - strategy = build_auth_strategy( - server_config=server_config, - credentials=credentials, - session=session, - ssl_context=True, - ) - - assert isinstance(strategy, LocalTokenAuthStrategy) - @pytest.mark.asyncio async def test_build_auth_strategy_local_bearer(self): """Test building local bearer token auth strategy.""" From 17f63e7df4dd90cf1db7c447dc91fc4924417038 Mon Sep 17 00:00:00 2001 From: Mick Vleeshouwer Date: Wed, 10 Jun 2026 18:31:56 +0200 Subject: [PATCH 6/8] Show local API support per vendor in supported hubs table --- README.md | 31 +++++++++++++++++-------------- docs/getting-started.md | 8 ++++++++ docs/index.md | 31 +++++++++++++++++-------------- 3 files changed, 42 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index 50551afc..b9d8c55d 100644 --- a/README.md +++ b/README.md @@ -16,23 +16,26 @@ Full documentation is available at **[imicknl.github.io/python-overkiz-api](http ## Supported hubs -- Atlantic Cozytouch -- Bouygues Flexom -- Brandt Smart Control † -- Hitachi Hi Kumo -- Nexity Eugénie -- Rexel Energeasy Connect ‡ -- Sauter Cozytouch -- Simu (LiveIn2) -- Somfy Connexoon IO -- Somfy Connexoon RTS -- Somfy TaHoma -- Somfy TaHoma Switch -- Thermor Cozytouch +| Vendor | Cloud | Local | +| --- | :-: | :-: | +| Atlantic Cozytouch | ✓ | | +| Bouygues Flexom | ✓ | | +| Brandt Smart Control † | ✓ | | +| Hexaom HexaConnect | ✓ | | +| Hitachi Hi Kumo | ✓ | | +| Nexity Eugénie | ✓ | | +| Rexel Energeasy Connect ‡ | ✓ | ✓ | +| Sauter Cozytouch | ✓ | | +| Simu LiveIn2 | ✓ | | +| Somfy | ✓ | ✓ | +| Thermor Cozytouch | ✓ | | +| Ubiwizz | ✓ | | + +Local API availability depends on your specific gateway. See the [Getting started guide](https://imicknl.github.io/python-overkiz-api/getting-started/) for the supported gateways and setup. † _This server's authentication method isn't supported yet. To use it, obtain an access token (by sniffing the original app) and create a local user on the Overkiz API platform._ -‡ _Requires OAuth credentials provided by Rexel._ +‡ _The cloud API requires OAuth credentials provided by Rexel; the local API uses a token from the EConnect app instead._ ## Installation diff --git a/docs/getting-started.md b/docs/getting-started.md index 4db92599..45fa3aff 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -78,6 +78,14 @@ Use a cloud server when you want to connect through the vendor’s public API. U Local authentication requires a token generated via the official mobile app. For details on obtaining a token, refer to [Somfy TaHoma Developer Mode](https://github.com/Somfy-Developer/Somfy-TaHoma-Developer-Mode). + The local API is available on the following gateways: + + - Somfy Connexoon IO + - Somfy Connexoon RTS + - Somfy TaHoma v2 + - Somfy TaHoma Beecon + - Somfy TaHoma Switch + Use the helper function `create_local_server_config` to create a `Server` with `LocalTokenCredentials` to provide your token. ```python diff --git a/docs/index.md b/docs/index.md index 1392a0ad..eb3bc1f9 100644 --- a/docs/index.md +++ b/docs/index.md @@ -22,20 +22,23 @@ pyOverkiz is an async Python library for interacting with Overkiz-based platform ## Supported hubs -- Atlantic Cozytouch -- Bouygues Flexom -- Brandt Smart Control † -- Hitachi Hi Kumo -- Nexity Eugénie -- Rexel Energeasy Connect ‡ -- Sauter Cozytouch -- Simu (LiveIn2) -- Somfy Connexoon IO -- Somfy Connexoon RTS -- Somfy TaHoma -- Somfy TaHoma Switch -- Thermor Cozytouch +| Vendor | Cloud | Local | +| --- | :-: | :-: | +| Atlantic Cozytouch | ✓ | | +| Bouygues Flexom | ✓ | | +| Brandt Smart Control † | ✓ | | +| Hexaom HexaConnect | ✓ | | +| Hitachi Hi Kumo | ✓ | | +| Nexity Eugénie | ✓ | | +| Rexel Energeasy Connect ‡ | ✓ | ✓ | +| Sauter Cozytouch | ✓ | | +| Simu LiveIn2 | ✓ | | +| Somfy | ✓ | ✓ | +| Thermor Cozytouch | ✓ | | +| Ubiwizz | ✓ | | + +Local API availability depends on your specific gateway. See the [Getting started guide](getting-started.md) for the supported gateways and setup. † _This server's authentication method isn't supported yet. To use it, obtain an access token (by sniffing the original app) and create a local user on the Overkiz API platform._ -‡ _Requires OAuth credentials provided by Rexel._ +‡ _The cloud API requires OAuth credentials provided by Rexel; the local API uses a token from the EConnect app instead._ From c4b27fd179b3da398314a7dfd39ad6ad577da96f Mon Sep 17 00:00:00 2001 From: Mick Vleeshouwer Date: Wed, 10 Jun 2026 19:03:02 +0200 Subject: [PATCH 7/8] Add per-vendor gateway sub-tables for Somfy and Rexel --- README.md | 19 +++++++++++++++++++ docs/index.md | 19 +++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/README.md b/README.md index b9d8c55d..c447afad 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,25 @@ Local API availability depends on your specific gateway. See the [Getting starte ‡ _The cloud API requires OAuth credentials provided by Rexel; the local API uses a token from the EConnect app instead._ +### Somfy + +| Gateway | Cloud | Local | +| --- | :-: | :-: | +| Connexoon IO | ✓ | ✓ | +| Connexoon RTS | ✓ | ✓ | +| TaHoma v2 | ✓ | ✓ | +| TaHoma Beecon | ✓ | ✓ | +| TaHoma Switch | ✓ | ✓ | + +### Rexel Energeasy Connect + +| Gateway | Cloud | Local | +| --- | :-: | :-: | +| Energeasy Connect Rail Din | ✓ | ✓ | +| Energeasy Connect V2 | ✓ | ✓ | +| Energeasy Connect V3 | ✓ | ✓ | +| Energeasy Connect V3 Rail Din | ✓ | ✓ | + ## Installation ```bash diff --git a/docs/index.md b/docs/index.md index eb3bc1f9..9d9cf63a 100644 --- a/docs/index.md +++ b/docs/index.md @@ -42,3 +42,22 @@ Local API availability depends on your specific gateway. See the [Getting starte † _This server's authentication method isn't supported yet. To use it, obtain an access token (by sniffing the original app) and create a local user on the Overkiz API platform._ ‡ _The cloud API requires OAuth credentials provided by Rexel; the local API uses a token from the EConnect app instead._ + +### Somfy + +| Gateway | Cloud | Local | +| --- | :-: | :-: | +| Connexoon IO | ✓ | ✓ | +| Connexoon RTS | ✓ | ✓ | +| TaHoma v2 | ✓ | ✓ | +| TaHoma Beecon | ✓ | ✓ | +| TaHoma Switch | ✓ | ✓ | + +### Rexel Energeasy Connect + +| Gateway | Cloud | Local | +| --- | :-: | :-: | +| Energeasy Connect Rail Din | ✓ | ✓ | +| Energeasy Connect V2 | ✓ | ✓ | +| Energeasy Connect V3 | ✓ | ✓ | +| Energeasy Connect V3 Rail Din | ✓ | ✓ | From 6da38ed8ad645f62f68d7e1b34051893ad17c6ed Mon Sep 17 00:00:00 2001 From: Mick Vleeshouwer Date: Wed, 10 Jun 2026 19:48:01 +0200 Subject: [PATCH 8/8] Add cloud-only gateways to Somfy and Rexel sub-tables --- README.md | 2 ++ docs/index.md | 2 ++ 2 files changed, 4 insertions(+) diff --git a/README.md b/README.md index c447afad..bc9bace1 100644 --- a/README.md +++ b/README.md @@ -46,11 +46,13 @@ Local API availability depends on your specific gateway. See the [Getting starte | TaHoma v2 | ✓ | ✓ | | TaHoma Beecon | ✓ | ✓ | | TaHoma Switch | ✓ | ✓ | +| Connectivity Kit | ✓ | | ### Rexel Energeasy Connect | Gateway | Cloud | Local | | --- | :-: | :-: | +| Energeasy Connect | ✓ | | | Energeasy Connect Rail Din | ✓ | ✓ | | Energeasy Connect V2 | ✓ | ✓ | | Energeasy Connect V3 | ✓ | ✓ | diff --git a/docs/index.md b/docs/index.md index 9d9cf63a..bc4c25f2 100644 --- a/docs/index.md +++ b/docs/index.md @@ -52,11 +52,13 @@ Local API availability depends on your specific gateway. See the [Getting starte | TaHoma v2 | ✓ | ✓ | | TaHoma Beecon | ✓ | ✓ | | TaHoma Switch | ✓ | ✓ | +| Connectivity Kit | ✓ | | ### Rexel Energeasy Connect | Gateway | Cloud | Local | | --- | :-: | :-: | +| Energeasy Connect | ✓ | | | Energeasy Connect Rail Din | ✓ | ✓ | | Energeasy Connect V2 | ✓ | ✓ | | Energeasy Connect V3 | ✓ | ✓ |