232 lines
6.3 KiB
TypeScript
232 lines
6.3 KiB
TypeScript
|
|
/**
|
||
|
|
* 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,
|
||
|
|
});
|
||
|
|
}
|
||
|
|
}
|