import * as plugins from '../plugins.js'; import { Device } from '../abstract/device.abstract.js'; import { SnmpProtocol, SNMP_OIDS, type ISnmpOptions, type ISnmpVarbind } from './snmp.classes.snmpprotocol.js'; import type { IDeviceInfo, IRetryOptions, TDeviceStatus } from '../interfaces/index.js'; /** * SNMP device information */ export interface ISnmpDeviceInfo extends IDeviceInfo { type: 'snmp'; sysDescr: string; sysObjectID: string; sysUpTime: number; sysContact?: string; sysName?: string; sysLocation?: string; } /** * SNMP Device class for generic SNMP-enabled devices */ export class SnmpDevice extends Device { private protocol: SnmpProtocol | null = null; private snmpOptions: ISnmpOptions; private _sysDescr: string = ''; private _sysObjectID: string = ''; private _sysUpTime: number = 0; private _sysContact?: string; private _sysName?: string; private _sysLocation?: string; constructor( info: IDeviceInfo, snmpOptions?: ISnmpOptions, retryOptions?: IRetryOptions ) { super(info, retryOptions); this.snmpOptions = { port: info.port, ...snmpOptions }; } // Getters for SNMP properties public get sysDescr(): string { return this._sysDescr; } public get sysObjectID(): string { return this._sysObjectID; } public get sysUpTime(): number { return this._sysUpTime; } public get sysContact(): string | undefined { return this._sysContact; } public get sysName(): string | undefined { return this._sysName; } public get sysLocation(): string | undefined { return this._sysLocation; } /** * Connect to the SNMP device */ protected async doConnect(): Promise { this.protocol = new SnmpProtocol(this.address, this.snmpOptions); // Verify connection by fetching system info const sysInfo = await this.protocol.getSystemInfo(); this._sysDescr = sysInfo.sysDescr; this._sysObjectID = sysInfo.sysObjectID; this._sysUpTime = sysInfo.sysUpTime; this._sysContact = sysInfo.sysContact || undefined; this._sysName = sysInfo.sysName || undefined; this._sysLocation = sysInfo.sysLocation || undefined; // Update device name if sysName is available if (sysInfo.sysName && !this.name.includes('SNMP Device')) { // Keep custom name } else if (sysInfo.sysName) { (this as { name: string }).name = sysInfo.sysName; } } /** * Disconnect from the SNMP device */ protected async doDisconnect(): Promise { if (this.protocol) { this.protocol.close(); this.protocol = null; } } /** * Refresh device status */ public async refreshStatus(): Promise { if (!this.protocol) { throw new Error('Not connected'); } const sysInfo = await this.protocol.getSystemInfo(); this._sysUpTime = sysInfo.sysUpTime; this.emit('status:updated', this.getDeviceInfo()); } /** * Get a single OID value */ public async get(oid: string): Promise { if (!this.protocol) { throw new Error('Not connected'); } return this.protocol.get(oid); } /** * Get multiple OID values */ public async getMultiple(oids: string[]): Promise { if (!this.protocol) { throw new Error('Not connected'); } return this.protocol.getMultiple(oids); } /** * Get next OID in the MIB tree */ public async getNext(oid: string): Promise { if (!this.protocol) { throw new Error('Not connected'); } return this.protocol.getNext(oid); } /** * GETBULK operation for efficient table retrieval */ public async getBulk( oids: string[], nonRepeaters?: number, maxRepetitions?: number ): Promise { if (!this.protocol) { throw new Error('Not connected'); } return this.protocol.getBulk(oids, nonRepeaters, maxRepetitions); } /** * Walk a MIB tree */ public async walk(baseOid: string): Promise { if (!this.protocol) { throw new Error('Not connected'); } return this.protocol.walk(baseOid); } /** * Set an OID value */ public async set( oid: string, type: 'Integer' | 'OctetString' | 'ObjectIdentifier' | 'IpAddress', value: unknown ): Promise { if (!this.protocol) { throw new Error('Not connected'); } return this.protocol.set(oid, type, value); } /** * Get device information */ public getDeviceInfo(): ISnmpDeviceInfo { return { id: this.id, name: this.name, type: 'snmp', address: this.address, port: this.port, status: this.status, sysDescr: this._sysDescr, sysObjectID: this._sysObjectID, sysUpTime: this._sysUpTime, sysContact: this._sysContact, sysName: this._sysName, sysLocation: this._sysLocation, }; } /** * Create SnmpDevice from discovery data */ public static fromDiscovery( data: { id: string; name: string; address: string; port?: number; community?: string; }, retryOptions?: IRetryOptions ): SnmpDevice { const info: IDeviceInfo = { id: data.id, name: data.name, type: 'snmp', address: data.address, port: data.port ?? 161, status: 'unknown', }; return new SnmpDevice( info, { community: data.community ?? 'public' }, retryOptions ); } /** * Probe an IP address for SNMP device */ public static async probe( address: string, port: number = 161, community: string = 'public', timeout: number = 5000 ): Promise { const protocol = new SnmpProtocol(address, { community, port, timeout, retries: 0, }); try { const sysInfo = await protocol.getSystemInfo(); return { id: `snmp:${address}:${port}`, name: sysInfo.sysName || `SNMP Device at ${address}`, type: 'snmp', address, port, status: 'online', sysDescr: sysInfo.sysDescr, sysObjectID: sysInfo.sysObjectID, sysUpTime: sysInfo.sysUpTime, sysContact: sysInfo.sysContact || undefined, sysName: sysInfo.sysName || undefined, sysLocation: sysInfo.sysLocation || undefined, }; } catch { return null; } finally { protocol.close(); } } } export { SNMP_OIDS };