Compare commits
6 Commits
Author | SHA1 | Date | |
---|---|---|---|
7d595fa175 | |||
df417432b0 | |||
e5f1ebf343 | |||
3ff0dd7ac8 | |||
bb87316dd3 | |||
d6e0a1a274 |
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@serve.zone/nupst",
|
"name": "@serve.zone/nupst",
|
||||||
"version": "4.0.9",
|
"version": "4.1.3",
|
||||||
"exports": "./mod.ts",
|
"exports": "./mod.ts",
|
||||||
"tasks": {
|
"tasks": {
|
||||||
"dev": "deno run --allow-all mod.ts",
|
"dev": "deno run --allow-all mod.ts",
|
||||||
|
@@ -6,5 +6,5 @@ import denoConfig from '../deno.json' with { type: 'json' };
|
|||||||
export const commitinfo = {
|
export const commitinfo = {
|
||||||
name: denoConfig.name,
|
name: denoConfig.name,
|
||||||
version: denoConfig.version,
|
version: denoConfig.version,
|
||||||
description: 'Deno-powered UPS monitoring tool for SNMP-enabled UPS devices',
|
description: 'Network UPS Shutdown Tool (https://nupst.serve.zone)',
|
||||||
};
|
};
|
||||||
|
@@ -469,7 +469,7 @@ export class NupstCli {
|
|||||||
private showVersion(): void {
|
private showVersion(): void {
|
||||||
const version = this.nupst.getVersion();
|
const version = this.nupst.getVersion();
|
||||||
logger.log(`NUPST version ${version}`);
|
logger.log(`NUPST version ${version}`);
|
||||||
logger.log('Deno-powered UPS monitoring tool');
|
logger.log('Network UPS Shutdown Tool (https://nupst.serve.zone)');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -192,6 +192,7 @@ export class GroupHandler {
|
|||||||
logger.log('\nGroup setup complete!');
|
logger.log('\nGroup setup complete!');
|
||||||
} finally {
|
} finally {
|
||||||
rl.close();
|
rl.close();
|
||||||
|
process.stdin.destroy();
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(`Add group error: ${error instanceof Error ? error.message : String(error)}`);
|
logger.error(`Add group error: ${error instanceof Error ? error.message : String(error)}`);
|
||||||
@@ -309,6 +310,7 @@ export class GroupHandler {
|
|||||||
logger.log('\nGroup edit complete!');
|
logger.log('\nGroup edit complete!');
|
||||||
} finally {
|
} finally {
|
||||||
rl.close();
|
rl.close();
|
||||||
|
process.stdin.destroy();
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(`Edit group error: ${error instanceof Error ? error.message : String(error)}`);
|
logger.error(`Edit group error: ${error instanceof Error ? error.message : String(error)}`);
|
||||||
@@ -366,6 +368,7 @@ export class GroupHandler {
|
|||||||
});
|
});
|
||||||
|
|
||||||
rl.close();
|
rl.close();
|
||||||
|
process.stdin.destroy();
|
||||||
|
|
||||||
if (confirm !== 'y' && confirm !== 'yes') {
|
if (confirm !== 'y' && confirm !== 'yes') {
|
||||||
logger.log('Deletion cancelled.');
|
logger.log('Deletion cancelled.');
|
||||||
|
@@ -213,9 +213,11 @@ export class ServiceHandler {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
console.log('\nNUPST Uninstaller');
|
logger.log('');
|
||||||
console.log('===============');
|
logger.highlight('NUPST Uninstaller');
|
||||||
console.log('This will completely remove NUPST from your system.\n');
|
logger.dim('===============');
|
||||||
|
logger.log('This will completely remove NUPST from your system.');
|
||||||
|
logger.log('');
|
||||||
|
|
||||||
// Ask about removing configuration
|
// Ask about removing configuration
|
||||||
const removeConfig = await prompt(
|
const removeConfig = await prompt(
|
||||||
@@ -251,17 +253,20 @@ export class ServiceHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!uninstallScriptPath) {
|
if (!uninstallScriptPath) {
|
||||||
console.error('Could not locate uninstall.sh script. Aborting uninstall.');
|
logger.error('Could not locate uninstall.sh script. Aborting uninstall.');
|
||||||
rl.close();
|
rl.close();
|
||||||
|
process.stdin.destroy();
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close readline before executing script
|
// Close readline before executing script
|
||||||
rl.close();
|
rl.close();
|
||||||
|
process.stdin.destroy();
|
||||||
|
|
||||||
// Execute uninstall.sh with the appropriate option
|
// Execute uninstall.sh with the appropriate option
|
||||||
console.log(`\nRunning uninstaller from ${uninstallScriptPath}...`);
|
logger.log('');
|
||||||
|
logger.log(`Running uninstaller from ${uninstallScriptPath}...`);
|
||||||
|
|
||||||
// Pass the configuration removal option as an environment variable
|
// Pass the configuration removal option as an environment variable
|
||||||
const env = {
|
const env = {
|
||||||
@@ -277,7 +282,7 @@ export class ServiceHandler {
|
|||||||
stdio: 'inherit', // Show output in the terminal
|
stdio: 'inherit', // Show output in the terminal
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`Uninstall failed: ${error instanceof Error ? error.message : String(error)}`);
|
logger.error(`Uninstall failed: ${error instanceof Error ? error.message : String(error)}`);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -47,6 +47,7 @@ export class UpsHandler {
|
|||||||
await this.runAddProcess(prompt);
|
await this.runAddProcess(prompt);
|
||||||
} finally {
|
} finally {
|
||||||
rl.close();
|
rl.close();
|
||||||
|
process.stdin.destroy();
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(`Add UPS error: ${error instanceof Error ? error.message : String(error)}`);
|
logger.error(`Add UPS error: ${error instanceof Error ? error.message : String(error)}`);
|
||||||
@@ -178,6 +179,7 @@ export class UpsHandler {
|
|||||||
await this.runEditProcess(upsId, prompt);
|
await this.runEditProcess(upsId, prompt);
|
||||||
} finally {
|
} finally {
|
||||||
rl.close();
|
rl.close();
|
||||||
|
process.stdin.destroy();
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(`Edit UPS error: ${error instanceof Error ? error.message : String(error)}`);
|
logger.error(`Edit UPS error: ${error instanceof Error ? error.message : String(error)}`);
|
||||||
@@ -344,6 +346,7 @@ export class UpsHandler {
|
|||||||
});
|
});
|
||||||
|
|
||||||
rl.close();
|
rl.close();
|
||||||
|
process.stdin.destroy();
|
||||||
|
|
||||||
if (confirm !== 'y' && confirm !== 'yes') {
|
if (confirm !== 'y' && confirm !== 'yes') {
|
||||||
logger.log('Deletion cancelled.');
|
logger.log('Deletion cancelled.');
|
||||||
@@ -667,10 +670,11 @@ export class UpsHandler {
|
|||||||
|
|
||||||
// SNMP Version
|
// SNMP Version
|
||||||
const defaultVersion = snmpConfig.version || 1;
|
const defaultVersion = snmpConfig.version || 1;
|
||||||
console.log('\nSNMP Version:');
|
logger.log('');
|
||||||
console.log(' 1) SNMPv1');
|
logger.info('SNMP Version:');
|
||||||
console.log(' 2) SNMPv2c');
|
logger.dim(' 1) SNMPv1');
|
||||||
console.log(' 3) SNMPv3 (with security features)');
|
logger.dim(' 2) SNMPv2c');
|
||||||
|
logger.dim(' 3) SNMPv3 (with security features)');
|
||||||
const versionInput = await prompt(`Select SNMP version [${defaultVersion}]: `);
|
const versionInput = await prompt(`Select SNMP version [${defaultVersion}]: `);
|
||||||
const version = parseInt(versionInput, 10);
|
const version = parseInt(versionInput, 10);
|
||||||
snmpConfig.version = versionInput.trim() && (version === 1 || version === 2 || version === 3)
|
snmpConfig.version = versionInput.trim() && (version === 1 || version === 2 || version === 3)
|
||||||
@@ -697,13 +701,15 @@ export class UpsHandler {
|
|||||||
snmpConfig: any,
|
snmpConfig: any,
|
||||||
prompt: (question: string) => Promise<string>,
|
prompt: (question: string) => Promise<string>,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
console.log('\nSNMPv3 Security Settings:');
|
logger.log('');
|
||||||
|
logger.info('SNMPv3 Security Settings:');
|
||||||
|
|
||||||
// Security Level
|
// Security Level
|
||||||
console.log('\nSecurity Level:');
|
logger.log('');
|
||||||
console.log(' 1) noAuthNoPriv (No Authentication, No Privacy)');
|
logger.info('Security Level:');
|
||||||
console.log(' 2) authNoPriv (Authentication, No Privacy)');
|
logger.dim(' 1) noAuthNoPriv (No Authentication, No Privacy)');
|
||||||
console.log(' 3) authPriv (Authentication and Privacy)');
|
logger.dim(' 2) authNoPriv (Authentication, No Privacy)');
|
||||||
|
logger.dim(' 3) authPriv (Authentication and Privacy)');
|
||||||
const defaultSecLevel = snmpConfig.securityLevel
|
const defaultSecLevel = snmpConfig.securityLevel
|
||||||
? snmpConfig.securityLevel === 'noAuthNoPriv'
|
? snmpConfig.securityLevel === 'noAuthNoPriv'
|
||||||
? 1
|
? 1
|
||||||
@@ -752,8 +758,9 @@ export class UpsHandler {
|
|||||||
|
|
||||||
// Allow customizing the timeout value
|
// Allow customizing the timeout value
|
||||||
const defaultTimeout = snmpConfig.timeout / 1000; // Convert from ms to seconds for display
|
const defaultTimeout = snmpConfig.timeout / 1000; // Convert from ms to seconds for display
|
||||||
console.log(
|
logger.log('');
|
||||||
'\nSNMPv3 operations with authentication and privacy may require longer timeouts.',
|
logger.info(
|
||||||
|
'SNMPv3 operations with authentication and privacy may require longer timeouts.',
|
||||||
);
|
);
|
||||||
const timeoutInput = await prompt(`SNMP Timeout in seconds [${defaultTimeout}]: `);
|
const timeoutInput = await prompt(`SNMP Timeout in seconds [${defaultTimeout}]: `);
|
||||||
const timeout = parseInt(timeoutInput, 10);
|
const timeout = parseInt(timeoutInput, 10);
|
||||||
@@ -773,9 +780,10 @@ export class UpsHandler {
|
|||||||
prompt: (question: string) => Promise<string>,
|
prompt: (question: string) => Promise<string>,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
// Authentication protocol
|
// Authentication protocol
|
||||||
console.log('\nAuthentication Protocol:');
|
logger.log('');
|
||||||
console.log(' 1) MD5');
|
logger.info('Authentication Protocol:');
|
||||||
console.log(' 2) SHA');
|
logger.dim(' 1) MD5');
|
||||||
|
logger.dim(' 2) SHA');
|
||||||
const defaultAuthProtocol = snmpConfig.authProtocol === 'SHA' ? 2 : 1;
|
const defaultAuthProtocol = snmpConfig.authProtocol === 'SHA' ? 2 : 1;
|
||||||
const authProtocolInput = await prompt(
|
const authProtocolInput = await prompt(
|
||||||
`Select Authentication Protocol [${defaultAuthProtocol}]: `,
|
`Select Authentication Protocol [${defaultAuthProtocol}]: `,
|
||||||
@@ -799,9 +807,10 @@ export class UpsHandler {
|
|||||||
prompt: (question: string) => Promise<string>,
|
prompt: (question: string) => Promise<string>,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
// Privacy protocol
|
// Privacy protocol
|
||||||
console.log('\nPrivacy Protocol:');
|
logger.log('');
|
||||||
console.log(' 1) DES');
|
logger.info('Privacy Protocol:');
|
||||||
console.log(' 2) AES');
|
logger.dim(' 1) DES');
|
||||||
|
logger.dim(' 2) AES');
|
||||||
const defaultPrivProtocol = snmpConfig.privProtocol === 'AES' ? 2 : 1;
|
const defaultPrivProtocol = snmpConfig.privProtocol === 'AES' ? 2 : 1;
|
||||||
const privProtocolInput = await prompt(`Select Privacy Protocol [${defaultPrivProtocol}]: `);
|
const privProtocolInput = await prompt(`Select Privacy Protocol [${defaultPrivProtocol}]: `);
|
||||||
const privProtocol = parseInt(privProtocolInput, 10) || defaultPrivProtocol;
|
const privProtocol = parseInt(privProtocolInput, 10) || defaultPrivProtocol;
|
||||||
@@ -822,7 +831,8 @@ export class UpsHandler {
|
|||||||
thresholds: any,
|
thresholds: any,
|
||||||
prompt: (question: string) => Promise<string>,
|
prompt: (question: string) => Promise<string>,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
console.log('\nShutdown Thresholds:');
|
logger.log('');
|
||||||
|
logger.info('Shutdown Thresholds:');
|
||||||
|
|
||||||
// Battery threshold
|
// Battery threshold
|
||||||
const defaultBatteryThreshold = thresholds.battery || 60;
|
const defaultBatteryThreshold = thresholds.battery || 60;
|
||||||
@@ -854,13 +864,14 @@ export class UpsHandler {
|
|||||||
snmpConfig: any,
|
snmpConfig: any,
|
||||||
prompt: (question: string) => Promise<string>,
|
prompt: (question: string) => Promise<string>,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
console.log('\nUPS Model Selection:');
|
logger.log('');
|
||||||
console.log(' 1) CyberPower');
|
logger.info('UPS Model Selection:');
|
||||||
console.log(' 2) APC');
|
logger.dim(' 1) CyberPower');
|
||||||
console.log(' 3) Eaton');
|
logger.dim(' 2) APC');
|
||||||
console.log(' 4) TrippLite');
|
logger.dim(' 3) Eaton');
|
||||||
console.log(' 5) Liebert/Vertiv');
|
logger.dim(' 4) TrippLite');
|
||||||
console.log(' 6) Custom (Advanced)');
|
logger.dim(' 5) Liebert/Vertiv');
|
||||||
|
logger.dim(' 6) Custom (Advanced)');
|
||||||
|
|
||||||
const defaultModelValue = snmpConfig.upsModel === 'cyberpower'
|
const defaultModelValue = snmpConfig.upsModel === 'cyberpower'
|
||||||
? 1
|
? 1
|
||||||
@@ -891,8 +902,9 @@ export class UpsHandler {
|
|||||||
snmpConfig.upsModel = 'liebert';
|
snmpConfig.upsModel = 'liebert';
|
||||||
} else if (modelValue === 6) {
|
} else if (modelValue === 6) {
|
||||||
snmpConfig.upsModel = 'custom';
|
snmpConfig.upsModel = 'custom';
|
||||||
console.log('\nEnter custom OIDs for your UPS:');
|
logger.log('');
|
||||||
console.log('(Leave blank to use standard RFC 1628 OIDs as fallback)');
|
logger.info('Enter custom OIDs for your UPS:');
|
||||||
|
logger.dim('(Leave blank to use standard RFC 1628 OIDs as fallback)');
|
||||||
|
|
||||||
// Custom OIDs
|
// Custom OIDs
|
||||||
const powerStatusOID = await prompt('Power Status OID: ');
|
const powerStatusOID = await prompt('Power Status OID: ');
|
||||||
|
11
ts/daemon.ts
11
ts/daemon.ts
@@ -207,11 +207,9 @@ export class NupstDaemon {
|
|||||||
fs.writeFileSync(this.CONFIG_PATH, JSON.stringify(configToSave, null, 2));
|
fs.writeFileSync(this.CONFIG_PATH, JSON.stringify(configToSave, null, 2));
|
||||||
this.config = configToSave;
|
this.config = configToSave;
|
||||||
|
|
||||||
console.log('┌─ Configuration Saved ─────────────────────┐');
|
logger.logBox('Configuration Saved', [`Location: ${this.CONFIG_PATH}`], 45, 'success');
|
||||||
console.log(`│ Location: ${this.CONFIG_PATH}`);
|
|
||||||
console.log('└──────────────────────────────────────────┘');
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error saving configuration:', error);
|
logger.error(`Error saving configuration: ${error}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -219,10 +217,7 @@ export class NupstDaemon {
|
|||||||
* Helper method to log configuration errors consistently
|
* Helper method to log configuration errors consistently
|
||||||
*/
|
*/
|
||||||
private logConfigError(message: string): void {
|
private logConfigError(message: string): void {
|
||||||
console.error('┌─ Configuration Error ─────────────────────┐');
|
logger.logBox('Configuration Error', [message, "Please run 'nupst setup' first to create a configuration."], 45, 'error');
|
||||||
console.error(`│ ${message}`);
|
|
||||||
console.error("│ Please run 'nupst setup' first to create a configuration.");
|
|
||||||
console.error('└───────────────────────────────────────────┘');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -34,12 +34,14 @@ export class MigrationRunner {
|
|||||||
let currentConfig = config;
|
let currentConfig = config;
|
||||||
let anyMigrationsRan = false;
|
let anyMigrationsRan = false;
|
||||||
|
|
||||||
logger.dim('Checking for required config migrations...');
|
|
||||||
|
|
||||||
for (const migration of this.migrations) {
|
for (const migration of this.migrations) {
|
||||||
const shouldRun = await migration.shouldRun(currentConfig);
|
const shouldRun = await migration.shouldRun(currentConfig);
|
||||||
|
|
||||||
if (shouldRun) {
|
if (shouldRun) {
|
||||||
|
// Only show "checking" message when we actually need to migrate
|
||||||
|
if (!anyMigrationsRan) {
|
||||||
|
logger.dim('Checking for required config migrations...');
|
||||||
|
}
|
||||||
logger.info(`Running ${migration.getName()}...`);
|
logger.info(`Running ${migration.getName()}...`);
|
||||||
currentConfig = await migration.migrate(currentConfig);
|
currentConfig = await migration.migrate(currentConfig);
|
||||||
anyMigrationsRan = true;
|
anyMigrationsRan = true;
|
||||||
@@ -49,7 +51,7 @@ export class MigrationRunner {
|
|||||||
if (anyMigrationsRan) {
|
if (anyMigrationsRan) {
|
||||||
logger.success('Configuration migrations complete');
|
logger.success('Configuration migrations complete');
|
||||||
} else {
|
} else {
|
||||||
logger.dim('No migrations needed');
|
logger.success('config format ok');
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@@ -525,6 +525,7 @@ export class NupstSnmp {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine power status based on UPS model and raw value
|
* Determine power status based on UPS model and raw value
|
||||||
|
* Uses the value mappings defined in the OID sets
|
||||||
* @param upsModel UPS model
|
* @param upsModel UPS model
|
||||||
* @param powerStatusValue Raw power status value
|
* @param powerStatusValue Raw power status value
|
||||||
* @returns Standardized power status
|
* @returns Standardized power status
|
||||||
@@ -533,39 +534,28 @@ export class NupstSnmp {
|
|||||||
upsModel: TUpsModel | undefined,
|
upsModel: TUpsModel | undefined,
|
||||||
powerStatusValue: number,
|
powerStatusValue: number,
|
||||||
): 'online' | 'onBattery' | 'unknown' {
|
): 'online' | 'onBattery' | 'unknown' {
|
||||||
if (upsModel === 'cyberpower') {
|
// Get the OID set for this UPS model
|
||||||
// CyberPower RMCARD205: upsBaseOutputStatus values
|
if (upsModel && upsModel !== 'custom') {
|
||||||
// 2=onLine, 3=onBattery, 4=onBoost, 5=onSleep, 6=off, etc.
|
const oidSet = UpsOidSets.getOidSet(upsModel);
|
||||||
if (powerStatusValue === 2) {
|
|
||||||
return 'online';
|
// Use the value mappings if available
|
||||||
} else if (powerStatusValue === 3) {
|
if (oidSet.POWER_STATUS_VALUES) {
|
||||||
return 'onBattery';
|
if (powerStatusValue === oidSet.POWER_STATUS_VALUES.online) {
|
||||||
}
|
return 'online';
|
||||||
} else if (upsModel === 'eaton') {
|
} else if (powerStatusValue === oidSet.POWER_STATUS_VALUES.onBattery) {
|
||||||
// Eaton UPS: xupsOutputSource values
|
return 'onBattery';
|
||||||
// 3=normal/mains, 5=battery, etc.
|
}
|
||||||
if (powerStatusValue === 3) {
|
|
||||||
return 'online';
|
|
||||||
} else if (powerStatusValue === 5) {
|
|
||||||
return 'onBattery';
|
|
||||||
}
|
|
||||||
} else if (upsModel === 'apc') {
|
|
||||||
// APC UPS: upsBasicOutputStatus values
|
|
||||||
// 2=online, 3=onBattery, etc.
|
|
||||||
if (powerStatusValue === 2) {
|
|
||||||
return 'online';
|
|
||||||
} else if (powerStatusValue === 3) {
|
|
||||||
return 'onBattery';
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Default interpretation for other UPS models
|
|
||||||
if (powerStatusValue === 1) {
|
|
||||||
return 'online';
|
|
||||||
} else if (powerStatusValue === 2) {
|
|
||||||
return 'onBattery';
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fallback for custom or undefined models (RFC 1628 standard)
|
||||||
|
// upsOutputSource: 3=normal (mains), 5=battery
|
||||||
|
if (powerStatusValue === 3) {
|
||||||
|
return 'online';
|
||||||
|
} else if (powerStatusValue === 5) {
|
||||||
|
return 'onBattery';
|
||||||
|
}
|
||||||
|
|
||||||
return 'unknown';
|
return 'unknown';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -11,37 +11,57 @@ export class UpsOidSets {
|
|||||||
private static readonly UPS_OID_SETS: Record<TUpsModel, IOidSet> = {
|
private static readonly UPS_OID_SETS: Record<TUpsModel, IOidSet> = {
|
||||||
// Cyberpower OIDs for RMCARD205 (based on CyberPower_MIB_v2.11)
|
// Cyberpower OIDs for RMCARD205 (based on CyberPower_MIB_v2.11)
|
||||||
cyberpower: {
|
cyberpower: {
|
||||||
POWER_STATUS: '1.3.6.1.4.1.3808.1.1.1.4.1.1.0', // upsBaseOutputStatus (2=online, 3=on battery)
|
POWER_STATUS: '1.3.6.1.4.1.3808.1.1.1.4.1.1.0', // upsBaseOutputStatus
|
||||||
BATTERY_CAPACITY: '1.3.6.1.4.1.3808.1.1.1.2.2.1.0', // upsAdvanceBatteryCapacity (percentage)
|
BATTERY_CAPACITY: '1.3.6.1.4.1.3808.1.1.1.2.2.1.0', // upsAdvanceBatteryCapacity (percentage)
|
||||||
BATTERY_RUNTIME: '1.3.6.1.4.1.3808.1.1.1.2.2.4.0', // upsAdvanceBatteryRunTimeRemaining (TimeTicks)
|
BATTERY_RUNTIME: '1.3.6.1.4.1.3808.1.1.1.2.2.4.0', // upsAdvanceBatteryRunTimeRemaining (TimeTicks)
|
||||||
|
POWER_STATUS_VALUES: {
|
||||||
|
online: 2, // upsBaseOutputStatus: 2=onLine
|
||||||
|
onBattery: 3, // upsBaseOutputStatus: 3=onBattery
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
// APC OIDs
|
// APC OIDs
|
||||||
apc: {
|
apc: {
|
||||||
POWER_STATUS: '1.3.6.1.4.1.318.1.1.1.4.1.1.0', // Power status (1=online, 2=on battery)
|
POWER_STATUS: '1.3.6.1.4.1.318.1.1.1.4.1.1.0', // upsBasicOutputStatus
|
||||||
BATTERY_CAPACITY: '1.3.6.1.4.1.318.1.1.1.2.2.1.0', // Battery capacity in percentage
|
BATTERY_CAPACITY: '1.3.6.1.4.1.318.1.1.1.2.2.1.0', // Battery capacity in percentage
|
||||||
BATTERY_RUNTIME: '1.3.6.1.4.1.318.1.1.1.2.2.3.0', // Remaining runtime in minutes
|
BATTERY_RUNTIME: '1.3.6.1.4.1.318.1.1.1.2.2.3.0', // Remaining runtime in minutes
|
||||||
|
POWER_STATUS_VALUES: {
|
||||||
|
online: 2, // upsBasicOutputStatus: 2=onLine
|
||||||
|
onBattery: 3, // upsBasicOutputStatus: 3=onBattery
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
// Eaton OIDs
|
// Eaton OIDs
|
||||||
eaton: {
|
eaton: {
|
||||||
POWER_STATUS: '1.3.6.1.4.1.534.1.4.4.0', // xupsOutputSource (3=normal/mains, 5=battery)
|
POWER_STATUS: '1.3.6.1.4.1.534.1.4.4.0', // xupsOutputSource
|
||||||
BATTERY_CAPACITY: '1.3.6.1.4.1.534.1.2.4.0', // xupsBatCapacity (percentage)
|
BATTERY_CAPACITY: '1.3.6.1.4.1.534.1.2.4.0', // xupsBatCapacity (percentage)
|
||||||
BATTERY_RUNTIME: '1.3.6.1.4.1.534.1.2.1.0', // xupsBatTimeRemaining (seconds)
|
BATTERY_RUNTIME: '1.3.6.1.4.1.534.1.2.1.0', // xupsBatTimeRemaining (seconds)
|
||||||
|
POWER_STATUS_VALUES: {
|
||||||
|
online: 3, // xupsOutputSource: 3=normal (mains power)
|
||||||
|
onBattery: 5, // xupsOutputSource: 5=battery
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
// TrippLite OIDs
|
// TrippLite OIDs
|
||||||
tripplite: {
|
tripplite: {
|
||||||
POWER_STATUS: '1.3.6.1.4.1.850.1.1.3.1.1.1.0', // Power status
|
POWER_STATUS: '1.3.6.1.4.1.850.1.1.3.1.1.1.0', // tlUpsOutputSource
|
||||||
BATTERY_CAPACITY: '1.3.6.1.4.1.850.1.1.3.2.4.1.0', // Battery capacity in percentage
|
BATTERY_CAPACITY: '1.3.6.1.4.1.850.1.1.3.2.4.1.0', // Battery capacity in percentage
|
||||||
BATTERY_RUNTIME: '1.3.6.1.4.1.850.1.1.3.2.2.1.0', // Remaining runtime in minutes
|
BATTERY_RUNTIME: '1.3.6.1.4.1.850.1.1.3.2.2.1.0', // Remaining runtime in minutes
|
||||||
|
POWER_STATUS_VALUES: {
|
||||||
|
online: 2, // tlUpsOutputSource: 2=normal (mains power)
|
||||||
|
onBattery: 3, // tlUpsOutputSource: 3=onBattery
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
// Liebert/Vertiv OIDs
|
// Liebert/Vertiv OIDs
|
||||||
liebert: {
|
liebert: {
|
||||||
POWER_STATUS: '1.3.6.1.4.1.476.1.42.3.9.20.1.20.1.2.1.2.1', // Power status
|
POWER_STATUS: '1.3.6.1.4.1.476.1.42.3.9.20.1.20.1.2.1.2.1', // lgpPwrOutputSource
|
||||||
BATTERY_CAPACITY: '1.3.6.1.4.1.476.1.42.3.9.20.1.20.1.2.1.4.1', // Battery capacity in percentage
|
BATTERY_CAPACITY: '1.3.6.1.4.1.476.1.42.3.9.20.1.20.1.2.1.4.1', // Battery capacity in percentage
|
||||||
BATTERY_RUNTIME: '1.3.6.1.4.1.476.1.42.3.9.20.1.20.1.2.1.5.1', // Remaining runtime in minutes
|
BATTERY_RUNTIME: '1.3.6.1.4.1.476.1.42.3.9.20.1.20.1.2.1.5.1', // Remaining runtime in minutes
|
||||||
|
POWER_STATUS_VALUES: {
|
||||||
|
online: 2, // lgpPwrOutputSource: 2=normal (mains power)
|
||||||
|
onBattery: 3, // lgpPwrOutputSource: 3=onBattery
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
// Custom OIDs (to be provided by the user)
|
// Custom OIDs (to be provided by the user)
|
||||||
|
@@ -28,6 +28,13 @@ export interface IOidSet {
|
|||||||
BATTERY_CAPACITY: string;
|
BATTERY_CAPACITY: string;
|
||||||
/** OID for battery runtime */
|
/** OID for battery runtime */
|
||||||
BATTERY_RUNTIME: string;
|
BATTERY_RUNTIME: string;
|
||||||
|
/** Power status value mappings */
|
||||||
|
POWER_STATUS_VALUES?: {
|
||||||
|
/** SNMP value that indicates UPS is online (on AC power) */
|
||||||
|
online: number;
|
||||||
|
/** SNMP value that indicates UPS is on battery */
|
||||||
|
onBattery: number;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -50,11 +50,11 @@ WantedBy=multi-user.target
|
|||||||
try {
|
try {
|
||||||
await fs.access(configPath);
|
await fs.access(configPath);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const boxWidth = 50;
|
logger.log('');
|
||||||
logger.logBoxTitle('Configuration Error', boxWidth);
|
logger.error('No configuration found');
|
||||||
logger.logBoxLine(`No configuration file found at ${configPath}`);
|
logger.log(` ${theme.dim('Config file:')} ${configPath}`);
|
||||||
logger.logBoxLine("Please run 'nupst add' first to create a UPS configuration.");
|
logger.log(` ${theme.dim('Run')} ${theme.command('nupst ups add')} ${theme.dim('to create a configuration')}`);
|
||||||
logger.logBoxEnd();
|
logger.log('');
|
||||||
throw new Error('Configuration not found');
|
throw new Error('Configuration not found');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -138,16 +138,12 @@ WantedBy=multi-user.target
|
|||||||
try {
|
try {
|
||||||
// Enable debug mode if requested
|
// Enable debug mode if requested
|
||||||
if (debugMode) {
|
if (debugMode) {
|
||||||
const boxWidth = 45;
|
console.log('');
|
||||||
logger.logBoxTitle('Debug Mode', boxWidth);
|
logger.info('Debug Mode: SNMP debugging enabled');
|
||||||
logger.logBoxLine('SNMP debugging enabled - detailed logs will be shown');
|
console.log('');
|
||||||
logger.logBoxEnd();
|
|
||||||
this.daemon.getNupstSnmp().enableDebug();
|
this.daemon.getNupstSnmp().enableDebug();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Display version information
|
|
||||||
this.daemon.getNupstSnmp().getNupst().logVersionInfo();
|
|
||||||
|
|
||||||
// Check if config exists first
|
// Check if config exists first
|
||||||
try {
|
try {
|
||||||
await this.checkConfigExists();
|
await this.checkConfigExists();
|
||||||
@@ -196,11 +192,11 @@ WantedBy=multi-user.target
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Display beautiful status
|
// Display beautiful status
|
||||||
console.log('');
|
logger.log('');
|
||||||
if (isActive) {
|
if (isActive) {
|
||||||
console.log(`${symbols.running} ${theme.success('Service:')} ${theme.statusActive('active (running)')}`);
|
logger.log(`${symbols.running} ${theme.success('Service:')} ${theme.statusActive('active (running)')}`);
|
||||||
} else {
|
} else {
|
||||||
console.log(`${symbols.stopped} ${theme.dim('Service:')} ${theme.statusInactive('inactive')}`);
|
logger.log(`${symbols.stopped} ${theme.dim('Service:')} ${theme.statusInactive('inactive')}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pid || memory || cpu) {
|
if (pid || memory || cpu) {
|
||||||
@@ -208,14 +204,14 @@ WantedBy=multi-user.target
|
|||||||
if (pid) details.push(`PID: ${theme.dim(pid)}`);
|
if (pid) details.push(`PID: ${theme.dim(pid)}`);
|
||||||
if (memory) details.push(`Memory: ${theme.dim(memory)}`);
|
if (memory) details.push(`Memory: ${theme.dim(memory)}`);
|
||||||
if (cpu) details.push(`CPU: ${theme.dim(cpu)}`);
|
if (cpu) details.push(`CPU: ${theme.dim(cpu)}`);
|
||||||
console.log(` ${details.join(' ')}`);
|
logger.log(` ${details.join(' ')}`);
|
||||||
}
|
}
|
||||||
console.log('');
|
logger.log('');
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log('');
|
logger.log('');
|
||||||
console.log(`${symbols.stopped} ${theme.dim('Service:')} ${theme.statusInactive('not installed')}`);
|
logger.log(`${symbols.stopped} ${theme.dim('Service:')} ${theme.statusInactive('not installed')}`);
|
||||||
console.log('');
|
logger.log('');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -232,7 +228,7 @@ WantedBy=multi-user.target
|
|||||||
|
|
||||||
// Check if we have the new multi-UPS config format
|
// Check if we have the new multi-UPS config format
|
||||||
if (config.upsDevices && Array.isArray(config.upsDevices) && config.upsDevices.length > 0) {
|
if (config.upsDevices && Array.isArray(config.upsDevices) && config.upsDevices.length > 0) {
|
||||||
console.log(theme.info(`UPS Devices (${config.upsDevices.length}):`));
|
logger.info(`UPS Devices (${config.upsDevices.length}):`);
|
||||||
|
|
||||||
// Show status for each UPS
|
// Show status for each UPS
|
||||||
for (const ups of config.upsDevices) {
|
for (const ups of config.upsDevices) {
|
||||||
@@ -240,7 +236,7 @@ WantedBy=multi-user.target
|
|||||||
}
|
}
|
||||||
} else if (config.snmp) {
|
} else if (config.snmp) {
|
||||||
// Legacy single UPS configuration
|
// Legacy single UPS configuration
|
||||||
console.log(theme.info('UPS Devices (1):'));
|
logger.info('UPS Devices (1):');
|
||||||
const legacyUps = {
|
const legacyUps = {
|
||||||
id: 'default',
|
id: 'default',
|
||||||
name: 'Default UPS',
|
name: 'Default UPS',
|
||||||
@@ -251,16 +247,16 @@ WantedBy=multi-user.target
|
|||||||
|
|
||||||
await this.displaySingleUpsStatus(legacyUps, snmp);
|
await this.displaySingleUpsStatus(legacyUps, snmp);
|
||||||
} else {
|
} else {
|
||||||
console.log('');
|
logger.log('');
|
||||||
console.log(`${symbols.warning} ${theme.warning('No UPS devices configured')}`);
|
logger.warn('No UPS devices configured');
|
||||||
console.log(` ${theme.dim('Run')} ${theme.command('nupst ups add')} ${theme.dim('to add a device')}`);
|
logger.log(` ${theme.dim('Run')} ${theme.command('nupst ups add')} ${theme.dim('to add a device')}`);
|
||||||
console.log('');
|
logger.log('');
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log('');
|
logger.log('');
|
||||||
console.log(`${symbols.error} ${theme.error('Failed to retrieve UPS status')}`);
|
logger.error('Failed to retrieve UPS status');
|
||||||
console.log(` ${theme.dim(error instanceof Error ? error.message : String(error))}`);
|
logger.log(` ${theme.dim(error instanceof Error ? error.message : String(error))}`);
|
||||||
console.log('');
|
logger.log('');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -288,15 +284,15 @@ WantedBy=multi-user.target
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Display UPS name and power status
|
// Display UPS name and power status
|
||||||
console.log(` ${statusSymbol} ${theme.highlight(ups.name)} - ${formatPowerStatus(status.powerStatus)}`);
|
logger.log(` ${statusSymbol} ${theme.highlight(ups.name)} - ${formatPowerStatus(status.powerStatus)}`);
|
||||||
|
|
||||||
// Display battery with color coding
|
// Display battery with color coding
|
||||||
const batteryColor = getBatteryColor(status.batteryCapacity);
|
const batteryColor = getBatteryColor(status.batteryCapacity);
|
||||||
const batterySymbol = status.batteryCapacity >= ups.thresholds.battery ? symbols.success : symbols.warning;
|
const batterySymbol = status.batteryCapacity >= ups.thresholds.battery ? symbols.success : symbols.warning;
|
||||||
console.log(` Battery: ${batteryColor(status.batteryCapacity + '%')} ${batterySymbol} Runtime: ${getRuntimeColor(status.batteryRuntime)(status.batteryRuntime + ' min')}`);
|
logger.log(` Battery: ${batteryColor(status.batteryCapacity + '%')} ${batterySymbol} Runtime: ${getRuntimeColor(status.batteryRuntime)(status.batteryRuntime + ' min')}`);
|
||||||
|
|
||||||
// Display host info
|
// Display host info
|
||||||
console.log(` ${theme.dim(`Host: ${ups.snmp.host}:${ups.snmp.port}`)}`);
|
logger.log(` ${theme.dim(`Host: ${ups.snmp.host}:${ups.snmp.port}`)}`);
|
||||||
|
|
||||||
// Display groups if any
|
// Display groups if any
|
||||||
if (ups.groups && ups.groups.length > 0) {
|
if (ups.groups && ups.groups.length > 0) {
|
||||||
@@ -305,17 +301,17 @@ WantedBy=multi-user.target
|
|||||||
const group = config.groups?.find((g: { id: string }) => g.id === groupId);
|
const group = config.groups?.find((g: { id: string }) => g.id === groupId);
|
||||||
return group ? group.name : groupId;
|
return group ? group.name : groupId;
|
||||||
});
|
});
|
||||||
console.log(` ${theme.dim(`Groups: ${groupNames.join(', ')}`)}`);
|
logger.log(` ${theme.dim(`Groups: ${groupNames.join(', ')}`)}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('');
|
logger.log('');
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Display error for this UPS
|
// Display error for this UPS
|
||||||
console.log(` ${symbols.error} ${theme.highlight(ups.name)} - ${theme.error('Connection failed')}`);
|
logger.log(` ${symbols.error} ${theme.highlight(ups.name)} - ${theme.error('Connection failed')}`);
|
||||||
console.log(` ${theme.dim(error instanceof Error ? error.message : String(error))}`);
|
logger.log(` ${theme.dim(error instanceof Error ? error.message : String(error))}`);
|
||||||
console.log(` ${theme.dim(`Host: ${ups.snmp.host}:${ups.snmp.port}`)}`);
|
logger.log(` ${theme.dim(`Host: ${ups.snmp.host}:${ups.snmp.port}`)}`);
|
||||||
console.log('');
|
logger.log('');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user