Initial commit

This commit is contained in:
Douglas Barone 2023-05-15 10:55:22 -04:00
parent 809200a451
commit aa41351185
11 changed files with 505 additions and 9 deletions

86
dist/Printer.mjs vendored Normal file
View File

@ -0,0 +1,86 @@
const printerObjectIDConfigs = [
{
model: 'm3655idn',
objectIds: {
model: '1.3.6.1.2.1.25.3.2.1.3.1',
serial: '1.3.6.1.2.1.43.5.1.1.17.1',
counter: '1.3.6.1.4.1.1347.43.10.1.1.12.1.1',
toners: {
black: {
current: '1.3.6.1.2.1.43.11.1.1.9.1.1',
max: '1.3.6.1.2.1.43.11.1.1.8.1.1',
model: '1.3.6.1.2.1.43.11.1.1.6.1.1'
}
}
}
},
{
model: 'p6235cdn',
objectIds: {
model: '1.3.6.1.2.1.25.3.2.1.3.1',
serial: '1.3.6.1.2.1.43.5.1.1.17.1',
counter: '1.3.6.1.4.1.1347.43.10.1.1.12.1.1',
toners: {
black: {
current: '1.3.6.1.2.1.43.11.1.1.9.1.4',
max: '1.3.6.1.2.1.43.11.1.1.8.1.4',
model: '1.3.6.1.2.1.43.11.1.1.6.1.4'
},
cyan: {
current: '1.3.6.1.2.1.43.11.1.1.9.1.1',
max: '1.3.6.1.2.1.43.11.1.1.8.1.1',
model: '1.3.6.1.2.1.43.11.1.1.6.1.1'
},
magenta: {
current: '1.3.6.1.2.1.43.11.1.1.9.1.2',
max: '1.3.6.1.2.1.43.11.1.1.8.1.2',
model: '1.3.6.1.2.1.43.11.1.1.6.1.2'
},
yellow: {
current: '1.3.6.1.2.1.43.11.1.1.9.1.3',
max: '1.3.6.1.2.1.43.11.1.1.8.1.3',
model: '1.3.6.1.2.1.43.11.1.1.6.1.3'
}
}
}
},
{
model: 'm2040dn',
objectIds: {
model: '1.3.6.1.2.1.25.3.2.1.3.1',
serial: '1.3.6.1.2.1.43.5.1.1.17.1',
counter: '1.3.6.1.4.1.1347.43.10.1.1.12.1.1',
toners: {
black: {
current: '1.3.6.1.2.1.43.11.1.1.9.1.1',
max: '1.3.6.1.2.1.43.11.1.1.8.1.1',
model: '1.3.6.1.2.1.43.11.1.1.6.1.1'
}
}
}
}
];
export class Printer {
constructor(name, ip, model) {
this.name = name;
this.ip = ip;
this.model = model;
this.objectIds = printerObjectIDConfigs.find(x => x.model === model).objectIds;
}
oIDsArray() {
const oIDsArray = [];
function extractObjValues(obj) {
for (let key in obj) {
if (typeof obj[key] === 'object') {
extractObjValues(obj[key]);
}
else {
const oID = obj[key];
oIDsArray.push(oID);
}
}
}
extractObjValues(this.objectIds);
return oIDsArray;
}
}

7
dist/index.js vendored Normal file
View File

@ -0,0 +1,7 @@
import express from 'express';
const app = express();
const port = 3000;
app.get('/', (req, res) => { });
app.listen(port, () => {
console.log(`http://localhost:${port}`);
});

27
dist/printers.mjs vendored Normal file
View File

@ -0,0 +1,27 @@
export const printers = [
{
name: 'p04',
ip: '10.7.0.134',
model: 'm3655idn'
},
{
name: 'p05',
ip: '10.7.0.135',
model: 'm2040dn'
},
{
name: 'p06',
ip: '10.7.0.136',
model: 'm2040dn'
},
{
name: 'p07',
ip: '10.7.0.137',
model: 'm2040dn'
},
{
name: 'p08',
ip: '10.7.0.138',
model: 'p6235cdn'
}
];

1
dist/types.mjs vendored Normal file
View File

@ -0,0 +1 @@
export {};

26
package-lock.json generated
View File

@ -10,7 +10,8 @@
"license": "ISC",
"dependencies": {
"body-parser": "^1.20.2",
"express": "^4.18.2"
"express": "^4.18.2",
"net-snmp": "^3.9.3"
},
"devDependencies": {
"@types/express": "^4.17.17",
@ -297,6 +298,11 @@
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
"integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg=="
},
"node_modules/asn1-ber": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/asn1-ber/-/asn1-ber-1.2.2.tgz",
"integrity": "sha512-CbNem/7hxrjSiOAOOTX4iZxu+0m3jiLqlsERQwwPM1IDR/22M8IPpA1VVndCLw5KtjRYyRODbvAEIfuTogNDng=="
},
"node_modules/available-typed-arrays": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz",
@ -1655,6 +1661,15 @@
"node": ">= 0.6"
}
},
"node_modules/net-snmp": {
"version": "3.9.3",
"resolved": "https://registry.npmjs.org/net-snmp/-/net-snmp-3.9.3.tgz",
"integrity": "sha512-d2QjmDu7qs7QrgI8WuzALoARpOhzX4dQX/E5XY/P8zQhBga3QCRZqoJ5Tt1yFxkNFL2D+NFPvbT4LrAur754yg==",
"dependencies": {
"asn1-ber": "^1.2.1",
"smart-buffer": "^4.1.0"
}
},
"node_modules/nice-try": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
@ -2208,6 +2223,15 @@
"semver": "bin/semver.js"
}
},
"node_modules/smart-buffer": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz",
"integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==",
"engines": {
"node": ">= 6.0.0",
"npm": ">= 3.0.0"
}
},
"node_modules/spdx-correct": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz",

View File

@ -8,8 +8,7 @@
"clean": "rimraf ./dist",
"build": "npm run clean && tsc",
"start": "node src",
"local": "ts-node-esm ./src/index.ts",
"dev": "nodemon src -e ts,json --exec 'npm run local'"
"dev": "nodemon --ext js,ts,mts,mjs ./src/index.ts --exec ts-node-esm"
},
"keywords": [],
"author": "",
@ -25,6 +24,7 @@
},
"dependencies": {
"body-parser": "^1.20.2",
"express": "^4.18.2"
"express": "^4.18.2",
"net-snmp": "^3.9.3"
}
}

219
src/Printer.mts Normal file
View File

@ -0,0 +1,219 @@
import {
PrinterInfo,
PrinterModel,
PrinterObjectIDConfig,
PrinterObjectIDs,
Varbind,
VarbindString
} from './types.mjs'
import snmp from 'net-snmp'
const printerObjectIDConfigs: PrinterObjectIDConfig[] = [
{
model: 'm3655idn',
objectIds: {
model: '1.3.6.1.2.1.25.3.2.1.3.1',
serial: '1.3.6.1.2.1.43.5.1.1.17.1',
counter: '1.3.6.1.4.1.1347.43.10.1.1.12.1.1',
toners: {
black: {
current: '1.3.6.1.2.1.43.11.1.1.9.1.1',
max: '1.3.6.1.2.1.43.11.1.1.8.1.1',
model: '1.3.6.1.2.1.43.11.1.1.6.1.1'
}
}
}
},
{
model: 'p6235cdn',
objectIds: {
model: '1.3.6.1.2.1.25.3.2.1.3.1',
serial: '1.3.6.1.2.1.43.5.1.1.17.1',
counter: '1.3.6.1.4.1.1347.43.10.1.1.12.1.1',
toners: {
black: {
current: '1.3.6.1.2.1.43.11.1.1.9.1.4',
max: '1.3.6.1.2.1.43.11.1.1.8.1.4',
model: '1.3.6.1.2.1.43.11.1.1.6.1.4'
},
cyan: {
current: '1.3.6.1.2.1.43.11.1.1.9.1.1',
max: '1.3.6.1.2.1.43.11.1.1.8.1.1',
model: '1.3.6.1.2.1.43.11.1.1.6.1.1'
},
magenta: {
current: '1.3.6.1.2.1.43.11.1.1.9.1.2',
max: '1.3.6.1.2.1.43.11.1.1.8.1.2',
model: '1.3.6.1.2.1.43.11.1.1.6.1.2'
},
yellow: {
current: '1.3.6.1.2.1.43.11.1.1.9.1.3',
max: '1.3.6.1.2.1.43.11.1.1.8.1.3',
model: '1.3.6.1.2.1.43.11.1.1.6.1.3'
}
}
}
},
{
model: 'm2040dn',
objectIds: {
model: '1.3.6.1.2.1.25.3.2.1.3.1',
serial: '1.3.6.1.2.1.43.5.1.1.17.1',
counter: '1.3.6.1.4.1.1347.43.10.1.1.12.1.1',
toners: {
black: {
current: '1.3.6.1.2.1.43.11.1.1.9.1.1',
max: '1.3.6.1.2.1.43.11.1.1.8.1.1',
model: '1.3.6.1.2.1.43.11.1.1.6.1.1'
}
}
}
}
]
export class Printer {
constructor(name: string, ip: string, model: PrinterModel) {
this.name = name
this.ip = ip
this.model = model
const objectIds = printerObjectIDConfigs.find(
x => x.model === model
)?.objectIds
if (!objectIds) throw new Error(`No objectIds found for model ${model}`)
this.objectIds = objectIds
this.getPrinterInfo()
}
name: string
ip: string
model: PrinterModel
objectIds: PrinterObjectIDs
private oIDsArray(): string[] {
const oIDsArray: string[] = []
function extractObjValues(obj: any) {
for (let key in obj) {
if (typeof obj[key] === 'object') {
extractObjValues(obj[key])
} else {
const oID = obj[key]
oIDsArray.push(oID)
}
}
}
extractObjValues(this.objectIds)
return oIDsArray
}
private deBufferizeVarbinds(varbinds: Varbind[]) {
const varbindsString: VarbindString[] = []
varbinds.forEach((varbind: Varbind) => {
if (varbind.value instanceof Buffer)
varbindsString.push({ ...varbind, value: varbind.value.toString() })
else varbindsString.push({ ...varbind, value: varbind.value })
})
return varbindsString
}
private calcTonerLevelPercentage(
current: string | undefined,
max: string | undefined
) {
if (typeof current === 'undefined' || typeof max === 'undefined')
throw new Error('current or max is undefined')
return (+current! / +max!) * 100
}
private objectIDsToPrinterInfo(varbinds: Varbind[]) {
const snmpInfo = this.deBufferizeVarbinds(varbinds)
const printerInfo: PrinterInfo = {
name: this.name,
ip: this.ip,
model: snmpInfo.find(x => x.oid === this.objectIds.model)
?.value as string,
serial: snmpInfo.find(x => x.oid === this.objectIds.serial)
?.value as string,
counter: snmpInfo.find(x => x.oid === this.objectIds.counter)
?.value as string,
toners: {
black: {
level: this.calcTonerLevelPercentage(
snmpInfo.find(x => x.oid === this.objectIds.toners.black.current)
?.value,
snmpInfo.find(x => x.oid === this.objectIds.toners.black.max)?.value
),
model: snmpInfo.find(x => x.oid === this.objectIds.toners.black.model)
?.value as string
},
cyan: this.objectIds.toners.cyan
? {
level: this.calcTonerLevelPercentage(
snmpInfo.find(
x => x.oid === this.objectIds.toners.cyan?.current
)?.value,
snmpInfo.find(x => x.oid === this.objectIds.toners.cyan?.max)
?.value
),
model: snmpInfo.find(
x => x.oid === this.objectIds.toners.cyan?.model
)?.value as string
}
: undefined,
magenta: this.objectIds.toners.magenta
? {
level: this.calcTonerLevelPercentage(
snmpInfo.find(
x => x.oid === this.objectIds.toners.magenta?.current
)?.value,
snmpInfo.find(x => x.oid === this.objectIds.toners.magenta?.max)
?.value
),
model: snmpInfo.find(
x => x.oid === this.objectIds.toners.magenta?.model
)?.value as string
}
: undefined,
yellow: this.objectIds.toners.yellow
? {
level: this.calcTonerLevelPercentage(
snmpInfo.find(
x => x.oid === this.objectIds.toners.yellow?.current
)?.value,
snmpInfo.find(x => x.oid === this.objectIds.toners.yellow?.max)
?.value
),
model: snmpInfo.find(
x => x.oid === this.objectIds.toners.yellow?.model
)?.value as string
}
: undefined
}
}
return printerInfo
}
async getPrinterInfo(): Promise<PrinterInfo> {
const session = snmp.createSession(this.ip, 'public')
return new Promise((resolve, reject) => {
session.get(this.oIDsArray(), (error, varbinds) => {
const varbindsString = this.deBufferizeVarbinds(varbinds)
const info = this.objectIDsToPrinterInfo(varbindsString)
resolve(info)
})
})
}
}

View File

@ -1,10 +1,35 @@
import express from 'express'
import { printersDB } from './printers.mjs'
import { Printer } from './Printer.mjs'
import { PrinterInfo } from './types.mjs'
const app = express()
const port = 3000
app.get('/', (req, res) => {
res.send('Hello World!')
const printers: Printer[] = printersDB.map(
printer => new Printer(printer.name, printer.ip, printer.model)
)
app.get('/', async (req, res) => {
const printersInfo: PrinterInfo[] = await Promise.all(
printers.map(printer => printer.getPrinterInfo())
)
res.send(printersInfo)
})
app.get('/:printerName', async (req, res) => {
const printerName = req.params.printerName
const printer = printers.find(printer => printer.name === printerName)
if (!printer) {
res.status(404).send('Printer not found')
return
}
const printerInfo: PrinterInfo = await printer.getPrinterInfo()
res.send(printerInfo)
})
app.listen(port, () => {

29
src/printers.mts Normal file
View File

@ -0,0 +1,29 @@
import { Printer } from './types.mjs'
export const printersDB: Printer[] = [
{
name: 'p04',
ip: '10.7.0.134',
model: 'm3655idn'
},
{
name: 'p05',
ip: '10.7.0.135',
model: 'm2040dn'
},
{
name: 'p06',
ip: '10.7.0.136',
model: 'm2040dn'
},
{
name: 'p07',
ip: '10.7.0.137',
model: 'm2040dn'
},
{
name: 'p08',
ip: '10.7.0.138',
model: 'p6235cdn'
}
]

78
src/types.mts Normal file
View File

@ -0,0 +1,78 @@
export type PrinterModel = 'm3655idn' | 'm2040dn' | 'p6235cdn'
export type PrinterObjectIDs = {
model: string
serial: string
counter: string
toners: {
black: {
current: string
max: string
model: string
}
cyan?: {
current: string
max: string
model: string
}
magenta?: {
current: string
max: string
model: string
}
yellow?: {
current: string
max: string
model: string
}
}
}
export type PrinterObjectIDConfig = {
model: PrinterModel
objectIds: PrinterObjectIDs
}
export type PrinterInfo = {
name: string
ip: string
model: string
serial: string
counter: string
toners: {
black: {
level: number
model: string
}
cyan?: {
level: number
model: string
}
magenta?: {
level: number
model: string
}
yellow?: {
level: number
model: string
}
}
}
export type Printer = {
name: string
ip: string
model: PrinterModel
}
export type Varbind = {
oid: string
type: number
value: string | Buffer
}
export type VarbindString = {
oid: string
type: number
value: string
}

View File

@ -35,7 +35,7 @@
// "types": [], /* Specify type package names to be included without being referenced in a source file. */
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
// "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */
// "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */
// "allowImportingTsExtensions": true /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */,
// "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */
// "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */
// "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */
@ -83,7 +83,7 @@
/* Type Checking */
"strict": true /* Enable all strict type-checking options. */,
"noImplicitAny": true /* Enable error reporting for expressions and declarations with an implied 'any' type. */,
"noImplicitAny": false /* Enable error reporting for expressions and declarations with an implied 'any' type. */,
// "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
// "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
// "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
@ -91,7 +91,7 @@
// "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */
// "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
// "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
// "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
"noUnusedLocals": true /* Enable error reporting when local variables aren't read. */,
// "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
// "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
// "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */