import * as plugins from './plugins.js'; import { logger } from './unifi.logger.js'; import { UnifiHttp } from './classes.unifihttp.js'; import { DoorManager } from './classes.doormanager.js'; import type { IUnifiAccessOptions, IAccessDevice, IAccessUser, IAccessPolicy, IAccessLocation, IAccessEvent, IAccessApiResponse, THttpMethod, } from './interfaces/index.js'; /** * UniFi Access - Entry point for Access Controller API * * This class provides access to the UniFi Access API for managing doors, * users, credentials, and access events. It uses bearer token authentication. * * @example * ```typescript * const access = new UnifiAccess({ * host: '192.168.1.1', * token: 'your-bearer-token', * }); * * const doors = await access.doorManager.listDoors(); * const users = await access.getUsers(); * * // Unlock a door * await access.unlockDoor('door-id'); * ``` */ export class UnifiAccess { /** Access API port */ private static readonly API_PORT = 12445; /** Access host */ private host: string; /** Bearer token for authentication */ private token: string; /** Whether to verify SSL certificates */ private verifySsl: boolean; /** HTTP client */ private http: UnifiHttp; /** Door manager instance */ public doorManager: DoorManager; constructor(options: IUnifiAccessOptions) { this.host = options.host.replace(/\/$/, ''); this.token = options.token; this.verifySsl = options.verifySsl ?? false; // Build base URL with Access API port const baseHost = this.host.startsWith('http') ? this.host : `https://${this.host}`; // Access API is typically on port 12445 at /api/v1/developer const baseUrl = `${baseHost}:${UnifiAccess.API_PORT}/api/v1/developer`; this.http = new UnifiHttp(baseUrl, this.verifySsl); this.http.setHeader('Authorization', `Bearer ${this.token}`); // Initialize managers this.doorManager = new DoorManager(this); logger.log('info', `UnifiAccess initialized for ${this.host}`); } /** * Make a request to the Access API */ public async request( method: THttpMethod, endpoint: string, data?: unknown ): Promise { return this.http.request(method, endpoint, data); } /** * Unlock a door by ID */ public async unlockDoor(doorId: string): Promise { logger.log('info', `Unlocking door: ${doorId}`); await this.request('PUT', `/door/${doorId}/unlock`); } /** * Lock a door by ID */ public async lockDoor(doorId: string): Promise { logger.log('info', `Locking door: ${doorId}`); await this.request('PUT', `/door/${doorId}/lock`); } /** * Get all doors (convenience method) */ public async getDoors() { return this.doorManager.listDoors(); } /** * Get all devices (hubs, readers, etc.) */ public async getDevices(): Promise { logger.log('debug', 'Fetching Access devices'); const response = await this.request>( 'GET', '/device' ); return response.data || []; } /** * Get a device by ID */ public async getDeviceById(deviceId: string): Promise { try { const response = await this.request>( 'GET', `/device/${deviceId}` ); return response.data; } catch { return null; } } /** * Get all users/credential holders */ public async getUsers(): Promise { logger.log('debug', 'Fetching Access users'); const response = await this.request>( 'GET', '/user' ); return response.data || []; } /** * Get a user by ID */ public async getUserById(userId: string): Promise { try { const response = await this.request>( 'GET', `/user/${userId}` ); return response.data; } catch { return null; } } /** * Create a new user */ public async createUser(user: Partial): Promise { logger.log('info', `Creating user: ${user.first_name} ${user.last_name}`); const response = await this.request>( 'POST', '/user', user ); return response.data; } /** * Update a user */ public async updateUser(userId: string, user: Partial): Promise { logger.log('info', `Updating user: ${userId}`); const response = await this.request>( 'PUT', `/user/${userId}`, user ); return response.data; } /** * Delete a user */ public async deleteUser(userId: string): Promise { logger.log('info', `Deleting user: ${userId}`); await this.request('DELETE', `/user/${userId}`); } /** * Get access policies/groups */ public async getPolicies(): Promise { logger.log('debug', 'Fetching Access policies'); const response = await this.request>( 'GET', '/policy' ); return response.data || []; } /** * Get a policy by ID */ public async getPolicyById(policyId: string): Promise { try { const response = await this.request>( 'GET', `/policy/${policyId}` ); return response.data; } catch { return null; } } /** * Get locations */ public async getLocations(): Promise { logger.log('debug', 'Fetching Access locations'); const response = await this.request>( 'GET', '/location' ); return response.data || []; } /** * Get access events (entry log) */ public async getEvents( options: { start?: number; end?: number; limit?: number; doorId?: string; userId?: string } = {} ): Promise { logger.log('debug', 'Fetching Access events'); const params = new URLSearchParams(); if (options.start) params.append('start', options.start.toString()); if (options.end) params.append('end', options.end.toString()); if (options.limit) params.append('limit', options.limit.toString()); if (options.doorId) params.append('door_id', options.doorId); if (options.userId) params.append('user_id', options.userId); const queryString = params.toString() ? `?${params.toString()}` : ''; const response = await this.request>( 'GET', `/event${queryString}` ); return response.data || []; } /** * Get recent access events */ public async getRecentEvents(limit: number = 100): Promise { return this.getEvents({ limit }); } /** * Grant user access to a door */ public async grantAccess(userId: string, doorId: string): Promise { logger.log('info', `Granting user ${userId} access to door ${doorId}`); await this.request('POST', `/user/${userId}/access`, { door_id: doorId, }); } /** * Revoke user access from a door */ public async revokeAccess(userId: string, doorId: string): Promise { logger.log('info', `Revoking user ${userId} access from door ${doorId}`); await this.request('DELETE', `/user/${userId}/access/${doorId}`); } /** * Assign NFC card to user */ public async assignNfcCard( userId: string, cardToken: string, alias?: string ): Promise { logger.log('info', `Assigning NFC card to user ${userId}`); await this.request('POST', `/user/${userId}/nfc_card`, { token: cardToken, alias: alias, }); } /** * Remove NFC card from user */ public async removeNfcCard(userId: string, cardId: string): Promise { logger.log('info', `Removing NFC card ${cardId} from user ${userId}`); await this.request('DELETE', `/user/${userId}/nfc_card/${cardId}`); } /** * Set user PIN code */ public async setUserPin(userId: string, pinCode: string): Promise { logger.log('info', `Setting PIN for user ${userId}`); await this.request('PUT', `/user/${userId}`, { pin_code: pinCode, }); } }