Skip to content

Commit d7bdda0

Browse files
committed
Refactor server configuration handling to use create_local_server_config and update related references
1 parent 9f28a26 commit d7bdda0

10 files changed

Lines changed: 105 additions & 69 deletions

File tree

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ import time
9595

9696
from pyoverkiz.auth.credentials import LocalTokenCredentials
9797
from pyoverkiz.client import OverkizClient
98-
from pyoverkiz.utils import generate_local_server
98+
from pyoverkiz.utils import create_local_server_config
9999

100100
LOCAL_GATEWAY = "gateway-xxxx-xxxx-xxxx.local" # or use the IP address of your gateway
101101
VERIFY_SSL = True # set verify_ssl to False if you don't use the .local hostname
@@ -105,7 +105,7 @@ async def main() -> None:
105105
token = "" # generate your token via the Somfy app and include it here
106106

107107
async with OverkizClient(
108-
server=generate_local_server(host=LOCAL_GATEWAY),
108+
server=create_local_server_config(host=LOCAL_GATEWAY),
109109
credentials=LocalTokenCredentials(token),
110110
verify_ssl=VERIFY_SSL,
111111
) as client:

pyoverkiz/auth/credentials.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ class LocalTokenCredentials(TokenCredentials):
3131

3232
@dataclass(slots=True)
3333
class RexelOAuthCodeCredentials(Credentials):
34-
""" "Credentials using Rexel OAuth2 authorization code."""
34+
"""Credentials using Rexel OAuth2 authorization code."""
3535

3636
code: str
3737
redirect_uri: str

pyoverkiz/auth/factory.py

Lines changed: 27 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -23,34 +23,32 @@
2323
SessionLoginStrategy,
2424
SomfyAuthStrategy,
2525
)
26-
from pyoverkiz.const import LOCAL_API_PATH, SUPPORTED_SERVERS
26+
from pyoverkiz.const import SUPPORTED_SERVERS
2727
from pyoverkiz.enums import APIType, Server
2828
from pyoverkiz.models import ServerConfig
2929

3030

3131
def build_auth_strategy(
3232
server_key: str | Server | None,
33-
server: ServerConfig,
33+
server_config: ServerConfig,
3434
credentials: Credentials,
3535
session: ClientSession,
3636
ssl_context: ssl.SSLContext | bool,
3737
) -> Any:
3838
"""Build the correct auth strategy for the given server and credentials."""
39-
api_type = APIType.LOCAL if LOCAL_API_PATH in server.endpoint else APIType.CLOUD
40-
4139
# Normalize server key
4240
try:
43-
key = Server(server_key) if server_key else _match_server_key(server)
41+
key = Server(server_key) if server_key else _match_server_key(server_config)
4442
except ValueError:
4543
key = None
4644

4745
if key == Server.SOMFY_EUROPE:
4846
return SomfyAuthStrategy(
4947
_ensure_username_password(credentials),
5048
session,
51-
server,
49+
server_config,
5250
ssl_context,
53-
api_type,
51+
server_config.type,
5452
)
5553

5654
if key in {
@@ -61,43 +59,55 @@ def build_auth_strategy(
6159
return CozytouchAuthStrategy(
6260
_ensure_username_password(credentials),
6361
session,
64-
server,
62+
server_config,
6563
ssl_context,
66-
api_type,
64+
server_config.type,
6765
)
6866

6967
if key == Server.NEXITY:
7068
return NexityAuthStrategy(
7169
_ensure_username_password(credentials),
7270
session,
73-
server,
71+
server_config,
7472
ssl_context,
75-
api_type,
73+
server_config.type,
7674
)
7775

7876
if key == Server.REXEL:
7977
return RexelAuthStrategy(
80-
_ensure_rexel(credentials), session, server, ssl_context, api_type
78+
_ensure_rexel(credentials),
79+
session,
80+
server_config,
81+
ssl_context,
82+
server_config.type,
8183
)
8284

83-
if api_type == APIType.LOCAL:
85+
if server_config.type == APIType.LOCAL:
8486
if isinstance(credentials, LocalTokenCredentials):
8587
return LocalTokenAuthStrategy(
86-
credentials, session, server, ssl_context, api_type
88+
credentials, session, server_config, ssl_context, server_config.type
8789
)
8890
return BearerTokenAuthStrategy(
89-
_ensure_token(credentials), session, server, ssl_context, api_type
91+
_ensure_token(credentials),
92+
session,
93+
server_config,
94+
ssl_context,
95+
server_config.type,
9096
)
9197

9298
if isinstance(credentials, TokenCredentials) and not isinstance(
9399
credentials, LocalTokenCredentials
94100
):
95101
return BearerTokenAuthStrategy(
96-
credentials, session, server, ssl_context, api_type
102+
credentials, session, server_config, ssl_context, server_config.type
97103
)
98104

99105
return SessionLoginStrategy(
100-
_ensure_username_password(credentials), session, server, ssl_context, api_type
106+
_ensure_username_password(credentials),
107+
session,
108+
server_config,
109+
ssl_context,
110+
server_config.type,
101111
)
102112

103113

pyoverkiz/auth/strategies.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -364,7 +364,7 @@ async def login(self) -> None:
364364
)
365365

366366
async def refresh_if_needed(self) -> bool:
367-
""" "Refresh Rexel OAuth2 tokens if needed."""
367+
"""Refresh Rexel OAuth2 tokens if needed."""
368368
if not self.context.is_expired() or not self.context.refresh_token:
369369
return False
370370

pyoverkiz/client.py

Lines changed: 23 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
)
2121

2222
from pyoverkiz.auth import Credentials, build_auth_strategy
23-
from pyoverkiz.const import LOCAL_API_PATH, SUPPORTED_SERVERS
23+
from pyoverkiz.const import SUPPORTED_SERVERS
2424
from pyoverkiz.enums import APIType, CommandMode, Server
2525
from pyoverkiz.exceptions import (
2626
AccessDeniedToGatewayException,
@@ -128,13 +128,12 @@ def _create_local_ssl_context() -> ssl.SSLContext:
128128
class OverkizClient:
129129
"""Interface class for the Overkiz API."""
130130

131-
server: ServerConfig
131+
server_config: ServerConfig
132132
setup: Setup | None
133133
devices: list[Device]
134134
gateways: list[Gateway]
135135
event_listener_id: str | None
136136
session: ClientSession
137-
api_type: APIType
138137
_ssl: ssl.SSLContext | bool = True
139138

140139
def __init__(
@@ -151,7 +150,7 @@ def __init__(
151150
:param server: ServerConfig
152151
:param session: optional ClientSession
153152
"""
154-
self.server = self._normalize_server(server)
153+
self.server_config = self._normalize_server(server)
155154

156155
self.setup: Setup | None = None
157156
self.devices: list[Device] = []
@@ -161,23 +160,22 @@ def __init__(
161160
self.session = session if session else ClientSession()
162161
self._ssl = verify_ssl
163162

164-
if LOCAL_API_PATH in self.server.endpoint:
165-
self.api_type = APIType.LOCAL
163+
if self.server_config.type == APIType.LOCAL and verify_ssl:
164+
# To avoid security issues while authentication to local API, we add the following authority to
165+
# our HTTPS client trust store: https://ca.overkiz.com/overkiz-root-ca-2048.crt
166+
self._ssl = SSL_CONTEXT_LOCAL_API
166167

167-
if verify_ssl:
168-
# To avoid security issues while authentication to local API, we add the following authority to
169-
# our HTTPS client trust store: https://ca.overkiz.com/overkiz-root-ca-2048.crt
170-
self._ssl = SSL_CONTEXT_LOCAL_API
171-
172-
# Disable strict validation introduced in Python 3.13, which doesn't
173-
# work with Overkiz self-signed gateway certificates
174-
self._ssl.verify_flags &= ~ssl.VERIFY_X509_STRICT
175-
else:
176-
self.api_type = APIType.CLOUD
168+
# Disable strict validation introduced in Python 3.13, which doesn't
169+
# work with Overkiz self-signed gateway certificates
170+
self._ssl.verify_flags &= ~ssl.VERIFY_X509_STRICT
177171

178172
inferred_server_key = server_key or self._resolve_server_key()
179173
self._auth = build_auth_strategy(
180-
inferred_server_key, self.server, credentials, self.session, self._ssl
174+
inferred_server_key,
175+
self.server_config,
176+
credentials,
177+
self.session,
178+
self._ssl,
181179
)
182180

183181
async def __aenter__(self) -> OverkizClient:
@@ -211,10 +209,13 @@ def _normalize_server(server: ServerConfig | Server | str) -> ServerConfig:
211209
def _resolve_server_key(self) -> Server:
212210
"""Infer a `Server` enum for the current server configuration."""
213211
for key, value in SUPPORTED_SERVERS.items():
214-
if self.server is value or self.server.endpoint == value.endpoint:
212+
if (
213+
self.server_config is value
214+
or self.server_config.endpoint == value.endpoint
215+
):
215216
return Server(key)
216217

217-
if self.api_type == APIType.LOCAL:
218+
if self.server_config.type == APIType.LOCAL:
218219
return Server(Server.SOMFY_DEVELOPER_MODE)
219220

220221
raise OverkizException(
@@ -603,7 +604,7 @@ async def __get(self, path: str) -> Any:
603604
headers = dict(self._auth.auth_headers(path))
604605

605606
async with self.session.get(
606-
f"{self.server.endpoint}{path}",
607+
f"{self.server_config.endpoint}{path}",
607608
headers=headers,
608609
ssl=self._ssl,
609610
) as response:
@@ -618,7 +619,7 @@ async def __post(
618619
headers = dict(self._auth.auth_headers(path))
619620

620621
async with self.session.post(
621-
f"{self.server.endpoint}{path}",
622+
f"{self.server_config.endpoint}{path}",
622623
data=data,
623624
json=payload,
624625
headers=headers,
@@ -633,7 +634,7 @@ async def __delete(self, path: str) -> None:
633634
headers = dict(self._auth.auth_headers(path))
634635

635636
async with self.session.delete(
636-
f"{self.server.endpoint}{path}",
637+
f"{self.server_config.endpoint}{path}",
637638
headers=headers,
638639
ssl=self._ssl,
639640
) as response:

pyoverkiz/const.py

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

55
from pyoverkiz.enums import Server
6+
from pyoverkiz.enums.server import APIType
67
from pyoverkiz.models import ServerConfig
78

89
COZYTOUCH_ATLANTIC_API = "https://apis.groupe-atlantic.com"
@@ -44,96 +45,96 @@
4445
name="Atlantic Cozytouch",
4546
endpoint="https://ha110-1.overkiz.com/enduser-mobile-web/enduserAPI/",
4647
manufacturer="Atlantic",
47-
configuration_url=None,
48+
type=APIType.CLOUD,
4849
),
4950
Server.BRANDT: ServerConfig(
5051
name="Brandt Smart Control",
5152
endpoint="https://ha3-1.overkiz.com/enduser-mobile-web/enduserAPI/",
5253
manufacturer="Brandt",
53-
configuration_url=None,
54+
type=APIType.CLOUD,
5455
),
5556
Server.FLEXOM: ServerConfig(
5657
name="Flexom",
5758
endpoint="https://ha108-1.overkiz.com/enduser-mobile-web/enduserAPI/",
5859
manufacturer="Bouygues",
59-
configuration_url=None,
60+
type=APIType.CLOUD,
6061
),
6162
Server.HEXAOM_HEXACONNECT: ServerConfig(
6263
name="Hexaom HexaConnect",
6364
endpoint="https://ha5-1.overkiz.com/enduser-mobile-web/enduserAPI/",
6465
manufacturer="Hexaom",
65-
configuration_url=None,
66+
type=APIType.CLOUD,
6667
),
6768
Server.HI_KUMO_ASIA: ServerConfig(
6869
name="Hitachi Hi Kumo (Asia)",
6970
endpoint="https://ha203-1.overkiz.com/enduser-mobile-web/enduserAPI/",
7071
manufacturer="Hitachi",
71-
configuration_url=None,
72+
type=APIType.CLOUD,
7273
),
7374
Server.HI_KUMO_EUROPE: ServerConfig(
7475
name="Hitachi Hi Kumo (Europe)",
7576
endpoint="https://ha117-1.overkiz.com/enduser-mobile-web/enduserAPI/",
7677
manufacturer="Hitachi",
77-
configuration_url=None,
78+
type=APIType.CLOUD,
7879
),
7980
Server.HI_KUMO_OCEANIA: ServerConfig(
8081
name="Hitachi Hi Kumo (Oceania)",
8182
endpoint="https://ha203-1.overkiz.com/enduser-mobile-web/enduserAPI/",
8283
manufacturer="Hitachi",
83-
configuration_url=None,
84+
type=APIType.CLOUD,
8485
),
8586
Server.NEXITY: ServerConfig(
8687
name="Nexity Eugénie",
8788
endpoint="https://ha106-1.overkiz.com/enduser-mobile-web/enduserAPI/",
8889
manufacturer="Nexity",
89-
configuration_url=None,
90+
type=APIType.CLOUD,
9091
),
9192
Server.REXEL: ServerConfig(
9293
name="Rexel Energeasy Connect",
9394
endpoint=REXEL_BACKEND_API,
9495
manufacturer="Rexel",
95-
configuration_url="https://utilisateur.energeasyconnect.com/user/#/zone/equipements",
96+
type=APIType.CLOUD,
9697
),
9798
Server.SAUTER_COZYTOUCH: ServerConfig( # duplicate of Atlantic Cozytouch
9899
name="Sauter Cozytouch",
99100
endpoint="https://ha110-1.overkiz.com/enduser-mobile-web/enduserAPI/",
100101
manufacturer="Sauter",
101-
configuration_url=None,
102+
type=APIType.CLOUD,
102103
),
103104
Server.SIMU_LIVEIN2: ServerConfig( # alias of https://tahomalink.com
104105
name="SIMU (LiveIn2)",
105106
endpoint="https://ha101-1.overkiz.com/enduser-mobile-web/enduserAPI/",
106107
manufacturer="Somfy",
107-
configuration_url=None,
108+
type=APIType.CLOUD,
108109
),
109110
Server.SOMFY_EUROPE: ServerConfig( # alias of https://tahomalink.com
110111
name="Somfy (Europe)",
111112
endpoint="https://ha101-1.overkiz.com/enduser-mobile-web/enduserAPI/",
112113
manufacturer="Somfy",
113-
configuration_url=None,
114+
type=APIType.CLOUD,
114115
),
115116
Server.SOMFY_AMERICA: ServerConfig(
116117
name="Somfy (North America)",
117118
endpoint="https://ha401-1.overkiz.com/enduser-mobile-web/enduserAPI/",
118119
manufacturer="Somfy",
119-
configuration_url=None,
120+
type=APIType.CLOUD,
120121
),
121122
Server.SOMFY_OCEANIA: ServerConfig(
122123
name="Somfy (Oceania)",
123124
endpoint="https://ha201-1.overkiz.com/enduser-mobile-web/enduserAPI/",
124125
manufacturer="Somfy",
125-
configuration_url=None,
126+
type=APIType.CLOUD,
126127
),
127128
Server.THERMOR_COZYTOUCH: ServerConfig( # duplicate of Atlantic Cozytouch
128129
name="Thermor Cozytouch",
129130
endpoint="https://ha110-1.overkiz.com/enduser-mobile-web/enduserAPI/",
130131
manufacturer="Thermor",
131-
configuration_url=None,
132+
type=APIType.CLOUD,
132133
),
133134
Server.UBIWIZZ: ServerConfig(
134135
name="Ubiwizz",
135136
endpoint="https://ha129-1.overkiz.com/enduser-mobile-web/enduserAPI/",
136137
manufacturer="Decelect",
137-
configuration_url=None,
138+
type=APIType.CLOUD,
138139
),
139140
}

pyoverkiz/models.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
)
2525
from pyoverkiz.enums.command import OverkizCommand, OverkizCommandParam
2626
from pyoverkiz.enums.protocol import Protocol
27+
from pyoverkiz.enums.server import APIType
2728
from pyoverkiz.obfuscate import obfuscate_email, obfuscate_id, obfuscate_string
2829
from pyoverkiz.types import DATA_TYPE_TO_PYTHON, StateType
2930

@@ -965,7 +966,8 @@ class ServerConfig:
965966
name: str
966967
endpoint: str
967968
manufacturer: str
968-
configuration_url: str | None
969+
type: APIType | str
970+
configuration_url: str | None = None
969971

970972

971973
@define(kw_only=True)

0 commit comments

Comments
 (0)