This commit is contained in:
2026-01-09 07:14:39 +00:00
parent 95da37590c
commit 05e1f94c79
22 changed files with 6549 additions and 10 deletions

View File

@@ -0,0 +1,471 @@
import * as plugins from '../plugins.js';
/**
* NUT Protocol variable definitions
*/
export const NUT_VARIABLES = {
// Device info
deviceMfr: 'device.mfr',
deviceModel: 'device.model',
deviceSerial: 'device.serial',
deviceType: 'device.type',
// UPS status
upsStatus: 'ups.status',
upsAlarm: 'ups.alarm',
upsTime: 'ups.time',
upsLoad: 'ups.load',
upsTemperature: 'ups.temperature',
// Battery
batteryCharge: 'battery.charge',
batteryRuntime: 'battery.runtime',
batteryVoltage: 'battery.voltage',
batteryVoltageNominal: 'battery.voltage.nominal',
batteryType: 'battery.type',
batteryDate: 'battery.date',
batteryTemperature: 'battery.temperature',
// Input
inputVoltage: 'input.voltage',
inputVoltageNominal: 'input.voltage.nominal',
inputFrequency: 'input.frequency',
inputFrequencyNominal: 'input.frequency.nominal',
inputTransferHigh: 'input.transfer.high',
inputTransferLow: 'input.transfer.low',
// Output
outputVoltage: 'output.voltage',
outputVoltageNominal: 'output.voltage.nominal',
outputFrequency: 'output.frequency',
outputCurrent: 'output.current',
};
/**
* NUT instant commands
*/
export const NUT_COMMANDS = {
testBatteryStart: 'test.battery.start',
testBatteryStartQuick: 'test.battery.start.quick',
testBatteryStartDeep: 'test.battery.start.deep',
testBatteryStop: 'test.battery.stop',
calibrateStart: 'calibrate.start',
calibrateStop: 'calibrate.stop',
shutdown: 'shutdown.return',
shutdownStayOff: 'shutdown.stayoff',
shutdownStop: 'shutdown.stop',
shutdownReboot: 'shutdown.reboot',
beeperEnable: 'beeper.enable',
beeperDisable: 'beeper.disable',
beeperMute: 'beeper.mute',
beeperToggle: 'beeper.toggle',
loadOff: 'load.off',
loadOn: 'load.on',
};
/**
* UPS status flags from NUT
*/
export type TNutStatusFlag =
| 'OL' // Online (on utility power)
| 'OB' // On battery
| 'LB' // Low battery
| 'HB' // High battery
| 'RB' // Replace battery
| 'CHRG' // Charging
| 'DISCHRG' // Discharging
| 'BYPASS' // On bypass
| 'CAL' // Calibrating
| 'OFF' // Offline
| 'OVER' // Overloaded
| 'TRIM' // Trimming voltage
| 'BOOST' // Boosting voltage
| 'FSD'; // Forced shutdown
/**
* NUT UPS information
*/
export interface INutUpsInfo {
name: string;
description: string;
}
/**
* NUT variable
*/
export interface INutVariable {
name: string;
value: string;
}
/**
* NUT Protocol handler for Network UPS Tools
* TCP-based text protocol on port 3493
*/
export class NutProtocol {
private socket: plugins.net.Socket | null = null;
private address: string;
private port: number;
private connected: boolean = false;
private responseBuffer: string = '';
private responseResolver: ((value: string[]) => void) | null = null;
private responseRejecter: ((error: Error) => void) | null = null;
constructor(address: string, port: number = 3493) {
this.address = address;
this.port = port;
}
/**
* Connect to NUT server
*/
public async connect(): Promise<void> {
if (this.connected) {
return;
}
return new Promise((resolve, reject) => {
this.socket = new plugins.net.Socket();
const timeout = setTimeout(() => {
if (this.socket) {
this.socket.destroy();
this.socket = null;
}
reject(new Error(`Connection timeout to ${this.address}:${this.port}`));
}, 5000);
this.socket.on('connect', () => {
clearTimeout(timeout);
this.connected = true;
resolve();
});
this.socket.on('error', (err) => {
clearTimeout(timeout);
this.connected = false;
if (this.responseRejecter) {
this.responseRejecter(err);
this.responseRejecter = null;
this.responseResolver = null;
}
reject(err);
});
this.socket.on('data', (data) => {
this.handleData(data);
});
this.socket.on('close', () => {
this.connected = false;
if (this.responseRejecter) {
this.responseRejecter(new Error('Connection closed'));
this.responseRejecter = null;
this.responseResolver = null;
}
});
this.socket.connect(this.port, this.address);
});
}
/**
* Disconnect from NUT server
*/
public async disconnect(): Promise<void> {
if (!this.connected || !this.socket) {
return;
}
try {
await this.sendCommand('LOGOUT');
} catch {
// Ignore logout errors
}
this.socket.destroy();
this.socket = null;
this.connected = false;
}
/**
* Check if connected
*/
public get isConnected(): boolean {
return this.connected;
}
/**
* Handle incoming data
*/
private handleData(data: Buffer): void {
this.responseBuffer += data.toString();
// Check for complete response (ends with newline)
const lines = this.responseBuffer.split('\n');
// Check if we have a complete response
if (this.responseBuffer.endsWith('\n')) {
const responseLines = lines.filter((l) => l.trim().length > 0);
this.responseBuffer = '';
if (this.responseResolver) {
this.responseResolver(responseLines);
this.responseResolver = null;
this.responseRejecter = null;
}
}
}
/**
* Send command and get response
*/
private async sendCommand(command: string): Promise<string[]> {
if (!this.socket || !this.connected) {
throw new Error('Not connected to NUT server');
}
return new Promise((resolve, reject) => {
this.responseResolver = resolve;
this.responseRejecter = reject;
const timeout = setTimeout(() => {
this.responseResolver = null;
this.responseRejecter = null;
reject(new Error(`Command timeout: ${command}`));
}, 10000);
this.responseResolver = (lines) => {
clearTimeout(timeout);
resolve(lines);
};
this.responseRejecter = (err) => {
clearTimeout(timeout);
reject(err);
};
this.socket!.write(`${command}\n`);
});
}
/**
* List available UPS devices
*/
public async listUps(): Promise<INutUpsInfo[]> {
await this.ensureConnected();
const response = await this.sendCommand('LIST UPS');
const upsList: INutUpsInfo[] = [];
for (const line of response) {
// Format: UPS <name> "<description>"
const match = line.match(/^UPS\s+(\S+)\s+"([^"]*)"/);
if (match) {
upsList.push({
name: match[1],
description: match[2],
});
}
}
return upsList;
}
/**
* Get all variables for a UPS
*/
public async listVariables(upsName: string): Promise<INutVariable[]> {
await this.ensureConnected();
const response = await this.sendCommand(`LIST VAR ${upsName}`);
const variables: INutVariable[] = [];
for (const line of response) {
// Format: VAR <ups> <name> "<value>"
const match = line.match(/^VAR\s+\S+\s+(\S+)\s+"([^"]*)"/);
if (match) {
variables.push({
name: match[1],
value: match[2],
});
}
}
return variables;
}
/**
* Get a specific variable value
*/
public async getVariable(upsName: string, varName: string): Promise<string | null> {
await this.ensureConnected();
const response = await this.sendCommand(`GET VAR ${upsName} ${varName}`);
for (const line of response) {
// Format: VAR <ups> <name> "<value>"
const match = line.match(/^VAR\s+\S+\s+\S+\s+"([^"]*)"/);
if (match) {
return match[1];
}
// Handle error responses
if (line.startsWith('ERR')) {
return null;
}
}
return null;
}
/**
* Get multiple variables at once
*/
public async getVariables(upsName: string, varNames: string[]): Promise<Map<string, string>> {
const results = new Map<string, string>();
for (const varName of varNames) {
const value = await this.getVariable(upsName, varName);
if (value !== null) {
results.set(varName, value);
}
}
return results;
}
/**
* Execute an instant command
*/
public async runCommand(upsName: string, command: string): Promise<boolean> {
await this.ensureConnected();
const response = await this.sendCommand(`INSTCMD ${upsName} ${command}`);
for (const line of response) {
if (line === 'OK') {
return true;
}
if (line.startsWith('ERR')) {
return false;
}
}
return false;
}
/**
* List available commands for a UPS
*/
public async listCommands(upsName: string): Promise<string[]> {
await this.ensureConnected();
const response = await this.sendCommand(`LIST CMD ${upsName}`);
const commands: string[] = [];
for (const line of response) {
// Format: CMD <ups> <command>
const match = line.match(/^CMD\s+\S+\s+(\S+)/);
if (match) {
commands.push(match[1]);
}
}
return commands;
}
/**
* Parse UPS status string into flags
*/
public parseStatus(statusString: string): TNutStatusFlag[] {
return statusString.split(/\s+/).filter((s) => s.length > 0) as TNutStatusFlag[];
}
/**
* Get comprehensive UPS status
*/
public async getUpsStatus(upsName: string): Promise<{
status: TNutStatusFlag[];
batteryCharge: number;
batteryRuntime: number;
inputVoltage: number;
outputVoltage: number;
load: number;
}> {
const vars = await this.getVariables(upsName, [
NUT_VARIABLES.upsStatus,
NUT_VARIABLES.batteryCharge,
NUT_VARIABLES.batteryRuntime,
NUT_VARIABLES.inputVoltage,
NUT_VARIABLES.outputVoltage,
NUT_VARIABLES.upsLoad,
]);
return {
status: this.parseStatus(vars.get(NUT_VARIABLES.upsStatus) || ''),
batteryCharge: parseFloat(vars.get(NUT_VARIABLES.batteryCharge) || '0'),
batteryRuntime: parseFloat(vars.get(NUT_VARIABLES.batteryRuntime) || '0'),
inputVoltage: parseFloat(vars.get(NUT_VARIABLES.inputVoltage) || '0'),
outputVoltage: parseFloat(vars.get(NUT_VARIABLES.outputVoltage) || '0'),
load: parseFloat(vars.get(NUT_VARIABLES.upsLoad) || '0'),
};
}
/**
* Get device information
*/
public async getDeviceInfo(upsName: string): Promise<{
manufacturer: string;
model: string;
serial: string;
type: string;
}> {
const vars = await this.getVariables(upsName, [
NUT_VARIABLES.deviceMfr,
NUT_VARIABLES.deviceModel,
NUT_VARIABLES.deviceSerial,
NUT_VARIABLES.deviceType,
]);
return {
manufacturer: vars.get(NUT_VARIABLES.deviceMfr) || '',
model: vars.get(NUT_VARIABLES.deviceModel) || '',
serial: vars.get(NUT_VARIABLES.deviceSerial) || '',
type: vars.get(NUT_VARIABLES.deviceType) || '',
};
}
/**
* Ensure connected before command
*/
private async ensureConnected(): Promise<void> {
if (!this.connected) {
await this.connect();
}
}
/**
* Check if a NUT server is reachable
*/
public static async probe(address: string, port: number = 3493, timeout: number = 3000): Promise<boolean> {
return new Promise((resolve) => {
const socket = new plugins.net.Socket();
const timer = setTimeout(() => {
socket.destroy();
resolve(false);
}, timeout);
socket.on('connect', () => {
clearTimeout(timer);
socket.destroy();
resolve(true);
});
socket.on('error', () => {
clearTimeout(timer);
resolve(false);
});
socket.connect(port, address);
});
}
}

View File

@@ -0,0 +1,377 @@
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<number, TUpsBatteryStatus> = {
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<number, TUpsOutputSource> = {
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<IUpsSnmpStatus> {
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<boolean> {
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),
};
}
}