Files
bambuddy/backend/app/schemas/printer.py
T
maziggy 6fb71de6a2 Add firmware update helper for LAN-only printers
Enables checking and uploading firmware updates for printers operating
  in LAN-only mode without Bambu Cloud connectivity.

  Features:
  - Automatic firmware version checking against Bambu Lab servers
  - Orange "Update" badge on printer cards when updates available
  - Firmware update modal with version info and release notes
  - One-click firmware upload to printer SD card via FTP
  - Real-time upload progress (actual bytes transferred)
  - Step-by-step instructions for triggering update from printer
  - Local firmware caching for faster re-uploads
  - Supports all Bambu Lab printer models

  New files:
  - backend/app/services/firmware_check.py - Version checking service
  - backend/app/services/firmware_update.py - Upload orchestration
  - backend/app/api/routes/firmware.py - REST API endpoints

  Also includes:
  - FTP upload progress callback support
  - 10-minute upload timeout protection
  - Firmware cache directory in .gitignore
2026-01-04 18:41:24 +01:00

163 lines
6.1 KiB
Python

from datetime import datetime
from pydantic import BaseModel, Field
class PrinterBase(BaseModel):
name: str = Field(..., min_length=1, max_length=100)
serial_number: str = Field(..., min_length=1, max_length=50)
ip_address: str = Field(..., pattern=r"^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$")
access_code: str = Field(..., min_length=1, max_length=20)
model: str | None = None
location: str | None = None # Group/location name
auto_archive: bool = True
class PrinterCreate(PrinterBase):
pass
class PrinterUpdate(BaseModel):
name: str | None = None
ip_address: str | None = None
access_code: str | None = None
model: str | None = None
location: str | None = None
is_active: bool | None = None
auto_archive: bool | None = None
print_hours_offset: float | None = None
class PrinterResponse(PrinterBase):
id: int
is_active: bool
nozzle_count: int = 1 # 1 or 2, auto-detected from MQTT
print_hours_offset: float = 0.0
created_at: datetime
updated_at: datetime
class Config:
from_attributes = True
class HMSErrorResponse(BaseModel):
code: str
attr: int = 0 # Attribute value for constructing wiki URL
module: int
severity: int # 1=fatal, 2=serious, 3=common, 4=info
class AMSTray(BaseModel):
id: int
tray_color: str | None = None
tray_type: str | None = None
tray_sub_brands: str | None = None # Full name like "PLA Basic", "PETG HF"
tray_id_name: str | None = None # Bambu filament ID like "A00-Y2" (can decode to color)
tray_info_idx: str | None = None # Filament preset ID like "GFA00"
remain: int = 0
k: float | None = None # Pressure advance value (from tray or K-profile lookup)
cali_idx: int | None = None # Calibration index for K-profile lookup
tag_uid: str | None = None # RFID tag UID (any tag)
tray_uuid: str | None = None # Bambu Lab spool UUID (32-char hex)
nozzle_temp_min: int | None = None # Min nozzle temperature
nozzle_temp_max: int | None = None # Max nozzle temperature
class AMSUnit(BaseModel):
id: int
humidity: int | None = None
temp: float | None = None
is_ams_ht: bool = False # True for AMS-HT (single spool), False for regular AMS (4 spools)
tray: list[AMSTray] = []
class NozzleInfoResponse(BaseModel):
nozzle_type: str = "" # "stainless_steel" or "hardened_steel"
nozzle_diameter: str = "" # e.g., "0.4"
class PrintOptionsResponse(BaseModel):
"""AI detection and print options from xcam data."""
# Core AI detectors
spaghetti_detector: bool = False
print_halt: bool = False
halt_print_sensitivity: str = "medium" # Spaghetti sensitivity
first_layer_inspector: bool = False
printing_monitor: bool = False
buildplate_marker_detector: bool = False
allow_skip_parts: bool = False
# Additional AI detectors (decoded from cfg bitmask)
nozzle_clumping_detector: bool = True
nozzle_clumping_sensitivity: str = "medium"
pileup_detector: bool = True
pileup_sensitivity: str = "medium"
airprint_detector: bool = True
airprint_sensitivity: str = "medium"
auto_recovery_step_loss: bool = True
filament_tangle_detect: bool = False
class PrinterStatus(BaseModel):
id: int
name: str
connected: bool
state: str | None = None
current_print: str | None = None
subtask_name: str | None = None
gcode_file: str | None = None
progress: float | None = None
remaining_time: int | None = None
layer_num: int | None = None
total_layers: int | None = None
temperatures: dict | None = None
cover_url: str | None = None
hms_errors: list[HMSErrorResponse] = []
ams: list[AMSUnit] = []
ams_exists: bool = False
vt_tray: AMSTray | None = None # Virtual tray / external spool
sdcard: bool = False # SD card inserted
store_to_sdcard: bool = False # Store sent files on SD card
timelapse: bool = False # Timelapse recording active
ipcam: bool = False # Live view enabled
wifi_signal: int | None = None # WiFi signal strength in dBm
nozzles: list[NozzleInfoResponse] = [] # Nozzle hardware info (index 0=left/primary, 1=right)
print_options: PrintOptionsResponse | None = None # AI detection and print options
# Calibration stage tracking
stg_cur: int = -1 # Current stage number (-1 = not calibrating)
stg_cur_name: str | None = None # Human-readable current stage name
stg: list[int] = [] # List of stage numbers in calibration sequence
# Air conditioning mode (0=cooling, 1=heating)
airduct_mode: int = 0
# Print speed level (1=silent, 2=standard, 3=sport, 4=ludicrous)
speed_level: int = 2
# Chamber light on/off
chamber_light: bool = False
# Active extruder for dual nozzle (0=right, 1=left)
active_extruder: int = 0
# AMS mapping for dual nozzle: which AMS is connected to which nozzle
ams_mapping: list[int] = []
# Per-AMS extruder map: {ams_id: extruder_id} where 0=right, 1=left
ams_extruder_map: dict[str, int] = {}
# Currently loaded tray (global ID): 254 = external spool, 255 = no filament
tray_now: int = 255
# AMS status for filament change tracking
# Main status: 0=idle, 1=filament_change, 2=rfid_identifying, 3=assist, 4=calibration
ams_status_main: int = 0
# Sub status: specific step within filament change (when main=1)
# Known values: 4=retraction, 6=load verification, 7=purge
ams_status_sub: int = 0
# mc_print_sub_stage - filament change step indicator used by OrcaSlicer/BambuStudio
mc_print_sub_stage: int = 0
# Timestamp of last AMS data update (for RFID refresh detection)
last_ams_update: float = 0.0
# Number of printable objects in current print (for skip objects feature)
printable_objects_count: int = 0
# Fan speeds (0-100 percentage, None if not available for this model)
cooling_fan_speed: int | None = None # Part cooling fan
big_fan1_speed: int | None = None # Auxiliary fan
big_fan2_speed: int | None = None # Chamber/exhaust fan
heatbreak_fan_speed: int | None = None # Hotend heatbreak fan
# Firmware version (from info.module[name="ota"].sw_ver)
firmware_version: str | None = None