homebridge-generac 0.1.0-beta.1
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/CHANGELOG.md +32 -0
- package/LICENSE +200 -0
- package/NOTICE +20 -0
- package/README.md +179 -0
- package/SECURITY.md +17 -0
- package/config.schema.json +133 -0
- package/dist/accessory.d.ts +54 -0
- package/dist/accessory.js +191 -0
- package/dist/accessory.js.map +1 -0
- package/dist/api.d.ts +43 -0
- package/dist/api.js +124 -0
- package/dist/api.js.map +1 -0
- package/dist/attention.d.ts +18 -0
- package/dist/attention.js +40 -0
- package/dist/attention.js.map +1 -0
- package/dist/auth.d.ts +56 -0
- package/dist/auth.js +412 -0
- package/dist/auth.js.map +1 -0
- package/dist/captures.d.ts +9 -0
- package/dist/captures.js +42 -0
- package/dist/captures.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +116 -0
- package/dist/cli.js.map +1 -0
- package/dist/exercise.d.ts +42 -0
- package/dist/exercise.js +115 -0
- package/dist/exercise.js.map +1 -0
- package/dist/files.d.ts +5 -0
- package/dist/files.js +28 -0
- package/dist/files.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/model.d.ts +74 -0
- package/dist/model.js +167 -0
- package/dist/model.js.map +1 -0
- package/dist/platform.d.ts +117 -0
- package/dist/platform.js +596 -0
- package/dist/platform.js.map +1 -0
- package/dist/settings.d.ts +75 -0
- package/dist/settings.js +115 -0
- package/dist/settings.js.map +1 -0
- package/dist/state.d.ts +28 -0
- package/dist/state.js +43 -0
- package/dist/state.js.map +1 -0
- package/dist/types.d.ts +98 -0
- package/dist/types.js +48 -0
- package/dist/types.js.map +1 -0
- package/dist/ui/server.d.ts +96 -0
- package/dist/ui/server.js +328 -0
- package/dist/ui/server.js.map +1 -0
- package/homebridge-ui/public/generac-banner.png +0 -0
- package/homebridge-ui/public/generac.css +255 -0
- package/homebridge-ui/public/index.css +510 -0
- package/homebridge-ui/public/index.html +14 -0
- package/homebridge-ui/public/js/api.js +31 -0
- package/homebridge-ui/public/js/app.js +1 -0
- package/homebridge-ui/public/js/card.js +27 -0
- package/homebridge-ui/public/js/copy.js +167 -0
- package/homebridge-ui/public/js/dom.js +247 -0
- package/homebridge-ui/public/js/footer.js +26 -0
- package/homebridge-ui/public/js/format.js +62 -0
- package/homebridge-ui/public/js/main.js +288 -0
- package/homebridge-ui/public/js/mark.js +34 -0
- package/homebridge-ui/public/js/model.js +120 -0
- package/homebridge-ui/public/js/sections/account.js +272 -0
- package/homebridge-ui/public/js/sections/generators.js +165 -0
- package/homebridge-ui/public/js/sections/settings.js +114 -0
- package/homebridge-ui/public/js/validate.js +45 -0
- package/homebridge-ui/server.js +8 -0
- package/package.json +70 -0
|
@@ -0,0 +1,510 @@
|
|
|
1
|
+
/* Layout for the Generac for Homebridge settings UI: the shell stylesheet of homebridge-notify-switch 1.3.2
|
|
2
|
+
(homebridge-ui/public/index.css) with the rules this page has no use for removed and the root class renamed.
|
|
3
|
+
Colours, controls and typography come from the Bootstrap 5 stylesheet and theme the Homebridge UI injects into
|
|
4
|
+
this iframe. Every colour this file sets is a theme variable with a fallback that follows the surrounding text
|
|
5
|
+
colour, never a fixed grey, so secondary text stays readable in light and dark mode (SPEC section 11.2). */
|
|
6
|
+
|
|
7
|
+
/* The iframe document is never a scroll container. The host sizes the iframe to the page's
|
|
8
|
+
`document.body.scrollHeight` plus 10px and scrolls its modal body around it; the document itself scrolls nothing.
|
|
9
|
+
No fixed or percentage height on either element: the height is the content's. The host links its own stylesheet
|
|
10
|
+
into the iframe after this one, and that stylesheet carries `html, body { height: 100% }`; the rule is written on
|
|
11
|
+
`:root` (a pseudo-class, so it outranks the host's element selectors whatever the order of the sheets). */
|
|
12
|
+
:root,
|
|
13
|
+
:root > body {
|
|
14
|
+
overflow: hidden;
|
|
15
|
+
height: auto;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/* The Homebridge UI shows this page in an iframe inside its settings modal with no padding of its own. The container
|
|
19
|
+
supplies it. Every element on the page keeps its box inside the viewport: no negative margins, no width over 100%. */
|
|
20
|
+
.generac-ui {
|
|
21
|
+
box-sizing: border-box;
|
|
22
|
+
width: 100%;
|
|
23
|
+
max-width: 100%;
|
|
24
|
+
padding: 0 16px 1rem;
|
|
25
|
+
overflow-wrap: anywhere;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.generac-ui *,
|
|
29
|
+
.generac-ui *::before,
|
|
30
|
+
.generac-ui *::after {
|
|
31
|
+
box-sizing: border-box;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/* Theme variables. The Homebridge UI marks dark mode inside this iframe by adding `dark-mode` (and
|
|
35
|
+
`config-ui-x-dark-mode-{theme}`) to the body and colouring the body and the cards itself; it does not put
|
|
36
|
+
Bootstrap's `data-bs-theme` on the root, so Bootstrap's own variables keep their light values in dark mode. Under
|
|
37
|
+
the host's marker the variables this page reads are re-declared with Bootstrap's dark values. */
|
|
38
|
+
body.dark-mode .generac-ui,
|
|
39
|
+
body[class*="config-ui-x-dark-mode"] .generac-ui {
|
|
40
|
+
--bs-secondary-color: rgba(222, 226, 230, 0.75);
|
|
41
|
+
--bs-secondary-color-rgb: 222, 226, 230;
|
|
42
|
+
--bs-tertiary-color: rgba(222, 226, 230, 0.5);
|
|
43
|
+
--bs-border-color: #495057;
|
|
44
|
+
--bs-border-color-translucent: rgba(255, 255, 255, 0.15);
|
|
45
|
+
--bs-secondary-bg: #343a40;
|
|
46
|
+
--bs-link-color: #6ea8fe;
|
|
47
|
+
--bs-link-color-rgb: 110, 168, 254;
|
|
48
|
+
--bs-link-hover-color: #8bb9fe;
|
|
49
|
+
--bs-link-hover-color-rgb: 139, 185, 254;
|
|
50
|
+
--bs-warning-bg-subtle: #3a3524;
|
|
51
|
+
--bs-warning-text-emphasis: #ffe69c;
|
|
52
|
+
--bs-success-bg-subtle: #051b11;
|
|
53
|
+
--bs-success-border-subtle: #0f5132;
|
|
54
|
+
--bs-success-text-emphasis: #75b798;
|
|
55
|
+
--bs-danger-bg-subtle: #2c0b0e;
|
|
56
|
+
--bs-danger-border-subtle: #842029;
|
|
57
|
+
--bs-danger-text-emphasis: #ea868f;
|
|
58
|
+
--bs-info-bg-subtle: #032830;
|
|
59
|
+
--bs-info-border-subtle: #087990;
|
|
60
|
+
--bs-info-text-emphasis: #6edff6;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/* The card's own variables are declared on `.card` by Bootstrap, so the dark value it needs is re-declared there too. */
|
|
64
|
+
body.dark-mode .generac-ui .card,
|
|
65
|
+
body[class*="config-ui-x-dark-mode"] .generac-ui .card {
|
|
66
|
+
--bs-card-cap-bg: #262626;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/* Secondary text, one rule for all of it: field help, hints, empty-state lines, captions, disclosure labels and
|
|
70
|
+
disabled buttons. The theme's secondary colour where the host defines it, otherwise the surrounding text colour. */
|
|
71
|
+
.generac-ui .form-text,
|
|
72
|
+
.generac-ui .ns-secondary,
|
|
73
|
+
.generac-ui details > summary,
|
|
74
|
+
.generac-ui .btn:disabled,
|
|
75
|
+
.generac-ui .btn.disabled {
|
|
76
|
+
color: var(--bs-secondary-color, currentColor);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/* Disabled buttons (Connect while Checking, Sign in while signing in, Continue after three failures, the Reset
|
|
80
|
+
dialog's Confirm until RESET is typed): full opacity, a transparent fill, the border colour and the secondary text
|
|
81
|
+
colour, never Bootstrap's dimmed copy of the enabled button and never the host's dark fill. Pointer events stay
|
|
82
|
+
on so the not-allowed cursor shows (a disabled button fires no click either way). */
|
|
83
|
+
.generac-ui .btn:disabled,
|
|
84
|
+
.generac-ui .btn.disabled,
|
|
85
|
+
.ns-modal .btn:disabled,
|
|
86
|
+
.ns-modal .btn.disabled {
|
|
87
|
+
opacity: 1;
|
|
88
|
+
color: var(--bs-secondary-color, currentColor);
|
|
89
|
+
border-color: var(--bs-border-color, currentColor);
|
|
90
|
+
background-color: transparent;
|
|
91
|
+
cursor: not-allowed;
|
|
92
|
+
pointer-events: auto;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/* Links inside help text and the footer: the theme's link colour (re-declared above for dark mode). */
|
|
96
|
+
.generac-ui .ns-help-link,
|
|
97
|
+
.generac-ui .ns-footer a {
|
|
98
|
+
color: var(--bs-link-color, inherit);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/* The 12-column grid: gap based, no negative margins. */
|
|
102
|
+
.ns-grid {
|
|
103
|
+
display: grid;
|
|
104
|
+
grid-template-columns: repeat(12, minmax(0, 1fr));
|
|
105
|
+
column-gap: 0.5rem;
|
|
106
|
+
row-gap: 0;
|
|
107
|
+
width: 100%;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
.ns-grid > * {
|
|
111
|
+
min-width: 0;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
.ns-span-4 { grid-column: span 4; }
|
|
115
|
+
.ns-span-6 { grid-column: span 6; }
|
|
116
|
+
.ns-span-12 { grid-column: span 12; }
|
|
117
|
+
|
|
118
|
+
/* A single column on phones: every field takes the full width below 600px. */
|
|
119
|
+
@media (max-width: 599.98px) {
|
|
120
|
+
.ns-grid > * {
|
|
121
|
+
grid-column: 1 / -1;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
.lead-copy,
|
|
126
|
+
.section-copy {
|
|
127
|
+
line-height: 1.5;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
.lead-copy {
|
|
131
|
+
margin-bottom: 0.5rem;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
.ns-section {
|
|
135
|
+
margin-top: 1.5rem;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
.ns-section h2 {
|
|
139
|
+
font-weight: 300;
|
|
140
|
+
margin-bottom: 0.5rem;
|
|
141
|
+
padding-bottom: 0.25rem;
|
|
142
|
+
border-bottom: 1px solid var(--bs-border-color, rgba(128, 128, 128, 0.35));
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/* ---- Shell: the card and control rules shared with the author's other plugins. ---- */
|
|
146
|
+
|
|
147
|
+
/* The page banner: the first element of the page, full container width at 4:1, served from the plugin's own folder. */
|
|
148
|
+
.generac-ui .ns-banner {
|
|
149
|
+
display: block;
|
|
150
|
+
width: 100%;
|
|
151
|
+
height: auto;
|
|
152
|
+
aspect-ratio: 4 / 1;
|
|
153
|
+
object-fit: cover;
|
|
154
|
+
border-radius: 6px;
|
|
155
|
+
margin-top: 16px;
|
|
156
|
+
margin-bottom: 1rem;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/* Buttons: 4px radius throughout; small (secondary) actions are 31px tall, 13.5px uppercase; the one section
|
|
160
|
+
primary button is 38px with uppercase 16px text. Text buttons keep their case. Form controls keep their 6px radius. */
|
|
161
|
+
.generac-ui .btn {
|
|
162
|
+
border-radius: 4px;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
.generac-ui .btn-sm {
|
|
166
|
+
min-height: 31px;
|
|
167
|
+
padding: 4px 8px;
|
|
168
|
+
font-size: 13.5px;
|
|
169
|
+
line-height: 21px;
|
|
170
|
+
text-transform: uppercase;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
.generac-ui .ns-section-add {
|
|
174
|
+
min-height: 38px;
|
|
175
|
+
padding: 6px 12px;
|
|
176
|
+
font-size: 16px;
|
|
177
|
+
line-height: 24px;
|
|
178
|
+
text-transform: uppercase;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/* Text buttons (links) and the password field's attached Show / Hide keep their case and size; the attached button
|
|
182
|
+
follows the 38px control it is joined to. */
|
|
183
|
+
.generac-ui .btn-link {
|
|
184
|
+
text-transform: none;
|
|
185
|
+
font-size: inherit;
|
|
186
|
+
line-height: inherit;
|
|
187
|
+
min-height: 0;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
.generac-ui .btn-sm.btn-link {
|
|
191
|
+
padding: 0;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
.generac-ui .input-group > .btn {
|
|
195
|
+
font-size: 13.5px;
|
|
196
|
+
text-transform: uppercase;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/* Form controls: 38px tall, 6px radius, 11px side padding, 16px text; checkboxes 16px. */
|
|
200
|
+
.generac-ui .form-control:not(.form-control-sm) {
|
|
201
|
+
padding-left: 11px;
|
|
202
|
+
padding-right: 11px;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
.generac-ui .form-check-input {
|
|
206
|
+
width: 16px;
|
|
207
|
+
height: 16px;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/* Card header strip: the bold title and the status badges on the left, aligned on the first baseline, so on a phone
|
|
211
|
+
the badges wrap under the title. */
|
|
212
|
+
.generac-ui .ns-card-header {
|
|
213
|
+
display: flex;
|
|
214
|
+
justify-content: space-between;
|
|
215
|
+
align-items: baseline;
|
|
216
|
+
gap: 0.5rem 1rem;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
.generac-ui .ns-card-header .ns-card-title {
|
|
220
|
+
flex: 1 1 auto;
|
|
221
|
+
min-width: 0;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
.ns-card-title {
|
|
225
|
+
display: inline-flex;
|
|
226
|
+
flex-wrap: wrap;
|
|
227
|
+
align-items: center;
|
|
228
|
+
gap: 0.25rem 0.5rem;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
.ns-card-title .badge {
|
|
232
|
+
margin-left: 0;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/* The card's name in the header: bold at weight 700. */
|
|
236
|
+
.generac-ui .ns-card-name {
|
|
237
|
+
font-weight: 700;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/* Badges: 11.5px weight 600, 3px 7px padding, 4px radius; the shell's status badge on the success fill. */
|
|
241
|
+
.generac-ui .badge {
|
|
242
|
+
font-size: 11.5px;
|
|
243
|
+
font-weight: 600;
|
|
244
|
+
padding: 3px 7px;
|
|
245
|
+
border-radius: 4px;
|
|
246
|
+
vertical-align: middle;
|
|
247
|
+
top: 0;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
.generac-ui .badge.text-bg-success {
|
|
251
|
+
color: #fff;
|
|
252
|
+
background-color: var(--bs-success, #198754) !important;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/* Card body: 16px padding with none at the bottom (each field carries 16px below it). */
|
|
256
|
+
.generac-ui .card-body {
|
|
257
|
+
padding: 16px 16px 0;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
.generac-ui .card-body details.ns-advanced {
|
|
261
|
+
margin-bottom: 1rem;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/* The Advanced disclosure line: "▸ Advanced" closed, "▾ Advanced" open, in secondary text at 12.6px. The glyph is drawn
|
|
265
|
+
by the stylesheet so the label itself stays the word. */
|
|
266
|
+
.generac-ui details.ns-advanced > summary {
|
|
267
|
+
list-style: none;
|
|
268
|
+
cursor: pointer;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
.generac-ui details.ns-advanced > summary::-webkit-details-marker {
|
|
272
|
+
display: none;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
.generac-ui details.ns-advanced > summary::before {
|
|
276
|
+
content: "\25B8\00A0";
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
.generac-ui details.ns-advanced[open] > summary::before {
|
|
280
|
+
content: "\25BE\00A0";
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/* Footer strip: 8px 16px on the subtle fill with a 1px rule above (Bootstrap's card footer); the primary action is an
|
|
284
|
+
outlined, link-coloured 31px button. */
|
|
285
|
+
.generac-ui .ns-card-footer {
|
|
286
|
+
padding: 8px 16px;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
.generac-ui .ns-card-footer .btn-outline-primary,
|
|
290
|
+
.generac-ui .ns-footer-action {
|
|
291
|
+
color: var(--bs-link-color, #0d6efd);
|
|
292
|
+
border-color: var(--bs-link-color, #0d6efd);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
.generac-ui .ns-footer-action:not(:disabled):hover,
|
|
296
|
+
.generac-ui .ns-footer-action:not(:disabled):focus-visible {
|
|
297
|
+
color: var(--bs-link-color, #0d6efd);
|
|
298
|
+
background-color: rgba(128, 128, 128, 0.08);
|
|
299
|
+
border-color: var(--bs-link-color, #0d6efd);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/* Every alert box (a Connect error) is left-aligned whatever the host's own alert styling. */
|
|
303
|
+
.generac-ui .alert {
|
|
304
|
+
text-align: start;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/* The danger box (a Connect error) on the theme's danger colours, which the host's own alert rule would otherwise
|
|
308
|
+
replace with its grey box in both themes. Written with the body element so it outranks the host's
|
|
309
|
+
`.config-ui-x-{theme} .alert` rule, which the host loads after this file. */
|
|
310
|
+
body .generac-ui .alert-danger {
|
|
311
|
+
color: var(--bs-danger-text-emphasis, #58151c);
|
|
312
|
+
background-color: var(--bs-danger-bg-subtle, #f8d7da);
|
|
313
|
+
border: 1px solid var(--bs-danger-border-subtle, #f1aeb5);
|
|
314
|
+
box-shadow: none;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/* Text buttons keep their intent colours under the host's dark marker, which paints every `.btn-link` grey: one rule
|
|
318
|
+
reads the button's intent (danger for Reset, the link colour otherwise). */
|
|
319
|
+
body.dark-mode .generac-ui .btn-link,
|
|
320
|
+
body[class*="config-ui-x-dark-mode"] .generac-ui .btn-link {
|
|
321
|
+
color: var(--ns-text-button-color, var(--bs-link-color, #6ea8fe)) !important;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
.generac-ui .ns-danger-link {
|
|
325
|
+
--ns-text-button-color: var(--bs-danger, #dc3545);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/* ---- End of the shell rules. ---- */
|
|
329
|
+
|
|
330
|
+
/* Placeholders read as examples: italic and lighter than typed text. The colour itself is the host's. */
|
|
331
|
+
.generac-ui .form-control::placeholder {
|
|
332
|
+
font-style: italic;
|
|
333
|
+
opacity: 1;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/* Outlined secondary buttons keep their outline on hover and focus with a light highlight instead of Bootstrap's
|
|
337
|
+
solid fill; only a press (active) is a shade darker, so a hovered button never reads as pressed. */
|
|
338
|
+
.generac-ui .btn-outline-secondary:not(:disabled):hover,
|
|
339
|
+
.generac-ui .btn-outline-secondary:not(:disabled):focus,
|
|
340
|
+
.generac-ui .btn-outline-secondary:not(:disabled):focus-visible {
|
|
341
|
+
color: var(--bs-btn-color, inherit);
|
|
342
|
+
background-color: rgba(128, 128, 128, 0.08);
|
|
343
|
+
border-color: var(--bs-border-color, #6c757d);
|
|
344
|
+
box-shadow: none;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
.generac-ui .btn-outline-secondary:not(:disabled):active,
|
|
348
|
+
.generac-ui .btn-outline-secondary:not(:disabled).active {
|
|
349
|
+
color: var(--bs-btn-color, inherit);
|
|
350
|
+
background-color: rgba(128, 128, 128, 0.2);
|
|
351
|
+
border-color: var(--bs-border-color, #6c757d);
|
|
352
|
+
box-shadow: none;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
.generac-ui .btn-outline-secondary:not(:disabled):focus-visible {
|
|
356
|
+
box-shadow: 0 0 0 0.2rem rgba(128, 128, 128, 0.25);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
.form-label {
|
|
360
|
+
font-weight: 600;
|
|
361
|
+
margin-bottom: 0.25rem;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
.form-text {
|
|
365
|
+
line-height: 1.4;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
.has-issue > .invalid-feedback {
|
|
369
|
+
display: block;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
.status-box:empty {
|
|
373
|
+
display: none;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
/* Link-style buttons (Cancel, Keep, Disconnect) and the red text button (Reset). */
|
|
377
|
+
.ns-link-button {
|
|
378
|
+
text-decoration: none;
|
|
379
|
+
vertical-align: baseline;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
.ns-link-button:hover,
|
|
383
|
+
.ns-link-button:focus {
|
|
384
|
+
text-decoration: underline;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
/* Read-only and disabled controls: the theme's secondary background (re-declared for dark mode above) behind the
|
|
388
|
+
surrounding text colour, so a disabled field is never a light box with dark text on a dark page. */
|
|
389
|
+
.generac-ui .form-control[readonly],
|
|
390
|
+
.generac-ui .form-control:disabled {
|
|
391
|
+
background-color: var(--bs-secondary-bg, rgba(128, 128, 128, 0.12));
|
|
392
|
+
color: inherit;
|
|
393
|
+
opacity: 1;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
/* Card footer: one outlined action on the right (Rename), or the Disconnect link and its in-place confirmation. */
|
|
397
|
+
.ns-card-footer {
|
|
398
|
+
display: flex;
|
|
399
|
+
flex-direction: row-reverse;
|
|
400
|
+
flex-wrap: wrap;
|
|
401
|
+
align-items: center;
|
|
402
|
+
gap: 0.5rem;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
.ns-footer-right {
|
|
406
|
+
display: flex;
|
|
407
|
+
flex-wrap: wrap;
|
|
408
|
+
align-items: center;
|
|
409
|
+
gap: 0.5rem;
|
|
410
|
+
min-width: 0;
|
|
411
|
+
flex: 1 1 auto;
|
|
412
|
+
justify-content: flex-end;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
.ns-footer-right:empty {
|
|
416
|
+
display: none;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
/* Touch targets: every button, text buttons included, is at least 44px tall on touch devices. */
|
|
420
|
+
@media (pointer: coarse) {
|
|
421
|
+
.generac-ui .btn,
|
|
422
|
+
.generac-ui .btn-close,
|
|
423
|
+
.ns-modal .btn,
|
|
424
|
+
.ns-modal .btn-close {
|
|
425
|
+
min-height: 44px;
|
|
426
|
+
display: inline-flex;
|
|
427
|
+
align-items: center;
|
|
428
|
+
justify-content: center;
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
body.ns-touch .generac-ui .btn,
|
|
433
|
+
body.ns-touch .generac-ui .btn-close,
|
|
434
|
+
body.ns-touch .ns-modal .btn,
|
|
435
|
+
body.ns-touch .ns-modal .btn-close {
|
|
436
|
+
min-height: 44px;
|
|
437
|
+
display: inline-flex;
|
|
438
|
+
align-items: center;
|
|
439
|
+
justify-content: center;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
/* Modal drawn by the page (the Reset dialog). */
|
|
443
|
+
.ns-modal-backdrop {
|
|
444
|
+
position: fixed;
|
|
445
|
+
inset: 0;
|
|
446
|
+
z-index: 50;
|
|
447
|
+
background: rgba(0, 0, 0, 0.55);
|
|
448
|
+
display: flex;
|
|
449
|
+
align-items: center;
|
|
450
|
+
justify-content: center;
|
|
451
|
+
padding: 1rem;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
.ns-modal {
|
|
455
|
+
background: var(--bs-body-bg, #fff);
|
|
456
|
+
color: var(--bs-body-color, inherit);
|
|
457
|
+
border-radius: 0.5rem;
|
|
458
|
+
width: 100%;
|
|
459
|
+
max-width: 28rem;
|
|
460
|
+
max-height: 100%;
|
|
461
|
+
overflow: auto;
|
|
462
|
+
box-shadow: 0 0.5rem 1.5rem rgba(0, 0, 0, 0.3);
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
.ns-modal-header {
|
|
466
|
+
display: flex;
|
|
467
|
+
justify-content: space-between;
|
|
468
|
+
align-items: center;
|
|
469
|
+
padding: 0.75rem 1rem;
|
|
470
|
+
border-bottom: 1px solid var(--bs-border-color, rgba(128, 128, 128, 0.35));
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
.ns-modal-body {
|
|
474
|
+
padding: 1rem;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
.ns-modal-actions {
|
|
478
|
+
display: flex;
|
|
479
|
+
flex-wrap: wrap;
|
|
480
|
+
justify-content: flex-end;
|
|
481
|
+
gap: 0.5rem;
|
|
482
|
+
padding: 0.75rem 1rem;
|
|
483
|
+
border-top: 1px solid var(--bs-border-color, rgba(128, 128, 128, 0.35));
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
/* Version and credit footer: one line of secondary text that wraps between its items on narrow screens. */
|
|
487
|
+
.ns-footer {
|
|
488
|
+
margin-top: 1.5rem;
|
|
489
|
+
padding-top: 0.75rem;
|
|
490
|
+
border-top: 1px solid var(--bs-border-color, rgba(128, 128, 128, 0.35));
|
|
491
|
+
line-height: 1.5;
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
.ns-footer-item {
|
|
495
|
+
white-space: nowrap;
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
/* The brand mark beside the plugin name in the footer credit: 20px, in the text colour (currentColor). */
|
|
499
|
+
.ns-mark-label {
|
|
500
|
+
display: inline-flex;
|
|
501
|
+
align-items: center;
|
|
502
|
+
gap: 0.35em;
|
|
503
|
+
vertical-align: bottom;
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
.ns-mark-svg {
|
|
507
|
+
width: 20px;
|
|
508
|
+
height: 20px;
|
|
509
|
+
flex: 0 0 auto;
|
|
510
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
+
<title>Generac for Homebridge settings</title>
|
|
7
|
+
<link rel="stylesheet" href="index.css">
|
|
8
|
+
<link rel="stylesheet" href="generac.css">
|
|
9
|
+
</head>
|
|
10
|
+
<body>
|
|
11
|
+
<div id="app" class="generac-ui"></div>
|
|
12
|
+
<script type="module" src="js/main.js"></script>
|
|
13
|
+
</body>
|
|
14
|
+
</html>
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wrappers over the `homebridge` object the Homebridge UI injects into the settings iframe. A request the server
|
|
3
|
+
* could not answer resolves to `undefined`; callers show the network copy.
|
|
4
|
+
*/
|
|
5
|
+
import { TOAST_TITLE } from './copy.js';
|
|
6
|
+
function hb() {
|
|
7
|
+
return window.homebridge;
|
|
8
|
+
}
|
|
9
|
+
export async function callServer(path, payload = {}) {
|
|
10
|
+
try {
|
|
11
|
+
const result = (await hb().request(path, payload));
|
|
12
|
+
return result && typeof result === 'object' ? result : undefined;
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
return undefined;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
export function toastError(message) {
|
|
19
|
+
hb().toast.error(message, TOAST_TITLE);
|
|
20
|
+
}
|
|
21
|
+
export function toastSuccess(message) {
|
|
22
|
+
hb().toast.success(message, TOAST_TITLE);
|
|
23
|
+
}
|
|
24
|
+
export function setSaveEnabled(enabled) {
|
|
25
|
+
if (enabled) {
|
|
26
|
+
hb().enableSaveButton();
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
hb().disableSaveButton();
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The shell card (homebridge-notify-switch homebridge-ui/src/card.ts, the pieces this page needs): a header strip
|
|
3
|
+
* with the bold title and its badges, a body, and a footer strip with at most one outlined action on the right.
|
|
4
|
+
*/
|
|
5
|
+
import { el } from './dom.js';
|
|
6
|
+
/**
|
|
7
|
+
* A badge for a card header or a status row. `status` is the shell's success-filled status badge (Connected);
|
|
8
|
+
* the rest follow SPEC section 11.2: success (Ready), info (Running, Exercising), danger (Fault) and secondary
|
|
9
|
+
* (Not responding) subtle variables, warning for Maintenance due and Low, an outline for Checking.
|
|
10
|
+
*/
|
|
11
|
+
export function badge(text, kind) {
|
|
12
|
+
if (kind === 'status') {
|
|
13
|
+
return el('span', { class: 'badge text-bg-success ns-status-badge' }, text);
|
|
14
|
+
}
|
|
15
|
+
return el('span', { class: `badge gn-badge gn-badge-${kind}` }, text);
|
|
16
|
+
}
|
|
17
|
+
export function card(opts) {
|
|
18
|
+
const node = el('div', { class: `card ns-card${opts.cls ? ` ${opts.cls}` : ''}` }, el('div', { class: 'card-header ns-card-header' }, el('span', { class: 'ns-card-title' }, opts.title, ...(opts.badges ?? []))), el('div', { class: 'card-body' }, ...opts.body));
|
|
19
|
+
if (opts.footerRight) {
|
|
20
|
+
node.appendChild(el('div', { class: 'card-footer ns-card-footer' }, el('div', { class: 'ns-footer-right' }, opts.footerRight)));
|
|
21
|
+
}
|
|
22
|
+
return node;
|
|
23
|
+
}
|
|
24
|
+
/** The bold card name, live from the Name field where there is one. */
|
|
25
|
+
export function cardName(text) {
|
|
26
|
+
return el('span', { class: 'ns-card-name' }, text);
|
|
27
|
+
}
|