printer_models.py 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. """Printer model normalization utilities.
  2. Converts 3MF printer model names (e.g., "Bambu Lab X1 Carbon") to
  3. normalized short names (e.g., "X1C") that match database storage.
  4. """
  5. # Map from 3MF printer_model strings to normalized short names
  6. PRINTER_MODEL_MAP = {
  7. "Bambu Lab X1 Carbon": "X1C",
  8. "Bambu Lab X1": "X1",
  9. "Bambu Lab X1E": "X1E",
  10. "Bambu Lab P1S": "P1S",
  11. "Bambu Lab P1P": "P1P",
  12. "Bambu Lab P2S": "P2S",
  13. "Bambu Lab A1": "A1",
  14. "Bambu Lab A1 Mini": "A1 Mini",
  15. "Bambu Lab A1 mini": "A1 Mini",
  16. # Bambu cloud rolled out a terse model-code rename mid-2026 (#1649);
  17. # 3MFs prepared with newer cloud presets may carry this short form.
  18. "Bambu Lab A1M": "A1 Mini",
  19. "Bambu Lab H2D": "H2D",
  20. "Bambu Lab H2D Pro": "H2D Pro",
  21. "Bambu Lab H2C": "H2C",
  22. "Bambu Lab H2S": "H2S",
  23. "Bambu Lab X2D": "X2D",
  24. }
  25. # Map from printer_model_id (internal codes in slice_info.config) to short names
  26. # These are the codes Bambu Studio uses internally
  27. PRINTER_MODEL_ID_MAP = {
  28. # X1 series
  29. "C11": "X1C",
  30. "C12": "X1",
  31. "C13": "X1E",
  32. # P1 series
  33. "P1P": "P1P",
  34. "P1S": "P1S",
  35. # P2 series
  36. "P2S": "P2S",
  37. # X2 series
  38. "N6": "X2D",
  39. # A1 series
  40. "A11": "A1",
  41. "A12": "A1 Mini",
  42. "N1": "A1",
  43. "N2S": "A1 Mini",
  44. "A04": "A1 Mini",
  45. # H2 series (Office/H series)
  46. "O1D": "H2D",
  47. "O1E": "H2D Pro", # Some devices report O1E
  48. "O2D": "H2D Pro", # Some devices report O2D
  49. "O1C": "H2C",
  50. "O1C2": "H2C",
  51. "O1S": "H2S",
  52. }
  53. # Rod/rail type classification for maintenance tasks.
  54. # Carbon rods: X1, P1 series (CoreXY with carbon fiber rods)
  55. # Steel rods: P2S, X2D series (hardened steel linear shafts)
  56. # Linear rails: A1, H2 series (linear rail motion system)
  57. # Values must be uppercase with spaces stripped for normalized comparison.
  58. CARBON_ROD_MODELS = frozenset(
  59. [
  60. # Display names (uppercase, no spaces)
  61. "X1",
  62. "X1C",
  63. "X1E",
  64. "P1P",
  65. "P1S",
  66. # Internal codes
  67. "C11", # X1C
  68. "C12", # X1
  69. "C13", # X1E
  70. ]
  71. )
  72. STEEL_ROD_MODELS = frozenset(
  73. [
  74. # Display names (uppercase, no spaces)
  75. "P2S",
  76. "X2D",
  77. # Internal codes
  78. "N7", # P2S
  79. "N6", # X2D
  80. ]
  81. )
  82. LINEAR_RAIL_MODELS = frozenset(
  83. [
  84. # Display names (uppercase, no spaces)
  85. "A1",
  86. "A1MINI",
  87. "H2D",
  88. "H2DPRO",
  89. "H2C",
  90. "H2S",
  91. # Internal codes
  92. "N1", # A1
  93. "N2S", # A1 Mini
  94. "A04", # A1 Mini (alternate)
  95. "A11", # A1
  96. "A12", # A1 Mini
  97. "O1D", # H2D
  98. "O1E", # H2D Pro
  99. "O2D", # H2D Pro (alternate)
  100. "O1C", # H2C
  101. "O1C2", # H2C (dual nozzle variant)
  102. "O1S", # H2S
  103. ]
  104. )
  105. # Models with an ethernet port.
  106. # X1, P1P, A1, A1 Mini do NOT have ethernet.
  107. ETHERNET_MODELS = frozenset(
  108. [
  109. # Display names (uppercase, no spaces)
  110. "X1C",
  111. "X1E",
  112. "X2D",
  113. "P1S",
  114. "P2S",
  115. "H2D",
  116. "H2DPRO",
  117. "H2C",
  118. "H2S",
  119. # Internal codes
  120. "C11", # X1C
  121. "C13", # X1E
  122. "N6", # X2D
  123. "P1S", # P1S
  124. "O1D", # H2D
  125. "O1E", # H2D Pro
  126. "O2D", # H2D Pro (alternate)
  127. "O1C", # H2C
  128. "O1C2", # H2C (dual nozzle variant)
  129. "O1S", # H2S
  130. ]
  131. )
  132. # Dual-nozzle (dual-extruder) printers. Single source of truth for nozzle
  133. # class — consumed by ``BambuMQTTClient.start_print``, the K-profile routes,
  134. # and the re-slice nozzle-class guard (previously an inline model tuple
  135. # duplicated across all three). Re-slicing a model laid out for a single-nozzle
  136. # printer onto one of these — or vice versa — is not yet supported: the source
  137. # 3MF's embedded single-nozzle filament/extruder layout is not a valid
  138. # dual-nozzle project and BambuStudio's multi-extruder validator rejects it.
  139. DUAL_NOZZLE_MODELS = frozenset(
  140. [
  141. # Display names (uppercase, no spaces)
  142. "H2D",
  143. "H2DPRO",
  144. "H2C",
  145. "X2D",
  146. # Internal codes
  147. "O1D", # H2D
  148. "O1E", # H2D Pro
  149. "O2D", # H2D Pro (alternate)
  150. "O1C", # H2C
  151. "O1C2", # H2C (dual nozzle variant)
  152. "N6", # X2D
  153. ]
  154. )
  155. def has_ethernet(model: str | None) -> bool:
  156. """Return True if the printer model has an ethernet port."""
  157. if not model:
  158. return False
  159. normalized = model.strip().upper().replace(" ", "").replace("-", "")
  160. return normalized in ETHERNET_MODELS
  161. def is_dual_nozzle_model(model: str | None) -> bool:
  162. """Return True if the printer model has two nozzles (H2D family / X2D)."""
  163. if not model:
  164. return False
  165. normalized = model.strip().upper().replace(" ", "").replace("-", "")
  166. return normalized in DUAL_NOZZLE_MODELS
  167. def get_rod_type(model: str | None) -> str | None:
  168. """Return the rod/rail type for a printer model.
  169. Returns:
  170. "carbon" for X1/P1 series (carbon fiber rods),
  171. "steel_rod" for P2S/X2D series (hardened steel rods),
  172. "linear_rail" for A1/H2 series (linear rails),
  173. None for unknown models.
  174. """
  175. if not model:
  176. return None
  177. normalized = model.strip().upper().replace(" ", "").replace("-", "")
  178. if normalized in CARBON_ROD_MODELS:
  179. return "carbon"
  180. if normalized in STEEL_ROD_MODELS:
  181. return "steel_rod"
  182. if normalized in LINEAR_RAIL_MODELS:
  183. return "linear_rail"
  184. return None
  185. def normalize_printer_model_id(model_id: str | None) -> str | None:
  186. """Convert printer_model_id (internal code) to normalized short name.
  187. Args:
  188. model_id: The printer_model_id from slice_info.config (e.g., "C11", "O1D")
  189. Returns:
  190. Normalized short name (e.g., "X1C", "H2D") or the original ID if unknown.
  191. """
  192. if not model_id:
  193. return None
  194. # Check known mappings
  195. if model_id in PRINTER_MODEL_ID_MAP:
  196. return PRINTER_MODEL_ID_MAP[model_id]
  197. # Return original if unknown (might already be a short name)
  198. return model_id
  199. def normalize_printer_model(raw_model: str | None) -> str | None:
  200. """Convert 3MF printer_model to normalized short name.
  201. Args:
  202. raw_model: The printer_model string from 3MF metadata
  203. (e.g., "Bambu Lab X1 Carbon")
  204. Returns:
  205. Normalized short name (e.g., "X1C") or None if input is empty.
  206. Unknown models have "Bambu Lab " prefix stripped.
  207. """
  208. if not raw_model:
  209. return None
  210. # Check known mappings first
  211. if raw_model in PRINTER_MODEL_MAP:
  212. return PRINTER_MODEL_MAP[raw_model]
  213. # Strip "Bambu Lab " prefix for unknown models
  214. stripped = raw_model.replace("Bambu Lab ", "").strip()
  215. return stripped or None