anycubic-cloud-api 0.4.33__tar.gz → 0.4.35__tar.gz
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-0.4.33/src/anycubic_cloud_api.egg-info → anycubic_cloud_api-0.4.35}/PKG-INFO +1 -1
- {anycubic_cloud_api-0.4.33 → anycubic_cloud_api-0.4.35}/pyproject.toml +1 -1
- {anycubic_cloud_api-0.4.33 → anycubic_cloud_api-0.4.35}/src/anycubic_cloud_api/api/mqtt.py +5 -1
- {anycubic_cloud_api-0.4.33 → anycubic_cloud_api-0.4.35}/src/anycubic_cloud_api/data_models/printer.py +171 -2
- {anycubic_cloud_api-0.4.33 → anycubic_cloud_api-0.4.35}/src/anycubic_cloud_api/data_models/project.py +16 -0
- {anycubic_cloud_api-0.4.33 → anycubic_cloud_api-0.4.35/src/anycubic_cloud_api.egg-info}/PKG-INFO +1 -1
- {anycubic_cloud_api-0.4.33 → anycubic_cloud_api-0.4.35}/src/anycubic_cloud_api.egg-info/SOURCES.txt +2 -0
- anycubic_cloud_api-0.4.35/tests/test_filament_change_0435.py +133 -0
- anycubic_cloud_api-0.4.35/tests/test_print_reports_0434.py +273 -0
- {anycubic_cloud_api-0.4.33 → anycubic_cloud_api-0.4.35}/COPYRIGHT +0 -0
- {anycubic_cloud_api-0.4.33 → anycubic_cloud_api-0.4.35}/LICENSE +0 -0
- {anycubic_cloud_api-0.4.33 → anycubic_cloud_api-0.4.35}/MANIFEST.in +0 -0
- {anycubic_cloud_api-0.4.33 → anycubic_cloud_api-0.4.35}/README.md +0 -0
- {anycubic_cloud_api-0.4.33 → anycubic_cloud_api-0.4.35}/setup.cfg +0 -0
- {anycubic_cloud_api-0.4.33 → anycubic_cloud_api-0.4.35}/src/anycubic_cloud_api/__init__.py +0 -0
- {anycubic_cloud_api-0.4.33 → anycubic_cloud_api-0.4.35}/src/anycubic_cloud_api/anycubic_api.py +0 -0
- {anycubic_cloud_api-0.4.33 → anycubic_cloud_api-0.4.35}/src/anycubic_cloud_api/api/base.py +0 -0
- {anycubic_cloud_api-0.4.33 → anycubic_cloud_api-0.4.35}/src/anycubic_cloud_api/api/functions.py +0 -0
- {anycubic_cloud_api-0.4.33 → anycubic_cloud_api-0.4.35}/src/anycubic_cloud_api/const/api_endpoints.py +0 -0
- {anycubic_cloud_api-0.4.33 → anycubic_cloud_api-0.4.35}/src/anycubic_cloud_api/const/const.py +0 -0
- {anycubic_cloud_api-0.4.33 → anycubic_cloud_api-0.4.35}/src/anycubic_cloud_api/const/enums.py +0 -0
- {anycubic_cloud_api-0.4.33 → anycubic_cloud_api-0.4.35}/src/anycubic_cloud_api/const/error_codes.py +0 -0
- {anycubic_cloud_api-0.4.33 → anycubic_cloud_api-0.4.35}/src/anycubic_cloud_api/const/lan.py +0 -0
- {anycubic_cloud_api-0.4.33 → anycubic_cloud_api-0.4.35}/src/anycubic_cloud_api/const/mqtt.py +0 -0
- {anycubic_cloud_api-0.4.33 → anycubic_cloud_api-0.4.35}/src/anycubic_cloud_api/const/regions.py +0 -0
- {anycubic_cloud_api-0.4.33 → anycubic_cloud_api-0.4.35}/src/anycubic_cloud_api/data_models/consumable.py +0 -0
- {anycubic_cloud_api-0.4.33 → anycubic_cloud_api-0.4.35}/src/anycubic_cloud_api/data_models/files.py +0 -0
- {anycubic_cloud_api-0.4.33 → anycubic_cloud_api-0.4.35}/src/anycubic_cloud_api/data_models/gcode_file.py +0 -0
- {anycubic_cloud_api-0.4.33 → anycubic_cloud_api-0.4.35}/src/anycubic_cloud_api/data_models/orders.py +0 -0
- {anycubic_cloud_api-0.4.33 → anycubic_cloud_api-0.4.35}/src/anycubic_cloud_api/data_models/print_response.py +0 -0
- {anycubic_cloud_api-0.4.33 → anycubic_cloud_api-0.4.35}/src/anycubic_cloud_api/data_models/print_speed_mode.py +0 -0
- {anycubic_cloud_api-0.4.33 → anycubic_cloud_api-0.4.35}/src/anycubic_cloud_api/data_models/printer_properties.py +0 -0
- {anycubic_cloud_api-0.4.33 → anycubic_cloud_api-0.4.35}/src/anycubic_cloud_api/data_models/printing_settings.py +0 -0
- {anycubic_cloud_api-0.4.33 → anycubic_cloud_api-0.4.35}/src/anycubic_cloud_api/exceptions/error_strings.py +0 -0
- {anycubic_cloud_api-0.4.33 → anycubic_cloud_api-0.4.35}/src/anycubic_cloud_api/exceptions/exceptions.py +0 -0
- {anycubic_cloud_api-0.4.33 → anycubic_cloud_api-0.4.35}/src/anycubic_cloud_api/helpers/helpers.py +0 -0
- {anycubic_cloud_api-0.4.33 → anycubic_cloud_api-0.4.35}/src/anycubic_cloud_api/lan/__init__.py +0 -0
- {anycubic_cloud_api-0.4.33 → anycubic_cloud_api-0.4.35}/src/anycubic_cloud_api/lan/client.py +0 -0
- {anycubic_cloud_api-0.4.33 → anycubic_cloud_api-0.4.35}/src/anycubic_cloud_api/lan/commands.py +0 -0
- {anycubic_cloud_api-0.4.33 → anycubic_cloud_api-0.4.35}/src/anycubic_cloud_api/lan/handshake.py +0 -0
- {anycubic_cloud_api-0.4.33 → anycubic_cloud_api-0.4.35}/src/anycubic_cloud_api/models/auth.py +0 -0
- {anycubic_cloud_api-0.4.33 → anycubic_cloud_api-0.4.35}/src/anycubic_cloud_api/models/cloud_upload.py +0 -0
- {anycubic_cloud_api-0.4.33 → anycubic_cloud_api-0.4.35}/src/anycubic_cloud_api/models/http.py +0 -0
- {anycubic_cloud_api-0.4.33 → anycubic_cloud_api-0.4.35}/src/anycubic_cloud_api/resources/anycubic_mqqt_tls_ca.crt +0 -0
- {anycubic_cloud_api-0.4.33 → anycubic_cloud_api-0.4.35}/src/anycubic_cloud_api/resources/anycubic_mqqt_tls_client.crt +0 -0
- {anycubic_cloud_api-0.4.33 → anycubic_cloud_api-0.4.35}/src/anycubic_cloud_api/resources/anycubic_mqqt_tls_client.key +0 -0
- {anycubic_cloud_api-0.4.33 → anycubic_cloud_api-0.4.35}/src/anycubic_cloud_api.egg-info/dependency_links.txt +0 -0
- {anycubic_cloud_api-0.4.33 → anycubic_cloud_api-0.4.35}/src/anycubic_cloud_api.egg-info/requires.txt +0 -0
- {anycubic_cloud_api-0.4.33 → anycubic_cloud_api-0.4.35}/src/anycubic_cloud_api.egg-info/top_level.txt +0 -0
- {anycubic_cloud_api-0.4.33 → anycubic_cloud_api-0.4.35}/tests/test_auth_mode_fallback.py +0 -0
- {anycubic_cloud_api-0.4.33 → anycubic_cloud_api-0.4.35}/tests/test_axis_position.py +0 -0
- {anycubic_cloud_api-0.4.33 → anycubic_cloud_api-0.4.35}/tests/test_endpoint_urls.py +0 -0
- {anycubic_cloud_api-0.4.33 → anycubic_cloud_api-0.4.35}/tests/test_exchange_rate_limit.py +0 -0
- {anycubic_cloud_api-0.4.33 → anycubic_cloud_api-0.4.35}/tests/test_lan_client.py +0 -0
- {anycubic_cloud_api-0.4.33 → anycubic_cloud_api-0.4.35}/tests/test_lan_commands.py +0 -0
- {anycubic_cloud_api-0.4.33 → anycubic_cloud_api-0.4.35}/tests/test_lan_handshake.py +0 -0
- {anycubic_cloud_api-0.4.33 → anycubic_cloud_api-0.4.35}/tests/test_lan_reports.py +0 -0
- {anycubic_cloud_api-0.4.33 → anycubic_cloud_api-0.4.35}/tests/test_null_tolerant_parsing.py +0 -0
- {anycubic_cloud_api-0.4.33 → anycubic_cloud_api-0.4.35}/tests/test_partial_reports.py +0 -0
- {anycubic_cloud_api-0.4.33 → anycubic_cloud_api-0.4.35}/tests/test_printer_error_codes.py +0 -0
- {anycubic_cloud_api-0.4.33 → anycubic_cloud_api-0.4.35}/tests/test_project_null_status.py +0 -0
- {anycubic_cloud_api-0.4.33 → anycubic_cloud_api-0.4.35}/tests/test_regions.py +0 -0
- {anycubic_cloud_api-0.4.33 → anycubic_cloud_api-0.4.35}/tests/test_secondary_ace.py +0 -0
|
@@ -171,8 +171,12 @@ class AnycubicMQTTAPI(AnycubicAPIFunctions):
|
|
|
171
171
|
printer.process_mqtt_update(topic, AnycubicConsumableData(payload))
|
|
172
172
|
|
|
173
173
|
except AnycubicMQTTUnhandledData as e:
|
|
174
|
+
# The message was applied; only fields this version has no use
|
|
175
|
+
# for were left over. That is for whoever extends the parser,
|
|
176
|
+
# not something a user can act on, and at warning level it was
|
|
177
|
+
# repeated every few seconds through every print (#42).
|
|
174
178
|
redacted_topic = redact_part_from_mqtt_topic(topic, 6)
|
|
175
|
-
self.
|
|
179
|
+
self._log_to_debug(
|
|
176
180
|
f"Anycubic MQTT Message unhandled data in: {e}\n"
|
|
177
181
|
f" on MQTT topic: {redacted_topic}\n"
|
|
178
182
|
f" with type: {e.unhandled_mqtt_type}, "
|
|
@@ -24,6 +24,7 @@ from ..exceptions.exceptions import (
|
|
|
24
24
|
AnycubicMQTTUnknownUpdate,
|
|
25
25
|
)
|
|
26
26
|
from ..helpers.helpers import (
|
|
27
|
+
as_int,
|
|
27
28
|
get_part_from_mqtt_topic,
|
|
28
29
|
time_duration_string_to_delta,
|
|
29
30
|
timedelta_to_dhm_string,
|
|
@@ -52,6 +53,46 @@ if TYPE_CHECKING:
|
|
|
52
53
|
from .print_response import AnycubicPrintResponse
|
|
53
54
|
from .printer_properties import AnycubicDryingStatus, AnycubicMaterialMapping
|
|
54
55
|
|
|
56
|
+
# Job progress a print update can carry, applied to the job it is about.
|
|
57
|
+
_PRINT_PROGRESS_KEYS = (
|
|
58
|
+
'curr_layer',
|
|
59
|
+
'total_layers',
|
|
60
|
+
'filename',
|
|
61
|
+
'print_time',
|
|
62
|
+
'progress',
|
|
63
|
+
'remain_time',
|
|
64
|
+
'supplies_usage',
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
# The steps of a filament change, as a Kobra X with an ACE 2 Pro reports
|
|
68
|
+
# them (#33). The ACE unfeed is the mirror of the feed, not yet seen.
|
|
69
|
+
_FILAMENT_CHANGE_STATES = frozenset({
|
|
70
|
+
'extfilbox_filament_feeding',
|
|
71
|
+
'extfilbox_filament_unfeeding',
|
|
72
|
+
'ace_filament_feeding',
|
|
73
|
+
'ace_filament_unfeeding',
|
|
74
|
+
'nozzle_purging',
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
# Printers with a feeder of their own, which shares the box list with any
|
|
78
|
+
# ACE fitted: the Kobra X.
|
|
79
|
+
_PRINTERS_WITH_OWN_FEEDER = frozenset({20030})
|
|
80
|
+
|
|
81
|
+
# How a printer words a refused order when it gives no code.
|
|
82
|
+
_REFUSED_STATES = frozenset({'failed', 'fail', 'error', 'refused'})
|
|
83
|
+
|
|
84
|
+
# What a print report says about the file being printed, which the job already
|
|
85
|
+
# knows by name. Read so the report is not flagged as unhandled data on every
|
|
86
|
+
# progress tick (#34, #42) -- nothing here changes what is shown.
|
|
87
|
+
_PRINT_FILE_KEYS = (
|
|
88
|
+
'file_root_path',
|
|
89
|
+
'filetype',
|
|
90
|
+
'display_filename',
|
|
91
|
+
'origin3mf',
|
|
92
|
+
'source_info',
|
|
93
|
+
'temp_gcode',
|
|
94
|
+
)
|
|
95
|
+
|
|
55
96
|
|
|
56
97
|
def _drain(consumable: Any) -> dict[str, Any]:
|
|
57
98
|
"""Read every remaining key, returning them as a plain dict.
|
|
@@ -1283,6 +1324,29 @@ class AnycubicPrinter:
|
|
|
1283
1324
|
settings['target_hotbed_temp'],
|
|
1284
1325
|
settings['target_nozzle_temp'],
|
|
1285
1326
|
)
|
|
1327
|
+
# The light is reported by its own message; here it only repeats.
|
|
1328
|
+
settings.get('light')
|
|
1329
|
+
# Read again now it is empty, which is what marks a nested block
|
|
1330
|
+
# consumed; left in place it was reported as unhandled data.
|
|
1331
|
+
data.get('settings')
|
|
1332
|
+
|
|
1333
|
+
if 'channel_no' in data:
|
|
1334
|
+
self._apply_active_channel(data['channel_no'], data.get('unit_no'))
|
|
1335
|
+
|
|
1336
|
+
# A Kobra X reports progress this way between info reports, with
|
|
1337
|
+
# no task id. It was logged as unhandled and dropped (#34).
|
|
1338
|
+
if any(key in data for key in _PRINT_PROGRESS_KEYS):
|
|
1339
|
+
self._apply_print_progress(project_id, data)
|
|
1340
|
+
return
|
|
1341
|
+
elif action == 'start' and state in _FILAMENT_CHANGE_STATES:
|
|
1342
|
+
# Each step of a filament change, naming the channel (and, for an
|
|
1343
|
+
# ACE, the unit). Only a feed says what is now loaded (#33).
|
|
1344
|
+
data = payload.get('data') or {}
|
|
1345
|
+
# Read on every step, so the ones that change nothing are not
|
|
1346
|
+
# reported as unhandled.
|
|
1347
|
+
channel_no, unit_no = data.get('channel_no'), data.get('unit_no')
|
|
1348
|
+
if state.endswith('_filament_feeding'):
|
|
1349
|
+
self._apply_active_channel(channel_no, unit_no)
|
|
1286
1350
|
return
|
|
1287
1351
|
elif action in ['start', 'stop'] and state in ['failed']:
|
|
1288
1352
|
err_msg = payload.get('msg')
|
|
@@ -1296,6 +1360,101 @@ class AnycubicPrinter:
|
|
|
1296
1360
|
else:
|
|
1297
1361
|
raise AnycubicMQTTUnknownUpdate(ErrorsMQTTUpdate.job_status)
|
|
1298
1362
|
|
|
1363
|
+
def _apply_active_channel(self, channel_no: Any, unit_no: Any = None) -> None:
|
|
1364
|
+
"""Follow the filament channel the printer says it is printing from.
|
|
1365
|
+
|
|
1366
|
+
A Kobra X reports each filament change as a print update carrying
|
|
1367
|
+
``channel_no``, and stops sending the box's own ``loaded_slot`` after
|
|
1368
|
+
the first few changes of a print, which is why the loaded slot froze
|
|
1369
|
+
(#34). Each box numbers its channels from 1; an ACE adds ``unit_no``
|
|
1370
|
+
and the printer's own feeder sends none (#33, Kobra X + ACE 2 Pro).
|
|
1371
|
+
|
|
1372
|
+
With one box the channel can only mean that box. With several, an
|
|
1373
|
+
ACE's ``unit_no`` picks it on a printer whose boxes are all ACEs.
|
|
1374
|
+
On a Kobra X with an ACE, which list entry is the printer's own
|
|
1375
|
+
feeder is not yet confirmed, so nothing is guessed there: a wrong
|
|
1376
|
+
slot is worse than a stale one.
|
|
1377
|
+
"""
|
|
1378
|
+
channel = as_int(channel_no, 0)
|
|
1379
|
+
boxes = self._multi_color_box
|
|
1380
|
+
|
|
1381
|
+
if channel < 1 or not boxes:
|
|
1382
|
+
return
|
|
1383
|
+
|
|
1384
|
+
if len(boxes) == 1:
|
|
1385
|
+
box = boxes[0]
|
|
1386
|
+
else:
|
|
1387
|
+
unit = as_int(unit_no, 0)
|
|
1388
|
+
if unit < 1 or unit > len(boxes) or self._machine_type in _PRINTERS_WITH_OWN_FEEDER:
|
|
1389
|
+
return
|
|
1390
|
+
box = boxes[unit - 1]
|
|
1391
|
+
|
|
1392
|
+
if channel <= len(box.slots):
|
|
1393
|
+
box.set_slot_loaded(channel - 1)
|
|
1394
|
+
|
|
1395
|
+
def _apply_print_progress(
|
|
1396
|
+
self,
|
|
1397
|
+
project_id: int,
|
|
1398
|
+
data: AnycubicConsumableData,
|
|
1399
|
+
) -> None:
|
|
1400
|
+
"""Apply a progress-only print update to the job it is about.
|
|
1401
|
+
|
|
1402
|
+
Sent without a task id, it can only be about the job in hand. With
|
|
1403
|
+
no job to apply it to it is dropped, as the cloud path always has.
|
|
1404
|
+
"""
|
|
1405
|
+
if not self._check_latest_project_id_valid(project_id):
|
|
1406
|
+
data.force_empty()
|
|
1407
|
+
return
|
|
1408
|
+
|
|
1409
|
+
assert self._latest_project
|
|
1410
|
+
self._latest_project.update_with_mqtt_print_status_data(None, data)
|
|
1411
|
+
|
|
1412
|
+
def _report_refused_drying(
|
|
1413
|
+
self,
|
|
1414
|
+
state: str | None,
|
|
1415
|
+
payload: AnycubicConsumableData,
|
|
1416
|
+
) -> bool:
|
|
1417
|
+
"""Say so when the printer will not start a dry cycle.
|
|
1418
|
+
|
|
1419
|
+
A start the ACE refuses -- 65 C on an ACE Pro that tops out at 55 is
|
|
1420
|
+
code 11505 -- came back as a setDry reply that was not a success. It
|
|
1421
|
+
was thrown away as "not understood", so the button press looked like
|
|
1422
|
+
it had worked and nothing in Home Assistant said otherwise (#42).
|
|
1423
|
+
|
|
1424
|
+
The code is kept as the printer's latest error, the same as any other
|
|
1425
|
+
fault, so the last-error sensors show it too. A reply that is neither
|
|
1426
|
+
a fault code nor a failed state is left to be reported as unknown.
|
|
1427
|
+
"""
|
|
1428
|
+
data = payload.remaining_data.get('data')
|
|
1429
|
+
code = payload.remaining_data.get('code')
|
|
1430
|
+
|
|
1431
|
+
if isinstance(data, AnycubicConsumableData) and 'code' in data:
|
|
1432
|
+
code = data.remaining_data.get('code')
|
|
1433
|
+
|
|
1434
|
+
if not isinstance(code, int) or isinstance(code, bool) or code in PRINTER_OK_CODES:
|
|
1435
|
+
code = None
|
|
1436
|
+
|
|
1437
|
+
if code is None and str(state or '').lower() not in _REFUSED_STATES:
|
|
1438
|
+
return False
|
|
1439
|
+
|
|
1440
|
+
message = payload.get('msg')
|
|
1441
|
+
|
|
1442
|
+
if code is not None:
|
|
1443
|
+
self._record_error_code(code, message)
|
|
1444
|
+
reason = f"code {code}: {describe_printer_code(code)}"
|
|
1445
|
+
else:
|
|
1446
|
+
reason = str(message or state or 'no reason given')
|
|
1447
|
+
|
|
1448
|
+
self._api_parent._log_to_warn(
|
|
1449
|
+
f"The printer refused to start drying on the ACE ({reason})."
|
|
1450
|
+
)
|
|
1451
|
+
|
|
1452
|
+
payload.get('data')
|
|
1453
|
+
if isinstance(data, AnycubicConsumableData):
|
|
1454
|
+
data.force_empty()
|
|
1455
|
+
|
|
1456
|
+
return True
|
|
1457
|
+
|
|
1299
1458
|
def _process_mqtt_update_multicolorbox(
|
|
1300
1459
|
self,
|
|
1301
1460
|
action: str,
|
|
@@ -1337,8 +1496,14 @@ class AnycubicPrinter:
|
|
|
1337
1496
|
continue
|
|
1338
1497
|
|
|
1339
1498
|
assert self._multi_color_box
|
|
1340
|
-
|
|
1341
|
-
|
|
1499
|
+
# An ACE 2 Pro's periodic drying report carries no `temp`, and
|
|
1500
|
+
# reading it raised every six minutes, so drying never updated
|
|
1501
|
+
# (#33). Keep the last reading rather than inventing one.
|
|
1502
|
+
if 'temp' in box:
|
|
1503
|
+
self._multi_color_box[box_id].set_current_temperature(box['temp'])
|
|
1504
|
+
self._multi_color_box[box_id].set_drying_status(box.get('drying_status'))
|
|
1505
|
+
return
|
|
1506
|
+
elif action == 'setDry' and self._report_refused_drying(state, payload):
|
|
1342
1507
|
return
|
|
1343
1508
|
elif action == 'feedFilament' and state == 'done':
|
|
1344
1509
|
data = payload['data']['multi_color_box']
|
|
@@ -1734,6 +1899,10 @@ class AnycubicPrinter:
|
|
|
1734
1899
|
remaining_data.get('taskid')
|
|
1735
1900
|
remaining_data.get('localtask')
|
|
1736
1901
|
|
|
1902
|
+
if msg_type == 'print':
|
|
1903
|
+
for key in _PRINT_FILE_KEYS:
|
|
1904
|
+
remaining_data.get(key)
|
|
1905
|
+
|
|
1737
1906
|
payload.get('data')
|
|
1738
1907
|
payload.get('msg')
|
|
1739
1908
|
payload.get('timestamp')
|
|
@@ -46,6 +46,15 @@ PRINT_STATES_NOT_IN_PROGRESS = frozenset({
|
|
|
46
46
|
'stopped',
|
|
47
47
|
})
|
|
48
48
|
|
|
49
|
+
# The speed presets an FDM printer takes over its own network, in the shape
|
|
50
|
+
# the cloud sends them. 1, 2 and 3 are what a Kobra S1 and a Kobra 4 report
|
|
51
|
+
# and accept; a printer that sends its own list is never overridden.
|
|
52
|
+
LAN_PRINT_SPEED_MODES: tuple[dict[str, Any], ...] = (
|
|
53
|
+
{'title': 'Silent', 'print_speed_mode': 1},
|
|
54
|
+
{'title': 'Standard', 'print_speed_mode': 2},
|
|
55
|
+
{'title': 'Sport', 'print_speed_mode': 3},
|
|
56
|
+
)
|
|
57
|
+
|
|
49
58
|
|
|
50
59
|
class AnycubicProject:
|
|
51
60
|
__slots__ = (
|
|
@@ -305,6 +314,13 @@ class AnycubicProject:
|
|
|
305
314
|
project_type=data.get('project_type'),
|
|
306
315
|
localtask=data.get('localtask'),
|
|
307
316
|
print_speed_mode=data.get('print_speed_mode'),
|
|
317
|
+
# The cloud sends the printer's speed presets with the job; the
|
|
318
|
+
# printer does not. Without a list the speed select stayed
|
|
319
|
+
# unavailable through every LAN print, though the printer takes
|
|
320
|
+
# the change locally as the slicer shows (discussion #37).
|
|
321
|
+
available_print_speed_modes=(
|
|
322
|
+
data.get('print_speed_model_des') or list(LAN_PRINT_SPEED_MODES)
|
|
323
|
+
),
|
|
308
324
|
)
|
|
309
325
|
|
|
310
326
|
@classmethod
|
{anycubic_cloud_api-0.4.33 → anycubic_cloud_api-0.4.35}/src/anycubic_cloud_api.egg-info/SOURCES.txt
RENAMED
|
@@ -47,12 +47,14 @@ tests/test_auth_mode_fallback.py
|
|
|
47
47
|
tests/test_axis_position.py
|
|
48
48
|
tests/test_endpoint_urls.py
|
|
49
49
|
tests/test_exchange_rate_limit.py
|
|
50
|
+
tests/test_filament_change_0435.py
|
|
50
51
|
tests/test_lan_client.py
|
|
51
52
|
tests/test_lan_commands.py
|
|
52
53
|
tests/test_lan_handshake.py
|
|
53
54
|
tests/test_lan_reports.py
|
|
54
55
|
tests/test_null_tolerant_parsing.py
|
|
55
56
|
tests/test_partial_reports.py
|
|
57
|
+
tests/test_print_reports_0434.py
|
|
56
58
|
tests/test_printer_error_codes.py
|
|
57
59
|
tests/test_project_null_status.py
|
|
58
60
|
tests/test_regions.py
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
"""Filament-change reports and the ACE 2 Pro's drying report (0.4.35).
|
|
2
|
+
|
|
3
|
+
From a Kobra X with an ACE 2 Pro (#33). Each step of a filament change
|
|
4
|
+
arrives as a print ``start`` naming ``channel_no``, counted from 1 within
|
|
5
|
+
its box, plus ``unit_no`` when the feed comes from an ACE. These were
|
|
6
|
+
dropped as unhandled. The ACE 2 Pro's periodic drying report has no
|
|
7
|
+
``temp``, and reading it raised every six minutes.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from unittest.mock import MagicMock
|
|
11
|
+
|
|
12
|
+
import pytest
|
|
13
|
+
|
|
14
|
+
from anycubic_cloud_api.data_models.consumable import AnycubicConsumableData
|
|
15
|
+
from anycubic_cloud_api.data_models.printer import AnycubicPrinter
|
|
16
|
+
|
|
17
|
+
KOBRA_X = 20030
|
|
18
|
+
KOBRA_S1 = 20025
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def slot(index):
|
|
22
|
+
return {"index": index, "sku": "", "type": "PLA", "color": [1, 2, 3], "status": 5, "edit_status": 1}
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def box(box_id, slots=4):
|
|
26
|
+
return {"id": box_id, "loaded_slot": -1, "temp": 21, "slots": [slot(i) for i in range(slots)]}
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def make_printer(boxes, machine_type=KOBRA_X):
|
|
30
|
+
return AnycubicPrinter(
|
|
31
|
+
api_parent=MagicMock(),
|
|
32
|
+
machine_type=machine_type,
|
|
33
|
+
machine_name="Printer",
|
|
34
|
+
id=1,
|
|
35
|
+
multi_color_box=boxes,
|
|
36
|
+
ignore_init_errors=True,
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def message(msg_type, action, state, data):
|
|
41
|
+
return AnycubicConsumableData({
|
|
42
|
+
"type": msg_type, "action": action, "state": state,
|
|
43
|
+
"code": 200, "msg": "done", "msgid": "m", "timestamp": 1, "data": data,
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def topic(machine_type, kind):
|
|
48
|
+
return f"anycubic/anycubicCloud/v1/printer/public/{machine_type}/key/{kind}/report"
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def feed(printer, state, channel_no, unit_no=None, machine_type=KOBRA_X):
|
|
52
|
+
data = {"channel_no": channel_no, "taskid": "t"}
|
|
53
|
+
if unit_no is not None:
|
|
54
|
+
data["unit_no"] = unit_no
|
|
55
|
+
printer.process_mqtt_update(topic(machine_type, "print"), message("print", "start", state, data))
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def loaded(printer):
|
|
59
|
+
return [b.loaded_slot for b in printer.multi_color_box]
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class TestFilamentChange:
|
|
63
|
+
@pytest.mark.parametrize(("state", "unit_no"), [
|
|
64
|
+
("extfilbox_filament_feeding", None),
|
|
65
|
+
("ace_filament_feeding", 1),
|
|
66
|
+
])
|
|
67
|
+
def test_one_box_follows_the_feed(self, state, unit_no):
|
|
68
|
+
printer = make_printer([box(0)])
|
|
69
|
+
|
|
70
|
+
feed(printer, state, 3, unit_no)
|
|
71
|
+
|
|
72
|
+
assert loaded(printer) == [2]
|
|
73
|
+
|
|
74
|
+
@pytest.mark.parametrize("state", [
|
|
75
|
+
"extfilbox_filament_unfeeding", "ace_filament_unfeeding", "nozzle_purging",
|
|
76
|
+
])
|
|
77
|
+
def test_other_steps_are_read_but_change_nothing(self, state):
|
|
78
|
+
"""Consumed, so not logged as unhandled; only a feed says what is loaded."""
|
|
79
|
+
printer = make_printer([box(0)])
|
|
80
|
+
printer.multi_color_box[0].set_slot_loaded(1)
|
|
81
|
+
|
|
82
|
+
feed(printer, state, 3, 1)
|
|
83
|
+
|
|
84
|
+
assert loaded(printer) == [1]
|
|
85
|
+
|
|
86
|
+
@pytest.mark.parametrize(("unit_no", "expected"), [(1, [1, -1]), (2, [-1, 1])])
|
|
87
|
+
def test_an_ace_unit_picks_its_box_on_a_printer_of_aces(self, unit_no, expected):
|
|
88
|
+
"""A Kobra S1 has no feeder of its own: unit 1 is the first ACE."""
|
|
89
|
+
printer = make_printer([box(0), box(1)], machine_type=KOBRA_S1)
|
|
90
|
+
|
|
91
|
+
feed(printer, "ace_filament_feeding", 2, unit_no, machine_type=KOBRA_S1)
|
|
92
|
+
|
|
93
|
+
assert loaded(printer) == expected
|
|
94
|
+
|
|
95
|
+
@pytest.mark.parametrize(("state", "unit_no"), [
|
|
96
|
+
("extfilbox_filament_feeding", None),
|
|
97
|
+
("ace_filament_feeding", 1),
|
|
98
|
+
])
|
|
99
|
+
def test_a_kobra_x_with_an_ace_guesses_nothing(self, state, unit_no):
|
|
100
|
+
"""Which list entry is the Kobra X's own feeder is unconfirmed (#33)."""
|
|
101
|
+
printer = make_printer([box(0), box(1)])
|
|
102
|
+
|
|
103
|
+
feed(printer, state, 1, unit_no)
|
|
104
|
+
|
|
105
|
+
assert loaded(printer) == [-1, -1]
|
|
106
|
+
|
|
107
|
+
@pytest.mark.parametrize(("channel_no", "unit_no"), [(5, 1), (0, 1), (2, 3), ("x", 1), (2, None)])
|
|
108
|
+
def test_a_channel_or_unit_that_fits_no_box_changes_nothing(self, channel_no, unit_no):
|
|
109
|
+
printer = make_printer([box(0), box(1)], machine_type=KOBRA_S1)
|
|
110
|
+
|
|
111
|
+
feed(printer, "ace_filament_feeding", channel_no, unit_no, machine_type=KOBRA_S1)
|
|
112
|
+
|
|
113
|
+
assert loaded(printer) == [-1, -1]
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
class TestAce2ProDryingReport:
|
|
117
|
+
REPORT = {"multi_color_box": [{
|
|
118
|
+
"drying_status": {"duration": 14400, "humidity": 28, "remain_time": 162, "status": 1, "target_temp": 50},
|
|
119
|
+
"id": 0,
|
|
120
|
+
}]}
|
|
121
|
+
|
|
122
|
+
def test_a_report_without_temp_still_updates_drying(self):
|
|
123
|
+
printer = make_printer([box(0)])
|
|
124
|
+
|
|
125
|
+
printer.process_mqtt_update(
|
|
126
|
+
topic(KOBRA_X, "multiColorBox"),
|
|
127
|
+
message("multiColorBox", "autoUpdateDryStatus", "success", self.REPORT),
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
ace = printer.multi_color_box[0]
|
|
131
|
+
assert ace.drying_status.is_drying
|
|
132
|
+
assert ace.drying_status.target_temperature == 50
|
|
133
|
+
assert ace.current_temperature == 21 # the last reading is kept
|
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
"""Print and ACE reports that were being dropped or misread (0.4.34).
|
|
2
|
+
|
|
3
|
+
From a Kobra X (#34) and a Kobra S1 with two ACE Pro units (#42): the active
|
|
4
|
+
filament channel arrived in print updates nobody read, progress-only updates
|
|
5
|
+
were logged as unhandled, a refused dry cycle passed silently, and a LAN job
|
|
6
|
+
had no speed presets for the speed select to offer (discussion #37).
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from unittest.mock import MagicMock
|
|
10
|
+
|
|
11
|
+
import pytest
|
|
12
|
+
|
|
13
|
+
from anycubic_cloud_api.data_models.consumable import AnycubicConsumableData
|
|
14
|
+
from anycubic_cloud_api.data_models.printer import AnycubicPrinter
|
|
15
|
+
from anycubic_cloud_api.data_models.project import AnycubicProject
|
|
16
|
+
from anycubic_cloud_api.exceptions.exceptions import AnycubicMQTTUnknownUpdate
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def slot(index):
|
|
20
|
+
return {
|
|
21
|
+
"index": index,
|
|
22
|
+
"sku": "",
|
|
23
|
+
"type": "PLA",
|
|
24
|
+
"color": [1, 2, 3],
|
|
25
|
+
"status": 5,
|
|
26
|
+
"edit_status": 1,
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def box(box_id, slots=4):
|
|
31
|
+
return {
|
|
32
|
+
"id": box_id,
|
|
33
|
+
"loaded_slot": -1,
|
|
34
|
+
"slots": [slot(i) for i in range(slots)],
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def make_printer(boxes, parent=None):
|
|
39
|
+
return AnycubicPrinter(
|
|
40
|
+
api_parent=parent or MagicMock(),
|
|
41
|
+
machine_type=20030,
|
|
42
|
+
machine_name="Kobra X",
|
|
43
|
+
id=1,
|
|
44
|
+
multi_color_box=boxes,
|
|
45
|
+
ignore_init_errors=True,
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def message(msg_type, action, state, data, **envelope):
|
|
50
|
+
return AnycubicConsumableData({
|
|
51
|
+
"type": msg_type,
|
|
52
|
+
"action": action,
|
|
53
|
+
"state": state,
|
|
54
|
+
"msgid": "m",
|
|
55
|
+
"timestamp": 1,
|
|
56
|
+
"data": data,
|
|
57
|
+
**envelope,
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def lan_job(task_id=42, **fields):
|
|
62
|
+
return {
|
|
63
|
+
"task_id": task_id,
|
|
64
|
+
"filename": "cube.gcode.3mf",
|
|
65
|
+
"progress": 0,
|
|
66
|
+
"print_status": 1,
|
|
67
|
+
**fields,
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
TOPIC = "anycubic/anycubicCloud/v1/printer/public/20030/key/print/report"
|
|
72
|
+
ACE_TOPIC = "anycubic/anycubicCloud/v1/printer/public/20030/key/multiColorBox/report"
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
class TestActiveChannel:
|
|
76
|
+
def _channel(self, printer, channel_no):
|
|
77
|
+
printer.process_mqtt_update(
|
|
78
|
+
TOPIC,
|
|
79
|
+
message(
|
|
80
|
+
"print", "update", "updated",
|
|
81
|
+
{"channel_no": channel_no, "settings": {"light": 0}},
|
|
82
|
+
),
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
@pytest.mark.parametrize(("channel_no", "slot_index"), [(1, 0), (3, 2), (4, 3)])
|
|
86
|
+
def test_a_channel_change_moves_the_loaded_slot(self, channel_no, slot_index):
|
|
87
|
+
printer = make_printer([box(0)])
|
|
88
|
+
|
|
89
|
+
self._channel(printer, channel_no)
|
|
90
|
+
|
|
91
|
+
assert printer.primary_multi_color_box_loaded_slot == slot_index
|
|
92
|
+
|
|
93
|
+
@pytest.mark.parametrize("channel_no", [1, 5])
|
|
94
|
+
def test_with_two_boxes_and_no_unit_nothing_is_guessed(self, channel_no):
|
|
95
|
+
"""Channels are numbered per box (#33), so without a unit the box is unknown.
|
|
96
|
+
|
|
97
|
+
0.4.34 counted across boxes and put channel 5 in the second box.
|
|
98
|
+
"""
|
|
99
|
+
printer = make_printer([box(0), box(1)])
|
|
100
|
+
before = [b.loaded_slot for b in printer.multi_color_box]
|
|
101
|
+
|
|
102
|
+
self._channel(printer, channel_no)
|
|
103
|
+
|
|
104
|
+
assert [b.loaded_slot for b in printer.multi_color_box] == before
|
|
105
|
+
|
|
106
|
+
@pytest.mark.parametrize("channel_no", [0, -1, 9, None, "x"])
|
|
107
|
+
def test_a_channel_with_no_slot_changes_nothing(self, channel_no):
|
|
108
|
+
printer = make_printer([box(0)])
|
|
109
|
+
printer._multi_color_box[0].set_slot_loaded(1)
|
|
110
|
+
|
|
111
|
+
self._channel(printer, channel_no)
|
|
112
|
+
|
|
113
|
+
assert printer.primary_multi_color_box_loaded_slot == 1
|
|
114
|
+
|
|
115
|
+
def test_a_printer_with_no_ace_ignores_it(self):
|
|
116
|
+
printer = make_printer(None)
|
|
117
|
+
|
|
118
|
+
self._channel(printer, 2)
|
|
119
|
+
|
|
120
|
+
assert printer.connected_ace_units == 0
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
class TestProgressUpdates:
|
|
124
|
+
PROGRESS = {
|
|
125
|
+
"curr_layer": 23,
|
|
126
|
+
"file_root_path": "cube.gcode.3mf",
|
|
127
|
+
"filename": "cube.gcode.3mf",
|
|
128
|
+
"filetype": 0,
|
|
129
|
+
"print_time": 7,
|
|
130
|
+
"progress": 10,
|
|
131
|
+
"remain_time": 66,
|
|
132
|
+
"settings": {"light": 0},
|
|
133
|
+
"supplies_usage": 1074,
|
|
134
|
+
"total_layers": 263,
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
def test_progress_without_a_task_id_reaches_the_job(self):
|
|
138
|
+
printer = make_printer([box(0)])
|
|
139
|
+
printer._apply_lan_project(AnycubicConsumableData(lan_job()))
|
|
140
|
+
|
|
141
|
+
printer.process_mqtt_update(
|
|
142
|
+
TOPIC, message("print", "update", "updated", dict(self.PROGRESS))
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
project = printer.latest_project
|
|
146
|
+
assert project is not None
|
|
147
|
+
assert project.progress_percentage == 10
|
|
148
|
+
assert project.print_time_remaining_minutes == 66
|
|
149
|
+
|
|
150
|
+
def test_progress_with_no_job_is_dropped_quietly(self):
|
|
151
|
+
printer = make_printer([box(0)])
|
|
152
|
+
|
|
153
|
+
printer.process_mqtt_update(
|
|
154
|
+
TOPIC, message("print", "update", "updated", dict(self.PROGRESS))
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
assert printer.latest_project is None
|
|
158
|
+
|
|
159
|
+
def test_progress_for_another_job_is_not_applied(self):
|
|
160
|
+
printer = make_printer([box(0)])
|
|
161
|
+
printer._apply_lan_project(AnycubicConsumableData(lan_job(task_id=42)))
|
|
162
|
+
|
|
163
|
+
printer.process_mqtt_update(
|
|
164
|
+
TOPIC,
|
|
165
|
+
message("print", "update", "updated", {"taskid": 7, **self.PROGRESS}),
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
assert printer.latest_project.progress_percentage == 0
|
|
169
|
+
|
|
170
|
+
def test_the_s1_file_fields_are_not_left_over(self):
|
|
171
|
+
"""These four were reported as unhandled every 30-60 s (#42)."""
|
|
172
|
+
printer = make_printer([box(0)])
|
|
173
|
+
printer._apply_lan_project(AnycubicConsumableData(lan_job()))
|
|
174
|
+
|
|
175
|
+
printer.process_mqtt_update(
|
|
176
|
+
TOPIC,
|
|
177
|
+
message(
|
|
178
|
+
"print", "update", "updated",
|
|
179
|
+
{
|
|
180
|
+
"display_filename": "cube",
|
|
181
|
+
"origin3mf": "cube.3mf",
|
|
182
|
+
"source_info": "slicer",
|
|
183
|
+
"temp_gcode": "/tmp/x",
|
|
184
|
+
"progress": 50,
|
|
185
|
+
},
|
|
186
|
+
),
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
assert printer.latest_project.progress_percentage == 50
|
|
190
|
+
|
|
191
|
+
def test_a_print_start_with_file_fields_is_handled(self):
|
|
192
|
+
printer = make_printer([box(0)])
|
|
193
|
+
printer._apply_lan_project(AnycubicConsumableData(lan_job()))
|
|
194
|
+
|
|
195
|
+
printer.process_mqtt_update(
|
|
196
|
+
TOPIC,
|
|
197
|
+
message(
|
|
198
|
+
"print", "start", "printing",
|
|
199
|
+
{"file_root_path": "cube.gcode.3mf", "filetype": 0},
|
|
200
|
+
),
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
class TestRefusedDrying:
|
|
205
|
+
def _printer(self):
|
|
206
|
+
parent = MagicMock()
|
|
207
|
+
logs: list[str] = []
|
|
208
|
+
parent._log_to_warn = logs.append
|
|
209
|
+
return make_printer([box(0), box(1)], parent), logs
|
|
210
|
+
|
|
211
|
+
def test_a_refusal_code_is_reported_and_kept(self):
|
|
212
|
+
printer, logs = self._printer()
|
|
213
|
+
|
|
214
|
+
printer.process_mqtt_update(
|
|
215
|
+
ACE_TOPIC,
|
|
216
|
+
message("multiColorBox", "setDry", "failed", {}, code=11505, msg="fail"),
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
assert len(logs) == 1
|
|
220
|
+
assert "11505" in logs[0]
|
|
221
|
+
assert "target temperature" in logs[0]
|
|
222
|
+
assert printer.latest_error_code == 11505
|
|
223
|
+
|
|
224
|
+
def test_a_code_inside_the_data_counts_too(self):
|
|
225
|
+
printer, logs = self._printer()
|
|
226
|
+
|
|
227
|
+
printer.process_mqtt_update(
|
|
228
|
+
ACE_TOPIC,
|
|
229
|
+
message("multiColorBox", "setDry", "done", {"code": 11505}, code=200),
|
|
230
|
+
)
|
|
231
|
+
|
|
232
|
+
assert "11505" in logs[0]
|
|
233
|
+
assert printer.latest_error_code == 11505
|
|
234
|
+
|
|
235
|
+
def test_a_failed_state_without_a_code_is_still_reported(self):
|
|
236
|
+
printer, logs = self._printer()
|
|
237
|
+
|
|
238
|
+
printer.process_mqtt_update(
|
|
239
|
+
ACE_TOPIC,
|
|
240
|
+
message("multiColorBox", "setDry", "failed", None, code=200, msg="busy"),
|
|
241
|
+
)
|
|
242
|
+
|
|
243
|
+
assert logs == ["The printer refused to start drying on the ACE (busy)."]
|
|
244
|
+
|
|
245
|
+
def test_an_unfamiliar_reply_is_not_called_a_refusal(self):
|
|
246
|
+
printer, logs = self._printer()
|
|
247
|
+
|
|
248
|
+
with pytest.raises(AnycubicMQTTUnknownUpdate):
|
|
249
|
+
printer.process_mqtt_update(
|
|
250
|
+
ACE_TOPIC,
|
|
251
|
+
message("multiColorBox", "setDry", "doing", {}, code=200),
|
|
252
|
+
)
|
|
253
|
+
|
|
254
|
+
assert logs == []
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
class TestLanSpeedModes:
|
|
258
|
+
def test_a_lan_job_offers_the_standard_presets(self):
|
|
259
|
+
project = AnycubicProject.from_lan_project(MagicMock(), 1, lan_job())
|
|
260
|
+
|
|
261
|
+
assert project is not None
|
|
262
|
+
assert [m.mode for m in project.available_print_speed_modes] == [1, 2, 3]
|
|
263
|
+
project.validate_print_speed_mode(3)
|
|
264
|
+
|
|
265
|
+
def test_a_list_the_printer_sends_is_used_instead(self):
|
|
266
|
+
project = AnycubicProject.from_lan_project(
|
|
267
|
+
MagicMock(),
|
|
268
|
+
1,
|
|
269
|
+
lan_job(print_speed_model_des=[{"title": "Ludicrous", "print_speed_mode": 4}]),
|
|
270
|
+
)
|
|
271
|
+
|
|
272
|
+
assert project is not None
|
|
273
|
+
assert [m.title for m in project.available_print_speed_modes] == ["Ludicrous"]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{anycubic_cloud_api-0.4.33 → anycubic_cloud_api-0.4.35}/src/anycubic_cloud_api/anycubic_api.py
RENAMED
|
File without changes
|
|
File without changes
|
{anycubic_cloud_api-0.4.33 → anycubic_cloud_api-0.4.35}/src/anycubic_cloud_api/api/functions.py
RENAMED
|
File without changes
|
|
File without changes
|
{anycubic_cloud_api-0.4.33 → anycubic_cloud_api-0.4.35}/src/anycubic_cloud_api/const/const.py
RENAMED
|
File without changes
|
{anycubic_cloud_api-0.4.33 → anycubic_cloud_api-0.4.35}/src/anycubic_cloud_api/const/enums.py
RENAMED
|
File without changes
|
{anycubic_cloud_api-0.4.33 → anycubic_cloud_api-0.4.35}/src/anycubic_cloud_api/const/error_codes.py
RENAMED
|
File without changes
|
|
File without changes
|
{anycubic_cloud_api-0.4.33 → anycubic_cloud_api-0.4.35}/src/anycubic_cloud_api/const/mqtt.py
RENAMED
|
File without changes
|
{anycubic_cloud_api-0.4.33 → anycubic_cloud_api-0.4.35}/src/anycubic_cloud_api/const/regions.py
RENAMED
|
File without changes
|
|
File without changes
|
{anycubic_cloud_api-0.4.33 → anycubic_cloud_api-0.4.35}/src/anycubic_cloud_api/data_models/files.py
RENAMED
|
File without changes
|
|
File without changes
|
{anycubic_cloud_api-0.4.33 → anycubic_cloud_api-0.4.35}/src/anycubic_cloud_api/data_models/orders.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{anycubic_cloud_api-0.4.33 → anycubic_cloud_api-0.4.35}/src/anycubic_cloud_api/helpers/helpers.py
RENAMED
|
File without changes
|
{anycubic_cloud_api-0.4.33 → anycubic_cloud_api-0.4.35}/src/anycubic_cloud_api/lan/__init__.py
RENAMED
|
File without changes
|
{anycubic_cloud_api-0.4.33 → anycubic_cloud_api-0.4.35}/src/anycubic_cloud_api/lan/client.py
RENAMED
|
File without changes
|
{anycubic_cloud_api-0.4.33 → anycubic_cloud_api-0.4.35}/src/anycubic_cloud_api/lan/commands.py
RENAMED
|
File without changes
|
{anycubic_cloud_api-0.4.33 → anycubic_cloud_api-0.4.35}/src/anycubic_cloud_api/lan/handshake.py
RENAMED
|
File without changes
|
{anycubic_cloud_api-0.4.33 → anycubic_cloud_api-0.4.35}/src/anycubic_cloud_api/models/auth.py
RENAMED
|
File without changes
|
|
File without changes
|
{anycubic_cloud_api-0.4.33 → anycubic_cloud_api-0.4.35}/src/anycubic_cloud_api/models/http.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{anycubic_cloud_api-0.4.33 → anycubic_cloud_api-0.4.35}/src/anycubic_cloud_api.egg-info/requires.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|