171 lines
		
	
	
		
			5.4 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
		
		
			
		
	
	
			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 | ||
|  |     ); | ||
|  |   } | ||
|  | } |