import axios from 'axios';
// UniFi Cloud API base URL
const CLOUD_API_BASE = 'https://api.ui.com';
// Create axios instance for UniFi Cloud API
const cloudApi = axios.create({
baseURL: CLOUD_API_BASE,
timeout: 30000,
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
}
});
// Get API key from environment
function getApiKey() {
const apiKey = process.env.UNIFI_API_KEY;
if (!apiKey) {
throw new Error('UNIFI_API_KEY environment variable is required');
}
return apiKey;
}
// Add API key to requests
cloudApi.interceptors.request.use((config) => {
config.headers['X-API-Key'] = getApiKey();
return config;
});
// Response interceptor for error handling
cloudApi.interceptors.response.use(
(response) => response,
(error) => {
const message = error.response?.data?.message || error.message;
const status = error.response?.status;
console.error(`UniFi API Error [${status}]:`, message);
throw new Error(`UniFi API Error: ${message}`);
}
);
// ========================================
// Hosts / Consoles
// ========================================
/**
* List all UniFi OS hosts (consoles)
*/
export async function listHosts() {
const response = await cloudApi.get('/v1/hosts');
return response.data;
}
/**
* Get a specific host by ID
*/
export async function getHost(hostId) {
const response = await cloudApi.get(`/v1/hosts/${hostId}`);
return response.data;
}
// ========================================
// Sites
// ========================================
/**
* List all sites across all hosts
*/
export async function listSites() {
const response = await cloudApi.get('/v1/sites');
return response.data;
}
/**
* Get sites for a specific host
*/
export async function getSitesForHost(hostId) {
const response = await cloudApi.get(`/v1/hosts/${hostId}/sites`);
return response.data;
}
// ========================================
// Devices
// ========================================
/**
* List all devices across all sites
*/
export async function listAllDevices() {
const response = await cloudApi.get('/v1/devices');
return response.data;
}
/**
* Get devices for a specific host
*/
export async function getDevicesForHost(hostId) {
const response = await cloudApi.get(`/v1/hosts/${hostId}/devices`);
return response.data;
}
/**
* Get devices for a specific site
*/
export async function getDevicesForSite(hostId, siteId) {
const response = await cloudApi.get(`/v1/hosts/${hostId}/sites/${siteId}/devices`);
return response.data;
}
/**
* Get a specific device
*/
export async function getDevice(hostId, deviceId) {
const response = await cloudApi.get(`/v1/hosts/${hostId}/devices/${deviceId}`);
return response.data;
}
// ========================================
// Clients
// ========================================
/**
* List all clients across all sites
*/
export async function listAllClients() {
const response = await cloudApi.get('/v1/clients');
return response.data;
}
/**
* Get clients for a specific host
*/
export async function getClientsForHost(hostId) {
const response = await cloudApi.get(`/v1/hosts/${hostId}/clients`);
return response.data;
}
/**
* Get clients for a specific site
*/
export async function getClientsForSite(hostId, siteId) {
const response = await cloudApi.get(`/v1/hosts/${hostId}/sites/${siteId}/clients`);
return response.data;
}
// ========================================
// Network Operations
// ========================================
/**
* Block a client
*/
export async function blockClient(hostId, siteId, mac) {
const response = await cloudApi.post(`/v1/hosts/${hostId}/sites/${siteId}/clients/${mac}/block`);
return response.data;
}
/**
* Unblock a client
*/
export async function unblockClient(hostId, siteId, mac) {
const response = await cloudApi.post(`/v1/hosts/${hostId}/sites/${siteId}/clients/${mac}/unblock`);
return response.data;
}
/**
* Reconnect/kick a client
*/
export async function reconnectClient(hostId, siteId, mac) {
const response = await cloudApi.post(`/v1/hosts/${hostId}/sites/${siteId}/clients/${mac}/reconnect`);
return response.data;
}
/**
* Locate device (flash LEDs)
*/
export async function locateDevice(hostId, deviceId, enabled = true) {
const response = await cloudApi.post(`/v1/hosts/${hostId}/devices/${deviceId}/locate`, {
enabled
});
return response.data;
}
/**
* Restart a device
*/
export async function restartDevice(hostId, deviceId) {
const response = await cloudApi.post(`/v1/hosts/${hostId}/devices/${deviceId}/restart`);
return response.data;
}
// ========================================
// ISP Metrics
// ========================================
/**
* Get ISP metrics for a host
*/
export async function getIspMetrics(hostId, duration = '1d') {
const response = await cloudApi.get(`/v1/hosts/${hostId}/isp-metrics`, {
params: { duration }
});
return response.data;
}
// ========================================
// Protect (Cameras)
// ========================================
/**
* List all cameras
*/
export async function listCameras(hostId) {
const response = await cloudApi.get(`/v1/hosts/${hostId}/cameras`);
return response.data;
}
/**
* Get camera details
*/
export async function getCamera(hostId, cameraId) {
const response = await cloudApi.get(`/v1/hosts/${hostId}/cameras/${cameraId}`);
return response.data;
}
/**
* Set camera privacy mode
*/
export async function setCameraPrivacy(hostId, cameraId, enabled) {
const response = await cloudApi.patch(`/v1/hosts/${hostId}/cameras/${cameraId}`, {
privacyMode: enabled
});
return response.data;
}
/**
* Set camera recording mode
*/
export async function setCameraRecording(hostId, cameraId, mode) {
const response = await cloudApi.patch(`/v1/hosts/${hostId}/cameras/${cameraId}`, {
recordingSettings: { mode }
});
return response.data;
}
// ========================================
// Access (Doors)
// ========================================
/**
* List all doors
*/
export async function listDoors(hostId) {
const response = await cloudApi.get(`/v1/hosts/${hostId}/doors`);
return response.data;
}
/**
* Get door details
*/
export async function getDoor(hostId, doorId) {
const response = await cloudApi.get(`/v1/hosts/${hostId}/doors/${doorId}`);
return response.data;
}
/**
* Unlock a door
*/
export async function unlockDoor(hostId, doorId, duration = 5) {
const response = await cloudApi.post(`/v1/hosts/${hostId}/doors/${doorId}/unlock`, {
duration
});
return response.data;
}
/**
* Lock a door
*/
export async function lockDoor(hostId, doorId) {
const response = await cloudApi.post(`/v1/hosts/${hostId}/doors/${doorId}/lock`);
return response.data;
}
// ========================================
// Health & Status
// ========================================
/**
* Check API connectivity and get basic info
*/
export async function healthCheck() {
try {
const hosts = await listHosts();
return {
ok: true,
timestamp: new Date().toISOString(),
hostsCount: hosts.data?.length || 0,
apiBase: CLOUD_API_BASE
};
} catch (error) {
return {
ok: false,
timestamp: new Date().toISOString(),
error: error.message,
apiBase: CLOUD_API_BASE
};
}
}
/**
* Get comprehensive system status
*/
export async function getSystemStatus() {
const status = {
timestamp: new Date().toISOString(),
hosts: [],
summary: {
totalHosts: 0,
totalSites: 0,
totalDevices: 0,
totalClients: 0,
onlineDevices: 0,
offlineDevices: 0
}
};
try {
const hostsResponse = await listHosts();
const hosts = hostsResponse.data || [];
status.summary.totalHosts = hosts.length;
for (const host of hosts) {
const hostStatus = {
id: host.id,
name: host.name || host.hostname,
type: host.type,
status: host.status,
sites: [],
deviceCount: 0,
clientCount: 0
};
try {
const sitesResponse = await getSitesForHost(host.id);
const sites = sitesResponse.data || [];
status.summary.totalSites += sites.length;
for (const site of sites) {
hostStatus.sites.push({
id: site.id,
name: site.name
});
}
} catch (e) {
// Site fetch may fail for some hosts
}
try {
const devicesResponse = await getDevicesForHost(host.id);
const devices = devicesResponse.data || [];
hostStatus.deviceCount = devices.length;
status.summary.totalDevices += devices.length;
for (const device of devices) {
if (device.state === 'ONLINE' || device.state === 1) {
status.summary.onlineDevices++;
} else {
status.summary.offlineDevices++;
}
}
} catch (e) {
// Device fetch may fail
}
try {
const clientsResponse = await getClientsForHost(host.id);
const clients = clientsResponse.data || [];
hostStatus.clientCount = clients.length;
status.summary.totalClients += clients.length;
} catch (e) {
// Client fetch may fail
}
status.hosts.push(hostStatus);
}
status.ok = true;
} catch (error) {
status.ok = false;
status.error = error.message;
}
return status;
}
export default {
// Hosts
listHosts,
getHost,
// Sites
listSites,
getSitesForHost,
// Devices
listAllDevices,
getDevicesForHost,
getDevicesForSite,
getDevice,
locateDevice,
restartDevice,
// Clients
listAllClients,
getClientsForHost,
getClientsForSite,
blockClient,
unblockClient,
reconnectClient,
// ISP
getIspMetrics,
// Protect
listCameras,
getCamera,
setCameraPrivacy,
setCameraRecording,
// Access
listDoors,
getDoor,
unlockDoor,
lockDoor,
// Health
healthCheck,
getSystemStatus
};