mirror of
https://github.com/maziggy/bambuddy.git
synced 2026-10-22 03:49:18 +02:00
fix(ams): stop reading the printer's command acks as status (issue #3040)
Every project_file carried "cfg": "0" — the device-config bitmask, which Bambu Studio has never sent and the firmware ignores. The printer echoes a command's fields back in its ack, and the ack was ingested as telemetry, so bit 18 read as "AMS Filament Backup off" 25 ms after every dispatch. Families that repeat cfg in their periodic status (P2S, H2C, X2D) corrected themselves a second later; the P1S, A1, A1 Mini and A2L send it only in a full status dump, so the wrong value stuck and silently disabled the prefer-lowest-remaining gate. The A1 family, which reports no cfg at all and is meant to stay "unknown", was pinned to a definite "off". Acks are no longer read as status, for the backup bit or the per-job timelapse flag they also echo, and cfg is gone from the print command.
This commit is contained in:
@@ -4699,8 +4699,10 @@ class TestStartPrintUniqueIdentityFields:
|
||||
assert cmd["url"] == "ftp://test.3mf"
|
||||
assert cmd["file"] == "test.3mf"
|
||||
assert cmd["profile_id"] == "0"
|
||||
assert cmd["cfg"] == "0"
|
||||
assert cmd["subtask_name"] == "test"
|
||||
# The device-config bitmask is not a per-job field and is no longer
|
||||
# sent; the printer echoed it back and we read it as telemetry (#3040).
|
||||
assert "cfg" not in cmd
|
||||
|
||||
|
||||
class TestDeleteKProfileDualNozzleDetection:
|
||||
@@ -6951,6 +6953,82 @@ class TestAmsFilamentBackupHoldTimer:
|
||||
assert mqtt_client._xcam_hold_start["print_option_auto_switch_filament"] == before_hold
|
||||
|
||||
|
||||
class TestCommandAckIsNotTelemetry:
|
||||
"""Regression (#3040): a printer's command acknowledgement echoes the
|
||||
fields Bambuddy sent, so ingesting one as status reads our own request
|
||||
back as the printer's state.
|
||||
|
||||
Bambuddy used to put ``"cfg": "0"`` in every project_file. The ack came
|
||||
back carrying it, bit 18 read as "AMS Filament Backup OFF", and on the
|
||||
families that don't repeat ``cfg`` in their periodic frames (P1S, A1,
|
||||
A1 Mini, A2L) the wrong value stuck until the user toggled it — which
|
||||
silently disabled the prefer-lowest-remaining gate for the rest of the day.
|
||||
"""
|
||||
|
||||
@pytest.fixture
|
||||
def mqtt_client(self):
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
from backend.app.services.bambu_mqtt import BambuMQTTClient
|
||||
|
||||
client = BambuMQTTClient(
|
||||
ip_address="192.168.1.100",
|
||||
serial_number="TEST123",
|
||||
access_code="12345678",
|
||||
)
|
||||
client.state.connected = True
|
||||
client._client = MagicMock()
|
||||
return client
|
||||
|
||||
def test_project_file_ack_does_not_clear_backup_state(self, mqtt_client):
|
||||
mqtt_client.state.ams_filament_backup = True
|
||||
|
||||
mqtt_client._process_message(
|
||||
{"print": {"command": "project_file", "sequence_id": "20000", "cfg": "0", "result": "success"}}
|
||||
)
|
||||
|
||||
assert mqtt_client.state.ams_filament_backup is True
|
||||
|
||||
def test_project_file_ack_leaves_unknown_backup_unknown(self, mqtt_client):
|
||||
"""A1 / A1 Mini never report cfg, so the state must stay None ("unknown")
|
||||
— the value the prefer-lowest gate reads as "preserve old behaviour"."""
|
||||
assert mqtt_client.state.ams_filament_backup is None
|
||||
|
||||
mqtt_client._process_message({"print": {"command": "project_file", "cfg": "0"}})
|
||||
|
||||
assert mqtt_client.state.ams_filament_backup is None
|
||||
|
||||
def test_push_status_still_updates_backup_state(self, mqtt_client):
|
||||
mqtt_client.state.ams_filament_backup = True
|
||||
|
||||
mqtt_client._process_message({"print": {"command": "push_status", "cfg": "C0340BC219"}}) # bit18=0
|
||||
|
||||
assert mqtt_client.state.ams_filament_backup is False
|
||||
|
||||
def test_status_frame_without_command_still_updates_backup_state(self, mqtt_client):
|
||||
"""Some firmwares omit `command` on a status frame; those stay trusted."""
|
||||
mqtt_client.state.ams_filament_backup = False
|
||||
|
||||
mqtt_client._process_message({"print": {"cfg": "C0340FC219"}}) # bit18=1
|
||||
|
||||
assert mqtt_client.state.ams_filament_backup is True
|
||||
|
||||
def test_project_file_ack_does_not_clear_timelapse_state(self, mqtt_client):
|
||||
"""The ack echoes the per-job timelapse request, not the recorder."""
|
||||
mqtt_client.state.timelapse = True
|
||||
|
||||
mqtt_client._process_message({"print": {"command": "project_file", "timelapse": False}})
|
||||
|
||||
assert mqtt_client.state.timelapse is True
|
||||
|
||||
def test_push_status_still_updates_timelapse_state(self, mqtt_client):
|
||||
mqtt_client.state.timelapse = True
|
||||
|
||||
mqtt_client._process_message({"print": {"command": "push_status", "timelapse": False}})
|
||||
|
||||
assert mqtt_client.state.timelapse is False
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 2c. Single-nozzle H2S — external-spool tray_now override (#1822)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user