How to Detect Printer Status (Online, Offline, Paper Out, or Errors) in Node JS

How to Detect Printer Status (Online, Offline, Paper Out, or Errors) in Node.js with Kiosk

Nov 18, 2025 |

13 minutes read

How to Detect Printer Status (Online, Offline, Paper Out, or Errors) in Node JS

Printer Status Monitoring with Node.js

When managing kiosks, retail terminals, or POS systems, it is essential to know whether your printer is online, offline, or out of paper. We’ll build a Node.js-based printer status monitor using SNMP, ESC/POS commands, and Express.js to expose a simple API.

What We’ll Build

  • Automatically discovers printers via SNMP
  • Uses ESC/POS status commands to detect:

○     Online/offline state

○     Paper out / paper near the end

○     Cover open

○     Mechanical or cutter errors

  • Serves a REST API endpoint (/) that returns real-time printer information in JSON.

Project Setup:

1. Environment Variables

Create a .env file


# Printer SNMP settings
PRINTER_SNMP_COMMUNITY=public
PRINTER_IP=192.168.10.148
PRINTER_INTERVAL=60000
PRINTER_START_INTERVAL=15000
PORT=9100

2. Printer Discovery via SNMP

 File: printers/discovery.js


const snmp = require('net-snmp');
const { getEscPosStatus, getEscPosInfo } = require('./status');
 
let printers = [];
 
// ----------------------
// Helper: Add or update printer (deduplicate by IP + port)
// ----------------------
function addOrUpdatePrinter(newPrinter) {
 const existing = printers.find(p => p.ip === newPrinter.ip && p.port === newPrinter.port);
 if (existing) {
   // Prefer Bonjour info if available
   if (newPrinter.protocol === "Bonjour") {
 	existing.name = newPrinter.name || existing.name;
 	existing.protocol = newPrinter.protocol;
   }
   existing.model = newPrinter.model || existing.model;
   existing.firmware = newPrinter.firmware || existing.firmware;
 } else {
   printers.push(newPrinter);
 }
}
 
 function discoverSNMP(printerIp = process.env.PRINTER_IP) {
 return new Promise((resolve) => {
   const found = [];
   let completed = 0;
 
   // SNMP OIDs
   const OIDS = {
 	sysName: "1.3.6.1.2.1.1.5.0",  // Hostname
 	sysDescr: "1.3.6.1.2.1.1.1.0",  // System description (model/firmware)
 	sysLocation: "1.3.6.1.2.1.1.6.0", // Location string
 	prtGeneralPrinterName: "1.3.6.1.2.1.43.5.1.1.16.1", // Printer name
 	prtGeneralSerialNumber: "1.3.6.1.2.1.43.5.1.1.17.1", // Serial number
   };
 
   const ip = printerIp;
   const session = snmp.createSession(ip, process.env.PRINTER_SNMP_COMMUNITY);
 
   session.get(Object.values(OIDS), (err, varbinds) => {
 	if (!err && varbinds && varbinds.length > 0) {
   	const printer = {
     	id: `${ip}:9100`,
     	ip,
     	port: process.env.PORT,
     	protocol: "SNMP",
     	name: varbinds[0]?.value?.toString() || "", // sysName
     	model: varbinds[1]?.value?.toString() || "", // sysDescr
     	location: varbinds[2]?.value?.toString() || "", // sysLocation
     	printerName: varbinds[3]?.value?.toString() || "", // prtGeneralPrinterName
     	serialNumber: varbinds[4]?.value?.toString() || "", // prtGeneralSerialNumber
   	};
 
   	addOrUpdatePrinter(printer);
   	found.push(printer);
 	}
 
 	session.close();
 	completed++;
 	if (completed >= 254) resolve(found);
   });
 
   // Safety timeout
   setTimeout(() => resolve(found), 8000);
 });
}
 
// ----------------------
// Periodic discovery & cleanup (keep only online printers)
// ----------------------
function discoverPrintersPeriodic(interval = process.env.PRINTER_INTERVAL, ip = process.env.PRINTER_IP) {
 async function discover() {
   // await discoverBonjour();
   await discoverSNMP(ip);
 
   // Check status for all printers and remove offline ones
   for (let i = printers.length - 1; i >= 0; i--) {
 	const p = printers[i];
 	try {
   	const status = await getEscPosStatus(p);
   	if (!status.online) printers.splice(i, 1);
 	} catch {
   	printers.splice(i, 1);
 	}
   }
 
   setTimeout(discover, interval);
 }
 discover();
}
 
module.exports = { printers, discoverPrintersPeriodic, addOrUpdatePrinter };

3. Checking Printer Status via ESC/POS Commands

File: printers/status.js


const net = require('net');
 
// ESC/POS commands
const STATUS_COMMANDS = {
 printer: Buffer.from([0x10, 0x04, 0x01]),
 offline: Buffer.from([0x10, 0x04, 0x02]),
 error: Buffer.from([0x10, 0x04, 0x03]),
 paper: Buffer.from([0x10, 0x04, 0x04]),
};
const GS_I_MODEL = Buffer.from([0x1D, 0x49, 0x01]);
const GS_I_FIRMWARE = Buffer.from([0x1D, 0x49, 0x02]);
 
async function queryEscPos(ip, port, command) {
 return new Promise((resolve, reject) => {
   const client = new net.Socket();
   let buffer = Buffer.alloc(0);
 
   client.setTimeout(2000, () => { client.destroy(); reject(new Error("Timeout")); });
   client.connect(port, ip, () => client.write(command));
 
   client.on('data', data => { buffer = Buffer.concat([buffer, data]); client.destroy(); resolve(buffer.toString().trim()); });
   client.on('error', reject);
 });
}
 
async function getEscPosInfo(printer) {
 try {
   const model = await queryEscPos(printer.ip, printer.port, GS_I_MODEL);
   const firmware = await queryEscPos(printer.ip, printer.port, GS_I_FIRMWARE);
   return { model, firmware };
 } catch {
   return {};
 }
}
 
async function getEscPosStatus(printer) {
 const commands = [STATUS_COMMANDS.printer, STATUS_COMMANDS.offline, STATUS_COMMANDS.error, STATUS_COMMANDS.paper];
 const results = [];
 
 try {
   for (const cmd of commands) {
 	const res = await queryEscPos(printer.ip, printer.port, cmd);
 	results.push(res.charCodeAt(0) || 0);
   }
   const [printerB, offline, error, paper] = results;
   const status = {
 	online: !(offline & 0x08),
 	coverOpen: !!(error & 0x04),
 	paperOut: !!(paper & 0x60),
 	paperNearEnd: !!(paper & 0x0C),
 	mechanicalError: !!(error & 0x40),
 	cutterError: !!(error & 0x08),
 	unrecoverableError: !!(error & 0x20),
   };
   status.error = status.coverOpen || status.paperOut || status.mechanicalError || status.cutterError || status.unrecoverableError;
   return status;
 } catch {
   return { online: false, error: true };
 }
}
 
module.exports = { getEscPosInfo, getEscPosStatus };

4. Logging Printer Events

File: printers/events.js


function logPrinterStatus(printer, status) {
 const symbol = status.error ? 'âś–' : 'âś”';
 const color = status.error ? '\x1b[31m' : '\x1b[32m';
 console.log(`${color}${symbol} [${printer.protocol}] ${printer.name}: ${status.error ? 'ERROR' : 'OK'}\x1b[0m`);
}
 
module.exports = { logPrinterStatus };

5. Creating the Express API Endpoint

File: routes/printers.js


const express = require('express');
const router = express.Router();
const { printers } = require('../printers/discovery');
const { getEscPosStatus, getEscPosInfo } = require('../printers/status');
const { logPrinterStatus } = require('../printers/events');
const fs = require('fs');
const path = require('path');
 
router.get('/', async (req, res) => {
 const debug = req.query.debug === 'true';
 const enriched = [];
 const configPath = path.join(__dirname, '../config.json');
 const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
 
 const kioskName = config.KIOSK_NAME;
 for (const p of printers) {
   try {
 	// Get printer status
 	const status = await getEscPosStatus(p);
 	if (!status.online) continue;
 
 	// Get ESC/POS info (model, firmware)
 	const escposInfo = await getEscPosInfo(p);
 
 	// Build enriched printer object
 	const printerData = {
   	id: p.id,
   	ip: p.ip,
   	port: p.port,
   	protocol: p.protocol,
   	name: p.name || p.printerName || "",
   	model:
     	escposInfo.model && escposInfo.model.length > 2
       	? escposInfo.model
       	: p.model || p.printerName || "Unknown",
   	firmware:
     	escposInfo.firmware && /^[\x20-\x7E]+$/.test(escposInfo.firmware)
       	? escposInfo.firmware
       	: undefined,
   	serialNumber: p.serialNumber || "",
   	kioskId: kioskName,
   	location: p.location || "",
   	online: status.online,
   	coverOpen: status.coverOpen || false,
   	paperOut: status.paperOut || false,
   	paperNearEnd: status.paperNearEnd || false,
   	mechanicalError: status.mechanicalError || false,
   	cutterError: status.cutterError || false,
   	unrecoverableError: status.unrecoverableError || false,
   	error: status.error || false,
 	};
 
 	if (debug) logPrinterStatus(p, status);
 
 	enriched.push(printerData);
   } catch (e) {
 	console.error("Failed to enrich printer:", p.ip, e);
   }
 }
 
 res.json(enriched);
});
 
module.exports = router;

6. Starting the Service

File: server.js


require('dotenv').config();
const express = require('express');
const http = require('http');
const cors = require('cors');
 
const { discoverPrintersPeriodic } = require('./printers/discovery');
const printersRoute = require('./routes/printers');
const paymentRoute = require('./routes/payment');
const printRoute = require('./routes/print');
const { startWebSocket, startMonitoring } = require('./printers/monitor');
 
const app = express();
const PORT = 3001;
const server = http.createServer(app);
 
app.use(cors());
app.use(express.json());
 
app.use('/api/printers', printersRoute);
 
// Start periodic network discovery (removes offline printers automatically)
discoverPrintersPeriodic(process.env.PRINTER_INTERVAL);
 
// Start WebSocket server
startWebSocket(server);
 
// Start real-time monitoring
startMonitoring(5000);
 
server.listen(PORT, () => console.log(`Server running at http://localhost:${PORT}`));

Run it:

node server.js

Visit: http://localhost:3000/printers

7. Example Output:


[
 {
   "id": "192.168.10.148:9100",
   "ip": "192.168.10.148",
   "protocol": "SNMP",
   "name": "EPSON_TM_T88VI",
   "model": "EPSON TM-T88VI Receipt Printer",
   "firmware": "1.02",
   "serialNumber": "AB12345678",
   "kioskId": "Kiosk-001",
   "online": true,
   "coverOpen": false,
   "paperOut": false,
   "paperNearEnd": false,
   "mechanicalError": false,
   "cutterError": false,
   "unrecoverableError": false,
   "error": false
 }
]

Printer Status insights for real-time kiosk monitoring

The Way Forward

Integrating Apple Pay and Google Pay using Stripe in React Native is a powerful way to deliver seamless and secure payment experiences to users worldwide. With a few lines of configuration and careful setup, you can enable one-tap payments and boost conversion rates.

This guide covered the end-to-end process from SDK setup and backend creation to testing React Native apps online and deployment, giving you a solid foundation for handling mobile payments confidently.

Next step: Explore advanced flows like saving payment methods, handling subscriptions, or integrating 3D Secure authentication for maximum protection.

Free Consultation

    Chandra Rao



    MAP_New

    Global Footprints

    Served clients across the globe from38+ countries

    iFlair Web Technologies
    Privacy Overview

    This website uses cookies so that we can provide you with the best user experience possible. Cookie information is stored in your browser and performs functions such as recognising you when you return to our website and helping our team to understand which sections of the website you find most interesting and useful.