mirror of
https://github.com/maziggy/bambuddy.git
synced 2026-08-11 00:30:12 -04:00
[Feature]: HMS Actions (#1743)
This commit is contained in:
parent
d3fd8d6ad8
commit
3ddf8d847e
23 changed files with 10138 additions and 23 deletions
|
|
@ -27,6 +27,7 @@ from backend.app.schemas.printer import (
|
|||
AMSUnit,
|
||||
DiagnosticRequest,
|
||||
FilaSwitchResponse,
|
||||
HmsActionBody,
|
||||
HMSErrorResponse,
|
||||
NozzleInfoResponse,
|
||||
NozzleRackSlot,
|
||||
|
|
@ -456,7 +457,9 @@ async def get_printer_status(
|
|||
|
||||
# Convert HMS errors to response format
|
||||
hms_errors = [
|
||||
HMSErrorResponse(code=e.code, attr=e.attr, module=e.module, severity=e.severity)
|
||||
HMSErrorResponse(
|
||||
code=e.code, attr=e.attr, module=e.module, severity=e.severity, actions=e.actions, job_id=e.job_id
|
||||
)
|
||||
for e in (state.hms_errors or [])
|
||||
]
|
||||
|
||||
|
|
@ -3787,3 +3790,27 @@ async def get_runtime_debug(
|
|||
else None,
|
||||
"is_active": printer.is_active,
|
||||
}
|
||||
|
||||
|
||||
@router.post("/{printer_id}/hms/execute-action")
|
||||
async def execute_hms_action(
|
||||
printer_id: int,
|
||||
body: HmsActionBody,
|
||||
_=RequirePermissionIfAuthEnabled(Permission.PRINTERS_CONTROL),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
"""Execute an HMS action on the printer."""
|
||||
result = await db.execute(select(Printer).where(Printer.id == printer_id))
|
||||
printer = result.scalar_one_or_none()
|
||||
if not printer:
|
||||
raise HTTPException(404, "Printer not found")
|
||||
|
||||
client = printer_manager.get_client(printer_id)
|
||||
if not client:
|
||||
raise HTTPException(400, "Printer not connected")
|
||||
|
||||
success = client.execute_hms_action(body.print_error, body.action, body.job_id)
|
||||
if not success:
|
||||
raise HTTPException(400, "Failed to execute HMS action")
|
||||
|
||||
return {"success": True, "message": "HMS action executed"}
|
||||
|
|
|
|||
9009
backend/app/data/hms_actions.json
Normal file
9009
backend/app/data/hms_actions.json
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -153,6 +153,8 @@ class HMSErrorResponse(BaseModel):
|
|||
attr: int = 0 # Attribute value for constructing wiki URL
|
||||
module: int
|
||||
severity: int # 1=fatal, 2=serious, 3=common, 4=info
|
||||
actions: list[str] = [] # List of user-facing action keys (e.g. "CHECK_FILAMENT")
|
||||
job_id: str | None = None # Optional job ID for actions that require it (e.g. "CHECK_ASSISTANT")
|
||||
|
||||
|
||||
class AMSTray(BaseModel):
|
||||
|
|
@ -216,6 +218,18 @@ class AmsLabelBody(BaseModel):
|
|||
ams_serial: str = Field(default="", max_length=50)
|
||||
|
||||
|
||||
class HmsActionBody(BaseModel):
|
||||
# 8-char hex short code without separator (e.g. "05000070") — frontend strips
|
||||
# the underscore from the displayed `MMMM_EEEE` before sending.
|
||||
print_error: str = Field(..., min_length=8, max_length=8, pattern=r"^[0-9A-Fa-f]{8}$")
|
||||
# One of the HMSAction enum values. Length-capped to keep stray input from
|
||||
# reaching the dispatcher's `match` statement.
|
||||
action: str = Field(..., min_length=1, max_length=64)
|
||||
# The `subtask_id` snapshot from the HMSError that surfaced this dialog.
|
||||
# Bambu echoes it back in HMS-aware commands. Optional for idle errors.
|
||||
job_id: str | None = Field(default=None, max_length=64)
|
||||
|
||||
|
||||
class FilaSwitchResponse(BaseModel):
|
||||
"""Filament Track Switch (FTS) state — accessory that mediates AMS-to-extruder routing.
|
||||
|
||||
|
|
|
|||
|
|
@ -21,6 +21,8 @@ from datetime import datetime, timezone
|
|||
|
||||
import paho.mqtt.client as mqtt
|
||||
|
||||
from backend.app.services.hms_actions import HMSAction, get_actions_for_error_code
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# AMS module name prefixes used in get_version responses.
|
||||
|
|
@ -169,6 +171,8 @@ class HMSError:
|
|||
module: int
|
||||
severity: int # 1=fatal, 2=serious, 3=common, 4=info
|
||||
message: str = ""
|
||||
actions: list[str] | None = None # List of user-facing action keys (e.g. "CHECK_FILAMENT")
|
||||
job_id: str | None = None # Optional job ID for actions that require it (e.g. "CHECK_ASSISTANT")
|
||||
|
||||
|
||||
# HMS short codes the firmware emits during normal user-cancel sequences.
|
||||
|
|
@ -2729,12 +2733,15 @@ class BambuMQTTClient:
|
|||
short_code = f"{(attr >> 16) & 0xFFFF:04X}_{code & 0xFFFF:04X}"
|
||||
if short_code in _HMS_USER_ACTION_CODES:
|
||||
continue
|
||||
actions = get_actions_for_error_code(self.serial_number[:3], short_code.replace("_", ""))
|
||||
self.state.hms_errors.append(
|
||||
HMSError(
|
||||
code=f"0x{code:x}" if code else "0x0",
|
||||
attr=attr,
|
||||
module=module,
|
||||
severity=severity if severity > 0 else 2,
|
||||
actions=actions,
|
||||
job_id=self.state.subtask_id,
|
||||
)
|
||||
)
|
||||
|
||||
|
|
@ -2780,12 +2787,32 @@ class BambuMQTTClient:
|
|||
existing_short_codes.add(f"{e_module:04X}_{e_error:04X}")
|
||||
|
||||
if short_code not in existing_short_codes:
|
||||
# Bambu's HMS catalog keys by 3-letter device code (the SN
|
||||
# prefix) and a 16-char short error code without the
|
||||
# underscore separator we store internally.
|
||||
actions = get_actions_for_error_code(self.serial_number[:3], short_code.replace("_", ""))
|
||||
# Bambu pushes the current job as `subtask_id` on the
|
||||
# state stream; the HMS-action commands echo it back as
|
||||
# `job_id`. The error payload itself doesn't carry the
|
||||
# id, so snapshot it from the live state at parse time
|
||||
# and freeze it on the HMSError so subsequent
|
||||
# job changes don't invalidate the action.
|
||||
job_id = self.state.subtask_id
|
||||
logger.debug(
|
||||
"[%s, %s] HMS available actions: %s (job_id=%s)",
|
||||
self.serial_number[:3],
|
||||
short_code.replace("_", ""),
|
||||
actions,
|
||||
job_id,
|
||||
)
|
||||
self.state.hms_errors.append(
|
||||
HMSError(
|
||||
code=f"0x{error:x}",
|
||||
attr=print_error, # Store full value for display
|
||||
module=module >> 8, # High byte of module (e.g., 0x05)
|
||||
severity=3, # Warning level for print_error
|
||||
actions=actions,
|
||||
job_id=job_id,
|
||||
)
|
||||
)
|
||||
|
||||
|
|
@ -5378,3 +5405,196 @@ class BambuMQTTClient:
|
|||
self._client.publish(self.topic_publish, json.dumps(pushall), qos=1)
|
||||
logger.info("[%s] Set liveview %s", self.serial_number, "enabled" if enable else "disabled")
|
||||
return True
|
||||
|
||||
def execute_hms_action(self, print_error: str, action: str, job_id: str | None = None) -> bool:
|
||||
"""Dispatch the user's choice from the HMS-error modal as a printer command.
|
||||
|
||||
Args:
|
||||
print_error: 8-char hex short code with no separator (e.g. "05000070").
|
||||
The frontend strips the underscore from the displayed `MMMM_EEEE`
|
||||
before sending.
|
||||
action: One of HMSAction's string values.
|
||||
job_id: The `subtask_id` snapshotted onto the HMSError at parse-time.
|
||||
Bambu's HMS-aware commands echo it back as `job_id`. May be None
|
||||
for idle errors that never had a job.
|
||||
|
||||
Returns False when the MQTT client is offline or when `action` is unknown
|
||||
so the route surfaces it as a 4xx rather than a silent no-op.
|
||||
"""
|
||||
|
||||
if not self._client or not self.state.connected:
|
||||
logger.warning("[%s] Cannot execute HMS action: not connected", self.serial_number)
|
||||
return False
|
||||
|
||||
# Always re-push the full state after a command so the modal's underlying
|
||||
# status query reflects the new error list (or absence) on the next tick.
|
||||
def publish(payload: dict):
|
||||
self._client.publish(self.topic_publish, json.dumps(payload), qos=1)
|
||||
self._client.publish(
|
||||
self.topic_publish, json.dumps({"pushing": {"command": "pushall", "sequence_id": "0"}}), qos=1
|
||||
)
|
||||
|
||||
def hms_resume():
|
||||
publish(
|
||||
{
|
||||
"print": {
|
||||
"command": "resume",
|
||||
"err": print_error,
|
||||
"param": "reserve",
|
||||
"job_id": job_id,
|
||||
"sequence_id": "0",
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
def hms_stop():
|
||||
publish(
|
||||
{
|
||||
"print": {
|
||||
"command": "stop",
|
||||
"err": print_error,
|
||||
"param": "reserve",
|
||||
"job_id": job_id,
|
||||
"sequence_id": "0",
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
def hms_ignore(persistent: bool = False):
|
||||
# `idle_ignore` is BambuStudio's "dismiss this warning" command.
|
||||
# type=0 dismisses once, type=1 hides the same warning permanently.
|
||||
publish(
|
||||
{
|
||||
"print": {
|
||||
"command": "idle_ignore",
|
||||
"err": print_error,
|
||||
"type": 1 if persistent else 0,
|
||||
"sequence_id": "0",
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
def ams_control(param: str):
|
||||
publish(
|
||||
{
|
||||
"print": {
|
||||
"command": "ams_control",
|
||||
"param": param,
|
||||
"sequence_id": "0",
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
def clean_print_error():
|
||||
# Matches the existing `clear_hms_errors` shape — Bambu does not
|
||||
# expect `print_error` in the body; the command clears whatever
|
||||
# error dialog is currently active on the printer.
|
||||
publish(
|
||||
{
|
||||
"print": {
|
||||
"command": "clean_print_error",
|
||||
"sequence_id": "0",
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
def uiop_close():
|
||||
# `err` is the 8-char hex short code (already a string from the
|
||||
# frontend), uppercased for consistency with how BambuStudio sends it.
|
||||
publish(
|
||||
{
|
||||
"system": {
|
||||
"command": "uiop",
|
||||
"name": "print_error",
|
||||
"action": "close",
|
||||
"source": 1,
|
||||
"type": "dialog",
|
||||
"err": print_error.upper(),
|
||||
"sequence_id": "0",
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
match action:
|
||||
case (
|
||||
HMSAction.RESUME_PRINTING
|
||||
| HMSAction.RESUME_PRINTING_DEFECTS
|
||||
| HMSAction.RESUME_PRINTING_PROBELM_SOLVED
|
||||
| HMSAction.PROBLEM_SOLVED_RESUME
|
||||
| HMSAction.FILAMENT_LOAD_RESUME
|
||||
| HMSAction.PROCEED
|
||||
):
|
||||
hms_resume()
|
||||
|
||||
case HMSAction.STOP_PRINTING:
|
||||
hms_stop()
|
||||
|
||||
case HMSAction.IGNORE_RESUME | HMSAction.NO_REMINDER_NEXT_TIME:
|
||||
hms_ignore(persistent=False)
|
||||
|
||||
case HMSAction.IGNORE_NO_REMINDER_NEXT_TIME | HMSAction.DONT_REMIND_NEXT_TIME:
|
||||
hms_ignore(persistent=True)
|
||||
|
||||
case HMSAction.FILAMENT_EXTRUDED | HMSAction.DBL_CHECK_DONE:
|
||||
ams_control("done")
|
||||
|
||||
case (
|
||||
HMSAction.RETRY_FILAMENT_EXTRUDED
|
||||
| HMSAction.CONTINUE
|
||||
| HMSAction.RETRY_PROBLEM_SOLVED
|
||||
| HMSAction.DBL_CHECK_RETRY
|
||||
):
|
||||
ams_control("resume")
|
||||
|
||||
case HMSAction.ABORT:
|
||||
ams_control("abort")
|
||||
|
||||
case HMSAction.OK_BUTTON:
|
||||
clean_print_error()
|
||||
|
||||
case HMSAction.DBL_CHECK_OK:
|
||||
clean_print_error()
|
||||
uiop_close()
|
||||
|
||||
case HMSAction.DBL_CHECK_RESUME:
|
||||
# Plain resume — not HMS-aware, no err/job_id.
|
||||
publish(
|
||||
{
|
||||
"print": {
|
||||
"command": "resume",
|
||||
"param": "",
|
||||
"sequence_id": "0",
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
case HMSAction.REFRESH_NOZZLE:
|
||||
publish({"print": {"command": "refresh_nozzle", "sequence_id": "0"}})
|
||||
|
||||
case HMSAction.TURN_OFF_FIRE_ALARM:
|
||||
publish({"print": {"command": "buzzer_ctrl", "mode": 0, "sequence_id": "0"}})
|
||||
|
||||
case HMSAction.STOP_DRYING:
|
||||
publish({"print": {"command": "auto_stop_ams_dry", "sequence_id": "0"}})
|
||||
|
||||
case HMSAction.DISABLE_PURIFICATION:
|
||||
publish({"print": {"command": "close_air_filt", "sequence_id": "0"}})
|
||||
|
||||
case (
|
||||
HMSAction.CHECK_ASSISTANT
|
||||
| HMSAction.JUMP_TO_LIVEVIEW
|
||||
| HMSAction.OK_JUMP_RACK
|
||||
| HMSAction.REMOVE_CLOSE_BTN
|
||||
| HMSAction.LOAD_VIRTUAL_TRAY
|
||||
| HMSAction.CANCLE
|
||||
| HMSAction.DBL_CHECK_CANCEL
|
||||
):
|
||||
# UI-only actions — the printer's own screen handles these; the
|
||||
# modal still surfaces them so the user has parity with Studio.
|
||||
pass
|
||||
|
||||
case _:
|
||||
logger.warning("[%s] Unknown HMS action '%s'", self.serial_number, action)
|
||||
return False
|
||||
|
||||
return True
|
||||
|
|
|
|||
75
backend/app/services/hms_actions.py
Normal file
75
backend/app/services/hms_actions.py
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
"""HMS action lookup.
|
||||
|
||||
Bambu printers report HMS errors with a fixed catalog of remediation actions
|
||||
(resume / stop / check assistant / etc.). The catalog is bundled as JSON, keyed
|
||||
by the 3-letter SN prefix (printer model code: 03W = A1, 31B = X1C, etc.) and
|
||||
the short error code with no separator.
|
||||
|
||||
The action IDs and their string names are derived from BambuStudio's source via
|
||||
`scripts/update_hms_actions.py`. The data file itself is fetched from Bambu's
|
||||
public `e.bambulab.com/hms/GetActionImage.php` endpoint.
|
||||
"""
|
||||
|
||||
import json
|
||||
from enum import StrEnum
|
||||
from pathlib import Path
|
||||
|
||||
_DATA_FILE = Path(__file__).resolve().parent.parent / "data" / "hms_actions.json"
|
||||
|
||||
# Loaded eagerly at import — the file is ~150KB and only read once. Using an
|
||||
# absolute path keeps the load independent of CWD (systemd unit, Docker
|
||||
# entrypoint, pytest run from `backend/`).
|
||||
with _DATA_FILE.open("r", encoding="utf-8") as _f:
|
||||
_actions: dict[str, dict[str, list[str]]] = json.load(_f)
|
||||
|
||||
|
||||
class HMSAction(StrEnum):
|
||||
"""Remediation actions a Bambu printer can offer for an HMS error.
|
||||
|
||||
Values intentionally match the constants used in BambuStudio's source so the
|
||||
HMS-data fetcher can map Bambu's integer action IDs straight to these
|
||||
strings. The CANCLE typo is preserved verbatim — it's how BambuStudio spells
|
||||
it, and changing it would break the action lookup against the catalog.
|
||||
"""
|
||||
|
||||
RESUME_PRINTING = "RESUME_PRINTING"
|
||||
RESUME_PRINTING_DEFECTS = "RESUME_PRINTING_DEFECTS"
|
||||
RESUME_PRINTING_PROBELM_SOLVED = "RESUME_PRINTING_PROBELM_SOLVED"
|
||||
STOP_PRINTING = "STOP_PRINTING"
|
||||
CHECK_ASSISTANT = "CHECK_ASSISTANT"
|
||||
FILAMENT_EXTRUDED = "FILAMENT_EXTRUDED"
|
||||
RETRY_FILAMENT_EXTRUDED = "RETRY_FILAMENT_EXTRUDED"
|
||||
CONTINUE = "CONTINUE"
|
||||
LOAD_VIRTUAL_TRAY = "LOAD_VIRTUAL_TRAY"
|
||||
OK_BUTTON = "OK_BUTTON"
|
||||
FILAMENT_LOAD_RESUME = "FILAMENT_LOAD_RESUME"
|
||||
JUMP_TO_LIVEVIEW = "JUMP_TO_LIVEVIEW"
|
||||
NO_REMINDER_NEXT_TIME = "NO_REMINDER_NEXT_TIME"
|
||||
REFRESH_NOZZLE = "REFRESH_NOZZLE"
|
||||
IGNORE_NO_REMINDER_NEXT_TIME = "IGNORE_NO_REMINDER_NEXT_TIME"
|
||||
IGNORE_RESUME = "IGNORE_RESUME"
|
||||
PROBLEM_SOLVED_RESUME = "PROBLEM_SOLVED_RESUME"
|
||||
TURN_OFF_FIRE_ALARM = "TURN_OFF_FIRE_ALARM"
|
||||
RETRY_PROBLEM_SOLVED = "RETRY_PROBLEM_SOLVED"
|
||||
STOP_DRYING = "STOP_DRYING"
|
||||
CANCLE = "CANCLE" # sic — verbatim from BambuStudio
|
||||
REMOVE_CLOSE_BTN = "REMOVE_CLOSE_BTN"
|
||||
PROCEED = "PROCEED"
|
||||
OK_JUMP_RACK = "OK_JUMP_RACK"
|
||||
ABORT = "ABORT"
|
||||
DISABLE_PURIFICATION = "DISABLE_PURIFICATION"
|
||||
DONT_REMIND_NEXT_TIME = "DONT_REMIND_NEXT_TIME"
|
||||
DBL_CHECK_CANCEL = "DBL_CHECK_CANCEL"
|
||||
DBL_CHECK_DONE = "DBL_CHECK_DONE"
|
||||
DBL_CHECK_RETRY = "DBL_CHECK_RETRY"
|
||||
DBL_CHECK_RESUME = "DBL_CHECK_RESUME"
|
||||
DBL_CHECK_OK = "DBL_CHECK_OK"
|
||||
|
||||
|
||||
def get_actions_for_error_code(device: str, error_code: str) -> list[str]:
|
||||
"""Look up the action list for a printer SN prefix + short error code.
|
||||
|
||||
Returns the empty list if the printer model or the error code is unknown —
|
||||
the modal renders no buttons in that case, which is the correct fallback.
|
||||
"""
|
||||
return _actions.get(device, {}).get(error_code, [])
|
||||
|
|
@ -1137,7 +1137,14 @@ def printer_state_to_dict(
|
|||
"total_layers": state.total_layers,
|
||||
"temperatures": temperatures,
|
||||
"hms_errors": [
|
||||
{"code": e.code, "attr": e.attr, "module": e.module, "severity": e.severity}
|
||||
{
|
||||
"code": e.code,
|
||||
"attr": e.attr,
|
||||
"module": e.module,
|
||||
"severity": e.severity,
|
||||
"actions": e.actions,
|
||||
"job_id": e.job_id,
|
||||
}
|
||||
for e in (state.hms_errors or [])
|
||||
],
|
||||
# AMS data for filament colors
|
||||
|
|
|
|||
229
backend/tests/unit/services/test_hms_actions.py
Normal file
229
backend/tests/unit/services/test_hms_actions.py
Normal file
|
|
@ -0,0 +1,229 @@
|
|||
"""Tests for HMS-action lookup and the MQTT dispatcher in execute_hms_action.
|
||||
|
||||
The lookup tests confirm the bundled catalog round-trips correctly. The
|
||||
dispatcher tests are payload-shape contracts — wrong shape sends a bogus
|
||||
command to the printer, which is the failure mode this PR is most exposed to,
|
||||
so each HMSAction case publishes the expected JSON.
|
||||
"""
|
||||
|
||||
import json
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
import pytest
|
||||
|
||||
from backend.app.services.bambu_mqtt import BambuMQTTClient
|
||||
from backend.app.services.hms_actions import (
|
||||
HMSAction,
|
||||
get_actions_for_error_code,
|
||||
)
|
||||
|
||||
|
||||
class TestActionLookup:
|
||||
def test_known_a1_error_returns_actions(self):
|
||||
# 03W is the A1 model code; 03008070 is "Heat the nozzle…" and Bambu's
|
||||
# catalog lists CHECK_ASSISTANT for it.
|
||||
actions = get_actions_for_error_code("03W", "03008070")
|
||||
assert isinstance(actions, list)
|
||||
assert len(actions) > 0
|
||||
for a in actions:
|
||||
assert isinstance(a, str)
|
||||
|
||||
def test_unknown_device_returns_empty_list(self):
|
||||
assert get_actions_for_error_code("ZZZ", "03008070") == []
|
||||
|
||||
def test_unknown_error_returns_empty_list(self):
|
||||
# Real model code, made-up error.
|
||||
assert get_actions_for_error_code("03W", "DEADBEEF") == []
|
||||
|
||||
def test_underscore_form_does_not_match(self):
|
||||
# Caller is responsible for stripping the `_` before lookup. Guards
|
||||
# against accidental rewires that pass the underscore form.
|
||||
assert get_actions_for_error_code("03W", "0300_8070") == []
|
||||
|
||||
def test_action_enum_values_are_uppercase_strings(self):
|
||||
# The catalog stores actions verbatim from BambuStudio. Drift here
|
||||
# silently breaks the dispatcher's `match` because StrEnum compares
|
||||
# by value.
|
||||
assert HMSAction.RESUME_PRINTING == "RESUME_PRINTING"
|
||||
assert HMSAction.CANCLE == "CANCLE" # sic — kept from BambuStudio
|
||||
|
||||
|
||||
class TestExecuteHmsActionDispatch:
|
||||
"""Each case in the `match` publishes a specific JSON shape. These tests
|
||||
pin those shapes so silent regressions surface as test failures, not as
|
||||
a printer receiving a malformed command on a live print.
|
||||
"""
|
||||
|
||||
@pytest.fixture
|
||||
def client(self):
|
||||
c = BambuMQTTClient(
|
||||
ip_address="192.168.1.100",
|
||||
serial_number="03W-TEST",
|
||||
access_code="12345678",
|
||||
)
|
||||
c._client = MagicMock()
|
||||
c.state.connected = True
|
||||
return c
|
||||
|
||||
def _published_commands(self, client):
|
||||
"""Return the list of `print`/`system` command dicts from publish calls,
|
||||
skipping the `pushing.pushall` echoes that follow every action."""
|
||||
out = []
|
||||
for call in client._client.publish.call_args_list:
|
||||
_topic, payload = call.args[0], call.args[1]
|
||||
data = json.loads(payload)
|
||||
if "pushing" in data:
|
||||
continue
|
||||
out.append(data)
|
||||
return out
|
||||
|
||||
def test_returns_false_when_disconnected(self, client):
|
||||
client.state.connected = False
|
||||
assert client.execute_hms_action("03008070", HMSAction.OK_BUTTON) is False
|
||||
client._client.publish.assert_not_called()
|
||||
|
||||
def test_returns_false_on_unknown_action(self, client):
|
||||
assert client.execute_hms_action("03008070", "DOES_NOT_EXIST") is False
|
||||
# No printer command, but the publish-list check tolerates the pushall
|
||||
# tail — just confirm no command went out by inspecting the helper.
|
||||
assert self._published_commands(client) == []
|
||||
|
||||
def test_resume_carries_err_param_and_job_id(self, client):
|
||||
ok = client.execute_hms_action("03008070", HMSAction.RESUME_PRINTING, job_id="task-42")
|
||||
assert ok is True
|
||||
cmds = self._published_commands(client)
|
||||
assert cmds == [
|
||||
{
|
||||
"print": {
|
||||
"command": "resume",
|
||||
"err": "03008070",
|
||||
"param": "reserve",
|
||||
"job_id": "task-42",
|
||||
"sequence_id": "0",
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
def test_proceed_falls_through_to_resume(self, client):
|
||||
client.execute_hms_action("03008070", HMSAction.PROCEED, job_id="task-1")
|
||||
cmds = self._published_commands(client)
|
||||
assert cmds[0]["print"]["command"] == "resume"
|
||||
assert cmds[0]["print"]["err"] == "03008070"
|
||||
|
||||
def test_stop_carries_err_and_job_id(self, client):
|
||||
client.execute_hms_action("03008070", HMSAction.STOP_PRINTING, job_id="task-1")
|
||||
cmds = self._published_commands(client)
|
||||
assert cmds[0]["print"]["command"] == "stop"
|
||||
assert cmds[0]["print"]["job_id"] == "task-1"
|
||||
|
||||
def test_ignore_resume_uses_idle_ignore_type_zero(self, client):
|
||||
client.execute_hms_action("03008070", HMSAction.IGNORE_RESUME)
|
||||
cmds = self._published_commands(client)
|
||||
assert cmds[0] == {
|
||||
"print": {
|
||||
"command": "idle_ignore",
|
||||
"err": "03008070",
|
||||
"type": 0,
|
||||
"sequence_id": "0",
|
||||
}
|
||||
}
|
||||
|
||||
def test_dont_remind_uses_idle_ignore_type_one(self, client):
|
||||
# DONT_REMIND_NEXT_TIME and IGNORE_NO_REMINDER_NEXT_TIME are the
|
||||
# persistent variants — Bambu hides the warning for future prints.
|
||||
client.execute_hms_action("03008070", HMSAction.DONT_REMIND_NEXT_TIME)
|
||||
cmds = self._published_commands(client)
|
||||
assert cmds[0]["print"]["command"] == "idle_ignore"
|
||||
assert cmds[0]["print"]["type"] == 1
|
||||
|
||||
def test_filament_extruded_sends_ams_done(self, client):
|
||||
client.execute_hms_action("07008029", HMSAction.FILAMENT_EXTRUDED)
|
||||
cmds = self._published_commands(client)
|
||||
assert cmds[0] == {"print": {"command": "ams_control", "param": "done", "sequence_id": "0"}}
|
||||
|
||||
def test_retry_sends_ams_resume(self, client):
|
||||
client.execute_hms_action("07008029", HMSAction.RETRY_FILAMENT_EXTRUDED)
|
||||
cmds = self._published_commands(client)
|
||||
assert cmds[0]["print"]["param"] == "resume"
|
||||
assert cmds[0]["print"]["command"] == "ams_control"
|
||||
|
||||
def test_abort_sends_ams_abort(self, client):
|
||||
client.execute_hms_action("07008029", HMSAction.ABORT)
|
||||
cmds = self._published_commands(client)
|
||||
assert cmds[0]["print"]["param"] == "abort"
|
||||
|
||||
def test_ok_button_sends_bare_clean_print_error(self, client):
|
||||
# Matches the existing `clear_hms_errors` shape — no `print_error` body
|
||||
# field, which the original PR mistakenly added.
|
||||
client.execute_hms_action("03008070", HMSAction.OK_BUTTON)
|
||||
cmds = self._published_commands(client)
|
||||
assert cmds[0] == {"print": {"command": "clean_print_error", "sequence_id": "0"}}
|
||||
|
||||
def test_dbl_check_ok_sends_clean_then_uiop_close(self, client):
|
||||
client.execute_hms_action("03008070", HMSAction.DBL_CHECK_OK)
|
||||
cmds = self._published_commands(client)
|
||||
assert len(cmds) == 2
|
||||
assert cmds[0]["print"]["command"] == "clean_print_error"
|
||||
assert cmds[1]["system"]["command"] == "uiop"
|
||||
# `err` is the already-string short code, NOT `f"{x:08X}"` against a
|
||||
# str (which would TypeError on the old code path).
|
||||
assert cmds[1]["system"]["err"] == "03008070"
|
||||
|
||||
def test_uiop_close_uppercases_lowercase_input(self, client):
|
||||
# Frontend may send the short code in either case; we normalise.
|
||||
client.execute_hms_action("0300abcd", HMSAction.DBL_CHECK_OK)
|
||||
cmds = self._published_commands(client)
|
||||
assert cmds[1]["system"]["err"] == "0300ABCD"
|
||||
|
||||
def test_dbl_check_resume_is_plain_resume(self, client):
|
||||
# No err/job_id — explicitly different from RESUME_PRINTING.
|
||||
client.execute_hms_action("03008070", HMSAction.DBL_CHECK_RESUME)
|
||||
cmds = self._published_commands(client)
|
||||
assert cmds[0] == {"print": {"command": "resume", "param": "", "sequence_id": "0"}}
|
||||
assert "err" not in cmds[0]["print"]
|
||||
|
||||
def test_refresh_nozzle(self, client):
|
||||
client.execute_hms_action("03008070", HMSAction.REFRESH_NOZZLE)
|
||||
cmds = self._published_commands(client)
|
||||
assert cmds[0] == {"print": {"command": "refresh_nozzle", "sequence_id": "0"}}
|
||||
|
||||
def test_turn_off_fire_alarm_sends_buzzer_off(self, client):
|
||||
client.execute_hms_action("03008044", HMSAction.TURN_OFF_FIRE_ALARM)
|
||||
cmds = self._published_commands(client)
|
||||
assert cmds[0]["print"]["command"] == "buzzer_ctrl"
|
||||
assert cmds[0]["print"]["mode"] == 0
|
||||
|
||||
def test_stop_drying_sends_auto_stop_ams_dry(self, client):
|
||||
client.execute_hms_action("07008017", HMSAction.STOP_DRYING)
|
||||
cmds = self._published_commands(client)
|
||||
assert cmds[0]["print"]["command"] == "auto_stop_ams_dry"
|
||||
|
||||
def test_disable_purification_sends_close_air_filt(self, client):
|
||||
client.execute_hms_action("03008063", HMSAction.DISABLE_PURIFICATION)
|
||||
cmds = self._published_commands(client)
|
||||
assert cmds[0]["print"]["command"] == "close_air_filt"
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"action",
|
||||
[
|
||||
HMSAction.CHECK_ASSISTANT,
|
||||
HMSAction.JUMP_TO_LIVEVIEW,
|
||||
HMSAction.OK_JUMP_RACK,
|
||||
HMSAction.REMOVE_CLOSE_BTN,
|
||||
HMSAction.LOAD_VIRTUAL_TRAY,
|
||||
HMSAction.CANCLE,
|
||||
HMSAction.DBL_CHECK_CANCEL,
|
||||
],
|
||||
)
|
||||
def test_ui_only_actions_publish_nothing(self, client, action):
|
||||
# These actions exist for parity with BambuStudio's modal but have no
|
||||
# MQTT counterpart — the printer's own screen drives them.
|
||||
assert client.execute_hms_action("03008070", action) is True
|
||||
assert self._published_commands(client) == []
|
||||
|
||||
def test_every_publish_is_followed_by_pushall(self, client):
|
||||
# The dispatcher pairs every command with a `pushing.pushall` echo so
|
||||
# the state stream refreshes on the next tick. Regression guard.
|
||||
client.execute_hms_action("03008070", HMSAction.RESUME_PRINTING)
|
||||
payloads = [json.loads(c.args[1]) for c in client._client.publish.call_args_list]
|
||||
assert any("pushing" in p for p in payloads)
|
||||
|
|
@ -333,6 +333,14 @@ export interface HMSError {
|
|||
attr: number; // Attribute value for constructing wiki URL
|
||||
module: number;
|
||||
severity: number; // 1=fatal, 2=serious, 3=common, 4=info
|
||||
actions?: string[]; // List of user-facing action keys (e.g. "CHECK_FILAMENT")
|
||||
job_id?: string; // Optional job ID for actions that require it (e.g. "CHECK_ASSISTANT")
|
||||
}
|
||||
|
||||
export interface HMSActionBody {
|
||||
print_error: string; // HMS error code (e.g. "05000070")
|
||||
action: string; // "HMS action to execute (e.g. 'resume_after_error')"
|
||||
job_id: string | null; // Optional job ID for context (if applicable)
|
||||
}
|
||||
|
||||
export interface AMSTray {
|
||||
|
|
@ -3703,6 +3711,11 @@ export const api = {
|
|||
// HMS Errors
|
||||
clearHMSErrors: (printerId: number) =>
|
||||
request<{ success: boolean; message: string }>(`/printers/${printerId}/hms/clear`, { method: 'POST' }),
|
||||
executeHMSAction: (printerId: number, data: HMSActionBody) =>
|
||||
request<{ success: boolean; message: string }>(`/printers/${printerId}/hms/execute-action`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(data),
|
||||
}),
|
||||
|
||||
// AMS Control
|
||||
refreshAmsSlot: (printerId: number, amsId: number, slotId: number) =>
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
// Source: https://github.com/greghesp/ha-bambulab
|
||||
import { useEffect } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useMutation } from '@tanstack/react-query';
|
||||
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import { X, AlertTriangle, AlertCircle, Info, ExternalLink, Loader2, Trash2 } from 'lucide-react';
|
||||
import type { HMSError, Permission } from '../api/client';
|
||||
import { api } from '../api/client';
|
||||
|
|
@ -874,17 +874,17 @@ const ERROR_DESCRIPTIONS: Record<string, string> = {
|
|||
'18FF_C00A': 'Please observe the nozzle of the right extruder. If the filament has been extruded, select \'Continue\'; if not, please push the filament forward slightly and then select \'Retry\'.',
|
||||
};
|
||||
|
||||
function getSeverityInfo(severity: number): { label: string; color: string; bgColor: string; Icon: typeof AlertTriangle } {
|
||||
function getSeverityInfo(severity: number): { label: string; color: string; bgColor: string; buttonHoverColor: string; Icon: typeof AlertTriangle } {
|
||||
switch (severity) {
|
||||
case 1:
|
||||
return { label: 'Fatal', color: 'text-red-500', bgColor: 'bg-red-500/20', Icon: AlertTriangle };
|
||||
return { label: 'Fatal', color: 'text-red-500', bgColor: 'bg-red-500/20', buttonHoverColor: 'bg-red-500/10', Icon: AlertTriangle };
|
||||
case 2:
|
||||
return { label: 'Serious', color: 'text-red-400', bgColor: 'bg-red-500/15', Icon: AlertTriangle };
|
||||
return { label: 'Serious', color: 'text-red-400', bgColor: 'bg-red-500/15', buttonHoverColor: 'bg-red-500/10', Icon: AlertTriangle };
|
||||
case 3:
|
||||
return { label: 'Warning', color: 'text-orange-400', bgColor: 'bg-orange-500/20', Icon: AlertCircle };
|
||||
return { label: 'Warning', color: 'text-orange-400', bgColor: 'bg-orange-500/20', buttonHoverColor: 'bg-orange-500/10', Icon: AlertCircle };
|
||||
case 4:
|
||||
default:
|
||||
return { label: 'Info', color: 'text-blue-400', bgColor: 'bg-blue-500/20', Icon: Info };
|
||||
return { label: 'Info', color: 'text-blue-400', bgColor: 'bg-blue-500/20', buttonHoverColor: 'bg-blue-500/10', Icon: Info };
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -912,6 +912,7 @@ function getHMSHomeUrl(): string {
|
|||
export function HMSErrorModal({ printerName, errors, onClose, printerId, hasPermission }: HMSErrorModalProps) {
|
||||
const { t } = useTranslation();
|
||||
const { showToast } = useToast();
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
const clearMutation = useMutation({
|
||||
mutationFn: () => api.clearHMSErrors(printerId),
|
||||
|
|
@ -940,6 +941,30 @@ export function HMSErrorModal({ printerName, errors, onClose, printerId, hasPerm
|
|||
return () => window.removeEventListener('keydown', handleKeyDown);
|
||||
}, [onClose]);
|
||||
|
||||
// printerStatusMutation with optimistic update
|
||||
const activateActionMutation = useMutation({
|
||||
mutationFn: (data: {
|
||||
action: string,
|
||||
print_error: string,
|
||||
job_id: string | null,
|
||||
}) => api.executeHMSAction(printerId, {
|
||||
action: data.action,
|
||||
print_error: data.print_error,
|
||||
job_id: data.job_id,
|
||||
}),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['printerStatus'] });
|
||||
showToast(t('hmsErrors.actionSuccess', 'Action sent to printer'), 'success');
|
||||
onClose();
|
||||
},
|
||||
onError: (error: Error) => {
|
||||
showToast(
|
||||
`${t('hmsErrors.actionFailed', 'Failed to send action')}: ${error.message}`,
|
||||
'error',
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50 p-4">
|
||||
<div className="bg-bambu-dark-secondary rounded-lg shadow-xl max-w-lg w-full max-h-[80vh] flex flex-col">
|
||||
|
|
@ -967,7 +992,7 @@ export function HMSErrorModal({ printerName, errors, onClose, printerId, hasPerm
|
|||
) : (
|
||||
<div className="space-y-3">
|
||||
{knownErrors.map((error, index) => {
|
||||
const { label, color, bgColor, Icon } = getSeverityInfo(error.severity);
|
||||
const { label, color, bgColor, buttonHoverColor, Icon } = getSeverityInfo(error.severity);
|
||||
const codeNum = parseInt(error.code.replace('0x', ''), 16) || 0;
|
||||
const shortCode = getShortCode(error.attr, codeNum);
|
||||
const description = ERROR_DESCRIPTIONS[shortCode];
|
||||
|
|
@ -989,6 +1014,25 @@ export function HMSErrorModal({ printerName, errors, onClose, printerId, hasPerm
|
|||
</span>
|
||||
</div>
|
||||
<p className="text-sm text-bambu-gray mb-2">{description}</p>
|
||||
{error.actions && error.actions.length > 0 && (
|
||||
<div className="flex flex-wrap gap-2 my-2">
|
||||
{error.actions.map((action) => (
|
||||
<button
|
||||
key={action}
|
||||
onClick={() => {
|
||||
activateActionMutation.mutate({
|
||||
action,
|
||||
print_error: shortCode.replace("_", ""),
|
||||
job_id: error.job_id ?? null,
|
||||
});
|
||||
}}
|
||||
className={`flex items-center gap-1.5 px-3 py-1.5 text-sm font-medium rounded-lg ${bgColor} ${color} hover:${buttonHoverColor} transition-colors disabled:opacity-50 disabled:cursor-not-allowed flex-shrink-0`}
|
||||
>
|
||||
{t(`hmsErrors.actions.${action}`, action)}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
<a
|
||||
href={hmsHomeUrl}
|
||||
target="_blank"
|
||||
|
|
|
|||
|
|
@ -2586,6 +2586,42 @@ export default {
|
|||
clearErrors: 'Fehler löschen',
|
||||
clearSuccess: 'HMS-Fehler gelöscht',
|
||||
clearFailed: 'HMS-Fehler konnten nicht gelöscht werden',
|
||||
actionSuccess: 'Aktion an Drucker gesendet',
|
||||
actionFailed: 'Aktion konnte nicht gesendet werden',
|
||||
actions: {
|
||||
RESUME_PRINTING: 'Druck fortsetzen',
|
||||
RESUME_PRINTING_DEFECTS: 'Fortsetzen (Mängel akzeptabel)',
|
||||
RESUME_PRINTING_PROBELM_SOLVED: 'Fortsetzen (Problem gelöst)',
|
||||
STOP_PRINTING: 'Druck stoppen',
|
||||
CHECK_ASSISTANT: 'Assistent öffnen',
|
||||
FILAMENT_EXTRUDED: 'Filament extrudiert, weiter',
|
||||
RETRY_FILAMENT_EXTRUDED: 'Noch nicht extrudiert, erneut',
|
||||
CONTINUE: 'Fertig, weiter',
|
||||
LOAD_VIRTUAL_TRAY: 'Filament laden',
|
||||
OK_BUTTON: 'OK',
|
||||
FILAMENT_LOAD_RESUME: 'Filament geladen, fortsetzen',
|
||||
JUMP_TO_LIVEVIEW: 'Live-Ansicht öffnen',
|
||||
NO_REMINDER_NEXT_TIME: 'Nicht mehr erinnern',
|
||||
REFRESH_NOZZLE: 'Erneut prüfen',
|
||||
IGNORE_NO_REMINDER_NEXT_TIME: 'Ignorieren und nicht mehr erinnern',
|
||||
IGNORE_RESUME: 'Ignorieren und fortsetzen',
|
||||
PROBLEM_SOLVED_RESUME: 'Problem gelöst, fortsetzen',
|
||||
TURN_OFF_FIRE_ALARM: 'Verstanden, Brandalarm ausschalten',
|
||||
RETRY_PROBLEM_SOLVED: 'Erneut versuchen (Problem gelöst)',
|
||||
CANCLE: 'Abbrechen',
|
||||
STOP_DRYING: 'Trocknen stoppen',
|
||||
PROCEED: 'Fortfahren',
|
||||
OK_JUMP_RACK: 'OK',
|
||||
ABORT: 'Abbrechen',
|
||||
DISABLE_PURIFICATION: 'Luftreinigung für diesen Druck deaktivieren',
|
||||
DONT_REMIND_NEXT_TIME: 'Nicht mehr erinnern',
|
||||
DBL_CHECK_CANCEL: 'Abbrechen',
|
||||
DBL_CHECK_DONE: 'Fertig',
|
||||
DBL_CHECK_RETRY: 'Erneut versuchen',
|
||||
DBL_CHECK_RESUME: 'Fortsetzen',
|
||||
DBL_CHECK_OK: 'Bestätigen',
|
||||
REMOVE_CLOSE_BTN: 'Schließen',
|
||||
},
|
||||
},
|
||||
|
||||
// MQTT Debug modal
|
||||
|
|
|
|||
|
|
@ -2601,6 +2601,42 @@ export default {
|
|||
clearErrors: 'Clear Errors',
|
||||
clearSuccess: 'HMS errors cleared',
|
||||
clearFailed: 'Failed to clear HMS errors',
|
||||
actionSuccess: 'Action sent to printer',
|
||||
actionFailed: 'Failed to send action',
|
||||
actions: {
|
||||
RESUME_PRINTING: "Resume Printing",
|
||||
RESUME_PRINTING_DEFECTS: "Resume (defects acceptable)",
|
||||
RESUME_PRINTING_PROBELM_SOLVED: "Resume (problem solved)",
|
||||
STOP_PRINTING: "Stop Printing",
|
||||
CHECK_ASSISTANT: "Check Assistant",
|
||||
FILAMENT_EXTRUDED: "Filament Extruded, Continue",
|
||||
RETRY_FILAMENT_EXTRUDED: "Not Extruded Yet, Retry",
|
||||
CONTINUE: "Finished, Continue",
|
||||
LOAD_VIRTUAL_TRAY: "Load Filament",
|
||||
OK_BUTTON: "OK",
|
||||
FILAMENT_LOAD_RESUME: "Filament Loaded, Resume",
|
||||
JUMP_TO_LIVEVIEW: "View Liveview",
|
||||
NO_REMINDER_NEXT_TIME: "No Reminder Next Time",
|
||||
REFRESH_NOZZLE: "Recheck",
|
||||
IGNORE_NO_REMINDER_NEXT_TIME: "Ignore. Don't Remind Next Time",
|
||||
IGNORE_RESUME: "Ignore this and Resume",
|
||||
PROBLEM_SOLVED_RESUME: "Problem Solved and Resume",
|
||||
TURN_OFF_FIRE_ALARM: "Got it, Turn off the Fire Alarm.",
|
||||
RETRY_PROBLEM_SOLVED: "Retry (problem solved)",
|
||||
CANCLE: "Cancle",
|
||||
STOP_DRYING: "Stop Drying",
|
||||
PROCEED: "Proceed",
|
||||
OK_JUMP_RACK: "OK",
|
||||
ABORT: "Abort",
|
||||
DISABLE_PURIFICATION: "Disable Purification for This Print",
|
||||
DONT_REMIND_NEXT_TIME: "Don't Remind Me",
|
||||
DBL_CHECK_CANCEL: "Cancel",
|
||||
DBL_CHECK_DONE: "Done",
|
||||
DBL_CHECK_RETRY: "Retry",
|
||||
DBL_CHECK_RESUME: "Resume",
|
||||
DBL_CHECK_OK: "Confirm",
|
||||
REMOVE_CLOSE_BTN: "Close",
|
||||
}
|
||||
},
|
||||
|
||||
// MQTT Debug modal
|
||||
|
|
|
|||
|
|
@ -2589,6 +2589,42 @@ export default {
|
|||
clearErrors: 'Borrar errores',
|
||||
clearSuccess: 'Errores HMS borrados',
|
||||
clearFailed: 'Error al borrar los errores HMS',
|
||||
actionSuccess: 'Acción enviada a la impresora',
|
||||
actionFailed: 'No se pudo enviar la acción',
|
||||
actions: {
|
||||
RESUME_PRINTING: 'Reanudar impresión',
|
||||
RESUME_PRINTING_DEFECTS: 'Reanudar (defectos aceptables)',
|
||||
RESUME_PRINTING_PROBELM_SOLVED: 'Reanudar (problema resuelto)',
|
||||
STOP_PRINTING: 'Detener impresión',
|
||||
CHECK_ASSISTANT: 'Ver asistente',
|
||||
FILAMENT_EXTRUDED: 'Filamento extruido, continuar',
|
||||
RETRY_FILAMENT_EXTRUDED: 'Aún no extruido, reintentar',
|
||||
CONTINUE: 'Finalizado, continuar',
|
||||
LOAD_VIRTUAL_TRAY: 'Cargar filamento',
|
||||
OK_BUTTON: 'OK',
|
||||
FILAMENT_LOAD_RESUME: 'Filamento cargado, reanudar',
|
||||
JUMP_TO_LIVEVIEW: 'Ver en vivo',
|
||||
NO_REMINDER_NEXT_TIME: 'No recordar la próxima vez',
|
||||
REFRESH_NOZZLE: 'Volver a comprobar',
|
||||
IGNORE_NO_REMINDER_NEXT_TIME: 'Ignorar y no recordar',
|
||||
IGNORE_RESUME: 'Ignorar y reanudar',
|
||||
PROBLEM_SOLVED_RESUME: 'Problema resuelto, reanudar',
|
||||
TURN_OFF_FIRE_ALARM: 'Entendido, apagar alarma de incendio',
|
||||
RETRY_PROBLEM_SOLVED: 'Reintentar (problema resuelto)',
|
||||
CANCLE: 'Cancelar',
|
||||
STOP_DRYING: 'Detener secado',
|
||||
PROCEED: 'Continuar',
|
||||
OK_JUMP_RACK: 'OK',
|
||||
ABORT: 'Cancelar',
|
||||
DISABLE_PURIFICATION: 'Desactivar purificación para esta impresión',
|
||||
DONT_REMIND_NEXT_TIME: 'No recordarme',
|
||||
DBL_CHECK_CANCEL: 'Cancelar',
|
||||
DBL_CHECK_DONE: 'Hecho',
|
||||
DBL_CHECK_RETRY: 'Reintentar',
|
||||
DBL_CHECK_RESUME: 'Reanudar',
|
||||
DBL_CHECK_OK: 'Confirmar',
|
||||
REMOVE_CLOSE_BTN: 'Cerrar',
|
||||
},
|
||||
},
|
||||
|
||||
// MQTT Debug modal
|
||||
|
|
|
|||
|
|
@ -2575,6 +2575,42 @@ export default {
|
|||
clearErrors: 'Effacer les erreurs',
|
||||
clearSuccess: 'Erreurs HMS effacées',
|
||||
clearFailed: 'Échec de l\'effacement des erreurs HMS',
|
||||
actionSuccess: 'Action envoyée à l\'imprimante',
|
||||
actionFailed: 'Échec de l\'envoi de l\'action',
|
||||
actions: {
|
||||
RESUME_PRINTING: 'Reprendre l\'impression',
|
||||
RESUME_PRINTING_DEFECTS: 'Reprendre (défauts acceptables)',
|
||||
RESUME_PRINTING_PROBELM_SOLVED: 'Reprendre (problème résolu)',
|
||||
STOP_PRINTING: 'Arrêter l\'impression',
|
||||
CHECK_ASSISTANT: 'Ouvrir l\'assistant',
|
||||
FILAMENT_EXTRUDED: 'Filament extrudé, continuer',
|
||||
RETRY_FILAMENT_EXTRUDED: 'Pas encore extrudé, réessayer',
|
||||
CONTINUE: 'Terminé, continuer',
|
||||
LOAD_VIRTUAL_TRAY: 'Charger le filament',
|
||||
OK_BUTTON: 'OK',
|
||||
FILAMENT_LOAD_RESUME: 'Filament chargé, reprendre',
|
||||
JUMP_TO_LIVEVIEW: 'Voir en direct',
|
||||
NO_REMINDER_NEXT_TIME: 'Ne plus rappeler la prochaine fois',
|
||||
REFRESH_NOZZLE: 'Revérifier',
|
||||
IGNORE_NO_REMINDER_NEXT_TIME: 'Ignorer et ne plus rappeler',
|
||||
IGNORE_RESUME: 'Ignorer et reprendre',
|
||||
PROBLEM_SOLVED_RESUME: 'Problème résolu, reprendre',
|
||||
TURN_OFF_FIRE_ALARM: 'Compris, désactiver l\'alarme incendie',
|
||||
RETRY_PROBLEM_SOLVED: 'Réessayer (problème résolu)',
|
||||
CANCLE: 'Annuler',
|
||||
STOP_DRYING: 'Arrêter le séchage',
|
||||
PROCEED: 'Continuer',
|
||||
OK_JUMP_RACK: 'OK',
|
||||
ABORT: 'Annuler',
|
||||
DISABLE_PURIFICATION: 'Désactiver la purification pour cette impression',
|
||||
DONT_REMIND_NEXT_TIME: 'Ne plus me rappeler',
|
||||
DBL_CHECK_CANCEL: 'Annuler',
|
||||
DBL_CHECK_DONE: 'Terminé',
|
||||
DBL_CHECK_RETRY: 'Réessayer',
|
||||
DBL_CHECK_RESUME: 'Reprendre',
|
||||
DBL_CHECK_OK: 'Confirmer',
|
||||
REMOVE_CLOSE_BTN: 'Fermer',
|
||||
},
|
||||
},
|
||||
|
||||
// MQTT Debug modal
|
||||
|
|
|
|||
|
|
@ -2574,6 +2574,42 @@ export default {
|
|||
clearErrors: 'Cancella errori',
|
||||
clearSuccess: 'Errori HMS cancellati',
|
||||
clearFailed: 'Impossibile cancellare gli errori HMS',
|
||||
actionSuccess: 'Azione inviata alla stampante',
|
||||
actionFailed: 'Impossibile inviare l\'azione',
|
||||
actions: {
|
||||
RESUME_PRINTING: 'Riprendi stampa',
|
||||
RESUME_PRINTING_DEFECTS: 'Riprendi (difetti accettabili)',
|
||||
RESUME_PRINTING_PROBELM_SOLVED: 'Riprendi (problema risolto)',
|
||||
STOP_PRINTING: 'Ferma stampa',
|
||||
CHECK_ASSISTANT: 'Apri assistente',
|
||||
FILAMENT_EXTRUDED: 'Filamento estruso, continua',
|
||||
RETRY_FILAMENT_EXTRUDED: 'Non ancora estruso, riprova',
|
||||
CONTINUE: 'Completato, continua',
|
||||
LOAD_VIRTUAL_TRAY: 'Carica filamento',
|
||||
OK_BUTTON: 'OK',
|
||||
FILAMENT_LOAD_RESUME: 'Filamento caricato, riprendi',
|
||||
JUMP_TO_LIVEVIEW: 'Visualizza in diretta',
|
||||
NO_REMINDER_NEXT_TIME: 'Non ricordare la prossima volta',
|
||||
REFRESH_NOZZLE: 'Ricontrolla',
|
||||
IGNORE_NO_REMINDER_NEXT_TIME: 'Ignora e non ricordare',
|
||||
IGNORE_RESUME: 'Ignora e riprendi',
|
||||
PROBLEM_SOLVED_RESUME: 'Problema risolto, riprendi',
|
||||
TURN_OFF_FIRE_ALARM: 'Capito, spegni allarme antincendio',
|
||||
RETRY_PROBLEM_SOLVED: 'Riprova (problema risolto)',
|
||||
CANCLE: 'Annulla',
|
||||
STOP_DRYING: 'Ferma essiccazione',
|
||||
PROCEED: 'Procedi',
|
||||
OK_JUMP_RACK: 'OK',
|
||||
ABORT: 'Annulla',
|
||||
DISABLE_PURIFICATION: 'Disattiva purificazione per questa stampa',
|
||||
DONT_REMIND_NEXT_TIME: 'Non ricordarmelo',
|
||||
DBL_CHECK_CANCEL: 'Annulla',
|
||||
DBL_CHECK_DONE: 'Fatto',
|
||||
DBL_CHECK_RETRY: 'Riprova',
|
||||
DBL_CHECK_RESUME: 'Riprendi',
|
||||
DBL_CHECK_OK: 'Conferma',
|
||||
REMOVE_CLOSE_BTN: 'Chiudi',
|
||||
},
|
||||
},
|
||||
|
||||
// MQTT Debug modal
|
||||
|
|
|
|||
|
|
@ -2586,6 +2586,42 @@ export default {
|
|||
clearErrors: 'エラーをクリア',
|
||||
clearSuccess: 'HMSエラーをクリアしました',
|
||||
clearFailed: 'HMSエラーのクリアに失敗しました',
|
||||
actionSuccess: 'アクションをプリンターに送信しました',
|
||||
actionFailed: 'アクションの送信に失敗しました',
|
||||
actions: {
|
||||
RESUME_PRINTING: '印刷を再開',
|
||||
RESUME_PRINTING_DEFECTS: '再開(不具合を許容)',
|
||||
RESUME_PRINTING_PROBELM_SOLVED: '再開(問題解決)',
|
||||
STOP_PRINTING: '印刷を停止',
|
||||
CHECK_ASSISTANT: 'アシスタントを開く',
|
||||
FILAMENT_EXTRUDED: 'フィラメント排出済み、続行',
|
||||
RETRY_FILAMENT_EXTRUDED: 'まだ排出されていない、再試行',
|
||||
CONTINUE: '完了、続行',
|
||||
LOAD_VIRTUAL_TRAY: 'フィラメントを装填',
|
||||
OK_BUTTON: 'OK',
|
||||
FILAMENT_LOAD_RESUME: 'フィラメント装填完了、再開',
|
||||
JUMP_TO_LIVEVIEW: 'ライブビューを表示',
|
||||
NO_REMINDER_NEXT_TIME: '次回は通知しない',
|
||||
REFRESH_NOZZLE: '再確認',
|
||||
IGNORE_NO_REMINDER_NEXT_TIME: '無視して次回は通知しない',
|
||||
IGNORE_RESUME: '無視して再開',
|
||||
PROBLEM_SOLVED_RESUME: '問題解決、再開',
|
||||
TURN_OFF_FIRE_ALARM: '了解、火災警報をオフ',
|
||||
RETRY_PROBLEM_SOLVED: '再試行(問題解決)',
|
||||
CANCLE: 'キャンセル',
|
||||
STOP_DRYING: '乾燥を停止',
|
||||
PROCEED: '続行',
|
||||
OK_JUMP_RACK: 'OK',
|
||||
ABORT: '中止',
|
||||
DISABLE_PURIFICATION: 'この印刷では空気清浄を無効化',
|
||||
DONT_REMIND_NEXT_TIME: '今後表示しない',
|
||||
DBL_CHECK_CANCEL: 'キャンセル',
|
||||
DBL_CHECK_DONE: '完了',
|
||||
DBL_CHECK_RETRY: '再試行',
|
||||
DBL_CHECK_RESUME: '再開',
|
||||
DBL_CHECK_OK: '確認',
|
||||
REMOVE_CLOSE_BTN: '閉じる',
|
||||
},
|
||||
},
|
||||
|
||||
// MQTT Debug modal
|
||||
|
|
|
|||
|
|
@ -2437,7 +2437,43 @@ export default {
|
|||
clearInstructions: '오류를 해제하려면 프린터에서 오류를 지우세요.',
|
||||
clearErrors: '오류 지우기',
|
||||
clearSuccess: 'HMS 오류가 지워졌습니다',
|
||||
clearFailed: 'HMS 오류 지우기 실패'
|
||||
clearFailed: 'HMS 오류 지우기 실패',
|
||||
actionSuccess: '프린터에 작업을 전송함',
|
||||
actionFailed: '작업 전송 실패',
|
||||
actions: {
|
||||
RESUME_PRINTING: '인쇄 재개',
|
||||
RESUME_PRINTING_DEFECTS: '재개 (결함 허용)',
|
||||
RESUME_PRINTING_PROBELM_SOLVED: '재개 (문제 해결됨)',
|
||||
STOP_PRINTING: '인쇄 중지',
|
||||
CHECK_ASSISTANT: '도우미 보기',
|
||||
FILAMENT_EXTRUDED: '필라멘트 압출됨, 계속',
|
||||
RETRY_FILAMENT_EXTRUDED: '아직 압출되지 않음, 다시 시도',
|
||||
CONTINUE: '완료, 계속',
|
||||
LOAD_VIRTUAL_TRAY: '필라멘트 로드',
|
||||
OK_BUTTON: '확인',
|
||||
FILAMENT_LOAD_RESUME: '필라멘트 로드됨, 재개',
|
||||
JUMP_TO_LIVEVIEW: '실시간 보기',
|
||||
NO_REMINDER_NEXT_TIME: '다음에 알리지 않음',
|
||||
REFRESH_NOZZLE: '다시 확인',
|
||||
IGNORE_NO_REMINDER_NEXT_TIME: '무시 및 다시 알리지 않음',
|
||||
IGNORE_RESUME: '무시하고 재개',
|
||||
PROBLEM_SOLVED_RESUME: '문제 해결됨, 재개',
|
||||
TURN_OFF_FIRE_ALARM: '확인, 화재 경보 끄기',
|
||||
RETRY_PROBLEM_SOLVED: '다시 시도 (문제 해결됨)',
|
||||
CANCLE: '취소',
|
||||
STOP_DRYING: '건조 중지',
|
||||
PROCEED: '계속',
|
||||
OK_JUMP_RACK: '확인',
|
||||
ABORT: '중단',
|
||||
DISABLE_PURIFICATION: '이 인쇄에 대해 공기 정화 비활성화',
|
||||
DONT_REMIND_NEXT_TIME: '알리지 않음',
|
||||
DBL_CHECK_CANCEL: '취소',
|
||||
DBL_CHECK_DONE: '완료',
|
||||
DBL_CHECK_RETRY: '다시 시도',
|
||||
DBL_CHECK_RESUME: '재개',
|
||||
DBL_CHECK_OK: '확인',
|
||||
REMOVE_CLOSE_BTN: '닫기',
|
||||
},
|
||||
},
|
||||
mqttDebug: {
|
||||
title: 'MQTT 디버그 로그',
|
||||
|
|
|
|||
|
|
@ -2574,6 +2574,42 @@ export default {
|
|||
clearErrors: 'Limpar Erros',
|
||||
clearSuccess: 'Erros HMS limpos',
|
||||
clearFailed: 'Falha ao limpar erros HMS',
|
||||
actionSuccess: 'Ação enviada à impressora',
|
||||
actionFailed: 'Falha ao enviar ação',
|
||||
actions: {
|
||||
RESUME_PRINTING: 'Retomar impressão',
|
||||
RESUME_PRINTING_DEFECTS: 'Retomar (defeitos aceitáveis)',
|
||||
RESUME_PRINTING_PROBELM_SOLVED: 'Retomar (problema resolvido)',
|
||||
STOP_PRINTING: 'Parar impressão',
|
||||
CHECK_ASSISTANT: 'Ver assistente',
|
||||
FILAMENT_EXTRUDED: 'Filamento extrudado, continuar',
|
||||
RETRY_FILAMENT_EXTRUDED: 'Ainda não extrudado, tentar novamente',
|
||||
CONTINUE: 'Finalizado, continuar',
|
||||
LOAD_VIRTUAL_TRAY: 'Carregar filamento',
|
||||
OK_BUTTON: 'OK',
|
||||
FILAMENT_LOAD_RESUME: 'Filamento carregado, retomar',
|
||||
JUMP_TO_LIVEVIEW: 'Ver ao vivo',
|
||||
NO_REMINDER_NEXT_TIME: 'Não lembrar da próxima vez',
|
||||
REFRESH_NOZZLE: 'Verificar novamente',
|
||||
IGNORE_NO_REMINDER_NEXT_TIME: 'Ignorar e não lembrar',
|
||||
IGNORE_RESUME: 'Ignorar e retomar',
|
||||
PROBLEM_SOLVED_RESUME: 'Problema resolvido, retomar',
|
||||
TURN_OFF_FIRE_ALARM: 'Entendido, desligar alarme de incêndio',
|
||||
RETRY_PROBLEM_SOLVED: 'Tentar novamente (problema resolvido)',
|
||||
CANCLE: 'Cancelar',
|
||||
STOP_DRYING: 'Parar secagem',
|
||||
PROCEED: 'Prosseguir',
|
||||
OK_JUMP_RACK: 'OK',
|
||||
ABORT: 'Abortar',
|
||||
DISABLE_PURIFICATION: 'Desativar purificação para esta impressão',
|
||||
DONT_REMIND_NEXT_TIME: 'Não me lembrar',
|
||||
DBL_CHECK_CANCEL: 'Cancelar',
|
||||
DBL_CHECK_DONE: 'Concluído',
|
||||
DBL_CHECK_RETRY: 'Tentar novamente',
|
||||
DBL_CHECK_RESUME: 'Retomar',
|
||||
DBL_CHECK_OK: 'Confirmar',
|
||||
REMOVE_CLOSE_BTN: 'Fechar',
|
||||
},
|
||||
},
|
||||
|
||||
// MQTT Debug modal
|
||||
|
|
|
|||
|
|
@ -2590,6 +2590,42 @@ export default {
|
|||
clearErrors: 'Hataları Temizle',
|
||||
clearSuccess: 'HMS hataları temizlendi',
|
||||
clearFailed: 'HMS hataları temizlenemedi',
|
||||
actionSuccess: 'Eylem yazıcıya gönderildi',
|
||||
actionFailed: 'Eylem gönderilemedi',
|
||||
actions: {
|
||||
RESUME_PRINTING: 'Baskıyı sürdür',
|
||||
RESUME_PRINTING_DEFECTS: 'Sürdür (kusurlar kabul edilebilir)',
|
||||
RESUME_PRINTING_PROBELM_SOLVED: 'Sürdür (sorun çözüldü)',
|
||||
STOP_PRINTING: 'Baskıyı durdur',
|
||||
CHECK_ASSISTANT: 'Asistanı aç',
|
||||
FILAMENT_EXTRUDED: 'Filament ekstrude edildi, devam et',
|
||||
RETRY_FILAMENT_EXTRUDED: 'Henüz ekstrude edilmedi, tekrar dene',
|
||||
CONTINUE: 'Tamamlandı, devam et',
|
||||
LOAD_VIRTUAL_TRAY: 'Filament yükle',
|
||||
OK_BUTTON: 'Tamam',
|
||||
FILAMENT_LOAD_RESUME: 'Filament yüklendi, sürdür',
|
||||
JUMP_TO_LIVEVIEW: 'Canlı görünümü aç',
|
||||
NO_REMINDER_NEXT_TIME: 'Bir daha hatırlatma',
|
||||
REFRESH_NOZZLE: 'Tekrar kontrol et',
|
||||
IGNORE_NO_REMINDER_NEXT_TIME: 'Yok say ve bir daha hatırlatma',
|
||||
IGNORE_RESUME: 'Yok say ve sürdür',
|
||||
PROBLEM_SOLVED_RESUME: 'Sorun çözüldü, sürdür',
|
||||
TURN_OFF_FIRE_ALARM: 'Anlaşıldı, yangın alarmını kapat',
|
||||
RETRY_PROBLEM_SOLVED: 'Tekrar dene (sorun çözüldü)',
|
||||
CANCLE: 'İptal',
|
||||
STOP_DRYING: 'Kurutmayı durdur',
|
||||
PROCEED: 'Devam et',
|
||||
OK_JUMP_RACK: 'Tamam',
|
||||
ABORT: 'İptal',
|
||||
DISABLE_PURIFICATION: 'Bu baskı için arıtmayı devre dışı bırak',
|
||||
DONT_REMIND_NEXT_TIME: 'Beni hatırlatma',
|
||||
DBL_CHECK_CANCEL: 'İptal',
|
||||
DBL_CHECK_DONE: 'Tamam',
|
||||
DBL_CHECK_RETRY: 'Tekrar dene',
|
||||
DBL_CHECK_RESUME: 'Sürdür',
|
||||
DBL_CHECK_OK: 'Onayla',
|
||||
REMOVE_CLOSE_BTN: 'Kapat',
|
||||
},
|
||||
},
|
||||
|
||||
// MQTT Hata Ayıklama modali
|
||||
|
|
|
|||
|
|
@ -2574,6 +2574,42 @@ export default {
|
|||
clearErrors: '清除错误',
|
||||
clearSuccess: 'HMS 错误已清除',
|
||||
clearFailed: '清除 HMS 错误失败',
|
||||
actionSuccess: '已向打印机发送操作',
|
||||
actionFailed: '操作发送失败',
|
||||
actions: {
|
||||
RESUME_PRINTING: '恢复打印',
|
||||
RESUME_PRINTING_DEFECTS: '恢复 (缺陷可接受)',
|
||||
RESUME_PRINTING_PROBELM_SOLVED: '恢复 (问题已解决)',
|
||||
STOP_PRINTING: '停止打印',
|
||||
CHECK_ASSISTANT: '查看助手',
|
||||
FILAMENT_EXTRUDED: '已挤出耗材,继续',
|
||||
RETRY_FILAMENT_EXTRUDED: '尚未挤出,重试',
|
||||
CONTINUE: '已完成,继续',
|
||||
LOAD_VIRTUAL_TRAY: '加载耗材',
|
||||
OK_BUTTON: '确定',
|
||||
FILAMENT_LOAD_RESUME: '耗材已加载,恢复',
|
||||
JUMP_TO_LIVEVIEW: '查看实时画面',
|
||||
NO_REMINDER_NEXT_TIME: '下次不再提醒',
|
||||
REFRESH_NOZZLE: '重新检查',
|
||||
IGNORE_NO_REMINDER_NEXT_TIME: '忽略,下次不再提醒',
|
||||
IGNORE_RESUME: '忽略并恢复',
|
||||
PROBLEM_SOLVED_RESUME: '问题已解决,恢复',
|
||||
TURN_OFF_FIRE_ALARM: '知道了,关闭火警',
|
||||
RETRY_PROBLEM_SOLVED: '重试 (问题已解决)',
|
||||
CANCLE: '取消',
|
||||
STOP_DRYING: '停止干燥',
|
||||
PROCEED: '继续',
|
||||
OK_JUMP_RACK: '确定',
|
||||
ABORT: '终止',
|
||||
DISABLE_PURIFICATION: '本次打印禁用空气净化',
|
||||
DONT_REMIND_NEXT_TIME: '不再提醒',
|
||||
DBL_CHECK_CANCEL: '取消',
|
||||
DBL_CHECK_DONE: '完成',
|
||||
DBL_CHECK_RETRY: '重试',
|
||||
DBL_CHECK_RESUME: '恢复',
|
||||
DBL_CHECK_OK: '确认',
|
||||
REMOVE_CLOSE_BTN: '关闭',
|
||||
},
|
||||
},
|
||||
|
||||
// MQTT Debug modal
|
||||
|
|
|
|||
|
|
@ -2574,6 +2574,42 @@ export default {
|
|||
clearErrors: '清除錯誤',
|
||||
clearSuccess: 'HMS 錯誤已清除',
|
||||
clearFailed: '清除 HMS 錯誤失敗',
|
||||
actionSuccess: '已向印表機傳送動作',
|
||||
actionFailed: '動作傳送失敗',
|
||||
actions: {
|
||||
RESUME_PRINTING: '恢復列印',
|
||||
RESUME_PRINTING_DEFECTS: '恢復 (瑕疵可接受)',
|
||||
RESUME_PRINTING_PROBELM_SOLVED: '恢復 (問題已解決)',
|
||||
STOP_PRINTING: '停止列印',
|
||||
CHECK_ASSISTANT: '檢視助理',
|
||||
FILAMENT_EXTRUDED: '已擠出耗材,繼續',
|
||||
RETRY_FILAMENT_EXTRUDED: '尚未擠出,重試',
|
||||
CONTINUE: '已完成,繼續',
|
||||
LOAD_VIRTUAL_TRAY: '載入耗材',
|
||||
OK_BUTTON: '確定',
|
||||
FILAMENT_LOAD_RESUME: '耗材已載入,恢復',
|
||||
JUMP_TO_LIVEVIEW: '檢視即時畫面',
|
||||
NO_REMINDER_NEXT_TIME: '下次不再提醒',
|
||||
REFRESH_NOZZLE: '重新檢查',
|
||||
IGNORE_NO_REMINDER_NEXT_TIME: '忽略,下次不再提醒',
|
||||
IGNORE_RESUME: '忽略並恢復',
|
||||
PROBLEM_SOLVED_RESUME: '問題已解決,恢復',
|
||||
TURN_OFF_FIRE_ALARM: '知道了,關閉火警',
|
||||
RETRY_PROBLEM_SOLVED: '重試 (問題已解決)',
|
||||
CANCLE: '取消',
|
||||
STOP_DRYING: '停止乾燥',
|
||||
PROCEED: '繼續',
|
||||
OK_JUMP_RACK: '確定',
|
||||
ABORT: '中止',
|
||||
DISABLE_PURIFICATION: '本次列印停用空氣淨化',
|
||||
DONT_REMIND_NEXT_TIME: '不再提醒',
|
||||
DBL_CHECK_CANCEL: '取消',
|
||||
DBL_CHECK_DONE: '完成',
|
||||
DBL_CHECK_RETRY: '重試',
|
||||
DBL_CHECK_RESUME: '恢復',
|
||||
DBL_CHECK_OK: '確認',
|
||||
REMOVE_CLOSE_BTN: '關閉',
|
||||
},
|
||||
},
|
||||
|
||||
// MQTT Debug modal
|
||||
|
|
|
|||
81
scripts/update_hms_actions.py
Normal file
81
scripts/update_hms_actions.py
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
import asyncio
|
||||
import json
|
||||
|
||||
import requests
|
||||
|
||||
HMS_ACTIONS_JSON_PATH = "backend/app/data/hms_actions.json"
|
||||
HMS_REQUEST_URL = "https://e.bambulab.com/hms/GetActionImage.php"
|
||||
|
||||
HMS_ID_TO_ACTION_NAME_MAP: dict[int, str] = {
|
||||
2: "RESUME_PRINTING",
|
||||
3: "RESUME_PRINTING_DEFECTS",
|
||||
4: "RESUME_PRINTING_PROBELM_SOLVED",
|
||||
5: "STOP_PRINTING",
|
||||
6: "CHECK_ASSISTANT",
|
||||
7: "FILAMENT_EXTRUDED",
|
||||
8: "RETRY_FILAMENT_EXTRUDED",
|
||||
9: "CONTINUE",
|
||||
10: "LOAD_VIRTUAL_TRAY",
|
||||
11: "OK_BUTTON",
|
||||
12: "FILAMENT_LOAD_RESUME",
|
||||
13: "JUMP_TO_LIVEVIEW",
|
||||
23: "NO_REMINDER_NEXT_TIME",
|
||||
24: "REFRESH_NOZZLE",
|
||||
25: "IGNORE_NO_REMINDER_NEXT_TIME",
|
||||
27: "IGNORE_RESUME",
|
||||
28: "PROBLEM_SOLVED_RESUME",
|
||||
29: "TURN_OFF_FIRE_ALARM",
|
||||
34: "RETRY_PROBLEM_SOLVED",
|
||||
35: "STOP_DRYING",
|
||||
37: "CANCLE", # Note: "CANCLE" is intentionally misspelled in the BambuStudio source code
|
||||
39: "REMOVE_CLOSE_BTN",
|
||||
41: "PROCEED",
|
||||
49: "OK_JUMP_RACK",
|
||||
51: "ABORT",
|
||||
54: "DISABLE_PURIFICATION",
|
||||
57: "DONT_REMIND_NEXT_TIME",
|
||||
10000: "DBL_CHECK_CANCEL",
|
||||
10001: "DBL_CHECK_DONE",
|
||||
10002: "DBL_CHECK_RETRY",
|
||||
10003: "DBL_CHECK_RESUME",
|
||||
10004: "DBL_CHECK_OK",
|
||||
}
|
||||
|
||||
|
||||
async def main():
|
||||
error_to_action_map: dict[str, list[str]] = {}
|
||||
# get the json response from the url
|
||||
response = requests.get(HMS_REQUEST_URL)
|
||||
if response.status_code == 200:
|
||||
data = response.json()
|
||||
ready_data = {}
|
||||
for item in data["data"]:
|
||||
# error_code = item["ecode"][:4] + "_" + item["ecode"][4:]
|
||||
mapped_actions = []
|
||||
for hms_id in item["actions"]:
|
||||
if hms_id not in HMS_ID_TO_ACTION_NAME_MAP:
|
||||
print(f"Warning: Unrecognized HMS action ID {hms_id} for error code {item['ecode']}")
|
||||
else:
|
||||
mapped_actions.append(HMS_ID_TO_ACTION_NAME_MAP.get(hms_id, f"UNKNOWN_ACTION_{hms_id}"))
|
||||
print(f"ecode: {item['ecode']}, actions: {mapped_actions}, device: {item['device']}")
|
||||
if item["device"] not in ready_data:
|
||||
ready_data[item["device"]] = {}
|
||||
ready_data[item["device"]][item["ecode"]] = mapped_actions
|
||||
# ready_data.append(
|
||||
# {
|
||||
# "ecode": item["ecode"],
|
||||
# "actions": mapped_actions,
|
||||
# "device": item["device"],
|
||||
# }
|
||||
# )
|
||||
with open(HMS_ACTIONS_JSON_PATH, "w") as f:
|
||||
json.dump(ready_data, f, indent=4)
|
||||
else:
|
||||
print("Failed to fetch data")
|
||||
print(error_to_action_map)
|
||||
|
||||
# autogenerate
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
File diff suppressed because one or more lines are too long
|
|
@ -26,7 +26,7 @@
|
|||
|
||||
<!-- Splash screens for iOS -->
|
||||
<link rel="apple-touch-startup-image" href="/img/android-chrome-512x512.png" />
|
||||
<script type="module" crossorigin src="/assets/index-BSFuaWEQ.js"></script>
|
||||
<script type="module" crossorigin src="/assets/index-B8n3e67k.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-BKwIZ5yr.css">
|
||||
</head>
|
||||
<body>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue