Skip to content

Commit 1849f13

Browse files
committed
Refactor authentication handling to replace OverkizServer with ServerConfig across the codebase
1 parent 66a05a1 commit 1849f13

File tree

7 files changed

+70
-45
lines changed

7 files changed

+70
-45
lines changed

pyoverkiz/auth/factory.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,16 +23,17 @@
2323
)
2424
from pyoverkiz.const import LOCAL_API_PATH, SUPPORTED_SERVERS
2525
from pyoverkiz.enums import APIType, Server
26-
from pyoverkiz.models import OverkizServer
26+
from pyoverkiz.models import ServerConfig
2727

2828

2929
def build_auth_strategy(
3030
server_key: str | Server | None,
31-
server: OverkizServer,
31+
server: ServerConfig,
3232
credentials: Credentials,
3333
session: ClientSession,
3434
ssl_context: ssl.SSLContext | bool,
3535
) -> Any:
36+
"""Build the correct auth strategy for the given server and credentials."""
3637
api_type = APIType.LOCAL if LOCAL_API_PATH in server.endpoint else APIType.CLOUD
3738

3839
# Normalize server key
@@ -98,7 +99,8 @@ def build_auth_strategy(
9899
)
99100

100101

101-
def _match_server_key(server: OverkizServer) -> Server:
102+
def _match_server_key(server: ServerConfig) -> Server:
103+
"""Find the `Server` enum corresponding to a `ServerConfig` entry."""
102104
for key, value in SUPPORTED_SERVERS.items():
103105
if server is value or server.endpoint == value.endpoint:
104106
return Server(key)
@@ -107,18 +109,21 @@ def _match_server_key(server: OverkizServer) -> Server:
107109

108110

109111
def _ensure_username_password(credentials: Credentials) -> UsernamePasswordCredentials:
112+
"""Validate that credentials are username/password based."""
110113
if not isinstance(credentials, UsernamePasswordCredentials):
111114
raise TypeError("UsernamePasswordCredentials are required for this server.")
112115
return credentials
113116

114117

115118
def _ensure_token(credentials: Credentials) -> TokenCredentials:
119+
"""Validate that credentials carry a bearer token."""
116120
if not isinstance(credentials, TokenCredentials):
117121
raise TypeError("TokenCredentials are required for this server.")
118122
return credentials
119123

120124

121125
def _ensure_rexel(credentials: Credentials) -> RexelOAuthCodeCredentials:
126+
"""Validate that credentials are of Rexel OAuth code type."""
122127
if not isinstance(credentials, RexelOAuthCodeCredentials):
123128
raise TypeError("RexelOAuthCodeCredentials are required for this server.")
124129
return credentials

pyoverkiz/auth/strategies.py

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -47,32 +47,37 @@
4747
SomfyBadCredentialsException,
4848
SomfyServiceException,
4949
)
50-
from pyoverkiz.models import OverkizServer
50+
from pyoverkiz.models import ServerConfig
5151

5252

5353
class BaseAuthStrategy(AuthStrategy):
5454
def __init__(
5555
self,
5656
session: ClientSession,
57-
server: OverkizServer,
57+
server: ServerConfig,
5858
ssl_context: ssl.SSLContext | bool,
5959
api_type: APIType,
6060
) -> None:
61+
"""Store shared auth context for Overkiz API interactions."""
6162
self.session = session
6263
self.server = server
6364
self._ssl = ssl_context
6465
self.api_type = api_type
6566

6667
async def login(self) -> None:
68+
"""Perform authentication; default is a no-op for subclasses to override."""
6769
return None
6870

6971
async def refresh_if_needed(self) -> bool:
72+
"""Refresh authentication tokens if needed; default returns False."""
7073
return False
7174

7275
def auth_headers(self, path: str | None = None) -> Mapping[str, str]:
76+
"""Return authentication headers for a request path."""
7377
return {}
7478

7579
async def close(self) -> None:
80+
"""Close any resources held by the strategy; default is no-op."""
7681
return None
7782

7883

@@ -81,7 +86,7 @@ def __init__(
8186
self,
8287
credentials: UsernamePasswordCredentials,
8388
session: ClientSession,
84-
server: OverkizServer,
89+
server: ServerConfig,
8590
ssl_context: ssl.SSLContext | bool,
8691
api_type: APIType,
8792
) -> None:
@@ -116,7 +121,7 @@ def __init__(
116121
self,
117122
credentials: UsernamePasswordCredentials,
118123
session: ClientSession,
119-
server: OverkizServer,
124+
server: ServerConfig,
120125
ssl_context: ssl.SSLContext | bool,
121126
api_type: APIType,
122127
) -> None:
@@ -189,7 +194,7 @@ def __init__(
189194
self,
190195
credentials: UsernamePasswordCredentials,
191196
session: ClientSession,
192-
server: OverkizServer,
197+
server: ServerConfig,
193198
ssl_context: ssl.SSLContext | bool,
194199
api_type: APIType,
195200
) -> None:
@@ -238,7 +243,7 @@ def __init__(
238243
self,
239244
credentials: UsernamePasswordCredentials,
240245
session: ClientSession,
241-
server: OverkizServer,
246+
server: ServerConfig,
242247
ssl_context: ssl.SSLContext | bool,
243248
api_type: APIType,
244249
) -> None:
@@ -286,7 +291,7 @@ def __init__(
286291
self,
287292
credentials: LocalTokenCredentials,
288293
session: ClientSession,
289-
server: OverkizServer,
294+
server: ServerConfig,
290295
ssl_context: ssl.SSLContext | bool,
291296
api_type: APIType,
292297
) -> None:
@@ -306,7 +311,7 @@ def __init__(
306311
self,
307312
credentials: RexelOAuthCodeCredentials,
308313
session: ClientSession,
309-
server: OverkizServer,
314+
server: ServerConfig,
310315
ssl_context: ssl.SSLContext | bool,
311316
api_type: APIType,
312317
) -> None:
@@ -381,7 +386,7 @@ def __init__(
381386
self,
382387
credentials: TokenCredentials,
383388
session: ClientSession,
384-
server: OverkizServer,
389+
server: ServerConfig,
385390
ssl_context: ssl.SSLContext | bool,
386391
api_type: APIType,
387392
) -> None:

pyoverkiz/client.py

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,9 @@
5656
LocalToken,
5757
Option,
5858
OptionParameter,
59-
OverkizServer,
6059
Place,
6160
Scenario,
61+
ServerConfig,
6262
Setup,
6363
State,
6464
)
@@ -127,7 +127,7 @@ def _create_local_ssl_context() -> ssl.SSLContext:
127127
class OverkizClient:
128128
"""Interface class for the Overkiz API."""
129129

130-
server: OverkizServer
130+
server: ServerConfig
131131
setup: Setup | None
132132
devices: list[Device]
133133
gateways: list[Gateway]
@@ -139,18 +139,18 @@ class OverkizClient:
139139
def __init__(
140140
self,
141141
*,
142-
server: OverkizServer,
142+
server: ServerConfig | Server | str,
143143
credentials: Credentials,
144144
verify_ssl: bool = True,
145145
session: ClientSession | None = None,
146146
server_key: Server | str | None = None,
147147
) -> None:
148148
"""Constructor.
149149
150-
:param server: OverkizServer
150+
:param server: ServerConfig
151151
:param session: optional ClientSession
152152
"""
153-
self.server = server
153+
self.server = self._normalize_server(server)
154154

155155
self.setup: Setup | None = None
156156
self.devices: list[Device] = []
@@ -180,7 +180,7 @@ def __init__(
180180
)
181181

182182
async def __aenter__(self) -> OverkizClient:
183-
"""Enter the async context manager and return the client."""
183+
"""Enter async context manager and return the client instance."""
184184
return self
185185

186186
async def __aexit__(
@@ -192,7 +192,23 @@ async def __aexit__(
192192
"""Exit the async context manager and close the client session."""
193193
await self.close()
194194

195+
@staticmethod
196+
def _normalize_server(server: ServerConfig | Server | str) -> ServerConfig:
197+
"""Resolve user-provided server identifiers into a `ServerConfig`."""
198+
if isinstance(server, ServerConfig):
199+
return server
200+
201+
server_key = server.value if isinstance(server, Server) else str(server)
202+
203+
try:
204+
return SUPPORTED_SERVERS[server_key]
205+
except KeyError as error:
206+
raise OverkizException(
207+
f"Unknown server '{server_key}'. Provide a supported server key or ServerConfig instance."
208+
) from error
209+
195210
def _resolve_server_key(self) -> Server:
211+
"""Infer a `Server` enum for the current server configuration."""
196212
for key, value in SUPPORTED_SERVERS.items():
197213
if self.server is value or self.server.endpoint == value.endpoint:
198214
return Server(key)

pyoverkiz/const.py

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from __future__ import annotations
44

55
from pyoverkiz.enums import Server
6-
from pyoverkiz.models import OverkizServer
6+
from pyoverkiz.models import ServerConfig
77

88
COZYTOUCH_ATLANTIC_API = "https://apis.groupe-atlantic.com"
99
COZYTOUCH_CLIENT_ID = (
@@ -39,98 +39,98 @@
3939
Server.SOMFY_AMERICA,
4040
]
4141

42-
SUPPORTED_SERVERS: dict[str, OverkizServer] = {
43-
Server.ATLANTIC_COZYTOUCH: OverkizServer(
42+
SUPPORTED_SERVERS: dict[str, ServerConfig] = {
43+
Server.ATLANTIC_COZYTOUCH: ServerConfig(
4444
name="Atlantic Cozytouch",
4545
endpoint="https://ha110-1.overkiz.com/enduser-mobile-web/enduserAPI/",
4646
manufacturer="Atlantic",
4747
configuration_url=None,
4848
),
49-
Server.BRANDT: OverkizServer(
49+
Server.BRANDT: ServerConfig(
5050
name="Brandt Smart Control",
5151
endpoint="https://ha3-1.overkiz.com/enduser-mobile-web/enduserAPI/",
5252
manufacturer="Brandt",
5353
configuration_url=None,
5454
),
55-
Server.FLEXOM: OverkizServer(
55+
Server.FLEXOM: ServerConfig(
5656
name="Flexom",
5757
endpoint="https://ha108-1.overkiz.com/enduser-mobile-web/enduserAPI/",
5858
manufacturer="Bouygues",
5959
configuration_url=None,
6060
),
61-
Server.HEXAOM_HEXACONNECT: OverkizServer(
61+
Server.HEXAOM_HEXACONNECT: ServerConfig(
6262
name="Hexaom HexaConnect",
6363
endpoint="https://ha5-1.overkiz.com/enduser-mobile-web/enduserAPI/",
6464
manufacturer="Hexaom",
6565
configuration_url=None,
6666
),
67-
Server.HI_KUMO_ASIA: OverkizServer(
67+
Server.HI_KUMO_ASIA: ServerConfig(
6868
name="Hitachi Hi Kumo (Asia)",
6969
endpoint="https://ha203-1.overkiz.com/enduser-mobile-web/enduserAPI/",
7070
manufacturer="Hitachi",
7171
configuration_url=None,
7272
),
73-
Server.HI_KUMO_EUROPE: OverkizServer(
73+
Server.HI_KUMO_EUROPE: ServerConfig(
7474
name="Hitachi Hi Kumo (Europe)",
7575
endpoint="https://ha117-1.overkiz.com/enduser-mobile-web/enduserAPI/",
7676
manufacturer="Hitachi",
7777
configuration_url=None,
7878
),
79-
Server.HI_KUMO_OCEANIA: OverkizServer(
79+
Server.HI_KUMO_OCEANIA: ServerConfig(
8080
name="Hitachi Hi Kumo (Oceania)",
8181
endpoint="https://ha203-1.overkiz.com/enduser-mobile-web/enduserAPI/",
8282
manufacturer="Hitachi",
8383
configuration_url=None,
8484
),
85-
Server.NEXITY: OverkizServer(
85+
Server.NEXITY: ServerConfig(
8686
name="Nexity Eugénie",
8787
endpoint="https://ha106-1.overkiz.com/enduser-mobile-web/enduserAPI/",
8888
manufacturer="Nexity",
8989
configuration_url=None,
9090
),
91-
Server.REXEL: OverkizServer(
91+
Server.REXEL: ServerConfig(
9292
name="Rexel Energeasy Connect",
9393
endpoint=REXEL_BACKEND_API,
9494
manufacturer="Rexel",
9595
configuration_url="https://utilisateur.energeasyconnect.com/user/#/zone/equipements",
9696
),
97-
Server.SAUTER_COZYTOUCH: OverkizServer( # duplicate of Atlantic Cozytouch
97+
Server.SAUTER_COZYTOUCH: ServerConfig( # duplicate of Atlantic Cozytouch
9898
name="Sauter Cozytouch",
9999
endpoint="https://ha110-1.overkiz.com/enduser-mobile-web/enduserAPI/",
100100
manufacturer="Sauter",
101101
configuration_url=None,
102102
),
103-
Server.SIMU_LIVEIN2: OverkizServer( # alias of https://tahomalink.com
103+
Server.SIMU_LIVEIN2: ServerConfig( # alias of https://tahomalink.com
104104
name="SIMU (LiveIn2)",
105105
endpoint="https://ha101-1.overkiz.com/enduser-mobile-web/enduserAPI/",
106106
manufacturer="Somfy",
107107
configuration_url=None,
108108
),
109-
Server.SOMFY_EUROPE: OverkizServer( # alias of https://tahomalink.com
109+
Server.SOMFY_EUROPE: ServerConfig( # alias of https://tahomalink.com
110110
name="Somfy (Europe)",
111111
endpoint="https://ha101-1.overkiz.com/enduser-mobile-web/enduserAPI/",
112112
manufacturer="Somfy",
113113
configuration_url=None,
114114
),
115-
Server.SOMFY_AMERICA: OverkizServer(
115+
Server.SOMFY_AMERICA: ServerConfig(
116116
name="Somfy (North America)",
117117
endpoint="https://ha401-1.overkiz.com/enduser-mobile-web/enduserAPI/",
118118
manufacturer="Somfy",
119119
configuration_url=None,
120120
),
121-
Server.SOMFY_OCEANIA: OverkizServer(
121+
Server.SOMFY_OCEANIA: ServerConfig(
122122
name="Somfy (Oceania)",
123123
endpoint="https://ha201-1.overkiz.com/enduser-mobile-web/enduserAPI/",
124124
manufacturer="Somfy",
125125
configuration_url=None,
126126
),
127-
Server.THERMOR_COZYTOUCH: OverkizServer( # duplicate of Atlantic Cozytouch
127+
Server.THERMOR_COZYTOUCH: ServerConfig( # duplicate of Atlantic Cozytouch
128128
name="Thermor Cozytouch",
129129
endpoint="https://ha110-1.overkiz.com/enduser-mobile-web/enduserAPI/",
130130
manufacturer="Thermor",
131131
configuration_url=None,
132132
),
133-
Server.UBIWIZZ: OverkizServer(
133+
Server.UBIWIZZ: ServerConfig(
134134
name="Ubiwizz",
135135
endpoint="https://ha129-1.overkiz.com/enduser-mobile-web/enduserAPI/",
136136
manufacturer="Decelect",

pyoverkiz/models.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -917,8 +917,8 @@ def __init__(
917917

918918

919919
@define(kw_only=True)
920-
class OverkizServer:
921-
"""Class to describe an Overkiz server."""
920+
class ServerConfig:
921+
"""Connection target details for an Overkiz-compatible server."""
922922

923923
name: str
924924
endpoint: str

pyoverkiz/utils.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,17 @@
55
import re
66

77
from pyoverkiz.const import LOCAL_API_PATH
8-
from pyoverkiz.models import OverkizServer
8+
from pyoverkiz.models import ServerConfig
99

1010

1111
def generate_local_server(
1212
host: str,
1313
name: str = "Somfy Developer Mode",
1414
manufacturer: str = "Somfy",
1515
configuration_url: str | None = None,
16-
) -> OverkizServer:
17-
"""Generate OverkizServer class for connection with a local API (Somfy Developer mode)."""
18-
return OverkizServer(
16+
) -> ServerConfig:
17+
"""Generate server configuration for a local API (Somfy Developer mode)."""
18+
return ServerConfig(
1919
name=name,
2020
endpoint=f"https://{host}{LOCAL_API_PATH}",
2121
manufacturer=manufacturer,

0 commit comments

Comments
 (0)