mirror of
https://github.com/maziggy/bambuddy.git
synced 2026-10-02 20:22:15 +02:00
fix(inventory): emulate state=9 for bare-tray empty-slot signal on P1S/A1 (#1322)
Follow-up to the #1322 root fix. Reporter @RosdasHH traced the raw MQTT payload and found that P1S and A1 Mini send only {"id": N} for a physically empty slot — no state, no tray_type, no other fields. Without that signal, the assign-spool path was firing one wasted MQTT publish per click on a truly-empty slot (firmware dropped it silently, but still). The AMS parser in printer_manager.py now detects the bare-tray shape and promotes it to state=9 — the firmware's explicit "no spool" code — which lets the existing state in {9, 10} short-circuit in the inventory route apply automatically. The detection is intentionally narrow: len(tray) == 1 and "id" in tray and state is None so the post-Reset-Slot A1 Mini BMCU case (populated payload with state=3 and tray_type="") has more than one key and stays unaffected — the #1322 root fix is preserved.
This commit is contained in:
+1
-1
File diff suppressed because one or more lines are too long
@@ -768,6 +768,19 @@ def printer_state_to_dict(state: PrinterState, printer_id: int | None = None, mo
|
||||
if k_value is None and cali_idx is not None and cali_idx in kprofile_map:
|
||||
k_value = kprofile_map[cali_idx]
|
||||
|
||||
# P1S / A1 Mini physically-empty-slot signal (#1322 follow-up by
|
||||
# @RosdasHH): for a truly empty slot the firmware sends only
|
||||
# {"id": N} — no state, no tray_type, no anything else. Treat
|
||||
# that as the firmware's "no spool" indicator (state=9) so the
|
||||
# assign-spool path in inventory.py can short-circuit a MQTT
|
||||
# publish the firmware would silently drop anyway. The
|
||||
# post-"Reset Slot" A1 Mini BMCU case sends a populated payload
|
||||
# (state=3, tray_type="") — different shape, doesn't match this
|
||||
# guard, still attempts the MQTT push per the #1322 fix.
|
||||
state_val = tray.get("state")
|
||||
if state_val is None and len(tray) == 1 and "id" in tray:
|
||||
state_val = 9
|
||||
|
||||
trays.append(
|
||||
{
|
||||
"id": int(tray.get("id", 0)),
|
||||
@@ -785,7 +798,7 @@ def printer_state_to_dict(state: PrinterState, printer_id: int | None = None, mo
|
||||
"nozzle_temp_max": tray.get("nozzle_temp_max"),
|
||||
"drying_temp": tray.get("drying_temp"),
|
||||
"drying_time": tray.get("drying_time"),
|
||||
"state": tray.get("state"),
|
||||
"state": state_val,
|
||||
}
|
||||
)
|
||||
# Prefer humidity_raw (actual percentage) over humidity (index 1-5)
|
||||
|
||||
@@ -741,6 +741,60 @@ class TestPrinterStateToDict:
|
||||
assert result["ams"][0]["tray"][0]["tag_uid"] is None
|
||||
assert result["ams"][0]["tray"][0]["tray_uuid"] is None
|
||||
|
||||
def test_bare_tray_emulates_state_9(self, mock_state):
|
||||
"""P1S / A1 Mini physically-empty-slot signal (#1322 follow-up by @RosdasHH):
|
||||
the firmware sends only `{"id": N}` for a truly empty slot. Treat that as
|
||||
the firmware's "no spool" state (state=9) so the inventory assign-spool
|
||||
path can short-circuit the doomed MQTT publish.
|
||||
"""
|
||||
mock_state.raw_data = {
|
||||
"ams": [
|
||||
{
|
||||
"id": 0,
|
||||
"tray": [
|
||||
{"id": 0, "state": 11, "tray_type": "PLA"}, # loaded slot
|
||||
{"id": 1}, # P1S empty-slot signal — only id
|
||||
],
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
result = printer_state_to_dict(mock_state)
|
||||
trays = result["ams"][0]["tray"]
|
||||
|
||||
assert trays[0]["state"] == 11, "loaded slot keeps its firmware state"
|
||||
assert trays[1]["state"] == 9, "bare {id} tray must be promoted to state=9"
|
||||
|
||||
def test_populated_payload_with_empty_state_3_is_not_promoted(self, mock_state):
|
||||
"""A1 Mini BMCU / P1S Standard AMS post-Reset-Slot case (#1322 root):
|
||||
firmware sends state=3 + tray_type="" but with the FULL field set
|
||||
populated. Must NOT be confused with the bare-tray empty signal —
|
||||
else inventory.py would short-circuit MQTT and we'd reintroduce the
|
||||
deadlock the #1322 fix removed.
|
||||
"""
|
||||
mock_state.raw_data = {
|
||||
"ams": [
|
||||
{
|
||||
"id": 0,
|
||||
"tray": [
|
||||
{
|
||||
"id": 0,
|
||||
"state": 3,
|
||||
"tray_type": "", # cleared
|
||||
"tray_color": "",
|
||||
"tag_uid": "0000000000000000",
|
||||
"remain": 0,
|
||||
}
|
||||
],
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
result = printer_state_to_dict(mock_state)
|
||||
# state stays at 3 — the bare-tray promotion requires the dict to have
|
||||
# ONLY the id key, not just empty/falsy values for the other fields.
|
||||
assert result["ams"][0]["tray"][0]["state"] == 3
|
||||
|
||||
def test_zero_tag_uid_becomes_none(self, mock_state):
|
||||
"""Verify zero tag_uid is converted to None."""
|
||||
mock_state.raw_data = {
|
||||
|
||||
Reference in New Issue
Block a user