weheat 2025.1.14rc1__py3-none-any.whl → 2025.1.15rc1__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 +2 -4
- weheat/abstractions/heat_pump.py +6 -13
- weheat/abstractions/user.py +3 -5
- 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 +4 -8
- 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 +142 -257
- weheat-2025.1.15rc1.dist-info/METADATA +115 -0
- weheat-2025.1.15rc1.dist-info/RECORD +37 -0
- weheat-2025.1.14rc1.dist-info/METADATA +0 -117
- weheat-2025.1.14rc1.dist-info/RECORD +0 -36
- {weheat-2025.1.14rc1.dist-info → weheat-2025.1.15rc1.dist-info}/LICENSE +0 -0
- {weheat-2025.1.14rc1.dist-info → weheat-2025.1.15rc1.dist-info}/WHEEL +0 -0
- {weheat-2025.1.14rc1.dist-info → weheat-2025.1.15rc1.dist-info}/top_level.txt +0 -0
weheat/rest.py
CHANGED
|
@@ -14,143 +14,125 @@
|
|
|
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
|
|
53
|
+
def __init__(self, configuration) -> None:
|
|
67
54
|
|
|
68
|
-
#
|
|
69
|
-
|
|
70
|
-
cert_reqs = ssl.CERT_REQUIRED
|
|
71
|
-
else:
|
|
72
|
-
cert_reqs = ssl.CERT_NONE
|
|
55
|
+
# maxsize is number of requests to host that are allowed in parallel
|
|
56
|
+
maxsize = configuration.connection_pool_maxsize
|
|
73
57
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
58
|
+
ssl_context = ssl.create_default_context(
|
|
59
|
+
cafile=configuration.ssl_ca_cert
|
|
60
|
+
)
|
|
61
|
+
if configuration.cert_file:
|
|
62
|
+
ssl_context.load_cert_chain(
|
|
63
|
+
configuration.cert_file, keyfile=configuration.key_file
|
|
64
|
+
)
|
|
80
65
|
|
|
81
|
-
if configuration.
|
|
82
|
-
|
|
66
|
+
if not configuration.verify_ssl:
|
|
67
|
+
ssl_context.check_hostname = False
|
|
68
|
+
ssl_context.verify_mode = ssl.CERT_NONE
|
|
83
69
|
|
|
70
|
+
connector = aiohttp.TCPConnector(
|
|
71
|
+
limit=maxsize,
|
|
72
|
+
ssl=ssl_context
|
|
73
|
+
)
|
|
84
74
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
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
|
|
75
|
+
self.proxy = configuration.proxy
|
|
76
|
+
self.proxy_headers = configuration.proxy_headers
|
|
93
77
|
|
|
94
78
|
# https pool manager
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
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
|
|
79
|
+
self.pool_manager = aiohttp.ClientSession(
|
|
80
|
+
connector=connector,
|
|
81
|
+
trust_env=True
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
retries = configuration.retries
|
|
85
|
+
if retries is not None:
|
|
86
|
+
self.retry_client = aiohttp_retry.RetryClient(
|
|
87
|
+
client_session=self.pool_manager,
|
|
88
|
+
retry_options=aiohttp_retry.ExponentialRetry(
|
|
89
|
+
attempts=retries,
|
|
90
|
+
factor=0.0,
|
|
91
|
+
start_timeout=0.0,
|
|
92
|
+
max_timeout=120.0
|
|
118
93
|
)
|
|
119
|
-
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
|
|
128
94
|
)
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
95
|
+
else:
|
|
96
|
+
self.retry_client = None
|
|
97
|
+
|
|
98
|
+
async def close(self):
|
|
99
|
+
await self.pool_manager.close()
|
|
100
|
+
if self.retry_client is not None:
|
|
101
|
+
await self.retry_client.close()
|
|
102
|
+
|
|
103
|
+
async def request(
|
|
104
|
+
self,
|
|
105
|
+
method,
|
|
106
|
+
url,
|
|
107
|
+
headers=None,
|
|
108
|
+
body=None,
|
|
109
|
+
post_params=None,
|
|
110
|
+
_request_timeout=None
|
|
111
|
+
):
|
|
112
|
+
"""Execute request
|
|
134
113
|
|
|
135
114
|
:param method: http request method
|
|
136
115
|
:param url: http request url
|
|
137
|
-
:param query_params: query parameters in the url
|
|
138
116
|
:param headers: http request headers
|
|
139
117
|
:param body: request json body, for `application/json`
|
|
140
118
|
:param post_params: request post parameters,
|
|
141
119
|
`application/x-www-form-urlencoded`
|
|
142
120
|
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
121
|
:param _request_timeout: timeout setting for this request. If one
|
|
147
122
|
number provided, it will be total request
|
|
148
123
|
timeout. It can also be a pair (tuple) of
|
|
149
124
|
(connection, read) timeouts.
|
|
150
125
|
"""
|
|
151
126
|
method = method.upper()
|
|
152
|
-
assert method in [
|
|
153
|
-
|
|
127
|
+
assert method in [
|
|
128
|
+
'GET',
|
|
129
|
+
'HEAD',
|
|
130
|
+
'DELETE',
|
|
131
|
+
'POST',
|
|
132
|
+
'PUT',
|
|
133
|
+
'PATCH',
|
|
134
|
+
'OPTIONS'
|
|
135
|
+
]
|
|
154
136
|
|
|
155
137
|
if post_params and body:
|
|
156
138
|
raise ApiValueError(
|
|
@@ -160,168 +142,71 @@ class RESTClientObject:
|
|
|
160
142
|
post_params = post_params or {}
|
|
161
143
|
headers = headers or {}
|
|
162
144
|
# 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`
|
|
145
|
+
timeout = _request_timeout or 5 * 60
|
|
146
|
+
|
|
147
|
+
if 'Content-Type' not in headers:
|
|
148
|
+
headers['Content-Type'] = 'application/json'
|
|
149
|
+
|
|
150
|
+
args = {
|
|
151
|
+
"method": method,
|
|
152
|
+
"url": url,
|
|
153
|
+
"timeout": timeout,
|
|
154
|
+
"headers": headers
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
if self.proxy:
|
|
158
|
+
args["proxy"] = self.proxy
|
|
159
|
+
if self.proxy_headers:
|
|
160
|
+
args["proxy_headers"] = self.proxy_headers
|
|
161
|
+
|
|
162
|
+
# For `POST`, `PUT`, `PATCH`, `OPTIONS`, `DELETE`
|
|
163
|
+
if method in ['POST', 'PUT', 'PATCH', 'OPTIONS', 'DELETE']:
|
|
164
|
+
if re.search('json', headers['Content-Type'], re.IGNORECASE):
|
|
165
|
+
if body is not None:
|
|
166
|
+
body = json.dumps(body)
|
|
167
|
+
args["data"] = body
|
|
168
|
+
elif headers['Content-Type'] == 'application/x-www-form-urlencoded':
|
|
169
|
+
args["data"] = aiohttp.FormData(post_params)
|
|
170
|
+
elif headers['Content-Type'] == 'multipart/form-data':
|
|
171
|
+
# must del headers['Content-Type'], or the correct
|
|
172
|
+
# Content-Type which generated by aiohttp
|
|
173
|
+
del headers['Content-Type']
|
|
174
|
+
data = aiohttp.FormData()
|
|
175
|
+
for param in post_params:
|
|
176
|
+
k, v = param
|
|
177
|
+
if isinstance(v, tuple) and len(v) == 3:
|
|
178
|
+
data.add_field(
|
|
179
|
+
k,
|
|
180
|
+
value=v[1],
|
|
181
|
+
filename=v[0],
|
|
182
|
+
content_type=v[2]
|
|
183
|
+
)
|
|
184
|
+
else:
|
|
185
|
+
data.add_field(k, v)
|
|
186
|
+
args["data"] = data
|
|
187
|
+
|
|
188
|
+
# Pass a `bytes` parameter directly in the body to support
|
|
189
|
+
# other content types than Json when `body` argument is provided
|
|
190
|
+
# in serialized form
|
|
191
|
+
elif isinstance(body, bytes):
|
|
192
|
+
args["data"] = body
|
|
228
193
|
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)
|
|
194
|
+
# Cannot generate the request from given parameters
|
|
195
|
+
msg = """Cannot prepare a request message for provided
|
|
196
|
+
arguments. Please check that your arguments match
|
|
197
|
+
declared content type."""
|
|
198
|
+
raise ApiException(status=0, reason=msg)
|
|
199
|
+
|
|
200
|
+
if self.retry_client is not None and method in ALLOW_RETRY_METHODS:
|
|
201
|
+
pool_manager = self.retry_client
|
|
202
|
+
else:
|
|
203
|
+
pool_manager = self.pool_manager
|
|
204
|
+
|
|
205
|
+
r = await pool_manager.request(**args)
|
|
206
|
+
|
|
207
|
+
return RESTResponse(r)
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
|
+
Name: weheat
|
|
3
|
+
Version: 2025.1.15rc1
|
|
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=bSjeNWmg5SuxyULRbUR9KCuPK9yHSYCbrZlIbY6UiAo,14278
|
|
5
|
+
weheat/exceptions.py,sha256=jZjLtEMBKFpLTdV1GPsbQSPriG1ilgMSodGnhEKlWh4,5913
|
|
6
|
+
weheat/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
7
|
+
weheat/rest.py,sha256=r0ot19pStSTb4Nwco98UeSsppIQAR8FxQecABO4y9aY,6598
|
|
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=Z1_tLFJzZkbA8CYIs2KpxoMB7S6Xc-V8jRk-LQeX1UE,2287
|
|
11
|
+
weheat/abstractions/heat_pump.py,sha256=MwOXkUSJaq820clvM-iYaeMmYn4YfR7k6ML3pPR5nbY,9787
|
|
12
|
+
weheat/abstractions/user.py,sha256=gQ5hSthG7c434lqsliI91uBOIkUQ8uTdRmCV-e7Y_Gk,663
|
|
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.15rc1.dist-info/LICENSE,sha256=rWmFUq0uth2jpet-RQ2QPd2VhZkcPSUs6Dxfmbqkbis,1068
|
|
34
|
+
weheat-2025.1.15rc1.dist-info/METADATA,sha256=6-x-xTzWRV5bFUC4lUNjN1PULmzeH7IpHtnsHu3F8cM,3877
|
|
35
|
+
weheat-2025.1.15rc1.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
|
36
|
+
weheat-2025.1.15rc1.dist-info/top_level.txt,sha256=hLzdyvGZ9rs4AqK7U48mdHx_-FcP5sDuTSleDUvGAZw,7
|
|
37
|
+
weheat-2025.1.15rc1.dist-info/RECORD,,
|