From 3451996a8f5614f99b4b2e353136e284c126490f Mon Sep 17 00:00:00 2001 From: tonym Date: Wed, 14 Jan 2026 20:05:02 -0600 Subject: [PATCH] feat: Add temperature ranges for extruder and bed temps (#5) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Store extruder and bed temperatures as min/max ranges instead of single values for more accurate filament specifications. Backend changes: - Add 4 new columns: settings_extruder_temp_min/max, settings_bed_temp_min/max - Keep old columns for backward compatibility (marked deprecated) - Alembic migration copies existing values to both min and max - Update Pydantic models and API endpoints Frontend changes: - Filament model updated with new fields - Create form uses min/max input pairs with Space.Compact - Import from external DB populates both min and max with same value - Label template updated to show ranges: "ET: 190-220 °C" - Template help includes new tags Labels now display temperature ranges like "190-220°C" which is more useful for printing than a single recommended value. Co-Authored-By: Claude Opus 4.5 --- client/public/locales/en/common.json | 2 + client/src/pages/filaments/create.tsx | 68 +++++++++++-------- client/src/pages/filaments/model.tsx | 4 ++ .../printing/spoolQrCodePrintingDialog.tsx | 8 ++- ...200-a1b2c3d4e5f6_add_temperature_ranges.py | 56 +++++++++++++++ spoolman/api/v1/filament.py | 32 ++++++++- spoolman/api/v1/models.py | 32 ++++++++- spoolman/database/filament.py | 8 +++ spoolman/database/models.py | 8 ++- 9 files changed, 183 insertions(+), 35 deletions(-) create mode 100644 migrations/versions/2025_01_15_0200-a1b2c3d4e5f6_add_temperature_ranges.py diff --git a/client/public/locales/en/common.json b/client/public/locales/en/common.json index 713b4e2..9a9017f 100644 --- a/client/public/locales/en/common.json +++ b/client/public/locales/en/common.json @@ -236,6 +236,8 @@ "comment": "Comment", "settings_extruder_temp": "Extruder Temp", "settings_bed_temp": "Bed Temp", + "temp_min": "Min", + "temp_max": "Max", "color_hex": "Color", "single_color": "Single", "multi_color": "Multi", diff --git a/client/src/pages/filaments/create.tsx b/client/src/pages/filaments/create.tsx index 47b0c5e..f046f05 100644 --- a/client/src/pages/filaments/create.tsx +++ b/client/src/pages/filaments/create.tsx @@ -1,6 +1,6 @@ import { Create, useForm, useSelect } from "@refinedev/antd"; import { HttpError, IResourceComponentsProps, useInvalidate, useTranslate } from "@refinedev/core"; -import { AutoComplete, Button, ColorPicker, Divider, Form, Input, InputNumber, Radio, Select, Typography } from "antd"; +import { AutoComplete, Button, ColorPicker, Divider, Form, Input, InputNumber, Radio, Select, Space, Typography } from "antd"; import { PlusOutlined } from "@ant-design/icons"; import TextArea from "antd/es/input/TextArea"; import dayjs from "dayjs"; @@ -126,8 +126,10 @@ export const FilamentCreate: React.FC - - + + + + + + - + + + + - - + + + + + + - + + + + = ({ spoolI `**{filament.vendor.name} - {filament.name} #{id} - {filament.material}** Spool Weight: {filament.spool_weight} g -{ET: {filament.settings_extruder_temp} °C} -{BT: {filament.settings_bed_temp} °C} +{ET: {filament.settings_extruder_temp_min}-{filament.settings_extruder_temp_max} °C} +{BT: {filament.settings_bed_temp_min}-{filament.settings_bed_temp_max} °C} {Lot Nr: {lot_nr}} {{comment}} {filament.comment} @@ -201,6 +201,10 @@ Spool Weight: {filament.spool_weight} g { tag: "filament.comment" }, { tag: "filament.settings_extruder_temp" }, { tag: "filament.settings_bed_temp" }, + { tag: "filament.settings_extruder_temp_min" }, + { tag: "filament.settings_extruder_temp_max" }, + { tag: "filament.settings_bed_temp_min" }, + { tag: "filament.settings_bed_temp_max" }, { tag: "filament.color_hex" }, { tag: "filament.multi_color_hexes" }, { tag: "filament.multi_color_direction" }, diff --git a/migrations/versions/2025_01_15_0200-a1b2c3d4e5f6_add_temperature_ranges.py b/migrations/versions/2025_01_15_0200-a1b2c3d4e5f6_add_temperature_ranges.py new file mode 100644 index 0000000..1f9b5bc --- /dev/null +++ b/migrations/versions/2025_01_15_0200-a1b2c3d4e5f6_add_temperature_ranges.py @@ -0,0 +1,56 @@ +"""add_temperature_ranges. + +Revision ID: a1b2c3d4e5f6 +Revises: 415a8f855e14 +Create Date: 2025-01-15 02:00:00.000000 +""" + +import sqlalchemy as sa +from alembic import op + +# revision identifiers, used by Alembic. +revision = "a1b2c3d4e5f6" +down_revision = "415a8f855e14" +branch_labels = None +depends_on = None + + +def upgrade() -> None: + """Add temperature range columns and migrate existing data.""" + # Add new columns for temperature ranges + op.add_column("filament", sa.Column("settings_extruder_temp_min", sa.Integer(), nullable=True)) + op.add_column("filament", sa.Column("settings_extruder_temp_max", sa.Integer(), nullable=True)) + op.add_column("filament", sa.Column("settings_bed_temp_min", sa.Integer(), nullable=True)) + op.add_column("filament", sa.Column("settings_bed_temp_max", sa.Integer(), nullable=True)) + + # Migrate existing single temp values to both min and max + # This preserves the existing data while enabling the new range functionality + connection = op.get_bind() + connection.execute( + sa.text( + """ + UPDATE filament + SET settings_extruder_temp_min = settings_extruder_temp, + settings_extruder_temp_max = settings_extruder_temp + WHERE settings_extruder_temp IS NOT NULL + """ + ) + ) + connection.execute( + sa.text( + """ + UPDATE filament + SET settings_bed_temp_min = settings_bed_temp, + settings_bed_temp_max = settings_bed_temp + WHERE settings_bed_temp IS NOT NULL + """ + ) + ) + + +def downgrade() -> None: + """Remove temperature range columns.""" + op.drop_column("filament", "settings_bed_temp_max") + op.drop_column("filament", "settings_bed_temp_min") + op.drop_column("filament", "settings_extruder_temp_max") + op.drop_column("filament", "settings_extruder_temp_min") diff --git a/spoolman/api/v1/filament.py b/spoolman/api/v1/filament.py index 07e3492..b0139ec 100644 --- a/spoolman/api/v1/filament.py +++ b/spoolman/api/v1/filament.py @@ -75,15 +75,39 @@ class FilamentParameters(BaseModel): settings_extruder_temp: Optional[int] = Field( None, ge=0, - description="Overridden extruder temperature, in °C.", + description="Overridden extruder temperature, in °C. Deprecated: use settings_extruder_temp_min/max.", examples=[210], ) settings_bed_temp: Optional[int] = Field( None, ge=0, - description="Overridden bed temperature, in °C.", + description="Overridden bed temperature, in °C. Deprecated: use settings_bed_temp_min/max.", examples=[60], ) + settings_extruder_temp_min: Optional[int] = Field( + None, + ge=0, + description="Minimum extruder temperature, in °C.", + examples=[190], + ) + settings_extruder_temp_max: Optional[int] = Field( + None, + ge=0, + description="Maximum extruder temperature, in °C.", + examples=[220], + ) + settings_bed_temp_min: Optional[int] = Field( + None, + ge=0, + description="Minimum bed temperature, in °C.", + examples=[50], + ) + settings_bed_temp_max: Optional[int] = Field( + None, + ge=0, + description="Maximum bed temperature, in °C.", + examples=[70], + ) color_hex: Optional[str] = Field( None, description=( @@ -454,6 +478,10 @@ async def create( # noqa: ANN201 comment=body.comment, settings_extruder_temp=body.settings_extruder_temp, settings_bed_temp=body.settings_bed_temp, + settings_extruder_temp_min=body.settings_extruder_temp_min, + settings_extruder_temp_max=body.settings_extruder_temp_max, + settings_bed_temp_min=body.settings_bed_temp_min, + settings_bed_temp_max=body.settings_bed_temp_max, color_hex=body.color_hex, multi_color_hexes=body.multi_color_hexes, multi_color_direction=body.multi_color_direction, diff --git a/spoolman/api/v1/models.py b/spoolman/api/v1/models.py index 79aa320..3c02b9f 100644 --- a/spoolman/api/v1/models.py +++ b/spoolman/api/v1/models.py @@ -149,15 +149,39 @@ class Filament(BaseModel): settings_extruder_temp: Optional[int] = Field( None, ge=0, - description="Overridden extruder temperature, in °C.", + description="Overridden extruder temperature, in °C. Deprecated: use settings_extruder_temp_min/max.", examples=[210], ) settings_bed_temp: Optional[int] = Field( None, ge=0, - description="Overridden bed temperature, in °C.", + description="Overridden bed temperature, in °C. Deprecated: use settings_bed_temp_min/max.", examples=[60], ) + settings_extruder_temp_min: Optional[int] = Field( + None, + ge=0, + description="Minimum extruder temperature, in °C.", + examples=[190], + ) + settings_extruder_temp_max: Optional[int] = Field( + None, + ge=0, + description="Maximum extruder temperature, in °C.", + examples=[220], + ) + settings_bed_temp_min: Optional[int] = Field( + None, + ge=0, + description="Minimum bed temperature, in °C.", + examples=[50], + ) + settings_bed_temp_max: Optional[int] = Field( + None, + ge=0, + description="Maximum bed temperature, in °C.", + examples=[70], + ) color_hex: Optional[str] = Field( None, min_length=6, @@ -216,6 +240,10 @@ class Filament(BaseModel): comment=item.comment, settings_extruder_temp=item.settings_extruder_temp, settings_bed_temp=item.settings_bed_temp, + settings_extruder_temp_min=item.settings_extruder_temp_min, + settings_extruder_temp_max=item.settings_extruder_temp_max, + settings_bed_temp_min=item.settings_bed_temp_min, + settings_bed_temp_max=item.settings_bed_temp_max, color_hex=item.color_hex, multi_color_hexes=item.multi_color_hexes, multi_color_direction=( diff --git a/spoolman/database/filament.py b/spoolman/database/filament.py index 7ff7c65..13d227e 100644 --- a/spoolman/database/filament.py +++ b/spoolman/database/filament.py @@ -41,6 +41,10 @@ async def create( comment: Optional[str] = None, settings_extruder_temp: Optional[int] = None, settings_bed_temp: Optional[int] = None, + settings_extruder_temp_min: Optional[int] = None, + settings_extruder_temp_max: Optional[int] = None, + settings_bed_temp_min: Optional[int] = None, + settings_bed_temp_max: Optional[int] = None, color_hex: Optional[str] = None, multi_color_hexes: Optional[str] = None, multi_color_direction: Optional[MultiColorDirection] = None, @@ -69,6 +73,10 @@ async def create( comment=comment, settings_extruder_temp=settings_extruder_temp, settings_bed_temp=settings_bed_temp, + settings_extruder_temp_min=settings_extruder_temp_min, + settings_extruder_temp_max=settings_extruder_temp_max, + settings_bed_temp_min=settings_bed_temp_min, + settings_bed_temp_max=settings_bed_temp_max, color_hex=color_hex, multi_color_hexes=multi_color_hexes, multi_color_direction=multi_color_direction.value if multi_color_direction is not None else None, diff --git a/spoolman/database/models.py b/spoolman/database/models.py index 9922969..2808547 100644 --- a/spoolman/database/models.py +++ b/spoolman/database/models.py @@ -46,8 +46,12 @@ class Filament(Base): spool_weight: Mapped[Optional[float]] = mapped_column(comment="The weight of an empty spool.") article_number: Mapped[Optional[str]] = mapped_column(String(64)) comment: Mapped[Optional[str]] = mapped_column(String(1024)) - settings_extruder_temp: Mapped[Optional[int]] = mapped_column(comment="Overridden extruder temperature.") - settings_bed_temp: Mapped[Optional[int]] = mapped_column(comment="Overridden bed temperature.") + settings_extruder_temp: Mapped[Optional[int]] = mapped_column(comment="Overridden extruder temperature (deprecated, use min/max).") + settings_bed_temp: Mapped[Optional[int]] = mapped_column(comment="Overridden bed temperature (deprecated, use min/max).") + settings_extruder_temp_min: Mapped[Optional[int]] = mapped_column(comment="Minimum extruder temperature.") + settings_extruder_temp_max: Mapped[Optional[int]] = mapped_column(comment="Maximum extruder temperature.") + settings_bed_temp_min: Mapped[Optional[int]] = mapped_column(comment="Minimum bed temperature.") + settings_bed_temp_max: Mapped[Optional[int]] = mapped_column(comment="Maximum bed temperature.") color_hex: Mapped[Optional[str]] = mapped_column(String(8)) multi_color_hexes: Mapped[Optional[str]] = mapped_column(String(128)) multi_color_direction: Mapped[Optional[str]] = mapped_column(String(16))