anycubic-cloud-api 0.3.0__tar.gz → 0.3.2__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.
Files changed (47) hide show
  1. {anycubic_cloud_api-0.3.0/src/anycubic_cloud_api.egg-info → anycubic_cloud_api-0.3.2}/PKG-INFO +1 -1
  2. {anycubic_cloud_api-0.3.0 → anycubic_cloud_api-0.3.2}/pyproject.toml +1 -1
  3. {anycubic_cloud_api-0.3.0 → anycubic_cloud_api-0.3.2}/src/anycubic_cloud_api/data_models/printer.py +37 -12
  4. {anycubic_cloud_api-0.3.0 → anycubic_cloud_api-0.3.2/src/anycubic_cloud_api.egg-info}/PKG-INFO +1 -1
  5. {anycubic_cloud_api-0.3.0 → anycubic_cloud_api-0.3.2}/tests/test_lan_reports.py +84 -0
  6. {anycubic_cloud_api-0.3.0 → anycubic_cloud_api-0.3.2}/LICENSE +0 -0
  7. {anycubic_cloud_api-0.3.0 → anycubic_cloud_api-0.3.2}/README.md +0 -0
  8. {anycubic_cloud_api-0.3.0 → anycubic_cloud_api-0.3.2}/setup.cfg +0 -0
  9. {anycubic_cloud_api-0.3.0 → anycubic_cloud_api-0.3.2}/src/anycubic_cloud_api/__init__.py +0 -0
  10. {anycubic_cloud_api-0.3.0 → anycubic_cloud_api-0.3.2}/src/anycubic_cloud_api/anycubic_api.py +0 -0
  11. {anycubic_cloud_api-0.3.0 → anycubic_cloud_api-0.3.2}/src/anycubic_cloud_api/api/base.py +0 -0
  12. {anycubic_cloud_api-0.3.0 → anycubic_cloud_api-0.3.2}/src/anycubic_cloud_api/api/functions.py +0 -0
  13. {anycubic_cloud_api-0.3.0 → anycubic_cloud_api-0.3.2}/src/anycubic_cloud_api/api/mqtt.py +0 -0
  14. {anycubic_cloud_api-0.3.0 → anycubic_cloud_api-0.3.2}/src/anycubic_cloud_api/const/api_endpoints.py +0 -0
  15. {anycubic_cloud_api-0.3.0 → anycubic_cloud_api-0.3.2}/src/anycubic_cloud_api/const/const.py +0 -0
  16. {anycubic_cloud_api-0.3.0 → anycubic_cloud_api-0.3.2}/src/anycubic_cloud_api/const/enums.py +0 -0
  17. {anycubic_cloud_api-0.3.0 → anycubic_cloud_api-0.3.2}/src/anycubic_cloud_api/const/lan.py +0 -0
  18. {anycubic_cloud_api-0.3.0 → anycubic_cloud_api-0.3.2}/src/anycubic_cloud_api/const/mqtt.py +0 -0
  19. {anycubic_cloud_api-0.3.0 → anycubic_cloud_api-0.3.2}/src/anycubic_cloud_api/data_models/consumable.py +0 -0
  20. {anycubic_cloud_api-0.3.0 → anycubic_cloud_api-0.3.2}/src/anycubic_cloud_api/data_models/files.py +0 -0
  21. {anycubic_cloud_api-0.3.0 → anycubic_cloud_api-0.3.2}/src/anycubic_cloud_api/data_models/gcode_file.py +0 -0
  22. {anycubic_cloud_api-0.3.0 → anycubic_cloud_api-0.3.2}/src/anycubic_cloud_api/data_models/orders.py +0 -0
  23. {anycubic_cloud_api-0.3.0 → anycubic_cloud_api-0.3.2}/src/anycubic_cloud_api/data_models/print_response.py +0 -0
  24. {anycubic_cloud_api-0.3.0 → anycubic_cloud_api-0.3.2}/src/anycubic_cloud_api/data_models/print_speed_mode.py +0 -0
  25. {anycubic_cloud_api-0.3.0 → anycubic_cloud_api-0.3.2}/src/anycubic_cloud_api/data_models/printer_properties.py +0 -0
  26. {anycubic_cloud_api-0.3.0 → anycubic_cloud_api-0.3.2}/src/anycubic_cloud_api/data_models/printing_settings.py +0 -0
  27. {anycubic_cloud_api-0.3.0 → anycubic_cloud_api-0.3.2}/src/anycubic_cloud_api/data_models/project.py +0 -0
  28. {anycubic_cloud_api-0.3.0 → anycubic_cloud_api-0.3.2}/src/anycubic_cloud_api/exceptions/error_strings.py +0 -0
  29. {anycubic_cloud_api-0.3.0 → anycubic_cloud_api-0.3.2}/src/anycubic_cloud_api/exceptions/exceptions.py +0 -0
  30. {anycubic_cloud_api-0.3.0 → anycubic_cloud_api-0.3.2}/src/anycubic_cloud_api/helpers/helpers.py +0 -0
  31. {anycubic_cloud_api-0.3.0 → anycubic_cloud_api-0.3.2}/src/anycubic_cloud_api/lan/__init__.py +0 -0
  32. {anycubic_cloud_api-0.3.0 → anycubic_cloud_api-0.3.2}/src/anycubic_cloud_api/lan/client.py +0 -0
  33. {anycubic_cloud_api-0.3.0 → anycubic_cloud_api-0.3.2}/src/anycubic_cloud_api/lan/handshake.py +0 -0
  34. {anycubic_cloud_api-0.3.0 → anycubic_cloud_api-0.3.2}/src/anycubic_cloud_api/models/auth.py +0 -0
  35. {anycubic_cloud_api-0.3.0 → anycubic_cloud_api-0.3.2}/src/anycubic_cloud_api/models/cloud_upload.py +0 -0
  36. {anycubic_cloud_api-0.3.0 → anycubic_cloud_api-0.3.2}/src/anycubic_cloud_api/models/http.py +0 -0
  37. {anycubic_cloud_api-0.3.0 → anycubic_cloud_api-0.3.2}/src/anycubic_cloud_api/resources/anycubic_mqqt_tls_ca.crt +0 -0
  38. {anycubic_cloud_api-0.3.0 → anycubic_cloud_api-0.3.2}/src/anycubic_cloud_api/resources/anycubic_mqqt_tls_client.crt +0 -0
  39. {anycubic_cloud_api-0.3.0 → anycubic_cloud_api-0.3.2}/src/anycubic_cloud_api/resources/anycubic_mqqt_tls_client.key +0 -0
  40. {anycubic_cloud_api-0.3.0 → anycubic_cloud_api-0.3.2}/src/anycubic_cloud_api.egg-info/SOURCES.txt +0 -0
  41. {anycubic_cloud_api-0.3.0 → anycubic_cloud_api-0.3.2}/src/anycubic_cloud_api.egg-info/dependency_links.txt +0 -0
  42. {anycubic_cloud_api-0.3.0 → anycubic_cloud_api-0.3.2}/src/anycubic_cloud_api.egg-info/requires.txt +0 -0
  43. {anycubic_cloud_api-0.3.0 → anycubic_cloud_api-0.3.2}/src/anycubic_cloud_api.egg-info/top_level.txt +0 -0
  44. {anycubic_cloud_api-0.3.0 → anycubic_cloud_api-0.3.2}/tests/test_axis_position.py +0 -0
  45. {anycubic_cloud_api-0.3.0 → anycubic_cloud_api-0.3.2}/tests/test_lan_client.py +0 -0
  46. {anycubic_cloud_api-0.3.0 → anycubic_cloud_api-0.3.2}/tests/test_lan_handshake.py +0 -0
  47. {anycubic_cloud_api-0.3.0 → anycubic_cloud_api-0.3.2}/tests/test_partial_reports.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: anycubic-cloud-api
3
- Version: 0.3.0
3
+ Version: 0.3.2
4
4
  Summary: Async client for the Anycubic Cloud API and its MQTT stream
5
5
  Author: Nino Bondonno
6
6
  Maintainer: Nino Bondonno
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "anycubic-cloud-api"
7
- version = "0.3.0"
7
+ version = "0.3.2"
8
8
  description = "Async client for the Anycubic Cloud API and its MQTT stream"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.12"
@@ -875,19 +875,29 @@ class AnycubicPrinter:
875
875
  state: str,
876
876
  payload: AnycubicConsumableData,
877
877
  ) -> None:
878
- if action == 'auto' and state == 'done':
878
+ # 'auto' over the cloud, 'query' when asked for locally -- same body.
879
+ if action in ('auto', 'query') and state == 'done':
879
880
  data = payload['data']
880
881
 
882
+ # Read every field first: reading is what marks it consumed, and
883
+ # anything left behind raises at the end of dispatch -- so these
884
+ # must not sit inside the conditionals below.
885
+ curr_hotbed = data.get('curr_hotbed_temp')
886
+ curr_nozzle = data.get('curr_nozzle_temp')
887
+ target_hotbed = data.get('target_hotbed_temp')
888
+ target_nozzle = data.get('target_nozzle_temp')
889
+
890
+ # Asked for locally, an enclosed printer also answers with the
891
+ # chamber. A Kobra S1 sends zeroes because it has no sensor, so
892
+ # these are kept raw rather than turned into an always-zero entity.
893
+ self._chamber_temp = data.get('curr_chamber_temp')
894
+ self._target_chamber_temp = data.get('target_chamber_temp')
895
+
881
896
  if self.parameter:
882
- self.parameter.update_current_temps(
883
- data['curr_hotbed_temp'],
884
- data['curr_nozzle_temp'],
885
- )
897
+ self.parameter.update_current_temps(curr_hotbed, curr_nozzle)
898
+
886
899
  if self._latest_project:
887
- self._latest_project.update_target_temps(
888
- data['target_hotbed_temp'],
889
- data['target_nozzle_temp'],
890
- )
900
+ self._latest_project.update_target_temps(target_hotbed, target_nozzle)
891
901
 
892
902
  return
893
903
  else:
@@ -899,10 +909,13 @@ class AnycubicPrinter:
899
909
  state: str,
900
910
  payload: AnycubicConsumableData,
901
911
  ) -> None:
902
- if action == 'auto' and state == 'done':
912
+ # The cloud pushes these unprompted as action 'auto'; asked for over
913
+ # the local connection the same body comes back as 'query'.
914
+ if action in ('auto', 'query') and state == 'done':
903
915
  data = payload['data']
904
916
 
905
- self._fan_speed = int(data['fan_speed_pct'])
917
+ if 'fan_speed_pct' in data:
918
+ self._fan_speed = int(data['fan_speed_pct'])
906
919
 
907
920
  # The Kobra S1 also reports the auxiliary part fan and the ACE box
908
921
  # fan here. Both were previously discarded as unhandled data.
@@ -1264,12 +1277,24 @@ class AnycubicPrinter:
1264
1277
  self._local_firmware_version = data.get('version')
1265
1278
  self._local_ip = data.get('ip')
1266
1279
 
1280
+ # A report arriving at all means the printer is reachable, and the
1281
+ # cloud fields that normally drive availability are never set when
1282
+ # there is no cloud. Without this every entity reads unavailable on a
1283
+ # working local connection.
1284
+ self._device_status = 1
1285
+
1286
+ printer_state = data.get('state')
1287
+
1288
+ if printer_state == 'busy':
1289
+ self._is_printing = 2
1290
+ elif printer_state == 'free':
1291
+ self._is_printing = 1
1292
+
1267
1293
  # Consumed but not modelled: the printer's own name and model strings
1268
1294
  # duplicate what the device already carries, and the project blocks are
1269
1295
  # covered by the print report.
1270
1296
  data.get('printerName')
1271
1297
  data.get('model')
1272
- data.get('state')
1273
1298
  data.get('project')
1274
1299
  data.get('last_project')
1275
1300
  data.get('aux_fan_speed_pct')
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: anycubic-cloud-api
3
- Version: 0.3.0
3
+ Version: 0.3.2
4
4
  Summary: Async client for the Anycubic Cloud API and its MQTT stream
5
5
  Author: Nino Bondonno
6
6
  Maintainer: Nino Bondonno
@@ -212,3 +212,87 @@ class TestAiSettings:
212
212
  def test_an_unseen_printer_reports_nothing(self):
213
213
  assert make_printer().ai_detection_enabled is None
214
214
  assert make_printer().ai_settings == {}
215
+
216
+
217
+ class TestQueryActionReplies:
218
+ """The cloud pushes these unprompted; locally they answer a query.
219
+
220
+ The body is identical either way, so refusing 'query' meant fan and
221
+ temperature reports were discarded over the local connection.
222
+ """
223
+
224
+ FAN = {
225
+ "type": "fan",
226
+ "action": "query",
227
+ "state": "done",
228
+ "data": {"aux_fan_speed_pct": 0, "box_fan_level": 0, "fan_speed_pct": 55},
229
+ }
230
+ TEMPERATURE = {
231
+ "type": "tempature",
232
+ "action": "query",
233
+ "state": "done",
234
+ "data": {
235
+ "curr_hotbed_temp": 31,
236
+ "curr_nozzle_temp": 34,
237
+ "curr_chamber_temp": 0,
238
+ "target_hotbed_temp": 0,
239
+ "target_nozzle_temp": 0,
240
+ "target_chamber_temp": 0,
241
+ },
242
+ }
243
+
244
+ def test_a_queried_fan_report_is_applied(self):
245
+ printer = make_printer()
246
+
247
+ apply(printer, self.FAN)
248
+
249
+ assert printer._fan_speed == 55
250
+
251
+ def test_a_pushed_fan_report_still_works(self):
252
+ printer = make_printer()
253
+
254
+ apply(printer, {**self.FAN, "action": "auto"})
255
+
256
+ assert printer._fan_speed == 55
257
+
258
+ def test_a_fan_report_without_the_main_speed_does_not_raise(self):
259
+ """Not every model reports all three."""
260
+ apply(make_printer(), {**self.FAN, "data": {"box_fan_level": 2}})
261
+
262
+ def test_a_queried_temperature_report_is_applied(self):
263
+ apply(make_printer(), self.TEMPERATURE)
264
+
265
+
266
+ class TestAvailabilityFromLocalReports:
267
+ """With no cloud, nothing else sets the fields availability is read from."""
268
+
269
+ def test_a_report_marks_the_printer_online(self):
270
+ printer = make_printer()
271
+
272
+ apply(printer, INFO_REPORT)
273
+
274
+ assert printer.printer_online is True
275
+
276
+ def test_free_reads_as_available_and_not_busy(self):
277
+ printer = make_printer()
278
+
279
+ apply(printer, INFO_REPORT)
280
+
281
+ assert printer.is_available is True
282
+ assert printer.is_busy is False
283
+
284
+ def test_busy_reads_as_busy(self):
285
+ printer = make_printer()
286
+ report = {**INFO_REPORT, "data": {**INFO_REPORT["data"], "state": "busy"}}
287
+
288
+ apply(printer, report)
289
+
290
+ assert printer.is_busy is True
291
+
292
+ def test_an_unknown_state_leaves_it_alone(self):
293
+ printer = make_printer()
294
+ report = {**INFO_REPORT, "data": {**INFO_REPORT["data"], "state": "wat"}}
295
+
296
+ apply(printer, report)
297
+
298
+ assert printer.printer_online is True