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

    Mayur Dosi

    I am Assistant Project Manager at iFlair, specializing in PHP, Laravel, CodeIgniter, Symphony, JavaScript, JS frameworks ,Python, and DevOps. With extensive experience in web development and cloud infrastructure, I play a key role in managing and delivering high-quality software solutions. I am Passionate about technology, automation, and scalable architectures, I am ensures seamless project execution, bridging the gap between development and operations. I am adept at leading teams, optimizing workflows, and integrating cutting-edge solutions to enhance performance and efficiency. Project planning and good strategy to manage projects tasks and deliver to clients on time. Easy to adopt new technologies learn and work on it as per the new requirments and trends. When not immersed in code and project planning, I am enjoy exploring the latest advancements in AI, cloud computing, and open-source technologies.



    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.