@mp-consulting/homebridge-unifi-access 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.
Files changed (74) hide show
  1. package/.claude/settings.local.json +91 -0
  2. package/CHANGELOG.md +13 -0
  3. package/LICENSE.md +22 -0
  4. package/README.md +159 -0
  5. package/config.schema.json +202 -0
  6. package/dist/access-controller.d.ts +41 -0
  7. package/dist/access-controller.js +342 -0
  8. package/dist/access-controller.js.map +1 -0
  9. package/dist/access-device-catalog.d.ts +43 -0
  10. package/dist/access-device-catalog.js +151 -0
  11. package/dist/access-device-catalog.js.map +1 -0
  12. package/dist/access-device.d.ts +68 -0
  13. package/dist/access-device.js +330 -0
  14. package/dist/access-device.js.map +1 -0
  15. package/dist/access-events.d.ts +27 -0
  16. package/dist/access-events.js +152 -0
  17. package/dist/access-events.js.map +1 -0
  18. package/dist/access-options.d.ts +32 -0
  19. package/dist/access-options.js +65 -0
  20. package/dist/access-options.js.map +1 -0
  21. package/dist/access-platform.d.ts +15 -0
  22. package/dist/access-platform.js +74 -0
  23. package/dist/access-platform.js.map +1 -0
  24. package/dist/access-types.d.ts +30 -0
  25. package/dist/access-types.js +42 -0
  26. package/dist/access-types.js.map +1 -0
  27. package/dist/hub/access-hub-api.d.ts +13 -0
  28. package/dist/hub/access-hub-api.js +140 -0
  29. package/dist/hub/access-hub-api.js.map +1 -0
  30. package/dist/hub/access-hub-events.d.ts +2 -0
  31. package/dist/hub/access-hub-events.js +229 -0
  32. package/dist/hub/access-hub-events.js.map +1 -0
  33. package/dist/hub/access-hub-mqtt.d.ts +2 -0
  34. package/dist/hub/access-hub-mqtt.js +137 -0
  35. package/dist/hub/access-hub-mqtt.js.map +1 -0
  36. package/dist/hub/access-hub-services.d.ts +4 -0
  37. package/dist/hub/access-hub-services.js +451 -0
  38. package/dist/hub/access-hub-services.js.map +1 -0
  39. package/dist/hub/access-hub-types.d.ts +145 -0
  40. package/dist/hub/access-hub-types.js +35 -0
  41. package/dist/hub/access-hub-types.js.map +1 -0
  42. package/dist/hub/access-hub-utils.d.ts +20 -0
  43. package/dist/hub/access-hub-utils.js +128 -0
  44. package/dist/hub/access-hub-utils.js.map +1 -0
  45. package/dist/hub/access-hub.d.ts +39 -0
  46. package/dist/hub/access-hub.js +185 -0
  47. package/dist/hub/access-hub.js.map +1 -0
  48. package/dist/hub/index.d.ts +4 -0
  49. package/dist/hub/index.js +7 -0
  50. package/dist/hub/index.js.map +1 -0
  51. package/dist/index.d.ts +3 -0
  52. package/dist/index.js +11 -0
  53. package/dist/index.js.map +1 -0
  54. package/dist/settings.d.ts +16 -0
  55. package/dist/settings.js +49 -0
  56. package/dist/settings.js.map +1 -0
  57. package/docs/FeatureOptions.md +120 -0
  58. package/docs/MQTT.md +116 -0
  59. package/docs/api_reference.pdf +0 -0
  60. package/docs/media/homebridge-unifi-access.png +0 -0
  61. package/docs/media/homebridge-unifi-access.svg +21 -0
  62. package/eslint.config.mjs +99 -0
  63. package/homebridge-ui/public/app.js +104 -0
  64. package/homebridge-ui/public/index.html +267 -0
  65. package/homebridge-ui/public/modules/constants.js +22 -0
  66. package/homebridge-ui/public/modules/controllers.js +202 -0
  67. package/homebridge-ui/public/modules/discovery.js +89 -0
  68. package/homebridge-ui/public/modules/dom-helpers.js +41 -0
  69. package/homebridge-ui/public/modules/feature-options.js +625 -0
  70. package/homebridge-ui/public/modules/state.js +26 -0
  71. package/homebridge-ui/public/styles.css +533 -0
  72. package/homebridge-ui/server.js +374 -0
  73. package/package.json +83 -0
  74. package/scripts/event-schema-monitor.ts +350 -0
@@ -0,0 +1,533 @@
1
+ /* @mp-consulting/homebridge-unifi-access Custom UI Styles */
2
+
3
+ /* ============================================================================
4
+ SCREENS
5
+ ============================================================================ */
6
+
7
+ #discoveryScreen,
8
+ #setupScreen,
9
+ #controllersScreen,
10
+ #featureOptionsScreen,
11
+ #supportScreen {
12
+ display: none;
13
+ }
14
+
15
+ /* ============================================================================
16
+ DISCOVERY
17
+ ============================================================================ */
18
+
19
+ .spinner-border-sm {
20
+ display: none;
21
+ }
22
+
23
+ /* ============================================================================
24
+ GENERAL
25
+ ============================================================================ */
26
+
27
+ .list-group {
28
+ border: 1px solid rgba(0, 0, 0, 0.125);
29
+ border-radius: 0.25rem;
30
+ }
31
+
32
+ /* ============================================================================
33
+ CARDS
34
+ ============================================================================ */
35
+
36
+ .card {
37
+ border: none;
38
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
39
+ transition: box-shadow 0.2s ease;
40
+ }
41
+
42
+ .card:hover {
43
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.12);
44
+ }
45
+
46
+ .card-header {
47
+ font-weight: 600;
48
+ font-size: 0.95rem;
49
+ }
50
+
51
+ /* ============================================================================
52
+ SCOPE SIDEBAR
53
+ ============================================================================ */
54
+
55
+ .scope-sidebar {
56
+ position: sticky;
57
+ top: 0;
58
+ }
59
+
60
+ .scope-legend-bar {
61
+ display: inline-block;
62
+ width: 14px;
63
+ height: 4px;
64
+ border-radius: 2px;
65
+ flex-shrink: 0;
66
+ }
67
+
68
+ /* ============================================================================
69
+ SCOPE CASCADE DIAGRAM (vertical)
70
+ ============================================================================ */
71
+
72
+ .scope-cascade {
73
+ display: flex;
74
+ flex-direction: column;
75
+ gap: 0;
76
+ }
77
+
78
+ .scope-level {
79
+ display: flex;
80
+ align-items: center;
81
+ gap: 10px;
82
+ padding: 6px 0;
83
+ opacity: 0.3;
84
+ transition: opacity 0.3s ease;
85
+ }
86
+
87
+ .scope-level.active {
88
+ opacity: 1;
89
+ }
90
+
91
+ .scope-level.inherited {
92
+ opacity: 0.6;
93
+ }
94
+
95
+ .scope-dot {
96
+ width: 32px;
97
+ height: 32px;
98
+ min-width: 32px;
99
+ border-radius: 50%;
100
+ display: flex;
101
+ align-items: center;
102
+ justify-content: center;
103
+ font-size: 0.75rem;
104
+ color: white;
105
+ transition: box-shadow 0.3s ease;
106
+ }
107
+
108
+ .scope-level[data-scope="global"] .scope-dot {
109
+ background-color: #ffc107;
110
+ color: #664d00;
111
+ }
112
+
113
+ .scope-level[data-scope="controller"] .scope-dot {
114
+ background-color: #198754;
115
+ }
116
+
117
+ .scope-level[data-scope="device"] .scope-dot {
118
+ background-color: #0dcaf0;
119
+ color: #055160;
120
+ }
121
+
122
+ .scope-level.active .scope-dot {
123
+ box-shadow: 0 0 0 3px rgba(var(--scope-rgb), 0.3);
124
+ }
125
+
126
+ .scope-level[data-scope="global"].active .scope-dot {
127
+ --scope-rgb: 255, 193, 7;
128
+ }
129
+
130
+ .scope-level[data-scope="controller"].active .scope-dot {
131
+ --scope-rgb: 25, 135, 84;
132
+ }
133
+
134
+ .scope-level[data-scope="device"].active .scope-dot {
135
+ --scope-rgb: 13, 202, 240;
136
+ }
137
+
138
+ .scope-text {
139
+ min-width: 0;
140
+ }
141
+
142
+ .scope-label {
143
+ font-weight: 600;
144
+ font-size: 0.8rem;
145
+ line-height: 1.2;
146
+ }
147
+
148
+ .scope-hint {
149
+ font-size: 0.65rem;
150
+ color: #6c757d;
151
+ line-height: 1.2;
152
+ }
153
+
154
+ .scope-connector {
155
+ display: flex;
156
+ justify-content: center;
157
+ padding-left: 15px;
158
+ height: 12px;
159
+ }
160
+
161
+ .scope-connector-line {
162
+ width: 2px;
163
+ height: 100%;
164
+ background: #dee2e6;
165
+ transition: background 0.3s ease;
166
+ }
167
+
168
+ .scope-connector.active .scope-connector-line {
169
+ background: #495057;
170
+ }
171
+
172
+ /* ============================================================================
173
+ FEATURE OPTIONS - CATEGORY CARDS
174
+ ============================================================================ */
175
+
176
+ #optionsContainer .card-header {
177
+ user-select: none;
178
+ }
179
+
180
+ #optionsContainer .card-header:hover {
181
+ background-color: rgba(0, 0, 0, 0.02);
182
+ }
183
+
184
+ #optionsContainer .form-check-input {
185
+ cursor: pointer;
186
+ }
187
+
188
+ #optionsContainer .form-check-label {
189
+ cursor: pointer;
190
+ }
191
+
192
+ /* Category accent borders */
193
+ .category-card {
194
+ border-left: 4px solid var(--category-color, #6c757d) !important;
195
+ }
196
+
197
+ /* Category icon circles */
198
+ .category-icon {
199
+ display: inline-flex;
200
+ align-items: center;
201
+ justify-content: center;
202
+ width: 26px;
203
+ height: 26px;
204
+ min-width: 26px;
205
+ border-radius: 50%;
206
+ font-size: 0.7rem;
207
+ color: white;
208
+ background-color: var(--category-color, #6c757d);
209
+ }
210
+
211
+ /* Category summary count */
212
+ .category-summary {
213
+ font-size: 0.75rem;
214
+ color: #6c757d;
215
+ white-space: nowrap;
216
+ }
217
+
218
+ /* Category progress bar */
219
+ .category-progress {
220
+ height: 4px;
221
+ background: rgba(0, 0, 0, 0.06);
222
+ border-radius: 2px;
223
+ overflow: hidden;
224
+ min-width: 40px;
225
+ }
226
+
227
+ .category-progress-fill {
228
+ height: 100%;
229
+ border-radius: 2px;
230
+ background-color: var(--category-color, #6c757d);
231
+ transition: width 0.3s ease;
232
+ }
233
+
234
+ /* Toggle icon */
235
+ .toggle-icon {
236
+ transition: transform 0.3s ease;
237
+ font-size: 0.8rem;
238
+ opacity: 0.4;
239
+ }
240
+
241
+ /* Collapse */
242
+ .category-body {
243
+ display: none;
244
+ }
245
+
246
+ .category-body.open {
247
+ display: block;
248
+ }
249
+
250
+ /* ============================================================================
251
+ FEATURE OPTIONS - OPTION ITEMS
252
+ ============================================================================ */
253
+
254
+ /* Scope-colored left border */
255
+ .option-item {
256
+ border-left: 3px solid transparent !important;
257
+ transition: border-color 0.2s ease, background-color 0.2s ease;
258
+ }
259
+
260
+ .option-item.scope-device {
261
+ border-left-color: #0dcaf0 !important;
262
+ background-color: rgba(13, 202, 240, 0.04);
263
+ }
264
+
265
+ .option-item.scope-controller {
266
+ border-left-color: #198754 !important;
267
+ background-color: rgba(25, 135, 84, 0.04);
268
+ }
269
+
270
+ .option-item.scope-global {
271
+ border-left-color: #ffc107 !important;
272
+ background-color: rgba(255, 193, 7, 0.04);
273
+ }
274
+
275
+ /* Non-default indicator */
276
+ .option-item.non-default {
277
+ background-color: rgba(108, 117, 125, 0.03);
278
+ }
279
+
280
+ /* Default value indicator badge */
281
+ .default-indicator {
282
+ font-size: 0.65rem;
283
+ font-weight: 500;
284
+ padding: 1px 5px;
285
+ border-radius: 3px;
286
+ text-transform: uppercase;
287
+ letter-spacing: 0.03em;
288
+ }
289
+
290
+ .default-on {
291
+ background-color: rgba(25, 135, 84, 0.1);
292
+ color: #198754;
293
+ }
294
+
295
+ .default-off {
296
+ background-color: rgba(108, 117, 125, 0.1);
297
+ color: #6c757d;
298
+ }
299
+
300
+ /* Grouped/indented option */
301
+ .option-grouped {
302
+ padding-left: 2rem !important;
303
+ }
304
+
305
+ .option-grouped::before {
306
+ content: "";
307
+ position: absolute;
308
+ left: 14px;
309
+ top: 0;
310
+ bottom: 0;
311
+ width: 2px;
312
+ background: rgba(0, 0, 0, 0.06);
313
+ }
314
+
315
+ /* Option name styling */
316
+ .option-name {
317
+ font-weight: 600;
318
+ font-size: 0.9rem;
319
+ }
320
+
321
+ .option-description {
322
+ font-size: 0.8rem;
323
+ line-height: 1.4;
324
+ opacity: 0.75;
325
+ }
326
+
327
+ /* Reset button */
328
+ .reset-option-btn {
329
+ opacity: 0.5;
330
+ transition: opacity 0.15s ease;
331
+ }
332
+
333
+ .option-item:hover .reset-option-btn {
334
+ opacity: 1;
335
+ }
336
+
337
+ /* Modified count summary */
338
+ .modified-summary {
339
+ font-size: 0.8rem;
340
+ font-weight: 500;
341
+ }
342
+
343
+ /* ============================================================================
344
+ LIST GROUP ITEMS
345
+ ============================================================================ */
346
+
347
+ .list-group-item {
348
+ transition: background-color 0.15s ease;
349
+ position: relative;
350
+ }
351
+
352
+ .list-group-item i {
353
+ width: 1.25em;
354
+ text-align: center;
355
+ }
356
+
357
+ .list-group-item a {
358
+ text-decoration: none;
359
+ }
360
+
361
+ .list-group-item a:hover {
362
+ text-decoration: underline;
363
+ }
364
+
365
+ /* ============================================================================
366
+ DEVICE INFO PANEL
367
+ ============================================================================ */
368
+
369
+ #deviceInfoPanel code {
370
+ font-size: 0.85rem;
371
+ }
372
+
373
+ /* ============================================================================
374
+ DARK MODE SUPPORT
375
+ ============================================================================ */
376
+
377
+ @media (prefers-color-scheme: dark) {
378
+ .text-muted {
379
+ color: #a0a0a0 !important;
380
+ }
381
+
382
+ .card {
383
+ background-color: rgba(255, 255, 255, 0.05);
384
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
385
+ }
386
+
387
+ .card:hover {
388
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
389
+ }
390
+
391
+ .card-header {
392
+ background-color: rgba(255, 255, 255, 0.05) !important;
393
+ border-bottom-color: rgba(255, 255, 255, 0.1) !important;
394
+ }
395
+
396
+ #optionsContainer .card-header:hover {
397
+ background-color: rgba(255, 255, 255, 0.08) !important;
398
+ }
399
+
400
+ .list-group-item {
401
+ border-color: rgba(255, 255, 255, 0.08);
402
+ }
403
+
404
+ .category-progress {
405
+ background: rgba(255, 255, 255, 0.08);
406
+ }
407
+
408
+ .option-item.scope-device {
409
+ background-color: rgba(13, 202, 240, 0.08);
410
+ }
411
+
412
+ .option-item.scope-controller {
413
+ background-color: rgba(25, 135, 84, 0.08);
414
+ }
415
+
416
+ .option-item.scope-global {
417
+ background-color: rgba(255, 193, 7, 0.08);
418
+ }
419
+
420
+ .option-grouped::before {
421
+ background: rgba(255, 255, 255, 0.1);
422
+ }
423
+
424
+ .default-on {
425
+ background-color: rgba(25, 135, 84, 0.2);
426
+ }
427
+
428
+ .default-off {
429
+ background-color: rgba(108, 117, 125, 0.2);
430
+ }
431
+
432
+ .scope-hint {
433
+ color: #a0a0a0;
434
+ }
435
+
436
+ .scope-connector-line {
437
+ background: rgba(255, 255, 255, 0.1);
438
+ }
439
+
440
+ .scope-connector.active .scope-connector-line {
441
+ background: rgba(255, 255, 255, 0.3);
442
+ }
443
+
444
+ }
445
+
446
+ [data-theme="dark"] .text-muted,
447
+ .dark-mode .text-muted {
448
+ color: #a0a0a0 !important;
449
+ }
450
+
451
+ [data-theme="dark"] .card,
452
+ .dark-mode .card {
453
+ background-color: rgba(255, 255, 255, 0.05);
454
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
455
+ }
456
+
457
+ [data-theme="dark"] .card:hover,
458
+ .dark-mode .card:hover {
459
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
460
+ }
461
+
462
+ [data-theme="dark"] .card-header,
463
+ .dark-mode .card-header {
464
+ background-color: rgba(255, 255, 255, 0.05) !important;
465
+ border-bottom-color: rgba(255, 255, 255, 0.1) !important;
466
+ }
467
+
468
+ [data-theme="dark"] #optionsContainer .card-header:hover,
469
+ .dark-mode #optionsContainer .card-header:hover {
470
+ background-color: rgba(255, 255, 255, 0.08) !important;
471
+ }
472
+
473
+ [data-theme="dark"] .list-group-item,
474
+ .dark-mode .list-group-item {
475
+ border-color: rgba(255, 255, 255, 0.08);
476
+ }
477
+
478
+ [data-theme="dark"] .badge.bg-secondary,
479
+ .dark-mode .badge.bg-secondary {
480
+ background-color: #6c757d !important;
481
+ color: #fff !important;
482
+ }
483
+
484
+ [data-theme="dark"] .category-progress,
485
+ .dark-mode .category-progress {
486
+ background: rgba(255, 255, 255, 0.08);
487
+ }
488
+
489
+ [data-theme="dark"] .option-item.scope-device,
490
+ .dark-mode .option-item.scope-device {
491
+ background-color: rgba(13, 202, 240, 0.08);
492
+ }
493
+
494
+ [data-theme="dark"] .option-item.scope-controller,
495
+ .dark-mode .option-item.scope-controller {
496
+ background-color: rgba(25, 135, 84, 0.08);
497
+ }
498
+
499
+ [data-theme="dark"] .option-item.scope-global,
500
+ .dark-mode .option-item.scope-global {
501
+ background-color: rgba(255, 193, 7, 0.08);
502
+ }
503
+
504
+ [data-theme="dark"] .option-grouped::before,
505
+ .dark-mode .option-grouped::before {
506
+ background: rgba(255, 255, 255, 0.1);
507
+ }
508
+
509
+ [data-theme="dark"] .default-on,
510
+ .dark-mode .default-on {
511
+ background-color: rgba(25, 135, 84, 0.2);
512
+ }
513
+
514
+ [data-theme="dark"] .default-off,
515
+ .dark-mode .default-off {
516
+ background-color: rgba(108, 117, 125, 0.2);
517
+ }
518
+
519
+ [data-theme="dark"] .scope-hint,
520
+ .dark-mode .scope-hint {
521
+ color: #a0a0a0;
522
+ }
523
+
524
+ [data-theme="dark"] .scope-connector-line,
525
+ .dark-mode .scope-connector-line {
526
+ background: rgba(255, 255, 255, 0.1);
527
+ }
528
+
529
+ [data-theme="dark"] .scope-connector.active .scope-connector-line,
530
+ .dark-mode .scope-connector.active .scope-connector-line {
531
+ background: rgba(255, 255, 255, 0.3);
532
+ }
533
+