weheat 2024.11.1__py3-none-any.whl → 2025.11.24rc1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. weheat/__init__.py +6 -2
  2. weheat/abstractions/__init__.py +1 -1
  3. weheat/abstractions/discovery.py +11 -7
  4. weheat/abstractions/heat_pump.py +223 -81
  5. weheat/abstractions/user.py +16 -11
  6. weheat/api/__init__.py +1 -0
  7. weheat/api/energy_log_api.py +615 -127
  8. weheat/api/heat_pump_api.py +580 -374
  9. weheat/api/heat_pump_log_api.py +884 -360
  10. weheat/api/user_api.py +260 -115
  11. weheat/api_client.py +238 -265
  12. weheat/api_response.py +11 -15
  13. weheat/configuration.py +14 -9
  14. weheat/exceptions.py +59 -25
  15. weheat/models/__init__.py +8 -0
  16. weheat/models/boiler_type.py +8 -3
  17. weheat/models/device_state.py +9 -5
  18. weheat/models/dhw_type.py +8 -3
  19. weheat/models/energy_view_dto.py +87 -65
  20. weheat/models/heat_pump_log_view_dto.py +1394 -559
  21. weheat/models/heat_pump_model.py +8 -3
  22. weheat/models/heat_pump_status_enum.py +9 -4
  23. weheat/models/pagination_metadata.py +95 -0
  24. weheat/models/raw_heat_pump_log_dto.py +438 -375
  25. weheat/models/raw_heatpump_log_and_is_online_dto.py +578 -0
  26. weheat/models/read_all_heat_pump_dto.py +69 -53
  27. weheat/models/read_all_heat_pump_dto_paged_response.py +107 -0
  28. weheat/models/read_heat_pump_dto.py +64 -48
  29. weheat/models/read_user_dto.py +55 -37
  30. weheat/models/read_user_me_dto.py +124 -0
  31. weheat/models/role.py +10 -4
  32. weheat/models/total_energy_aggregate.py +111 -0
  33. weheat/rest.py +152 -259
  34. weheat-2025.11.24rc1.dist-info/METADATA +104 -0
  35. weheat-2025.11.24rc1.dist-info/RECORD +39 -0
  36. {weheat-2024.11.1.dist-info → weheat-2025.11.24rc1.dist-info}/WHEEL +1 -1
  37. weheat/abstractions/auth.py +0 -34
  38. weheat/models/heat_pump_type.py +0 -42
  39. weheat-2024.11.1.dist-info/METADATA +0 -107
  40. weheat-2024.11.1.dist-info/RECORD +0 -36
  41. {weheat-2024.11.1.dist-info → weheat-2025.11.24rc1.dist-info/licenses}/LICENSE +0 -0
  42. {weheat-2024.11.1.dist-info → weheat-2025.11.24rc1.dist-info}/top_level.txt +0 -0
weheat/rest.py CHANGED
@@ -14,143 +14,133 @@
14
14
 
15
15
  import io
16
16
  import json
17
- import logging
18
17
  import re
19
18
  import ssl
20
19
 
21
- from urllib.parse import urlencode, quote_plus
22
- import urllib3
20
+ import aiohttp
21
+ import aiohttp_retry
23
22
 
24
- from weheat.exceptions import ApiException, UnauthorizedException, ForbiddenException, NotFoundException, ServiceException, ApiValueError, BadRequestException
23
+ from weheat.exceptions import ApiException, ApiValueError
25
24
 
25
+ RESTResponseType = aiohttp.ClientResponse
26
26
 
27
- logger = logging.getLogger(__name__)
28
-
29
- SUPPORTED_SOCKS_PROXIES = {"socks5", "socks5h", "socks4", "socks4a"}
30
-
31
-
32
- def is_socks_proxy_url(url):
33
- if url is None:
34
- return False
35
- split_section = url.split("://")
36
- if len(split_section) < 2:
37
- return False
38
- else:
39
- return split_section[0].lower() in SUPPORTED_SOCKS_PROXIES
40
-
27
+ ALLOW_RETRY_METHODS = frozenset({'DELETE', 'GET', 'HEAD', 'OPTIONS', 'PUT', 'TRACE'})
41
28
 
42
29
  class RESTResponse(io.IOBase):
43
30
 
44
31
  def __init__(self, resp) -> None:
45
- self.urllib3_response = resp
32
+ self.response = resp
46
33
  self.status = resp.status
47
34
  self.reason = resp.reason
48
- self.data = resp.data
35
+ self.data = None
36
+
37
+ async def read(self):
38
+ if self.data is None:
39
+ self.data = await self.response.read()
40
+ return self.data
49
41
 
50
42
  def getheaders(self):
51
- """Returns a dictionary of the response headers."""
52
- return self.urllib3_response.headers
43
+ """Returns a CIMultiDictProxy of the response headers."""
44
+ return self.response.headers
53
45
 
54
46
  def getheader(self, name, default=None):
55
47
  """Returns a given response header."""
56
- return self.urllib3_response.headers.get(name, default)
48
+ return self.response.headers.get(name, default)
57
49
 
58
50
 
59
51
  class RESTClientObject:
60
52
 
61
- def __init__(self, configuration, pools_size=4, maxsize=None) -> None:
62
- # urllib3.PoolManager will pass all kw parameters to connectionpool
63
- # https://github.com/shazow/urllib3/blob/f9409436f83aeb79fbaf090181cd81b784f1b8ce/urllib3/poolmanager.py#L75 # noqa: E501
64
- # https://github.com/shazow/urllib3/blob/f9409436f83aeb79fbaf090181cd81b784f1b8ce/urllib3/connectionpool.py#L680 # noqa: E501
65
- # maxsize is the number of requests to host that are allowed in parallel # noqa: E501
66
- # Custom SSL certificates and client certificates: http://urllib3.readthedocs.io/en/latest/advanced-usage.html # noqa: E501
67
-
68
- # cert_reqs
69
- if configuration.verify_ssl:
70
- cert_reqs = ssl.CERT_REQUIRED
71
- else:
72
- cert_reqs = ssl.CERT_NONE
53
+ def __init__(self, configuration) -> None:
73
54
 
74
- addition_pool_args = {}
75
- if configuration.assert_hostname is not None:
76
- addition_pool_args['assert_hostname'] = configuration.assert_hostname # noqa: E501
55
+ # maxsize is number of requests to host that are allowed in parallel
56
+ maxsize = configuration.connection_pool_maxsize
77
57
 
78
- if configuration.retries is not None:
79
- addition_pool_args['retries'] = configuration.retries
80
-
81
- if configuration.tls_server_name:
82
- addition_pool_args['server_hostname'] = configuration.tls_server_name
58
+ self._client_session = configuration.client_session
59
+ # only setup SSL when the session is not provided. Otherwise, it will do disk access and HA will get mad.
60
+ if not configuration.client_session:
61
+ ssl_context = ssl.create_default_context(
62
+ cafile=configuration.ssl_ca_cert
63
+ )
64
+ if configuration.cert_file:
65
+ ssl_context.load_cert_chain(
66
+ configuration.cert_file, keyfile=configuration.key_file
67
+ )
83
68
 
69
+ if not configuration.verify_ssl:
70
+ ssl_context.check_hostname = False
71
+ ssl_context.verify_mode = ssl.CERT_NONE
84
72
 
85
- if configuration.socket_options is not None:
86
- addition_pool_args['socket_options'] = configuration.socket_options
73
+ connector = aiohttp.TCPConnector(
74
+ limit=maxsize,
75
+ ssl=ssl_context
76
+ )
87
77
 
88
- if maxsize is None:
89
- if configuration.connection_pool_maxsize is not None:
90
- maxsize = configuration.connection_pool_maxsize
91
- else:
92
- maxsize = 4
93
-
94
- # https pool manager
95
- if configuration.proxy:
96
- if is_socks_proxy_url(configuration.proxy):
97
- from urllib3.contrib.socks import SOCKSProxyManager
98
- self.pool_manager = SOCKSProxyManager(
99
- cert_reqs=cert_reqs,
100
- ca_certs=configuration.ssl_ca_cert,
101
- cert_file=configuration.cert_file,
102
- key_file=configuration.key_file,
103
- proxy_url=configuration.proxy,
104
- headers=configuration.proxy_headers,
105
- **addition_pool_args
106
- )
107
- else:
108
- self.pool_manager = urllib3.ProxyManager(
109
- num_pools=pools_size,
110
- maxsize=maxsize,
111
- cert_reqs=cert_reqs,
112
- ca_certs=configuration.ssl_ca_cert,
113
- cert_file=configuration.cert_file,
114
- key_file=configuration.key_file,
115
- proxy_url=configuration.proxy,
116
- proxy_headers=configuration.proxy_headers,
117
- **addition_pool_args
118
- )
78
+ # https pool manager
79
+ self.pool_manager = aiohttp.ClientSession(
80
+ connector=connector,
81
+ trust_env=True
82
+ )
119
83
  else:
120
- self.pool_manager = urllib3.PoolManager(
121
- num_pools=pools_size,
122
- maxsize=maxsize,
123
- cert_reqs=cert_reqs,
124
- ca_certs=configuration.ssl_ca_cert,
125
- cert_file=configuration.cert_file,
126
- key_file=configuration.key_file,
127
- **addition_pool_args
84
+ self.pool_manager = configuration.client_session
85
+
86
+ self.proxy = configuration.proxy
87
+ self.proxy_headers = configuration.proxy_headers
88
+
89
+ retries = configuration.retries
90
+ if retries is not None:
91
+ self.retry_client = aiohttp_retry.RetryClient(
92
+ client_session=self.pool_manager,
93
+ retry_options=aiohttp_retry.ExponentialRetry(
94
+ attempts=retries,
95
+ factor=0.0,
96
+ start_timeout=0.0,
97
+ max_timeout=120.0
98
+ )
128
99
  )
129
-
130
- def request(self, method, url, query_params=None, headers=None,
131
- body=None, post_params=None, _preload_content=True,
132
- _request_timeout=None):
133
- """Perform requests.
100
+ else:
101
+ self.retry_client = None
102
+
103
+ async def close(self):
104
+ # do not close the session if provided via client_session
105
+ if self._client_session:
106
+ return
107
+ await self.pool_manager.close()
108
+ if self.retry_client is not None:
109
+ await self.retry_client.close()
110
+
111
+ async def request(
112
+ self,
113
+ method,
114
+ url,
115
+ headers=None,
116
+ body=None,
117
+ post_params=None,
118
+ _request_timeout=None
119
+ ):
120
+ """Execute request
134
121
 
135
122
  :param method: http request method
136
123
  :param url: http request url
137
- :param query_params: query parameters in the url
138
124
  :param headers: http request headers
139
125
  :param body: request json body, for `application/json`
140
126
  :param post_params: request post parameters,
141
127
  `application/x-www-form-urlencoded`
142
128
  and `multipart/form-data`
143
- :param _preload_content: if False, the urllib3.HTTPResponse object will
144
- be returned without reading/decoding response
145
- data. Default is True.
146
129
  :param _request_timeout: timeout setting for this request. If one
147
130
  number provided, it will be total request
148
131
  timeout. It can also be a pair (tuple) of
149
132
  (connection, read) timeouts.
150
133
  """
151
134
  method = method.upper()
152
- assert method in ['GET', 'HEAD', 'DELETE', 'POST', 'PUT',
153
- 'PATCH', 'OPTIONS']
135
+ assert method in [
136
+ 'GET',
137
+ 'HEAD',
138
+ 'DELETE',
139
+ 'POST',
140
+ 'PUT',
141
+ 'PATCH',
142
+ 'OPTIONS'
143
+ ]
154
144
 
155
145
  if post_params and body:
156
146
  raise ApiValueError(
@@ -160,168 +150,71 @@ class RESTClientObject:
160
150
  post_params = post_params or {}
161
151
  headers = headers or {}
162
152
  # url already contains the URL query string
163
- # so reset query_params to empty dict
164
- query_params = {}
165
-
166
- timeout = None
167
- if _request_timeout:
168
- if isinstance(_request_timeout, (int,float)): # noqa: E501,F821
169
- timeout = urllib3.Timeout(total=_request_timeout)
170
- elif (isinstance(_request_timeout, tuple) and
171
- len(_request_timeout) == 2):
172
- timeout = urllib3.Timeout(
173
- connect=_request_timeout[0], read=_request_timeout[1])
174
-
175
- try:
176
- # For `POST`, `PUT`, `PATCH`, `OPTIONS`, `DELETE`
177
- if method in ['POST', 'PUT', 'PATCH', 'OPTIONS', 'DELETE']:
178
-
179
- # no content type provided or payload is json
180
- if not headers.get('Content-Type') or re.search('json', headers['Content-Type'], re.IGNORECASE):
181
- request_body = None
182
- if body is not None:
183
- request_body = json.dumps(body)
184
- r = self.pool_manager.request(
185
- method, url,
186
- body=request_body,
187
- preload_content=_preload_content,
188
- timeout=timeout,
189
- headers=headers)
190
- elif headers['Content-Type'] == 'application/x-www-form-urlencoded': # noqa: E501
191
- r = self.pool_manager.request(
192
- method, url,
193
- fields=post_params,
194
- encode_multipart=False,
195
- preload_content=_preload_content,
196
- timeout=timeout,
197
- headers=headers)
198
- elif headers['Content-Type'] == 'multipart/form-data':
199
- # must del headers['Content-Type'], or the correct
200
- # Content-Type which generated by urllib3 will be
201
- # overwritten.
202
- del headers['Content-Type']
203
- r = self.pool_manager.request(
204
- method, url,
205
- fields=post_params,
206
- encode_multipart=True,
207
- preload_content=_preload_content,
208
- timeout=timeout,
209
- headers=headers)
210
- # Pass a `string` parameter directly in the body to support
211
- # other content types than Json when `body` argument is
212
- # provided in serialized form
213
- elif isinstance(body, str) or isinstance(body, bytes):
214
- request_body = body
215
- r = self.pool_manager.request(
216
- method, url,
217
- body=request_body,
218
- preload_content=_preload_content,
219
- timeout=timeout,
220
- headers=headers)
221
- else:
222
- # Cannot generate the request from given parameters
223
- msg = """Cannot prepare a request message for provided
224
- arguments. Please check that your arguments match
225
- declared content type."""
226
- raise ApiException(status=0, reason=msg)
227
- # For `GET`, `HEAD`
153
+ timeout = _request_timeout or 5 * 60
154
+
155
+ if 'Content-Type' not in headers:
156
+ headers['Content-Type'] = 'application/json'
157
+
158
+ args = {
159
+ "method": method,
160
+ "url": url,
161
+ "timeout": timeout,
162
+ "headers": headers
163
+ }
164
+
165
+ if self.proxy:
166
+ args["proxy"] = self.proxy
167
+ if self.proxy_headers:
168
+ args["proxy_headers"] = self.proxy_headers
169
+
170
+ # For `POST`, `PUT`, `PATCH`, `OPTIONS`, `DELETE`
171
+ if method in ['POST', 'PUT', 'PATCH', 'OPTIONS', 'DELETE']:
172
+ if re.search('json', headers['Content-Type'], re.IGNORECASE):
173
+ if body is not None:
174
+ body = json.dumps(body)
175
+ args["data"] = body
176
+ elif headers['Content-Type'] == 'application/x-www-form-urlencoded':
177
+ args["data"] = aiohttp.FormData(post_params)
178
+ elif headers['Content-Type'] == 'multipart/form-data':
179
+ # must del headers['Content-Type'], or the correct
180
+ # Content-Type which generated by aiohttp
181
+ del headers['Content-Type']
182
+ data = aiohttp.FormData()
183
+ for param in post_params:
184
+ k, v = param
185
+ if isinstance(v, tuple) and len(v) == 3:
186
+ data.add_field(
187
+ k,
188
+ value=v[1],
189
+ filename=v[0],
190
+ content_type=v[2]
191
+ )
192
+ else:
193
+ data.add_field(k, v)
194
+ args["data"] = data
195
+
196
+ # Pass a `bytes` parameter directly in the body to support
197
+ # other content types than Json when `body` argument is provided
198
+ # in serialized form
199
+ elif isinstance(body, bytes):
200
+ args["data"] = body
228
201
  else:
229
- r = self.pool_manager.request(method, url,
230
- fields={},
231
- preload_content=_preload_content,
232
- timeout=timeout,
233
- headers=headers)
234
- except urllib3.exceptions.SSLError as e:
235
- msg = "{0}\n{1}".format(type(e).__name__, str(e))
236
- raise ApiException(status=0, reason=msg)
237
-
238
- if _preload_content:
239
- r = RESTResponse(r)
240
-
241
- # log response body
242
- logger.debug("response body: %s", r.data)
243
-
244
- if not 200 <= r.status <= 299:
245
- if r.status == 400:
246
- raise BadRequestException(http_resp=r)
247
-
248
- if r.status == 401:
249
- raise UnauthorizedException(http_resp=r)
250
-
251
- if r.status == 403:
252
- raise ForbiddenException(http_resp=r)
253
-
254
- if r.status == 404:
255
- raise NotFoundException(http_resp=r)
256
-
257
- if 500 <= r.status <= 599:
258
- raise ServiceException(http_resp=r)
259
-
260
- raise ApiException(http_resp=r)
261
-
262
- return r
263
-
264
- def get_request(self, url, headers=None, query_params=None, _preload_content=True,
265
- _request_timeout=None):
266
- return self.request("GET", url,
267
- headers=headers,
268
- _preload_content=_preload_content,
269
- _request_timeout=_request_timeout,
270
- query_params=query_params)
271
-
272
- def head_request(self, url, headers=None, query_params=None, _preload_content=True,
273
- _request_timeout=None):
274
- return self.request("HEAD", url,
275
- headers=headers,
276
- _preload_content=_preload_content,
277
- _request_timeout=_request_timeout,
278
- query_params=query_params)
279
-
280
- def options_request(self, url, headers=None, query_params=None, post_params=None,
281
- body=None, _preload_content=True, _request_timeout=None):
282
- return self.request("OPTIONS", url,
283
- headers=headers,
284
- query_params=query_params,
285
- post_params=post_params,
286
- _preload_content=_preload_content,
287
- _request_timeout=_request_timeout,
288
- body=body)
289
-
290
- def delete_request(self, url, headers=None, query_params=None, body=None,
291
- _preload_content=True, _request_timeout=None):
292
- return self.request("DELETE", url,
293
- headers=headers,
294
- query_params=query_params,
295
- _preload_content=_preload_content,
296
- _request_timeout=_request_timeout,
297
- body=body)
298
-
299
- def post_request(self, url, headers=None, query_params=None, post_params=None,
300
- body=None, _preload_content=True, _request_timeout=None):
301
- return self.request("POST", url,
302
- headers=headers,
303
- query_params=query_params,
304
- post_params=post_params,
305
- _preload_content=_preload_content,
306
- _request_timeout=_request_timeout,
307
- body=body)
308
-
309
- def put_request(self, url, headers=None, query_params=None, post_params=None,
310
- body=None, _preload_content=True, _request_timeout=None):
311
- return self.request("PUT", url,
312
- headers=headers,
313
- query_params=query_params,
314
- post_params=post_params,
315
- _preload_content=_preload_content,
316
- _request_timeout=_request_timeout,
317
- body=body)
318
-
319
- def patch_request(self, url, headers=None, query_params=None, post_params=None,
320
- body=None, _preload_content=True, _request_timeout=None):
321
- return self.request("PATCH", url,
322
- headers=headers,
323
- query_params=query_params,
324
- post_params=post_params,
325
- _preload_content=_preload_content,
326
- _request_timeout=_request_timeout,
327
- body=body)
202
+ # Cannot generate the request from given parameters
203
+ msg = """Cannot prepare a request message for provided
204
+ arguments. Please check that your arguments match
205
+ declared content type."""
206
+ raise ApiException(status=0, reason=msg)
207
+
208
+ if self.retry_client is not None and method in ALLOW_RETRY_METHODS:
209
+ pool_manager = self.retry_client
210
+ else:
211
+ pool_manager = self.pool_manager
212
+
213
+ r = await pool_manager.request(**args)
214
+
215
+ return RESTResponse(r)
216
+
217
+
218
+
219
+
220
+
@@ -0,0 +1,104 @@
1
+ Metadata-Version: 2.4
2
+ Name: weheat
3
+ Version: 2025.11.24rc1
4
+ Summary: Weheat Backend client
5
+ Home-page: https://github.com/wefabricate/wh-python
6
+ Author: Jesper Raemaekers
7
+ Author-email: jesper.raemaekers@wefabricate.com
8
+ License: MIT
9
+ Keywords: OpenAPI,OpenAPI-Generator,Weheat Backend
10
+ Description-Content-Type: text/markdown
11
+ License-File: LICENSE
12
+ Requires-Dist: urllib3<3.0.0,>=1.25.3
13
+ Requires-Dist: python-dateutil
14
+ Requires-Dist: aiohttp>=3.0.0
15
+ Requires-Dist: aiohttp-retry>=2.8.3
16
+ Requires-Dist: pydantic>=2
17
+ Requires-Dist: typing-extensions>=4.7.1
18
+ Dynamic: author
19
+ Dynamic: author-email
20
+ Dynamic: description
21
+ Dynamic: description-content-type
22
+ Dynamic: home-page
23
+ Dynamic: keywords
24
+ Dynamic: license
25
+ Dynamic: license-file
26
+ Dynamic: requires-dist
27
+ Dynamic: summary
28
+
29
+ # Weheat backend client
30
+
31
+ This is a client for the Weheat backend. It is automatically generated from the OpenAPI specification.
32
+
33
+ ## Requirements.
34
+
35
+ Python 3.7+
36
+
37
+ ## Installation & Usage
38
+
39
+ You can install directly using:
40
+
41
+ ```sh
42
+ pip install weheat
43
+ ```
44
+
45
+ Then import the package:
46
+
47
+ ```python
48
+ import weheat
49
+ ```
50
+
51
+
52
+
53
+ ## Getting Started
54
+
55
+ After installation, you can now use the client to interact with the Weheat backend. Note that all methods are async as of version 2025.1.15.
56
+
57
+ ```python
58
+ import asyncio
59
+ from keycloak import KeycloakOpenID # install with pip install python-keycloak
60
+ from weheat.abstractions.heat_pump import HeatPump
61
+
62
+ # connection information
63
+ auth_url = 'https://auth.weheat.nl/auth/'
64
+ api_url = 'https://api.weheat.nl/third_party'
65
+ realm_name = 'Weheat'
66
+
67
+ # client ID and secret provided by Weheat
68
+ client_id = ''
69
+ client_secret = ''
70
+
71
+ # username and password used for the online portal
72
+ username = ''
73
+ password = ''
74
+
75
+ # your heat pump UUID
76
+ my_heat_pump_id = ''
77
+
78
+
79
+ async def demo():
80
+ # login into keycloak and get an access token
81
+ keycloak_open_id = KeycloakOpenID(server_url=auth_url,
82
+ client_id=client_id,
83
+ realm_name=realm_name,
84
+ client_secret_key=client_secret)
85
+
86
+ token_response = keycloak_open_id.token(username, password)
87
+ # The access token is valid for its lifetime even after logging out
88
+ keycloak_open_id.logout(token_response['refresh_token'])
89
+
90
+ # construct the heat pump object and fetch its data
91
+ hp = HeatPump(api_url=api_url, uuid=my_heat_pump_id)
92
+ await hp.async_get_status(token_response['access_token'])
93
+
94
+ # Print some of the information, look in the files for all available properties
95
+ print(f'Heat pump status: {hp.heat_pump_state}')
96
+ print(f'Heat pump RPM: {hp.compressor_percentage}%')
97
+ print(f'Total produced central heating energy: {hp.energy_out_heating} kWh')
98
+
99
+ asyncio.run(demo())
100
+
101
+ ```
102
+
103
+
104
+
@@ -0,0 +1,39 @@
1
+ weheat/__init__.py,sha256=VJOkGpBUsrl9m3maSaQZLZYoYTobqvf4wHjP1bfIl60,1711
2
+ weheat/api_client.py,sha256=cCxYMNmN2_FCf4NxUwMqmXNztxunYPzmBvoqqjXYWqQ,24816
3
+ weheat/api_response.py,sha256=A7O_XgliD6y7jEv82fgIaxR3T8KiwaOqHR6djpZyh_o,674
4
+ weheat/configuration.py,sha256=p1dbZ6a_bQPCqpUBQKwqSpQHE1a8CT9uixMSWxvtDUc,14534
5
+ weheat/exceptions.py,sha256=jZjLtEMBKFpLTdV1GPsbQSPriG1ilgMSodGnhEKlWh4,5913
6
+ weheat/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
+ weheat/rest.py,sha256=hSt8T6NQfdrgz_7Dxo9U-DO5ZEgUVVG38OUErThWqlU,7086
8
+ weheat/abstractions/__init__.py,sha256=BbA5WW8RE8vtpK0Dq80ayMILN4m6lmCyIPYzYXy3rTE,177
9
+ weheat/abstractions/discovery.py,sha256=U6OtacaHES82t-XMVz8-lxRCwXrSgpqT2DciA7_9Lcc,2405
10
+ weheat/abstractions/heat_pump.py,sha256=NdwzqSiIuYNPRemaG0ju1-ByyZcK7_ZMW-dPjyo-xVU,14686
11
+ weheat/abstractions/user.py,sha256=moeJAmbjdYuXAkL7Xuks5w4zcPpjKUXM9nYJDv8WEXs,775
12
+ weheat/api/__init__.py,sha256=zZ_Xqek8VY6gARsJK6hRess0qqGii-Ls1uXm92k0jPE,244
13
+ weheat/api/energy_log_api.py,sha256=wCykXw1pu4LMOfzw6f6nhw4ih2uaRUBbQFampI0uLWo,31893
14
+ weheat/api/heat_pump_api.py,sha256=aPI2DwQWjaFLrF6ZCR-VGrNW3Hq41GMEeYsHKHQb7aY,33222
15
+ weheat/api/heat_pump_log_api.py,sha256=K3g7N5TwWcarRMNtYiqotatRj63GSttOFxA_p7cdNbM,50618
16
+ weheat/api/user_api.py,sha256=uRl-3C7A6KnH9tbtgBh4UK8YCyQoS4GZUyajxcog-ho,13975
17
+ weheat/models/__init__.py,sha256=x7dozSdn1nIuE_PWL35xf1uos8ZC65FxX1bkhIzbeeU,1294
18
+ weheat/models/boiler_type.py,sha256=ebfHlTCNRns2c85imgwWBB5whZxeQ0jgiZYM98dSjB4,1011
19
+ weheat/models/device_state.py,sha256=PvNOiiwVsSqUV4PunGEMidDhfkUfWdbNFeU7vFv-fHU,1115
20
+ weheat/models/dhw_type.py,sha256=J-_bh0JZnbOG5o6_pn7oXctmZh0wIs4bnqJrNlF_lc0,928
21
+ weheat/models/energy_view_dto.py,sha256=zIqpPTjpR7VW-5dRUFcRYdbfK567vuczPXUZWd6SRJw,9721
22
+ weheat/models/heat_pump_log_view_dto.py,sha256=ejCtArRzpMlihNO9dR_uemp8lkPN-24AzWCdzSoZjz4,132034
23
+ weheat/models/heat_pump_model.py,sha256=9rPJuJ-xLxJyqdAdj1EfJric4EqMof6ICOeZSsLoSlo,1178
24
+ weheat/models/heat_pump_status_enum.py,sha256=3qoyKdeYZ8VPqRU47nkVfO1rPYf9r3jPleTFHKGTzRc,1106
25
+ weheat/models/pagination_metadata.py,sha256=9LJIsxYFs6PXtNtZIVaFgPUir3JJk06MhNlAcebNYAQ,3042
26
+ weheat/models/raw_heat_pump_log_dto.py,sha256=29T3nq9OGbAQZYBy4TVAkbAaT89O2jUlqM8uKk7caBo,39374
27
+ weheat/models/raw_heatpump_log_and_is_online_dto.py,sha256=F2WUygGCXUQmgRdy8gpHNeNP_ggdQbJ5vKz3cVpGf1E,39557
28
+ weheat/models/read_all_heat_pump_dto.py,sha256=tSMVLnFjoxjYxr8ZfJPRp2XyyDP_Tekg1d_kiZFoOzM,5531
29
+ weheat/models/read_all_heat_pump_dto_paged_response.py,sha256=7wAENqeJm-HMsvd62rXEfj8r41ApZF48lHx4A-YjKbA,3497
30
+ weheat/models/read_heat_pump_dto.py,sha256=HAXp2lbD4beDTPZ4hEOrsJSiw7gOMp-X4miklMX9KMw,4972
31
+ weheat/models/read_user_dto.py,sha256=OIsWQZcdByN94ViSv0DjFHORRsMnkQ93jc-gJuudRdg,4018
32
+ weheat/models/read_user_me_dto.py,sha256=Ger6qKlbZBBvG_4MStl6aEMkrBBJIjQtV6pvK1-lw28,4441
33
+ weheat/models/role.py,sha256=6KUInAmkhoxEdWxBo9jBHXiy_nqJHrKXVeh8RvQPEYM,1101
34
+ weheat/models/total_energy_aggregate.py,sha256=TF08TQrk5ZKeO0916cdO_XOi0DvRUqpf1ZScWitOYpY,4663
35
+ weheat-2025.11.24rc1.dist-info/licenses/LICENSE,sha256=rWmFUq0uth2jpet-RQ2QPd2VhZkcPSUs6Dxfmbqkbis,1068
36
+ weheat-2025.11.24rc1.dist-info/METADATA,sha256=YHMTxjsc_49WNXY8Lr0DXzNan0Qt2tX4GiVL6WS6td4,2712
37
+ weheat-2025.11.24rc1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
38
+ weheat-2025.11.24rc1.dist-info/top_level.txt,sha256=hLzdyvGZ9rs4AqK7U48mdHx_-FcP5sDuTSleDUvGAZw,7
39
+ weheat-2025.11.24rc1.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (75.3.0)
2
+ Generator: setuptools (80.9.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,34 +0,0 @@
1
- from abc import ABC, abstractmethod
2
- from aiohttp import ClientSession, ClientResponse
3
-
4
-
5
- class AbstractAuth(ABC):
6
- """Abstract class to make authenticated requests."""
7
-
8
- def __init__(self, websession: ClientSession, host: str) -> None:
9
- """Initialize the auth."""
10
- self.websession = websession
11
- self.host = host
12
-
13
- @abstractmethod
14
- async def async_get_access_token(self) -> str:
15
- """Return a valid access token."""
16
-
17
- async def request(self, method, url, **kwargs) -> ClientResponse:
18
- """Make a request."""
19
- headers = kwargs.get("headers")
20
-
21
- if headers is None:
22
- headers = {}
23
- else:
24
- headers = dict(headers)
25
-
26
- access_token = await self.async_get_access_token()
27
- headers["authorization"] = f"Bearer {access_token}"
28
-
29
- return await self.websession.request(
30
- method,
31
- f"{self.host}/{url}",
32
- **kwargs,
33
- headers=headers,
34
- )