Getting started
This guide will help you install the library, connect to your hub, and perform your first actions.
Need the official Overkiz cloud API reference? Visit the Overkiz API Documentation (mirror). Most endpoints are accessible via the pyOverkiz package.
Upgrading from pyOverkiz 1.x? See the migration guide for a full list of breaking changes.
Prerequisites
- Python 3.12+
- An OverKiz-compatible hub and account
Install pyOverkiz from PyPI
With UV recommended
uv add pyoverkiz
With pip
pip install pyoverkiz
Optional extras
Some servers require additional dependencies that are not installed by default:
| Extra | Server | Packages |
|---|---|---|
nexity |
Nexity | boto3, warrant-lite |
Install an extra with:
uv add "pyoverkiz[nexity]"
# or
pip install "pyoverkiz[nexity]"
Choose your server
Use a cloud server when you want to connect through the vendor’s public API. Use a local server when you want LAN access to a gateway.
- Cloud servers use the
Serverenum. - Local servers use
create_local_server_configwith a hostname or IP address.
Authentication
Authentication to the Somfy cloud requires your mobile app username and password and your region.
Use Server.SOMFY_EUROPE, Server.SOMFY_AMERICA, or Server.SOMFY_OCEANIA with UsernamePasswordCredentials to select your region and authenticate.
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()
asyncio.run(main())
Experimental
Server.SOMFY copies how the TaHoma app signs in and may change within
2.x. Stored SomfyTokenCredentials will keep working. For an account
with a single site, SOMFY_EUROPE, SOMFY_AMERICA and SOMFY_OCEANIA
remain the stable choice.
Use Server.SOMFY when one Somfy account has access to several sites
(homes). It finds every site on the account and picks the right region.
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,
credentials=UsernamePasswordCredentials("you@example.com", "password"),
) as client:
# The event listener needs a selected site.
await client.login(register_event_listener=False)
# login() already selects the site if there is only one.
if client.selected_gateway is None:
gateways = await client.discover_gateways()
client.select_gateway(gateways[0].gateway_id)
setup = await client.get_setup()
print(f"{len(setup.devices)} device(s)")
# Only needed if you want to poll events.
await client.register_event_listener()
asyncio.run(main())
Each GatewayCandidate has a label (the site name) and home_id you can
show in a site picker. roles holds the account's role on that site:
owner, secondary, or an id for a custom role. Sites you were invited to
are listed too; they may only give access to some devices.
Requests made before a site is selected raise NoGatewaySelectedError.
Resume without a password. After selecting a site, store the result of
client.to_credentials() and pass it back on the next run. This skips login
and site discovery. The refresh token changes over time, so pass
on_token_refresh to save each new one.
import asyncio
from pyoverkiz.auth.credentials import SomfyTokenCredentials
from pyoverkiz.client import OverkizClient
from pyoverkiz.enums import Server
async def persist(refresh_token: str) -> None:
# Store the rotated refresh token for next time.
...
async def main(stored: SomfyTokenCredentials) -> None:
async with OverkizClient(server=Server.SOMFY, credentials=stored) as client:
await client.login() # no network round trips
setup = await client.get_setup()
print(f"{len(setup.devices)} device(s)")
# `stored` is what you persisted earlier via:
# stored = client.to_credentials(on_token_refresh=persist)
See Who owns the tokens.
Local authentication requires a token generated via the official mobile app. For details on obtaining a token, refer to 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.
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-your-mobile-app"),
verify_ssl=True, # disable if you connect via IP
) as client:
await client.login()
asyncio.run(main())
Authentication to the Cozytouch cloud requires your mobile app username and password and your vendor.
Use Server.ATLANTIC_COZYTOUCH, Server.SAUTER_COZYTOUCH, or Server.THERMOR_COZYTOUCH with UsernamePasswordCredentials to select your vendor and authenticate.
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.ATLANTIC_COZYTOUCH,
credentials=UsernamePasswordCredentials("you@example.com", "password"),
) as client:
await client.login()
asyncio.run(main())
Authentication to the Hitachi Hi Kumo cloud requires your mobile app username and password and your region.
Use Server.HI_KUMO_ASIA, Server.HI_KUMO_EUROPE, or Server.HI_KUMO_OCEANIA with UsernamePasswordCredentials to select your region and authenticate.
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.HI_KUMO_EUROPE,
credentials=UsernamePasswordCredentials("you@example.com", "password"),
) as client:
await client.login()
asyncio.run(main())
Authentication to the Rexel cloud uses OAuth2 with PKCE (Proof Key for Code Exchange).
Step 1: Generate PKCE parameters and authorization URL
import secrets
from pyoverkiz.pkce import generate_pkce_pair
from pyoverkiz.utils import build_rexel_authorization_url
# Generate PKCE code verifier and challenge
code_verifier, code_challenge = generate_pkce_pair()
# Generate authorization URL (user must visit this in browser)
state = secrets.token_urlsafe(16) # For CSRF protection
auth_url = build_rexel_authorization_url(code_challenge, state)
print(f"Visit this URL to authorize: {auth_url}")
Step 2: Redirect user to authorization URL
Direct the user to the auth_url. After successful login, they will be redirected to:
https://my.home-assistant.io/redirect/oauth?code=AUTHORIZATION_CODE&state=STATE_VALUE
Compare the returned state against the value from step 1 before
continuing, and reject the response if they differ — this is what guards
against CSRF.
Step 3: Exchange authorization code for access token
import asyncio
from pyoverkiz.auth.credentials import RexelOAuthCodeCredentials
from pyoverkiz.client import OverkizClient
from pyoverkiz.enums import Server
from pyoverkiz.const import REXEL_OAUTH_REDIRECT_URI
async def main(returned_state: str) -> None:
# Verify the state echoed back by the redirect matches step 1.
if returned_state != state:
raise ValueError("State mismatch — possible CSRF, aborting.")
# Use the authorization code from the redirect
async with OverkizClient(
server=Server.REXEL,
credentials=RexelOAuthCodeCredentials(
code="AUTHORIZATION_CODE_FROM_REDIRECT",
redirect_uri=REXEL_OAUTH_REDIRECT_URI,
code_verifier=code_verifier, # From step 1
),
) as client:
await client.login() # auto-selects a sole gateway
# Accounts with more than one gateway must select one explicitly.
gateways = await client.discover_gateways()
if len(gateways) > 1:
client.select_gateway(gateways[0].gateway_id)
# Client is now authenticated and ready to use
asyncio.run(main(returned_state="STATE_VALUE_FROM_REDIRECT"))
Use this when an external system already owns the OAuth2 lifecycle — for
example the Home Assistant application_credentials platform, which
authorizes, exchanges, refreshes, and persists tokens for you. pyoverkiz
then only needs the current access token and the Rexel gateway selection.
Supply a token in one of two ways:
Async callback (recommended for long-running apps). pyoverkiz calls it before each request, so the owner can refresh and persist transparently — see Who owns the tokens.
import asyncio
from pyoverkiz.auth.credentials import RexelTokenCredentials
from pyoverkiz.client import OverkizClient
from pyoverkiz.enums import Server
async def get_access_token() -> str:
# Return a currently-valid access token (refresh upstream as needed).
...
async def main() -> None:
async with OverkizClient(
server=Server.REXEL,
credentials=RexelTokenCredentials(
access_token_callback=get_access_token,
),
) as client:
await client.login() # discovers + auto-selects a sole gateway
gateways = await client.discover_gateways()
if len(gateways) > 1:
client.select_gateway(gateways[0].gateway_id)
setup = await client.get_setup()
print(f"{len(setup.devices)} device(s)")
asyncio.run(main())
Static token (simplest, for quick standalone or test use). No refresh — when the token expires you construct a new client.
credentials = RexelTokenCredentials(access_token="YOUR_ACCESS_TOKEN")
Reload without re-discovering. Persist the chosen gateway_id and pass
it back on the next run; login() applies it directly and skips discovery:
credentials = RexelTokenCredentials(
access_token_callback=get_access_token,
gateway_id="STORED_GATEWAY_ID",
)
Rexel Energeasy Connect gateways expose a local API that third-party software 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:
- Open the EConnect app.
- Go to Settings » My home » Maintenance.
- Select your gateway » Local API.
- Generate a token to authenticate your API requests.
- 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.
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",
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:
await client.login()
asyncio.run(main())
Who owns the tokens
Somfy and Rexel can both resume a session without the password, but they handle token refresh in opposite ways.
Somfy (SomfyTokenCredentials) |
Rexel (RexelTokenCredentials) |
|
|---|---|---|
| Who refreshes | pyoverkiz | you |
| How | pyoverkiz calls on_token_refresh(new_token) after each refresh |
pyoverkiz calls access_token_callback() before each request |
| What you store | the latest refresh token | whatever your OAuth2 setup needs |
Somfy: pyoverkiz refreshes the token itself, because it has to be scoped to the selected site. Save each new token in your callback:
async def persist(refresh_token: str) -> None:
# Called only when the token changed.
...
credentials = client.to_credentials(on_token_refresh=persist)
If the callback raises, the error is logged and the request still succeeds. Saving is retried on the next refresh.
Rexel: Rexel uses standard OAuth2, so your app (Home Assistant, for example) usually refreshes tokens already. pyoverkiz asks for the current token when it needs one:
async def get_access_token() -> str:
# Refresh if needed, then return a valid token.
...
credentials = RexelTokenCredentials(access_token_callback=get_access_token)