| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137 |
- """Pydantic schemas for the unified slicer-presets endpoint.
- The SliceModal pulls printer/process/filament options from three sources, in
- priority order: cloud (the user's Bambu Cloud account), local (DB-backed
- imported profiles), and standard (slicer-bundled stock profiles). The endpoint
- returns all three lists with name-based dedup applied so each preset appears
- exactly once across the response.
- """
- from typing import Literal
- from pydantic import BaseModel
- CloudStatus = Literal["ok", "not_authenticated", "expired", "unreachable"]
- class UnifiedPreset(BaseModel):
- """A single printer/process/filament preset with its source.
- The ``id`` shape varies by source:
- - cloud → Bambu Cloud setting_id (e.g. ``"PFUS9ac902733670a9"``)
- - local → stringified DB row id from ``local_presets``
- - standard → preset name as written in the bundled JSON (the slicer
- resolves bundled profiles by name during inheritance walk)
- The frontend treats ``id`` as opaque; the slice dispatch path uses
- ``(source, id)`` to fetch / pass the preset content to the sidecar.
- ``filament_type`` and ``filament_colour`` are populated for the filament
- slot only — they let the SliceModal pre-pick a preset per plate slot in
- the multi-color flow by matching against the source 3MF's per-slot type
- and color. Populated when the underlying preset JSON exposes them; left
- as ``None`` on bundled profiles where colour is a runtime spool attribute.
- ``compatible_printers`` is the slicer's own list of printer-preset names a
- process / filament preset declares itself valid for. Populated for the
- local tier (stored at import time); left ``None`` for cloud (no per-preset
- detail is fetched — rate limits) and standard (the sidecar's bundled
- listing doesn't expose it). The SliceModal uses it to filter the
- process / filament dropdowns by the selected printer (#1325); when it is
- ``None`` the modal falls back to matching the preset name against the
- ``@BBL <code>`` printer-model registry.
- """
- id: str
- name: str
- source: Literal["orca_cloud", "cloud", "local", "standard"]
- filament_type: str | None = None
- filament_colour: str | None = None
- compatible_printers: list[str] | None = None
- class UnifiedPresetsBySlot(BaseModel):
- """Three slots in the order Bambu Studio / OrcaSlicer use."""
- printer: list[UnifiedPreset] = []
- process: list[UnifiedPreset] = []
- filament: list[UnifiedPreset] = []
- class UnifiedPresetsResponse(BaseModel):
- """Every tier carries its full preset list — no cross-tier dedup.
- Priority order: ``local > orca_cloud > cloud > standard``. The order
- drives auto-pick (first non-empty tier wins, name-lookup walks tiers
- in this order, filament scoring tiebreaks by per-tier bonus) and
- determines the visual rendering order of the SliceModal's optgroups,
- but a name that exists in multiple tiers appears in EACH of their
- groups so the user can pick any source.
- ``cloud_status`` / ``orca_cloud_status`` let the frontend show a banner
- explaining why a cloud tier is empty when the user expected to see it
- (signed out / token expired / network down). Each tier has its own
- status because they can fail independently.
- """
- orca_cloud: UnifiedPresetsBySlot = UnifiedPresetsBySlot()
- cloud: UnifiedPresetsBySlot = UnifiedPresetsBySlot()
- local: UnifiedPresetsBySlot = UnifiedPresetsBySlot()
- standard: UnifiedPresetsBySlot = UnifiedPresetsBySlot()
- cloud_status: CloudStatus = "ok"
- orca_cloud_status: CloudStatus = "ok"
- class LoadedSpoolPreset(BaseModel):
- """The filament profile Bambuddy recorded for a slot (``slot_preset_mappings``).
- ``tray_info_idx`` is the filament id the slot was configured with alongside
- it. The SliceModal compares it with the tray's live id the same way the
- slot card does, so a row that outlived a spool swap isn't trusted (#3216).
- """
- preset_id: str
- preset_name: str
- preset_source: str
- tray_info_idx: str | None = None
- class LoadedSpoolTray(BaseModel):
- """One AMS slot or external holder, as the printer reports it right now."""
- ams_id: int
- tray_id: int
- tray_type: str | None = None
- tray_sub_brands: str | None = None
- tray_color: str | None = None
- tray_info_idx: str | None = None
- # Firmware's presence bit. A non-RFID spool it can't identify is present
- # (True) but has no tray_type, which reads as "loaded, unconfigured".
- exists: bool | None = None
- state: int | None = None
- saved_preset: LoadedSpoolPreset | None = None
- class LoadedSpoolUnit(BaseModel):
- id: int
- is_ams_ht: bool
- trays: list[LoadedSpoolTray]
- class LoadedSpoolPrinter(BaseModel):
- id: int
- name: str
- # Short model name ("X1C", "H2D") — the form the slicer's @BBL tags use.
- model: str | None = None
- ams: list[LoadedSpoolUnit] = []
- # Only holders that currently have a spool in them.
- external: list[LoadedSpoolTray] = []
- # How many holders the printer has, loaded or not: two on a dual-nozzle
- # printer, whose holders are labelled left and right.
- external_holders: int = 0
- class LoadedSpoolsResponse(BaseModel):
- """Connected printers and what is loaded in them, for the SliceModal (#3172)."""
- printers: list[LoadedSpoolPrinter] = []
|