homebridge-ratgdo 2.2.0 → 2.2.2
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.
- package/README.md +6 -4
- package/config.schema.json +1 -1
- package/dist/ratgdo-device.d.ts +3 -2
- package/dist/ratgdo-device.js +3 -3
- package/dist/ratgdo-device.js.map +1 -1
- package/dist/ratgdo-options.d.ts +0 -1
- package/dist/ratgdo-options.js +4 -4
- package/dist/ratgdo-options.js.map +1 -1
- package/dist/ratgdo-platform.js +24 -24
- package/dist/ratgdo-platform.js.map +1 -1
- package/dist/ratgdo-types.d.ts +0 -6
- package/dist/ratgdo-types.js.map +1 -1
- package/homebridge-ui/public/index.html +13 -20
- package/homebridge-ui/public/lib/featureoptions.js +32 -22
- package/homebridge-ui/public/lib/featureoptions.js.map +1 -0
- package/homebridge-ui/public/lib/{webui-featureoptions.mjs → webUi-featureoptions.mjs} +353 -117
- package/homebridge-ui/public/lib/webUi.mjs +85 -56
- package/homebridge-ui/public/ui.mjs +17 -0
- package/homebridge-ui/server.js +1 -0
- package/package.json +11 -9
|
@@ -4,22 +4,43 @@
|
|
|
4
4
|
*/
|
|
5
5
|
"use strict";
|
|
6
6
|
|
|
7
|
+
import { webUiFeatureOptions } from "./webUi-featureoptions.mjs";
|
|
8
|
+
|
|
7
9
|
export class webUi {
|
|
8
10
|
|
|
9
11
|
// Feature options class instance.
|
|
10
|
-
|
|
12
|
+
featureOptions;
|
|
11
13
|
|
|
12
|
-
//
|
|
13
|
-
#
|
|
14
|
+
// First run webUI callback endpoints for customization.
|
|
15
|
+
#firstRun;
|
|
14
16
|
|
|
15
17
|
// Plugin name.
|
|
16
18
|
#name;
|
|
17
19
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
/**
|
|
21
|
+
* featureOptions - parameters to webUiFeatureOptions.
|
|
22
|
+
* firstRun - first run handlers:
|
|
23
|
+
* isRequired - do we need to run the first run UI workflow?
|
|
24
|
+
* onStart - initialization for the first run webUI to populate forms and other startup tasks.
|
|
25
|
+
* onSubmit - execute the first run workflow, typically a login or configuration validation of some sort.
|
|
26
|
+
* name - plugin name.
|
|
27
|
+
*/
|
|
28
|
+
constructor({ featureOptions, firstRun = {}, name } = {}) {
|
|
29
|
+
|
|
30
|
+
// Defaults for our first run handlers.
|
|
31
|
+
this.firstRun = { isRequired: () => false, onStart: () => true, onSubmit: () => true };
|
|
32
|
+
|
|
33
|
+
// Figure out the options passed in to us.
|
|
34
|
+
this.featureOptions = new webUiFeatureOptions(featureOptions);
|
|
35
|
+
this.firstRun = Object.assign({}, this.firstRun, firstRun);
|
|
22
36
|
this.name = name;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Render the webUI.
|
|
41
|
+
*/
|
|
42
|
+
// Render the UI.
|
|
43
|
+
show() {
|
|
23
44
|
|
|
24
45
|
// Fire off our UI, catching errors along the way.
|
|
25
46
|
try {
|
|
@@ -28,11 +49,11 @@ export class webUi {
|
|
|
28
49
|
} catch(err) {
|
|
29
50
|
|
|
30
51
|
// If we had an error instantiating or updating the UI, notify the user.
|
|
31
|
-
|
|
52
|
+
homebridge.toast.error(err.message, "Error");
|
|
32
53
|
} finally {
|
|
33
54
|
|
|
34
55
|
// Always leave the UI in a usable place for the end user.
|
|
35
|
-
|
|
56
|
+
homebridge.hideSpinner();
|
|
36
57
|
}
|
|
37
58
|
}
|
|
38
59
|
|
|
@@ -41,31 +62,31 @@ export class webUi {
|
|
|
41
62
|
|
|
42
63
|
const buttonFirstRun = document.getElementById("firstRun");
|
|
43
64
|
|
|
65
|
+
// Run a custom initialization handler the user may have provided.
|
|
66
|
+
if(!(await this.#processHandler(this.firstRun.onStart))) {
|
|
67
|
+
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
|
|
44
71
|
// First run user experience.
|
|
45
72
|
buttonFirstRun.addEventListener("click", async () => {
|
|
46
73
|
|
|
47
74
|
// Show the beachball while we setup.
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
// Get the list of devices the plugin knows about.
|
|
51
|
-
const devices = await this.homebridge.getCachedAccessories();
|
|
75
|
+
homebridge.showSpinner();
|
|
52
76
|
|
|
53
|
-
//
|
|
54
|
-
|
|
77
|
+
// Run a custom submit handler the user may have provided.
|
|
78
|
+
if(!(await this.#processHandler(this.firstRun.onSubmit))) {
|
|
55
79
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
return aCase > bCase ? 1 : (bCase > aCase ? -1 : 0);
|
|
60
|
-
});
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
61
82
|
|
|
62
83
|
// Create our UI.
|
|
63
84
|
document.getElementById("pageFirstRun").style.display = "none";
|
|
64
85
|
document.getElementById("menuWrapper").style.display = "inline-flex";
|
|
65
|
-
this.featureOptions.
|
|
86
|
+
this.featureOptions.show();
|
|
66
87
|
|
|
67
88
|
// All done. Let the user interact with us, although in practice, we shouldn't get here.
|
|
68
|
-
//
|
|
89
|
+
// homebridge.hideSpinner();
|
|
69
90
|
});
|
|
70
91
|
|
|
71
92
|
document.getElementById("pageFirstRun").style.display = "block";
|
|
@@ -75,81 +96,89 @@ export class webUi {
|
|
|
75
96
|
#showSettings() {
|
|
76
97
|
|
|
77
98
|
// Show the beachball while we setup.
|
|
78
|
-
|
|
99
|
+
homebridge.showSpinner();
|
|
79
100
|
|
|
80
|
-
//
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
document.getElementById("menuFeatureOptions").classList.add("btn-primary");
|
|
85
|
-
document.getElementById("menuSettings").classList.add("btn-elegant");
|
|
86
|
-
document.getElementById("menuSettings").classList.remove("btn-primary");
|
|
101
|
+
// Highlight the tab in our UI.
|
|
102
|
+
this.#toggleClasses("menuHome", "btn-elegant", "btn-primary");
|
|
103
|
+
this.#toggleClasses("menuFeatureOptions", "btn-elegant", "btn-primary");
|
|
104
|
+
this.#toggleClasses("menuSettings", "btn-primary", "btn-elegant");
|
|
87
105
|
|
|
88
106
|
document.getElementById("pageSupport").style.display = "none";
|
|
89
107
|
document.getElementById("pageFeatureOptions").style.display = "none";
|
|
90
108
|
|
|
91
|
-
|
|
109
|
+
homebridge.showSchemaForm();
|
|
92
110
|
|
|
93
111
|
// All done. Let the user interact with us.
|
|
94
|
-
|
|
112
|
+
homebridge.hideSpinner();
|
|
95
113
|
}
|
|
96
114
|
|
|
97
115
|
// Show the support tab.
|
|
98
116
|
#showSupport() {
|
|
99
117
|
|
|
100
118
|
// Show the beachball while we setup.
|
|
101
|
-
|
|
102
|
-
|
|
119
|
+
homebridge.showSpinner();
|
|
120
|
+
homebridge.hideSchemaForm();
|
|
103
121
|
|
|
104
|
-
//
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
document.getElementById("menuFeatureOptions").classList.add("btn-primary");
|
|
109
|
-
document.getElementById("menuSettings").classList.remove("btn-elegant");
|
|
110
|
-
document.getElementById("menuSettings").classList.add("btn-primary");
|
|
122
|
+
// Highlight the tab in our UI.
|
|
123
|
+
this.#toggleClasses("menuHome", "btn-primary", "btn-elegant");
|
|
124
|
+
this.#toggleClasses("menuFeatureOptions", "btn-elegant", "btn-primary");
|
|
125
|
+
this.#toggleClasses("menuSettings", "btn-elegant", "btn-primary");
|
|
111
126
|
|
|
112
127
|
document.getElementById("pageSupport").style.display = "block";
|
|
113
128
|
document.getElementById("pageFeatureOptions").style.display = "none";
|
|
114
129
|
|
|
115
130
|
// All done. Let the user interact with us.
|
|
116
|
-
|
|
131
|
+
homebridge.hideSpinner();
|
|
117
132
|
}
|
|
118
133
|
|
|
119
134
|
// Launch our webUI.
|
|
120
135
|
async #launchWebUI() {
|
|
121
136
|
|
|
122
137
|
// Retrieve the current plugin configuration.
|
|
123
|
-
this.featureOptions.currentConfig = await
|
|
138
|
+
this.featureOptions.currentConfig = await homebridge.getPluginConfig();
|
|
124
139
|
|
|
125
140
|
// Add our event listeners to animate the UI.
|
|
126
141
|
document.getElementById("menuHome").addEventListener("click", () => this.#showSupport());
|
|
127
|
-
document.getElementById("menuFeatureOptions").addEventListener("click", () => this.featureOptions.
|
|
142
|
+
document.getElementById("menuFeatureOptions").addEventListener("click", () => this.featureOptions.show());
|
|
128
143
|
document.getElementById("menuSettings").addEventListener("click", () => this.#showSettings());
|
|
129
144
|
|
|
130
145
|
// Get the list of devices the plugin knows about.
|
|
131
|
-
const devices = await
|
|
146
|
+
const devices = await homebridge.getCachedAccessories();
|
|
132
147
|
|
|
133
148
|
// If we've got devices detected, we launch our feature option UI. Otherwise, we launch our first run UI.
|
|
134
|
-
if(this.featureOptions.currentConfig.length && devices?.length) {
|
|
149
|
+
if(this.featureOptions.currentConfig.length && devices?.length && !(await this.#processHandler(this.firstRun.isRequired))) {
|
|
135
150
|
|
|
136
151
|
document.getElementById("menuWrapper").style.display = "inline-flex";
|
|
137
|
-
this.featureOptions.
|
|
152
|
+
this.featureOptions.show();
|
|
153
|
+
|
|
138
154
|
return;
|
|
139
155
|
}
|
|
140
156
|
|
|
141
|
-
// If we have
|
|
142
|
-
|
|
157
|
+
// If we have the name property set for the plugin configuration yet, let's do so now. If we don't have a configuration, let's initialize it as well.
|
|
158
|
+
(this.featureOptions.currentConfig[0] ??= { name: this.name }).name ??= this.name;
|
|
143
159
|
|
|
144
|
-
|
|
145
|
-
|
|
160
|
+
// Update the plugin configuration and launch the first run UI.
|
|
161
|
+
await homebridge.updatePluginConfig(this.featureOptions.currentConfig);
|
|
162
|
+
this.#showFirstRun();
|
|
163
|
+
}
|
|
146
164
|
|
|
147
|
-
|
|
148
|
-
|
|
165
|
+
// Utility to process user-provided custom handlers that can handle both synchronous and asynchronous handlers.
|
|
166
|
+
async #processHandler(handler) {
|
|
167
|
+
|
|
168
|
+
if(((typeof handler === "function") && !(await handler())) || ((typeof handler !== "function") && !handler)) {
|
|
169
|
+
|
|
170
|
+
return false;
|
|
149
171
|
}
|
|
150
172
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
173
|
+
return true;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Utility to toggle our classes.
|
|
177
|
+
#toggleClasses(id, removeClass, addClass) {
|
|
178
|
+
|
|
179
|
+
const element = document.getElementById(id);
|
|
180
|
+
|
|
181
|
+
element.classList.remove(removeClass);
|
|
182
|
+
element.classList.add(addClass);
|
|
154
183
|
}
|
|
155
184
|
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/* Copyright(C) 2017-2024, HJD (https://github.com/hjdhjd). All rights reserved.
|
|
2
|
+
*
|
|
3
|
+
* ui.mjs: Homebridge Ratgdo webUI.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
"use strict";
|
|
7
|
+
|
|
8
|
+
import { webUi } from "./lib/webUi.mjs";
|
|
9
|
+
|
|
10
|
+
// Parameters for our feature options webUI.
|
|
11
|
+
const featureOptionsParams = { hasControllers: false, sidebar: { deviceLabel: "Ratgdo Devices" } };
|
|
12
|
+
|
|
13
|
+
// Instantiate the webUI.
|
|
14
|
+
const ui = new webUi({ featureOptions: featureOptionsParams, name: "Ratgdo" });
|
|
15
|
+
|
|
16
|
+
// Display the webUI.
|
|
17
|
+
ui.show();
|
package/homebridge-ui/server.js
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "homebridge-ratgdo",
|
|
3
3
|
"displayName": "Homebridge Ratgdo",
|
|
4
|
-
"version": "2.2.
|
|
4
|
+
"version": "2.2.2",
|
|
5
5
|
"description": "HomeKit integration for LiftMaster and Chamberlain garage door openers, without requiring myQ.",
|
|
6
6
|
"license": "ISC",
|
|
7
7
|
"repository": {
|
|
@@ -14,13 +14,15 @@
|
|
|
14
14
|
"type": "module",
|
|
15
15
|
"engines": {
|
|
16
16
|
"node": ">=18.0",
|
|
17
|
-
"homebridge": "
|
|
17
|
+
"homebridge": ">=1.8.0"
|
|
18
18
|
},
|
|
19
19
|
"main": "dist/index.js",
|
|
20
20
|
"scripts": {
|
|
21
|
-
"
|
|
22
|
-
"build
|
|
23
|
-
"
|
|
21
|
+
"prebuild": "npm run clean && npm run build-ui",
|
|
22
|
+
"build": "tsc",
|
|
23
|
+
"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",
|
|
24
|
+
"clean": "shx rm -rf dist homebridge-ui/public/lib",
|
|
25
|
+
"prelint": "npm run build-ui",
|
|
24
26
|
"lint": "eslint eslint.config.mjs src/**.ts homebridge-ui/*.js homebridge-ui/public/**/*.mjs",
|
|
25
27
|
"postpublish": "npm run clean",
|
|
26
28
|
"prepublishOnly": "npm run lint && npm run build"
|
|
@@ -31,18 +33,18 @@
|
|
|
31
33
|
"devDependencies": {
|
|
32
34
|
"@stylistic/eslint-plugin": "^2.1.0",
|
|
33
35
|
"@types/eventsource": "^1.1.15",
|
|
34
|
-
"@types/node": "^20.
|
|
36
|
+
"@types/node": "^20.14.2",
|
|
35
37
|
"eslint": "8.57.0",
|
|
36
38
|
"homebridge": "^1.8.2",
|
|
37
39
|
"shx": "^0.3.4",
|
|
38
40
|
"typescript": "^5.4.5",
|
|
39
|
-
"typescript-eslint": "^7.
|
|
41
|
+
"typescript-eslint": "^7.12.0"
|
|
40
42
|
},
|
|
41
43
|
"dependencies": {
|
|
42
|
-
"@adobe/fetch": "^4.1.
|
|
44
|
+
"@adobe/fetch": "^4.1.4",
|
|
43
45
|
"@homebridge/plugin-ui-utils": "^1.0.3",
|
|
44
46
|
"bonjour-service": "^1.2.1",
|
|
45
47
|
"eventsource": "^2.0.2",
|
|
46
|
-
"homebridge-plugin-utils": "^1.
|
|
48
|
+
"homebridge-plugin-utils": "^1.4.0"
|
|
47
49
|
}
|
|
48
50
|
}
|