import { BaseMigration } from './base-migration.ts'; import { logger } from '../logger.ts'; /** * Migration from v4.0 to v4.1 * * Major changes: * 1. Moves thresholds from UPS level to action level * 2. Creates default shutdown action for UPS devices that had thresholds * 3. Adds empty actions array to UPS devices without actions * 4. Adds empty actions array to groups * * Transforms v4.0 format (with UPS-level thresholds): * { * version: "4.0", * upsDevices: [ * { * id: "ups-1", * name: "UPS 1", * snmp: {...}, * thresholds: { battery: 60, runtime: 20 }, // UPS-level * groups: [] * } * ] * } * * To v4.1 format (with action-level thresholds): * { * version: "4.1", * upsDevices: [ * { * id: "ups-1", * name: "UPS 1", * snmp: {...}, * groups: [], * actions: [ // Thresholds moved here * { * type: "shutdown", * thresholds: { battery: 60, runtime: 20 }, * triggerMode: "onlyThresholds", * shutdownDelay: 5 * } * ] * } * ] * } */ export class MigrationV4_0ToV4_1 extends BaseMigration { readonly fromVersion = '4.0'; readonly toVersion = '4.1'; async shouldRun(config: Record): Promise { // Run if config is version 4.0 if (config.version === '4.0') { return true; } // Also run if config has upsDevices with thresholds at UPS level (v4.0 format) if (Array.isArray(config.upsDevices) && config.upsDevices.length > 0) { const firstDevice = config.upsDevices[0] as Record; // v4.0 has thresholds at UPS level, v4.1 has them in actions return firstDevice.thresholds !== undefined; } return false; } async migrate(config: Record): Promise> { logger.info(`${this.getName()}: Migrating v4.0 config to v4.1 format...`); logger.dim(` - Moving thresholds from UPS level to action level`); logger.dim(` - Creating default shutdown actions from existing thresholds`); // Migrate UPS devices const devices = (config.upsDevices as Array>) || []; const migratedDevices = devices.map((device) => { const migrated: Record = { id: device.id, name: device.name, snmp: device.snmp, groups: device.groups || [], }; // If device has thresholds at UPS level, convert to shutdown action const deviceThresholds = device.thresholds as { battery: number; runtime: number } | undefined; if (deviceThresholds) { migrated.actions = [ { type: 'shutdown', thresholds: { battery: deviceThresholds.battery, runtime: deviceThresholds.runtime, }, triggerMode: 'onlyThresholds', // Preserve old behavior (only on threshold violation) shutdownDelay: 5, // Default delay }, ]; logger.dim( ` → ${device.name}: Created shutdown action (battery: ${deviceThresholds.battery}%, runtime: ${deviceThresholds.runtime}min)`, ); } else { // No thresholds, just add empty actions array migrated.actions = device.actions || []; } return migrated; }); // Add actions to groups const groups = (config.groups as Array>) || []; const migratedGroups = groups.map((group) => ({ ...group, actions: group.actions || [], })); const result = { version: this.toVersion, upsDevices: migratedDevices, groups: migratedGroups, checkInterval: config.checkInterval || 30000, }; logger.success( `${this.getName()}: Migration complete (${migratedDevices.length} devices, ${migratedGroups.length} groups updated)`, ); return result; } }