homebridge-ratgdo 0.1.0 → 1.0.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.
@@ -0,0 +1,144 @@
1
+ /* Copyright(C) 2017-2024, HJD (https://github.com/hjdhjd). All rights reserved.
2
+ *
3
+ * ui.mjs: Ratgdo webUI.
4
+ */
5
+ "use strict";
6
+
7
+ import { ratgdoFeatureOptions } from "./ratgdo-featureoptions.mjs";
8
+
9
+ // Keep a list of all the feature options and option groups.
10
+ const featureOptions = new ratgdoFeatureOptions();
11
+
12
+ // Show the first run user experience if we don't have valid login credentials.
13
+ async function showFirstRun () {
14
+
15
+ const buttonFirstRun = document.getElementById("firstRun");
16
+ const serverPortInfo = document.getElementById("serverPortInfo");
17
+
18
+ serverPortInfo.innerHTML = (featureOptions.currentConfig[0].port ?? "18830");
19
+
20
+ // First run user experience.
21
+ buttonFirstRun.addEventListener("click", async () => {
22
+
23
+ // Show the beachball while we setup.
24
+ homebridge.showSpinner();
25
+
26
+ // Get the list of devices the plugin knows about.
27
+ const ratgdoDevices = await homebridge.getCachedAccessories();
28
+
29
+ // Sort it for posterity.
30
+ ratgdoDevices?.sort((a, b) => {
31
+
32
+ const aCase = (a.displayName ?? "").toLowerCase();
33
+ const bCase = (b.displayName ?? "").toLowerCase();
34
+
35
+ return aCase > bCase ? 1 : (bCase > aCase ? -1 : 0);
36
+ });
37
+
38
+ // Create our UI.
39
+ document.getElementById("pageFirstRun").style.display = "none";
40
+ document.getElementById("menuWrapper").style.display = "inline-flex";
41
+ featureOptions.showUI();
42
+
43
+ // All done. Let the user interact with us, although in practice, we shouldn't get here.
44
+ // homebridge.hideSpinner();
45
+ });
46
+
47
+ document.getElementById("pageFirstRun").style.display = "block";
48
+ }
49
+
50
+ // Show the main plugin configuration tab.
51
+ function showSettings () {
52
+
53
+ // Show the beachball while we setup.
54
+ homebridge.showSpinner();
55
+
56
+ // Create our UI.
57
+ document.getElementById("menuHome").classList.remove("btn-elegant");
58
+ document.getElementById("menuHome").classList.add("btn-primary");
59
+ document.getElementById("menuFeatureOptions").classList.remove("btn-elegant");
60
+ document.getElementById("menuFeatureOptions").classList.add("btn-primary");
61
+ document.getElementById("menuSettings").classList.add("btn-elegant");
62
+ document.getElementById("menuSettings").classList.remove("btn-primary");
63
+
64
+ document.getElementById("pageSupport").style.display = "none";
65
+ document.getElementById("pageFeatureOptions").style.display = "none";
66
+
67
+ homebridge.showSchemaForm();
68
+
69
+ // All done. Let the user interact with us.
70
+ homebridge.hideSpinner();
71
+ }
72
+
73
+ // Show the support tab.
74
+ function showSupport() {
75
+
76
+ // Show the beachball while we setup.
77
+ homebridge.showSpinner();
78
+ homebridge.hideSchemaForm();
79
+
80
+ // Create our UI.
81
+ document.getElementById("menuHome").classList.add("btn-elegant");
82
+ document.getElementById("menuHome").classList.remove("btn-primary");
83
+ document.getElementById("menuFeatureOptions").classList.remove("btn-elegant");
84
+ document.getElementById("menuFeatureOptions").classList.add("btn-primary");
85
+ document.getElementById("menuSettings").classList.remove("btn-elegant");
86
+ document.getElementById("menuSettings").classList.add("btn-primary");
87
+
88
+ document.getElementById("pageSupport").style.display = "block";
89
+ document.getElementById("pageFeatureOptions").style.display = "none";
90
+
91
+ // All done. Let the user interact with us.
92
+ homebridge.hideSpinner();
93
+ }
94
+
95
+ // Launch our webUI.
96
+ async function launchWebUI() {
97
+
98
+ // Retrieve the current plugin configuration.
99
+ featureOptions.currentConfig = await homebridge.getPluginConfig();
100
+
101
+ // Add our event listeners to animate the UI.
102
+ menuHome.addEventListener("click", () => showSupport());
103
+ menuFeatureOptions.addEventListener("click", () => featureOptions.showUI());
104
+ menuSettings.addEventListener("click", () => showSettings());
105
+
106
+ // Get the list of devices the plugin knows about.
107
+ const ratgdoDevices = await homebridge.getCachedAccessories();
108
+
109
+ // If we've got Ratgdo devices detected, we launch our feature option UI. Otherwise, we launch our first run UI.
110
+ if(featureOptions.currentConfig.length && ratgdoDevices?.length) {
111
+
112
+ document.getElementById("menuWrapper").style.display = "inline-flex";
113
+ featureOptions.showUI();
114
+ return;
115
+ }
116
+
117
+ // If we have no configuration, let's create one.
118
+ if(!featureOptions.currentConfig.length) {
119
+
120
+ featureOptions.currentConfig.push({ name: "Ratgdo" });
121
+ } else if(!("name" in featureOptions.currentConfig[0])) {
122
+
123
+ // If we haven't set the name, let's do so now.
124
+ featureOptions.currentConfig[0].name = "Ratgdo";
125
+ }
126
+
127
+ // Update the plugin configuration and launch the first run UI.
128
+ await homebridge.updatePluginConfig(featureOptions.currentConfig);
129
+ showFirstRun();
130
+ }
131
+
132
+ // Fire off our UI, catching errors along the way.
133
+ try {
134
+
135
+ launchWebUI();
136
+ } catch(err) {
137
+
138
+ // If we had an error instantiating or updating the UI, notify the user.
139
+ homebridge.toast.error(err.message, "Error");
140
+ } finally {
141
+
142
+ // Always leave the UI in a usable place for the end user.
143
+ homebridge.hideSpinner();
144
+ }
@@ -0,0 +1,58 @@
1
+ /* Copyright(C) 2017-2024, HJD (https://github.com/hjdhjd). All rights reserved.
2
+ *
3
+ * server.js: homebridge-ratgdo webUI server API.
4
+ *
5
+ * This module is heavily inspired by the homebridge-config-ui-x source code and borrows from both.
6
+ * Thank you oznu for your contributions to the HomeKit world.
7
+ */
8
+ "use strict";
9
+
10
+ import { featureOptionCategories, featureOptions, isOptionEnabled } from "../dist/ratgdo-options.js";
11
+ import { HomebridgePluginUiServer } from "@homebridge/plugin-ui-utils";
12
+ import util from "node:util";
13
+
14
+ class PluginUiServer extends HomebridgePluginUiServer {
15
+
16
+ constructor () {
17
+ super();
18
+
19
+ // Register getOptions() with the Homebridge server API.
20
+ this.#registerGetOptions();
21
+
22
+ this.ready();
23
+ }
24
+
25
+ // Register the getOptions() webUI server API endpoint.
26
+ #registerGetOptions() {
27
+
28
+ // Return the list of options configured for a given Ratgdo device.
29
+ this.onRequest("/getOptions", async(request) => {
30
+
31
+ try {
32
+
33
+ const optionSet = {};
34
+
35
+ // Loop through all the feature option categories.
36
+ for(const category of featureOptionCategories) {
37
+
38
+ optionSet[category.name] = [];
39
+
40
+ for(const options of featureOptions[category.name]) {
41
+
42
+ options.value = isOptionEnabled(request.configOptions, request.ratgdoDevice, category.name + "." + options.name, options.default);
43
+ optionSet[category.name].push(options);
44
+ }
45
+ }
46
+
47
+ return { categories: featureOptionCategories, options: optionSet };
48
+
49
+ } catch(err) {
50
+
51
+ // Return nothing if we error out for some reason.
52
+ return {};
53
+ }
54
+ });
55
+ }
56
+ }
57
+
58
+ (() => new PluginUiServer())();
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "homebridge-ratgdo",
3
3
  "displayName": "Homebridge Ratgdo",
4
- "version": "0.1.0",
4
+ "version": "1.0.0",
5
5
  "description": "HomeKit integration for LiftMaster and Chamberlain garage door openers, without requiring myQ.",
6
6
  "license": "ISC",
7
7
  "repository": {
@@ -11,6 +11,7 @@
11
11
  "bugs": {
12
12
  "url": "https://github.com/hjdhjd/homebridge-ratgdo/issues"
13
13
  },
14
+ "type": "module",
14
15
  "engines": {
15
16
  "node": ">=18.0",
16
17
  "homebridge": ">1.2.0"
@@ -26,17 +27,20 @@
26
27
  "homebridge-plugin"
27
28
  ],
28
29
  "devDependencies": {
29
- "@types/node": "^20.10.0",
30
- "@typescript-eslint/eslint-plugin": "^6.12.0",
31
- "@typescript-eslint/parser": "^6.12.0",
32
- "eslint": "^8.54.0",
30
+ "@stylistic/eslint-plugin": "^1.6.2",
31
+ "@types/node": "^20.11.21",
32
+ "@typescript-eslint/eslint-plugin": "^7.1.0",
33
+ "@typescript-eslint/parser": "^7.1.0",
34
+ "eslint": "^8.57.0",
33
35
  "homebridge": "^1.7.0",
34
- "nodemon": "^3.0.1",
36
+ "nodemon": "^3.1.0",
35
37
  "rimraf": "^5.0.5",
36
- "ts-node": "^10.9.1",
37
- "typescript": "^5.3.2"
38
+ "ts-node": "^10.9.2",
39
+ "typescript": "^5.3.3"
38
40
  },
39
41
  "dependencies": {
40
- "aedes": "^0.49.0"
42
+ "@homebridge/plugin-ui-utils": "^1.0.1",
43
+ "aedes": "^0.51.0",
44
+ "mqtt": "^5.3.6"
41
45
  }
42
46
  }