mirror of
https://github.com/maziggy/bambuddy.git
synced 2026-09-30 19:21:33 +02:00
The reporter found what his P1S was doing, and it is in Bambu's P1 manual: "P1S connected AMS drying functions may only be controlled from the P1S screen." The firmware acks ams_filament_drying with result: success and then discards it, which is why three commands on an idle printer left the AMS 2 Pro at dry_status 0. No command can start a cycle on a P1, on any firmware, so don't offer one. supports_drying() now excludes the P1 series outright, replacing the 01.08+ gate carried since #292 — that version is when P1 firmware gained AMS 2 Pro support, not remote drying, and it was never checked against a live P1. Both drying routes refuse with a specific 400 instead of publishing a message the printer will drop; queue and ambient auto-drying skip P1s via the same helper. A new drying_screen_only flag keeps the control on the card, disabled, saying why — a P1 owner needs to learn where to dry, not watch the button disappear. A cycle started at the printer still shows with its countdown; only Stop goes away, since a P1 ignores stop exactly as it ignores start. Also corrects the wiki firmware matrix, which listed P1P/P1S as supported and (separately) P2S/H2S/H2C as unsupported. 8 tests.
71 lines
2.5 KiB
Python
71 lines
2.5 KiB
Python
"""P1-series AMS drying is screen-only — the API must refuse it (#2533).
|
|
|
|
Bambu's P1 manual states that "P1S connected AMS drying functions may only be
|
|
controlled from the P1S screen". The firmware still answers
|
|
``ams_filament_drying`` with ``result: success`` and then ignores it, which is
|
|
exactly what the reporter saw: three commands accepted on an idle P1S with an
|
|
AMS 2 Pro, and the unit never left ``dry_status: 0``.
|
|
|
|
So a command we can't fulfil must be refused rather than acked, and that has to
|
|
hold for stop as well as start — a cycle a P1S user started at the printer can
|
|
only be ended there.
|
|
"""
|
|
|
|
from unittest.mock import MagicMock, patch
|
|
|
|
import pytest
|
|
from httpx import AsyncClient
|
|
|
|
|
|
@pytest.fixture
|
|
def mqtt_send():
|
|
"""Watch the MQTT command so we can assert nothing was published."""
|
|
with patch(
|
|
"backend.app.services.printer_manager.printer_manager.send_drying_command",
|
|
new=MagicMock(return_value=True),
|
|
) as m:
|
|
yield m
|
|
|
|
|
|
@pytest.fixture
|
|
def live_state():
|
|
"""A connected printer on firmware new enough that only the model gates drying."""
|
|
state = MagicMock()
|
|
state.firmware_version = "01.10.00.00"
|
|
state.raw_data = {"ams": [{"id": 0, "module_type": "n3f", "tray": []}]}
|
|
with patch(
|
|
"backend.app.services.printer_manager.printer_manager.get_status",
|
|
new=MagicMock(return_value=state),
|
|
) as m:
|
|
yield m
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
@pytest.mark.integration
|
|
@pytest.mark.parametrize("model", ["P1S", "P1P"])
|
|
@pytest.mark.parametrize("action", ["start", "stop"])
|
|
async def test_screen_only_model_refuses_drying(
|
|
async_client: AsyncClient, printer_factory, mqtt_send, live_state, model, action
|
|
):
|
|
printer = await printer_factory(model=model)
|
|
|
|
response = await async_client.post(f"/api/v1/printers/{printer.id}/drying/{action}?ams_id=0")
|
|
|
|
assert response.status_code == 400
|
|
assert "screen" in response.json()["detail"].lower()
|
|
# And nothing went out on the wire — an ack the printer would drop is worse
|
|
# than a refusal, because it leaves the user believing drying is running.
|
|
mqtt_send.assert_not_called()
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
@pytest.mark.integration
|
|
@pytest.mark.parametrize("action", ["start", "stop"])
|
|
async def test_commandable_model_still_dries(async_client: AsyncClient, printer_factory, mqtt_send, live_state, action):
|
|
printer = await printer_factory(model="X1C")
|
|
|
|
response = await async_client.post(f"/api/v1/printers/{printer.id}/drying/{action}?ams_id=0")
|
|
|
|
assert response.status_code == 200
|
|
mqtt_send.assert_called_once()
|