@homebridge-plugins/homebridge-eufy-security 0.0.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.
Files changed (185) hide show
  1. package/CHANGELOG.md +5 -0
  2. package/FUNDING.yml +1 -0
  3. package/LICENSE +176 -0
  4. package/README.md +67 -0
  5. package/config.schema.json +6 -0
  6. package/dist/accessories/AutoSyncStationAccessory.js +156 -0
  7. package/dist/accessories/AutoSyncStationAccessory.js.map +1 -0
  8. package/dist/accessories/BaseAccessory.js +247 -0
  9. package/dist/accessories/BaseAccessory.js.map +1 -0
  10. package/dist/accessories/CameraAccessory.js +431 -0
  11. package/dist/accessories/CameraAccessory.js.map +1 -0
  12. package/dist/accessories/Device.js +67 -0
  13. package/dist/accessories/Device.js.map +1 -0
  14. package/dist/accessories/EntrySensorAccessory.js +48 -0
  15. package/dist/accessories/EntrySensorAccessory.js.map +1 -0
  16. package/dist/accessories/LockAccessory.js +142 -0
  17. package/dist/accessories/LockAccessory.js.map +1 -0
  18. package/dist/accessories/MotionSensorAccessory.js +48 -0
  19. package/dist/accessories/MotionSensorAccessory.js.map +1 -0
  20. package/dist/accessories/SmartDropAccessory.js +145 -0
  21. package/dist/accessories/SmartDropAccessory.js.map +1 -0
  22. package/dist/accessories/StationAccessory.js +371 -0
  23. package/dist/accessories/StationAccessory.js.map +1 -0
  24. package/dist/config.js +25 -0
  25. package/dist/config.js.map +1 -0
  26. package/dist/controller/LocalLivestreamManager.js +116 -0
  27. package/dist/controller/LocalLivestreamManager.js.map +1 -0
  28. package/dist/controller/recordingDelegate.js +208 -0
  29. package/dist/controller/recordingDelegate.js.map +1 -0
  30. package/dist/controller/snapshotDelegate.js +345 -0
  31. package/dist/controller/snapshotDelegate.js.map +1 -0
  32. package/dist/controller/streamingDelegate.js +345 -0
  33. package/dist/controller/streamingDelegate.js.map +1 -0
  34. package/dist/index.js +11 -0
  35. package/dist/index.js.map +1 -0
  36. package/dist/interfaces.js +2 -0
  37. package/dist/interfaces.js.map +1 -0
  38. package/dist/media/Snapshot-Unavailable.png +0 -0
  39. package/dist/media/Snapshot-Unavailable.xcf +0 -0
  40. package/dist/media/Snapshot-black.png +0 -0
  41. package/dist/media/camera-disabled.png +0 -0
  42. package/dist/media/camera-offline.png +0 -0
  43. package/dist/media/media/Snapshot-Unavailable.png +0 -0
  44. package/dist/media/media/Snapshot-Unavailable.xcf +0 -0
  45. package/dist/media/media/Snapshot-black.png +0 -0
  46. package/dist/media/media/camera-disabled.png +0 -0
  47. package/dist/media/media/camera-offline.png +0 -0
  48. package/dist/platform.js +716 -0
  49. package/dist/platform.js.map +1 -0
  50. package/dist/settings.js +38 -0
  51. package/dist/settings.js.map +1 -0
  52. package/dist/utils/Talkback.js +92 -0
  53. package/dist/utils/Talkback.js.map +1 -0
  54. package/dist/utils/accessoriesStore.js +206 -0
  55. package/dist/utils/accessoriesStore.js.map +1 -0
  56. package/dist/utils/configTypes.js +35 -0
  57. package/dist/utils/configTypes.js.map +1 -0
  58. package/dist/utils/ffmpeg.js +843 -0
  59. package/dist/utils/ffmpeg.js.map +1 -0
  60. package/dist/utils/interfaces.js +8 -0
  61. package/dist/utils/interfaces.js.map +1 -0
  62. package/dist/utils/utils.js +44 -0
  63. package/dist/utils/utils.js.map +1 -0
  64. package/dist/version.js +2 -0
  65. package/dist/version.js.map +1 -0
  66. package/eslint.config.mjs +18 -0
  67. package/homebridge-eufy-security.png +0 -0
  68. package/homebridge-ui/public/app.js +225 -0
  69. package/homebridge-ui/public/assets/devices/4g_lte_starlight_large.jpg +0 -0
  70. package/homebridge-ui/public/assets/devices/BATTERY_DOORBELL_C30.png +0 -0
  71. package/homebridge-ui/public/assets/devices/BATTERY_DOORBELL_C31.png +0 -0
  72. package/homebridge-ui/public/assets/devices/batterydoorbell1080p_large.jpg +0 -0
  73. package/homebridge-ui/public/assets/devices/batterydoorbell2kdual_large.jpg +0 -0
  74. package/homebridge-ui/public/assets/devices/batterydoorbell_e340_large.png +0 -0
  75. package/homebridge-ui/public/assets/devices/eufy-security-client.png +0 -0
  76. package/homebridge-ui/public/assets/devices/eufycam2_large.png +0 -0
  77. package/homebridge-ui/public/assets/devices/eufycam2c_large.jpg +0 -0
  78. package/homebridge-ui/public/assets/devices/eufycam2cpro_large.jpg +0 -0
  79. package/homebridge-ui/public/assets/devices/eufycam2pro_large.jpg +0 -0
  80. package/homebridge-ui/public/assets/devices/eufycam3_large.jpg +0 -0
  81. package/homebridge-ui/public/assets/devices/eufycam3c_large.jpg +0 -0
  82. package/homebridge-ui/public/assets/devices/eufycam3pro_large.png +0 -0
  83. package/homebridge-ui/public/assets/devices/eufycam_large.jpg +0 -0
  84. package/homebridge-ui/public/assets/devices/eufycame330_large.jpg +0 -0
  85. package/homebridge-ui/public/assets/devices/floodlight2_large.jpg +0 -0
  86. package/homebridge-ui/public/assets/devices/floodlight2pro_large.jpg +0 -0
  87. package/homebridge-ui/public/assets/devices/floodlight_large.jpg +0 -0
  88. package/homebridge-ui/public/assets/devices/floodlightcame340_large.jpg +0 -0
  89. package/homebridge-ui/public/assets/devices/garage_camera_t8452_large.jpg +0 -0
  90. package/homebridge-ui/public/assets/devices/homebase2_large.png +0 -0
  91. package/homebridge-ui/public/assets/devices/homebase3_large.png +0 -0
  92. package/homebridge-ui/public/assets/devices/homebase_large.jpg +0 -0
  93. package/homebridge-ui/public/assets/devices/homebasemini_large.jpg +0 -0
  94. package/homebridge-ui/public/assets/devices/indoorcamC210_large.png +0 -0
  95. package/homebridge-ui/public/assets/devices/indoorcamC220_large.png +0 -0
  96. package/homebridge-ui/public/assets/devices/indoorcamE30_large.png +0 -0
  97. package/homebridge-ui/public/assets/devices/indoorcamc120_large.png +0 -0
  98. package/homebridge-ui/public/assets/devices/indoorcammini_large.jpg +0 -0
  99. package/homebridge-ui/public/assets/devices/indoorcamp24_large.png +0 -0
  100. package/homebridge-ui/public/assets/devices/indoorcams350_large.jpg +0 -0
  101. package/homebridge-ui/public/assets/devices/keypad_large.png +0 -0
  102. package/homebridge-ui/public/assets/devices/minibase_chime_T8023_large.jpg +0 -0
  103. package/homebridge-ui/public/assets/devices/motionsensor_large.png +0 -0
  104. package/homebridge-ui/public/assets/devices/sensor_large.png +0 -0
  105. package/homebridge-ui/public/assets/devices/smartdrop_t8790_large.png +0 -0
  106. package/homebridge-ui/public/assets/devices/smartlock_t8500_large.png +0 -0
  107. package/homebridge-ui/public/assets/devices/smartlock_t8500_wifibridge_large.jpg +0 -0
  108. package/homebridge-ui/public/assets/devices/smartlock_t8503_large.png +0 -0
  109. package/homebridge-ui/public/assets/devices/smartlock_t8504_large.jpg +0 -0
  110. package/homebridge-ui/public/assets/devices/smartlock_t8510P_t8520P_large.png +0 -0
  111. package/homebridge-ui/public/assets/devices/smartlock_touch_and_wifi_t8502_large.png +0 -0
  112. package/homebridge-ui/public/assets/devices/smartlock_touch_and_wifi_t8506_large.png +0 -0
  113. package/homebridge-ui/public/assets/devices/smartlock_touch_and_wifi_t8520_large.png +0 -0
  114. package/homebridge-ui/public/assets/devices/smartlock_touch_t8510_large.png +0 -0
  115. package/homebridge-ui/public/assets/devices/smartlock_touch_t8510_wifibridge_large.jpg +0 -0
  116. package/homebridge-ui/public/assets/devices/smartlock_video_t8530_large.png +0 -0
  117. package/homebridge-ui/public/assets/devices/smartlockwifibridge_t8021_large.jpg +0 -0
  118. package/homebridge-ui/public/assets/devices/smartsafe_s10_t7400_large.png +0 -0
  119. package/homebridge-ui/public/assets/devices/smartsafe_s12_t7401_large.png +0 -0
  120. package/homebridge-ui/public/assets/devices/smarttrack_card_t87B2_large.png +0 -0
  121. package/homebridge-ui/public/assets/devices/smarttrack_link_t87B0_large.png +0 -0
  122. package/homebridge-ui/public/assets/devices/solocamc210_large.jpg +0 -0
  123. package/homebridge-ui/public/assets/devices/solocamc35_large.png +0 -0
  124. package/homebridge-ui/public/assets/devices/solocame20_large.jpg +0 -0
  125. package/homebridge-ui/public/assets/devices/solocame30_large.png +0 -0
  126. package/homebridge-ui/public/assets/devices/solocame40_large.jpg +0 -0
  127. package/homebridge-ui/public/assets/devices/solocaml20_large.jpg +0 -0
  128. package/homebridge-ui/public/assets/devices/solocams220_large.jpg +0 -0
  129. package/homebridge-ui/public/assets/devices/solocams340_large.png +0 -0
  130. package/homebridge-ui/public/assets/devices/solocams40_large.jpg +0 -0
  131. package/homebridge-ui/public/assets/devices/soloindoorcamc24_large.jpg +0 -0
  132. package/homebridge-ui/public/assets/devices/solooutdoorcamc22_large.png +0 -0
  133. package/homebridge-ui/public/assets/devices/solooutdoorcamc24_large.jpg +0 -0
  134. package/homebridge-ui/public/assets/devices/unknown.png +0 -0
  135. package/homebridge-ui/public/assets/devices/walllight_s100_large.jpg +0 -0
  136. package/homebridge-ui/public/assets/devices/walllight_s120_large.jpg +0 -0
  137. package/homebridge-ui/public/assets/devices/wireddoorbell1080p_large.jpg +0 -0
  138. package/homebridge-ui/public/assets/devices/wireddoorbell2k_large.png +0 -0
  139. package/homebridge-ui/public/assets/devices/wireddoorbelldual_large.jpg +0 -0
  140. package/homebridge-ui/public/assets/icons/attach.svg +1 -0
  141. package/homebridge-ui/public/assets/icons/battery_0.svg +1 -0
  142. package/homebridge-ui/public/assets/icons/battery_1.svg +1 -0
  143. package/homebridge-ui/public/assets/icons/battery_2.svg +1 -0
  144. package/homebridge-ui/public/assets/icons/battery_3.svg +1 -0
  145. package/homebridge-ui/public/assets/icons/battery_4.svg +1 -0
  146. package/homebridge-ui/public/assets/icons/battery_5.svg +1 -0
  147. package/homebridge-ui/public/assets/icons/battery_6.svg +1 -0
  148. package/homebridge-ui/public/assets/icons/bolt.svg +1 -0
  149. package/homebridge-ui/public/assets/icons/bug-report.svg +1 -0
  150. package/homebridge-ui/public/assets/icons/copy.svg +1 -0
  151. package/homebridge-ui/public/assets/icons/delete.svg +1 -0
  152. package/homebridge-ui/public/assets/icons/download.svg +1 -0
  153. package/homebridge-ui/public/assets/icons/info.svg +1 -0
  154. package/homebridge-ui/public/assets/icons/inventory.svg +1 -0
  155. package/homebridge-ui/public/assets/icons/refresh.svg +1 -0
  156. package/homebridge-ui/public/assets/icons/satellite_alt.svg +1 -0
  157. package/homebridge-ui/public/assets/icons/settings.svg +1 -0
  158. package/homebridge-ui/public/assets/icons/settings_backup_restore.svg +1 -0
  159. package/homebridge-ui/public/assets/icons/solar_power.svg +1 -0
  160. package/homebridge-ui/public/assets/icons/warning.svg +1 -0
  161. package/homebridge-ui/public/components/device-card.js +162 -0
  162. package/homebridge-ui/public/components/guard-modes.js +88 -0
  163. package/homebridge-ui/public/components/number-input.js +121 -0
  164. package/homebridge-ui/public/components/select.js +73 -0
  165. package/homebridge-ui/public/components/toggle.js +68 -0
  166. package/homebridge-ui/public/index.html +27 -0
  167. package/homebridge-ui/public/services/api.js +214 -0
  168. package/homebridge-ui/public/services/config.js +144 -0
  169. package/homebridge-ui/public/style.css +775 -0
  170. package/homebridge-ui/public/utils/countries.js +73 -0
  171. package/homebridge-ui/public/utils/device-images.js +89 -0
  172. package/homebridge-ui/public/utils/helpers.js +87 -0
  173. package/homebridge-ui/public/views/dashboard.js +226 -0
  174. package/homebridge-ui/public/views/device-detail.js +610 -0
  175. package/homebridge-ui/public/views/diagnostics.js +296 -0
  176. package/homebridge-ui/public/views/login.js +636 -0
  177. package/homebridge-ui/public/views/settings.js +192 -0
  178. package/homebridge-ui/public/views/unsupported-detail.js +296 -0
  179. package/homebridge-ui/server.js +1327 -0
  180. package/media/Snapshot-Unavailable.png +0 -0
  181. package/media/Snapshot-Unavailable.xcf +0 -0
  182. package/media/Snapshot-black.png +0 -0
  183. package/media/camera-disabled.png +0 -0
  184. package/media/camera-offline.png +0 -0
  185. package/package.json +64 -0
@@ -0,0 +1,775 @@
1
+ /* ===== Eufy Security Plugin - Custom UI Styles ===== */
2
+ /* Bootstrap 5 is auto-injected by Homebridge UI */
3
+
4
+ :root {
5
+ --eufy-primary: #2d6ff7;
6
+ --eufy-primary-hover: #1a5ae0;
7
+ --eufy-success: #28a745;
8
+ --eufy-danger: #dc3545;
9
+ --eufy-warning: #ffc107;
10
+ --eufy-muted: #6c757d;
11
+ --eufy-card-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
12
+ --eufy-card-hover-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
13
+ --eufy-border-radius: 12px;
14
+ --eufy-transition: all 0.2s ease;
15
+ }
16
+
17
+ /* ===== Instant Tooltip ===== */
18
+ .eufy-tooltip {
19
+ position: relative;
20
+ }
21
+ .eufy-tooltip::after {
22
+ content: attr(data-tooltip);
23
+ position: absolute;
24
+ top: calc(100% + 6px);
25
+ left: 50%;
26
+ transform: translateX(-50%);
27
+ padding: 4px 8px;
28
+ background: rgba(0, 0, 0, 0.8);
29
+ color: #fff;
30
+ font-size: 0.75rem;
31
+ white-space: nowrap;
32
+ border-radius: 4px;
33
+ pointer-events: none;
34
+ opacity: 0;
35
+ transition: opacity 0.1s ease;
36
+ z-index: 10;
37
+ }
38
+ .eufy-tooltip:hover::after {
39
+ opacity: 1;
40
+ }
41
+ .d-flex > .eufy-tooltip:last-child::after {
42
+ left: auto;
43
+ right: 0;
44
+ transform: none;
45
+ }
46
+
47
+ /* ===== Layout ===== */
48
+ #app {
49
+ max-width: 960px;
50
+ margin: 0 auto;
51
+ padding: 0 12px;
52
+ }
53
+
54
+ /* ===== Header ===== */
55
+ .eufy-header {
56
+ display: flex;
57
+ align-items: center;
58
+ justify-content: space-between;
59
+ padding: 16px 0;
60
+ margin-bottom: 8px;
61
+ border-bottom: 1px solid rgba(0, 0, 0, 0.08);
62
+ }
63
+
64
+ .eufy-header h4 {
65
+ margin: 0;
66
+ font-weight: 600;
67
+ font-size: 1.25rem;
68
+ }
69
+
70
+ .eufy-header__right {
71
+ display: flex;
72
+ align-items: center;
73
+ gap: 12px;
74
+ }
75
+
76
+ .eufy-cache-date {
77
+ font-size: 0.75rem;
78
+ color: #888;
79
+ white-space: nowrap;
80
+ }
81
+
82
+ .eufy-header .btn-group .btn {
83
+ padding: 6px 10px;
84
+ font-size: 0.85rem;
85
+ }
86
+
87
+ /* ===== Device Cards ===== */
88
+ .device-card {
89
+ border: 1px solid rgba(0, 0, 0, 0.1);
90
+ border-radius: var(--eufy-border-radius);
91
+ box-shadow: var(--eufy-card-shadow);
92
+ transition: var(--eufy-transition);
93
+ cursor: pointer;
94
+ overflow: hidden;
95
+ height: 100%;
96
+ background: var(--bs-body-bg, #fff);
97
+ }
98
+
99
+ .device-card:hover:not(.device-card--disabled) {
100
+ box-shadow: var(--eufy-card-hover-shadow);
101
+ transform: translateY(-2px);
102
+ }
103
+
104
+ .device-card--unsupported {
105
+ opacity: 0.65;
106
+ cursor: pointer;
107
+ }
108
+
109
+ .device-card__hint {
110
+ font-size: 0.7rem;
111
+ color: var(--eufy-primary);
112
+ margin-top: 4px;
113
+ font-style: italic;
114
+ }
115
+
116
+ /* ===== Unsupported Detail View ===== */
117
+ .unsupported-detail {
118
+ padding: 16px 0;
119
+ }
120
+
121
+ .unsupported-detail__table {
122
+ width: 100%;
123
+ font-size: 0.85rem;
124
+ margin: 16px 0;
125
+ border-collapse: collapse;
126
+ }
127
+
128
+ .unsupported-detail__table td {
129
+ padding: 8px 12px;
130
+ border-bottom: 1px solid rgba(0, 0, 0, 0.06);
131
+ }
132
+
133
+ .unsupported-detail__table td:first-child {
134
+ font-weight: 600;
135
+ width: 40%;
136
+ color: var(--eufy-muted);
137
+ }
138
+
139
+ .unsupported-detail__steps {
140
+ margin-top: 20px;
141
+ }
142
+
143
+ .unsupported-stepper {
144
+ display: flex;
145
+ align-items: center;
146
+ justify-content: center;
147
+ gap: 0;
148
+ margin-bottom: 14px;
149
+ }
150
+
151
+ .unsupported-stepper__step {
152
+ display: flex;
153
+ flex-direction: column;
154
+ align-items: center;
155
+ gap: 4px;
156
+ flex: 0 0 auto;
157
+ }
158
+
159
+ .unsupported-stepper__circle {
160
+ width: 28px;
161
+ height: 28px;
162
+ border-radius: 50%;
163
+ color: #fff;
164
+ display: flex;
165
+ align-items: center;
166
+ justify-content: center;
167
+ font-size: 0.78rem;
168
+ font-weight: 700;
169
+ background: var(--eufy-primary);
170
+ }
171
+
172
+ .unsupported-stepper__circle--success {
173
+ background: var(--bs-success, #198754);
174
+ }
175
+
176
+ .unsupported-stepper__circle--primary {
177
+ background: var(--bs-primary, #0d6efd);
178
+ }
179
+
180
+ .unsupported-stepper__circle--warning {
181
+ background: var(--bs-warning, #ffc107);
182
+ }
183
+
184
+ .unsupported-stepper__circle--danger {
185
+ background: var(--bs-danger, #dc3545);
186
+ }
187
+
188
+ .unsupported-stepper__label {
189
+ font-size: 0.72rem;
190
+ font-weight: 600;
191
+ color: var(--eufy-muted);
192
+ text-transform: uppercase;
193
+ letter-spacing: 0.03em;
194
+ }
195
+
196
+ .unsupported-stepper__line {
197
+ flex: 1;
198
+ height: 2px;
199
+ background: var(--eufy-border, #dee2e6);
200
+ margin: 0 8px;
201
+ align-self: flex-start;
202
+ margin-top: 13px;
203
+ }
204
+
205
+ .unsupported-detail__actions {
206
+ display: flex;
207
+ gap: 12px;
208
+ }
209
+
210
+ .unsupported-detail__actions .btn {
211
+ flex: 1;
212
+ text-align: center;
213
+ }
214
+
215
+ .unsupported-detail__paste-note {
216
+ font-size: 0.8rem;
217
+ color: var(--eufy-muted);
218
+ margin-top: 12px;
219
+ padding: 8px 12px;
220
+ background: rgba(255, 193, 7, 0.1);
221
+ border-left: 3px solid var(--bs-warning, #ffc107);
222
+ border-radius: 4px;
223
+ }
224
+
225
+ .unsupported-detail__info {
226
+ font-size: 0.82rem;
227
+ padding: 12px 14px;
228
+ margin: 16px 0;
229
+ background: rgba(45, 111, 247, 0.06);
230
+ border-left: 3px solid var(--eufy-primary);
231
+ border-radius: 4px;
232
+ line-height: 1.6;
233
+ }
234
+
235
+ .unsupported-detail__info a {
236
+ color: var(--eufy-primary);
237
+ }
238
+
239
+ .unsupported-detail__dump-wrap {
240
+ position: relative;
241
+ }
242
+
243
+ .unsupported-detail__copy-btn {
244
+ position: absolute;
245
+ top: 8px;
246
+ right: 8px;
247
+ padding: 3px 10px;
248
+ font-size: 0.72rem;
249
+ border: 1px solid rgba(0, 0, 0, 0.15);
250
+ border-radius: 6px;
251
+ background: var(--bs-body-bg, #fff);
252
+ color: var(--eufy-muted);
253
+ cursor: pointer;
254
+ transition: var(--eufy-transition);
255
+ z-index: 1;
256
+ }
257
+
258
+ .unsupported-detail__copy-btn:hover {
259
+ border-color: var(--eufy-primary);
260
+ color: var(--eufy-primary);
261
+ }
262
+
263
+ .unsupported-detail__dump {
264
+ background: rgba(0, 0, 0, 0.04);
265
+ border: 1px solid rgba(0, 0, 0, 0.1);
266
+ border-radius: 8px;
267
+ padding: 12px 16px;
268
+ font-size: 0.78rem;
269
+ max-height: 100px;
270
+ overflow-y: auto;
271
+ white-space: pre-wrap;
272
+ word-break: break-word;
273
+ margin-bottom: 8px;
274
+ }
275
+
276
+ .device-card--ignored {
277
+ opacity: 0.6;
278
+ }
279
+
280
+ .device-card__image-wrap {
281
+ display: flex;
282
+ align-items: center;
283
+ justify-content: center;
284
+ padding: 16px 16px 8px;
285
+ height: 120px;
286
+ }
287
+
288
+ .device-card__image-wrap img {
289
+ max-height: 100%;
290
+ max-width: 100%;
291
+ object-fit: contain;
292
+ }
293
+
294
+ .device-card__body {
295
+ padding: 8px 16px 14px;
296
+ }
297
+
298
+ .device-card__name {
299
+ font-weight: 600;
300
+ font-size: 0.9rem;
301
+ margin-bottom: 4px;
302
+ white-space: nowrap;
303
+ overflow: hidden;
304
+ text-overflow: ellipsis;
305
+ }
306
+
307
+ .device-card__meta-row {
308
+ display: flex;
309
+ align-items: center;
310
+ justify-content: space-between;
311
+ gap: 8px;
312
+ }
313
+
314
+ .device-card__meta {
315
+ font-size: 0.75rem;
316
+ color: var(--eufy-muted);
317
+ white-space: nowrap;
318
+ overflow: hidden;
319
+ text-overflow: ellipsis;
320
+ min-width: 0;
321
+ }
322
+
323
+ .device-card__footer {
324
+ display: flex;
325
+ align-items: center;
326
+ justify-content: space-between;
327
+ padding: 0 16px 12px;
328
+ }
329
+
330
+ /* ===== Badges ===== */
331
+ .badge-unsupported {
332
+ font-size: 0.7rem;
333
+ background-color: var(--eufy-warning) !important;
334
+ color: #000;
335
+ }
336
+
337
+ /* ===== Toggle Switch ===== */
338
+ .eufy-toggle {
339
+ display: flex;
340
+ align-items: center;
341
+ justify-content: space-between;
342
+ padding: 12px 0;
343
+ border-bottom: 1px solid rgba(0, 0, 0, 0.06);
344
+ }
345
+
346
+ .eufy-toggle:last-child {
347
+ border-bottom: none;
348
+ }
349
+
350
+ .eufy-toggle__label {
351
+ display: flex;
352
+ align-items: center;
353
+ gap: 8px;
354
+ font-size: 0.9rem;
355
+ font-weight: 500;
356
+ }
357
+
358
+ .eufy-toggle__help {
359
+ display: inline-flex;
360
+ align-items: center;
361
+ justify-content: center;
362
+ width: 18px;
363
+ height: 18px;
364
+ border-radius: 50%;
365
+ background: rgba(0, 0, 0, 0.08);
366
+ color: var(--eufy-muted);
367
+ font-size: 0.65rem;
368
+ cursor: help;
369
+ font-weight: 700;
370
+ flex-shrink: 0;
371
+ }
372
+
373
+ /* ===== Discovery / Loading ===== */
374
+ .discovery-screen {
375
+ text-align: center;
376
+ padding: 60px 20px;
377
+ }
378
+
379
+ .discovery-screen__icon {
380
+ font-size: 3rem;
381
+ margin-bottom: 16px;
382
+ animation: pulse 2s ease-in-out infinite;
383
+ }
384
+
385
+ .discovery-screen__title {
386
+ font-size: 1.2rem;
387
+ font-weight: 600;
388
+ margin-bottom: 8px;
389
+ }
390
+
391
+ .discovery-screen__subtitle {
392
+ color: var(--eufy-muted);
393
+ font-size: 0.9rem;
394
+ margin-bottom: 24px;
395
+ }
396
+
397
+ @keyframes pulse {
398
+ 0%, 100% { opacity: 1; transform: scale(1); }
399
+ 50% { opacity: 0.6; transform: scale(1.05); }
400
+ }
401
+
402
+ /* ===== Login ===== */
403
+ .login-card {
404
+ margin: 0px auto;
405
+ }
406
+
407
+ .login-section-title {
408
+ font-size: 1.2rem;
409
+ font-weight: 600;
410
+ margin-bottom: 16px;
411
+ padding-bottom: 12px;
412
+ border-bottom: 1px solid rgba(0, 0, 0, 0.08);
413
+ }
414
+
415
+ /* ===== Detail View ===== */
416
+ .detail-header {
417
+ display: flex;
418
+ align-items: center;
419
+ gap: 16px;
420
+ padding: 16px 0;
421
+ margin-bottom: 8px;
422
+ border-bottom: 1px solid rgba(0, 0, 0, 0.08);
423
+ }
424
+
425
+ .detail-header__image {
426
+ width: 64px;
427
+ height: 64px;
428
+ object-fit: contain;
429
+ }
430
+
431
+ .detail-header__info h5 {
432
+ margin: 0 0 4px;
433
+ font-weight: 600;
434
+ }
435
+
436
+ .detail-header__info small {
437
+ color: var(--eufy-muted);
438
+ }
439
+
440
+ .detail-section {
441
+ margin-bottom: 16px;
442
+ }
443
+
444
+ .detail-section__title {
445
+ font-size: 0.8rem;
446
+ text-transform: uppercase;
447
+ letter-spacing: 0.5px;
448
+ color: var(--eufy-muted);
449
+ font-weight: 600;
450
+ margin-bottom: 4px;
451
+ padding: 8px 0;
452
+ }
453
+
454
+ /* ===== Advanced Toggle ===== */
455
+ .advanced-toggle {
456
+ display: flex;
457
+ align-items: center;
458
+ gap: 6px;
459
+ color: var(--eufy-muted);
460
+ cursor: pointer;
461
+ font-size: 0.85rem;
462
+ padding: 12px 0;
463
+ border: none;
464
+ background: none;
465
+ width: 100%;
466
+ text-align: left;
467
+ }
468
+
469
+ .advanced-toggle:hover {
470
+ color: var(--eufy-primary);
471
+ }
472
+
473
+ .advanced-toggle__chevron {
474
+ transition: transform 0.2s;
475
+ font-size: 0.7rem;
476
+ }
477
+
478
+ .advanced-toggle__chevron--open {
479
+ transform: rotate(90deg);
480
+ }
481
+
482
+ /* ===== Settings ===== */
483
+ .settings-section {
484
+ padding: 16px 0;
485
+ border-bottom: 1px solid rgba(0, 0, 0, 0.06);
486
+ }
487
+
488
+ .settings-section:last-child {
489
+ border-bottom: none;
490
+ }
491
+
492
+ .settings-btn-row {
493
+ display: flex;
494
+ flex-wrap: wrap;
495
+ gap: 8px;
496
+ padding: 12px 0;
497
+ }
498
+
499
+ /* ===== Guard Modes ===== */
500
+ .guard-mode-grid {
501
+ display: grid;
502
+ grid-template-columns: 1fr 1fr;
503
+ gap: 12px;
504
+ }
505
+
506
+ .guard-mode-grid .form-group label {
507
+ font-size: 0.85rem;
508
+ font-weight: 500;
509
+ margin-bottom: 4px;
510
+ }
511
+
512
+ .guard-mode-grid .form-control,
513
+ .guard-mode-grid .form-select {
514
+ font-size: 0.85rem;
515
+ }
516
+
517
+ /* ===== Welcome Banner ===== */
518
+ .welcome-banner {
519
+ text-align: center;
520
+ padding: 24px 20px 16px;
521
+ }
522
+
523
+ .welcome-banner__title {
524
+ font-size: 1.1rem;
525
+ font-weight: 600;
526
+ margin-bottom: 8px;
527
+ }
528
+
529
+ .welcome-banner__text {
530
+ color: var(--eufy-muted);
531
+ font-size: 0.85rem;
532
+ line-height: 1.5;
533
+ max-width: 360px;
534
+ margin: 0 auto;
535
+ }
536
+
537
+ /* ===== Alert overrides ===== */
538
+ .alert-admin {
539
+ border-radius: var(--eufy-border-radius);
540
+ }
541
+
542
+ /* ===== Node.js Version Warning Banner ===== */
543
+ .node-version-banner {
544
+ display: flex;
545
+ align-items: flex-start;
546
+ gap: 12px;
547
+ padding: 14px 16px;
548
+ margin-bottom: 16px;
549
+ background: #fff3cd;
550
+ border: 1px solid #ffc107;
551
+ border-radius: var(--eufy-border-radius);
552
+ font-size: 0.85rem;
553
+ line-height: 1.5;
554
+ }
555
+
556
+ .node-version-banner__icon {
557
+ font-size: 1.3rem;
558
+ flex-shrink: 0;
559
+ margin-top: 1px;
560
+ }
561
+
562
+ .node-version-banner__content > strong {
563
+ display: block;
564
+ margin-bottom: 4px;
565
+ color: #664d03;
566
+ }
567
+
568
+ .node-version-banner__text {
569
+ color: #664d03;
570
+ }
571
+
572
+ .node-version-banner__text code {
573
+ background: rgba(0, 0, 0, 0.08);
574
+ padding: 1px 4px;
575
+ border-radius: 3px;
576
+ font-size: 0.8rem;
577
+ }
578
+
579
+ .node-version-banner__text a {
580
+ color: #664d03;
581
+ font-weight: 500;
582
+ }
583
+
584
+ /* ===== Number Input ===== */
585
+ .number-input-group {
586
+ display: flex;
587
+ align-items: center;
588
+ gap: 4px;
589
+ }
590
+
591
+ .number-input-group .btn {
592
+ padding: 4px 10px;
593
+ font-size: 0.85rem;
594
+ line-height: 1;
595
+ }
596
+
597
+ .number-input-group input {
598
+ width: 70px;
599
+ text-align: center;
600
+ font-size: 0.85rem;
601
+ }
602
+
603
+ /* ===== Progress bar for log download ===== */
604
+ .log-progress {
605
+ margin-top: 12px;
606
+ }
607
+
608
+ .log-progress .progress {
609
+ height: 8px;
610
+ border-radius: 4px;
611
+ }
612
+
613
+ /* ===== Responsive ===== */
614
+ @media (max-width: 576px) {
615
+ .welcome-actions {
616
+ flex-direction: column;
617
+ }
618
+
619
+ .guard-mode-grid {
620
+ grid-template-columns: 1fr;
621
+ }
622
+
623
+ .device-card__image-wrap {
624
+ height: 90px;
625
+ padding: 12px 12px 4px;
626
+ }
627
+ }
628
+
629
+ /* ===== Dark mode support ===== */
630
+ /* Homebridge UI applies .dark-mode on the body inside the plugin iframe */
631
+ body.dark-mode {
632
+ --eufy-card-shadow: 0 2px 8px rgba(0, 0, 0, 0.35);
633
+ --eufy-card-hover-shadow: 0 4px 16px rgba(0, 0, 0, 0.5);
634
+ --eufy-muted: #9ea7b3;
635
+ }
636
+
637
+ body.dark-mode .device-card {
638
+ border-color: rgba(255, 255, 255, 0.08);
639
+ background: rgba(255, 255, 255, 0.05);
640
+ }
641
+
642
+ body.dark-mode .device-card:hover:not(.device-card--disabled) {
643
+ box-shadow: var(--eufy-card-hover-shadow);
644
+ }
645
+
646
+ body.dark-mode .eufy-header {
647
+ border-bottom-color: rgba(255, 255, 255, 0.1);
648
+ }
649
+
650
+ body.dark-mode .eufy-toggle {
651
+ border-bottom-color: rgba(255, 255, 255, 0.08);
652
+ }
653
+
654
+ body.dark-mode .eufy-toggle__help {
655
+ background: rgba(255, 255, 255, 0.1);
656
+ color: rgba(255, 255, 255, 0.5);
657
+ }
658
+
659
+ body.dark-mode .detail-header {
660
+ border-bottom-color: rgba(255, 255, 255, 0.1);
661
+ }
662
+
663
+ body.dark-mode .login-section-title {
664
+ border-bottom-color: rgba(255, 255, 255, 0.1);
665
+ }
666
+
667
+ body.dark-mode .settings-section {
668
+ border-bottom-color: rgba(255, 255, 255, 0.08);
669
+ }
670
+
671
+ /* ===== Diagnostics Steps ===== */
672
+ .diag-step {
673
+ padding: 16px;
674
+ margin-bottom: 12px;
675
+ border-left: 3px solid var(--eufy-primary);
676
+ background: rgba(0, 0, 0, 0.02);
677
+ border-radius: 0 var(--eufy-border-radius) var(--eufy-border-radius) 0;
678
+ }
679
+
680
+ .diag-step__header {
681
+ display: flex;
682
+ align-items: center;
683
+ gap: 10px;
684
+ margin-bottom: 8px;
685
+ }
686
+
687
+ .diag-step__badge {
688
+ display: inline-flex;
689
+ align-items: center;
690
+ justify-content: center;
691
+ width: 26px;
692
+ height: 26px;
693
+ border-radius: 50%;
694
+ background: var(--eufy-primary);
695
+ color: #fff;
696
+ font-size: 0.8rem;
697
+ font-weight: 700;
698
+ flex-shrink: 0;
699
+ }
700
+
701
+ body.dark-mode .diag-step {
702
+ background: rgba(255, 255, 255, 0.04);
703
+ border-left-color: var(--eufy-primary);
704
+ }
705
+
706
+ body.dark-mode .advanced-toggle {
707
+ color: rgba(255, 255, 255, 0.5);
708
+ }
709
+
710
+ body.dark-mode .advanced-toggle:hover {
711
+ color: var(--eufy-primary);
712
+ }
713
+
714
+ body.dark-mode .device-card__meta {
715
+ color: var(--eufy-muted);
716
+ }
717
+
718
+ body.dark-mode .detail-header__info small {
719
+ color: var(--eufy-muted);
720
+ }
721
+
722
+ body.dark-mode .detail-section__title {
723
+ color: var(--eufy-muted);
724
+ }
725
+
726
+ body.dark-mode .welcome-banner__text {
727
+ color: var(--eufy-muted);
728
+ }
729
+
730
+ body.dark-mode .discovery-screen__subtitle {
731
+ color: var(--eufy-muted);
732
+ }
733
+
734
+ body.dark-mode .discovery-screen__title {
735
+ color: rgba(255, 255, 255, 0.9);
736
+ }
737
+
738
+ body.dark-mode .discovery-screen .text-muted {
739
+ color: var(--eufy-muted) !important;
740
+ }
741
+
742
+ body.dark-mode .text-muted {
743
+ color: rgba(255, 255, 255, 0.55) !important;
744
+ }
745
+
746
+ body.dark-mode .diag-step strong {
747
+ color: rgba(255, 255, 255, 0.9);
748
+ }
749
+
750
+ body.dark-mode .node-version-banner {
751
+ background: rgba(255, 193, 7, 0.12);
752
+ border-color: rgba(255, 193, 7, 0.3);
753
+ }
754
+
755
+ body.dark-mode .node-version-banner__content strong {
756
+ color: #ffda6a;
757
+ }
758
+
759
+ body.dark-mode .node-version-banner__text {
760
+ color: #ffe69c;
761
+ }
762
+
763
+ body.dark-mode .node-version-banner__text code {
764
+ background: rgba(255, 255, 255, 0.1);
765
+ color: #ffe69c;
766
+ }
767
+
768
+ body.dark-mode .node-version-banner__text a {
769
+ color: #ffda6a;
770
+ }
771
+
772
+ /* Invert dark-filled SVG icons so they render white on dark backgrounds */
773
+ body.dark-mode img[src*="assets/icons/"] {
774
+ filter: brightness(0) invert(1);
775
+ }