diff --git a/ts/daemon.ts b/ts/daemon.ts index b527bb9..828c28d 100644 --- a/ts/daemon.ts +++ b/ts/daemon.ts @@ -310,29 +310,58 @@ export class NupstDaemon { * Log the loaded configuration settings */ private logConfigLoaded(): void { - const boxWidth = 50; - logger.logBoxTitle('Configuration Loaded', boxWidth); - - if (this.config.upsDevices && this.config.upsDevices.length > 0) { - logger.logBoxLine(`UPS Devices: ${this.config.upsDevices.length}`); - for (const ups of this.config.upsDevices) { - logger.logBoxLine(` - ${ups.name} (${ups.id}): ${ups.snmp.host}:${ups.snmp.port}`); - } - } else { - logger.logBoxLine('No UPS devices configured'); - } - - if (this.config.groups && this.config.groups.length > 0) { - logger.logBoxLine(`Groups: ${this.config.groups.length}`); - for (const group of this.config.groups) { - logger.logBoxLine(` - ${group.name} (${group.id}): ${group.mode} mode`); - } - } else { - logger.logBoxLine('No Groups configured'); - } - + const { theme } = require('./colors.ts'); + + logger.log(''); + logger.logBoxTitle('Configuration Loaded', 70, 'success'); logger.logBoxLine(`Check Interval: ${this.config.checkInterval / 1000} seconds`); logger.logBoxEnd(); + logger.log(''); + + // Display UPS devices in a table + if (this.config.upsDevices && this.config.upsDevices.length > 0) { + logger.info(`UPS Devices (${this.config.upsDevices.length}):`); + + const upsColumns: Array<{ header: string; key: string; align?: 'left' | 'right'; color?: (val: string) => string }> = [ + { header: 'Name', key: 'name', align: 'left', color: theme.highlight }, + { header: 'ID', key: 'id', align: 'left', color: theme.dim }, + { header: 'Host:Port', key: 'host', align: 'left', color: theme.info }, + { header: 'Battery/Runtime', key: 'thresholds', align: 'left' }, + ]; + + const upsRows: Array> = this.config.upsDevices.map((ups) => ({ + name: ups.name, + id: ups.id, + host: `${ups.snmp.host}:${ups.snmp.port}`, + thresholds: `${ups.thresholds.battery}% / ${ups.thresholds.runtime} min`, + })); + + logger.logTable(upsColumns, upsRows); + logger.log(''); + } else { + logger.warn('No UPS devices configured'); + logger.log(''); + } + + // Display groups in a table + if (this.config.groups && this.config.groups.length > 0) { + logger.info(`Groups (${this.config.groups.length}):`); + + const groupColumns: Array<{ header: string; key: string; align?: 'left' | 'right'; color?: (val: string) => string }> = [ + { header: 'Name', key: 'name', align: 'left', color: theme.highlight }, + { header: 'ID', key: 'id', align: 'left', color: theme.dim }, + { header: 'Mode', key: 'mode', align: 'left', color: theme.info }, + ]; + + const groupRows: Array> = this.config.groups.map((group) => ({ + name: group.name, + id: group.id, + mode: group.mode, + })); + + logger.logTable(groupColumns, groupRows); + logger.log(''); + } } /** @@ -428,9 +457,14 @@ export class NupstDaemon { // Check if power status changed if (currentStatus && currentStatus.powerStatus !== status.powerStatus) { - logger.logBoxTitle(`Power Status Change: ${ups.name}`, 50); - logger.logBoxLine(`Status changed: ${currentStatus.powerStatus} → ${status.powerStatus}`); + const { theme, formatPowerStatus } = require('./colors.ts'); + logger.log(''); + logger.logBoxTitle(`Power Status Change: ${ups.name}`, 60, 'warning'); + logger.logBoxLine(`Previous: ${formatPowerStatus(currentStatus.powerStatus)}`); + logger.logBoxLine(`Current: ${formatPowerStatus(status.powerStatus)}`); + logger.logBoxLine(`Time: ${new Date().toISOString()}`); logger.logBoxEnd(); + logger.log(''); updatedStatus.lastStatusChange = currentTime; } @@ -452,21 +486,41 @@ export class NupstDaemon { */ private logAllUpsStatus(): void { const timestamp = new Date().toISOString(); - const boxWidth = 60; - logger.logBoxTitle('Periodic Status Update', boxWidth); + + logger.log(''); + logger.logBoxTitle('Periodic Status Update', 70, 'info'); logger.logBoxLine(`Timestamp: ${timestamp}`); - logger.logBoxLine(''); + logger.logBoxEnd(); + logger.log(''); + // Import theme and symbols for coloring + const { theme, symbols, getBatteryColor, getRuntimeColor, formatPowerStatus } = require('./colors.ts'); + + // Build table data + const columns: Array<{ header: string; key: string; align?: 'left' | 'right'; color?: (val: string) => string }> = [ + { header: 'UPS Name', key: 'name', align: 'left', color: theme.highlight }, + { header: 'ID', key: 'id', align: 'left', color: theme.dim }, + { header: 'Power Status', key: 'powerStatus', align: 'left' }, + { header: 'Battery', key: 'battery', align: 'right' }, + { header: 'Runtime', key: 'runtime', align: 'right' }, + ]; + + const rows: Array> = []; for (const [id, status] of this.upsStatus.entries()) { - logger.logBoxLine(`UPS: ${status.name} (${id})`); - logger.logBoxLine(` Power Status: ${status.powerStatus}`); - logger.logBoxLine( - ` Battery: ${status.batteryCapacity}% | Runtime: ${status.batteryRuntime} min`, - ); - logger.logBoxLine(''); + const batteryColor = getBatteryColor(status.batteryCapacity); + const runtimeColor = getRuntimeColor(status.batteryRuntime); + + rows.push({ + name: status.name, + id: id, + powerStatus: formatPowerStatus(status.powerStatus), + battery: batteryColor(status.batteryCapacity + '%'), + runtime: runtimeColor(status.batteryRuntime + ' min'), + }); } - logger.logBoxEnd(); + logger.logTable(columns, rows); + logger.log(''); } /** @@ -745,38 +799,63 @@ export class NupstDaemon { const MAX_MONITORING_TIME = 5 * 60 * 1000; // Max 5 minutes of monitoring const startTime = Date.now(); - logger.log( - `Emergency shutdown threshold: ${EMERGENCY_RUNTIME_THRESHOLD} minutes remaining battery runtime`, - ); + const { theme, getBatteryColor, getRuntimeColor } = require('./colors.ts'); + + logger.log(''); + logger.logBoxTitle('Shutdown Monitoring Active', 60, 'warning'); + logger.logBoxLine(`Emergency threshold: ${EMERGENCY_RUNTIME_THRESHOLD} minutes runtime`); + logger.logBoxLine(`Check interval: ${CHECK_INTERVAL / 1000} seconds`); + logger.logBoxLine(`Max monitoring time: ${MAX_MONITORING_TIME / 1000} seconds`); + logger.logBoxEnd(); + logger.log(''); // Continue monitoring until max monitoring time is reached while (Date.now() - startTime < MAX_MONITORING_TIME) { try { - logger.log('Checking UPS status during shutdown...'); + logger.info('Checking UPS status during shutdown...'); + + // Build table for UPS status during shutdown + const columns: Array<{ header: string; key: string; align?: 'left' | 'right'; color?: (val: string) => string }> = [ + { header: 'UPS Name', key: 'name', align: 'left', color: theme.highlight }, + { header: 'Battery', key: 'battery', align: 'right' }, + { header: 'Runtime', key: 'runtime', align: 'right' }, + { header: 'Status', key: 'status', align: 'left' }, + ]; + + const rows: Array> = []; + let emergencyDetected = false; + let emergencyUps: any = null; // Check all UPS devices for (const ups of this.config.upsDevices) { try { const status = await this.snmp.getUpsStatus(ups.snmp); - logger.log( - `UPS ${ups.name}: Battery ${status.batteryCapacity}%, Runtime: ${status.batteryRuntime} minutes`, - ); + const batteryColor = getBatteryColor(status.batteryCapacity); + const runtimeColor = getRuntimeColor(status.batteryRuntime); - // If any UPS battery runtime gets critically low, force immediate shutdown - if (status.batteryRuntime < EMERGENCY_RUNTIME_THRESHOLD) { - logger.logBoxTitle('EMERGENCY SHUTDOWN', 50); - logger.logBoxLine( - `UPS ${ups.name} runtime critically low: ${status.batteryRuntime} minutes`, - ); - logger.logBoxLine('Forcing immediate shutdown!'); - logger.logBoxEnd(); + const isCritical = status.batteryRuntime < EMERGENCY_RUNTIME_THRESHOLD; + + rows.push({ + name: ups.name, + battery: batteryColor(status.batteryCapacity + '%'), + runtime: runtimeColor(status.batteryRuntime + ' min'), + status: isCritical ? theme.error('CRITICAL!') : theme.success('OK'), + }); - // Force immediate shutdown - await this.forceImmediateShutdown(); - return; + // If any UPS battery runtime gets critically low, flag for immediate shutdown + if (isCritical && !emergencyDetected) { + emergencyDetected = true; + emergencyUps = { ups, status }; } } catch (upsError) { + rows.push({ + name: ups.name, + battery: theme.error('N/A'), + runtime: theme.error('N/A'), + status: theme.error('ERROR'), + }); + logger.error( `Error checking UPS ${ups.name} during shutdown: ${ upsError instanceof Error ? upsError.message : String(upsError) @@ -785,6 +864,27 @@ export class NupstDaemon { } } + // Display the table + logger.logTable(columns, rows); + logger.log(''); + + // If emergency detected, trigger immediate shutdown + if (emergencyDetected && emergencyUps) { + logger.log(''); + logger.logBoxTitle('EMERGENCY SHUTDOWN', 60, 'error'); + logger.logBoxLine( + `UPS ${emergencyUps.ups.name} runtime critically low: ${emergencyUps.status.batteryRuntime} minutes`, + ); + logger.logBoxLine(`Emergency threshold: ${EMERGENCY_RUNTIME_THRESHOLD} minutes`); + logger.logBoxLine('Forcing immediate shutdown!'); + logger.logBoxEnd(); + logger.log(''); + + // Force immediate shutdown + await this.forceImmediateShutdown(); + return; + } + // Wait before checking again await this.sleep(CHECK_INTERVAL); } catch (error) { @@ -797,7 +897,9 @@ export class NupstDaemon { } } - logger.log('UPS monitoring during shutdown completed'); + logger.log(''); + logger.success('UPS monitoring during shutdown completed'); + logger.log(''); } /**