// 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 { return { "X-Api-Key": this.apiKey, "Content-Type": "application/json", }; } async getPrinterState(): Promise { 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; } async getJobState(): Promise { 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; } async getConnectionState(): Promise { 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; } async pausePrint(): Promise { await fetch(`${this.baseUrl}/job`, { method: "POST", headers: this.headers, body: JSON.stringify({ command: "pause", action: "toggle" }), }); } async cancelPrint(): Promise { await fetch(`${this.baseUrl}/job`, { method: "POST", headers: this.headers, body: JSON.stringify({ command: "cancel" }), }); } async homeAxes(axes: string[] = ["x", "y", "z"]): Promise { 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 { 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 { 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; } async turnPSUOn(): Promise { await fetch(`${this.baseUrl}/plugin/psucontrol`, { method: "POST", headers: this.headers, body: JSON.stringify({ command: "turnPSUOn" }), }); } async turnPSUOff(): Promise { await fetch(`${this.baseUrl}/plugin/psucontrol`, { method: "POST", headers: this.headers, body: JSON.stringify({ command: "turnPSUOff" }), }); } async testConnection(): Promise { 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`; }