bambu-rs 0.1.0

AI-agent-friendly Bambu Lab 3D printer CLI & library
Documentation
import { useState } from "react";
import type { TempPoint } from "../useStatus";
import type { Control } from "../useControl";
import type { PrinterStatus } from "../types";
import { Sparkline, TempReadout } from "./widgets";

export function TempSection({
  s,
  history,
  control,
}: {
  s: PrinterStatus;
  history: TempPoint[];
  control: Control;
}) {
  const b = control.busy;
  const [nozzleInput, setNozzleInput] = useState("");
  const [bedInput, setBedInput] = useState("");

  const setTemp = (part: "nozzle" | "bed", raw: string) => {
    const v = Number(raw);
    if (!Number.isFinite(v)) return;
    void control.setTemp(part, v, true);
  };

  const fans: Array<[string, number]> = (
    [
      ["part", s.cooling_fan_speed],
      ["aux", s.big_fan1_speed],
      ["chamber", s.big_fan2_speed],
      ["hotend", s.heatbreak_fan_speed],
    ] as Array<[string, number | null | undefined]>
  ).filter((e): e is [string, number] => e[1] != null);
  const running = s.gcode_state === "RUNNING";

  return (
    <section className="panel" data-testid="temperature">
      <div className="lbl">temperature</div>
      {/* One tier per metric: each temperature shows its value, its own graph, and
          its set/cool control together (split by meaning, not "values" then "a
          graph" then "controls" off in another panel). */}
      <div className="temps">
        <div className="temprow">
          <TempReadout label="nozzle" cur={s.nozzle_temper} target={s.nozzle_target} accent />
          <Sparkline history={history} metric="nozzle" />
          <div className="tctl">
            <input
              className="pw tctl__in"
              inputMode="numeric"
              placeholder="°C"
              value={nozzleInput}
              disabled={!!b}
              onChange={(e) => setNozzleInput(e.target.value)}
              data-testid="temp-nozzle-input"
            />
            <button
              className="btn btn--sm"
              disabled={!!b || !nozzleInput.trim()}
              data-testid="temp-nozzle-set"
              onClick={() => setTemp("nozzle", nozzleInput)}
            >
              set
            </button>
            <button
              className="btn btn--sm"
              disabled={!!b}
              data-testid="temp-nozzle-cool"
              onClick={() => void control.cooldown("nozzle")}
            >
              cool
            </button>
          </div>
        </div>
        <div className="temprow">
          <TempReadout label="bed" cur={s.bed_temper} target={s.bed_target} />
          <Sparkline history={history} metric="bed" />
          <div className="tctl">
            <input
              className="pw tctl__in"
              inputMode="numeric"
              placeholder="°C"
              value={bedInput}
              disabled={!!b}
              onChange={(e) => setBedInput(e.target.value)}
              data-testid="temp-bed-input"
            />
            <button
              className="btn btn--sm"
              disabled={!!b || !bedInput.trim()}
              data-testid="temp-bed-set"
              onClick={() => setTemp("bed", bedInput)}
            >
              set
            </button>
            <button
              className="btn btn--sm"
              disabled={!!b}
              data-testid="temp-bed-cool"
              onClick={() => void control.cooldown("bed")}
            >
              cool
            </button>
          </div>
        </div>
      </div>
      {fans.length > 0 && (
        <div className="fans">
          {fans.map(([name, v]) => (
            <span key={name} className={`fan${running && v === 0 ? " fan--warn" : ""}`}>
              <span className="lbl">{name}</span>
              <span className="fan__v">{v}</span>
              {/* CUD: a stopped fan mid-print is a warning — flag it with a shape
                  (⚠), not just the red number, so it reads without colour. */}
              {running && v === 0 && (
                <span className="fan__warn" title="fan stopped mid-print">
                  ⚠ stopped
                </span>
              )}
            </span>
          ))}
        </div>
      )}
    </section>
  );
}