weheat 2025.1.14__py3-none-any.whl → 2025.1.15__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.
Potentially problematic release.
This version of weheat might be problematic. Click here for more details.
- weheat/__init__.py +7 -2
- weheat/abstractions/discovery.py +6 -6
- weheat/abstractions/heat_pump.py +11 -15
- weheat/abstractions/user.py +7 -7
- weheat/api/__init__.py +1 -0
- weheat/api/energy_log_api.py +306 -132
- weheat/api/heat_pump_api.py +521 -369
- weheat/api/heat_pump_log_api.py +836 -359
- weheat/api/user_api.py +243 -115
- weheat/api_client.py +234 -261
- weheat/api_response.py +10 -18
- weheat/configuration.py +14 -9
- weheat/exceptions.py +59 -25
- weheat/models/__init__.py +6 -0
- weheat/models/boiler_type.py +8 -3
- weheat/models/device_state.py +9 -4
- weheat/models/dhw_type.py +8 -3
- weheat/models/energy_view_dto.py +81 -66
- weheat/models/heat_pump_log_view_dto.py +527 -481
- weheat/models/heat_pump_model.py +8 -3
- weheat/models/heat_pump_status_enum.py +8 -3
- weheat/models/heat_pump_type.py +8 -3
- weheat/models/raw_heat_pump_log_dto.py +353 -315
- weheat/models/read_all_heat_pump_dto.py +64 -48
- weheat/models/read_heat_pump_dto.py +59 -43
- weheat/models/read_user_dto.py +54 -39
- weheat/models/read_user_me_dto.py +124 -0
- weheat/models/role.py +10 -4
- weheat/rest.py +152 -259
- weheat-2025.1.15.dist-info/METADATA +115 -0
- weheat-2025.1.15.dist-info/RECORD +37 -0
- weheat-2025.1.14.dist-info/METADATA +0 -117
- weheat-2025.1.14.dist-info/RECORD +0 -36
- {weheat-2025.1.14.dist-info → weheat-2025.1.15.dist-info}/LICENSE +0 -0
- {weheat-2025.1.14.dist-info → weheat-2025.1.15.dist-info}/WHEEL +0 -0
- {weheat-2025.1.14.dist-info → weheat-2025.1.15.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
|
-
|
|
22
|
-
import
|
|
20
|
+
import aiohttp
|
|
21
|
+
import aiohttp_retry
|
|
23
22
|
|
|
24
|
-
from weheat.exceptions import ApiException,
|
|
23
|
+
from weheat.exceptions import ApiException, ApiValueError
|
|
25
24
|
|
|
25
|
+
RESTResponseType = aiohttp.ClientResponse
|
|
26
26
|
|
|
27
|
-
|
|
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.
|
|
32
|
+
self.response = resp
|
|
46
33
|
self.status = resp.status
|
|
47
34
|
self.reason = resp.reason
|
|
48
|
-
self.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
|
|
52
|
-
return self.
|
|
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.
|
|
48
|
+
return self.response.headers.get(name, default)
|
|
57
49
|
|
|
58
50
|
|
|
59
51
|
class RESTClientObject:
|
|
60
52
|
|
|
61
|
-
def __init__(self, configuration
|
|
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
|
-
|
|
75
|
-
|
|
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
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
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
|
-
|
|
86
|
-
|
|
73
|
+
connector = aiohttp.TCPConnector(
|
|
74
|
+
limit=maxsize,
|
|
75
|
+
ssl=ssl_context
|
|
76
|
+
)
|
|
87
77
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
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 =
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
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
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
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 [
|
|
153
|
-
|
|
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
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
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
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
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,115 @@
|
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
|
+
Name: weheat
|
|
3
|
+
Version: 2025.1.15
|
|
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<2.1.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: requires-dist
|
|
26
|
+
Dynamic: summary
|
|
27
|
+
|
|
28
|
+
# Weheat backend client
|
|
29
|
+
|
|
30
|
+
This is a client for the Weheat backend. It is automatically generated from the OpenAPI specification.
|
|
31
|
+
|
|
32
|
+
## Requirements.
|
|
33
|
+
|
|
34
|
+
Python 3.7+
|
|
35
|
+
|
|
36
|
+
## Installation & Usage
|
|
37
|
+
|
|
38
|
+
You can install directly using:
|
|
39
|
+
|
|
40
|
+
```sh
|
|
41
|
+
pip install weheat
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Then import the package:
|
|
45
|
+
|
|
46
|
+
```python
|
|
47
|
+
import weheat
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
## Getting Started
|
|
53
|
+
|
|
54
|
+
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.
|
|
55
|
+
|
|
56
|
+
```python
|
|
57
|
+
import asyncio
|
|
58
|
+
import datetime
|
|
59
|
+
from keycloak import KeycloakOpenID # install with pip install python-keycloak
|
|
60
|
+
from weheat import ApiClient, Configuration, HeatPumpApi, HeatPumpLogApi, EnergyLogApi, UserApi
|
|
61
|
+
|
|
62
|
+
auth_url = 'https://auth.weheat.nl/auth/'
|
|
63
|
+
api_url = 'https://api.weheat.nl'
|
|
64
|
+
realm_name = 'Weheat'
|
|
65
|
+
my_client_id = 'WeheatCommunityAPI' # client ID and secret provided by Weheat
|
|
66
|
+
my_client_secret = ''
|
|
67
|
+
username = '' # username and password used for the online portal
|
|
68
|
+
password = ''
|
|
69
|
+
my_heat_pump_id = '' # your heat pump UUID
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
async def demo():
|
|
73
|
+
keycloak_open_id = KeycloakOpenID(server_url=auth_url,
|
|
74
|
+
client_id=my_client_id,
|
|
75
|
+
realm_name=realm_name,
|
|
76
|
+
client_secret_key=my_client_secret)
|
|
77
|
+
|
|
78
|
+
token_response = keycloak_open_id.token(username, password)
|
|
79
|
+
keycloak_open_id.logout(token_response['refresh_token'])
|
|
80
|
+
|
|
81
|
+
config = Configuration(host=api_url, access_token=token_response['access_token'])
|
|
82
|
+
async with ApiClient(configuration=config) as client:
|
|
83
|
+
response = await UserApi(client).api_v1_users_me_get_with_http_info()
|
|
84
|
+
|
|
85
|
+
if response.status_code == 200:
|
|
86
|
+
print(f'My user: {response.data}')
|
|
87
|
+
|
|
88
|
+
response = await HeatPumpApi(client).api_v1_heat_pumps_get_with_http_info()
|
|
89
|
+
|
|
90
|
+
if response.status_code == 200:
|
|
91
|
+
print(f'My heat pump: {response.data}')
|
|
92
|
+
|
|
93
|
+
response = await HeatPumpLogApi(client).api_v1_heat_pumps_heat_pump_id_logs_latest_get_with_http_info(
|
|
94
|
+
heat_pump_id=my_heat_pump_id)
|
|
95
|
+
|
|
96
|
+
if response.status_code == 200:
|
|
97
|
+
print(f'My heat pump logs: {response.data}')
|
|
98
|
+
|
|
99
|
+
response = await EnergyLogApi(client).api_v1_energy_logs_heat_pump_id_get_with_http_info(heat_pump_id=my_heat_pump_id,
|
|
100
|
+
start_time=datetime.datetime(2024, 6,
|
|
101
|
+
22, 0, 0,
|
|
102
|
+
0),
|
|
103
|
+
end_time=datetime.datetime(2024, 6, 22,
|
|
104
|
+
15, 0, 0),
|
|
105
|
+
interval='Hour')
|
|
106
|
+
|
|
107
|
+
if response.status_code == 200:
|
|
108
|
+
print(f'My energy logs: {response.data}')
|
|
109
|
+
|
|
110
|
+
asyncio.get_event_loop().run_until_complete(demo())
|
|
111
|
+
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
weheat/__init__.py,sha256=C24j_uAbbyEzqh8DmQyORUrVMdFDlzEv4jGClf4TY34,1765
|
|
2
|
+
weheat/api_client.py,sha256=Bh5xHchRGCcsHbT3rXrcmhKCMiumi1_2L6n0Q57KODw,24803
|
|
3
|
+
weheat/api_response.py,sha256=A7O_XgliD6y7jEv82fgIaxR3T8KiwaOqHR6djpZyh_o,674
|
|
4
|
+
weheat/configuration.py,sha256=8D0zAgx7B6YmVNuyS0AarcCAl5TmKKH655jHqWrvSl0,14531
|
|
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=cRdA_kyTIooo39I13_mqShSfZMqdzNGHbmrnITqgx6A,161
|
|
9
|
+
weheat/abstractions/auth.py,sha256=VCAxJ4OIj7bsYttqJl5-juU0VUlSd3xPu7kUjtHZr3U,979
|
|
10
|
+
weheat/abstractions/discovery.py,sha256=PrhyM29OKvCgKzWig5BAjaEF15CIcTp_AIBZg2lyJ6Y,2384
|
|
11
|
+
weheat/abstractions/heat_pump.py,sha256=DFgKF_4Mdv4dOVugECy9xB4y6e85B-HwZatrUBSnhPE,9920
|
|
12
|
+
weheat/abstractions/user.py,sha256=L48CGvBe7YrUa-wnIgmv9Hu0DOMN4IXN0fSmufrlJIc,760
|
|
13
|
+
weheat/api/__init__.py,sha256=zZ_Xqek8VY6gARsJK6hRess0qqGii-Ls1uXm92k0jPE,244
|
|
14
|
+
weheat/api/energy_log_api.py,sha256=plZ9YoQ9O39qufzr3B6CduuzJMY1K6iJS5f5AXN-vUo,17587
|
|
15
|
+
weheat/api/heat_pump_api.py,sha256=tyFA-Xf-Ld4cAVSYblCq1s5I1CDRQmKJx9UBJivnS0Y,28211
|
|
16
|
+
weheat/api/heat_pump_log_api.py,sha256=mz3KuVuNv0e1zadTpGYCnXtrCMMHIV6xjQ6TfZCib-s,45295
|
|
17
|
+
weheat/api/user_api.py,sha256=r08h_5HTwfY7M5eyf9FJs4VTSxIQCafqY-zYR78KTM4,12438
|
|
18
|
+
weheat/models/__init__.py,sha256=oG2r1nESdcGRBOBqEOLDCLUWCUs6KY2aePwjfQFd5H8,1092
|
|
19
|
+
weheat/models/boiler_type.py,sha256=ebfHlTCNRns2c85imgwWBB5whZxeQ0jgiZYM98dSjB4,1011
|
|
20
|
+
weheat/models/device_state.py,sha256=rM99B_K1dEbn6Wk610MbvROBl5Dv4YJ2mKLjIzEZXn8,1151
|
|
21
|
+
weheat/models/dhw_type.py,sha256=J-_bh0JZnbOG5o6_pn7oXctmZh0wIs4bnqJrNlF_lc0,928
|
|
22
|
+
weheat/models/energy_view_dto.py,sha256=pjQp9PSbJ32NvOe5bgPOdshDeCd-CZiWquuxMdVCC5Y,9139
|
|
23
|
+
weheat/models/heat_pump_log_view_dto.py,sha256=liughLpkpeTY3AoqTWNOk-zZXMhSn0gLm5c88apCvZE,65838
|
|
24
|
+
weheat/models/heat_pump_model.py,sha256=9rPJuJ-xLxJyqdAdj1EfJric4EqMof6ICOeZSsLoSlo,1178
|
|
25
|
+
weheat/models/heat_pump_status_enum.py,sha256=HxgJJgOWnP_aVHMUWEHwEOxP5LMxJTt7hHhzd9KwCMg,1106
|
|
26
|
+
weheat/models/heat_pump_type.py,sha256=WnOTgYf-NXlKNSsQwri9CThSYoW5Q1CK_jlhPMKKW4c,919
|
|
27
|
+
weheat/models/raw_heat_pump_log_dto.py,sha256=7IEV_l0sgPZrBTgPP-jt0HrXPOTGxDyKWuk7qC5shHw,38697
|
|
28
|
+
weheat/models/read_all_heat_pump_dto.py,sha256=44vte5hTUysrT7gkBFNYxz09TWSsuH10KP4-w03ax4M,5670
|
|
29
|
+
weheat/models/read_heat_pump_dto.py,sha256=7jH2MwMVKv_dOoJUUsKE8sTR5mROqco71OzOAFjgWTY,5111
|
|
30
|
+
weheat/models/read_user_dto.py,sha256=OIsWQZcdByN94ViSv0DjFHORRsMnkQ93jc-gJuudRdg,4018
|
|
31
|
+
weheat/models/read_user_me_dto.py,sha256=Ger6qKlbZBBvG_4MStl6aEMkrBBJIjQtV6pvK1-lw28,4441
|
|
32
|
+
weheat/models/role.py,sha256=6KUInAmkhoxEdWxBo9jBHXiy_nqJHrKXVeh8RvQPEYM,1101
|
|
33
|
+
weheat-2025.1.15.dist-info/LICENSE,sha256=rWmFUq0uth2jpet-RQ2QPd2VhZkcPSUs6Dxfmbqkbis,1068
|
|
34
|
+
weheat-2025.1.15.dist-info/METADATA,sha256=uXTKgpecpXb3GrH0QzN_ktTloByEMScxyP2huHnTGqY,3874
|
|
35
|
+
weheat-2025.1.15.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
|
36
|
+
weheat-2025.1.15.dist-info/top_level.txt,sha256=hLzdyvGZ9rs4AqK7U48mdHx_-FcP5sDuTSleDUvGAZw,7
|
|
37
|
+
weheat-2025.1.15.dist-info/RECORD,,
|