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.
Files changed (33) hide show
  1. anycubic_cloud_api/__init__.py +62 -0
  2. anycubic_cloud_api/anycubic_api.py +7 -0
  3. anycubic_cloud_api/api/base.py +400 -0
  4. anycubic_cloud_api/api/functions.py +2055 -0
  5. anycubic_cloud_api/api/mqtt.py +449 -0
  6. anycubic_cloud_api/const/api_endpoints.py +106 -0
  7. anycubic_cloud_api/const/const.py +47 -0
  8. anycubic_cloud_api/const/enums.py +97 -0
  9. anycubic_cloud_api/const/mqtt.py +13 -0
  10. anycubic_cloud_api/data_models/consumable.py +116 -0
  11. anycubic_cloud_api/data_models/files.py +429 -0
  12. anycubic_cloud_api/data_models/gcode_file.py +107 -0
  13. anycubic_cloud_api/data_models/orders.py +369 -0
  14. anycubic_cloud_api/data_models/print_response.py +71 -0
  15. anycubic_cloud_api/data_models/print_speed_mode.py +61 -0
  16. anycubic_cloud_api/data_models/printer.py +2753 -0
  17. anycubic_cloud_api/data_models/printer_properties.py +1107 -0
  18. anycubic_cloud_api/data_models/printing_settings.py +130 -0
  19. anycubic_cloud_api/data_models/project.py +1234 -0
  20. anycubic_cloud_api/exceptions/error_strings.py +340 -0
  21. anycubic_cloud_api/exceptions/exceptions.py +91 -0
  22. anycubic_cloud_api/helpers/helpers.py +240 -0
  23. anycubic_cloud_api/models/auth.py +396 -0
  24. anycubic_cloud_api/models/cloud_upload.py +249 -0
  25. anycubic_cloud_api/models/http.py +29 -0
  26. anycubic_cloud_api/resources/anycubic_mqqt_tls_ca.crt +24 -0
  27. anycubic_cloud_api/resources/anycubic_mqqt_tls_client.crt +24 -0
  28. anycubic_cloud_api/resources/anycubic_mqqt_tls_client.key +28 -0
  29. anycubic_cloud_api-0.1.0.dist-info/METADATA +79 -0
  30. anycubic_cloud_api-0.1.0.dist-info/RECORD +33 -0
  31. anycubic_cloud_api-0.1.0.dist-info/WHEEL +5 -0
  32. anycubic_cloud_api-0.1.0.dist-info/licenses/LICENSE +674 -0
  33. anycubic_cloud_api-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,449 @@
1
+ from __future__ import annotations
2
+
3
+ import asyncio
4
+ import json
5
+ import ssl
6
+ import traceback
7
+ from collections.abc import Callable
8
+ from os import path
9
+ from typing import TYPE_CHECKING, Any
10
+
11
+ from paho.mqtt import client as mqtt_client
12
+
13
+ # paho-mqtt 2.x requires CallbackAPIVersion as the first argument to Client().
14
+ # We pin VERSION1 so the existing callback signatures (on_connect with rc,
15
+ # on_disconnect with rc, on_message with msg, on_subscribe with mid/granted_qos)
16
+ # keep working without rewriting every callback in the file.
17
+ # Fall back to a sentinel for paho-mqtt 1.x where this enum doesn't exist.
18
+ try:
19
+ from paho.mqtt.enums import CallbackAPIVersion as _CallbackAPIVersion
20
+ _MQTT_CALLBACK_API_VERSION = _CallbackAPIVersion.VERSION1
21
+ except ImportError: # paho-mqtt 1.x
22
+ _MQTT_CALLBACK_API_VERSION = None
23
+
24
+ from ..const.mqtt import (
25
+ MQTT_HOST,
26
+ MQTT_PORT,
27
+ MQTT_ROOT_TOPIC_PLUS,
28
+ MQTT_ROOT_TOPIC_PRINTER,
29
+ MQTT_ROOT_TOPIC_PUBLISH_PRINTER,
30
+ MQTT_ROOT_TOPIC_SERVER,
31
+ MQTT_TIMEOUT,
32
+ )
33
+ from ..data_models.consumable import AnycubicConsumableData
34
+ from ..exceptions.error_strings import ErrorsMQTTClient
35
+ from ..exceptions.exceptions import (
36
+ AnycubicMQTTClientError,
37
+ AnycubicMQTTUnhandledData,
38
+ AnycubicMQTTUnknownUpdate,
39
+ )
40
+ from ..helpers.helpers import (
41
+ get_mqtt_ssl_path_ca,
42
+ get_mqtt_ssl_path_cert,
43
+ get_mqtt_ssl_path_key,
44
+ get_part_from_mqtt_topic,
45
+ get_ssl_cert_directory,
46
+ redact_part_from_mqtt_topic,
47
+ )
48
+ from .functions import AnycubicAPIFunctions
49
+
50
+ if TYPE_CHECKING:
51
+ from ..data_models.printer import AnycubicPrinter
52
+
53
+
54
+ class AnycubicMQTTAPI(AnycubicAPIFunctions):
55
+ __slots__ = (
56
+ "_mqtt_client",
57
+ "_mqtt_subscribed_printers",
58
+ "_mqtt_log_all_messages",
59
+ "_mqtt_connected",
60
+ "_mqtt_disconnected",
61
+ "_mqtt_callback_printer_update",
62
+ "_mqtt_callback_printer_busy",
63
+ "_mqtt_callback_subscribed",
64
+ )
65
+
66
+ def __init__(
67
+ self,
68
+ *args: Any,
69
+ mqtt_callback_printer_update: Callable[[], None] | None = None,
70
+ mqtt_callback_printer_busy: Callable[[], None] | None = None,
71
+ mqtt_callback_subscribed: Callable[[], None] | None = None,
72
+ **kwargs: Any,
73
+ ) -> None:
74
+ self._mqtt_client: mqtt_client.Client | None = None
75
+ self._mqtt_subscribed_printers: dict[str, AnycubicPrinter] = dict()
76
+ self._mqtt_log_all_messages: bool = False
77
+ self._mqtt_connected: asyncio.Event | None = None
78
+ self._mqtt_disconnected: asyncio.Event | None = None
79
+ self._mqtt_callback_printer_update: Callable[[], None] | None = mqtt_callback_printer_update
80
+ self._mqtt_callback_printer_busy: Callable[[], None] | None = mqtt_callback_printer_busy
81
+ self._mqtt_callback_subscribed: Callable[[], None] | None = mqtt_callback_subscribed
82
+ super().__init__(*args, **kwargs)
83
+
84
+ @property
85
+ def mqtt_is_started(self) -> bool:
86
+ return self._mqtt_client is not None
87
+
88
+ def set_mqtt_log_all_messages(self, val: bool) -> None:
89
+ self._mqtt_log_all_messages = bool(val)
90
+
91
+ async def mqtt_wait_for_connect(self) -> bool:
92
+ if self._mqtt_connected is None:
93
+ return True
94
+
95
+ try:
96
+ async with asyncio.timeout(10):
97
+ await self._mqtt_connected.wait()
98
+ self._mqtt_connected = None
99
+ await asyncio.sleep(2)
100
+ return True
101
+ except (asyncio.TimeoutError, asyncio.CancelledError):
102
+ return False
103
+
104
+ async def mqtt_wait_for_disconnect(self) -> bool:
105
+ if self._mqtt_disconnected is None:
106
+ return True
107
+
108
+ try:
109
+ async with asyncio.timeout(10):
110
+ await self._mqtt_disconnected.wait()
111
+ self._mqtt_disconnected = None
112
+ return True
113
+ except (asyncio.TimeoutError, asyncio.CancelledError):
114
+ return False
115
+
116
+ def _build_mqtt_printer_subscription(self, printer: AnycubicPrinter) -> list[str]:
117
+ # topic_slicer = f"{MQTT_ROOT_TOPIC_SLICER}{printer.machine_type}/{printer.key}/#"
118
+ topic_printer = f"{MQTT_ROOT_TOPIC_PRINTER}{printer.machine_type}/{printer.key}/#"
119
+ topic_plus = f"{MQTT_ROOT_TOPIC_PLUS}{printer.machine_type}/{printer.key}/#"
120
+ return list([
121
+ # topic_slicer,
122
+ topic_printer,
123
+ topic_plus
124
+ ])
125
+
126
+ def _build_mqtt_printer_publish_topic(self, printer: AnycubicPrinter, endpoint: str) -> str:
127
+ return f"{MQTT_ROOT_TOPIC_PUBLISH_PRINTER}{printer.machine_type}/{printer.key}/{endpoint}"
128
+
129
+ def _build_mqtt_user_subscription(self) -> list[str]:
130
+ user_id, user_id_md5 = self.anycubic_auth.get_user_id_md5_tuple()
131
+ root = f"{MQTT_ROOT_TOPIC_SERVER}{user_id}/{user_id_md5}"
132
+ topic_slice_report = f"{root}/slice/report"
133
+ topic_fdm_slice_report = f"{root}/fdmslice/report"
134
+
135
+ return list([topic_slice_report, topic_fdm_slice_report])
136
+
137
+ def _mqtt_topic_is_user_topic(self, topic: str) -> bool:
138
+ return get_part_from_mqtt_topic(topic, 3) == 'server'
139
+
140
+ def _mqtt_message_router(self, message: mqtt_client.MQTTMessage) -> None:
141
+ try:
142
+ topic = str(message.topic)
143
+ payload = json.loads(message.payload.decode('utf-8'))
144
+ except Exception as e:
145
+ self._log_to_error(
146
+ f"Anycubic MQTT Message decode error: {e}\n"
147
+ f" on MQTT topic: {message.topic}\n"
148
+ f" {message.payload!r}"
149
+ )
150
+ return
151
+
152
+ if self._mqtt_topic_is_user_topic(topic):
153
+ self._log_to_debug(
154
+ f"Anycubic MQTT USER Msg Received on `{topic}`:\n"
155
+ f" {payload}"
156
+ )
157
+
158
+ else:
159
+ printer_key = get_part_from_mqtt_topic(topic, 6)
160
+
161
+ if get_part_from_mqtt_topic(topic, 7) == 'response' and len(payload.keys()) == 1:
162
+ return
163
+
164
+ if printer_key not in self._mqtt_subscribed_printers:
165
+ return
166
+
167
+ printer = self._mqtt_subscribed_printers[printer_key]
168
+
169
+ printer_was_available = printer.is_available
170
+
171
+ try:
172
+ printer.process_mqtt_update(topic, AnycubicConsumableData(payload))
173
+
174
+ except AnycubicMQTTUnhandledData as e:
175
+ redacted_topic = redact_part_from_mqtt_topic(topic, 6)
176
+ self._log_to_warn(
177
+ f"Anycubic MQTT Message unhandled data in: {e}\n"
178
+ f" on MQTT topic: {redacted_topic}\n"
179
+ f" with type: {e.unhandled_mqtt_type}, "
180
+ f"action: {e.unhandled_mqtt_action}, "
181
+ f"state: {e.unhandled_mqtt_state}\n"
182
+ f" unhandled data: {e.unhandled_mqtt_data}"
183
+ )
184
+
185
+ except AnycubicMQTTUnknownUpdate as e:
186
+ # A message type this version doesn't parse yet. Nothing is
187
+ # broken and there is nothing the user can act on, so log a
188
+ # single debug line rather than an ERROR with a traceback.
189
+ redacted_topic = redact_part_from_mqtt_topic(topic, 6)
190
+ self._log_to_debug(
191
+ f"Anycubic MQTT Message not understood: {e}\n"
192
+ f" on MQTT topic: {redacted_topic}\n"
193
+ f" {payload}"
194
+ )
195
+
196
+ except Exception as e:
197
+ tb = traceback.format_exc()
198
+ redacted_topic = redact_part_from_mqtt_topic(topic, 6)
199
+ error_type = type(e)
200
+ self._log_to_error(
201
+ f"Anycubic MQTT Message error: {error_type}: {e}\n"
202
+ f" on MQTT topic: {redacted_topic}\n"
203
+ f" {payload}\n"
204
+ f"{tb}"
205
+ )
206
+
207
+ try:
208
+ if self._mqtt_callback_printer_update:
209
+ self._mqtt_callback_printer_update()
210
+
211
+ if (
212
+ self._mqtt_callback_printer_busy and
213
+ printer_was_available and printer.is_busy
214
+ ):
215
+ self._mqtt_callback_printer_busy()
216
+
217
+ except Exception as e:
218
+ tb = traceback.format_exc()
219
+ self._log_to_error(
220
+ f"Anycubic MQTT Callback error: {e}\n"
221
+ f"{tb}"
222
+ )
223
+
224
+ if self._mqtt_log_all_messages:
225
+ # Redact the printer key like the warning/error paths do --
226
+ # debug logs get pasted into issue reports.
227
+ self._log_to_debug(
228
+ f"Anycubic MQTT Message processed on topic: "
229
+ f"{redact_part_from_mqtt_topic(topic, 6)}\n"
230
+ f" {payload}"
231
+ )
232
+
233
+ def _mqtt_publish_on_topic(
234
+ self,
235
+ topic: str,
236
+ payload: dict[str, Any] | str,
237
+ ) -> None:
238
+ if self._mqtt_client is None:
239
+ return
240
+
241
+ mqtt_payload = json.dumps(payload) if isinstance(payload, dict) else payload
242
+
243
+ self._mqtt_client.publish(topic, payload=mqtt_payload)
244
+
245
+ def _mqtt_publish_to_printer(
246
+ self,
247
+ printer: AnycubicPrinter,
248
+ endpoint: str,
249
+ payload: dict[str, Any] | str,
250
+ ) -> None:
251
+ mqtt_topic = self._build_mqtt_printer_publish_topic(printer, endpoint)
252
+ self._mqtt_publish_on_topic(mqtt_topic, payload=payload)
253
+
254
+ def _mqtt_build_ssl_context(self) -> ssl.SSLContext:
255
+ ssl_root = get_ssl_cert_directory()
256
+
257
+ crt_path = get_mqtt_ssl_path_cert(ssl_root)
258
+
259
+ if not path.exists(crt_path):
260
+ self._log_to_error(f"Anycubic MQTT unable to start, no certificate found in root: {ssl_root}.")
261
+ raise AnycubicMQTTClientError(ErrorsMQTTClient.cert_missing)
262
+
263
+ # Anycubic's broker is a fixed cloud endpoint, so verify it properly.
264
+ # PROTOCOL_TLS_CLIENT enables certificate verification and hostname
265
+ # checking by default; both are set explicitly below to make that clear.
266
+ ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
267
+ ssl_context.minimum_version = ssl.TLSVersion.TLSv1_2
268
+
269
+ # Anycubic's client certificate is SHA-1 signed, which OpenSSL 3.x will
270
+ # not load at the default security level. Narrowed from upstream's
271
+ # "ALL:" so the normal cipher list still applies -- this relaxes the
272
+ # certificate signature check only.
273
+ ssl_context.set_ciphers('DEFAULT:@SECLEVEL=0')
274
+
275
+ # Anycubic's root CA omits the keyUsage extension. Python 3.13+ enables
276
+ # VERIFY_X509_STRICT by default, which rejects such a CA as a trust
277
+ # anchor outright. Clearing that single flag keeps full chain and
278
+ # hostname verification intact. No-op on older Python versions.
279
+ ssl_context.verify_flags &= ~ssl.VERIFY_X509_STRICT
280
+
281
+ ssl_context.check_hostname = True
282
+ ssl_context.verify_mode = ssl.CERT_REQUIRED
283
+
284
+ # Pin Anycubic's own root CA -- their broker certificate is issued by a
285
+ # private CA and is not present in any public trust store.
286
+ ssl_context.load_verify_locations(get_mqtt_ssl_path_ca(ssl_root))
287
+ ssl_context.load_cert_chain(
288
+ crt_path,
289
+ get_mqtt_ssl_path_key(ssl_root),
290
+ None,
291
+ )
292
+
293
+ return ssl_context
294
+
295
+ def _mqtt_on_subscribe(
296
+ self,
297
+ client: mqtt_client.Client,
298
+ userdata: Any,
299
+ mid: int,
300
+ granted_qos: tuple[int],
301
+ ) -> None:
302
+ if self._mqtt_connected is not None:
303
+ self._mqtt_connected.set()
304
+
305
+ def _mqtt_on_message(
306
+ self,
307
+ client: mqtt_client.Client,
308
+ userdata: Any,
309
+ message: mqtt_client.MQTTMessage,
310
+ ) -> None:
311
+ try:
312
+ self._mqtt_message_router(message)
313
+ except Exception as e:
314
+ tb = traceback.format_exc()
315
+ self._log_to_error(f"Anycubic MQTT internal error: {e}\n{tb}")
316
+ return
317
+
318
+ def _mqtt_on_disconnect(
319
+ self,
320
+ client: mqtt_client.Client,
321
+ userdata: Any,
322
+ rc: int,
323
+ ) -> None:
324
+ if rc == 0:
325
+ self._mqtt_client = None
326
+ self._log_to_debug("Anycubic MQTT Disconnected.")
327
+ if self._mqtt_disconnected is not None:
328
+ self._mqtt_disconnected.set()
329
+ else:
330
+ self._set_mqtt_username_password()
331
+ self._log_to_debug("Anycubic MQTT unintentionally disconnected, will reconnect.")
332
+
333
+ def _mqtt_on_connect(
334
+ self,
335
+ client: mqtt_client.Client,
336
+ userdata: Any,
337
+ flags: dict[str, Any],
338
+ rc: int,
339
+ ) -> None:
340
+ if rc == 0:
341
+ if self._mqtt_connected is None:
342
+ self._mqtt_connected = asyncio.Event()
343
+
344
+ self._log_to_debug("Anycubic MQTT Connected.")
345
+
346
+ if not self._mqtt_client:
347
+ raise AnycubicMQTTClientError(ErrorsMQTTClient.connect_client_missing)
348
+
349
+ for sub in self._build_mqtt_user_subscription():
350
+ self._log_to_debug(f"Anycubic MQTT Subscribing to USER {sub}.")
351
+ self._mqtt_client.subscribe(sub)
352
+
353
+ for printer_id, printer in self._mqtt_subscribed_printers.items():
354
+ self._mqtt_subscribe_printer_status(printer)
355
+
356
+ self._log_to_debug("Anycubic MQTT Subscribed.")
357
+
358
+ if (self._mqtt_callback_subscribed):
359
+ self._mqtt_callback_subscribed()
360
+ else:
361
+ self._log_to_warn(f"Anycubic MQTT Failed to connect, return code {rc}")
362
+
363
+ def _set_mqtt_username_password(self) -> None:
364
+ mqtt_username, mqtt_password = self.anycubic_auth.get_mqtt_login_info()
365
+
366
+ if self._mqtt_client is None:
367
+ return
368
+
369
+ self._mqtt_client.username_pw_set(
370
+ username=mqtt_username,
371
+ password=mqtt_password,
372
+ )
373
+
374
+ def connect_mqtt(self) -> None:
375
+ self._mqtt_connected = asyncio.Event()
376
+ self._mqtt_disconnected = asyncio.Event()
377
+
378
+ self._log_to_debug("Anycubic MQTT Connecting.")
379
+
380
+ # paho-mqtt 2.x: pass callback_api_version positionally; 1.x: omit.
381
+ if _MQTT_CALLBACK_API_VERSION is not None:
382
+ self._mqtt_client = mqtt_client.Client(
383
+ _MQTT_CALLBACK_API_VERSION,
384
+ client_id=self.anycubic_auth.get_mqtt_client_id(),
385
+ clean_session=True,
386
+ )
387
+ else:
388
+ self._mqtt_client = mqtt_client.Client(
389
+ client_id=self.anycubic_auth.get_mqtt_client_id(),
390
+ clean_session=True,
391
+ )
392
+
393
+ self._mqtt_client.on_connect = self._mqtt_on_connect
394
+ self._mqtt_client.on_disconnect = self._mqtt_on_disconnect
395
+ self._mqtt_client.on_message = self._mqtt_on_message
396
+ self._mqtt_client.on_subscribe = self._mqtt_on_subscribe
397
+
398
+ self._set_mqtt_username_password()
399
+
400
+ self._mqtt_client.tls_set_context(self._mqtt_build_ssl_context())
401
+
402
+ self._mqtt_client.reconnect_delay_set(5)
403
+
404
+ self._mqtt_client.connect(
405
+ host=MQTT_HOST,
406
+ port=MQTT_PORT,
407
+ keepalive=MQTT_TIMEOUT,
408
+ )
409
+
410
+ self._mqtt_client.loop_forever()
411
+ self._mqtt_client = None
412
+ if self._mqtt_disconnected is not None:
413
+ self._mqtt_disconnected.set()
414
+ self._mqtt_connected = None
415
+ self._mqtt_disconnected = None
416
+ self._log_to_debug("Anycubic MQTT Client removed.")
417
+
418
+ def disconnect_mqtt(self) -> None:
419
+ self._log_to_debug("Anycubic MQTT Disconnecting.")
420
+ if self._mqtt_client is None:
421
+ return
422
+
423
+ self._mqtt_client.disconnect()
424
+
425
+ def _mqtt_subscribe_printer_status(self, printer: AnycubicPrinter) -> None:
426
+ if not self._mqtt_client:
427
+ raise AnycubicMQTTClientError(ErrorsMQTTClient.sub_printer_status_client_missing)
428
+ for sub in self._build_mqtt_printer_subscription(printer):
429
+ self._log_to_debug(f"Anycubic MQTT Subscribing to PRINTER {sub}.")
430
+ self._mqtt_client.subscribe(sub)
431
+
432
+ def mqtt_add_subscribed_printer(self, printer: AnycubicPrinter) -> None:
433
+ if not printer.key:
434
+ raise AnycubicMQTTClientError(ErrorsMQTTClient.sub_printer_key_missing)
435
+
436
+ if printer.key in self._mqtt_subscribed_printers:
437
+ return
438
+
439
+ self._mqtt_subscribed_printers[printer.key] = printer
440
+
441
+ def mqtt_unsubscribe_printer_status(self, printer: AnycubicPrinter) -> None:
442
+
443
+ if printer.key in self._mqtt_subscribed_printers:
444
+ if self._mqtt_client is not None:
445
+ for sub in self._build_mqtt_printer_subscription(printer):
446
+ self._log_to_debug(f"Anycubic MQTT Ubsubscribing to PRINTER {sub}.")
447
+ self._mqtt_client.unsubscribe(sub)
448
+
449
+ del self._mqtt_subscribed_printers[printer.key]
@@ -0,0 +1,106 @@
1
+ from __future__ import annotations
2
+
3
+ from ..models.http import HTTP_METHODS, AnycubicAPIEndpoint
4
+
5
+
6
+ class API_ENDPOINT:
7
+ oauth_token_url = AnycubicAPIEndpoint(
8
+ HTTP_METHODS.GET,
9
+ '/v3/public/getoauthToken',
10
+ )
11
+ auth_sig_token = AnycubicAPIEndpoint(
12
+ HTTP_METHODS.POST,
13
+ '/v3/public/loginWithAccessToken',
14
+ )
15
+ user_store = AnycubicAPIEndpoint(
16
+ HTTP_METHODS.POST,
17
+ '/work/index/getUserStore',
18
+ )
19
+ lock_storage_space = AnycubicAPIEndpoint(
20
+ HTTP_METHODS.POST,
21
+ '/v2/cloud_storage/lockStorageSpace',
22
+ )
23
+ unlock_storage_space = AnycubicAPIEndpoint(
24
+ HTTP_METHODS.POST,
25
+ '/v2/cloud_storage/unlockStorageSpace',
26
+ )
27
+ new_file_upload = AnycubicAPIEndpoint(
28
+ HTTP_METHODS.POST,
29
+ '/v2/profile/newUploadFile',
30
+ )
31
+ delete_cloud_file = AnycubicAPIEndpoint(
32
+ HTTP_METHODS.POST,
33
+ '/work/index/delFiles',
34
+ )
35
+ user_files = AnycubicAPIEndpoint(
36
+ HTTP_METHODS.POST,
37
+ '/work/index/files',
38
+ )
39
+ user_info = AnycubicAPIEndpoint(
40
+ HTTP_METHODS.GET,
41
+ '/user/profile/userInfo',
42
+ )
43
+ printer_status = AnycubicAPIEndpoint(
44
+ HTTP_METHODS.GET,
45
+ '/v2/Printer/status',
46
+ )
47
+ printer_info = AnycubicAPIEndpoint(
48
+ HTTP_METHODS.GET,
49
+ '/v2/printer/info',
50
+ )
51
+ printer_tool = AnycubicAPIEndpoint(
52
+ HTTP_METHODS.GET,
53
+ '/v2/printer/tool',
54
+ )
55
+ printer_functions = AnycubicAPIEndpoint(
56
+ HTTP_METHODS.GET,
57
+ '/v2/printer/functions',
58
+ )
59
+ printer_all = AnycubicAPIEndpoint(
60
+ HTTP_METHODS.GET,
61
+ '/v2/printer/all',
62
+ )
63
+ printers_status = AnycubicAPIEndpoint(
64
+ HTTP_METHODS.GET,
65
+ '/work/printer/printersStatus',
66
+ )
67
+ printer_get_printers = AnycubicAPIEndpoint(
68
+ HTTP_METHODS.GET,
69
+ '/work/printer/getPrinters',
70
+ )
71
+ print_history = AnycubicAPIEndpoint(
72
+ HTTP_METHODS.GET,
73
+ '/v2/project/printHistory',
74
+ )
75
+ project_info = AnycubicAPIEndpoint(
76
+ HTTP_METHODS.GET,
77
+ '/v2/project/info',
78
+ )
79
+ project_monitor = AnycubicAPIEndpoint(
80
+ HTTP_METHODS.GET,
81
+ '/v2/project/monitor',
82
+ )
83
+ project_get_projects = AnycubicAPIEndpoint(
84
+ HTTP_METHODS.GET,
85
+ '/work/project/getProjects',
86
+ )
87
+ project_gcode_info_fdm = AnycubicAPIEndpoint(
88
+ HTTP_METHODS.GET,
89
+ '/work/gcode/infoFdm',
90
+ )
91
+ send_order = AnycubicAPIEndpoint(
92
+ HTTP_METHODS.POST,
93
+ '/work/operation/sendOrder',
94
+ )
95
+ printer_update_name = AnycubicAPIEndpoint(
96
+ HTTP_METHODS.POST,
97
+ '/work/printer/Info',
98
+ )
99
+ printer_firmware_update = AnycubicAPIEndpoint(
100
+ HTTP_METHODS.GET,
101
+ '/work/printer/update_version',
102
+ )
103
+ printer_multi_color_box_firmware_update = AnycubicAPIEndpoint(
104
+ HTTP_METHODS.POST,
105
+ '/v2/printer/update_multi_color_box_version'
106
+ )
@@ -0,0 +1,47 @@
1
+ import re
2
+
3
+ BASE_DOMAIN = "cloud-universe.anycubic.com"
4
+ APP_REDIRECT_URI = "anycubic-i18n://cloud.anycubic.com:8088"
5
+ AUTH_DOMAIN = "uc.makeronline.com"
6
+
7
+ PUBLIC_API_ENDPOINT = "p/p/workbench/api"
8
+
9
+ PROJECT_IMAGE_URL_BASE = "https://workbentch.s3.us-east-2.amazonaws.com/"
10
+
11
+ REX_JS_FILE = re.compile(r'src="(/js/app\.[^.]+\.js)"')
12
+ # REX_CLIENT_ID = re.compile(r',clientId:"([^"]+)",')
13
+ REX_CLIENT_ID = re.compile(r'\'(?!getEl)([a-zA-Z0-9]{20})\'')
14
+ REX_APP_ID_BASIC = re.compile(r'appid["\']?:["\']([^"\']+)["\'],')
15
+ REX_APP_ID_OBF = re.compile(r'(?<!:)\'([a-zA-Z0-9]{32})\'')
16
+ REX_APP_VERSION = re.compile(r'version["\']?:["\']([^"\']+)["\'],')
17
+ REX_APP_SECRET_BASIC = re.compile(r'appSecret["\']?:["\']([^"\']+)["\'],')
18
+ REX_APP_SECRET_OBF = re.compile(r'(?<!:)\'([a-zA-Z0-9]{32})\'')
19
+ REX_GCODE_EXT = re.compile(r'\.gcode$')
20
+
21
+ DEFAULT_USER_AGENT = (
22
+ 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) '
23
+ 'AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 '
24
+ 'Safari/537.36'
25
+ )
26
+
27
+ AC_KNOWN_CID_WEB = '672efcd4ec11a66c8513'
28
+ AC_KNOWN_CID_APP = 'ca4c8416cced85a1dc02'
29
+ AC_KNOWN_AID = 'f9b3528877c94d5c9c5af32245db46ef'
30
+ AC_KNOWN_VID_WEB = '1.0.0'
31
+ AC_KNOWN_VID_APP = '1.4.8'
32
+ AC_KNOWN_VID_SLICER_NEXT = 'V3.0.0'
33
+ AC_KNOWN_SEC = '0cf75926606049a3937f56b0373b99fb'
34
+
35
+ MAX_PROJECT_LIST_RESULTS = 2000
36
+ MAX_PROJECT_IMAGE_SEARCH_COUNT = 200
37
+
38
+ MAX_API_FETCH_TIME_WARN = 20
39
+
40
+ WARN_INTERVAL_API_DURATION = 60 * 10
41
+
42
+ ACCESS_TOKEN_LOGIN_RETRIES = 2
43
+ ACCESS_TOKEN_LOGIN_RETRY_INTERVAL = 2
44
+
45
+
46
+ class AnycubicServerMessage:
47
+ FILE_NOT_FOUND = "No file found"
@@ -0,0 +1,97 @@
1
+ from enum import IntEnum, StrEnum
2
+
3
+
4
+ class AnycubicFeedType(IntEnum):
5
+ Feed = 1
6
+ Retract = 2
7
+ Finish = 3
8
+
9
+
10
+ class AnycubicPrintStatus(IntEnum):
11
+ Printing = 1
12
+ Complete = 2
13
+ Cancelled = 3
14
+ Downloading = 4
15
+ Checking = 5
16
+ Preheating = 6
17
+ Slicing = 7
18
+
19
+
20
+ class AnycubicOrderID(IntEnum):
21
+ START_PRINT = 1
22
+ PAUSE_PRINT = 2
23
+ RESUME_PRINT = 3
24
+ STOP_PRINT = 4
25
+ PRINT_SETTINGS = 6
26
+ IGNORE = 11 # Not handled
27
+ DETECT = 12 # Not handled, appears un-used.
28
+ STOP_PRINT_FORCE = 44
29
+ LIST_UDISK_FILES = 101
30
+ DELETE_UDISK_FILE = 102
31
+ LIST_LOCAL_FILES = 103
32
+ DELETE_LOCAL_FILE = 104
33
+ MOVE_AXLE = 201 # Not handled
34
+ MOVE_AXLE_TO_COORDINATES = 202 # Not handled, appears un-used.
35
+ START_EXPOSURE = 301 # Not handled, appears un-used.
36
+ CANCEL_EXPOSURE = 302 # Not handled, appears un-used.
37
+ START_RESIDUAL = 501 # Not handled, appears un-used.
38
+ CANCEL_RESIDUAL = 502 # Not handled
39
+ SET_DEVICE_SELF_TEST = 601 # Not handled, appears un-used.
40
+ GET_DEVICE_SELF_TEST = 602 # Not handled
41
+ SET_AUTO_OPERATION = 701 # Not handled, appears un-used.
42
+ GET_AUTO_OPERATION = 702 # Not handled
43
+ RESET_RELEASE_FILM = 801 # Not handled
44
+ GET_RELEASE_FILM = 802 # Not handled
45
+ SET_PRINT_STATUS_FREE = 901 # Not handled, appears un-used.
46
+ CAMERA_OPEN = 1001
47
+ CAMERA_CLOSE = 1002 # Not handled, appears un-used.
48
+ MULTI_COLOR_BOX_GET_INFO = 1206
49
+ MULTI_COLOR_BOX_DRY = 1207
50
+ FEED_FILAMENT = 1208
51
+ FEED_FILAMENT_FINISH = 1209 # Not handled, appears un-used.
52
+ MULTI_COLOR_BOX_REFRESH_SLOT = 1210 # Not handled
53
+ MULTI_COLOR_BOX_SET_SLOT = 1211
54
+ MULTI_COLOR_BOX_AUTO_FEED = 1212
55
+ MOVE_AXLE_TURN_OFF = 1213 # Not handled
56
+ FILAMENT_CONTROL = 1215 # Not handled
57
+ FEED_RESIN = 1224 # Not handled
58
+ M7_AUTO_OPERATION = 1225 # Not handled
59
+ CYCLIC_CLEANING = 1226 # Not handled
60
+ SET_AUTO_FEED_INFO = 1227 # Not handled, appears un-used.
61
+ GET_M7_AUTO_OPERATION = 1228 # Not handled
62
+ EXTFILBOX = 1229 # Not handled
63
+ GET_EXTFILBOX_INFO = 1230 # Not handled
64
+ QUERY_PERIPHERALS = 1231
65
+ GET_LIGHT_STATUS = 1232
66
+ SET_LIGHT_STATUS = 1233
67
+
68
+
69
+ class AnycubicFunctionID(IntEnum):
70
+ AXLE_MOVEMENT = 1
71
+ FILE_MANAGER = 2
72
+ EXPOSURE_TEST = 3
73
+ LCD_PEER_VIDEO = 7
74
+ FDM_AXIS_MOVE = 13
75
+ FDM_PEER_VIDEO = 22
76
+ DEVICE_STARTUP_SELF_TEST = 26
77
+ PRINT_STARTUP_SELF_TEST = 27
78
+ AUTOMATIC_OPERATION = 28
79
+ RESIDUE_CLEAN = 29
80
+ NOVICE_GUIDE = 30
81
+ RELEASE_FILM = 31
82
+ TASK_MODE = 32
83
+ LCD_INTELLIGENT_MATERIALS_BOX = 33
84
+ LCD_AUTO_OUT_IN_MATERIALS = 34
85
+ M7PRO_AUTOMATIC_OPERATION = 35
86
+ AI_DETECTION = 36
87
+ AUTO_LEVELER = 37
88
+ VIBRATION_COMPENSATION = 38
89
+ TIME_LAPSE = 39
90
+ VIDEO_LIGHT = 40
91
+ BOX_LIGHT = 41
92
+ MULTI_COLOR_BOX = 2006
93
+
94
+
95
+ class AnycubicPrinterMaterialType(StrEnum):
96
+ FILAMENT = "Filament"
97
+ RESIN = "Resin"