Let the add-printer form take every supported kind
A kind selector (Bambu, Klipper, PrusaLink, OctoPrint) decides which fields appear: serial and access code for Bambu, an API key for PrusaLink and OctoPrint, neither for Klipper. Discovered printers of any of these kinds now fill the form in.
This commit is contained in:
@@ -75,7 +75,7 @@ async function addPrinter(farmId: string, printer: NewPrinter) {
|
|||||||
try {
|
try {
|
||||||
await apiFetch(`/farms/${farmId}/printers`, {
|
await apiFetch(`/farms/${farmId}/printers`, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: { ...printer, vendor: "bambu" },
|
body: printer,
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
throw new Error(apiErrorMessage(err, "the printer could not be added"));
|
throw new Error(apiErrorMessage(err, "the printer could not be added"));
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, ref } from "vue";
|
import { computed, ref } from "vue";
|
||||||
import type { AppDiscoveredCandidate, NewPrinter } from "../types";
|
import type { AppDiscoveredCandidate, NewPrinter, PrinterKind } from "../types";
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
gatewayOnline: boolean | null;
|
gatewayOnline: boolean | null;
|
||||||
@@ -10,6 +10,15 @@ const props = defineProps<{
|
|||||||
add: (printer: NewPrinter) => Promise<void>;
|
add: (printer: NewPrinter) => Promise<void>;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
|
const KINDS: { value: PrinterKind; label: string }[] = [
|
||||||
|
{ value: "bambu", label: "Bambu Lab" },
|
||||||
|
{ value: "klipper", label: "Klipper (Moonraker)" },
|
||||||
|
{ value: "prusa_link", label: "Prusa (PrusaLink)" },
|
||||||
|
{ value: "octoprint", label: "OctoPrint" },
|
||||||
|
];
|
||||||
|
|
||||||
|
const vendor = ref<PrinterKind>("bambu");
|
||||||
|
const apiKey = ref("");
|
||||||
const name = ref("");
|
const name = ref("");
|
||||||
const host = ref("");
|
const host = ref("");
|
||||||
const serialNumber = ref("");
|
const serialNumber = ref("");
|
||||||
@@ -18,11 +27,14 @@ const busy = ref(false);
|
|||||||
const failure = ref("");
|
const failure = ref("");
|
||||||
const added = ref("");
|
const added = ref("");
|
||||||
|
|
||||||
// Only Bambu has a working driver; the others are listed by discovery but
|
// Discovery names the vendors the same way the form does, except that a
|
||||||
// cannot be driven yet, so offering them would add a printer that does nothing.
|
// Bambu may be reported as `bambu_v1`/`bambu_v2`.
|
||||||
const usable = computed(() => props.candidates.filter((c) => c.vendor.startsWith("bambu") && !c.matchedOn));
|
const kindOf = (vendorName: string): PrinterKind | undefined =>
|
||||||
|
vendorName.startsWith("bambu") ? "bambu" : KINDS.find((k) => k.value === vendorName)?.value;
|
||||||
|
const usable = computed(() => props.candidates.filter((c) => kindOf(c.vendor) && !c.matchedOn));
|
||||||
|
|
||||||
function fill(candidate: AppDiscoveredCandidate) {
|
function fill(candidate: AppDiscoveredCandidate) {
|
||||||
|
vendor.value = kindOf(candidate.vendor) ?? vendor.value;
|
||||||
host.value = candidate.address;
|
host.value = candidate.address;
|
||||||
serialNumber.value = candidate.serial ?? "";
|
serialNumber.value = candidate.serial ?? "";
|
||||||
name.value = candidate.name || candidate.model || name.value;
|
name.value = candidate.name || candidate.model || name.value;
|
||||||
@@ -33,12 +45,14 @@ async function submit() {
|
|||||||
failure.value = "";
|
failure.value = "";
|
||||||
added.value = "";
|
added.value = "";
|
||||||
try {
|
try {
|
||||||
await props.add({
|
const printer: NewPrinter = { vendor: vendor.value, name: name.value.trim(), host: host.value.trim() };
|
||||||
name: name.value.trim(),
|
if (vendor.value === "bambu") {
|
||||||
host: host.value.trim(),
|
printer.serialNumber = serialNumber.value.trim();
|
||||||
serialNumber: serialNumber.value.trim(),
|
printer.accessCode = accessCode.value.trim();
|
||||||
accessCode: accessCode.value.trim(),
|
} else if (vendor.value !== "klipper") {
|
||||||
});
|
printer.apiKey = apiKey.value.trim();
|
||||||
|
}
|
||||||
|
await props.add(printer);
|
||||||
added.value = name.value;
|
added.value = name.value;
|
||||||
name.value = host.value = serialNumber.value = "";
|
name.value = host.value = serialNumber.value = "";
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -47,6 +61,7 @@ async function submit() {
|
|||||||
// Cleared on success AND failure: the code should not sit in a form
|
// Cleared on success AND failure: the code should not sit in a form
|
||||||
// after it has been sent, and retyping eight digits is cheap.
|
// after it has been sent, and retyping eight digits is cheap.
|
||||||
accessCode.value = "";
|
accessCode.value = "";
|
||||||
|
apiKey.value = "";
|
||||||
busy.value = false;
|
busy.value = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -54,9 +69,9 @@ async function submit() {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<section class="add-printer" aria-label="Add a printer">
|
<section class="add-printer" aria-label="Add a printer">
|
||||||
<h3>Add a Bambu printer</h3>
|
<h3>Add a printer</h3>
|
||||||
<p v-if="gatewayOnline === false">
|
<p v-if="gatewayOnline === false">
|
||||||
The gateway is not connected. The printer's access code is handed to the gateway and kept only there, so it
|
The gateway is not connected. The printer's credentials are handed to the gateway and kept only there, so it
|
||||||
has to be online to add a printer.
|
has to be online to add a printer.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
@@ -68,16 +83,33 @@ async function submit() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<form @submit.prevent="submit">
|
<form @submit.prevent="submit">
|
||||||
<label>Name <input v-model="name" required maxlength="200" placeholder="Bench P1S" /></label>
|
<label>
|
||||||
|
Kind
|
||||||
|
<select v-model="vendor">
|
||||||
|
<option v-for="k in KINDS" :key="k.value" :value="k.value">{{ k.label }}</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
<label>Name <input v-model="name" required maxlength="200" placeholder="Bench printer" /></label>
|
||||||
<label>
|
<label>
|
||||||
Address
|
Address
|
||||||
<input v-model="host" required maxlength="255" pattern="[A-Za-z0-9.:_\-]+" placeholder="192.168.0.20" />
|
<input v-model="host" required maxlength="255" pattern="[A-Za-z0-9.:_\-]+" placeholder="192.168.0.20" />
|
||||||
</label>
|
</label>
|
||||||
<label>
|
<label v-if="vendor === 'bambu'">
|
||||||
Serial number
|
Serial number
|
||||||
<input v-model="serialNumber" required minlength="5" maxlength="64" pattern="[A-Za-z0-9]+" />
|
<input v-model="serialNumber" required minlength="5" maxlength="64" pattern="[A-Za-z0-9]+" />
|
||||||
</label>
|
</label>
|
||||||
<label>
|
<label v-if="vendor === 'octoprint' || vendor === 'prusa_link'">
|
||||||
|
API key
|
||||||
|
<input
|
||||||
|
v-model="apiKey"
|
||||||
|
required
|
||||||
|
type="password"
|
||||||
|
autocomplete="off"
|
||||||
|
maxlength="256"
|
||||||
|
:placeholder="vendor === 'octoprint' ? 'from OctoPrint settings' : 'from PrusaLink settings'"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<label v-if="vendor === 'bambu'">
|
||||||
Access code
|
Access code
|
||||||
<input
|
<input
|
||||||
v-model="accessCode"
|
v-model="accessCode"
|
||||||
@@ -93,7 +125,7 @@ async function submit() {
|
|||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
<p class="add-printer__note">
|
<p class="add-printer__note">
|
||||||
The access code goes to your gateway and nowhere else; Continuum's servers do not keep it.
|
The access code or API key goes to your gateway and nowhere else; Continuum's servers do not keep it.
|
||||||
</p>
|
</p>
|
||||||
<p v-if="added" role="status" class="add-printer__ok">Added {{ added }}.</p>
|
<p v-if="added" role="status" class="add-printer__ok">Added {{ added }}.</p>
|
||||||
<p v-if="failure" role="alert" class="add-printer__error">{{ failure }}</p>
|
<p v-if="failure" role="alert" class="add-printer__error">{{ failure }}</p>
|
||||||
|
|||||||
@@ -11,4 +11,4 @@ export { default as GatewaySetup } from "./components/GatewaySetup.vue";
|
|||||||
export { default as AddPrinter } from "./components/AddPrinter.vue";
|
export { default as AddPrinter } from "./components/AddPrinter.vue";
|
||||||
export { default as ManagedPrinters } from "./components/ManagedPrinters.vue";
|
export { default as ManagedPrinters } from "./components/ManagedPrinters.vue";
|
||||||
export { default as FarmStatus } from "./components/FarmStatus.vue";
|
export { default as FarmStatus } from "./components/FarmStatus.vue";
|
||||||
export type { NewPrinter, AppOrg, AppAlert, AppDiscoveredCandidate, AppFarm, AppFarmDiscovery, AppJob, AppPrinterState, AppTelemetryMessage, PrinterCommand } from "./types";
|
export type { PrinterKind, NewPrinter, AppOrg, AppAlert, AppDiscoveredCandidate, AppFarm, AppFarmDiscovery, AppJob, AppPrinterState, AppTelemetryMessage, PrinterCommand } from "./types";
|
||||||
|
|||||||
@@ -125,10 +125,19 @@ export interface AppOrg {
|
|||||||
name: string;
|
name: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** What the add-printer form hands to the page. The access code is a secret: send it, do not keep it. */
|
/** The printer kinds a gateway can drive, by the name the API takes. */
|
||||||
|
export type PrinterKind = "bambu" | "klipper" | "octoprint" | "prusa_link";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* What the add-printer form hands to the page. The access code and API key
|
||||||
|
* are secrets: send them, do not keep them. Which are present depends on
|
||||||
|
* `vendor` (Bambu: code + serial; OctoPrint/PrusaLink: API key; Klipper: none).
|
||||||
|
*/
|
||||||
export interface NewPrinter {
|
export interface NewPrinter {
|
||||||
|
vendor: PrinterKind;
|
||||||
name: string;
|
name: string;
|
||||||
host: string;
|
host: string;
|
||||||
serialNumber: string;
|
serialNumber?: string;
|
||||||
accessCode: string;
|
accessCode?: string;
|
||||||
|
apiKey?: string;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user