feat(unifi): implement comprehensive UniFi API client with controllers, protect, access, account, managers, resources, HTTP client, interfaces, logging, plugins, and tests
This commit is contained in:
255
ts/classes.device.ts
Normal file
255
ts/classes.device.ts
Normal file
@@ -0,0 +1,255 @@
|
||||
import type { UnifiController } from './classes.unifi-controller.js';
|
||||
import type { INetworkDevice, IPortConfig } from './interfaces/index.js';
|
||||
|
||||
/**
|
||||
* Represents a UniFi network device (AP, switch, gateway, etc.)
|
||||
*/
|
||||
export class UnifiDevice implements INetworkDevice {
|
||||
/** Reference to parent controller */
|
||||
private controller?: UnifiController;
|
||||
/** Site ID for API calls */
|
||||
private siteId?: string;
|
||||
|
||||
// INetworkDevice properties
|
||||
public _id: string;
|
||||
public mac: string;
|
||||
public model: string;
|
||||
public type: string;
|
||||
public name?: string;
|
||||
public site_id: string;
|
||||
public adopted: boolean;
|
||||
public ip: string;
|
||||
public state: number;
|
||||
public serial?: string;
|
||||
public version?: string;
|
||||
public uptime?: number;
|
||||
public last_seen?: number;
|
||||
public upgradable?: boolean;
|
||||
public upgrade_to_firmware?: string;
|
||||
public config_network?: {
|
||||
type?: string;
|
||||
ip?: string;
|
||||
};
|
||||
public ethernet_table?: Array<{
|
||||
name: string;
|
||||
mac: string;
|
||||
num_port?: number;
|
||||
}>;
|
||||
public port_overrides?: Array<{
|
||||
port_idx: number;
|
||||
name?: string;
|
||||
poe_mode?: string;
|
||||
}>;
|
||||
public sys_stats?: {
|
||||
loadavg_1?: number;
|
||||
loadavg_5?: number;
|
||||
loadavg_15?: number;
|
||||
mem_total?: number;
|
||||
mem_used?: number;
|
||||
};
|
||||
public led_override?: string;
|
||||
public led_override_color?: string;
|
||||
public led_override_color_brightness?: number;
|
||||
|
||||
constructor() {
|
||||
this._id = '';
|
||||
this.mac = '';
|
||||
this.model = '';
|
||||
this.type = '';
|
||||
this.site_id = '';
|
||||
this.adopted = false;
|
||||
this.ip = '';
|
||||
this.state = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a device instance from API response object
|
||||
*/
|
||||
public static createFromApiObject(
|
||||
apiObject: INetworkDevice,
|
||||
controller?: UnifiController,
|
||||
siteId?: string
|
||||
): UnifiDevice {
|
||||
const device = new UnifiDevice();
|
||||
Object.assign(device, apiObject);
|
||||
device.controller = controller;
|
||||
device.siteId = siteId || apiObject.site_id;
|
||||
return device;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the raw API object representation
|
||||
*/
|
||||
public toApiObject(): INetworkDevice {
|
||||
return {
|
||||
_id: this._id,
|
||||
mac: this.mac,
|
||||
model: this.model,
|
||||
type: this.type,
|
||||
name: this.name,
|
||||
site_id: this.site_id,
|
||||
adopted: this.adopted,
|
||||
ip: this.ip,
|
||||
state: this.state,
|
||||
serial: this.serial,
|
||||
version: this.version,
|
||||
uptime: this.uptime,
|
||||
last_seen: this.last_seen,
|
||||
upgradable: this.upgradable,
|
||||
upgrade_to_firmware: this.upgrade_to_firmware,
|
||||
config_network: this.config_network,
|
||||
ethernet_table: this.ethernet_table,
|
||||
port_overrides: this.port_overrides,
|
||||
sys_stats: this.sys_stats,
|
||||
led_override: this.led_override,
|
||||
led_override_color: this.led_override_color,
|
||||
led_override_color_brightness: this.led_override_color_brightness,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if device is online (state 1 = connected)
|
||||
*/
|
||||
public isOnline(): boolean {
|
||||
return this.state === 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if device is an access point
|
||||
*/
|
||||
public isAccessPoint(): boolean {
|
||||
return this.type === 'uap';
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if device is a switch
|
||||
*/
|
||||
public isSwitch(): boolean {
|
||||
return this.type === 'usw';
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if device is a gateway/router
|
||||
*/
|
||||
public isGateway(): boolean {
|
||||
return this.type === 'ugw' || this.type === 'udm';
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if device has available firmware upgrade
|
||||
*/
|
||||
public hasUpgrade(): boolean {
|
||||
return this.upgradable === true && !!this.upgrade_to_firmware;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get device display name (name or MAC if no name)
|
||||
*/
|
||||
public getDisplayName(): string {
|
||||
return this.name || this.mac;
|
||||
}
|
||||
|
||||
/**
|
||||
* Restart the device
|
||||
*/
|
||||
public async restart(): Promise<void> {
|
||||
if (!this.controller || !this.siteId) {
|
||||
throw new Error('Cannot restart device: no controller reference');
|
||||
}
|
||||
|
||||
await this.controller.request(
|
||||
'POST',
|
||||
`/api/s/${this.siteId}/cmd/devmgr`,
|
||||
{
|
||||
cmd: 'restart',
|
||||
mac: this.mac,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Upgrade the device firmware
|
||||
*/
|
||||
public async upgrade(): Promise<void> {
|
||||
if (!this.controller || !this.siteId) {
|
||||
throw new Error('Cannot upgrade device: no controller reference');
|
||||
}
|
||||
|
||||
await this.controller.request(
|
||||
'POST',
|
||||
`/api/s/${this.siteId}/cmd/devmgr`,
|
||||
{
|
||||
cmd: 'upgrade',
|
||||
mac: this.mac,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set LED override
|
||||
*/
|
||||
public async setLedOverride(mode: 'default' | 'on' | 'off'): Promise<void> {
|
||||
if (!this.controller || !this.siteId) {
|
||||
throw new Error('Cannot set LED: no controller reference');
|
||||
}
|
||||
|
||||
await this.controller.request(
|
||||
'PUT',
|
||||
`/api/s/${this.siteId}/rest/device/${this._id}`,
|
||||
{
|
||||
led_override: mode,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Rename the device
|
||||
*/
|
||||
public async rename(newName: string): Promise<void> {
|
||||
if (!this.controller || !this.siteId) {
|
||||
throw new Error('Cannot rename device: no controller reference');
|
||||
}
|
||||
|
||||
await this.controller.request(
|
||||
'PUT',
|
||||
`/api/s/${this.siteId}/rest/device/${this._id}`,
|
||||
{
|
||||
name: newName,
|
||||
}
|
||||
);
|
||||
|
||||
this.name = newName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set port configuration
|
||||
*/
|
||||
public async setPortConfig(portIdx: number, config: Partial<IPortConfig>): Promise<void> {
|
||||
if (!this.controller || !this.siteId) {
|
||||
throw new Error('Cannot set port config: no controller reference');
|
||||
}
|
||||
|
||||
const portOverride = {
|
||||
port_idx: portIdx,
|
||||
...config,
|
||||
};
|
||||
|
||||
// Get existing port overrides and update
|
||||
const existingOverrides = this.port_overrides || [];
|
||||
const overrideIndex = existingOverrides.findIndex((p) => p.port_idx === portIdx);
|
||||
|
||||
if (overrideIndex >= 0) {
|
||||
existingOverrides[overrideIndex] = { ...existingOverrides[overrideIndex], ...portOverride };
|
||||
} else {
|
||||
existingOverrides.push(portOverride);
|
||||
}
|
||||
|
||||
await this.controller.request(
|
||||
'PUT',
|
||||
`/api/s/${this.siteId}/rest/device/${this._id}`,
|
||||
{
|
||||
port_overrides: existingOverrides,
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user