Refactoring AMS module

This commit is contained in:
Martin Ziegler
2025-12-06 15:24:48 +01:00
parent ef29acda2d
commit eb6d6e6ce5
23 changed files with 254 additions and 124 deletions
+37 -31
View File
@@ -753,10 +753,13 @@ class BambuMQTTClient:
if ams_id is not None and info is not None:
try:
info_val = int(info) if isinstance(info, str) else info
# Extract bit 8 for extruder assignment (0=right, 1=left)
extruder_id = (info_val >> 8) & 0x1
# Extract bit 8 for extruder assignment
# Bit 8 = 0 means LEFT extruder (id 1), bit 8 = 1 means RIGHT extruder (id 0)
# So we invert: extruder_id = 1 - bit8
bit8 = (info_val >> 8) & 0x1
extruder_id = 1 - bit8 # 0=right, 1=left
ams_extruder_map[str(ams_id)] = extruder_id
logger.debug(f"[{self.serial_number}] AMS {ams_id} info={info_val} -> extruder {extruder_id}")
logger.debug(f"[{self.serial_number}] AMS {ams_id} info={info_val} (bit8={bit8}) -> extruder {extruder_id}")
except (ValueError, TypeError):
pass
if ams_extruder_map:
@@ -2394,8 +2397,7 @@ class BambuMQTTClient:
Args:
tray_id: Global tray ID (0-15 for AMS slots, or 254 for external spool)
extruder_id: Extruder ID for dual-nozzle printers (0=right, 1=left).
If None, defaults to 0.
extruder_id: Unused - kept for API compatibility
Returns:
True if command was sent, False otherwise
@@ -2404,11 +2406,7 @@ class BambuMQTTClient:
logger.warning(f"[{self.serial_number}] Cannot load filament: not connected")
return False
# Default to extruder 0 if not specified
curr_nozzle = extruder_id if extruder_id is not None else 0
# Convert global tray_id to ams_id and slot_id
# External spool is special case (254)
# Calculate ams_id and slot_id for logging
if tray_id == 254:
ams_id = 255 # External spool
slot_id = 254
@@ -2416,24 +2414,26 @@ class BambuMQTTClient:
ams_id = tray_id // 4 # AMS unit (0, 1, 2, 3...)
slot_id = tray_id % 4 # Slot within AMS (0, 1, 2, 3)
# Build command with ams_id and slot_id format (per HA-Bambulab integration)
# Note: curr_nozzle is NOT included - ha-bambulab doesn't use it and it may cause issues
# Command format from BambuStudio traffic capture:
# - No extruder_id field
# - curr_temp and tar_temp are -1 (not 0)
self._sequence_id += 1
command = {
"print": {
"command": "ams_change_filament",
"target": tray_id, # Global tray ID (ams_id * 4 + slot_id)
"sequence_id": str(self._sequence_id),
"ams_id": ams_id,
"slot_id": slot_id,
"curr_temp": 220,
"tar_temp": 220,
"sequence_id": "0"
"target": tray_id,
"curr_temp": -1,
"tar_temp": -1
}
}
command_json = json.dumps(command)
logger.info(f"[{self.serial_number}] Publishing ams_change_filament command: {command_json}")
self._client.publish(self.topic_publish, command_json)
logger.info(f"[{self.serial_number}] Loading filament from AMS {ams_id} slot {slot_id} (global tray {tray_id}) to extruder {curr_nozzle}")
logger.info(f"[{self.serial_number}] Loading filament from tray {tray_id} (AMS {ams_id} slot {slot_id})")
# Track this load request for H2D dual-nozzle disambiguation
# H2D reports only slot number (0-3) in tray_now, so we use our tracked value
@@ -2457,32 +2457,38 @@ class BambuMQTTClient:
tray_now = self.state.tray_now
logger.info(f"[{self.serial_number}] Unload requested, tray_now={tray_now}")
# Determine the ams_id from tray_now (if valid tray is loaded)
# From BambuStudio source: unload requires target=255 AND slot_id=255
# plus the ams_id of the source AMS unit
if tray_now is not None and tray_now < 254:
ams_id = tray_now // 4
# Determine source ams_id for the unload command
if tray_now == 255 or tray_now == 254:
ams_id = 255 # No filament or external spool
else:
ams_id = 0 # Default to AMS 0 if no tray loaded
ams_id = tray_now // 4 # Source AMS
# Build unload command using BambuStudio's "new protocol"
# Key: Both target=255 AND slot_id=255 are required for unload
# Command format from BambuStudio traffic capture:
# - No extruder_id field
# - For UNLOAD: curr_temp and tar_temp are the actual nozzle temp (e.g., 210)
# - slot_id=255 and target=255 for unload
# Get current nozzle temperature for the unload command
nozzle_temp = int(self.state.temperatures.get("nozzle", 210))
if nozzle_temp < 180:
nozzle_temp = 210 # Default to PLA temp if nozzle is cold
self._sequence_id += 1
command = {
"print": {
"command": "ams_change_filament",
"sequence_id": "0",
"target": 255, # 255 = unload
"slot_id": 255, # 255 = unload (new protocol)
"sequence_id": str(self._sequence_id),
"ams_id": ams_id,
"curr_temp": 220,
"tar_temp": 220
"slot_id": 255, # 255 = unload marker
"target": 255, # 255 = unload destination
"curr_temp": nozzle_temp,
"tar_temp": nozzle_temp
}
}
command_json = json.dumps(command)
logger.info(f"[{self.serial_number}] Publishing ams_change_filament (unload) command: {command_json}")
self._client.publish(self.topic_publish, command_json)
logger.info(f"[{self.serial_number}] Unloading filament from AMS {ams_id}")
logger.info(f"[{self.serial_number}] Unloading filament (tray_now was {tray_now})")
# Clear tracked load request since we're unloading
self._last_load_tray_id = None
+4
View File
@@ -283,6 +283,10 @@ def printer_state_to_dict(state: PrinterState, printer_id: int | None = None) ->
{"code": e.code, "attr": e.attr, "module": e.module, "severity": e.severity}
for e in (state.hms_errors or [])
],
# AMS status for filament change tracking
"ams_status_main": state.ams_status_main,
"ams_status_sub": state.ams_status_sub,
"tray_now": state.tray_now,
}
# Add cover URL if there's an active print and printer_id is provided
if printer_id and state.state == "RUNNING" and state.gcode_file: