homebridge-unifi-access 1.3.0 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -7,7 +7,7 @@
7
7
  */
8
8
  "use strict";
9
9
 
10
- import { featureOptionCategories, featureOptions, isOptionEnabled } from "../dist/access-options.js";
10
+ import { featureOptionCategories, featureOptions } from "../dist/access-options.js";
11
11
  import { AccessApi } from "unifi-access";
12
12
  import { HomebridgePluginUiServer } from "@homebridge/plugin-ui-utils";
13
13
  import util from "node:util";
@@ -16,7 +16,8 @@ class PluginUiServer extends HomebridgePluginUiServer {
16
16
 
17
17
  errorInfo;
18
18
 
19
- constructor () {
19
+ constructor() {
20
+
20
21
  super();
21
22
 
22
23
  this.errorInfo = "";
@@ -36,48 +37,31 @@ class PluginUiServer extends HomebridgePluginUiServer {
36
37
  // Register the getErrorMessage() webUI server API endpoint.
37
38
  #registerGetErrorMessage() {
38
39
 
39
- // Return the most recent error message generated by the Access API.
40
- this.onRequest("/getErrorMessage", async () => {
41
-
42
- try {
43
-
44
- return this.errorInfo;
45
- } catch(err) {
46
-
47
- console.log(err);
48
-
49
- // Return nothing if we error out for some reason.
50
- return "";
51
- }
52
- });
40
+ // Return the most recent error message generated by the Protect API.
41
+ this.onRequest("/getErrorMessage", () => this.errorInfo);
53
42
  }
54
43
 
55
44
  // Register the getDevices() webUI server API endpoint.
56
45
  #registerGetDevices() {
57
46
 
58
- // Return the list of Access devices.
47
+ // Return the list of Protect devices.
59
48
  this.onRequest("/getDevices", async (controller) => {
60
49
 
61
50
  try {
62
51
 
63
52
  const log = {
64
53
 
65
- debug: (message, parameters) => {},
54
+ debug: () => {},
66
55
  error: (message, parameters = []) => {
67
56
 
68
57
  // Save the error to inform the user in the webUI.
69
- if(!!parameters?.[Symbol.iterator]) {
70
-
71
- this.errorInfo = util.format(message, ...parameters);
72
- } else {
73
-
74
- this.errorInfo = util.format(message, parameters);
75
- }
58
+ this.errorInfo = util.format(message, ...(Array.isArray(parameters) ? parameters : [parameters]));
76
59
 
60
+ // eslint-disable-next-line no-console
77
61
  console.error(this.errorInfo);
78
62
  },
79
- info: (message, parameters) => {},
80
- warn: (message, parameters = []) => {}
63
+ info: () => {},
64
+ warn: () => {}
81
65
  };
82
66
 
83
67
  // Connect to the Access controller.
@@ -107,6 +91,7 @@ class PluginUiServer extends HomebridgePluginUiServer {
107
91
  return [ udaApi.controller, ...devices ];
108
92
  } catch(err) {
109
93
 
94
+ // eslint-disable-next-line no-console
110
95
  console.log(err);
111
96
 
112
97
  // Return nothing if we error out for some reason.
@@ -118,35 +103,8 @@ class PluginUiServer extends HomebridgePluginUiServer {
118
103
  // Register the getOptions() webUI server API endpoint.
119
104
  #registerGetOptions() {
120
105
 
121
- // Return the list of options configured for a given Access device.
122
- this.onRequest("/getOptions", async(request) => {
123
-
124
- try {
125
-
126
- const optionSet = {};
127
-
128
- // Loop through all the feature option categories.
129
- for(const category of featureOptionCategories) {
130
-
131
- optionSet[category.name] = [];
132
-
133
- for(const options of featureOptions[category.name]) {
134
-
135
- options.value = isOptionEnabled(request.configOptions, request.controllerUda, request.deviceUda, category.name + "." + options.name, options.default);
136
- optionSet[category.name].push(options);
137
- }
138
- }
139
-
140
- return { categories: featureOptionCategories, options: optionSet };
141
-
142
- } catch(err) {
143
-
144
- console.log(err);
145
-
146
- // Return nothing if we error out for some reason.
147
- return {};
148
- }
149
- });
106
+ // Return the list of options configured for a given Protect device.
107
+ this.onRequest("/getOptions", () => ({ categories: featureOptionCategories, options: featureOptions }));
150
108
  }
151
109
  }
152
110
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "homebridge-unifi-access",
3
- "version": "1.3.0",
3
+ "version": "1.5.0",
4
4
  "displayName": "Homebridge UniFi Access",
5
5
  "description": "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.",
6
6
  "author": {
@@ -18,7 +18,7 @@
18
18
  },
19
19
  "type": "module",
20
20
  "engines": {
21
- "homebridge": ">=1.6.0",
21
+ "homebridge": ">=1.8.0 || >=2.0.0",
22
22
  "node": ">=18"
23
23
  },
24
24
  "keywords": [
@@ -44,29 +44,28 @@
44
44
  "unifios"
45
45
  ],
46
46
  "scripts": {
47
- "build": "rimraf ./dist && tsc",
48
- "clean": "rimraf ./dist",
49
- "lint": "eslint src/**.ts",
50
- "jlint": "eslint homebridge-ui/public/**.mjs",
47
+ "prebuild": "npm run clean && npm run build-ui",
48
+ "build": "tsc",
49
+ "build-ui": "shx mkdir -p homebridge-ui/public/lib && shx cp \"node_modules/homebridge-plugin-utils/dist/ui/**/*.@(js|mjs){,.map}\" homebridge-ui/public/lib",
50
+ "clean": "shx rm -rf dist homebridge-ui/public/lib",
51
+ "prelint": "npm run build-ui",
52
+ "lint": "eslint --max-warnings=${ESLINT_MAX_WARNINGS:-\"-1\"} eslint.config.mjs src homebridge-ui/*.js homebridge-ui/public/**.mjs",
51
53
  "postpublish": "npm run clean",
52
- "prepublishOnly": "npm run lint && npm run build",
53
- "test": "eslint src/**.ts",
54
- "watch": "npm run build && npm link && nodemon"
54
+ "prepublishOnly": "npm run lint && npm run build"
55
55
  },
56
56
  "main": "dist/index.js",
57
57
  "dependencies": {
58
58
  "@homebridge/plugin-ui-utils": "1.0.3",
59
- "mqtt": "5.5.3",
60
- "unifi-access": "^1.0.3"
59
+ "homebridge-plugin-utils": "1.9.0",
60
+ "unifi-access": "^1.2.0"
61
61
  },
62
62
  "devDependencies": {
63
- "@stylistic/eslint-plugin": "1.7.2",
64
- "@types/node": "20.12.7",
65
- "@typescript-eslint/eslint-plugin": "7.7.0",
66
- "@typescript-eslint/parser": "7.7.0",
67
- "eslint": "8.57.0",
68
- "homebridge": "1.8.0",
69
- "rimraf": "5.0.5",
70
- "typescript": "5.4.5"
63
+ "@stylistic/eslint-plugin": "2.8.0",
64
+ "@types/node": "22.7.4",
65
+ "eslint": "9.11.1",
66
+ "homebridge": "1.8.4",
67
+ "shx": "^0.3.4",
68
+ "typescript": "5.6.2",
69
+ "typescript-eslint": "^8.8.0"
71
70
  }
72
71
  }
@@ -1,20 +0,0 @@
1
- /// <reference types="node" />
2
- import { AccessController } from "./access-controller.js";
3
- import { PlatformAccessory } from "homebridge";
4
- export declare class AccessMqtt {
5
- private config;
6
- private controller;
7
- private isConnected;
8
- private log;
9
- private mqtt;
10
- private subscriptions;
11
- private udaApi;
12
- constructor(controller: AccessController);
13
- private configure;
14
- publish(accessory: PlatformAccessory | string, topic: string, message: string): void;
15
- subscribe(accessory: PlatformAccessory | string, topic: string, callback: (cbBuffer: Buffer) => void): void;
16
- subscribeGet(accessory: PlatformAccessory, topic: string, type: string, getValue: () => string): void;
17
- subscribeSet(accessory: PlatformAccessory, topic: string, type: string, setValue: (value: string) => void): void;
18
- unsubscribe(accessory: PlatformAccessory | string, topic: string): void;
19
- private expandTopic;
20
- }
@@ -1,163 +0,0 @@
1
- /* Copyright(C) 2017-2024, HJD (https://github.com/hjdhjd). All rights reserved.
2
- *
3
- * access-mqtt.ts: MQTT connectivity class for UniFi Access.
4
- */
5
- import mqtt from "mqtt";
6
- import { ACCESS_MQTT_RECONNECT_INTERVAL } from "./settings.js";
7
- export class AccessMqtt {
8
- config;
9
- controller;
10
- isConnected;
11
- log;
12
- mqtt;
13
- subscriptions;
14
- udaApi;
15
- constructor(controller) {
16
- this.config = controller.config;
17
- this.isConnected = false;
18
- this.log = controller.log;
19
- this.mqtt = null;
20
- this.controller = controller;
21
- this.udaApi = controller.udaApi;
22
- this.subscriptions = {};
23
- if (!this.config.mqttUrl) {
24
- return;
25
- }
26
- this.configure();
27
- }
28
- // Connect to the MQTT broker.
29
- configure() {
30
- // Try to connect to the MQTT broker and make sure we catch any URL errors.
31
- try {
32
- this.mqtt = mqtt.connect(this.config.mqttUrl, { reconnectPeriod: ACCESS_MQTT_RECONNECT_INTERVAL * 1000, rejectUnauthorized: false });
33
- }
34
- catch (error) {
35
- if (error instanceof Error) {
36
- switch (error.message) {
37
- case "Missing protocol":
38
- this.log.error("MQTT Broker: Invalid URL provided: %s.", this.config.mqttUrl);
39
- break;
40
- default:
41
- this.log.error("MQTT Broker: Error: %s.", error.message);
42
- break;
43
- }
44
- }
45
- }
46
- // We've been unable to even attempt to connect. It's likely we have a configuration issue - we're done here.
47
- if (!this.mqtt) {
48
- return;
49
- }
50
- // Notify the user when we connect to the broker.
51
- this.mqtt.on("connect", () => {
52
- this.isConnected = true;
53
- // Magic incantation to redact passwords.
54
- const redact = /^(?<pre>.*:\/{0,2}.*:)(?<pass>.*)(?<post>@.*)/;
55
- this.log.info("Connected to MQTT broker: %s (topic: %s).", this.config.mqttUrl.replace(redact, "$<pre>REDACTED$<post>"), this.config.mqttTopic);
56
- });
57
- // Notify the user when we've disconnected.
58
- this.mqtt.on("close", () => {
59
- if (this.isConnected) {
60
- this.isConnected = false;
61
- // Magic incantation to redact passwords.
62
- const redact = /^(?<pre>.*:\/{0,2}.*:)(?<pass>.*)(?<post>@.*)/;
63
- this.log.info("Disconnected from MQTT broker: %s.", this.config.mqttUrl.replace(redact, "$<pre>REDACTED$<post>"));
64
- }
65
- });
66
- // Process inbound messages and pass it to the right message handler.
67
- this.mqtt.on("message", (topic, message) => {
68
- if (this.subscriptions[topic]) {
69
- this.subscriptions[topic](message);
70
- }
71
- });
72
- // Notify the user when there's a connectivity error.
73
- this.mqtt.on("error", (error) => {
74
- switch (error.code) {
75
- case "ECONNREFUSED":
76
- this.log.error("MQTT Broker: Connection refused (url: %s). Will retry again in %s minute%s.", this.config.mqttUrl, ACCESS_MQTT_RECONNECT_INTERVAL / 60, ACCESS_MQTT_RECONNECT_INTERVAL / 60 > 1 ? "s" : "");
77
- break;
78
- case "ECONNRESET":
79
- this.log.error("MQTT Broker: Connection reset (url: %s). Will retry again in %s minute%s.", this.config.mqttUrl, ACCESS_MQTT_RECONNECT_INTERVAL / 60, ACCESS_MQTT_RECONNECT_INTERVAL / 60 > 1 ? "s" : "");
80
- break;
81
- case "ENOTFOUND":
82
- this.mqtt?.end(true);
83
- this.log.error("MQTT Broker: Hostname or IP address not found. (url: %s).", this.config.mqttUrl);
84
- break;
85
- default:
86
- this.log.error("MQTT Broker: %s (url: %s). Will retry again in %s minute%s.", error, this.config.mqttUrl, ACCESS_MQTT_RECONNECT_INTERVAL / 60, ACCESS_MQTT_RECONNECT_INTERVAL / 60 > 1 ? "s" : "");
87
- break;
88
- }
89
- });
90
- }
91
- // Publish an MQTT event to a broker.
92
- publish(accessory, topic, message) {
93
- const expandedTopic = this.expandTopic(accessory, topic);
94
- // No valid topic returned, we're done.
95
- if (!expandedTopic) {
96
- return;
97
- }
98
- this.log.debug("MQTT publish: %s Message: %s.", expandedTopic, message);
99
- // By default, we publish as: unifi/access/mac/event/name
100
- this.mqtt?.publish(expandedTopic, message);
101
- }
102
- // Subscribe to an MQTT topic.
103
- subscribe(accessory, topic, callback) {
104
- const expandedTopic = this.expandTopic(accessory, topic);
105
- // No valid topic returned, we're done.
106
- if (!expandedTopic) {
107
- return;
108
- }
109
- this.log.debug("MQTT subscribe: %s.", expandedTopic);
110
- // Add to our callback list.
111
- this.subscriptions[expandedTopic] = callback;
112
- // Tell MQTT we're subscribing to this event.
113
- // By default, we subscribe as: unifi/access/mac/event/name.
114
- this.mqtt?.subscribe(expandedTopic);
115
- }
116
- // Subscribe to a specific MQTT topic and publish a value on a get request.
117
- subscribeGet(accessory, topic, type, getValue) {
118
- // Return the current status of a given sensor.
119
- this.subscribe(accessory, topic + "/get", (message) => {
120
- const value = message.toString().toLowerCase();
121
- // When we get the right message, we return the system information JSON.
122
- if (value !== "true") {
123
- return;
124
- }
125
- this.publish(accessory, topic, getValue());
126
- (this.controller.configuredDevices[accessory.UUID]?.log ?? this.log).info("MQTT: %s status published.", type);
127
- });
128
- }
129
- // Subscribe to a specific MQTT topic and set a value on a set request.
130
- subscribeSet(accessory, topic, type, setValue) {
131
- // Return the current status of a given sensor.
132
- this.subscribe(accessory, topic + "/set", (message) => {
133
- const value = message.toString().toLowerCase();
134
- // Set our value and inform the user.
135
- setValue(value);
136
- (this.controller.configuredDevices[accessory.UUID]?.log ?? this.log).info("MQTT: set message received for %s: %s.", type, value);
137
- });
138
- }
139
- // Unsubscribe to an MQTT topic.
140
- unsubscribe(accessory, topic) {
141
- const expandedTopic = this.expandTopic(accessory, topic);
142
- // No valid topic returned, we're done.
143
- if (!expandedTopic) {
144
- return;
145
- }
146
- delete this.subscriptions[expandedTopic];
147
- }
148
- // Expand a topic to a unique, fully formed one.
149
- expandTopic(accessory, topic) {
150
- // No accessory, we're done.
151
- if (!accessory) {
152
- return null;
153
- }
154
- // Check if we were passed the MAC as an input. Otherwise, assume it's the controller's MAC initially.
155
- let mac = (typeof accessory === "string") ? accessory : accessory.context.controller;
156
- // Check to see if it's really an Access device...if it is, use it's MAC address.
157
- if ((typeof accessory !== "string") && ("mac" in accessory.context)) {
158
- mac = accessory.context.mac;
159
- }
160
- return this.config.mqttTopic + "/" + mac.replace(/:/g, "") + "/" + topic;
161
- }
162
- }
163
- //# sourceMappingURL=access-mqtt.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"access-mqtt.js","sourceRoot":"","sources":["../src/access-mqtt.ts"],"names":[],"mappings":"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"}