feat(devicemanager): Introduce a UniversalDevice architecture with composable Feature system; add extensive new device/protocol support and discovery/refactors
This commit is contained in:
231
ts/features/feature.power.ts
Normal file
231
ts/features/feature.power.ts
Normal file
@@ -0,0 +1,231 @@
|
||||
/**
|
||||
* Power Feature
|
||||
* Provides UPS/power monitoring and control capability
|
||||
*/
|
||||
|
||||
import { Feature, type TDeviceReference } from './feature.abstract.js';
|
||||
import type {
|
||||
TPowerProtocol,
|
||||
TPowerStatus,
|
||||
IBatteryInfo,
|
||||
IPowerInfo,
|
||||
IPowerFeatureInfo,
|
||||
IFeatureOptions,
|
||||
} from '../interfaces/feature.interfaces.js';
|
||||
|
||||
/**
|
||||
* Options for creating a PowerFeature
|
||||
*/
|
||||
export interface IPowerFeatureOptions extends IFeatureOptions {
|
||||
protocol: TPowerProtocol;
|
||||
hasBattery?: boolean;
|
||||
supportsShutdown?: boolean;
|
||||
supportsTest?: boolean;
|
||||
/** For NUT protocol: UPS name */
|
||||
upsName?: string;
|
||||
/** For SNMP: community string */
|
||||
community?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Power Feature - provides UPS/power monitoring and control
|
||||
*
|
||||
* Supports NUT (Network UPS Tools) and SNMP protocols for monitoring
|
||||
* UPS devices and smart power equipment.
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* const power = device.getFeature<PowerFeature>('power');
|
||||
* if (power) {
|
||||
* const status = await power.getStatus();
|
||||
* const battery = await power.getBatteryInfo();
|
||||
* console.log(`Status: ${status}, Battery: ${battery.charge}%`);
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
export class PowerFeature extends Feature {
|
||||
public readonly type = 'power' as const;
|
||||
public readonly protocol: TPowerProtocol;
|
||||
|
||||
// Configuration
|
||||
public readonly upsName: string;
|
||||
public readonly community: string;
|
||||
|
||||
// Capabilities
|
||||
public readonly hasBattery: boolean;
|
||||
public readonly supportsShutdown: boolean;
|
||||
public readonly supportsTest: boolean;
|
||||
|
||||
// Current state
|
||||
protected _status: TPowerStatus = 'unknown';
|
||||
protected _batteryCharge: number = 0;
|
||||
protected _batteryRuntime: number = 0;
|
||||
protected _load: number = 0;
|
||||
|
||||
constructor(
|
||||
device: TDeviceReference,
|
||||
port: number,
|
||||
options: IPowerFeatureOptions
|
||||
) {
|
||||
super(device, port, options);
|
||||
this.protocol = options.protocol;
|
||||
this.upsName = options.upsName ?? 'ups';
|
||||
this.community = options.community ?? 'public';
|
||||
this.hasBattery = options.hasBattery ?? true;
|
||||
this.supportsShutdown = options.supportsShutdown ?? false;
|
||||
this.supportsTest = options.supportsTest ?? false;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Properties
|
||||
// ============================================================================
|
||||
|
||||
public get powerStatus(): TPowerStatus {
|
||||
return this._status;
|
||||
}
|
||||
|
||||
public get batteryCharge(): number {
|
||||
return this._batteryCharge;
|
||||
}
|
||||
|
||||
public get batteryRuntime(): number {
|
||||
return this._batteryRuntime;
|
||||
}
|
||||
|
||||
public get load(): number {
|
||||
return this._load;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Connection
|
||||
// ============================================================================
|
||||
|
||||
protected async doConnect(): Promise<void> {
|
||||
// Protocol-specific connection would be implemented here
|
||||
// For now, just verify the port is reachable
|
||||
}
|
||||
|
||||
protected async doDisconnect(): Promise<void> {
|
||||
// Protocol-specific disconnection
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Status Monitoring
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Get current power status
|
||||
*/
|
||||
public async getStatus(): Promise<TPowerStatus> {
|
||||
// Protocol-specific implementation would fetch status
|
||||
return this._status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get battery information
|
||||
*/
|
||||
public async getBatteryInfo(): Promise<IBatteryInfo> {
|
||||
return {
|
||||
charge: this._batteryCharge,
|
||||
runtime: this._batteryRuntime,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get power information
|
||||
*/
|
||||
public async getPowerInfo(): Promise<IPowerInfo> {
|
||||
return {
|
||||
load: this._load,
|
||||
};
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Control Commands
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Initiate shutdown
|
||||
*/
|
||||
public async shutdown(delay?: number): Promise<void> {
|
||||
if (!this.supportsShutdown) {
|
||||
throw new Error('Shutdown not supported');
|
||||
}
|
||||
this.emit('power:shutdown', { delay });
|
||||
}
|
||||
|
||||
/**
|
||||
* Start battery test
|
||||
*/
|
||||
public async testBattery(): Promise<void> {
|
||||
if (!this.supportsTest) {
|
||||
throw new Error('Battery test not supported');
|
||||
}
|
||||
this.emit('power:test:started');
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// State Updates
|
||||
// ============================================================================
|
||||
|
||||
protected updateStatus(status: TPowerStatus): void {
|
||||
const oldStatus = this._status;
|
||||
this._status = status;
|
||||
if (oldStatus !== status) {
|
||||
this.emit('power:status:changed', { oldStatus, newStatus: status });
|
||||
|
||||
// Emit specific events
|
||||
if (status === 'onbattery') {
|
||||
this.emit('power:onbattery');
|
||||
} else if (status === 'online' && oldStatus === 'onbattery') {
|
||||
this.emit('power:restored');
|
||||
} else if (status === 'lowbattery') {
|
||||
this.emit('power:lowbattery');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected updateBattery(charge: number, runtime: number): void {
|
||||
const oldCharge = this._batteryCharge;
|
||||
this._batteryCharge = charge;
|
||||
this._batteryRuntime = runtime;
|
||||
|
||||
if (oldCharge !== charge) {
|
||||
this.emit('battery:changed', { charge, runtime });
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Serialization
|
||||
// ============================================================================
|
||||
|
||||
public getFeatureInfo(): IPowerFeatureInfo {
|
||||
return {
|
||||
...this.getBaseFeatureInfo(),
|
||||
type: 'power',
|
||||
protocol: this.protocol,
|
||||
hasBattery: this.hasBattery,
|
||||
supportsShutdown: this.supportsShutdown,
|
||||
supportsTest: this.supportsTest,
|
||||
};
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Static Factory
|
||||
// ============================================================================
|
||||
|
||||
public static fromDiscovery(
|
||||
device: TDeviceReference,
|
||||
port: number,
|
||||
protocol: TPowerProtocol,
|
||||
metadata: Record<string, unknown>
|
||||
): PowerFeature {
|
||||
return new PowerFeature(device, port, {
|
||||
protocol,
|
||||
upsName: metadata.upsName as string,
|
||||
hasBattery: metadata.hasBattery as boolean ?? true,
|
||||
supportsShutdown: metadata.supportsShutdown as boolean ?? false,
|
||||
supportsTest: metadata.supportsTest as boolean ?? false,
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user