Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| bf4d519428 | |||
| 579667b3cd |
@@ -1,5 +1,12 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 2026-04-14 - 5.6.0 - feat(config)
|
||||||
|
add configurable default shutdown delay for shutdown actions
|
||||||
|
|
||||||
|
- introduces a top-level defaultShutdownDelay config value used by shutdown actions that do not define their own delay
|
||||||
|
- applies the configured default during action execution, daemon-initiated shutdowns, CLI prompts, and status display output
|
||||||
|
- preserves explicit shutdownDelay values including 0 minutes and normalizes invalid config values back to the built-in default
|
||||||
|
|
||||||
## 2026-04-14 - 5.5.1 - fix(cli,daemon,snmp)
|
## 2026-04-14 - 5.5.1 - fix(cli,daemon,snmp)
|
||||||
normalize CLI argument parsing and extract daemon monitoring helpers with stronger SNMP typing
|
normalize CLI argument parsing and extract daemon monitoring helpers with stronger SNMP typing
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@serve.zone/nupst",
|
"name": "@serve.zone/nupst",
|
||||||
"version": "5.5.1",
|
"version": "5.6.0",
|
||||||
"exports": "./mod.ts",
|
"exports": "./mod.ts",
|
||||||
"nodeModulesDir": "auto",
|
"nodeModulesDir": "auto",
|
||||||
"tasks": {
|
"tasks": {
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@serve.zone/nupst",
|
"name": "@serve.zone/nupst",
|
||||||
"version": "5.5.1",
|
"version": "5.6.0",
|
||||||
"description": "Network UPS Shutdown Tool - Monitor SNMP-enabled UPS devices and orchestrate graceful system shutdowns during power emergencies",
|
"description": "Network UPS Shutdown Tool - Monitor SNMP-enabled UPS devices and orchestrate graceful system shutdowns during power emergencies",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"ups",
|
"ups",
|
||||||
|
|||||||
@@ -75,6 +75,8 @@
|
|||||||
shutdowns
|
shutdowns
|
||||||
- `ts/daemon.ts` now delegates OS shutdown execution instead of embedding command lookup logic
|
- `ts/daemon.ts` now delegates OS shutdown execution instead of embedding command lookup logic
|
||||||
inline
|
inline
|
||||||
|
- `defaultShutdownDelay` in config provides the inherited delay for shutdown actions without an
|
||||||
|
explicit `shutdownDelay` override
|
||||||
|
|
||||||
### Config Watch Handling
|
### Config Watch Handling
|
||||||
|
|
||||||
|
|||||||
@@ -219,12 +219,16 @@ nupst uninstall # Completely remove NUPST (requires root)
|
|||||||
|
|
||||||
NUPST stores configuration at `/etc/nupst/config.json`. The easiest way to configure is through the interactive CLI commands, but you can also edit the JSON directly.
|
NUPST stores configuration at `/etc/nupst/config.json`. The easiest way to configure is through the interactive CLI commands, but you can also edit the JSON directly.
|
||||||
|
|
||||||
|
`defaultShutdownDelay` sets the inherited delay in minutes for shutdown actions that do not define
|
||||||
|
their own `shutdownDelay`.
|
||||||
|
|
||||||
### Example Configuration
|
### Example Configuration
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"version": "4.3",
|
"version": "4.3",
|
||||||
"checkInterval": 30000,
|
"checkInterval": 30000,
|
||||||
|
"defaultShutdownDelay": 5,
|
||||||
"httpServer": {
|
"httpServer": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"port": 8080,
|
"port": 8080,
|
||||||
@@ -395,7 +399,7 @@ Actions define automated responses to UPS conditions. They run **sequentially in
|
|||||||
|
|
||||||
| Field | Description | Default |
|
| Field | Description | Default |
|
||||||
| --------------- | ---------------------------------- | ------- |
|
| --------------- | ---------------------------------- | ------- |
|
||||||
| `shutdownDelay` | Minutes to wait before shutdown | `5` |
|
| `shutdownDelay` | Minutes to wait before shutdown | Inherits `defaultShutdownDelay` (`5`) |
|
||||||
|
|
||||||
#### Webhook Action
|
#### Webhook Action
|
||||||
|
|
||||||
@@ -610,16 +614,16 @@ UPS Devices (2):
|
|||||||
Host: 192.168.1.100:161 (SNMP)
|
Host: 192.168.1.100:161 (SNMP)
|
||||||
Groups: Data Center
|
Groups: Data Center
|
||||||
Action: proxmox (onlyThresholds: battery<30%, runtime<15min)
|
Action: proxmox (onlyThresholds: battery<30%, runtime<15min)
|
||||||
Action: shutdown (onlyThresholds: battery<20%, runtime<10min, delay=10s)
|
Action: shutdown (onlyThresholds: battery<20%, runtime<10min, delay=10min)
|
||||||
|
|
||||||
✓ Local USB UPS (online - 95%, 2400min)
|
✓ Local USB UPS (online - 95%, 2400min)
|
||||||
Host: 127.0.0.1:3493 (UPSD)
|
Host: 127.0.0.1:3493 (UPSD)
|
||||||
Action: shutdown (onlyThresholds: battery<15%, runtime<5min, delay=5s)
|
Action: shutdown (onlyThresholds: battery<15%, runtime<5min, delay=5min)
|
||||||
|
|
||||||
Groups (1):
|
Groups (1):
|
||||||
ℹ Data Center (redundant)
|
ℹ Data Center (redundant)
|
||||||
UPS Devices (1): Main Server UPS
|
UPS Devices (1): Main Server UPS
|
||||||
Action: shutdown (onlyThresholds: battery<10%, runtime<5min, delay=15s)
|
Action: shutdown (onlyThresholds: battery<10%, runtime<5min, delay=15min)
|
||||||
```
|
```
|
||||||
|
|
||||||
### Live Logs
|
### Live Logs
|
||||||
|
|||||||
+21
-1
@@ -11,7 +11,11 @@ import { type IPauseState, loadPauseSnapshot } from '../ts/pause-state.ts';
|
|||||||
import { shortId } from '../ts/helpers/shortid.ts';
|
import { shortId } from '../ts/helpers/shortid.ts';
|
||||||
import { HTTP_SERVER, SNMP, THRESHOLDS, TIMING, UI } from '../ts/constants.ts';
|
import { HTTP_SERVER, SNMP, THRESHOLDS, TIMING, UI } from '../ts/constants.ts';
|
||||||
import { Action, type IActionContext } from '../ts/actions/base-action.ts';
|
import { Action, type IActionContext } from '../ts/actions/base-action.ts';
|
||||||
import { buildUpsActionContext, decideUpsActionExecution } from '../ts/action-orchestration.ts';
|
import {
|
||||||
|
applyDefaultShutdownDelay,
|
||||||
|
buildUpsActionContext,
|
||||||
|
decideUpsActionExecution,
|
||||||
|
} from '../ts/action-orchestration.ts';
|
||||||
import {
|
import {
|
||||||
buildShutdownErrorRow,
|
buildShutdownErrorRow,
|
||||||
buildShutdownStatusRow,
|
buildShutdownStatusRow,
|
||||||
@@ -353,6 +357,22 @@ Deno.test('decideUpsActionExecution: returns executable action plan when actions
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Deno.test('applyDefaultShutdownDelay: applies only to shutdown actions without explicit delay', () => {
|
||||||
|
const actions = [
|
||||||
|
{ type: 'shutdown' as const },
|
||||||
|
{ type: 'shutdown' as const, shutdownDelay: 0 },
|
||||||
|
{ type: 'shutdown' as const, shutdownDelay: 9 },
|
||||||
|
{ type: 'webhook' as const },
|
||||||
|
];
|
||||||
|
|
||||||
|
assertEquals(applyDefaultShutdownDelay(actions, 7), [
|
||||||
|
{ type: 'shutdown', shutdownDelay: 7 },
|
||||||
|
{ type: 'shutdown', shutdownDelay: 0 },
|
||||||
|
{ type: 'shutdown', shutdownDelay: 9 },
|
||||||
|
{ type: 'webhook' },
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// Shutdown Monitoring Tests
|
// Shutdown Monitoring Tests
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -3,6 +3,6 @@
|
|||||||
*/
|
*/
|
||||||
export const commitinfo = {
|
export const commitinfo = {
|
||||||
name: '@serve.zone/nupst',
|
name: '@serve.zone/nupst',
|
||||||
version: '5.5.1',
|
version: '5.6.0',
|
||||||
description: 'Network UPS Shutdown Tool - Monitor SNMP-enabled UPS devices and orchestrate graceful system shutdowns during power emergencies'
|
description: 'Network UPS Shutdown Tool - Monitor SNMP-enabled UPS devices and orchestrate graceful system shutdowns during power emergencies'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,6 +34,22 @@ export function buildUpsActionContext(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function applyDefaultShutdownDelay(
|
||||||
|
actions: IActionConfig[],
|
||||||
|
defaultDelayMinutes: number,
|
||||||
|
): IActionConfig[] {
|
||||||
|
return actions.map((action) => {
|
||||||
|
if (action.type !== 'shutdown' || action.shutdownDelay !== undefined) {
|
||||||
|
return action;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...action,
|
||||||
|
shutdownDelay: defaultDelayMinutes,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export function decideUpsActionExecution(
|
export function decideUpsActionExecution(
|
||||||
isPaused: boolean,
|
isPaused: boolean,
|
||||||
ups: IUpsActionSource,
|
ups: IUpsActionSource,
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ export interface IActionConfig {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Shutdown action configuration
|
// Shutdown action configuration
|
||||||
/** Delay before shutdown in minutes (default: 5) */
|
/** Delay before shutdown in minutes (defaults to the config-level shutdown delay, or 5) */
|
||||||
shutdownDelay?: number;
|
shutdownDelay?: number;
|
||||||
/** Only execute shutdown on threshold violation, not power status changes */
|
/** Only execute shutdown on threshold violation, not power status changes */
|
||||||
onlyOnThresholdViolation?: boolean;
|
onlyOnThresholdViolation?: boolean;
|
||||||
|
|||||||
@@ -124,7 +124,7 @@ export class ShutdownAction extends Action {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const shutdownDelay = this.config.shutdownDelay || SHUTDOWN.DEFAULT_DELAY_MINUTES;
|
const shutdownDelay = this.config.shutdownDelay ?? SHUTDOWN.DEFAULT_DELAY_MINUTES;
|
||||||
|
|
||||||
logger.log('');
|
logger.log('');
|
||||||
logger.logBoxTitle('Initiating System Shutdown', UI.WIDE_BOX_WIDTH, 'error');
|
logger.logBoxTitle('Initiating System Shutdown', UI.WIDE_BOX_WIDTH, 'error');
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { type ITableColumn, logger } from '../logger.ts';
|
|||||||
import { symbols, theme } from '../colors.ts';
|
import { symbols, theme } from '../colors.ts';
|
||||||
import type { IActionConfig } from '../actions/base-action.ts';
|
import type { IActionConfig } from '../actions/base-action.ts';
|
||||||
import { ProxmoxAction } from '../actions/proxmox-action.ts';
|
import { ProxmoxAction } from '../actions/proxmox-action.ts';
|
||||||
|
import { SHUTDOWN } from '../constants.ts';
|
||||||
import type { IGroupConfig, IUpsConfig } from '../daemon.ts';
|
import type { IGroupConfig, IUpsConfig } from '../daemon.ts';
|
||||||
import * as helpers from '../helpers/index.ts';
|
import * as helpers from '../helpers/index.ts';
|
||||||
|
|
||||||
@@ -81,16 +82,20 @@ export class ActionHandler {
|
|||||||
if (typeValue === 1) {
|
if (typeValue === 1) {
|
||||||
// Shutdown action
|
// Shutdown action
|
||||||
newAction.type = 'shutdown';
|
newAction.type = 'shutdown';
|
||||||
|
const defaultShutdownDelay =
|
||||||
|
this.nupst.getDaemon().getConfig().defaultShutdownDelay ?? SHUTDOWN.DEFAULT_DELAY_MINUTES;
|
||||||
|
|
||||||
const delayStr = await prompt(
|
const delayStr = await prompt(
|
||||||
` ${theme.dim('Shutdown delay')} ${theme.dim('(minutes) [5]:')} `,
|
` ${theme.dim('Shutdown delay')} ${theme.dim(`(minutes, leave empty for default ${defaultShutdownDelay}):`)} `,
|
||||||
);
|
);
|
||||||
const shutdownDelay = delayStr ? parseInt(delayStr, 10) : 5;
|
if (delayStr.trim()) {
|
||||||
if (isNaN(shutdownDelay) || shutdownDelay < 0) {
|
const shutdownDelay = parseInt(delayStr, 10);
|
||||||
logger.error('Invalid shutdown delay. Must be >= 0.');
|
if (isNaN(shutdownDelay) || shutdownDelay < 0) {
|
||||||
process.exit(1);
|
logger.error('Invalid shutdown delay. Must be >= 0.');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
newAction.shutdownDelay = shutdownDelay;
|
||||||
}
|
}
|
||||||
newAction.shutdownDelay = shutdownDelay;
|
|
||||||
} else if (typeValue === 2) {
|
} else if (typeValue === 2) {
|
||||||
// Webhook action
|
// Webhook action
|
||||||
newAction.type = 'webhook';
|
newAction.type = 'webhook';
|
||||||
@@ -468,7 +473,9 @@ export class ActionHandler {
|
|||||||
];
|
];
|
||||||
|
|
||||||
const rows = target.actions.map((action, index) => {
|
const rows = target.actions.map((action, index) => {
|
||||||
let details = `${action.shutdownDelay || 5}min delay`;
|
const defaultShutdownDelay =
|
||||||
|
this.nupst.getDaemon().getConfig().defaultShutdownDelay ?? SHUTDOWN.DEFAULT_DELAY_MINUTES;
|
||||||
|
let details = `${action.shutdownDelay ?? defaultShutdownDelay}min delay`;
|
||||||
if (action.type === 'proxmox') {
|
if (action.type === 'proxmox') {
|
||||||
const mode = action.proxmoxMode || 'auto';
|
const mode = action.proxmoxMode || 'auto';
|
||||||
if (mode === 'cli' || (mode === 'auto' && !action.proxmoxTokenId)) {
|
if (mode === 'cli' || (mode === 'auto' && !action.proxmoxTokenId)) {
|
||||||
|
|||||||
+13
-5
@@ -10,7 +10,7 @@ import type { TProtocol } from '../protocol/types.ts';
|
|||||||
import type { INupstConfig, IUpsConfig } from '../daemon.ts';
|
import type { INupstConfig, IUpsConfig } from '../daemon.ts';
|
||||||
import type { IActionConfig } from '../actions/base-action.ts';
|
import type { IActionConfig } from '../actions/base-action.ts';
|
||||||
import { ProxmoxAction } from '../actions/proxmox-action.ts';
|
import { ProxmoxAction } from '../actions/proxmox-action.ts';
|
||||||
import { UPSD } from '../constants.ts';
|
import { SHUTDOWN, UPSD } from '../constants.ts';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Thresholds configuration for CLI display
|
* Thresholds configuration for CLI display
|
||||||
@@ -1152,11 +1152,19 @@ export class UpsHandler {
|
|||||||
if (typeValue === 1) {
|
if (typeValue === 1) {
|
||||||
// Shutdown action
|
// Shutdown action
|
||||||
action.type = 'shutdown';
|
action.type = 'shutdown';
|
||||||
|
const defaultShutdownDelay =
|
||||||
|
this.nupst.getDaemon().getConfig().defaultShutdownDelay ?? SHUTDOWN.DEFAULT_DELAY_MINUTES;
|
||||||
|
|
||||||
const delayInput = await prompt('Shutdown delay in minutes [5]: ');
|
const delayInput = await prompt(
|
||||||
const delay = parseInt(delayInput, 10);
|
`Shutdown delay in minutes (leave empty for default ${defaultShutdownDelay}): `,
|
||||||
if (delayInput.trim() && !isNaN(delay)) {
|
);
|
||||||
action.shutdownDelay = delay;
|
if (delayInput.trim()) {
|
||||||
|
const delay = parseInt(delayInput, 10);
|
||||||
|
if (isNaN(delay) || delay < 0) {
|
||||||
|
logger.warn('Invalid shutdown delay, using configured default');
|
||||||
|
} else {
|
||||||
|
action.shutdownDelay = delay;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if (typeValue === 2) {
|
} else if (typeValue === 2) {
|
||||||
// Webhook action
|
// Webhook action
|
||||||
|
|||||||
+39
-9
@@ -12,9 +12,13 @@ import { MigrationRunner } from './migrations/index.ts';
|
|||||||
import { formatPowerStatus, getBatteryColor, getRuntimeColor, theme } from './colors.ts';
|
import { formatPowerStatus, getBatteryColor, getRuntimeColor, theme } from './colors.ts';
|
||||||
import type { IActionConfig } from './actions/base-action.ts';
|
import type { IActionConfig } from './actions/base-action.ts';
|
||||||
import { ActionManager } from './actions/index.ts';
|
import { ActionManager } from './actions/index.ts';
|
||||||
import { decideUpsActionExecution, type TUpsTriggerReason } from './action-orchestration.ts';
|
import {
|
||||||
|
applyDefaultShutdownDelay,
|
||||||
|
decideUpsActionExecution,
|
||||||
|
type TUpsTriggerReason,
|
||||||
|
} from './action-orchestration.ts';
|
||||||
import { NupstHttpServer } from './http-server.ts';
|
import { NupstHttpServer } from './http-server.ts';
|
||||||
import { NETWORK, PAUSE, THRESHOLDS, TIMING, UI } from './constants.ts';
|
import { NETWORK, PAUSE, SHUTDOWN, THRESHOLDS, TIMING, UI } from './constants.ts';
|
||||||
import {
|
import {
|
||||||
analyzeConfigReload,
|
analyzeConfigReload,
|
||||||
shouldRefreshPauseState,
|
shouldRefreshPauseState,
|
||||||
@@ -97,6 +101,8 @@ export interface INupstConfig {
|
|||||||
groups: IGroupConfig[];
|
groups: IGroupConfig[];
|
||||||
/** Check interval in milliseconds */
|
/** Check interval in milliseconds */
|
||||||
checkInterval: number;
|
checkInterval: number;
|
||||||
|
/** Default delay in minutes for shutdown actions without an override */
|
||||||
|
defaultShutdownDelay?: number;
|
||||||
/** HTTP Server configuration */
|
/** HTTP Server configuration */
|
||||||
httpServer?: IHttpServerConfig;
|
httpServer?: IHttpServerConfig;
|
||||||
|
|
||||||
@@ -125,6 +131,7 @@ export class NupstDaemon {
|
|||||||
/** Default configuration */
|
/** Default configuration */
|
||||||
private readonly DEFAULT_CONFIG: INupstConfig = {
|
private readonly DEFAULT_CONFIG: INupstConfig = {
|
||||||
version: '4.3',
|
version: '4.3',
|
||||||
|
defaultShutdownDelay: SHUTDOWN.DEFAULT_DELAY_MINUTES,
|
||||||
upsDevices: [
|
upsDevices: [
|
||||||
{
|
{
|
||||||
id: 'default',
|
id: 'default',
|
||||||
@@ -155,7 +162,6 @@ export class NupstDaemon {
|
|||||||
battery: THRESHOLDS.DEFAULT_BATTERY_PERCENT, // Shutdown when battery below 60%
|
battery: THRESHOLDS.DEFAULT_BATTERY_PERCENT, // Shutdown when battery below 60%
|
||||||
runtime: THRESHOLDS.DEFAULT_RUNTIME_MINUTES, // Shutdown when runtime below 20 minutes
|
runtime: THRESHOLDS.DEFAULT_RUNTIME_MINUTES, // Shutdown when runtime below 20 minutes
|
||||||
},
|
},
|
||||||
shutdownDelay: 5,
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@@ -208,10 +214,13 @@ export class NupstDaemon {
|
|||||||
const migrationRunner = new MigrationRunner();
|
const migrationRunner = new MigrationRunner();
|
||||||
const { config: migratedConfig, migrated } = await migrationRunner.run(parsedConfig);
|
const { config: migratedConfig, migrated } = await migrationRunner.run(parsedConfig);
|
||||||
|
|
||||||
// Save migrated config back to disk if any migrations ran
|
// Save migrated or normalized config back to disk when needed.
|
||||||
// Cast to INupstConfig since migrations ensure the output is valid
|
// Cast to INupstConfig since migrations ensure the output is valid.
|
||||||
const validConfig = migratedConfig as unknown as INupstConfig;
|
const validConfig = migratedConfig as unknown as INupstConfig;
|
||||||
if (migrated) {
|
const normalizedShutdownDelay = this.normalizeShutdownDelay(validConfig.defaultShutdownDelay);
|
||||||
|
const shouldPersistNormalizedConfig = validConfig.defaultShutdownDelay !== normalizedShutdownDelay;
|
||||||
|
validConfig.defaultShutdownDelay = normalizedShutdownDelay;
|
||||||
|
if (migrated || shouldPersistNormalizedConfig) {
|
||||||
this.config = validConfig;
|
this.config = validConfig;
|
||||||
await this.saveConfig(this.config);
|
await this.saveConfig(this.config);
|
||||||
} else {
|
} else {
|
||||||
@@ -249,6 +258,7 @@ export class NupstDaemon {
|
|||||||
upsDevices: config.upsDevices,
|
upsDevices: config.upsDevices,
|
||||||
groups: config.groups,
|
groups: config.groups,
|
||||||
checkInterval: config.checkInterval,
|
checkInterval: config.checkInterval,
|
||||||
|
defaultShutdownDelay: this.normalizeShutdownDelay(config.defaultShutdownDelay),
|
||||||
...(config.httpServer ? { httpServer: config.httpServer } : {}),
|
...(config.httpServer ? { httpServer: config.httpServer } : {}),
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -280,6 +290,22 @@ export class NupstDaemon {
|
|||||||
return this.config;
|
return this.config;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private normalizeShutdownDelay(delayMinutes: number | undefined): number {
|
||||||
|
if (
|
||||||
|
typeof delayMinutes !== 'number' ||
|
||||||
|
!Number.isFinite(delayMinutes) ||
|
||||||
|
delayMinutes < 0
|
||||||
|
) {
|
||||||
|
return SHUTDOWN.DEFAULT_DELAY_MINUTES;
|
||||||
|
}
|
||||||
|
|
||||||
|
return delayMinutes;
|
||||||
|
}
|
||||||
|
|
||||||
|
private getDefaultShutdownDelayMinutes(): number {
|
||||||
|
return this.normalizeShutdownDelay(this.config.defaultShutdownDelay);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the SNMP instance
|
* Get the SNMP instance
|
||||||
*/
|
*/
|
||||||
@@ -758,7 +784,12 @@ export class NupstDaemon {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await ActionManager.executeActions(decision.actions, decision.context);
|
const actions = applyDefaultShutdownDelay(
|
||||||
|
decision.actions,
|
||||||
|
this.getDefaultShutdownDelayMinutes(),
|
||||||
|
);
|
||||||
|
|
||||||
|
await ActionManager.executeActions(actions, decision.context);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -768,8 +799,7 @@ export class NupstDaemon {
|
|||||||
public async initiateShutdown(reason: string): Promise<void> {
|
public async initiateShutdown(reason: string): Promise<void> {
|
||||||
logger.log(`Initiating system shutdown due to: ${reason}`);
|
logger.log(`Initiating system shutdown due to: ${reason}`);
|
||||||
|
|
||||||
// Set a longer delay for shutdown to allow VMs and services to close
|
const shutdownDelayMinutes = this.getDefaultShutdownDelayMinutes();
|
||||||
const shutdownDelayMinutes = 5;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await this.shutdownExecutor.scheduleShutdown(shutdownDelayMinutes);
|
await this.shutdownExecutor.scheduleShutdown(shutdownDelayMinutes);
|
||||||
|
|||||||
@@ -37,8 +37,7 @@ import { logger } from '../logger.ts';
|
|||||||
* {
|
* {
|
||||||
* type: "shutdown",
|
* type: "shutdown",
|
||||||
* thresholds: { battery: 60, runtime: 20 },
|
* thresholds: { battery: 60, runtime: 20 },
|
||||||
* triggerMode: "onlyThresholds",
|
* triggerMode: "onlyThresholds"
|
||||||
* shutdownDelay: 5
|
|
||||||
* }
|
* }
|
||||||
* ]
|
* ]
|
||||||
* }
|
* }
|
||||||
@@ -93,7 +92,6 @@ export class MigrationV4_0ToV4_1 extends BaseMigration {
|
|||||||
runtime: deviceThresholds.runtime,
|
runtime: deviceThresholds.runtime,
|
||||||
},
|
},
|
||||||
triggerMode: 'onlyThresholds', // Preserve old behavior (only on threshold violation)
|
triggerMode: 'onlyThresholds', // Preserve old behavior (only on threshold violation)
|
||||||
shutdownDelay: 5, // Default delay
|
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
logger.dim(
|
logger.dim(
|
||||||
|
|||||||
+16
-9
@@ -5,6 +5,7 @@ import { type IUpsConfig, NupstDaemon } from './daemon.ts';
|
|||||||
import { NupstSnmp } from './snmp/manager.ts';
|
import { NupstSnmp } from './snmp/manager.ts';
|
||||||
import { logger } from './logger.ts';
|
import { logger } from './logger.ts';
|
||||||
import { formatPowerStatus, getBatteryColor, getRuntimeColor, symbols, theme } from './colors.ts';
|
import { formatPowerStatus, getBatteryColor, getRuntimeColor, symbols, theme } from './colors.ts';
|
||||||
|
import { SHUTDOWN } from './constants.ts';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class for managing systemd service
|
* Class for managing systemd service
|
||||||
@@ -316,7 +317,6 @@ WantedBy=multi-user.target
|
|||||||
type: 'shutdown',
|
type: 'shutdown',
|
||||||
thresholds: config.thresholds,
|
thresholds: config.thresholds,
|
||||||
triggerMode: 'onlyThresholds',
|
triggerMode: 'onlyThresholds',
|
||||||
shutdownDelay: 5,
|
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
: [],
|
: [],
|
||||||
@@ -346,6 +346,8 @@ WantedBy=multi-user.target
|
|||||||
*/
|
*/
|
||||||
private async displaySingleUpsStatus(ups: IUpsConfig, snmp: NupstSnmp): Promise<void> {
|
private async displaySingleUpsStatus(ups: IUpsConfig, snmp: NupstSnmp): Promise<void> {
|
||||||
try {
|
try {
|
||||||
|
const defaultShutdownDelay =
|
||||||
|
this.daemon.getConfig().defaultShutdownDelay ?? SHUTDOWN.DEFAULT_DELAY_MINUTES;
|
||||||
const protocol = ups.protocol || 'snmp';
|
const protocol = ups.protocol || 'snmp';
|
||||||
let status;
|
let status;
|
||||||
|
|
||||||
@@ -432,14 +434,16 @@ WantedBy=multi-user.target
|
|||||||
actionDesc += ` (${
|
actionDesc += ` (${
|
||||||
action.triggerMode || 'onlyThresholds'
|
action.triggerMode || 'onlyThresholds'
|
||||||
}: battery<${action.thresholds.battery}%, runtime<${action.thresholds.runtime}min`;
|
}: battery<${action.thresholds.battery}%, runtime<${action.thresholds.runtime}min`;
|
||||||
if (action.shutdownDelay) {
|
if (action.type === 'shutdown') {
|
||||||
actionDesc += `, delay=${action.shutdownDelay}s`;
|
const shutdownDelay = action.shutdownDelay ?? defaultShutdownDelay;
|
||||||
|
actionDesc += `, delay=${shutdownDelay}min`;
|
||||||
}
|
}
|
||||||
actionDesc += ')';
|
actionDesc += ')';
|
||||||
} else {
|
} else {
|
||||||
actionDesc += ` (${action.triggerMode || 'onlyPowerChanges'}`;
|
actionDesc += ` (${action.triggerMode || 'onlyPowerChanges'}`;
|
||||||
if (action.shutdownDelay) {
|
if (action.type === 'shutdown') {
|
||||||
actionDesc += `, delay=${action.shutdownDelay}s`;
|
const shutdownDelay = action.shutdownDelay ?? defaultShutdownDelay;
|
||||||
|
actionDesc += `, delay=${shutdownDelay}min`;
|
||||||
}
|
}
|
||||||
actionDesc += ')';
|
actionDesc += ')';
|
||||||
}
|
}
|
||||||
@@ -506,20 +510,23 @@ WantedBy=multi-user.target
|
|||||||
|
|
||||||
// Display actions if any
|
// Display actions if any
|
||||||
if (group.actions && group.actions.length > 0) {
|
if (group.actions && group.actions.length > 0) {
|
||||||
|
const defaultShutdownDelay = config.defaultShutdownDelay ?? SHUTDOWN.DEFAULT_DELAY_MINUTES;
|
||||||
for (const action of group.actions) {
|
for (const action of group.actions) {
|
||||||
let actionDesc = `${action.type}`;
|
let actionDesc = `${action.type}`;
|
||||||
if (action.thresholds) {
|
if (action.thresholds) {
|
||||||
actionDesc += ` (${
|
actionDesc += ` (${
|
||||||
action.triggerMode || 'onlyThresholds'
|
action.triggerMode || 'onlyThresholds'
|
||||||
}: battery<${action.thresholds.battery}%, runtime<${action.thresholds.runtime}min`;
|
}: battery<${action.thresholds.battery}%, runtime<${action.thresholds.runtime}min`;
|
||||||
if (action.shutdownDelay) {
|
if (action.type === 'shutdown') {
|
||||||
actionDesc += `, delay=${action.shutdownDelay}s`;
|
const shutdownDelay = action.shutdownDelay ?? defaultShutdownDelay;
|
||||||
|
actionDesc += `, delay=${shutdownDelay}min`;
|
||||||
}
|
}
|
||||||
actionDesc += ')';
|
actionDesc += ')';
|
||||||
} else {
|
} else {
|
||||||
actionDesc += ` (${action.triggerMode || 'onlyPowerChanges'}`;
|
actionDesc += ` (${action.triggerMode || 'onlyPowerChanges'}`;
|
||||||
if (action.shutdownDelay) {
|
if (action.type === 'shutdown') {
|
||||||
actionDesc += `, delay=${action.shutdownDelay}s`;
|
const shutdownDelay = action.shutdownDelay ?? defaultShutdownDelay;
|
||||||
|
actionDesc += `, delay=${shutdownDelay}min`;
|
||||||
}
|
}
|
||||||
actionDesc += ')';
|
actionDesc += ')';
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user