import type { IConfigFlow, IConfigFlowContext, IConfigFlowStep, IDiscoveryCandidate } from '../../core/types.js'; import type { IIppAttributeRecord, IIppConfig, IIppSnapshot } from './ipp.types.js'; import { ippDefaultBasePath, ippDefaultPort, ippDefaultTimeoutMs } from './ipp.types.js'; export class IppConfigFlow implements IConfigFlow { public async start(candidateArg: IDiscoveryCandidate, contextArg: IConfigFlowContext): Promise> { void contextArg; return { kind: 'form', title: 'Connect IPP printer', description: 'Configure the local IPP printer endpoint.', fields: [ { name: 'host', label: 'Host', type: 'text', required: true }, { name: 'port', label: 'Port', type: 'number' }, { name: 'basePath', label: 'Relative path to the printer', type: 'text' }, { name: 'tls', label: 'Use SSL/TLS', type: 'boolean' }, { name: 'verifySsl', label: 'Verify SSL certificate', type: 'boolean' }, { name: 'name', label: 'Name', type: 'text' }, ], submit: async (valuesArg) => { const metadata = candidateArg.metadata || {}; const snapshot = snapshotFromMetadata(metadata); const attributes = attributesFromMetadata(metadata); const host = this.stringValue(valuesArg.host) || candidateArg.host || snapshot?.printer.host || ''; if (!host && !snapshot && !attributes && !metadata.client) { return { kind: 'error', title: 'IPP setup failed', error: 'IPP setup requires a host, snapshot, attributes, or injected client.' }; } const basePath = this.basePathValue(valuesArg.basePath) || this.stringMetadata(metadata, 'basePath') || snapshot?.printer.basePath || ippDefaultBasePath; const port = this.numberValue(valuesArg.port) || candidateArg.port || snapshot?.printer.port || ippDefaultPort; const tls = this.booleanValue(valuesArg.tls) ?? this.booleanMetadata(metadata, 'tls') ?? snapshot?.printer.tls ?? false; const verifySsl = this.booleanValue(valuesArg.verifySsl) ?? this.booleanMetadata(metadata, 'verifySsl') ?? false; const uuid = this.stringMetadata(metadata, 'uuid') || snapshot?.printer.uuid; const serial = candidateArg.serialNumber || snapshot?.printer.serialNumber; return { kind: 'done', title: 'IPP printer configured', config: { host, port, basePath, tls, verifySsl, name: this.stringValue(valuesArg.name) || candidateArg.name || snapshot?.printer.name, manufacturer: candidateArg.manufacturer || snapshot?.printer.manufacturer, model: candidateArg.model || snapshot?.printer.model, uniqueId: candidateArg.id || uuid || serial || (host ? `${host}:${port}${basePath}` : undefined), uuid, serial, timeoutMs: ippDefaultTimeoutMs, snapshot, attributes, client: metadata.client as IIppConfig['client'], }, }; }, }; } private stringValue(valueArg: unknown): string | undefined { return typeof valueArg === 'string' && valueArg.trim() ? valueArg.trim() : undefined; } private numberValue(valueArg: unknown): number | undefined { if (typeof valueArg === 'number' && Number.isFinite(valueArg) && valueArg > 0) { return Math.round(valueArg); } if (typeof valueArg === 'string' && valueArg.trim()) { const parsed = Number(valueArg); return Number.isFinite(parsed) && parsed > 0 ? Math.round(parsed) : undefined; } return undefined; } private booleanValue(valueArg: unknown): boolean | undefined { if (typeof valueArg === 'boolean') { return valueArg; } if (typeof valueArg === 'string') { const normalized = valueArg.trim().toLowerCase(); if (['true', 'yes', 'on', '1'].includes(normalized)) { return true; } if (['false', 'no', 'off', '0'].includes(normalized)) { return false; } } return undefined; } private basePathValue(valueArg: unknown): string | undefined { const value = this.stringValue(valueArg); if (!value) { return undefined; } return value.startsWith('/') ? value : `/${value}`; } private stringMetadata(metadataArg: Record, keyArg: string): string | undefined { return this.stringValue(metadataArg[keyArg]); } private booleanMetadata(metadataArg: Record, keyArg: string): boolean | undefined { const value = metadataArg[keyArg]; return typeof value === 'boolean' ? value : undefined; } } const snapshotFromMetadata = (metadataArg: Record): IIppSnapshot | undefined => { const snapshot = metadataArg.snapshot; return snapshot && typeof snapshot === 'object' && 'printer' in snapshot && 'status' in snapshot ? snapshot as IIppSnapshot : undefined; }; const attributesFromMetadata = (metadataArg: Record): IIppAttributeRecord | undefined => { const attributes = metadataArg.attributes; return attributes && typeof attributes === 'object' && !Array.isArray(attributes) ? attributes as IIppAttributeRecord : undefined; };