This document describes the printer state monitoring and event subscription functionality added to the @printers/printers library.
The printer state monitoring system allows you to:
- Monitor printer connections - Get notified when printers connect or disconnect
- Track state changes - Monitor when printers change state (idle ↔ printing ↔ paused ↔ error)
- Monitor error conditions - Get notifications when printer state reasons change (e.g., paper jam, low ink)
- Subscribe to events - Register callbacks to handle state change events in real-time
import {
startPrinterStateMonitoring,
stopPrinterStateMonitoring,
isPrinterStateMonitoringActive,
} from "@printers/printers";
// Check if monitoring is active
console.log("Active:", isPrinterStateMonitoringActive()); // false
// Start monitoring with default settings (2 second polling)
await startPrinterStateMonitoring();
// Start monitoring with custom configuration
await startPrinterStateMonitoring({
pollInterval: 5, // Poll every 5 seconds
autoStart: true, // Start immediately
});
// Check status
console.log("Active:", isPrinterStateMonitoringActive()); // true
// Stop monitoring
await stopPrinterStateMonitoring();import {
subscribeToPrinterStateChanges,
type PrinterStateChangeEvent,
} from "@printers/printers";
// Subscribe to all printer state change events
const subscription = await subscribeToPrinterStateChanges(
(event: PrinterStateChangeEvent) => {
console.log(`Event: ${event.eventType} for ${event.printerName}`);
switch (event.eventType) {
case "connected":
console.log(`Printer ${event.printerName} connected`);
break;
case "disconnected":
console.log(`Printer ${event.printerName} disconnected`);
break;
case "state_changed":
console.log(
`Printer ${event.printerName}: ${event.oldState} → ${event.newState}`
);
break;
case "state_reasons_changed":
console.log(`Printer ${event.printerName} reasons changed:`);
console.log(` Old: [${event.oldReasons?.join(", ")}]`);
console.log(` New: [${event.newReasons?.join(", ")}]`);
break;
}
}
);
// Unsubscribe when done
await subscription.unsubscribe();import { getPrinterStateSnapshots } from "@printers/printers";
const snapshots = await getPrinterStateSnapshots();
for (const [printerName, snapshot] of snapshots) {
console.log(`${printerName}:`);
console.log(` State: ${snapshot.state}`);
console.log(` Reasons: [${snapshot.stateReasons.join(", ")}]`);
console.log(` Timestamp: ${new Date(snapshot.timestamp)}`);
}// Create multiple event handlers
const generalSubscription = await subscribeToPrinterStateChanges(event => {
// Log all events
console.log(`[General] ${event.eventType}: ${event.printerName}`);
});
const errorSubscription = await subscribeToPrinterStateChanges(event => {
// Handle only error-related events
if (
event.eventType === "state_reasons_changed" &&
event.newReasons &&
event.newReasons.length > 0
) {
console.log(
`[Error] Printer ${event.printerName} has issues: ${event.newReasons.join(", ")}`
);
// Send alert, log to monitoring system, etc.
}
});
// Both subscriptions will receive events
// Monitoring automatically starts when first subscription is created
// Monitoring automatically stops when last subscription is removed// Start with fast polling for responsive monitoring
await startPrinterStateMonitoring({ pollInterval: 1 });
// Later, reduce polling frequency to save resources
await setPrinterStateMonitoringInterval(10);import { getPrinterByName } from "@printers/printers";
const subscription = await subscribeToPrinterStateChanges(event => {
// Filter events for specific printer
if (event.printerName === "Important Printer") {
console.log(`Important printer event: ${event.eventType}`);
if (event.eventType === "disconnected") {
// Send urgent alert
sendAlert("Critical printer offline!");
}
}
});Starts printer state monitoring.
Parameters:
config(optional): Configuration objectpollInterval: Polling interval in seconds (default: 2)autoStart: Whether to start immediately (default: true)
Stops printer state monitoring.
Checks if printer state monitoring is currently active.
subscribeToPrinterStateChanges(callback: PrinterStateChangeCallback): Promise<PrinterStateSubscription>
Subscribes to printer state change events.
Parameters:
callback: Function to call when events occur
Returns:
PrinterStateSubscription: Object withidandunsubscribe()method
Gets current state of all printers.
Returns:
- Map of printer names to their current state information
Sets the polling interval for state monitoring.
Parameters:
seconds: Polling interval (minimum: 1 second)
type PrinterStateEventType =
| "connected" // Printer appeared/connected
| "disconnected" // Printer removed/disconnected
| "state_changed" // Printer state changed
| "state_reasons_changed"; // Printer state reasons changedinterface PrinterStateChangeEvent {
eventType: PrinterStateEventType;
printerName: string;
oldState?: PrinterState; // For state_changed events
newState?: PrinterState; // For state_changed events
oldReasons?: string[]; // For state_reasons_changed events
newReasons?: string[]; // For state_reasons_changed events
timestamp: number; // Unix timestamp
}interface PrinterStateSnapshot {
name: string;
state: PrinterState; // "idle" | "printing" | "paused" | "offline" | "unknown"
stateReasons: string[];
timestamp: number;
}interface PrinterStateSubscription {
id: number;
unsubscribe(): Promise<boolean>;
}interface PrinterStateMonitorConfig {
pollInterval?: number; // Polling interval in seconds (default: 2)
autoStart?: boolean; // Start monitoring immediately (default: true)
}The following printer states are reported (mapped from the printers crate's PrinterState enum):
"idle"- Printer is ready and available (fromPrinterState::READY)"printing"- Printer is currently printing (fromPrinterState::PRINTING)"paused"- Printer is paused (fromPrinterState::PAUSED)"offline"- Printer is offline or disconnected (fromPrinterState::OFFLINE)"unknown"- Printer state cannot be determined (fromPrinterState::UNKNOWN)
import {
subscribeToPrinterStateChanges,
getPrinterStateSnapshots,
} from "@printers/printers";
class PrintFarmMonitor {
private subscription: PrinterStateSubscription | null = null;
private alerts: string[] = [];
async start() {
this.subscription = await subscribeToPrinterStateChanges(event => {
this.handlePrinterEvent(event);
});
// Initial status check
this.checkAllPrinters();
}
async stop() {
if (this.subscription) {
await this.subscription.unsubscribe();
this.subscription = null;
}
}
private handlePrinterEvent(event: PrinterStateChangeEvent) {
switch (event.eventType) {
case "disconnected":
this.addAlert(`CRITICAL: Printer ${event.printerName} went offline`);
break;
case "state_reasons_changed":
if (event.newReasons && event.newReasons.length > 0) {
this.addAlert(
`WARNING: ${event.printerName} - ${event.newReasons.join(", ")}`
);
}
break;
case "state_changed":
if (event.newState === "paused") {
this.addAlert(`WARNING: Printer ${event.printerName} paused`);
}
break;
}
}
private async checkAllPrinters() {
const snapshots = await getPrinterStateSnapshots();
for (const [name, snapshot] of snapshots) {
if (
snapshot.state === "offline" ||
snapshot.state === "paused" ||
snapshot.stateReasons.length > 0
) {
this.addAlert(
`STATUS: ${name} - ${snapshot.state} (${snapshot.stateReasons.join(", ")})`
);
}
}
}
private addAlert(message: string) {
this.alerts.push(`${new Date().toISOString()}: ${message}`);
console.log(message);
// Send to monitoring system, email alerts, etc.
}
}
// Usage
const monitor = new PrintFarmMonitor();
await monitor.start();
// Monitor will run until stopped
// await monitor.stop();import {
subscribeToPrinterStateChanges,
getPrinterByName,
getAllPrinters,
} from "@printers/printers";
class PrinterFailover {
private primaryPrinter: string;
private backupPrinters: string[];
private subscription: PrinterStateSubscription | null = null;
constructor(primaryPrinter: string, backupPrinters: string[]) {
this.primaryPrinter = primaryPrinter;
this.backupPrinters = backupPrinters;
}
async start() {
this.subscription = await subscribeToPrinterStateChanges(event => {
if (event.printerName === this.primaryPrinter) {
this.handlePrimaryPrinterEvent(event);
}
});
}
private handlePrimaryPrinterEvent(event: PrinterStateChangeEvent) {
if (
event.eventType === "disconnected" ||
(event.eventType === "state_changed" &&
(event.newState === "offline" || event.newState === "paused"))
) {
console.log(
`Primary printer ${this.primaryPrinter} failed, switching to backup...`
);
this.switchToBackup();
}
}
private async switchToBackup() {
for (const backupName of this.backupPrinters) {
const backup = await getPrinterByName(backupName);
if (backup && backup.state === "idle") {
console.log(`Switched to backup printer: ${backupName}`);
// Update application configuration to use backup printer
return;
}
}
console.error("No backup printers available!");
}
}The state monitoring system works seamlessly in simulation mode for safe testing:
// Set simulation mode
process.env.PRINTERS_JS_SIMULATE = "true";
// or for Deno:
// Deno.env.set("PRINTERS_JS_SIMULATE", "true");
import { subscribeToPrinterStateChanges } from "@printers/printers";
// Events will be generated for simulated printers
const subscription = await subscribeToPrinterStateChanges(event => {
console.log(`Simulated event: ${event.eventType} for ${event.printerName}`);
});- Polling frequency: Lower polling intervals (1-2 seconds) provide more responsive monitoring but use more CPU
- Multiple subscriptions: Each subscription receives all events; filter in callbacks for efficiency
- Resource cleanup: Always unsubscribe when done to prevent memory leaks
- Network printers: State changes may have delay depending on network conditions
The state monitoring system works identically across all supported runtimes:
// Node.js
import { subscribeToPrinterStateChanges } from "@printers/printers";
// Deno
import { subscribeToPrinterStateChanges } from "npm:@printers/printers";
// Bun
import { subscribeToPrinterStateChanges } from "@printers/printers";
// Usage is identical across all runtimes
const subscription = await subscribeToPrinterStateChanges(event => {
console.log(`${event.eventType}: ${event.printerName}`);
});