UNPKG

homebridge-unifi-access

Version:

Homebridge UniFi Access plugin providing complete HomeKit integration for the UniFi Access ecosystem with full support for most features including autoconfiguration, motion detection, multiple controllers, and realtime updates.

155 lines 7.49 kB
import { validateName } from "homebridge-plugin-utils"; import { AccessReservedNames } from "./access-types.js"; import { EventEmitter } from "node:events"; export class AccessEvents extends EventEmitter { api; controller; eventsHandler; eventTimers; hap; log; mqttPublishTelemetry; platform; udaApi; udaDeviceState; udaUpdatesHandler; unsupportedDevices; // Initialize an instance of our Access events handler. constructor(controller) { super(); this.api = controller.platform.api; this.eventTimers = {}; this.hap = controller.platform.api.hap; this.log = controller.log; this.mqttPublishTelemetry = controller.hasFeature("Controller.Publish.Telemetry"); this.controller = controller; this.udaApi = controller.udaApi; this.udaDeviceState = {}; this.platform = controller.platform; this.unsupportedDevices = {}; this.eventsHandler = null; this.udaUpdatesHandler = null; // If we've enabled telemetry from the controller inform the user. if (this.mqttPublishTelemetry) { this.log.info("Access controller telemetry enabled."); } this.configureEvents(); } // Process Access API update events. udaUpdates(packet) { let accessDevice; switch (packet.data.device_type) { case "UA-Hub-Door-Mini": case "UA-ULTRA": case "UAH": case "UAH-DOOR": default: // Lookup the device. accessDevice = this.controller.deviceLookup(packet.event_object_id); // No device found, we're done. if (!accessDevice) { break; } // Update our device configuration state. accessDevice.uda = packet.data; // If we have services on the accessory associated with the Access device that have a StatusActive characteristic set, update our availability state. accessDevice.accessory.services.filter(x => x.testCharacteristic(this.hap.Characteristic.StatusActive)) ?.map(x => x.updateCharacteristic(this.hap.Characteristic.StatusActive, accessDevice?.isOnline ?? false)); // Sync names, if configured to do so. if (accessDevice.hints.syncName && (accessDevice.accessoryName !== validateName(accessDevice.uda.alias))) { accessDevice.log.info("Name change detected. A restart of Homebridge may be needed in order to complete name synchronization with HomeKit."); accessDevice.configureInfo(); } break; } // Update the internal list we maintain. this.udaDeviceState[packet.event_object_id] = packet.data; } // Process device additions and removals from the Access events API. manageDevices(packet) { // Lookup the device. const accessDevice = this.controller.deviceLookup(packet.event_object_id); // We're unadopting. if (packet.event === "access.data.device.delete") { // If it's already gone, we're done. if (!accessDevice) { return; } // Remove the device. this.controller.removeHomeKitDevice(accessDevice); return; } } // Listen to the UniFi Access events API for updates we are interested in (e.g. unlock). configureEvents() { // Only configure the event listener if it exists and it's not already configured. if (this.eventsHandler && this.udaUpdatesHandler) { return true; } // Ensure we update our UDA state before we process any other events. this.prependListener("access.data.device.update", this.udaUpdatesHandler = this.udaUpdates.bind(this)); // Process remove events. this.prependListener("access.data.device.delete", this.manageDevices.bind(this)); // Listen for any messages coming in from our listener. We route events to the appropriate handlers based on the type of event that comes across. this.udaApi.on("message", this.eventsHandler = (packet) => { // Emit messages based on the event type. this.emit(packet.event, packet); // Emit messages based on the specific device. this.emit(packet.event_object_id, packet); // Finally, emit messages based on the specific event and device combination. this.emit(packet.event + "." + packet.event_object_id, packet); // If enabled, publish all the event traffic coming from the Access controller to MQTT. if (this.mqttPublishTelemetry) { this.controller.mqtt?.publish(this.controller.uda.host.mac.replace(/:/g, ""), "telemetry", JSON.stringify(packet)); } }); return true; } // Motion event processing from UniFi Access. motionEventHandler(accessDevice) { if (!accessDevice) { return; } // Only notify the user if we have a motion sensor and it's active. const motionService = accessDevice.accessory.getService(this.hap.Service.MotionSensor); if (motionService) { this.motionEventDelivery(accessDevice, motionService); } } // Motion event delivery to HomeKit. motionEventDelivery(accessDevice, motionService) { if (!accessDevice) { return; } // If we have disabled motion events, we're done here. if (("detectMotion" in accessDevice.accessory.context) && !accessDevice.accessory.context.detectMotion) { return; } // If we have an active motion event inflight, we're done. if (this.eventTimers[accessDevice.id]) { return; } // Trigger the motion event in HomeKit. motionService.updateCharacteristic(this.hap.Characteristic.MotionDetected, true); // If we have a motion trigger switch configured, update it. accessDevice.accessory.getServiceById(this.hap.Service.Switch, AccessReservedNames.SWITCH_MOTION_TRIGGER)?.updateCharacteristic(this.hap.Characteristic.On, true); // Publish the motion event to MQTT, if the user has configured it. this.controller.mqtt?.publish(accessDevice.id, "motion", "true"); // Log the event, if configured to do so. if (accessDevice.hints.logMotion) { accessDevice.log.info("Motion detected."); } // Reset our motion event after motionDuration. this.eventTimers[accessDevice.id] = setTimeout(() => { motionService.updateCharacteristic(this.hap.Characteristic.MotionDetected, false); // If we have a motion trigger switch configured, update it. accessDevice.accessory.getServiceById(this.hap.Service.Switch, AccessReservedNames.SWITCH_MOTION_TRIGGER)?.updateCharacteristic(this.hap.Characteristic.On, false); accessDevice.log.debug("Resetting motion event."); // Publish to MQTT, if the user has configured it. this.controller.mqtt?.publish(accessDevice.id, "motion", "false"); // Delete the timer from our motion event tracker. delete this.eventTimers[accessDevice.id]; }, accessDevice.hints.motionDuration * 1000); } } //# sourceMappingURL=access-events.js.map