@switchbot/homebridge-switchbot 5.0.0-beta.111 → 5.0.0-beta.112
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/dist/homebridge-ui/public/css/styles.css +111 -16
- package/dist/homebridge-ui/public/index.html +34 -60
- package/dist/homebridge-ui/public/js/app.js +103 -41
- package/dist/homebridge-ui/public/js/app.js.map +3 -3
- package/dist/homebridge-ui/public/js/discovery.d.ts.map +1 -1
- package/dist/homebridge-ui/public/js/discovery.js +24 -1
- package/dist/homebridge-ui/public/js/discovery.js.map +1 -1
- package/dist/homebridge-ui/public/js/discovery.ts +25 -1
- package/dist/homebridge-ui/public/js/render.d.ts.map +1 -1
- package/dist/homebridge-ui/public/js/render.js +78 -35
- package/dist/homebridge-ui/public/js/render.js.map +1 -1
- package/dist/homebridge-ui/public/js/render.ts +79 -35
- package/dist/platform.d.ts.map +1 -1
- package/dist/platform.js +1 -6
- package/dist/platform.js.map +1 -1
- package/docs/variables/default.html +1 -1
- package/package.json +9 -8
- package/src/homebridge-ui/public/css/styles.css +111 -16
- package/src/homebridge-ui/public/index.html +34 -60
- package/src/homebridge-ui/public/js/discovery.ts +25 -1
- package/src/homebridge-ui/public/js/render.ts +79 -35
- package/src/platform.ts +1 -6
- package/test/e2e/run-e2e.spec.ts +12 -10
- package/test/helpers/matter-harness.ts +2 -2
- package/todo/COMPLETED.md +13 -0
- package/switchbot-homebridge-switchbot-5.0.0.tgz +0 -0
|
@@ -75,12 +75,13 @@ body {
|
|
|
75
75
|
system-ui,
|
|
76
76
|
-apple-system,
|
|
77
77
|
Arial;
|
|
78
|
-
padding:
|
|
78
|
+
padding: 10px;
|
|
79
79
|
}
|
|
80
80
|
|
|
81
81
|
h1 {
|
|
82
|
-
font-size:
|
|
82
|
+
font-size: 18px;
|
|
83
83
|
margin-top: 0;
|
|
84
|
+
margin-bottom: 10px;
|
|
84
85
|
}
|
|
85
86
|
|
|
86
87
|
h2 {
|
|
@@ -152,25 +153,25 @@ code {
|
|
|
152
153
|
}
|
|
153
154
|
|
|
154
155
|
.form-group {
|
|
155
|
-
margin-bottom:
|
|
156
|
+
margin-bottom: 10px;
|
|
156
157
|
}
|
|
157
158
|
|
|
158
159
|
label {
|
|
159
160
|
display: block;
|
|
160
|
-
margin-bottom:
|
|
161
|
+
margin-bottom: 4px;
|
|
161
162
|
font-weight: 500;
|
|
162
|
-
font-size:
|
|
163
|
+
font-size: 11px;
|
|
163
164
|
color: #6b7280;
|
|
164
165
|
}
|
|
165
166
|
|
|
166
167
|
input {
|
|
167
168
|
width: 100%;
|
|
168
169
|
max-width: 400px;
|
|
169
|
-
padding: 8px
|
|
170
|
+
padding: 6px 8px;
|
|
170
171
|
border: 1px solid #d1d5db;
|
|
171
172
|
border-radius: 6px;
|
|
172
173
|
font-family: system-ui, -apple-system, monospace;
|
|
173
|
-
font-size:
|
|
174
|
+
font-size: 12px;
|
|
174
175
|
transition: all 0.2s ease;
|
|
175
176
|
}
|
|
176
177
|
|
|
@@ -203,8 +204,8 @@ select:focus {
|
|
|
203
204
|
}
|
|
204
205
|
|
|
205
206
|
.status {
|
|
206
|
-
font-size:
|
|
207
|
-
margin-top:
|
|
207
|
+
font-size: 11px;
|
|
208
|
+
margin-top: 3px;
|
|
208
209
|
}
|
|
209
210
|
|
|
210
211
|
.status.ok {
|
|
@@ -221,27 +222,121 @@ select:focus {
|
|
|
221
222
|
}
|
|
222
223
|
|
|
223
224
|
.card {
|
|
224
|
-
padding:
|
|
225
|
+
padding: 12px;
|
|
225
226
|
border-radius: 8px;
|
|
226
|
-
margin-bottom:
|
|
227
|
+
margin-bottom: 10px;
|
|
227
228
|
}
|
|
228
229
|
|
|
229
|
-
.
|
|
230
|
-
padding: 10px
|
|
231
|
-
|
|
230
|
+
.card.compact {
|
|
231
|
+
padding: 10px;
|
|
232
|
+
margin-bottom: 10px;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
.card.compact h2 {
|
|
236
|
+
margin-top: 0;
|
|
237
|
+
margin-bottom: 6px;
|
|
238
|
+
font-size: 13px;
|
|
239
|
+
font-weight: 600;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
.card.compact .form-group {
|
|
232
243
|
margin-bottom: 6px;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
.card.compact label {
|
|
247
|
+
font-size: 11px;
|
|
248
|
+
margin-bottom: 2px;
|
|
249
|
+
display: block;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
.card.compact input {
|
|
253
|
+
padding: 5px 8px;
|
|
254
|
+
font-size: 12px;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
.card.compact button {
|
|
258
|
+
padding: 6px 12px;
|
|
259
|
+
font-size: 12px;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
.card.compact .status {
|
|
263
|
+
font-size: 11px;
|
|
264
|
+
margin-top: 4px;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
.credentials-row {
|
|
268
|
+
display: flex;
|
|
269
|
+
flex-wrap: wrap;
|
|
270
|
+
gap: 8px;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
.credentials-row .form-group {
|
|
274
|
+
flex: 1 1 100%;
|
|
275
|
+
min-width: 0;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
@media (min-width: 700px) {
|
|
279
|
+
.credentials-row {
|
|
280
|
+
gap: 10px;
|
|
281
|
+
align-items: start;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
.credentials-row .form-group {
|
|
285
|
+
flex: 1 1 calc(50% - 5px);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
.device-item {
|
|
290
|
+
padding: 5px 8px;
|
|
291
|
+
border-radius: 5px;
|
|
292
|
+
margin-bottom: 0;
|
|
233
293
|
display: flex;
|
|
234
|
-
gap:
|
|
294
|
+
gap: 8px;
|
|
235
295
|
align-items: center;
|
|
236
296
|
justify-content: space-between;
|
|
237
297
|
transition: all 0.2s ease;
|
|
298
|
+
height: 100%;
|
|
299
|
+
min-width: 0;
|
|
238
300
|
}
|
|
239
301
|
|
|
240
302
|
.device-item:hover {
|
|
241
|
-
transform: translateY(-
|
|
303
|
+
transform: translateY(-0.5px);
|
|
242
304
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
243
305
|
}
|
|
244
306
|
|
|
307
|
+
.device-actions {
|
|
308
|
+
display: flex;
|
|
309
|
+
flex-wrap: wrap;
|
|
310
|
+
gap: 6px;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
#devices.device-grid,
|
|
314
|
+
#discoveredDevices.device-grid,
|
|
315
|
+
ul.device-grid {
|
|
316
|
+
display: grid;
|
|
317
|
+
grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
|
|
318
|
+
gap: 6px;
|
|
319
|
+
padding-left: 0;
|
|
320
|
+
list-style: none;
|
|
321
|
+
margin: 0;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
.device-item code {
|
|
325
|
+
display: inline-block;
|
|
326
|
+
max-width: 100%;
|
|
327
|
+
white-space: normal;
|
|
328
|
+
overflow-wrap: anywhere;
|
|
329
|
+
word-break: break-word;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
@media (max-width: 900px) {
|
|
333
|
+
.device-actions {
|
|
334
|
+
width: 100%;
|
|
335
|
+
justify-content: flex-start !important;
|
|
336
|
+
margin-left: 0 !important;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
245
340
|
.card {
|
|
246
341
|
padding: 16px;
|
|
247
342
|
border-radius: 8px;
|
|
@@ -9,65 +9,45 @@
|
|
|
9
9
|
<body>
|
|
10
10
|
<h1>🤖 SwitchBot Configuration</h1>
|
|
11
11
|
|
|
12
|
-
<div class="card">
|
|
12
|
+
<div class="card compact">
|
|
13
13
|
<h2>API Credentials</h2>
|
|
14
|
-
<
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
<
|
|
21
|
-
|
|
22
|
-
type="password"
|
|
23
|
-
id="
|
|
24
|
-
|
|
25
|
-
/>
|
|
26
|
-
<div class="status" id="tokenStatus"></div>
|
|
27
|
-
</div>
|
|
28
|
-
|
|
29
|
-
<div class="form-group">
|
|
30
|
-
<label for="secret">API Secret:</label>
|
|
31
|
-
<input
|
|
32
|
-
type="password"
|
|
33
|
-
id="secret"
|
|
34
|
-
placeholder="Enter your SwitchBot API secret"
|
|
35
|
-
/>
|
|
36
|
-
<div class="status" id="secretStatus"></div>
|
|
14
|
+
<div class="credentials-row">
|
|
15
|
+
<div class="form-group">
|
|
16
|
+
<label for="token">Token:</label>
|
|
17
|
+
<input type="password" id="token" placeholder="Enter token" />
|
|
18
|
+
<div class="status" id="tokenStatus"></div>
|
|
19
|
+
</div>
|
|
20
|
+
<div class="form-group">
|
|
21
|
+
<label for="secret">Secret:</label>
|
|
22
|
+
<input type="password" id="secret" placeholder="Enter secret" />
|
|
23
|
+
<div class="status" id="secretStatus"></div>
|
|
24
|
+
</div>
|
|
37
25
|
</div>
|
|
38
|
-
|
|
39
|
-
<button id="saveBtn" onclick="saveCredentials()">Save Credentials</button>
|
|
26
|
+
<button id="saveBtn" onclick="saveCredentials()">Save</button>
|
|
40
27
|
<div id="saveStatus"></div>
|
|
41
28
|
</div>
|
|
42
29
|
|
|
43
|
-
<div class="card">
|
|
30
|
+
<div class="card compact">
|
|
44
31
|
<h2>Discover Devices</h2>
|
|
45
|
-
<
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
<input
|
|
54
|
-
type="checkbox"
|
|
55
|
-
id="autoAddAllCheckbox"
|
|
56
|
-
style="width: auto; margin: 0"
|
|
57
|
-
/>
|
|
58
|
-
<span>Auto-add all discovered devices</span>
|
|
32
|
+
<div style="margin-bottom: 6px">
|
|
33
|
+
<label style="display: flex; align-items: center; gap: 4px; cursor: pointer; font-size: 11px">
|
|
34
|
+
<input type="checkbox" id="autoAddAllCheckbox" style="width: auto; margin: 0" />
|
|
35
|
+
<span>Auto-add all</span>
|
|
36
|
+
<span
|
|
37
|
+
title="When enabled, Discover immediately adds all found devices to your config. Existing devices are skipped automatically."
|
|
38
|
+
style="font-size: 11px; opacity: 0.7"
|
|
39
|
+
>ⓘ</span>
|
|
59
40
|
</label>
|
|
41
|
+
<div style="margin-top: 2px; margin-left: 22px; font-size: 10px; opacity: 0.75; line-height: 1.25">
|
|
42
|
+
Adds all discovered devices automatically; already configured devices are skipped.
|
|
43
|
+
</div>
|
|
60
44
|
</div>
|
|
61
|
-
<button
|
|
62
|
-
|
|
63
|
-
onclick="discoverDevices()"
|
|
64
|
-
style="margin-bottom: 16px"
|
|
65
|
-
>
|
|
66
|
-
🔍 Discover Devices
|
|
45
|
+
<button id="discoverBtn" onclick="discoverDevices()" style="margin-bottom: 8px">
|
|
46
|
+
🔍 Discover
|
|
67
47
|
</button>
|
|
68
|
-
<div id="discoverStatus" style="margin-bottom:
|
|
48
|
+
<div id="discoverStatus" style="margin-bottom: 6px; font-size: 11px"></div>
|
|
69
49
|
<div id="discoveredList" style="display: none">
|
|
70
|
-
<h3 style="margin-top: 0">Available Devices</h3>
|
|
50
|
+
<h3 style="margin-top: 0; font-size: 12px; margin-bottom: 6px; font-weight: 600">Available Devices</h3>
|
|
71
51
|
<ul
|
|
72
52
|
id="discoveredDevices"
|
|
73
53
|
style="max-height: 300px; overflow-y: auto"
|
|
@@ -75,22 +55,16 @@
|
|
|
75
55
|
</div>
|
|
76
56
|
</div>
|
|
77
57
|
|
|
78
|
-
<div class="card">
|
|
58
|
+
<div class="card compact">
|
|
79
59
|
<h2>Configured Devices</h2>
|
|
80
|
-
<p>
|
|
81
|
-
This page lists devices found in your Homebridge config for the
|
|
82
|
-
SwitchBot platform. Use the copy button to insert device IDs into the
|
|
83
|
-
plugin configuration. Connection preference (BLE/OpenAPI) is shown when
|
|
84
|
-
available.
|
|
85
|
-
</p>
|
|
86
60
|
<div id="status">Loading…</div>
|
|
87
61
|
<ul id="devices"></ul>
|
|
88
62
|
<div
|
|
89
63
|
id="removeAllContainer"
|
|
90
64
|
style="
|
|
91
65
|
display: none;
|
|
92
|
-
margin-top:
|
|
93
|
-
padding-top:
|
|
66
|
+
margin-top: 12px;
|
|
67
|
+
padding-top: 10px;
|
|
94
68
|
border-top: 1px solid rgba(0, 0, 0, 0.1);
|
|
95
69
|
"
|
|
96
70
|
>
|
|
@@ -99,8 +73,8 @@
|
|
|
99
73
|
style="
|
|
100
74
|
background: #dc2626;
|
|
101
75
|
width: 100%;
|
|
102
|
-
padding:
|
|
103
|
-
font-size:
|
|
76
|
+
padding: 7px 12px;
|
|
77
|
+
font-size: 12px;
|
|
104
78
|
font-weight: 500;
|
|
105
79
|
"
|
|
106
80
|
>
|
|
@@ -229,6 +229,19 @@ export async function discoverDevices(): Promise<void> {
|
|
|
229
229
|
list.innerHTML = ''
|
|
230
230
|
list.appendChild(controlsDiv)
|
|
231
231
|
|
|
232
|
+
// Create or get the device list container
|
|
233
|
+
let deviceListContainer = document.getElementById('discoveredDevices')
|
|
234
|
+
if (!deviceListContainer) {
|
|
235
|
+
deviceListContainer = document.createElement('ul')
|
|
236
|
+
deviceListContainer.id = 'discoveredDevices'
|
|
237
|
+
deviceListContainer.style.maxHeight = '400px'
|
|
238
|
+
deviceListContainer.style.overflowY = 'auto'
|
|
239
|
+
deviceListContainer.style.marginTop = '12px'
|
|
240
|
+
deviceListContainer.style.padding = '0'
|
|
241
|
+
deviceListContainer.style.listStyle = 'none'
|
|
242
|
+
list.appendChild(deviceListContainer)
|
|
243
|
+
}
|
|
244
|
+
|
|
232
245
|
// Display discovery list
|
|
233
246
|
list.style.display = 'block'
|
|
234
247
|
|
|
@@ -270,7 +283,18 @@ async function updateDiscoveryView(
|
|
|
270
283
|
const ul = await renderDiscoveredDevices(sorted, {
|
|
271
284
|
configuredIds,
|
|
272
285
|
})
|
|
273
|
-
|
|
286
|
+
|
|
287
|
+
// Replace or append the rendered list
|
|
288
|
+
const existingList = document.getElementById('discoveredDevices')
|
|
289
|
+
if (existingList && existingList.parentNode) {
|
|
290
|
+
existingList.replaceWith(ul)
|
|
291
|
+
} else {
|
|
292
|
+
// Fallback: append to discovery list container
|
|
293
|
+
const listContainer = document.getElementById('discoveredList')
|
|
294
|
+
if (listContainer) {
|
|
295
|
+
listContainer.appendChild(ul)
|
|
296
|
+
}
|
|
297
|
+
}
|
|
274
298
|
ul.id = 'discoveredDevices'
|
|
275
299
|
|
|
276
300
|
// Update status with count
|
|
@@ -181,6 +181,7 @@ export async function renderDiscoveredDevices(
|
|
|
181
181
|
options: { configuredIds?: Set<string> } = {},
|
|
182
182
|
): Promise<HTMLElement> {
|
|
183
183
|
const ul = document.createElement('ul')
|
|
184
|
+
ul.className = 'device-grid'
|
|
184
185
|
ul.style.maxHeight = '400px'
|
|
185
186
|
ul.style.overflowY = 'auto'
|
|
186
187
|
ul.style.marginTop = '12px'
|
|
@@ -198,26 +199,29 @@ export async function renderDiscoveredDevices(
|
|
|
198
199
|
const li = document.createElement('li')
|
|
199
200
|
li.className = 'device-item'
|
|
200
201
|
li.style.display = 'flex'
|
|
201
|
-
li.style.
|
|
202
|
-
li.style.
|
|
203
|
-
li.style.
|
|
204
|
-
li.style.
|
|
205
|
-
li.style.
|
|
202
|
+
li.style.flexDirection = 'column'
|
|
203
|
+
li.style.alignItems = 'stretch'
|
|
204
|
+
li.style.justifyContent = 'flex-start'
|
|
205
|
+
li.style.padding = '5px 8px'
|
|
206
|
+
li.style.marginBottom = '0'
|
|
207
|
+
li.style.borderRadius = '5px'
|
|
206
208
|
li.style.transition = 'all 0.2s ease'
|
|
207
209
|
|
|
208
210
|
const info = document.createElement('div')
|
|
209
|
-
info.style.flex = '1'
|
|
211
|
+
info.style.flex = '1 1 auto'
|
|
212
|
+
info.style.width = '100%'
|
|
213
|
+
info.style.minWidth = '0'
|
|
210
214
|
|
|
211
215
|
const nameContainer = document.createElement('div')
|
|
212
216
|
nameContainer.style.display = 'flex'
|
|
213
217
|
nameContainer.style.alignItems = 'center'
|
|
214
|
-
nameContainer.style.marginBottom = '
|
|
218
|
+
nameContainer.style.marginBottom = '0'
|
|
215
219
|
nameContainer.style.flexWrap = 'wrap'
|
|
216
|
-
nameContainer.style.gap = '
|
|
220
|
+
nameContainer.style.gap = '4px'
|
|
217
221
|
|
|
218
222
|
const name = document.createElement('div')
|
|
219
223
|
name.style.fontWeight = '500'
|
|
220
|
-
name.style.fontSize = '
|
|
224
|
+
name.style.fontSize = '13px'
|
|
221
225
|
name.textContent = d.name || d.id
|
|
222
226
|
|
|
223
227
|
nameContainer.appendChild(name)
|
|
@@ -225,8 +229,8 @@ export async function renderDiscoveredDevices(
|
|
|
225
229
|
const duplicateBadge = document.createElement('span')
|
|
226
230
|
duplicateBadge.textContent = alreadyAdded ? '✓ Already Added' : '➕ New Device'
|
|
227
231
|
duplicateBadge.style.cssText = alreadyAdded
|
|
228
|
-
? 'background: #16a34a; color: white; padding:
|
|
229
|
-
: 'background: #2563eb; color: white; padding:
|
|
232
|
+
? 'background: #16a34a; color: white; padding: 1px 5px; border-radius: 3px; font-size: 9px; font-weight: 600;'
|
|
233
|
+
: 'background: #2563eb; color: white; padding: 1px 5px; border-radius: 3px; font-size: 9px; font-weight: 600;'
|
|
230
234
|
nameContainer.appendChild(duplicateBadge)
|
|
231
235
|
|
|
232
236
|
// Add connection type badge
|
|
@@ -247,10 +251,13 @@ export async function renderDiscoveredDevices(
|
|
|
247
251
|
}
|
|
248
252
|
|
|
249
253
|
const details = document.createElement('div')
|
|
250
|
-
details.style.fontSize = '
|
|
254
|
+
details.style.fontSize = '10px'
|
|
251
255
|
details.style.opacity = '0.7'
|
|
252
|
-
details.style.marginTop = '
|
|
256
|
+
details.style.marginTop = '0'
|
|
253
257
|
details.style.fontFamily = 'monospace'
|
|
258
|
+
details.style.whiteSpace = 'normal'
|
|
259
|
+
details.style.overflowWrap = 'anywhere'
|
|
260
|
+
details.style.wordBreak = 'break-word'
|
|
254
261
|
|
|
255
262
|
let detailsText = `ID: ${d.id} | Type: ${d.type} | Model: ${d.model || 'N/A'}`
|
|
256
263
|
if (d.hubDeviceId) { detailsText += ` | Hub: ${d.hubDeviceId}` }
|
|
@@ -262,9 +269,10 @@ export async function renderDiscoveredDevices(
|
|
|
262
269
|
|
|
263
270
|
const addBtn = document.createElement('button')
|
|
264
271
|
addBtn.textContent = alreadyAdded ? 'Already Added' : 'Add to Config'
|
|
265
|
-
addBtn.style.marginLeft = '
|
|
266
|
-
addBtn.style.
|
|
267
|
-
addBtn.style.
|
|
272
|
+
addBtn.style.marginLeft = '0'
|
|
273
|
+
addBtn.style.marginTop = '2px'
|
|
274
|
+
addBtn.style.padding = '4px 9px'
|
|
275
|
+
addBtn.style.fontSize = '11px'
|
|
268
276
|
addBtn.style.whiteSpace = 'nowrap'
|
|
269
277
|
addBtn.style.flexShrink = '0'
|
|
270
278
|
addBtn.disabled = alreadyAdded
|
|
@@ -284,17 +292,24 @@ export async function renderDiscoveredDevices(
|
|
|
284
292
|
const viewBtn = document.createElement('button')
|
|
285
293
|
viewBtn.textContent = 'View in Config'
|
|
286
294
|
viewBtn.className = 'secondary'
|
|
287
|
-
viewBtn.style.marginLeft = '
|
|
288
|
-
viewBtn.style.padding = '
|
|
289
|
-
viewBtn.style.fontSize = '
|
|
295
|
+
viewBtn.style.marginLeft = '0'
|
|
296
|
+
viewBtn.style.padding = '4px 9px'
|
|
297
|
+
viewBtn.style.fontSize = '11px'
|
|
290
298
|
viewBtn.onclick = async () => {
|
|
291
299
|
await loadConfiguredDevices()
|
|
292
300
|
scrollToConfiguredDevice(d.id)
|
|
293
301
|
}
|
|
294
302
|
li.appendChild(info)
|
|
295
303
|
const actions = document.createElement('div')
|
|
304
|
+
actions.className = 'device-actions'
|
|
296
305
|
actions.style.display = 'flex'
|
|
297
306
|
actions.style.alignItems = 'center'
|
|
307
|
+
actions.style.flexWrap = 'wrap'
|
|
308
|
+
actions.style.justifyContent = 'flex-start'
|
|
309
|
+
actions.style.marginLeft = '0'
|
|
310
|
+
actions.style.width = '100%'
|
|
311
|
+
actions.style.marginTop = '2px'
|
|
312
|
+
actions.style.gap = '5px'
|
|
298
313
|
actions.appendChild(viewBtn)
|
|
299
314
|
actions.appendChild(addBtn)
|
|
300
315
|
li.appendChild(actions)
|
|
@@ -302,8 +317,19 @@ export async function renderDiscoveredDevices(
|
|
|
302
317
|
continue
|
|
303
318
|
}
|
|
304
319
|
|
|
320
|
+
const actions = document.createElement('div')
|
|
321
|
+
actions.className = 'device-actions'
|
|
322
|
+
actions.style.display = 'flex'
|
|
323
|
+
actions.style.flexWrap = 'wrap'
|
|
324
|
+
actions.style.justifyContent = 'flex-start'
|
|
325
|
+
actions.style.marginLeft = '0'
|
|
326
|
+
actions.style.width = '100%'
|
|
327
|
+
actions.style.marginTop = '2px'
|
|
328
|
+
actions.style.gap = '5px'
|
|
329
|
+
actions.appendChild(addBtn)
|
|
330
|
+
|
|
305
331
|
li.appendChild(info)
|
|
306
|
-
li.appendChild(
|
|
332
|
+
li.appendChild(actions)
|
|
307
333
|
ul.appendChild(li)
|
|
308
334
|
}
|
|
309
335
|
|
|
@@ -474,6 +500,7 @@ export function renderDeviceList(list: any[]): void {
|
|
|
474
500
|
}
|
|
475
501
|
|
|
476
502
|
status.textContent = `Found ${list.length} device(s)`
|
|
503
|
+
ul.classList.add('device-grid')
|
|
477
504
|
ul.innerHTML = ''
|
|
478
505
|
|
|
479
506
|
// Show remove all button when devices exist
|
|
@@ -485,25 +512,36 @@ export function renderDeviceList(list: any[]): void {
|
|
|
485
512
|
const li = document.createElement('li')
|
|
486
513
|
li.className = 'device-item'
|
|
487
514
|
li.setAttribute('data-device-id', normalizeId(d.id))
|
|
515
|
+
li.style.display = 'flex'
|
|
516
|
+
li.style.flexDirection = 'column'
|
|
517
|
+
li.style.alignItems = 'stretch'
|
|
518
|
+
li.style.marginBottom = '0'
|
|
488
519
|
|
|
489
520
|
const info = document.createElement('div')
|
|
490
|
-
info.style.flex = '1'
|
|
521
|
+
info.style.flex = '1 1 auto'
|
|
522
|
+
info.style.width = '100%'
|
|
523
|
+
info.style.minWidth = '0'
|
|
491
524
|
|
|
492
525
|
const nameContainer = document.createElement('div')
|
|
493
526
|
nameContainer.style.display = 'flex'
|
|
494
|
-
nameContainer.style.
|
|
495
|
-
nameContainer.style.
|
|
496
|
-
nameContainer.style.gap = '
|
|
527
|
+
nameContainer.style.flexDirection = 'column'
|
|
528
|
+
nameContainer.style.alignItems = 'flex-start'
|
|
529
|
+
nameContainer.style.gap = '0'
|
|
497
530
|
|
|
498
531
|
const name = document.createElement('div')
|
|
499
532
|
name.style.fontWeight = '500'
|
|
533
|
+
name.style.fontSize = '13px'
|
|
500
534
|
name.textContent = d.name || d.id
|
|
501
535
|
|
|
502
536
|
const code = document.createElement('code')
|
|
503
537
|
code.textContent = d.id
|
|
504
|
-
code.style.fontSize = '
|
|
538
|
+
code.style.fontSize = '10px'
|
|
505
539
|
code.style.opacity = '0.75'
|
|
506
|
-
code.style.marginLeft = '
|
|
540
|
+
code.style.marginLeft = '0'
|
|
541
|
+
code.style.whiteSpace = 'normal'
|
|
542
|
+
code.style.overflowWrap = 'anywhere'
|
|
543
|
+
code.style.wordBreak = 'break-word'
|
|
544
|
+
code.style.maxWidth = '100%'
|
|
507
545
|
|
|
508
546
|
nameContainer.appendChild(name)
|
|
509
547
|
nameContainer.appendChild(code)
|
|
@@ -516,8 +554,8 @@ export function renderDeviceList(list: any[]): void {
|
|
|
516
554
|
|
|
517
555
|
const meta = document.createElement('div')
|
|
518
556
|
meta.style.opacity = '0.75'
|
|
519
|
-
meta.style.marginTop = '
|
|
520
|
-
meta.style.fontSize = '
|
|
557
|
+
meta.style.marginTop = '0'
|
|
558
|
+
meta.style.fontSize = '11px'
|
|
521
559
|
|
|
522
560
|
const typeText = d.type ? `type: ${d.type}` : ''
|
|
523
561
|
const connText = d.connectionPreference ? `conn: ${d.connectionPreference}` : ''
|
|
@@ -528,13 +566,19 @@ export function renderDeviceList(list: any[]): void {
|
|
|
528
566
|
info.appendChild(meta)
|
|
529
567
|
|
|
530
568
|
const buttons = document.createElement('div')
|
|
569
|
+
buttons.className = 'device-actions'
|
|
531
570
|
buttons.style.display = 'flex'
|
|
532
|
-
buttons.style.
|
|
571
|
+
buttons.style.flexWrap = 'wrap'
|
|
572
|
+
buttons.style.justifyContent = 'flex-start'
|
|
573
|
+
buttons.style.marginLeft = '0'
|
|
574
|
+
buttons.style.width = '100%'
|
|
575
|
+
buttons.style.marginTop = '2px'
|
|
576
|
+
buttons.style.gap = '5px'
|
|
533
577
|
|
|
534
578
|
const editBtn = document.createElement('button')
|
|
535
579
|
editBtn.textContent = '✏️ Edit'
|
|
536
|
-
editBtn.style.padding = '
|
|
537
|
-
editBtn.style.fontSize = '
|
|
580
|
+
editBtn.style.padding = '4px 9px'
|
|
581
|
+
editBtn.style.fontSize = '11px'
|
|
538
582
|
editBtn.onclick = async () => {
|
|
539
583
|
const { editDevice } = await import('./modals.js')
|
|
540
584
|
await editDevice(d)
|
|
@@ -542,8 +586,8 @@ export function renderDeviceList(list: any[]): void {
|
|
|
542
586
|
|
|
543
587
|
const copyBtn = document.createElement('button')
|
|
544
588
|
copyBtn.textContent = 'Copy ID'
|
|
545
|
-
copyBtn.style.padding = '
|
|
546
|
-
copyBtn.style.fontSize = '
|
|
589
|
+
copyBtn.style.padding = '4px 9px'
|
|
590
|
+
copyBtn.style.fontSize = '11px'
|
|
547
591
|
copyBtn.addEventListener('click', async () => {
|
|
548
592
|
try {
|
|
549
593
|
await navigator.clipboard.writeText(d.id)
|
|
@@ -560,8 +604,8 @@ export function renderDeviceList(list: any[]): void {
|
|
|
560
604
|
|
|
561
605
|
const deleteBtn = document.createElement('button')
|
|
562
606
|
deleteBtn.textContent = '🗑️ Delete'
|
|
563
|
-
deleteBtn.style.padding = '
|
|
564
|
-
deleteBtn.style.fontSize = '
|
|
607
|
+
deleteBtn.style.padding = '4px 9px'
|
|
608
|
+
deleteBtn.style.fontSize = '11px'
|
|
565
609
|
deleteBtn.style.background = '#ef4444'
|
|
566
610
|
deleteBtn.onclick = async () => {
|
|
567
611
|
const { deleteDeviceFromConfig } = await import('./devices-delete.js')
|
package/src/platform.ts
CHANGED
|
@@ -132,12 +132,7 @@ const DEVICE_MATTER_CLUSTERS: Record<string, any> = {
|
|
|
132
132
|
minLevel: 0,
|
|
133
133
|
maxLevel: 254,
|
|
134
134
|
},
|
|
135
|
-
colorControl: {
|
|
136
|
-
currentHue: 0,
|
|
137
|
-
currentSaturation: 0,
|
|
138
|
-
colorTemperatureMireds: 400,
|
|
139
|
-
colorMode: 0,
|
|
140
|
-
},
|
|
135
|
+
colorControl: {},
|
|
141
136
|
}, // Lightbulb with color → OnOff + LevelControl + ColorControl
|
|
142
137
|
lock: {
|
|
143
138
|
doorLock: {
|
package/test/e2e/run-e2e.spec.ts
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
4
|
-
import
|
|
1
|
+
import { execFile } from 'node:child_process'
|
|
2
|
+
import path from 'node:path'
|
|
3
|
+
import { promisify } from 'node:util'
|
|
4
|
+
import { describe, it } from 'vitest'
|
|
5
5
|
|
|
6
6
|
const execFileP = promisify(execFile)
|
|
7
7
|
|
|
8
|
-
describe('
|
|
8
|
+
describe('e2e scripts (manual-run harness)', () => {
|
|
9
9
|
if (!process.env.RUN_E2E) {
|
|
10
|
-
|
|
10
|
+
it.skip('e2e disabled - set RUN_E2E=true to enable', () => {})
|
|
11
11
|
return
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
it('should run configured e2e scripts sequentially', async () => {
|
|
15
15
|
const repoRoot = path.resolve(__dirname, '../../')
|
|
16
16
|
const scriptsDir = path.join(repoRoot, 'scripts', 'e2e')
|
|
17
17
|
|
|
18
|
-
const scriptsToRun: { envVar: string
|
|
18
|
+
const scriptsToRun: { envVar: string, script: string }[] = [
|
|
19
19
|
{ envVar: 'LIGHT_ACCESSORY_ID', script: 'light-e2e.sh' },
|
|
20
20
|
{ envVar: 'FAN_ACCESSORY_ID', script: 'fan-e2e.sh' },
|
|
21
21
|
{ envVar: 'CURTAIN_ACCESSORY_ID', script: 'curtain-e2e.sh' },
|
|
@@ -32,7 +32,7 @@ describe('E2E scripts (manual-run harness)', () => {
|
|
|
32
32
|
|
|
33
33
|
const scriptPath = path.join(scriptsDir, s.script)
|
|
34
34
|
// Forward environment so scripts can read HB_URL / HB_TOKEN / <ACCESSORY_ID>
|
|
35
|
-
const env =
|
|
35
|
+
const env = { ...process.env }
|
|
36
36
|
|
|
37
37
|
try {
|
|
38
38
|
// eslint-disable-next-line no-console
|
|
@@ -40,7 +40,9 @@ describe('E2E scripts (manual-run harness)', () => {
|
|
|
40
40
|
const { stdout, stderr } = await execFileP('bash', [scriptPath], { env })
|
|
41
41
|
// eslint-disable-next-line no-console
|
|
42
42
|
console.log(stdout)
|
|
43
|
-
if (stderr)
|
|
43
|
+
if (stderr) {
|
|
44
|
+
console.error(stderr)
|
|
45
|
+
}
|
|
44
46
|
} catch (e: any) {
|
|
45
47
|
// bubble up as test failure
|
|
46
48
|
throw new Error(`Script ${s.script} failed: ${e.message}`)
|
|
@@ -47,7 +47,7 @@ export function makeFakeApiWithoutMatter() {
|
|
|
47
47
|
isMatterEnabled: () => false,
|
|
48
48
|
hap: makeFakeHap(),
|
|
49
49
|
platformAccessory: PlatformAccessory,
|
|
50
|
-
registerPlatformAccessories: (
|
|
51
|
-
on: (
|
|
50
|
+
registerPlatformAccessories: () => {},
|
|
51
|
+
on: () => {},
|
|
52
52
|
}
|
|
53
53
|
}
|