anycubic-cloud-api 0.4.35__tar.gz → 0.4.36__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.35/src/anycubic_cloud_api.egg-info → anycubic_cloud_api-0.4.36}/PKG-INFO +1 -1
- {anycubic_cloud_api-0.4.35 → anycubic_cloud_api-0.4.36}/pyproject.toml +1 -1
- {anycubic_cloud_api-0.4.35 → anycubic_cloud_api-0.4.36}/src/anycubic_cloud_api/api/mqtt.py +6 -5
- {anycubic_cloud_api-0.4.35 → anycubic_cloud_api-0.4.36}/src/anycubic_cloud_api/data_models/printer.py +29 -0
- {anycubic_cloud_api-0.4.35 → anycubic_cloud_api-0.4.36}/src/anycubic_cloud_api/data_models/printer_properties.py +18 -0
- {anycubic_cloud_api-0.4.35 → anycubic_cloud_api-0.4.36}/src/anycubic_cloud_api/helpers/helpers.py +28 -0
- {anycubic_cloud_api-0.4.35 → anycubic_cloud_api-0.4.36/src/anycubic_cloud_api.egg-info}/PKG-INFO +1 -1
- {anycubic_cloud_api-0.4.35 → anycubic_cloud_api-0.4.36}/src/anycubic_cloud_api.egg-info/SOURCES.txt +1 -0
- anycubic_cloud_api-0.4.36/tests/test_printer_slot_0436.py +149 -0
- {anycubic_cloud_api-0.4.35 → anycubic_cloud_api-0.4.36}/COPYRIGHT +0 -0
- {anycubic_cloud_api-0.4.35 → anycubic_cloud_api-0.4.36}/LICENSE +0 -0
- {anycubic_cloud_api-0.4.35 → anycubic_cloud_api-0.4.36}/MANIFEST.in +0 -0
- {anycubic_cloud_api-0.4.35 → anycubic_cloud_api-0.4.36}/README.md +0 -0
- {anycubic_cloud_api-0.4.35 → anycubic_cloud_api-0.4.36}/setup.cfg +0 -0
- {anycubic_cloud_api-0.4.35 → anycubic_cloud_api-0.4.36}/src/anycubic_cloud_api/__init__.py +0 -0
- {anycubic_cloud_api-0.4.35 → anycubic_cloud_api-0.4.36}/src/anycubic_cloud_api/anycubic_api.py +0 -0
- {anycubic_cloud_api-0.4.35 → anycubic_cloud_api-0.4.36}/src/anycubic_cloud_api/api/base.py +0 -0
- {anycubic_cloud_api-0.4.35 → anycubic_cloud_api-0.4.36}/src/anycubic_cloud_api/api/functions.py +0 -0
- {anycubic_cloud_api-0.4.35 → anycubic_cloud_api-0.4.36}/src/anycubic_cloud_api/const/api_endpoints.py +0 -0
- {anycubic_cloud_api-0.4.35 → anycubic_cloud_api-0.4.36}/src/anycubic_cloud_api/const/const.py +0 -0
- {anycubic_cloud_api-0.4.35 → anycubic_cloud_api-0.4.36}/src/anycubic_cloud_api/const/enums.py +0 -0
- {anycubic_cloud_api-0.4.35 → anycubic_cloud_api-0.4.36}/src/anycubic_cloud_api/const/error_codes.py +0 -0
- {anycubic_cloud_api-0.4.35 → anycubic_cloud_api-0.4.36}/src/anycubic_cloud_api/const/lan.py +0 -0
- {anycubic_cloud_api-0.4.35 → anycubic_cloud_api-0.4.36}/src/anycubic_cloud_api/const/mqtt.py +0 -0
- {anycubic_cloud_api-0.4.35 → anycubic_cloud_api-0.4.36}/src/anycubic_cloud_api/const/regions.py +0 -0
- {anycubic_cloud_api-0.4.35 → anycubic_cloud_api-0.4.36}/src/anycubic_cloud_api/data_models/consumable.py +0 -0
- {anycubic_cloud_api-0.4.35 → anycubic_cloud_api-0.4.36}/src/anycubic_cloud_api/data_models/files.py +0 -0
- {anycubic_cloud_api-0.4.35 → anycubic_cloud_api-0.4.36}/src/anycubic_cloud_api/data_models/gcode_file.py +0 -0
- {anycubic_cloud_api-0.4.35 → anycubic_cloud_api-0.4.36}/src/anycubic_cloud_api/data_models/orders.py +0 -0
- {anycubic_cloud_api-0.4.35 → anycubic_cloud_api-0.4.36}/src/anycubic_cloud_api/data_models/print_response.py +0 -0
- {anycubic_cloud_api-0.4.35 → anycubic_cloud_api-0.4.36}/src/anycubic_cloud_api/data_models/print_speed_mode.py +0 -0
- {anycubic_cloud_api-0.4.35 → anycubic_cloud_api-0.4.36}/src/anycubic_cloud_api/data_models/printing_settings.py +0 -0
- {anycubic_cloud_api-0.4.35 → anycubic_cloud_api-0.4.36}/src/anycubic_cloud_api/data_models/project.py +0 -0
- {anycubic_cloud_api-0.4.35 → anycubic_cloud_api-0.4.36}/src/anycubic_cloud_api/exceptions/error_strings.py +0 -0
- {anycubic_cloud_api-0.4.35 → anycubic_cloud_api-0.4.36}/src/anycubic_cloud_api/exceptions/exceptions.py +0 -0
- {anycubic_cloud_api-0.4.35 → anycubic_cloud_api-0.4.36}/src/anycubic_cloud_api/lan/__init__.py +0 -0
- {anycubic_cloud_api-0.4.35 → anycubic_cloud_api-0.4.36}/src/anycubic_cloud_api/lan/client.py +0 -0
- {anycubic_cloud_api-0.4.35 → anycubic_cloud_api-0.4.36}/src/anycubic_cloud_api/lan/commands.py +0 -0
- {anycubic_cloud_api-0.4.35 → anycubic_cloud_api-0.4.36}/src/anycubic_cloud_api/lan/handshake.py +0 -0
- {anycubic_cloud_api-0.4.35 → anycubic_cloud_api-0.4.36}/src/anycubic_cloud_api/models/auth.py +0 -0
- {anycubic_cloud_api-0.4.35 → anycubic_cloud_api-0.4.36}/src/anycubic_cloud_api/models/cloud_upload.py +0 -0
- {anycubic_cloud_api-0.4.35 → anycubic_cloud_api-0.4.36}/src/anycubic_cloud_api/models/http.py +0 -0
- {anycubic_cloud_api-0.4.35 → anycubic_cloud_api-0.4.36}/src/anycubic_cloud_api/resources/anycubic_mqqt_tls_ca.crt +0 -0
- {anycubic_cloud_api-0.4.35 → anycubic_cloud_api-0.4.36}/src/anycubic_cloud_api/resources/anycubic_mqqt_tls_client.crt +0 -0
- {anycubic_cloud_api-0.4.35 → anycubic_cloud_api-0.4.36}/src/anycubic_cloud_api/resources/anycubic_mqqt_tls_client.key +0 -0
- {anycubic_cloud_api-0.4.35 → anycubic_cloud_api-0.4.36}/src/anycubic_cloud_api.egg-info/dependency_links.txt +0 -0
- {anycubic_cloud_api-0.4.35 → anycubic_cloud_api-0.4.36}/src/anycubic_cloud_api.egg-info/requires.txt +0 -0
- {anycubic_cloud_api-0.4.35 → anycubic_cloud_api-0.4.36}/src/anycubic_cloud_api.egg-info/top_level.txt +0 -0
- {anycubic_cloud_api-0.4.35 → anycubic_cloud_api-0.4.36}/tests/test_auth_mode_fallback.py +0 -0
- {anycubic_cloud_api-0.4.35 → anycubic_cloud_api-0.4.36}/tests/test_axis_position.py +0 -0
- {anycubic_cloud_api-0.4.35 → anycubic_cloud_api-0.4.36}/tests/test_endpoint_urls.py +0 -0
- {anycubic_cloud_api-0.4.35 → anycubic_cloud_api-0.4.36}/tests/test_exchange_rate_limit.py +0 -0
- {anycubic_cloud_api-0.4.35 → anycubic_cloud_api-0.4.36}/tests/test_filament_change_0435.py +0 -0
- {anycubic_cloud_api-0.4.35 → anycubic_cloud_api-0.4.36}/tests/test_lan_client.py +0 -0
- {anycubic_cloud_api-0.4.35 → anycubic_cloud_api-0.4.36}/tests/test_lan_commands.py +0 -0
- {anycubic_cloud_api-0.4.35 → anycubic_cloud_api-0.4.36}/tests/test_lan_handshake.py +0 -0
- {anycubic_cloud_api-0.4.35 → anycubic_cloud_api-0.4.36}/tests/test_lan_reports.py +0 -0
- {anycubic_cloud_api-0.4.35 → anycubic_cloud_api-0.4.36}/tests/test_null_tolerant_parsing.py +0 -0
- {anycubic_cloud_api-0.4.35 → anycubic_cloud_api-0.4.36}/tests/test_partial_reports.py +0 -0
- {anycubic_cloud_api-0.4.35 → anycubic_cloud_api-0.4.36}/tests/test_print_reports_0434.py +0 -0
- {anycubic_cloud_api-0.4.35 → anycubic_cloud_api-0.4.36}/tests/test_printer_error_codes.py +0 -0
- {anycubic_cloud_api-0.4.35 → anycubic_cloud_api-0.4.36}/tests/test_project_null_status.py +0 -0
- {anycubic_cloud_api-0.4.35 → anycubic_cloud_api-0.4.36}/tests/test_regions.py +0 -0
- {anycubic_cloud_api-0.4.35 → anycubic_cloud_api-0.4.36}/tests/test_secondary_ace.py +0 -0
|
@@ -42,6 +42,7 @@ from ..helpers.helpers import (
|
|
|
42
42
|
get_mqtt_ssl_path_key,
|
|
43
43
|
get_part_from_mqtt_topic,
|
|
44
44
|
get_ssl_cert_directory,
|
|
45
|
+
redact_for_log,
|
|
45
46
|
redact_part_from_mqtt_topic,
|
|
46
47
|
)
|
|
47
48
|
from .functions import AnycubicAPIFunctions
|
|
@@ -151,7 +152,7 @@ class AnycubicMQTTAPI(AnycubicAPIFunctions):
|
|
|
151
152
|
if self._mqtt_topic_is_user_topic(topic):
|
|
152
153
|
self._log_to_debug(
|
|
153
154
|
f"Anycubic MQTT USER Msg Received on `{topic}`:\n"
|
|
154
|
-
f" {payload}"
|
|
155
|
+
f" {redact_for_log(payload)}"
|
|
155
156
|
)
|
|
156
157
|
|
|
157
158
|
else:
|
|
@@ -182,7 +183,7 @@ class AnycubicMQTTAPI(AnycubicAPIFunctions):
|
|
|
182
183
|
f" with type: {e.unhandled_mqtt_type}, "
|
|
183
184
|
f"action: {e.unhandled_mqtt_action}, "
|
|
184
185
|
f"state: {e.unhandled_mqtt_state}\n"
|
|
185
|
-
f" unhandled data: {e.unhandled_mqtt_data}"
|
|
186
|
+
f" unhandled data: {redact_for_log(e.unhandled_mqtt_data)}"
|
|
186
187
|
)
|
|
187
188
|
|
|
188
189
|
except AnycubicMQTTUnknownUpdate as e:
|
|
@@ -193,7 +194,7 @@ class AnycubicMQTTAPI(AnycubicAPIFunctions):
|
|
|
193
194
|
self._log_to_debug(
|
|
194
195
|
f"Anycubic MQTT Message not understood: {e}\n"
|
|
195
196
|
f" on MQTT topic: {redacted_topic}\n"
|
|
196
|
-
f" {payload}"
|
|
197
|
+
f" {redact_for_log(payload)}"
|
|
197
198
|
)
|
|
198
199
|
|
|
199
200
|
except Exception as e:
|
|
@@ -203,7 +204,7 @@ class AnycubicMQTTAPI(AnycubicAPIFunctions):
|
|
|
203
204
|
self._log_to_error(
|
|
204
205
|
f"Anycubic MQTT Message error: {error_type}: {e}\n"
|
|
205
206
|
f" on MQTT topic: {redacted_topic}\n"
|
|
206
|
-
f" {payload}\n"
|
|
207
|
+
f" {redact_for_log(payload)}\n"
|
|
207
208
|
f"{tb}"
|
|
208
209
|
)
|
|
209
210
|
|
|
@@ -230,7 +231,7 @@ class AnycubicMQTTAPI(AnycubicAPIFunctions):
|
|
|
230
231
|
self._log_to_debug(
|
|
231
232
|
f"Anycubic MQTT Message processed on topic: "
|
|
232
233
|
f"{redact_part_from_mqtt_topic(topic, 6)}\n"
|
|
233
|
-
f" {payload}"
|
|
234
|
+
f" {redact_for_log(payload)}"
|
|
234
235
|
)
|
|
235
236
|
|
|
236
237
|
def _mqtt_publish_on_topic(
|
|
@@ -452,6 +452,12 @@ class AnycubicPrinter:
|
|
|
452
452
|
updated.get(box.box_id, box) for box in known_boxes
|
|
453
453
|
])
|
|
454
454
|
|
|
455
|
+
if known_boxes:
|
|
456
|
+
previous = {box.box_id: box for box in known_boxes}
|
|
457
|
+
for box in self._multi_color_box:
|
|
458
|
+
if (known := previous.get(box.box_id)) is not None and known is not box:
|
|
459
|
+
box.keep_printer_slot_from(known)
|
|
460
|
+
|
|
455
461
|
except Exception as e:
|
|
456
462
|
self._initialisation_error = True
|
|
457
463
|
# Swallowing this leaves the ACE list empty, which presents as
|
|
@@ -1542,9 +1548,32 @@ class AnycubicPrinter:
|
|
|
1542
1548
|
if self.external_shelves:
|
|
1543
1549
|
self.external_shelves.update_with_mqtt_data(data)
|
|
1544
1550
|
return
|
|
1551
|
+
elif action == 'auto' and state == 'done':
|
|
1552
|
+
# Sent as a filament change completes, naming the feeding slot as
|
|
1553
|
+
# "<box>-<slot>", both counted from 0 ("0-3" while the screen shows
|
|
1554
|
+
# slot 4 on a Kobra X, #34); empty while the filament is retracted.
|
|
1555
|
+
data = payload.get('data') or {}
|
|
1556
|
+
self._apply_current_filament(data.get('current_filament'))
|
|
1557
|
+
return
|
|
1545
1558
|
else:
|
|
1546
1559
|
raise AnycubicMQTTUnknownUpdate(ErrorsMQTTUpdate.shelves)
|
|
1547
1560
|
|
|
1561
|
+
def _apply_current_filament(self, current_filament: Any) -> None:
|
|
1562
|
+
"""Follow the printer's own "<box>-<slot>" reading, where the box is certain.
|
|
1563
|
+
|
|
1564
|
+
Only with a single box: which list entry the printer's box number
|
|
1565
|
+
means beside an ACE is not confirmed (#33). An empty reading is a
|
|
1566
|
+
retract for calibration or wiping, not a change of slot, so the last
|
|
1567
|
+
slot is kept.
|
|
1568
|
+
"""
|
|
1569
|
+
boxes = self._multi_color_box
|
|
1570
|
+
if not isinstance(current_filament, str) or not boxes or len(boxes) != 1:
|
|
1571
|
+
return
|
|
1572
|
+
box_no, _, slot_no = current_filament.partition('-')
|
|
1573
|
+
box_index, slot_index = as_int(box_no, -1), as_int(slot_no, -1)
|
|
1574
|
+
if box_index == 0 and 0 <= slot_index < len(boxes[0].slots):
|
|
1575
|
+
boxes[0].set_slot_loaded(slot_index)
|
|
1576
|
+
|
|
1548
1577
|
def _process_mqtt_update_file(
|
|
1549
1578
|
self,
|
|
1550
1579
|
action: str,
|
|
@@ -702,6 +702,7 @@ class AnycubicMultiColorBox:
|
|
|
702
702
|
"_model_id",
|
|
703
703
|
"_auto_feed",
|
|
704
704
|
"_loaded_slot",
|
|
705
|
+
"_slot_from_printer",
|
|
705
706
|
"_feed_status",
|
|
706
707
|
"_temp",
|
|
707
708
|
"_humidity",
|
|
@@ -734,6 +735,9 @@ class AnycubicMultiColorBox:
|
|
|
734
735
|
self._model_id: int = as_int(model_id)
|
|
735
736
|
self._auto_feed: int = as_int(auto_feed)
|
|
736
737
|
self._loaded_slot: int = as_int(loaded_slot, -1)
|
|
738
|
+
# Whether the printer itself has reported the loaded slot, as opposed
|
|
739
|
+
# to a cloud record that only knew it at some earlier point.
|
|
740
|
+
self._slot_from_printer = False
|
|
737
741
|
self.set_feed_status(feed_status)
|
|
738
742
|
self.set_current_temperature(temp)
|
|
739
743
|
self._humidity: float | None = (
|
|
@@ -759,7 +763,21 @@ class AnycubicMultiColorBox:
|
|
|
759
763
|
self._feed_status = AnycubicFeedStatus.from_json(feed_status)
|
|
760
764
|
|
|
761
765
|
def set_slot_loaded(self, slot_num: int) -> None:
|
|
766
|
+
"""A loaded slot reported by the printer (MQTT or LAN)."""
|
|
762
767
|
self._loaded_slot = as_int(slot_num, -1)
|
|
768
|
+
self._slot_from_printer = True
|
|
769
|
+
|
|
770
|
+
def keep_printer_slot_from(self, previous: AnycubicMultiColorBox) -> None:
|
|
771
|
+
"""Carry over a slot the printer reported, past a cloud refresh.
|
|
772
|
+
|
|
773
|
+
The cloud's record of the loaded slot goes stale during a print: a
|
|
774
|
+
Kobra X kept reporting slot 4 while each cloud poll said slot 2, and
|
|
775
|
+
the sensor flipped to 2 mid-print (#34). What the printer said last
|
|
776
|
+
wins until it says something else.
|
|
777
|
+
"""
|
|
778
|
+
if previous._slot_from_printer:
|
|
779
|
+
self._loaded_slot = previous._loaded_slot
|
|
780
|
+
self._slot_from_printer = True
|
|
763
781
|
|
|
764
782
|
def set_current_temperature(self, temp: int) -> None:
|
|
765
783
|
# Reached from the constructor as well as from MQTT, so a null
|
{anycubic_cloud_api-0.4.35 → anycubic_cloud_api-0.4.36}/src/anycubic_cloud_api/helpers/helpers.py
RENAMED
|
@@ -164,6 +164,34 @@ def get_part_from_mqtt_topic(topic: str, part: int) -> str | None:
|
|
|
164
164
|
return split_topic[part]
|
|
165
165
|
|
|
166
166
|
|
|
167
|
+
# Identifiers a printer puts in its own reports. A debug log gets pasted into
|
|
168
|
+
# public issues, and only the topic's printer key was being redacted: a
|
|
169
|
+
# Kobra X's PrintStart report carried its cn_code in the clear (#34).
|
|
170
|
+
_LOG_REDACT_KEYS = frozenset({
|
|
171
|
+
'cn_code', 'cn', 'sn', 'serial', 'machine_sn', 'mac', 'machine_mac',
|
|
172
|
+
'ip', 'key', 'printer_key', 'device_id', 'deviceId', 'user_id', 'email',
|
|
173
|
+
})
|
|
174
|
+
_LOG_REDACT_PARTS = ('token', 'secret', 'password')
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def redact_for_log(data: Any) -> Any:
|
|
178
|
+
"""A copy of an MQTT payload that is safe to put in a log."""
|
|
179
|
+
if isinstance(data, dict):
|
|
180
|
+
return {
|
|
181
|
+
key: (
|
|
182
|
+
"**REDACTED**"
|
|
183
|
+
if isinstance(key, str)
|
|
184
|
+
and data[key] not in (None, '')
|
|
185
|
+
and (key in _LOG_REDACT_KEYS or any(part in key.lower() for part in _LOG_REDACT_PARTS))
|
|
186
|
+
else redact_for_log(data[key])
|
|
187
|
+
)
|
|
188
|
+
for key in data
|
|
189
|
+
}
|
|
190
|
+
if isinstance(data, list):
|
|
191
|
+
return [redact_for_log(item) for item in data]
|
|
192
|
+
return data
|
|
193
|
+
|
|
194
|
+
|
|
167
195
|
def redact_part_from_mqtt_topic(topic: str, part: int) -> str:
|
|
168
196
|
split_topic = topic.split("/")
|
|
169
197
|
new_chunk = list()
|
{anycubic_cloud_api-0.4.35 → anycubic_cloud_api-0.4.36}/src/anycubic_cloud_api.egg-info/SOURCES.txt
RENAMED
|
@@ -56,6 +56,7 @@ tests/test_null_tolerant_parsing.py
|
|
|
56
56
|
tests/test_partial_reports.py
|
|
57
57
|
tests/test_print_reports_0434.py
|
|
58
58
|
tests/test_printer_error_codes.py
|
|
59
|
+
tests/test_printer_slot_0436.py
|
|
59
60
|
tests/test_project_null_status.py
|
|
60
61
|
tests/test_regions.py
|
|
61
62
|
tests/test_secondary_ace.py
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
"""The printer's own loaded slot, and logs safe to paste (0.4.36).
|
|
2
|
+
|
|
3
|
+
From a Kobra X printing from slot 4 (#34): the sensor flipped to 2 mid-print
|
|
4
|
+
with no message from the printer. Each cloud poll rebuilt the box from the
|
|
5
|
+
cloud's record, whose loaded slot was stale. The printer also names the
|
|
6
|
+
feeding slot itself in its extfilbox report, and its PrintStart report put
|
|
7
|
+
the printer's cn_code into the debug log.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import pytest
|
|
11
|
+
from test_filament_change_0435 import (
|
|
12
|
+
KOBRA_S1,
|
|
13
|
+
KOBRA_X,
|
|
14
|
+
box,
|
|
15
|
+
make_printer,
|
|
16
|
+
message,
|
|
17
|
+
topic,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
from anycubic_cloud_api.helpers.helpers import redact_for_log
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def cloud_info(boxes, machine_type=KOBRA_X):
|
|
24
|
+
return {
|
|
25
|
+
"id": 1, "name": "Printer", "key": "k", "machine_type": machine_type, "model": "Printer",
|
|
26
|
+
"img": "", "device_status": 1, "is_printing": 2, "type_function_ids": [], "version": None,
|
|
27
|
+
"multi_color_box": boxes,
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def stale_box(box_id, loaded_slot):
|
|
32
|
+
b = box(box_id)
|
|
33
|
+
b["loaded_slot"] = loaded_slot
|
|
34
|
+
return b
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def loaded(printer):
|
|
38
|
+
return [b.loaded_slot for b in printer.multi_color_box]
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class TestCloudPollKeepsThePrintersSlot:
|
|
42
|
+
def test_a_slot_the_printer_reported_survives_a_cloud_poll(self):
|
|
43
|
+
printer = make_printer([box(0)])
|
|
44
|
+
printer.multi_color_box[0].set_slot_loaded(3)
|
|
45
|
+
|
|
46
|
+
printer.update_from_info_json(cloud_info([stale_box(0, 1)]))
|
|
47
|
+
|
|
48
|
+
assert loaded(printer) == [3]
|
|
49
|
+
|
|
50
|
+
def test_without_a_printer_report_the_cloud_value_is_used(self):
|
|
51
|
+
printer = make_printer([box(0)])
|
|
52
|
+
|
|
53
|
+
printer.update_from_info_json(cloud_info([stale_box(0, 1)]))
|
|
54
|
+
|
|
55
|
+
assert loaded(printer) == [1]
|
|
56
|
+
|
|
57
|
+
def test_the_next_printer_report_still_wins(self):
|
|
58
|
+
printer = make_printer([box(0)])
|
|
59
|
+
printer.multi_color_box[0].set_slot_loaded(3)
|
|
60
|
+
printer.update_from_info_json(cloud_info([stale_box(0, 1)]))
|
|
61
|
+
|
|
62
|
+
printer.multi_color_box[0].set_slot_loaded(0)
|
|
63
|
+
|
|
64
|
+
assert loaded(printer) == [0]
|
|
65
|
+
|
|
66
|
+
def test_each_box_keeps_its_own(self):
|
|
67
|
+
printer = make_printer([box(0), box(1)], machine_type=KOBRA_S1)
|
|
68
|
+
printer.multi_color_box[1].set_slot_loaded(2)
|
|
69
|
+
|
|
70
|
+
printer.update_from_info_json(cloud_info([stale_box(0, 1), stale_box(1, 0)], machine_type=KOBRA_S1))
|
|
71
|
+
|
|
72
|
+
assert loaded(printer) == [1, 2]
|
|
73
|
+
|
|
74
|
+
def test_other_cloud_fields_still_update(self):
|
|
75
|
+
printer = make_printer([box(0)])
|
|
76
|
+
printer.multi_color_box[0].set_slot_loaded(3)
|
|
77
|
+
refreshed = stale_box(0, 1)
|
|
78
|
+
refreshed["temp"] = 33
|
|
79
|
+
|
|
80
|
+
printer.update_from_info_json(cloud_info([refreshed]))
|
|
81
|
+
|
|
82
|
+
assert printer.multi_color_box[0].current_temperature == 33
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def extfilbox(printer, current_filament, machine_type=KOBRA_X):
|
|
86
|
+
printer.process_mqtt_update(
|
|
87
|
+
topic(machine_type, "extfilbox"),
|
|
88
|
+
message("extfilbox", "auto", "done", {"current_filament": current_filament}),
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
class TestCurrentFilament:
|
|
93
|
+
@pytest.mark.parametrize(("reading", "slot"), [("0-3", 3), ("0-0", 0)])
|
|
94
|
+
def test_one_box_follows_the_printer(self, reading, slot):
|
|
95
|
+
printer = make_printer([box(0)])
|
|
96
|
+
|
|
97
|
+
extfilbox(printer, reading)
|
|
98
|
+
|
|
99
|
+
assert loaded(printer) == [slot]
|
|
100
|
+
|
|
101
|
+
@pytest.mark.parametrize("reading", ["", None, "0-9", "1-2", "x", "0-", "-1"])
|
|
102
|
+
def test_a_retract_or_unknown_reading_keeps_the_slot(self, reading):
|
|
103
|
+
printer = make_printer([box(0)])
|
|
104
|
+
printer.multi_color_box[0].set_slot_loaded(3)
|
|
105
|
+
|
|
106
|
+
extfilbox(printer, reading)
|
|
107
|
+
|
|
108
|
+
assert loaded(printer) == [3]
|
|
109
|
+
|
|
110
|
+
def test_with_two_boxes_nothing_is_guessed(self):
|
|
111
|
+
printer = make_printer([box(0), box(1)])
|
|
112
|
+
|
|
113
|
+
extfilbox(printer, "0-3")
|
|
114
|
+
|
|
115
|
+
assert loaded(printer) == [-1, -1]
|
|
116
|
+
|
|
117
|
+
def test_it_survives_the_next_cloud_poll(self):
|
|
118
|
+
printer = make_printer([box(0)])
|
|
119
|
+
extfilbox(printer, "0-3")
|
|
120
|
+
|
|
121
|
+
printer.update_from_info_json(cloud_info([stale_box(0, 1)]))
|
|
122
|
+
|
|
123
|
+
assert loaded(printer) == [3]
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
class TestRedactForLog:
|
|
127
|
+
def test_printer_identifiers_are_redacted_wherever_they_are(self):
|
|
128
|
+
payload = {"type": "buried", "data": {"cn_code": "CN123", "nested": [{"machine_mac": "AA:BB"}], "ip": "10.0.0.2"}}
|
|
129
|
+
|
|
130
|
+
assert redact_for_log(payload) == {
|
|
131
|
+
"type": "buried",
|
|
132
|
+
"data": {"cn_code": "**REDACTED**", "nested": [{"machine_mac": "**REDACTED**"}], "ip": "**REDACTED**"},
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
@pytest.mark.parametrize("key", ["token", "casdoor_user_token", "rtc_token", "app_secret", "password"])
|
|
136
|
+
def test_credentials_are_redacted(self, key):
|
|
137
|
+
assert redact_for_log({key: "secret-value"}) == {key: "**REDACTED**"}
|
|
138
|
+
|
|
139
|
+
def test_ordinary_fields_and_empty_values_pass_through(self):
|
|
140
|
+
payload = {"progress": 42, "taskid": "131448158", "cn_code": "", "state": "printing", "slots": [1, 2]}
|
|
141
|
+
|
|
142
|
+
assert redact_for_log(payload) == payload
|
|
143
|
+
|
|
144
|
+
def test_the_input_is_not_changed(self):
|
|
145
|
+
payload = {"data": {"cn_code": "CN123"}}
|
|
146
|
+
|
|
147
|
+
redact_for_log(payload)
|
|
148
|
+
|
|
149
|
+
assert payload == {"data": {"cn_code": "CN123"}}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{anycubic_cloud_api-0.4.35 → anycubic_cloud_api-0.4.36}/src/anycubic_cloud_api/anycubic_api.py
RENAMED
|
File without changes
|
|
File without changes
|
{anycubic_cloud_api-0.4.35 → anycubic_cloud_api-0.4.36}/src/anycubic_cloud_api/api/functions.py
RENAMED
|
File without changes
|
|
File without changes
|
{anycubic_cloud_api-0.4.35 → anycubic_cloud_api-0.4.36}/src/anycubic_cloud_api/const/const.py
RENAMED
|
File without changes
|
{anycubic_cloud_api-0.4.35 → anycubic_cloud_api-0.4.36}/src/anycubic_cloud_api/const/enums.py
RENAMED
|
File without changes
|
{anycubic_cloud_api-0.4.35 → anycubic_cloud_api-0.4.36}/src/anycubic_cloud_api/const/error_codes.py
RENAMED
|
File without changes
|
|
File without changes
|
{anycubic_cloud_api-0.4.35 → anycubic_cloud_api-0.4.36}/src/anycubic_cloud_api/const/mqtt.py
RENAMED
|
File without changes
|
{anycubic_cloud_api-0.4.35 → anycubic_cloud_api-0.4.36}/src/anycubic_cloud_api/const/regions.py
RENAMED
|
File without changes
|
|
File without changes
|
{anycubic_cloud_api-0.4.35 → anycubic_cloud_api-0.4.36}/src/anycubic_cloud_api/data_models/files.py
RENAMED
|
File without changes
|
|
File without changes
|
{anycubic_cloud_api-0.4.35 → anycubic_cloud_api-0.4.36}/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.35 → anycubic_cloud_api-0.4.36}/src/anycubic_cloud_api/lan/__init__.py
RENAMED
|
File without changes
|
{anycubic_cloud_api-0.4.35 → anycubic_cloud_api-0.4.36}/src/anycubic_cloud_api/lan/client.py
RENAMED
|
File without changes
|
{anycubic_cloud_api-0.4.35 → anycubic_cloud_api-0.4.36}/src/anycubic_cloud_api/lan/commands.py
RENAMED
|
File without changes
|
{anycubic_cloud_api-0.4.35 → anycubic_cloud_api-0.4.36}/src/anycubic_cloud_api/lan/handshake.py
RENAMED
|
File without changes
|
{anycubic_cloud_api-0.4.35 → anycubic_cloud_api-0.4.36}/src/anycubic_cloud_api/models/auth.py
RENAMED
|
File without changes
|
|
File without changes
|
{anycubic_cloud_api-0.4.35 → anycubic_cloud_api-0.4.36}/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.35 → anycubic_cloud_api-0.4.36}/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
|
|
File without changes
|
|
File without changes
|