Files
nupst/ts/actions/base-action.ts

171 lines
5.4 KiB
TypeScript

/**
* Base classes and interfaces for the NUPST action system
*
* Actions are triggered on:
* 1. Power status changes (online ↔ onBattery)
* 2. Threshold violations (battery/runtime cross below configured thresholds)
*/
export type TPowerStatus = 'online' | 'onBattery' | 'unknown';
/**
* Context provided to actions when they execute
* Contains all relevant UPS state and trigger information
*/
export interface IActionContext {
// UPS identification
/** Unique ID of the UPS */
upsId: string;
/** Human-readable name of the UPS */
upsName: string;
// Current state
/** Current power status */
powerStatus: TPowerStatus;
/** Current battery capacity percentage (0-100) */
batteryCapacity: number;
/** Estimated battery runtime in minutes */
batteryRuntime: number;
// State tracking
/** Previous power status before this trigger */
previousPowerStatus: TPowerStatus;
// Metadata
/** Timestamp when this action was triggered (milliseconds since epoch) */
timestamp: number;
/** Reason this action was triggered */
triggerReason: 'powerStatusChange' | 'thresholdViolation';
}
/**
* Action trigger mode - determines when an action executes
*/
export type TActionTriggerMode =
| 'onlyPowerChanges' // Only on power status changes (online ↔ onBattery)
| 'onlyThresholds' // Only when action's thresholds are exceeded
| 'powerChangesAndThresholds' // On power changes OR threshold violations
| 'anyChange'; // On every UPS poll/check (every ~30s)
/**
* Configuration for an action
*/
export interface IActionConfig {
/** Type of action to execute */
type: 'shutdown' | 'webhook' | 'script';
// Trigger configuration
/**
* When should this action be triggered?
* - onlyPowerChanges: Only on power status changes
* - onlyThresholds: Only when thresholds exceeded
* - powerChangesAndThresholds: On both (default)
* - anyChange: On every check
*/
triggerMode?: TActionTriggerMode;
// Threshold configuration (applies to all action types)
/** Threshold settings for this action */
thresholds?: {
/** Battery percentage threshold (0-100) */
battery: number;
/** Runtime threshold in minutes */
runtime: number;
};
// Shutdown action configuration
/** Delay before shutdown in minutes (default: 5) */
shutdownDelay?: number;
/** Only execute shutdown on threshold violation, not power status changes */
onlyOnThresholdViolation?: boolean;
// Webhook action configuration
/** URL to call for webhook */
webhookUrl?: string;
/** HTTP method to use (default: POST) */
webhookMethod?: 'GET' | 'POST';
/** Timeout for webhook request in milliseconds (default: 10000) */
webhookTimeout?: number;
/** Only execute webhook on threshold violation */
webhookOnlyOnThresholdViolation?: boolean;
// Script action configuration
/** Path to script relative to /etc/nupst (e.g., "myaction.sh") */
scriptPath?: string;
/** Timeout for script execution in milliseconds (default: 60000) */
scriptTimeout?: number;
/** Only execute script on threshold violation */
scriptOnlyOnThresholdViolation?: boolean;
}
/**
* Abstract base class for all actions
* Each action type must extend this class and implement execute()
*/
export abstract class Action {
/** Type identifier for this action */
abstract readonly type: string;
/**
* Create a new action with the given configuration
* @param config Action configuration
*/
constructor(protected config: IActionConfig) {}
/**
* Execute this action with the given context
* @param context Current UPS state and trigger information
*/
abstract execute(context: IActionContext): Promise<void>;
/**
* Helper to check if this action should execute based on trigger mode
* @param context Action context with current UPS state
* @returns True if action should execute
*/
protected shouldExecute(context: IActionContext): boolean {
const mode = this.config.triggerMode || 'powerChangesAndThresholds'; // Default
switch (mode) {
case 'onlyPowerChanges':
// Only execute on power status changes
return context.triggerReason === 'powerStatusChange';
case 'onlyThresholds':
// Only execute when this action's thresholds are exceeded
if (!this.config.thresholds) return false; // No thresholds = never execute
return this.areThresholdsExceeded(context.batteryCapacity, context.batteryRuntime);
case 'powerChangesAndThresholds':
// Execute on power changes OR when thresholds exceeded
if (context.triggerReason === 'powerStatusChange') return true;
if (!this.config.thresholds) return false;
return this.areThresholdsExceeded(context.batteryCapacity, context.batteryRuntime);
case 'anyChange':
// Execute on every trigger (power change or threshold check)
return true;
default:
return true;
}
}
/**
* Check if current battery/runtime exceeds this action's thresholds
* @param batteryCapacity Current battery percentage
* @param batteryRuntime Current runtime in minutes
* @returns True if thresholds are exceeded
*/
protected areThresholdsExceeded(batteryCapacity: number, batteryRuntime: number): boolean {
if (!this.config.thresholds) {
return false; // No thresholds configured
}
return (
batteryCapacity < this.config.thresholds.battery ||
batteryRuntime < this.config.thresholds.runtime
);
}
}