UNPKG

homebridge-unifi-occupancy-lite

Version:

Lightweight UniFi occupancy sensor plugin for Homebridge using API tokens only. Works with UDM Pro/SE, UDM, UDR and other UniFi OS devices.

199 lines (198 loc) 9.01 kB
"use strict"; /* * Based on homebridge-unifi-occupancy by DouweM * Original: https://github.com/DouweM/homebridge-unifi-occupancy * Licensed under Apache-2.0 * * Modified for homebridge-unifi-occupancy-lite - simplified, resident-based presence detection */ Object.defineProperty(exports, "__esModule", { value: true }); exports.UnifiOccupancyPlatform = void 0; const settings_1 = require("./settings"); const unifi_client_1 = require("./unifi-client"); const resident_1 = require("./resident"); const global_presence_accessory_handler_1 = require("./global_presence_accessory_handler"); const wifi_point_accessory_handler_1 = require("./wifi_point_accessory_handler"); class UnifiOccupancyPlatform { constructor(log, config, api) { this.log = log; this.config = config; this.api = api; this.Service = this.api.hap.Service; this.Characteristic = this.api.hap.Characteristic; this.residents = []; this.wifiPoints = []; this.accessories = new Map(); this.wifiPointHandlers = new Map(); if (!this.parseConfig()) { return; } this.api.on('didFinishLaunching', () => { this.connect(); this.setupAccessories(); this.refresh() .then(() => this.refreshPeriodically()) .catch(error => { this.log.error('Failed to initialize:', error); }); }); } parseConfig() { var _a, _b, _c, _d; if (!this.config.unifi) { this.log.error('ERROR: UniFi Controller is not configured.'); return false; } if (!this.config.unifi.apiKey) { this.log.error('ERROR: UniFi API Key is required.'); return false; } if (this.config.unifi.useSiteManagerApi && !this.config.unifi.hostId) { this.log.error('ERROR: Host ID is required when using Site Manager API.'); return false; } // Set defaults (_a = this.config).interval || (_a.interval = 180); (_b = this.config).globalPresenceSensor ?? (_b.globalPresenceSensor = true); (_c = this.config).residents || (_c.residents = []); (_d = this.config).wifiPoints || (_d.wifiPoints = []); // Initialize residents this.residents = this.config.residents.map((residentConfig) => new resident_1.Resident(residentConfig)); // Initialize wifi points this.wifiPoints = this.config.wifiPoints.map((wifiPointConfig) => new resident_1.WifiPoint(wifiPointConfig)); this.log.info(`Configured ${this.residents.length} residents with ${this.residents.reduce((sum, r) => sum + r.devices.length, 0)} total devices`); this.log.info(`Configured ${this.wifiPoints.length} WiFi points`); return true; } connect() { this.log.debug('Connecting to UniFi Controller...'); this.unifi = new unifi_client_1.UniFiLiteClient({ controller: this.config.unifi.controller, apiKey: this.config.unifi.apiKey, site: this.config.unifi.site || 'default', secure: this.config.unifi.secure || false, useSiteManagerApi: this.config.unifi.useSiteManagerApi || false, hostId: this.config.unifi.hostId }); this.log.info('UniFi API Client initialized'); } setupAccessories() { // Setup global presence sensor if (this.config.globalPresenceSensor) { this.setupGlobalPresenceAccessory(); } // Setup WiFi point accessories this.wifiPoints.forEach(wifiPoint => { this.setupWifiPointAccessory(wifiPoint); }); } setupGlobalPresenceAccessory() { const uuid = this.api.hap.uuid.generate('global-presence'); let accessory = this.accessories.get(uuid); if (!accessory) { accessory = new this.api.platformAccessory('Global Presence', uuid); this.api.registerPlatformAccessories(settings_1.PLUGIN_NAME, settings_1.PLATFORM_NAME, [accessory]); this.accessories.set(uuid, accessory); this.log.info('Created new global presence accessory'); } this.globalPresenceHandler = new global_presence_accessory_handler_1.GlobalPresenceAccessoryHandler(this, accessory, this.residents); } setupWifiPointAccessory(wifiPoint) { const uuid = this.api.hap.uuid.generate(`wifi-point-${wifiPoint.name}`); let accessory = this.accessories.get(uuid); if (!accessory) { accessory = new this.api.platformAccessory(`${wifiPoint.name} Presence`, uuid); this.api.registerPlatformAccessories(settings_1.PLUGIN_NAME, settings_1.PLATFORM_NAME, [accessory]); this.accessories.set(uuid, accessory); this.log.info(`Created new wifi point accessory: ${wifiPoint.name}`); } const handler = new wifi_point_accessory_handler_1.WifiPointAccessoryHandler(this, accessory, wifiPoint, this.residents); this.wifiPointHandlers.set(wifiPoint.name, handler); } refreshPeriodically() { setInterval(() => this.refresh(), this.config.interval * 1000); } configureAccessory(accessory) { this.accessories.set(accessory.UUID, accessory); } async refresh() { try { this.log.debug('Refreshing device presence...'); // Get clients from UniFi const clients = await this.unifi.getClients(); const accessPoints = await this.unifi.getNetworkDevices(); // Update device status for each resident for (const resident of this.residents) { for (const device of resident.devices) { // Reset device status device.isOnline = false; device.currentAccessPoint = undefined; // Find matching client const matchingClient = clients.find(client => device.matchesClient(client)); if (matchingClient) { // Get traffic data if needed let trafficData = null; if (device.minTrafficAmount && device.minTrafficAmount > 0) { trafficData = await this.unifi.getClientTrafficLast15Min(matchingClient.mac); } device.updateFromClient(matchingClient, trafficData); } } // Update resident presence based on device status resident.updatePresence(); } // Map access points for wifi point matching for (const wifiPoint of this.wifiPoints) { const matchingAP = accessPoints.find(ap => wifiPoint.matchesAccessPoint(ap)); if (matchingAP) { wifiPoint.mac = matchingAP.mac; } } // Update accessory handlers this.globalPresenceHandler?.updateResidents(this.residents); this.globalPresenceHandler?.refresh(); this.wifiPointHandlers.forEach(handler => { handler.updateResidents(this.residents); handler.refresh(); }); // Log presence summary const homeResidents = this.residents.filter(r => r.isHome).map(r => r.name); if (homeResidents.length > 0) { this.log.info(`Residents at home: ${homeResidents.join(', ')}`); } else { this.log.info('No residents detected at home'); } } catch (error) { this.log.error('Error refreshing device presence:', error); } } // Remove unused accessories removeUnusedAccessories() { const expectedAccessories = new Set(); // Add global presence if enabled if (this.config.globalPresenceSensor) { expectedAccessories.add(this.api.hap.uuid.generate('global-presence')); } // Add wifi point accessories this.wifiPoints.forEach(wifiPoint => { expectedAccessories.add(this.api.hap.uuid.generate(`wifi-point-${wifiPoint.name}`)); }); // Remove accessories that are no longer needed const accessoriesToRemove = []; this.accessories.forEach((accessory, uuid) => { if (!expectedAccessories.has(uuid)) { accessoriesToRemove.push(accessory); } }); if (accessoriesToRemove.length > 0) { this.log.info(`Removing ${accessoriesToRemove.length} unused accessories`); this.api.unregisterPlatformAccessories(settings_1.PLUGIN_NAME, settings_1.PLATFORM_NAME, accessoriesToRemove); accessoriesToRemove.forEach(accessory => { this.accessories.delete(accessory.UUID); }); } } } exports.UnifiOccupancyPlatform = UnifiOccupancyPlatform;