21
21
import logging
22
22
import typing
23
23
24
- import requests
25
- from requests .auth import HTTPBasicAuth
26
- from requests_oauthlib import OAuth2Session
24
+ import authlib .integrations .httpx_client
25
+ import httpx
27
26
28
27
import openhab .items
29
28
import openhab .rules
29
+
30
30
from .config import Oauth2Config , Oauth2Token
31
31
32
32
__author__ = 'Georges Toth <georges@trypill.org>'
@@ -39,7 +39,7 @@ class OpenHAB:
39
39
def __init__ (self , base_url : str ,
40
40
username : typing .Optional [str ] = None ,
41
41
password : typing .Optional [str ] = None ,
42
- http_auth : typing .Optional [requests . auth . AuthBase ] = None ,
42
+ http_auth : typing .Optional [httpx . Auth ] = None ,
43
43
timeout : typing .Optional [float ] = None ,
44
44
oauth2_config : typing .Optional [typing .Dict [str , typing .Any ]] = None ,
45
45
) -> None :
@@ -70,8 +70,8 @@ def __init__(self, base_url: str,
70
70
provided password, in case openHAB requires authentication.
71
71
password (str, optional): A optional password, used in conjunction with a optional
72
72
provided username, in case openHAB requires authentication.
73
- http_auth (AuthBase , optional): An alternative to username/password pair, is to
74
- specify a custom http authentication object of type :class:`requests.auth.AuthBase `.
73
+ http_auth (Auth , optional): An alternative to username/password pair, is to
74
+ specify a custom http authentication object of type :class:`requests.Auth `.
75
75
timeout (float, optional): An optional timeout for REST transactions
76
76
oauth2_config: Optional OAuth2 configuration dictionary
77
77
@@ -86,27 +86,25 @@ def __init__(self, base_url: str,
86
86
if oauth2_config is not None :
87
87
self .oauth2_config = Oauth2Config (** oauth2_config )
88
88
89
- self .session = OAuth2Session (self .oauth2_config .client_id ,
90
- token = self .oauth2_config .token .model_dump (),
91
- auto_refresh_url = f'{ self .url_rest } /auth/token' ,
92
- auto_refresh_kwargs = {'client_id' : self .oauth2_config .client_id },
93
- token_updater = self ._oauth2_token_updater ,
94
- )
89
+ self .session = authlib .integrations .httpx_client .OAuth2Client (client_id = self .oauth2_config .client_id ,
90
+ token = self .oauth2_config .token .model_dump (),
91
+ update_token = self ._oauth2_token_updater ,
92
+ )
93
+
94
+ print ('>>>>' , self .oauth2_config .token .refresh_token )
95
+
96
+ self .session .metadata ['token_endpoint' ] = f'{ self .url_rest } /auth/token'
95
97
96
98
if not self .oauth2_config .token_cache .is_file ():
97
99
self ._oauth2_token_updater (self .oauth2_config .token .model_dump ())
98
100
99
101
else :
100
- self .session = requests . Session ( )
102
+ self .session = httpx . Client ( timeout = timeout )
101
103
102
104
if http_auth is not None :
103
105
self .session .auth = http_auth
104
106
elif not (username is None or password is None ):
105
- self .session .auth = HTTPBasicAuth (username , password )
106
-
107
- self .session .headers ['accept' ] = 'application/json'
108
-
109
- self .timeout = timeout
107
+ self .session .auth = httpx .BasicAuth (username , password )
110
108
111
109
self .logger = logging .getLogger (__name__ )
112
110
@@ -121,7 +119,7 @@ def rules(self) -> openhab.rules.Rules:
121
119
return self ._rules
122
120
123
121
@staticmethod
124
- def _check_req_return (req : requests .Response ) -> None :
122
+ def _check_req_return (req : httpx .Response ) -> None :
125
123
"""Internal method for checking the return value of a REST HTTP request.
126
124
127
125
Args:
@@ -149,13 +147,14 @@ def req_get(self, uri_path: str) -> typing.Any:
149
147
Returns:
150
148
dict: Returns a dict containing the data returned by the OpenHAB REST server.
151
149
"""
152
- r = self .session .get (self .url_rest + uri_path , timeout = self . timeout )
150
+ r = self .session .get (self .url_rest + uri_path )
153
151
self ._check_req_return (r )
154
152
return r .json ()
155
153
156
154
def req_post (self ,
157
155
uri_path : str ,
158
- data : typing .Optional [typing .Union [str , bytes , typing .Mapping [str , typing .Any ], typing .Iterable [typing .Tuple [str , typing .Optional [str ]]]]] = None ,
156
+ data : typing .Optional [typing .Union [str , bytes , typing .Mapping [str , typing .Any ], typing .Iterable [
157
+ typing .Tuple [str , typing .Optional [str ]]]]] = None ,
159
158
) -> None :
160
159
"""Helper method for initiating a HTTP POST request.
161
160
@@ -169,7 +168,10 @@ def req_post(self,
169
168
Returns:
170
169
None: No data is returned.
171
170
"""
172
- r = self .session .post (self .url_rest + uri_path , data = data , headers = {'Content-Type' : 'text/plain' }, timeout = self .timeout )
171
+ headers = self .session .headers
172
+ headers ['Content-Type' ] = 'text/plain'
173
+
174
+ r = self .session .post (self .url_rest + uri_path , content = data , headers = headers )
173
175
self ._check_req_return (r )
174
176
175
177
def req_put (self ,
@@ -194,8 +196,12 @@ def req_put(self,
194
196
"""
195
197
if headers is None :
196
198
headers = {'Content-Type' : 'text/plain' }
199
+ content = data
200
+ data = None
201
+ else :
202
+ content = None
197
203
198
- r = self .session .put (self .url_rest + uri_path , data = data , json = json_data , headers = headers , timeout = self . timeout )
204
+ r = self .session .put (self .url_rest + uri_path , content = content , data = data , json = json_data , headers = headers )
199
205
self ._check_req_return (r )
200
206
201
207
# fetch all items
@@ -293,7 +299,7 @@ def logout(self) -> bool:
293
299
Returns:
294
300
True or False depending on if the logout did succeed.
295
301
"""
296
- if self .oauth2_config is None or not isinstance (self .session , OAuth2Session ):
302
+ if self .oauth2_config is None or not isinstance (self .session , authlib . integrations . httpx_client . OAuth2Client ):
297
303
raise ValueError ('You are trying to logout from a non-OAuth2 session. This is not supported!' )
298
304
299
305
data = {'refresh_token' : self .oauth2_config .token .refresh_token ,
@@ -305,12 +311,16 @@ def logout(self) -> bool:
305
311
306
312
return res .status_code == 200
307
313
308
- def _oauth2_token_updater (self , token : typing .Dict [str , typing .Any ]) -> None :
314
+ def _oauth2_token_updater (self , token : typing .Dict [str , typing .Any ],
315
+ refresh_token : typing .Any = None ,
316
+ access_token : typing .Any = None ) -> None :
309
317
if self .oauth2_config is None :
310
318
raise ValueError ('OAuth2 configuration is not set; invalid action!' )
311
319
312
320
self .oauth2_config .token = Oauth2Token (** token )
313
321
322
+ print ('>SSS>>>' , self .oauth2_config .token .refresh_token )
323
+
314
324
with self .oauth2_config .token_cache .open ('w' , encoding = 'utf-8' ) as fhdl :
315
325
fhdl .write (self .oauth2_config .token .model_dump_json ())
316
326
@@ -346,13 +356,15 @@ def create_or_update_item(self,
346
356
Can be one of ['EQUALITY', 'AND', 'OR', 'NAND', 'NOR', 'AVG', 'SUM', 'MAX', 'MIN', 'COUNT', 'LATEST', 'EARLIEST']
347
357
function_params: Optional list of function params (no documentation found), depending on function name.
348
358
"""
349
- paramdict : typing .Dict [str , typing .Union [str , typing .List [str ], typing .Dict [str , typing .Union [str , typing .List [str ]]]]] = {}
359
+ paramdict : typing .Dict [
360
+ str , typing .Union [str , typing .List [str ], typing .Dict [str , typing .Union [str , typing .List [str ]]]]] = {}
350
361
351
362
if isinstance (_type , type ):
352
363
if issubclass (_type , openhab .items .Item ):
353
364
itemtypename = _type .TYPENAME
354
365
else :
355
- raise ValueError (f'_type parameter must be a valid subclass of type *Item* or a string name of such a class; given value is "{ str (_type )} "' )
366
+ raise ValueError (
367
+ f'_type parameter must be a valid subclass of type *Item* or a string name of such a class; given value is "{ str (_type )} "' )
356
368
else :
357
369
itemtypename = _type
358
370
@@ -381,12 +393,14 @@ def create_or_update_item(self,
381
393
paramdict ['groupType' ] = group_type .TYPENAME
382
394
# paramdict['function'] = {'name': 'AVG'}
383
395
else :
384
- raise ValueError (f'group_type parameter must be a valid subclass of type *Item* or a string name of such a class; given value is "{ str (group_type )} "' )
396
+ raise ValueError (
397
+ f'group_type parameter must be a valid subclass of type *Item* or a string name of such a class; given value is "{ str (group_type )} "' )
385
398
else :
386
399
paramdict ['groupType' ] = group_type
387
400
388
401
if function_name is not None :
389
- if function_name not in ('EQUALITY' , 'AND' , 'OR' , 'NAND' , 'NOR' , 'AVG' , 'SUM' , 'MAX' , 'MIN' , 'COUNT' , 'LATEST' , 'EARLIEST' ):
402
+ if function_name not in (
403
+ 'EQUALITY' , 'AND' , 'OR' , 'NAND' , 'NOR' , 'AVG' , 'SUM' , 'MAX' , 'MIN' , 'COUNT' , 'LATEST' , 'EARLIEST' ):
390
404
raise ValueError (f'Invalid function name "{ function_name } ' )
391
405
392
406
if function_name in ('AND' , 'OR' , 'NAND' , 'NOR' ) and (not function_params or len (function_params ) != 2 ):
0 commit comments