anycubic-cloud-api 0.1.0__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.
- anycubic_cloud_api/__init__.py +62 -0
- anycubic_cloud_api/anycubic_api.py +7 -0
- anycubic_cloud_api/api/base.py +400 -0
- anycubic_cloud_api/api/functions.py +2055 -0
- anycubic_cloud_api/api/mqtt.py +449 -0
- anycubic_cloud_api/const/api_endpoints.py +106 -0
- anycubic_cloud_api/const/const.py +47 -0
- anycubic_cloud_api/const/enums.py +97 -0
- anycubic_cloud_api/const/mqtt.py +13 -0
- anycubic_cloud_api/data_models/consumable.py +116 -0
- anycubic_cloud_api/data_models/files.py +429 -0
- anycubic_cloud_api/data_models/gcode_file.py +107 -0
- anycubic_cloud_api/data_models/orders.py +369 -0
- anycubic_cloud_api/data_models/print_response.py +71 -0
- anycubic_cloud_api/data_models/print_speed_mode.py +61 -0
- anycubic_cloud_api/data_models/printer.py +2753 -0
- anycubic_cloud_api/data_models/printer_properties.py +1107 -0
- anycubic_cloud_api/data_models/printing_settings.py +130 -0
- anycubic_cloud_api/data_models/project.py +1234 -0
- anycubic_cloud_api/exceptions/error_strings.py +340 -0
- anycubic_cloud_api/exceptions/exceptions.py +91 -0
- anycubic_cloud_api/helpers/helpers.py +240 -0
- anycubic_cloud_api/models/auth.py +396 -0
- anycubic_cloud_api/models/cloud_upload.py +249 -0
- anycubic_cloud_api/models/http.py +29 -0
- anycubic_cloud_api/resources/anycubic_mqqt_tls_ca.crt +24 -0
- anycubic_cloud_api/resources/anycubic_mqqt_tls_client.crt +24 -0
- anycubic_cloud_api/resources/anycubic_mqqt_tls_client.key +28 -0
- anycubic_cloud_api-0.1.0.dist-info/METADATA +79 -0
- anycubic_cloud_api-0.1.0.dist-info/RECORD +33 -0
- anycubic_cloud_api-0.1.0.dist-info/WHEEL +5 -0
- anycubic_cloud_api-0.1.0.dist-info/licenses/LICENSE +674 -0
- anycubic_cloud_api-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"""Async client for the Anycubic Cloud API and its MQTT stream.
|
|
2
|
+
|
|
3
|
+
Extracted from the Home Assistant `anycubic_cloud` integration so the client
|
|
4
|
+
can be versioned, tested and consumed independently.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from .anycubic_api import AnycubicAPI, AnycubicMQTTAPI
|
|
8
|
+
from .const.enums import (
|
|
9
|
+
AnycubicFeedType,
|
|
10
|
+
AnycubicFunctionID,
|
|
11
|
+
AnycubicOrderID,
|
|
12
|
+
AnycubicPrinterMaterialType,
|
|
13
|
+
AnycubicPrintStatus,
|
|
14
|
+
)
|
|
15
|
+
from .data_models.printer import AnycubicPrinter
|
|
16
|
+
from .data_models.project import AnycubicProject
|
|
17
|
+
from .exceptions.exceptions import (
|
|
18
|
+
AnycubicAPIError,
|
|
19
|
+
AnycubicAPIParsingError,
|
|
20
|
+
AnycubicAuthError,
|
|
21
|
+
AnycubicAuthTokensExpired,
|
|
22
|
+
AnycubicCloudUploadError,
|
|
23
|
+
AnycubicDataParsingError,
|
|
24
|
+
AnycubicFileNotFoundError,
|
|
25
|
+
AnycubicGcodeParsingError,
|
|
26
|
+
AnycubicInvalidValue,
|
|
27
|
+
AnycubicMQTTClientError,
|
|
28
|
+
AnycubicMQTTUnhandledData,
|
|
29
|
+
AnycubicMQTTUnknownUpdate,
|
|
30
|
+
AnycubicPropertiesNotLoaded,
|
|
31
|
+
)
|
|
32
|
+
from .models.auth import AnycubicAuthentication, AnycubicAuthMode
|
|
33
|
+
|
|
34
|
+
__version__ = "0.1.0"
|
|
35
|
+
|
|
36
|
+
__all__ = [
|
|
37
|
+
"AnycubicAPI",
|
|
38
|
+
"AnycubicAPIError",
|
|
39
|
+
"AnycubicAPIParsingError",
|
|
40
|
+
"AnycubicAuthError",
|
|
41
|
+
"AnycubicAuthMode",
|
|
42
|
+
"AnycubicAuthTokensExpired",
|
|
43
|
+
"AnycubicAuthentication",
|
|
44
|
+
"AnycubicCloudUploadError",
|
|
45
|
+
"AnycubicDataParsingError",
|
|
46
|
+
"AnycubicFeedType",
|
|
47
|
+
"AnycubicFileNotFoundError",
|
|
48
|
+
"AnycubicFunctionID",
|
|
49
|
+
"AnycubicGcodeParsingError",
|
|
50
|
+
"AnycubicInvalidValue",
|
|
51
|
+
"AnycubicMQTTAPI",
|
|
52
|
+
"AnycubicMQTTClientError",
|
|
53
|
+
"AnycubicMQTTUnhandledData",
|
|
54
|
+
"AnycubicMQTTUnknownUpdate",
|
|
55
|
+
"AnycubicOrderID",
|
|
56
|
+
"AnycubicPrintStatus",
|
|
57
|
+
"AnycubicPrinter",
|
|
58
|
+
"AnycubicPrinterMaterialType",
|
|
59
|
+
"AnycubicProject",
|
|
60
|
+
"AnycubicPropertiesNotLoaded",
|
|
61
|
+
"__version__",
|
|
62
|
+
]
|
|
@@ -0,0 +1,400 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import json
|
|
5
|
+
import time
|
|
6
|
+
from typing import Any, overload
|
|
7
|
+
|
|
8
|
+
import aiohttp
|
|
9
|
+
from aiofiles import open as aio_file_open
|
|
10
|
+
from aiofiles.os import path as aio_path
|
|
11
|
+
|
|
12
|
+
from ..const.api_endpoints import API_ENDPOINT
|
|
13
|
+
from ..const.const import (
|
|
14
|
+
ACCESS_TOKEN_LOGIN_RETRIES,
|
|
15
|
+
ACCESS_TOKEN_LOGIN_RETRY_INTERVAL,
|
|
16
|
+
AUTH_DOMAIN,
|
|
17
|
+
BASE_DOMAIN,
|
|
18
|
+
DEFAULT_USER_AGENT,
|
|
19
|
+
MAX_API_FETCH_TIME_WARN,
|
|
20
|
+
PUBLIC_API_ENDPOINT,
|
|
21
|
+
WARN_INTERVAL_API_DURATION,
|
|
22
|
+
)
|
|
23
|
+
from ..exceptions.error_strings import (
|
|
24
|
+
ErrorsAPIParsing,
|
|
25
|
+
ErrorsAuth,
|
|
26
|
+
ErrorsAuthTokenExpired,
|
|
27
|
+
)
|
|
28
|
+
from ..exceptions.exceptions import (
|
|
29
|
+
AnycubicAPIParsingError,
|
|
30
|
+
AnycubicAuthError,
|
|
31
|
+
AnycubicAuthTokensExpired,
|
|
32
|
+
)
|
|
33
|
+
from ..models.auth import AnycubicAuthentication, AnycubicAuthMode
|
|
34
|
+
from ..models.http import HTTP_METHODS, AnycubicAPIEndpoint
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class AnycubicAPIBase:
|
|
38
|
+
__slots__ = (
|
|
39
|
+
"_cached_web_auth_token_path",
|
|
40
|
+
"_base_url",
|
|
41
|
+
"_public_api_root",
|
|
42
|
+
"_session",
|
|
43
|
+
"_sessionjar",
|
|
44
|
+
"_debug_logger",
|
|
45
|
+
"_tokens_changed",
|
|
46
|
+
"_log_api_call_info",
|
|
47
|
+
"_last_warn_api_duration",
|
|
48
|
+
"_anycubic_auth",
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
def __init__(
|
|
52
|
+
self,
|
|
53
|
+
session: aiohttp.ClientSession,
|
|
54
|
+
cookie_jar: aiohttp.CookieJar,
|
|
55
|
+
debug_logger: Any = None,
|
|
56
|
+
auth_token: str | None = None,
|
|
57
|
+
auth_mode: AnycubicAuthMode | None = None,
|
|
58
|
+
device_id: str | None = None,
|
|
59
|
+
) -> None:
|
|
60
|
+
# Cache
|
|
61
|
+
self._cached_web_auth_token_path: str | None = None
|
|
62
|
+
# API
|
|
63
|
+
self._base_url: str = f"https://{BASE_DOMAIN}/"
|
|
64
|
+
self._public_api_root: str = f"{self.base_url}{PUBLIC_API_ENDPOINT}"
|
|
65
|
+
# Internal
|
|
66
|
+
self._session: aiohttp.ClientSession = session
|
|
67
|
+
self._sessionjar: aiohttp.CookieJar = cookie_jar
|
|
68
|
+
self._debug_logger: Any = debug_logger
|
|
69
|
+
self._tokens_changed: bool = False
|
|
70
|
+
self._log_api_call_info: bool = False
|
|
71
|
+
self._last_warn_api_duration: int | None = None
|
|
72
|
+
self._anycubic_auth: AnycubicAuthentication | None = None
|
|
73
|
+
|
|
74
|
+
if auth_token:
|
|
75
|
+
self.set_authentication(
|
|
76
|
+
auth_token=auth_token,
|
|
77
|
+
auth_mode=auth_mode,
|
|
78
|
+
device_id=device_id,
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
@property
|
|
82
|
+
def base_url(self) -> str:
|
|
83
|
+
return self._base_url
|
|
84
|
+
|
|
85
|
+
def set_log_api_call_info(
|
|
86
|
+
self,
|
|
87
|
+
val: bool,
|
|
88
|
+
) -> None:
|
|
89
|
+
self._log_api_call_info = bool(val)
|
|
90
|
+
|
|
91
|
+
@property
|
|
92
|
+
def anycubic_auth(self) -> AnycubicAuthentication:
|
|
93
|
+
if self._anycubic_auth is None:
|
|
94
|
+
raise AnycubicAuthError(ErrorsAuth.missing_auth)
|
|
95
|
+
return self._anycubic_auth
|
|
96
|
+
|
|
97
|
+
@property
|
|
98
|
+
def tokens_changed(self) -> bool:
|
|
99
|
+
return self._tokens_changed
|
|
100
|
+
|
|
101
|
+
def _log_to_debug(self, msg: str) -> None:
|
|
102
|
+
if self._debug_logger:
|
|
103
|
+
self._debug_logger.debug(msg)
|
|
104
|
+
|
|
105
|
+
def _log_to_warn(self, msg: str) -> None:
|
|
106
|
+
if self._debug_logger:
|
|
107
|
+
self._debug_logger.warning(msg)
|
|
108
|
+
|
|
109
|
+
def _log_to_error(self, msg: str) -> None:
|
|
110
|
+
if self._debug_logger:
|
|
111
|
+
self._debug_logger.error(msg)
|
|
112
|
+
|
|
113
|
+
#
|
|
114
|
+
#
|
|
115
|
+
# API Functions
|
|
116
|
+
# ------------------------------------------
|
|
117
|
+
|
|
118
|
+
def _web_headers(self, with_origin: str | None = AUTH_DOMAIN) -> dict[str, Any]:
|
|
119
|
+
header_dict = {}
|
|
120
|
+
if self.anycubic_auth.requires_user_agent:
|
|
121
|
+
header_dict['User-Agent'] = DEFAULT_USER_AGENT
|
|
122
|
+
|
|
123
|
+
if with_origin:
|
|
124
|
+
header_dict['Origin'] = f'https://{with_origin}'
|
|
125
|
+
|
|
126
|
+
return header_dict
|
|
127
|
+
|
|
128
|
+
def _build_api_url(self, endpoint: AnycubicAPIEndpoint) -> str:
|
|
129
|
+
return f"{self._public_api_root}{endpoint.endpoint}"
|
|
130
|
+
|
|
131
|
+
@overload
|
|
132
|
+
async def _fetch_ext_resp(
|
|
133
|
+
self,
|
|
134
|
+
method: HTTP_METHODS,
|
|
135
|
+
base_url: str,
|
|
136
|
+
query: dict[str, Any] | None = None,
|
|
137
|
+
params: dict[str, Any] = {},
|
|
138
|
+
extra_headers: dict[str, Any] = {},
|
|
139
|
+
with_origin: str | None = AUTH_DOMAIN,
|
|
140
|
+
put_data: bytes | None = None,
|
|
141
|
+
) -> dict[Any, Any]: ...
|
|
142
|
+
|
|
143
|
+
@overload
|
|
144
|
+
async def _fetch_ext_resp(
|
|
145
|
+
self,
|
|
146
|
+
method: HTTP_METHODS,
|
|
147
|
+
base_url: str,
|
|
148
|
+
query: dict[str, Any] | None = None,
|
|
149
|
+
params: dict[str, Any] = {},
|
|
150
|
+
extra_headers: dict[str, Any] = {},
|
|
151
|
+
with_origin: str | None = AUTH_DOMAIN,
|
|
152
|
+
put_data: bytes | None = None,
|
|
153
|
+
is_json: bool = True,
|
|
154
|
+
return_url: bool = False,
|
|
155
|
+
) -> dict[Any, Any] | str: ...
|
|
156
|
+
|
|
157
|
+
async def _fetch_ext_resp(
|
|
158
|
+
self,
|
|
159
|
+
method: HTTP_METHODS,
|
|
160
|
+
base_url: str,
|
|
161
|
+
query: dict[str, Any] | None = None,
|
|
162
|
+
params: dict[str, Any] | list[Any] | str | None = {},
|
|
163
|
+
extra_headers: dict[str, Any] = {},
|
|
164
|
+
with_origin: str | None = AUTH_DOMAIN,
|
|
165
|
+
put_data: bytes | None = None,
|
|
166
|
+
is_json: bool = True,
|
|
167
|
+
return_url: bool = False,
|
|
168
|
+
) -> dict[Any, Any] | str:
|
|
169
|
+
url = base_url
|
|
170
|
+
time_start: float = time.time()
|
|
171
|
+
headers = {**self._web_headers(with_origin=with_origin), **extra_headers}
|
|
172
|
+
if method == HTTP_METHODS.POST:
|
|
173
|
+
if params is not None and (isinstance(params, dict) or isinstance(params, list)):
|
|
174
|
+
data = json.dumps(params)
|
|
175
|
+
elif params is not None:
|
|
176
|
+
data = str(params)
|
|
177
|
+
else:
|
|
178
|
+
data = None
|
|
179
|
+
h_coro = self._session.post(url, params=query, data=data, headers=headers)
|
|
180
|
+
elif method == HTTP_METHODS.PUT:
|
|
181
|
+
h_coro = self._session.put(url, params=query, data=put_data, headers=headers)
|
|
182
|
+
else:
|
|
183
|
+
h_coro = self._session.get(url, params=query, headers=headers)
|
|
184
|
+
|
|
185
|
+
response_url = None
|
|
186
|
+
|
|
187
|
+
try:
|
|
188
|
+
async with h_coro as resp:
|
|
189
|
+
if is_json:
|
|
190
|
+
resp_data: dict[str, Any] | str = await resp.json()
|
|
191
|
+
else:
|
|
192
|
+
resp_data = await resp.text()
|
|
193
|
+
|
|
194
|
+
response_url = resp.url
|
|
195
|
+
except Exception:
|
|
196
|
+
raise AnycubicAPIParsingError(ErrorsAPIParsing.api_error_server_maintenance)
|
|
197
|
+
|
|
198
|
+
time_end: float = time.time()
|
|
199
|
+
time_diff: float = time_end - time_start
|
|
200
|
+
over_limit: bool = int(time_diff) > MAX_API_FETCH_TIME_WARN
|
|
201
|
+
if (
|
|
202
|
+
over_limit
|
|
203
|
+
and (
|
|
204
|
+
not self._last_warn_api_duration
|
|
205
|
+
or time_end > self._last_warn_api_duration + WARN_INTERVAL_API_DURATION
|
|
206
|
+
)
|
|
207
|
+
):
|
|
208
|
+
self._log_to_warn(
|
|
209
|
+
f"Responses from server are taking over {MAX_API_FETCH_TIME_WARN}s (Took {int(time_diff)}s)"
|
|
210
|
+
)
|
|
211
|
+
if self._log_api_call_info:
|
|
212
|
+
self._log_to_debug(
|
|
213
|
+
f"Finished fetching {url} in {time_diff:.2f}s."
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
if return_url:
|
|
217
|
+
return str(response_url)
|
|
218
|
+
return resp_data
|
|
219
|
+
|
|
220
|
+
async def _fetch_aws_put_resp(self, final_url: str, put_data: bytes) -> dict[Any, Any] | str:
|
|
221
|
+
resp = await self._fetch_ext_resp(
|
|
222
|
+
method=HTTP_METHODS.PUT,
|
|
223
|
+
base_url=final_url,
|
|
224
|
+
is_json=False,
|
|
225
|
+
put_data=put_data,
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
if isinstance(resp, str) and len(resp) > 0:
|
|
229
|
+
raise AnycubicAPIParsingError(ErrorsAPIParsing.api_error_aws.format(resp))
|
|
230
|
+
|
|
231
|
+
return resp
|
|
232
|
+
|
|
233
|
+
async def _fetch_api_resp(
|
|
234
|
+
self,
|
|
235
|
+
endpoint: AnycubicAPIEndpoint,
|
|
236
|
+
query: dict[str, Any] | None = None,
|
|
237
|
+
params: dict[str, Any] = {},
|
|
238
|
+
extra_headers: dict[str, Any] = {},
|
|
239
|
+
with_origin: str | None = AUTH_DOMAIN,
|
|
240
|
+
with_token: bool = True,
|
|
241
|
+
) -> dict[Any, Any]:
|
|
242
|
+
resp = await self._fetch_ext_resp(
|
|
243
|
+
method=endpoint.method,
|
|
244
|
+
base_url=self._build_api_url(endpoint),
|
|
245
|
+
query=query,
|
|
246
|
+
params=params,
|
|
247
|
+
extra_headers=self.anycubic_auth.get_auth_headers(
|
|
248
|
+
with_token=with_token
|
|
249
|
+
),
|
|
250
|
+
with_origin=with_origin,
|
|
251
|
+
)
|
|
252
|
+
return resp
|
|
253
|
+
|
|
254
|
+
#
|
|
255
|
+
#
|
|
256
|
+
# Login Functions
|
|
257
|
+
# ------------------------------------------
|
|
258
|
+
|
|
259
|
+
def set_authentication(
|
|
260
|
+
self,
|
|
261
|
+
auth_token: str | None,
|
|
262
|
+
auth_mode: AnycubicAuthMode | int | None = None,
|
|
263
|
+
device_id: str | None = None,
|
|
264
|
+
auth_access_token: str | None = None,
|
|
265
|
+
auto_pick_token: bool = True,
|
|
266
|
+
) -> None:
|
|
267
|
+
if not auth_token and not auth_access_token:
|
|
268
|
+
raise AnycubicAuthError(ErrorsAuth.set_auth_missing_token)
|
|
269
|
+
|
|
270
|
+
if isinstance(auth_mode, int):
|
|
271
|
+
auth_mode = AnycubicAuthMode(auth_mode)
|
|
272
|
+
|
|
273
|
+
if (
|
|
274
|
+
auto_pick_token and (
|
|
275
|
+
not auth_access_token
|
|
276
|
+
and auth_mode == AnycubicAuthMode.SLICER
|
|
277
|
+
)
|
|
278
|
+
):
|
|
279
|
+
auth_access_token = f"{auth_token}"
|
|
280
|
+
auth_token = None
|
|
281
|
+
|
|
282
|
+
self._anycubic_auth = AnycubicAuthentication(
|
|
283
|
+
auth_token=auth_token,
|
|
284
|
+
auth_mode=auth_mode,
|
|
285
|
+
device_id=device_id,
|
|
286
|
+
auth_access_token=auth_access_token,
|
|
287
|
+
)
|
|
288
|
+
|
|
289
|
+
async def _get_user_token_with_access_token_with_retry(self) -> None:
|
|
290
|
+
retries = ACCESS_TOKEN_LOGIN_RETRIES
|
|
291
|
+
for x in range(retries):
|
|
292
|
+
try:
|
|
293
|
+
await self._get_user_token_with_access_token()
|
|
294
|
+
return
|
|
295
|
+
except AnycubicAuthError:
|
|
296
|
+
if x < retries - 1:
|
|
297
|
+
await asyncio.sleep(ACCESS_TOKEN_LOGIN_RETRY_INTERVAL)
|
|
298
|
+
else:
|
|
299
|
+
raise
|
|
300
|
+
|
|
301
|
+
async def _get_user_token_with_access_token(self) -> None:
|
|
302
|
+
params = self.anycubic_auth.auth_access_token_payload
|
|
303
|
+
resp = await self._fetch_api_resp(
|
|
304
|
+
endpoint=API_ENDPOINT.auth_sig_token,
|
|
305
|
+
query=None,
|
|
306
|
+
params=params,
|
|
307
|
+
with_token=False,
|
|
308
|
+
)
|
|
309
|
+
if not resp or not resp['data']:
|
|
310
|
+
server_message = resp.get('msg') if resp else None
|
|
311
|
+
error_message = ErrorsAuth.access_token_login_failed.format(server_message)
|
|
312
|
+
self._log_to_debug(error_message)
|
|
313
|
+
raise AnycubicAuthError(error_message)
|
|
314
|
+
self.anycubic_auth.set_auth_token(
|
|
315
|
+
resp['data']['token']
|
|
316
|
+
)
|
|
317
|
+
self._log_to_debug("Logged in and retrieved user token with access_token.")
|
|
318
|
+
|
|
319
|
+
def get_auth_config_dict(self) -> dict[str, Any]:
|
|
320
|
+
self._tokens_changed = False
|
|
321
|
+
|
|
322
|
+
return self.anycubic_auth.get_auth_config_dict()
|
|
323
|
+
|
|
324
|
+
def load_auth_config_from_dict(
|
|
325
|
+
self,
|
|
326
|
+
data: dict[str, Any],
|
|
327
|
+
minimal: bool = False,
|
|
328
|
+
) -> None:
|
|
329
|
+
self.anycubic_auth.load_auth_config_from_dict(
|
|
330
|
+
data,
|
|
331
|
+
minimal=minimal,
|
|
332
|
+
)
|
|
333
|
+
self._log_to_debug("Loaded auth tokens from dict.")
|
|
334
|
+
|
|
335
|
+
async def _load_cached_web_auth_token(self) -> None:
|
|
336
|
+
if (
|
|
337
|
+
self._cached_web_auth_token_path is not None
|
|
338
|
+
and (await aio_path.exists(self._cached_web_auth_token_path))
|
|
339
|
+
):
|
|
340
|
+
|
|
341
|
+
try:
|
|
342
|
+
async with aio_file_open(self._cached_web_auth_token_path, mode='r') as wo:
|
|
343
|
+
token = await wo.read()
|
|
344
|
+
self.set_authentication(
|
|
345
|
+
auth_token=token,
|
|
346
|
+
)
|
|
347
|
+
|
|
348
|
+
except Exception:
|
|
349
|
+
pass
|
|
350
|
+
|
|
351
|
+
async def _check_can_access_api(
|
|
352
|
+
self,
|
|
353
|
+
) -> bool:
|
|
354
|
+
await self._load_cached_web_auth_token()
|
|
355
|
+
if self.anycubic_auth.requires_access_token:
|
|
356
|
+
try:
|
|
357
|
+
await self._get_user_token_with_access_token_with_retry()
|
|
358
|
+
except AnycubicAuthError:
|
|
359
|
+
return False
|
|
360
|
+
try:
|
|
361
|
+
await self.get_user_info()
|
|
362
|
+
return True
|
|
363
|
+
except AnycubicAuthTokensExpired:
|
|
364
|
+
self._log_to_debug("Tokens expired.")
|
|
365
|
+
return False
|
|
366
|
+
|
|
367
|
+
async def check_api_tokens(self) -> bool:
|
|
368
|
+
if not await self._check_can_access_api():
|
|
369
|
+
if self.anycubic_auth.clear_cached_access_user_token():
|
|
370
|
+
self._tokens_changed = True
|
|
371
|
+
self._log_to_debug("Cleared cached user token.")
|
|
372
|
+
return await self._check_can_access_api()
|
|
373
|
+
return False
|
|
374
|
+
|
|
375
|
+
return True
|
|
376
|
+
|
|
377
|
+
async def get_user_info(
|
|
378
|
+
self,
|
|
379
|
+
raw_data: bool = False,
|
|
380
|
+
) -> dict[str, Any]:
|
|
381
|
+
resp = await self._fetch_api_resp(endpoint=API_ENDPOINT.user_info)
|
|
382
|
+
if raw_data:
|
|
383
|
+
return resp
|
|
384
|
+
|
|
385
|
+
data: dict[str, Any] | None = resp['data']
|
|
386
|
+
if resp and resp.get('msg') == 'request error':
|
|
387
|
+
raise AnycubicAPIParsingError(ErrorsAPIParsing.api_error_user_server_maintenance)
|
|
388
|
+
if data is None:
|
|
389
|
+
raise AnycubicAuthTokensExpired(ErrorsAuthTokenExpired.invalid_credentials)
|
|
390
|
+
|
|
391
|
+
# A rejected token can still return a data object, just without a user
|
|
392
|
+
# id. Treat that as invalid credentials so it surfaces as "invalid
|
|
393
|
+
# auth" rather than a TypeError from int(None) further down.
|
|
394
|
+
if data.get('id') is None:
|
|
395
|
+
raise AnycubicAuthTokensExpired(ErrorsAuthTokenExpired.invalid_credentials)
|
|
396
|
+
|
|
397
|
+
self.anycubic_auth.set_api_user_id(data['id'])
|
|
398
|
+
self.anycubic_auth.set_api_user_email(data.get('user_email'))
|
|
399
|
+
|
|
400
|
+
return data
|