179 lines
4.6 KiB
TypeScript
179 lines
4.6 KiB
TypeScript
// src/octoprint-client.ts
|
|
// OctoPrint REST API client
|
|
|
|
export interface OctoPrintSettings {
|
|
host: string; // e.g. "http://octopi.local" or "http://192.168.1.100"
|
|
port: string; // e.g. "80" or "5000"
|
|
apiKey: string; // OctoPrint API key
|
|
}
|
|
|
|
export interface PrinterState {
|
|
state: {
|
|
text: string;
|
|
flags: {
|
|
operational: boolean;
|
|
paused: boolean;
|
|
printing: boolean;
|
|
pausing: boolean;
|
|
cancelling: boolean;
|
|
sdReady: boolean;
|
|
error: boolean;
|
|
ready: boolean;
|
|
closedOrError: boolean;
|
|
};
|
|
};
|
|
temperature?: {
|
|
tool0?: { actual: number; target: number };
|
|
bed?: { actual: number; target: number };
|
|
};
|
|
}
|
|
|
|
export interface JobState {
|
|
job: {
|
|
file: { name: string | null };
|
|
estimatedPrintTime: number | null;
|
|
};
|
|
progress: {
|
|
completion: number | null;
|
|
printTimeLeft: number | null;
|
|
printTime: number | null;
|
|
};
|
|
state: string;
|
|
}
|
|
|
|
export interface ConnectionState {
|
|
current: {
|
|
baudrate: number | null;
|
|
port: string | null;
|
|
printerProfile: string;
|
|
state: string;
|
|
};
|
|
}
|
|
|
|
export interface PsuControlState {
|
|
isPSUOn: boolean;
|
|
}
|
|
|
|
export class OctoPrintClient {
|
|
private baseUrl: string;
|
|
private apiKey: string;
|
|
|
|
constructor(settings: OctoPrintSettings) {
|
|
const port = settings.port ? `:${settings.port}` : "";
|
|
this.baseUrl = `${settings.host}${port}/api`;
|
|
this.apiKey = settings.apiKey;
|
|
}
|
|
|
|
private get headers(): Record<string, string> {
|
|
return {
|
|
"X-Api-Key": this.apiKey,
|
|
"Content-Type": "application/json",
|
|
};
|
|
}
|
|
|
|
async getPrinterState(): Promise<PrinterState> {
|
|
const res = await fetch(`${this.baseUrl}/printer`, {
|
|
headers: this.headers,
|
|
});
|
|
if (!res.ok) throw new Error(`HTTP ${res.status}: ${res.statusText}`);
|
|
return res.json() as Promise<PrinterState>;
|
|
}
|
|
|
|
async getJobState(): Promise<JobState> {
|
|
const res = await fetch(`${this.baseUrl}/job`, {
|
|
headers: this.headers,
|
|
});
|
|
if (!res.ok) throw new Error(`HTTP ${res.status}: ${res.statusText}`);
|
|
return res.json() as Promise<JobState>;
|
|
}
|
|
|
|
async getConnectionState(): Promise<ConnectionState> {
|
|
const res = await fetch(`${this.baseUrl}/connection`, {
|
|
headers: this.headers,
|
|
});
|
|
if (!res.ok) throw new Error(`HTTP ${res.status}: ${res.statusText}`);
|
|
return res.json() as Promise<ConnectionState>;
|
|
}
|
|
|
|
async pausePrint(): Promise<void> {
|
|
await fetch(`${this.baseUrl}/job`, {
|
|
method: "POST",
|
|
headers: this.headers,
|
|
body: JSON.stringify({ command: "pause", action: "toggle" }),
|
|
});
|
|
}
|
|
|
|
async cancelPrint(): Promise<void> {
|
|
await fetch(`${this.baseUrl}/job`, {
|
|
method: "POST",
|
|
headers: this.headers,
|
|
body: JSON.stringify({ command: "cancel" }),
|
|
});
|
|
}
|
|
|
|
async homeAxes(axes: string[] = ["x", "y", "z"]): Promise<void> {
|
|
await fetch(`${this.baseUrl}/printer/printhead`, {
|
|
method: "POST",
|
|
headers: this.headers,
|
|
body: JSON.stringify({ command: "home", axes }),
|
|
});
|
|
}
|
|
|
|
async setTemperature(tool: "tool0" | "bed", target: number): Promise<void> {
|
|
if (tool === "bed") {
|
|
await fetch(`${this.baseUrl}/printer/bed`, {
|
|
method: "POST",
|
|
headers: this.headers,
|
|
body: JSON.stringify({ command: "target", target }),
|
|
});
|
|
} else {
|
|
await fetch(`${this.baseUrl}/printer/tool`, {
|
|
method: "POST",
|
|
headers: this.headers,
|
|
body: JSON.stringify({ command: "target", targets: { [tool]: target } }),
|
|
});
|
|
}
|
|
}
|
|
|
|
async getPSUState(): Promise<PsuControlState> {
|
|
const res = await fetch(`${this.baseUrl}/plugin/psucontrol`, {
|
|
headers: this.headers,
|
|
});
|
|
if (!res.ok) throw new Error(`HTTP ${res.status}: ${res.statusText}`);
|
|
return res.json() as Promise<PsuControlState>;
|
|
}
|
|
|
|
async turnPSUOn(): Promise<void> {
|
|
await fetch(`${this.baseUrl}/plugin/psucontrol`, {
|
|
method: "POST",
|
|
headers: this.headers,
|
|
body: JSON.stringify({ command: "turnPSUOn" }),
|
|
});
|
|
}
|
|
|
|
async turnPSUOff(): Promise<void> {
|
|
await fetch(`${this.baseUrl}/plugin/psucontrol`, {
|
|
method: "POST",
|
|
headers: this.headers,
|
|
body: JSON.stringify({ command: "turnPSUOff" }),
|
|
});
|
|
}
|
|
|
|
async testConnection(): Promise<boolean> {
|
|
try {
|
|
const res = await fetch(`${this.baseUrl}/version`, { headers: this.headers });
|
|
return res.ok;
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
export function formatTime(seconds: number | null): string {
|
|
if (seconds === null || seconds === undefined) return "--:--";
|
|
const h = Math.floor(seconds / 3600);
|
|
const m = Math.floor((seconds % 3600) / 60);
|
|
if (h > 0) return `${h}h ${m}m`;
|
|
return `${m}m`;
|
|
}
|