import { SnmpProtocol, SNMP_OIDS, type ISnmpOptions } from '../snmp/snmp.classes.snmpprotocol.js'; /** * Extended UPS-MIB OIDs (RFC 1628) */ export const UPS_SNMP_OIDS = { // Identity upsIdentManufacturer: '1.3.6.1.2.1.33.1.1.1.0', upsIdentModel: '1.3.6.1.2.1.33.1.1.2.0', upsIdentUPSSoftwareVersion: '1.3.6.1.2.1.33.1.1.3.0', upsIdentAgentSoftwareVersion: '1.3.6.1.2.1.33.1.1.4.0', upsIdentName: '1.3.6.1.2.1.33.1.1.5.0', upsIdentAttachedDevices: '1.3.6.1.2.1.33.1.1.6.0', // Battery group upsBatteryStatus: '1.3.6.1.2.1.33.1.2.1.0', upsSecondsOnBattery: '1.3.6.1.2.1.33.1.2.2.0', upsEstimatedMinutesRemaining: '1.3.6.1.2.1.33.1.2.3.0', upsEstimatedChargeRemaining: '1.3.6.1.2.1.33.1.2.4.0', upsBatteryVoltage: '1.3.6.1.2.1.33.1.2.5.0', upsBatteryCurrent: '1.3.6.1.2.1.33.1.2.6.0', upsBatteryTemperature: '1.3.6.1.2.1.33.1.2.7.0', // Input group upsInputLineBads: '1.3.6.1.2.1.33.1.3.1.0', upsInputNumLines: '1.3.6.1.2.1.33.1.3.2.0', upsInputLineIndex: '1.3.6.1.2.1.33.1.3.3.1.1', upsInputFrequency: '1.3.6.1.2.1.33.1.3.3.1.2', upsInputVoltage: '1.3.6.1.2.1.33.1.3.3.1.3', upsInputCurrent: '1.3.6.1.2.1.33.1.3.3.1.4', upsInputTruePower: '1.3.6.1.2.1.33.1.3.3.1.5', // Output group upsOutputSource: '1.3.6.1.2.1.33.1.4.1.0', upsOutputFrequency: '1.3.6.1.2.1.33.1.4.2.0', upsOutputNumLines: '1.3.6.1.2.1.33.1.4.3.0', upsOutputLineIndex: '1.3.6.1.2.1.33.1.4.4.1.1', upsOutputVoltage: '1.3.6.1.2.1.33.1.4.4.1.2', upsOutputCurrent: '1.3.6.1.2.1.33.1.4.4.1.3', upsOutputPower: '1.3.6.1.2.1.33.1.4.4.1.4', upsOutputPercentLoad: '1.3.6.1.2.1.33.1.4.4.1.5', // Bypass group upsBypassFrequency: '1.3.6.1.2.1.33.1.5.1.0', upsBypassNumLines: '1.3.6.1.2.1.33.1.5.2.0', // Alarm group upsAlarmsPresent: '1.3.6.1.2.1.33.1.6.1.0', // Test group upsTestId: '1.3.6.1.2.1.33.1.7.1.0', upsTestSpinLock: '1.3.6.1.2.1.33.1.7.2.0', upsTestResultsSummary: '1.3.6.1.2.1.33.1.7.3.0', upsTestResultsDetail: '1.3.6.1.2.1.33.1.7.4.0', upsTestStartTime: '1.3.6.1.2.1.33.1.7.5.0', upsTestElapsedTime: '1.3.6.1.2.1.33.1.7.6.0', // Control group upsShutdownType: '1.3.6.1.2.1.33.1.8.1.0', upsShutdownAfterDelay: '1.3.6.1.2.1.33.1.8.2.0', upsStartupAfterDelay: '1.3.6.1.2.1.33.1.8.3.0', upsRebootWithDuration: '1.3.6.1.2.1.33.1.8.4.0', upsAutoRestart: '1.3.6.1.2.1.33.1.8.5.0', // Config group upsConfigInputVoltage: '1.3.6.1.2.1.33.1.9.1.0', upsConfigInputFreq: '1.3.6.1.2.1.33.1.9.2.0', upsConfigOutputVoltage: '1.3.6.1.2.1.33.1.9.3.0', upsConfigOutputFreq: '1.3.6.1.2.1.33.1.9.4.0', upsConfigOutputVA: '1.3.6.1.2.1.33.1.9.5.0', upsConfigOutputPower: '1.3.6.1.2.1.33.1.9.6.0', upsConfigLowBattTime: '1.3.6.1.2.1.33.1.9.7.0', upsConfigAudibleStatus: '1.3.6.1.2.1.33.1.9.8.0', upsConfigLowVoltageTransferPoint: '1.3.6.1.2.1.33.1.9.9.0', upsConfigHighVoltageTransferPoint: '1.3.6.1.2.1.33.1.9.10.0', }; /** * Battery status values from UPS-MIB */ export type TUpsBatteryStatus = | 'unknown' | 'batteryNormal' | 'batteryLow' | 'batteryDepleted'; /** * Output source values from UPS-MIB */ export type TUpsOutputSource = | 'other' | 'none' | 'normal' | 'bypass' | 'battery' | 'booster' | 'reducer'; /** * Test results summary from UPS-MIB */ export type TUpsTestResult = | 'donePass' | 'doneWarning' | 'doneError' | 'aborted' | 'inProgress' | 'noTestsInitiated'; /** * SNMP-based UPS status interface */ export interface IUpsSnmpStatus { batteryStatus: TUpsBatteryStatus; secondsOnBattery: number; estimatedMinutesRemaining: number; estimatedChargeRemaining: number; batteryVoltage: number; batteryTemperature: number; outputSource: TUpsOutputSource; outputFrequency: number; outputVoltage: number; outputCurrent: number; outputPower: number; outputPercentLoad: number; inputFrequency: number; inputVoltage: number; alarmsPresent: number; } /** * UPS SNMP handler for querying UPS devices via SNMP */ export class UpsSnmpHandler { private protocol: SnmpProtocol; constructor(address: string, options?: ISnmpOptions) { this.protocol = new SnmpProtocol(address, options); } /** * Close SNMP session */ public close(): void { this.protocol.close(); } /** * Get UPS identity information */ public async getIdentity(): Promise<{ manufacturer: string; model: string; softwareVersion: string; name: string; }> { const varbinds = await this.protocol.getMultiple([ UPS_SNMP_OIDS.upsIdentManufacturer, UPS_SNMP_OIDS.upsIdentModel, UPS_SNMP_OIDS.upsIdentUPSSoftwareVersion, UPS_SNMP_OIDS.upsIdentName, ]); const getValue = (oid: string): string => { const vb = varbinds.find((v) => v.oid === oid); return String(vb?.value || ''); }; return { manufacturer: getValue(UPS_SNMP_OIDS.upsIdentManufacturer), model: getValue(UPS_SNMP_OIDS.upsIdentModel), softwareVersion: getValue(UPS_SNMP_OIDS.upsIdentUPSSoftwareVersion), name: getValue(UPS_SNMP_OIDS.upsIdentName), }; } /** * Get battery status */ public async getBatteryStatus(): Promise<{ status: TUpsBatteryStatus; secondsOnBattery: number; estimatedMinutesRemaining: number; estimatedChargeRemaining: number; voltage: number; temperature: number; }> { const varbinds = await this.protocol.getMultiple([ UPS_SNMP_OIDS.upsBatteryStatus, UPS_SNMP_OIDS.upsSecondsOnBattery, UPS_SNMP_OIDS.upsEstimatedMinutesRemaining, UPS_SNMP_OIDS.upsEstimatedChargeRemaining, UPS_SNMP_OIDS.upsBatteryVoltage, UPS_SNMP_OIDS.upsBatteryTemperature, ]); const getValue = (oid: string): number => { const vb = varbinds.find((v) => v.oid === oid); return Number(vb?.value || 0); }; const statusMap: Record = { 1: 'unknown', 2: 'batteryNormal', 3: 'batteryLow', 4: 'batteryDepleted', }; return { status: statusMap[getValue(UPS_SNMP_OIDS.upsBatteryStatus)] || 'unknown', secondsOnBattery: getValue(UPS_SNMP_OIDS.upsSecondsOnBattery), estimatedMinutesRemaining: getValue(UPS_SNMP_OIDS.upsEstimatedMinutesRemaining), estimatedChargeRemaining: getValue(UPS_SNMP_OIDS.upsEstimatedChargeRemaining), voltage: getValue(UPS_SNMP_OIDS.upsBatteryVoltage) / 10, // Typically in 0.1V units temperature: getValue(UPS_SNMP_OIDS.upsBatteryTemperature), }; } /** * Get input status */ public async getInputStatus(): Promise<{ frequency: number; voltage: number; lineBads: number; }> { const varbinds = await this.protocol.getMultiple([ UPS_SNMP_OIDS.upsInputFrequency + '.1', // Line 1 UPS_SNMP_OIDS.upsInputVoltage + '.1', // Line 1 UPS_SNMP_OIDS.upsInputLineBads, ]); const getValue = (oid: string): number => { const vb = varbinds.find((v) => v.oid === oid); return Number(vb?.value || 0); }; return { frequency: getValue(UPS_SNMP_OIDS.upsInputFrequency + '.1') / 10, // 0.1 Hz units voltage: getValue(UPS_SNMP_OIDS.upsInputVoltage + '.1'), lineBads: getValue(UPS_SNMP_OIDS.upsInputLineBads), }; } /** * Get output status */ public async getOutputStatus(): Promise<{ source: TUpsOutputSource; frequency: number; voltage: number; current: number; power: number; percentLoad: number; }> { const varbinds = await this.protocol.getMultiple([ UPS_SNMP_OIDS.upsOutputSource, UPS_SNMP_OIDS.upsOutputFrequency, UPS_SNMP_OIDS.upsOutputVoltage + '.1', // Line 1 UPS_SNMP_OIDS.upsOutputCurrent + '.1', // Line 1 UPS_SNMP_OIDS.upsOutputPower + '.1', // Line 1 UPS_SNMP_OIDS.upsOutputPercentLoad + '.1', // Line 1 ]); const getValue = (oid: string): number => { const vb = varbinds.find((v) => v.oid === oid); return Number(vb?.value || 0); }; const sourceMap: Record = { 1: 'other', 2: 'none', 3: 'normal', 4: 'bypass', 5: 'battery', 6: 'booster', 7: 'reducer', }; return { source: sourceMap[getValue(UPS_SNMP_OIDS.upsOutputSource)] || 'other', frequency: getValue(UPS_SNMP_OIDS.upsOutputFrequency) / 10, // 0.1 Hz units voltage: getValue(UPS_SNMP_OIDS.upsOutputVoltage + '.1'), current: getValue(UPS_SNMP_OIDS.upsOutputCurrent + '.1') / 10, // 0.1 A units power: getValue(UPS_SNMP_OIDS.upsOutputPower + '.1'), percentLoad: getValue(UPS_SNMP_OIDS.upsOutputPercentLoad + '.1'), }; } /** * Get full UPS status */ public async getFullStatus(): Promise { const [battery, input, output] = await Promise.all([ this.getBatteryStatus(), this.getInputStatus(), this.getOutputStatus(), ]); // Get alarms separately let alarmsPresent = 0; try { const alarmVb = await this.protocol.get(UPS_SNMP_OIDS.upsAlarmsPresent); alarmsPresent = Number(alarmVb.value || 0); } catch { // Ignore alarm fetch errors } return { batteryStatus: battery.status, secondsOnBattery: battery.secondsOnBattery, estimatedMinutesRemaining: battery.estimatedMinutesRemaining, estimatedChargeRemaining: battery.estimatedChargeRemaining, batteryVoltage: battery.voltage, batteryTemperature: battery.temperature, outputSource: output.source, outputFrequency: output.frequency, outputVoltage: output.voltage, outputCurrent: output.current, outputPower: output.power, outputPercentLoad: output.percentLoad, inputFrequency: input.frequency, inputVoltage: input.voltage, alarmsPresent, }; } /** * Check if UPS-MIB is supported on device */ public async isUpsDevice(): Promise { try { const vb = await this.protocol.get(UPS_SNMP_OIDS.upsBatteryStatus); return vb.value !== null && vb.value !== undefined; } catch { return false; } } /** * Get configuration info */ public async getConfiguration(): Promise<{ inputVoltage: number; inputFrequency: number; outputVoltage: number; outputFrequency: number; outputVA: number; outputPower: number; lowBatteryTime: number; }> { const varbinds = await this.protocol.getMultiple([ UPS_SNMP_OIDS.upsConfigInputVoltage, UPS_SNMP_OIDS.upsConfigInputFreq, UPS_SNMP_OIDS.upsConfigOutputVoltage, UPS_SNMP_OIDS.upsConfigOutputFreq, UPS_SNMP_OIDS.upsConfigOutputVA, UPS_SNMP_OIDS.upsConfigOutputPower, UPS_SNMP_OIDS.upsConfigLowBattTime, ]); const getValue = (oid: string): number => { const vb = varbinds.find((v) => v.oid === oid); return Number(vb?.value || 0); }; return { inputVoltage: getValue(UPS_SNMP_OIDS.upsConfigInputVoltage), inputFrequency: getValue(UPS_SNMP_OIDS.upsConfigInputFreq) / 10, outputVoltage: getValue(UPS_SNMP_OIDS.upsConfigOutputVoltage), outputFrequency: getValue(UPS_SNMP_OIDS.upsConfigOutputFreq) / 10, outputVA: getValue(UPS_SNMP_OIDS.upsConfigOutputVA), outputPower: getValue(UPS_SNMP_OIDS.upsConfigOutputPower), lowBatteryTime: getValue(UPS_SNMP_OIDS.upsConfigLowBattTime), }; } }