@switchbot/homebridge-switchbot 5.0.0-beta.109 → 5.0.0-beta.110
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/config.schema.json +4 -0
- package/dist/device-types.d.ts +18 -2
- package/dist/device-types.d.ts.map +1 -1
- package/dist/device-types.js +71 -0
- package/dist/device-types.js.map +1 -1
- package/dist/deviceFactory.d.ts.map +1 -1
- package/dist/deviceFactory.js +1 -0
- package/dist/deviceFactory.js.map +1 -1
- package/dist/homebridge-ui/endpoints/config.d.ts +0 -3
- package/dist/homebridge-ui/endpoints/config.d.ts.map +1 -1
- package/dist/homebridge-ui/endpoints/config.js +40 -9
- package/dist/homebridge-ui/endpoints/config.js.map +1 -1
- package/dist/homebridge-ui/endpoints/devices.d.ts.map +1 -1
- package/dist/homebridge-ui/endpoints/devices.js +59 -2
- package/dist/homebridge-ui/endpoints/devices.js.map +1 -1
- package/dist/homebridge-ui/public/css/styles.css +254 -0
- package/dist/homebridge-ui/public/index.html +23 -168
- package/dist/homebridge-ui/public/js/api.d.ts +4 -0
- package/dist/homebridge-ui/public/js/api.d.ts.map +1 -1
- package/dist/homebridge-ui/public/js/api.js +14 -0
- package/dist/homebridge-ui/public/js/api.js.map +1 -1
- package/dist/homebridge-ui/public/js/api.ts +17 -1
- package/dist/homebridge-ui/public/js/app.js +556 -44
- package/dist/homebridge-ui/public/js/app.js.map +3 -3
- package/dist/homebridge-ui/public/js/app.ts +2 -1
- package/dist/homebridge-ui/public/js/devices-delete.d.ts +1 -0
- package/dist/homebridge-ui/public/js/devices-delete.d.ts.map +1 -1
- package/dist/homebridge-ui/public/js/devices-delete.js +108 -1
- package/dist/homebridge-ui/public/js/devices-delete.js.map +1 -1
- package/dist/homebridge-ui/public/js/devices-delete.ts +124 -1
- package/dist/homebridge-ui/public/js/devices.d.ts +1 -0
- package/dist/homebridge-ui/public/js/devices.d.ts.map +1 -1
- package/dist/homebridge-ui/public/js/devices.js +10 -0
- package/dist/homebridge-ui/public/js/devices.js.map +1 -1
- package/dist/homebridge-ui/public/js/devices.ts +10 -0
- package/dist/homebridge-ui/public/js/discovery.d.ts.map +1 -1
- package/dist/homebridge-ui/public/js/discovery.js +130 -4
- package/dist/homebridge-ui/public/js/discovery.js.map +1 -1
- package/dist/homebridge-ui/public/js/discovery.ts +158 -4
- package/dist/homebridge-ui/public/js/modals.d.ts.map +1 -1
- package/dist/homebridge-ui/public/js/modals.js +71 -26
- package/dist/homebridge-ui/public/js/modals.js.map +1 -1
- package/dist/homebridge-ui/public/js/modals.ts +74 -27
- package/dist/homebridge-ui/public/js/render.d.ts +57 -0
- package/dist/homebridge-ui/public/js/render.d.ts.map +1 -1
- package/dist/homebridge-ui/public/js/render.js +286 -13
- package/dist/homebridge-ui/public/js/render.js.map +1 -1
- package/dist/homebridge-ui/public/js/render.ts +331 -11
- package/dist/homebridge-ui/utils/device-migration.d.ts +35 -0
- package/dist/homebridge-ui/utils/device-migration.d.ts.map +1 -0
- package/dist/homebridge-ui/utils/device-migration.js +111 -0
- package/dist/homebridge-ui/utils/device-migration.js.map +1 -0
- package/dist/platform.js +2 -2
- package/dist/platform.js.map +1 -1
- package/docs/variables/default.html +1 -1
- package/package.json +1 -1
- package/src/device-types.ts +85 -0
- package/src/deviceFactory.ts +1 -0
- package/src/homebridge-ui/endpoints/config.ts +54 -10
- package/src/homebridge-ui/endpoints/devices.ts +62 -3
- package/src/homebridge-ui/public/css/styles.css +254 -0
- package/src/homebridge-ui/public/index.html +23 -168
- package/src/homebridge-ui/public/js/api.ts +17 -1
- package/src/homebridge-ui/public/js/app.ts +2 -1
- package/src/homebridge-ui/public/js/devices-delete.ts +124 -1
- package/src/homebridge-ui/public/js/devices.ts +10 -0
- package/src/homebridge-ui/public/js/discovery.ts +158 -4
- package/src/homebridge-ui/public/js/modals.ts +74 -27
- package/src/homebridge-ui/public/js/render.ts +331 -11
- package/src/homebridge-ui/utils/device-migration.ts +144 -0
- package/src/platform.ts +2 -2
- package/todo/COMPLETED.md +189 -0
- package/todo/README.md +59 -0
- package/todo/TODO.md +205 -0
- package/todo/mockups/filtering-sorting.svg +112 -0
- package/todo/mockups/modal-switchbot-theme.svg +66 -0
- package/todo/mockups/signal-strength-visualization.svg +94 -0
- package/TODO.md +0 -564
|
@@ -39,6 +39,7 @@ var api_exports = {};
|
|
|
39
39
|
__export(api_exports, {
|
|
40
40
|
addDevice: () => addDevice,
|
|
41
41
|
addDevicesInBulk: () => addDevicesInBulk,
|
|
42
|
+
deleteAllDevices: () => deleteAllDevices,
|
|
42
43
|
deleteDevice: () => deleteDevice,
|
|
43
44
|
discoverDevices: () => discoverDevices,
|
|
44
45
|
fetchCredentialStatus: () => fetchCredentialStatus,
|
|
@@ -194,6 +195,19 @@ async function deleteDevice(deviceId) {
|
|
|
194
195
|
throw e;
|
|
195
196
|
}
|
|
196
197
|
}
|
|
198
|
+
async function deleteAllDevices() {
|
|
199
|
+
try {
|
|
200
|
+
uiLog.info("Sending delete all devices request");
|
|
201
|
+
const resp = await homebridge.request("/delete-all-devices", {});
|
|
202
|
+
uiLog.info("Delete all response:", resp);
|
|
203
|
+
if (!resp || resp.success === false) {
|
|
204
|
+
throw new Error(resp?.data?.message || "Failed to delete all devices");
|
|
205
|
+
}
|
|
206
|
+
return resp.data || resp;
|
|
207
|
+
} catch (e) {
|
|
208
|
+
throw e;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
197
211
|
var init_api = __esm({
|
|
198
212
|
"src/homebridge-ui/public/js/api.ts"() {
|
|
199
213
|
"use strict";
|
|
@@ -300,7 +314,10 @@ async function discoverDevices2() {
|
|
|
300
314
|
devices.map((d) => ({
|
|
301
315
|
deviceId: d.id,
|
|
302
316
|
name: d.name,
|
|
303
|
-
type: d.type
|
|
317
|
+
type: d.type,
|
|
318
|
+
rssi: d.rssi,
|
|
319
|
+
address: d.address,
|
|
320
|
+
model: d.model
|
|
304
321
|
}))
|
|
305
322
|
);
|
|
306
323
|
uiLog.info("Bulk add response:", bulkResult);
|
|
@@ -336,10 +353,102 @@ async function discoverDevices2() {
|
|
|
336
353
|
return;
|
|
337
354
|
}
|
|
338
355
|
status.textContent = `Found ${devices.length} device(s)`;
|
|
339
|
-
const
|
|
340
|
-
|
|
341
|
-
|
|
356
|
+
const controlsDiv = document.createElement("div");
|
|
357
|
+
controlsDiv.style.cssText = "margin-bottom: 12px; display: flex; gap: 12px; flex-wrap: wrap; align-items: center;";
|
|
358
|
+
const preferences = getDiscoveryPreferences();
|
|
359
|
+
const filterLabel = document.createElement("label");
|
|
360
|
+
filterLabel.style.fontSize = "12px";
|
|
361
|
+
filterLabel.style.fontWeight = "500";
|
|
362
|
+
filterLabel.textContent = "Filter:";
|
|
363
|
+
const filterGroup = document.createElement("div");
|
|
364
|
+
filterGroup.style.display = "flex";
|
|
365
|
+
filterGroup.style.gap = "4px";
|
|
366
|
+
const filterOptions = [
|
|
367
|
+
{ label: "All", value: "all" },
|
|
368
|
+
{ label: "BLE", value: "ble" },
|
|
369
|
+
{ label: "API", value: "api" },
|
|
370
|
+
{ label: "Both", value: "both" },
|
|
371
|
+
{ label: "IR", value: "ir" }
|
|
372
|
+
];
|
|
373
|
+
for (const option of filterOptions) {
|
|
374
|
+
const btn2 = document.createElement("button");
|
|
375
|
+
btn2.textContent = option.label;
|
|
376
|
+
btn2.style.padding = "4px 8px";
|
|
377
|
+
btn2.style.fontSize = "11px";
|
|
378
|
+
btn2.style.borderRadius = "3px";
|
|
379
|
+
btn2.style.cursor = "pointer";
|
|
380
|
+
btn2.style.border = preferences.connectionType === option.value ? "2px solid #007AFF" : "1px solid #ccc";
|
|
381
|
+
btn2.style.backgroundColor = preferences.connectionType === option.value ? "#f0f7ff" : "#fff";
|
|
382
|
+
btn2.onclick = () => {
|
|
383
|
+
preferences.connectionType = option.value;
|
|
384
|
+
setDiscoveryPreferences(preferences);
|
|
385
|
+
updateDiscoveryView(devices, preferences);
|
|
386
|
+
for (const b of Array.from(filterGroup.querySelectorAll("button"))) {
|
|
387
|
+
;
|
|
388
|
+
b.style.border = "1px solid #ccc";
|
|
389
|
+
b.style.backgroundColor = "#fff";
|
|
390
|
+
}
|
|
391
|
+
btn2.style.border = "2px solid #007AFF";
|
|
392
|
+
btn2.style.backgroundColor = "#f0f7ff";
|
|
393
|
+
};
|
|
394
|
+
filterGroup.appendChild(btn2);
|
|
395
|
+
}
|
|
396
|
+
const sortLabel = document.createElement("label");
|
|
397
|
+
sortLabel.style.fontSize = "12px";
|
|
398
|
+
sortLabel.style.fontWeight = "500";
|
|
399
|
+
sortLabel.style.marginLeft = "8px";
|
|
400
|
+
sortLabel.textContent = "Sort:";
|
|
401
|
+
const sortSelect = document.createElement("select");
|
|
402
|
+
sortSelect.style.fontSize = "11px";
|
|
403
|
+
sortSelect.style.padding = "4px 8px";
|
|
404
|
+
sortSelect.style.borderRadius = "3px";
|
|
405
|
+
sortSelect.value = preferences.sortBy;
|
|
406
|
+
const sortOptions = [
|
|
407
|
+
{ label: "Name", value: "name" },
|
|
408
|
+
{ label: "Signal Strength", value: "signal" },
|
|
409
|
+
{ label: "Type", value: "type" },
|
|
410
|
+
{ label: "Connection", value: "connection" }
|
|
411
|
+
];
|
|
412
|
+
for (const opt of sortOptions) {
|
|
413
|
+
const option = document.createElement("option");
|
|
414
|
+
option.value = opt.value;
|
|
415
|
+
option.textContent = opt.label;
|
|
416
|
+
sortSelect.appendChild(option);
|
|
417
|
+
}
|
|
418
|
+
sortSelect.onchange = () => {
|
|
419
|
+
preferences.sortBy = sortSelect.value;
|
|
420
|
+
setDiscoveryPreferences(preferences);
|
|
421
|
+
updateDiscoveryView(devices, preferences);
|
|
422
|
+
};
|
|
423
|
+
const searchInput = document.createElement("input");
|
|
424
|
+
searchInput.type = "text";
|
|
425
|
+
searchInput.placeholder = "Search by name, ID, or type...";
|
|
426
|
+
searchInput.style.fontSize = "11px";
|
|
427
|
+
searchInput.style.padding = "4px 8px";
|
|
428
|
+
searchInput.style.borderRadius = "3px";
|
|
429
|
+
searchInput.style.border = "1px solid #ccc";
|
|
430
|
+
searchInput.style.flex = "1";
|
|
431
|
+
searchInput.style.minWidth = "200px";
|
|
432
|
+
searchInput.style.maxWidth = "300px";
|
|
433
|
+
searchInput.value = preferences.searchQuery;
|
|
434
|
+
let searchTimeout;
|
|
435
|
+
searchInput.oninput = () => {
|
|
436
|
+
clearTimeout(searchTimeout);
|
|
437
|
+
searchTimeout = setTimeout(() => {
|
|
438
|
+
preferences.searchQuery = searchInput.value;
|
|
439
|
+
setDiscoveryPreferences(preferences);
|
|
440
|
+
updateDiscoveryView(devices, preferences);
|
|
441
|
+
}, 300);
|
|
442
|
+
};
|
|
443
|
+
controlsDiv.appendChild(filterLabel);
|
|
444
|
+
controlsDiv.appendChild(filterGroup);
|
|
445
|
+
controlsDiv.appendChild(sortLabel);
|
|
446
|
+
controlsDiv.appendChild(sortSelect);
|
|
447
|
+
controlsDiv.appendChild(searchInput);
|
|
448
|
+
list.innerHTML = "";
|
|
449
|
+
list.appendChild(controlsDiv);
|
|
342
450
|
list.style.display = "block";
|
|
451
|
+
await updateDiscoveryView(devices, preferences);
|
|
343
452
|
} catch (e) {
|
|
344
453
|
uiLog.error("Discovery error:", e);
|
|
345
454
|
status.textContent = `Error: ${e instanceof Error ? e.message : "Discovery failed"}`;
|
|
@@ -352,6 +461,19 @@ async function discoverDevices2() {
|
|
|
352
461
|
btn.textContent = "\u{1F50D} Discover Devices";
|
|
353
462
|
}
|
|
354
463
|
}
|
|
464
|
+
async function updateDiscoveryView(allDevices, preferences) {
|
|
465
|
+
const filtered = filterDevices(allDevices, preferences.connectionType, preferences.searchQuery);
|
|
466
|
+
const sorted = sortDevices(filtered, preferences.sortBy);
|
|
467
|
+
const ul = await renderDiscoveredDevices(sorted);
|
|
468
|
+
document.getElementById("discoveredDevices")?.replaceWith(ul);
|
|
469
|
+
ul.id = "discoveredDevices";
|
|
470
|
+
const status = document.getElementById("discoverStatus");
|
|
471
|
+
if (status) {
|
|
472
|
+
const totalCount = allDevices.length;
|
|
473
|
+
const filteredCount = sorted.length;
|
|
474
|
+
status.textContent = filteredCount === totalCount ? `Found ${totalCount} device(s)` : `Showing ${filteredCount} of ${totalCount} device(s)`;
|
|
475
|
+
}
|
|
476
|
+
}
|
|
355
477
|
async function addDeviceToConfig(device) {
|
|
356
478
|
const { addDeviceToConfig: addDevice2 } = await Promise.resolve().then(() => (init_devices(), devices_exports));
|
|
357
479
|
await addDevice2(device);
|
|
@@ -380,6 +502,7 @@ var init_device_types = __esm({
|
|
|
380
502
|
"Keypad Touch",
|
|
381
503
|
"Keypad Vision",
|
|
382
504
|
"Keypad Vision Pro",
|
|
505
|
+
"Lock Vision Pro",
|
|
383
506
|
"Lock Lite",
|
|
384
507
|
"Smart Lock",
|
|
385
508
|
"Smart Lock Pro",
|
|
@@ -411,8 +534,11 @@ var init_device_types = __esm({
|
|
|
411
534
|
"Humidifier2",
|
|
412
535
|
"Meter",
|
|
413
536
|
"MeterPlus",
|
|
537
|
+
"Meter Plus",
|
|
414
538
|
"MeterPro",
|
|
539
|
+
"Meter Pro",
|
|
415
540
|
"MeterPro(CO2)",
|
|
541
|
+
"Meter Pro (CO2)",
|
|
416
542
|
"Smart Radiator Thermostat",
|
|
417
543
|
"Standing Circulator Fan",
|
|
418
544
|
"WoIOSensor"
|
|
@@ -495,49 +621,80 @@ async function editDevice(device) {
|
|
|
495
621
|
const modal = document.createElement("div");
|
|
496
622
|
modal.style.background = getComputedStyle(document.body).backgroundColor;
|
|
497
623
|
modal.style.color = getComputedStyle(document.body).color;
|
|
498
|
-
modal.style.padding = "
|
|
499
|
-
modal.style.borderRadius = "
|
|
500
|
-
modal.style.minWidth = "
|
|
501
|
-
modal.style.
|
|
624
|
+
modal.style.padding = "0";
|
|
625
|
+
modal.style.borderRadius = "10px";
|
|
626
|
+
modal.style.minWidth = "440px";
|
|
627
|
+
modal.style.maxWidth = "90vw";
|
|
628
|
+
modal.style.boxShadow = "0 8px 32px rgba(0,0,0,0.35)";
|
|
629
|
+
modal.style.overflow = "hidden";
|
|
630
|
+
modal.style.borderTop = "3px solid var(--switchbot-red, #ef4444)";
|
|
502
631
|
const title = document.createElement("h3");
|
|
503
632
|
title.textContent = "Edit Device";
|
|
504
633
|
title.style.marginTop = "0";
|
|
634
|
+
title.style.marginBottom = "16px";
|
|
635
|
+
title.style.padding = "20px 20px 0";
|
|
636
|
+
title.style.fontSize = "18px";
|
|
637
|
+
title.style.fontWeight = "600";
|
|
638
|
+
title.style.color = "var(--switchbot-red, #ef4444)";
|
|
639
|
+
title.style.letterSpacing = "-0.02em";
|
|
640
|
+
const contentDiv = document.createElement("div");
|
|
641
|
+
contentDiv.style.padding = "0 20px 20px";
|
|
505
642
|
const nameLabel = document.createElement("label");
|
|
506
643
|
nameLabel.textContent = "Device Name";
|
|
507
644
|
nameLabel.style.display = "block";
|
|
508
|
-
nameLabel.style.marginBottom = "
|
|
645
|
+
nameLabel.style.marginBottom = "6px";
|
|
509
646
|
nameLabel.style.fontWeight = "500";
|
|
647
|
+
nameLabel.style.fontSize = "12px";
|
|
648
|
+
nameLabel.style.color = "#6b7280";
|
|
510
649
|
const nameInput = document.createElement("input");
|
|
511
650
|
nameInput.type = "text";
|
|
512
651
|
nameInput.value = device.name || device.id;
|
|
513
652
|
nameInput.style.width = "100%";
|
|
514
|
-
nameInput.style.marginBottom = "
|
|
653
|
+
nameInput.style.marginBottom = "12px";
|
|
654
|
+
nameInput.style.padding = "8px 10px";
|
|
655
|
+
nameInput.style.borderRadius = "6px";
|
|
656
|
+
nameInput.style.fontSize = "14px";
|
|
657
|
+
nameInput.style.boxSizing = "border-box";
|
|
658
|
+
nameInput.style.transition = "border-color 0.2s ease";
|
|
515
659
|
const apiTypeLabel = document.createElement("label");
|
|
516
660
|
apiTypeLabel.textContent = "Device Type (API - Read Only)";
|
|
517
661
|
apiTypeLabel.style.display = "block";
|
|
518
|
-
apiTypeLabel.style.marginBottom = "
|
|
662
|
+
apiTypeLabel.style.marginBottom = "6px";
|
|
519
663
|
apiTypeLabel.style.fontWeight = "500";
|
|
664
|
+
apiTypeLabel.style.fontSize = "12px";
|
|
665
|
+
apiTypeLabel.style.color = "#6b7280";
|
|
520
666
|
const apiTypeInput = document.createElement("input");
|
|
521
667
|
apiTypeInput.type = "text";
|
|
522
668
|
apiTypeInput.value = device.deviceType || device.type || "Unknown";
|
|
523
669
|
apiTypeInput.readOnly = true;
|
|
524
670
|
apiTypeInput.style.width = "100%";
|
|
525
|
-
apiTypeInput.style.marginBottom = "
|
|
671
|
+
apiTypeInput.style.marginBottom = "12px";
|
|
672
|
+
apiTypeInput.style.padding = "8px 10px";
|
|
673
|
+
apiTypeInput.style.borderRadius = "6px";
|
|
674
|
+
apiTypeInput.style.fontSize = "13px";
|
|
526
675
|
apiTypeInput.style.opacity = "0.6";
|
|
527
676
|
apiTypeInput.style.cursor = "not-allowed";
|
|
677
|
+
apiTypeInput.style.boxSizing = "border-box";
|
|
678
|
+
apiTypeInput.style.backgroundColor = "#f9fafb";
|
|
528
679
|
const typeLabel = document.createElement("label");
|
|
529
680
|
typeLabel.textContent = "Config Device Type (Override)";
|
|
530
681
|
typeLabel.style.display = "block";
|
|
531
|
-
typeLabel.style.marginBottom = "
|
|
682
|
+
typeLabel.style.marginBottom = "6px";
|
|
532
683
|
typeLabel.style.fontWeight = "500";
|
|
684
|
+
typeLabel.style.fontSize = "12px";
|
|
685
|
+
typeLabel.style.color = "#6b7280";
|
|
533
686
|
const typeSelect = document.createElement("select");
|
|
534
687
|
typeSelect.style.width = "100%";
|
|
535
|
-
typeSelect.style.padding = "8px";
|
|
536
|
-
typeSelect.style.marginBottom = "
|
|
537
|
-
typeSelect.style.borderRadius = "
|
|
688
|
+
typeSelect.style.padding = "8px 10px";
|
|
689
|
+
typeSelect.style.marginBottom = "12px";
|
|
690
|
+
typeSelect.style.borderRadius = "6px";
|
|
691
|
+
typeSelect.style.fontSize = "14px";
|
|
538
692
|
typeSelect.style.background = getComputedStyle(nameInput).background;
|
|
539
693
|
typeSelect.style.color = getComputedStyle(nameInput).color;
|
|
540
694
|
typeSelect.style.border = getComputedStyle(nameInput).border;
|
|
695
|
+
typeSelect.style.boxSizing = "border-box";
|
|
696
|
+
typeSelect.style.cursor = "pointer";
|
|
697
|
+
typeSelect.style.transition = "border-color 0.2s ease";
|
|
541
698
|
Object.keys(DEVICE_TYPES).forEach((categoryName) => {
|
|
542
699
|
const optgroup = document.createElement("optgroup");
|
|
543
700
|
optgroup.label = categoryName;
|
|
@@ -551,21 +708,34 @@ async function editDevice(device) {
|
|
|
551
708
|
});
|
|
552
709
|
typeSelect.appendChild(optgroup);
|
|
553
710
|
});
|
|
554
|
-
const buttons = document.createElement("div");
|
|
555
|
-
buttons.style.display = "flex";
|
|
556
|
-
buttons.style.gap = "8px";
|
|
557
|
-
buttons.style.justifyContent = "flex-end";
|
|
558
711
|
const errorMessage = document.createElement("div");
|
|
559
|
-
errorMessage.style.color = "#ef4444";
|
|
712
|
+
errorMessage.style.color = "var(--switchbot-red, #ef4444)";
|
|
560
713
|
errorMessage.style.marginBottom = "12px";
|
|
561
714
|
errorMessage.style.fontSize = "12px";
|
|
562
715
|
errorMessage.style.display = "none";
|
|
716
|
+
errorMessage.style.padding = "8px 10px";
|
|
717
|
+
errorMessage.style.background = "var(--switchbot-red-light, #fee2e2)";
|
|
718
|
+
errorMessage.style.borderRadius = "6px";
|
|
719
|
+
errorMessage.style.fontWeight = "500";
|
|
720
|
+
const buttons = document.createElement("div");
|
|
721
|
+
buttons.style.display = "flex";
|
|
722
|
+
buttons.style.gap = "10px";
|
|
723
|
+
buttons.style.justifyContent = "flex-end";
|
|
724
|
+
buttons.style.marginTop = "18px";
|
|
725
|
+
buttons.style.paddingTop = "18px";
|
|
726
|
+
buttons.style.borderTop = "1px solid rgba(0, 0, 0, 0.08)";
|
|
563
727
|
const cancelBtn = document.createElement("button");
|
|
564
728
|
cancelBtn.textContent = "Cancel";
|
|
565
|
-
cancelBtn.
|
|
729
|
+
cancelBtn.className = "secondary";
|
|
730
|
+
cancelBtn.style.background = "#6b7280";
|
|
731
|
+
cancelBtn.style.padding = "8px 16px";
|
|
732
|
+
cancelBtn.style.fontSize = "13px";
|
|
566
733
|
cancelBtn.onclick = () => div.remove();
|
|
567
734
|
const saveBtn = document.createElement("button");
|
|
568
735
|
saveBtn.textContent = "Save";
|
|
736
|
+
saveBtn.style.background = "var(--switchbot-red, #ef4444)";
|
|
737
|
+
saveBtn.style.padding = "8px 20px";
|
|
738
|
+
saveBtn.style.fontSize = "13px";
|
|
569
739
|
saveBtn.onclick = async () => {
|
|
570
740
|
try {
|
|
571
741
|
const { updateDevice: updateDevice2, syncParentPluginConfigFromDisk: syncParentPluginConfigFromDisk2, fetchDevices: fetchDevices3 } = await Promise.resolve().then(() => (init_api(), api_exports));
|
|
@@ -589,15 +759,16 @@ async function editDevice(device) {
|
|
|
589
759
|
};
|
|
590
760
|
buttons.appendChild(cancelBtn);
|
|
591
761
|
buttons.appendChild(saveBtn);
|
|
762
|
+
contentDiv.appendChild(nameLabel);
|
|
763
|
+
contentDiv.appendChild(nameInput);
|
|
764
|
+
contentDiv.appendChild(apiTypeLabel);
|
|
765
|
+
contentDiv.appendChild(apiTypeInput);
|
|
766
|
+
contentDiv.appendChild(typeLabel);
|
|
767
|
+
contentDiv.appendChild(typeSelect);
|
|
768
|
+
contentDiv.appendChild(errorMessage);
|
|
769
|
+
contentDiv.appendChild(buttons);
|
|
592
770
|
modal.appendChild(title);
|
|
593
|
-
modal.appendChild(
|
|
594
|
-
modal.appendChild(nameInput);
|
|
595
|
-
modal.appendChild(apiTypeLabel);
|
|
596
|
-
modal.appendChild(apiTypeInput);
|
|
597
|
-
modal.appendChild(typeLabel);
|
|
598
|
-
modal.appendChild(typeSelect);
|
|
599
|
-
modal.appendChild(errorMessage);
|
|
600
|
-
modal.appendChild(buttons);
|
|
771
|
+
modal.appendChild(contentDiv);
|
|
601
772
|
div.appendChild(modal);
|
|
602
773
|
document.body.appendChild(div);
|
|
603
774
|
nameInput.focus();
|
|
@@ -613,6 +784,7 @@ var init_modals = __esm({
|
|
|
613
784
|
// src/homebridge-ui/public/js/devices-delete.ts
|
|
614
785
|
var devices_delete_exports = {};
|
|
615
786
|
__export(devices_delete_exports, {
|
|
787
|
+
deleteAllDevicesFromConfig: () => deleteAllDevicesFromConfig,
|
|
616
788
|
deleteDeviceFromConfig: () => deleteDeviceFromConfig
|
|
617
789
|
});
|
|
618
790
|
async function confirmDeleteDialog(deviceNameOrId) {
|
|
@@ -705,6 +877,110 @@ async function deleteDeviceFromConfig(deviceId, deviceName) {
|
|
|
705
877
|
hideBusyUi();
|
|
706
878
|
}
|
|
707
879
|
}
|
|
880
|
+
async function confirmDeleteAllDialog(deviceCount) {
|
|
881
|
+
return new Promise((resolve) => {
|
|
882
|
+
const overlay = document.createElement("div");
|
|
883
|
+
overlay.style.position = "fixed";
|
|
884
|
+
overlay.style.top = "0";
|
|
885
|
+
overlay.style.left = "0";
|
|
886
|
+
overlay.style.width = "100%";
|
|
887
|
+
overlay.style.height = "100%";
|
|
888
|
+
overlay.style.background = "rgba(0,0,0,0.7)";
|
|
889
|
+
overlay.style.display = "flex";
|
|
890
|
+
overlay.style.alignItems = "center";
|
|
891
|
+
overlay.style.justifyContent = "center";
|
|
892
|
+
overlay.style.zIndex = "10000";
|
|
893
|
+
const modal = document.createElement("div");
|
|
894
|
+
modal.style.background = getComputedStyle(document.body).backgroundColor;
|
|
895
|
+
modal.style.color = getComputedStyle(document.body).color;
|
|
896
|
+
modal.style.padding = "20px";
|
|
897
|
+
modal.style.borderRadius = "10px";
|
|
898
|
+
modal.style.minWidth = "380px";
|
|
899
|
+
modal.style.maxWidth = "90vw";
|
|
900
|
+
modal.style.boxShadow = "0 12px 40px rgba(0,0,0,0.35)";
|
|
901
|
+
modal.style.borderTop = "3px solid #ef4444";
|
|
902
|
+
const title = document.createElement("h3");
|
|
903
|
+
title.textContent = "\u26A0\uFE0F Remove All Devices";
|
|
904
|
+
title.style.margin = "0 0 12px 0";
|
|
905
|
+
title.style.color = "#ef4444";
|
|
906
|
+
const message = document.createElement("p");
|
|
907
|
+
message.innerHTML = `Are you sure you want to remove <strong>all ${deviceCount} device(s)</strong> from your configuration?<br><br>This action cannot be undone.`;
|
|
908
|
+
message.style.margin = "0 0 18px 0";
|
|
909
|
+
message.style.lineHeight = "1.5";
|
|
910
|
+
const actions = document.createElement("div");
|
|
911
|
+
actions.style.display = "flex";
|
|
912
|
+
actions.style.justifyContent = "flex-end";
|
|
913
|
+
actions.style.gap = "10px";
|
|
914
|
+
const cancelBtn = document.createElement("button");
|
|
915
|
+
cancelBtn.textContent = "Cancel";
|
|
916
|
+
cancelBtn.style.background = "#6b7280";
|
|
917
|
+
cancelBtn.style.padding = "8px 16px";
|
|
918
|
+
cancelBtn.style.fontSize = "13px";
|
|
919
|
+
cancelBtn.className = "secondary";
|
|
920
|
+
const deleteBtn = document.createElement("button");
|
|
921
|
+
deleteBtn.textContent = "Remove All";
|
|
922
|
+
deleteBtn.style.background = "#ef4444";
|
|
923
|
+
deleteBtn.style.padding = "8px 20px";
|
|
924
|
+
deleteBtn.style.fontSize = "13px";
|
|
925
|
+
const cleanup = (result) => {
|
|
926
|
+
overlay.remove();
|
|
927
|
+
resolve(result);
|
|
928
|
+
};
|
|
929
|
+
cancelBtn.onclick = () => cleanup(false);
|
|
930
|
+
deleteBtn.onclick = () => cleanup(true);
|
|
931
|
+
overlay.addEventListener("click", (event) => {
|
|
932
|
+
if (event.target === overlay) {
|
|
933
|
+
cleanup(false);
|
|
934
|
+
}
|
|
935
|
+
});
|
|
936
|
+
actions.appendChild(cancelBtn);
|
|
937
|
+
actions.appendChild(deleteBtn);
|
|
938
|
+
modal.appendChild(title);
|
|
939
|
+
modal.appendChild(message);
|
|
940
|
+
modal.appendChild(actions);
|
|
941
|
+
overlay.appendChild(modal);
|
|
942
|
+
document.body.appendChild(overlay);
|
|
943
|
+
});
|
|
944
|
+
}
|
|
945
|
+
async function deleteAllDevicesFromConfig() {
|
|
946
|
+
uiLog.info("Remove All Devices button clicked");
|
|
947
|
+
try {
|
|
948
|
+
const list = await fetchDevices();
|
|
949
|
+
if (!list || list.length === 0) {
|
|
950
|
+
toastWarning("No devices to remove");
|
|
951
|
+
return;
|
|
952
|
+
}
|
|
953
|
+
const confirmed = await confirmDeleteAllDialog(list.length);
|
|
954
|
+
if (!confirmed) {
|
|
955
|
+
uiLog.info("Remove all cancelled by user");
|
|
956
|
+
return;
|
|
957
|
+
}
|
|
958
|
+
showBusyUi();
|
|
959
|
+
uiLog.info("Sending delete all devices request");
|
|
960
|
+
const resp = await deleteAllDevices();
|
|
961
|
+
uiLog.info("Delete all response:", resp);
|
|
962
|
+
if (!resp || resp.success === false) {
|
|
963
|
+
throw new Error(resp?.data?.message || "Failed to delete all devices");
|
|
964
|
+
}
|
|
965
|
+
uiLog.info("Syncing parent config from disk...");
|
|
966
|
+
const synced = await syncParentPluginConfigFromDisk(true);
|
|
967
|
+
if (!synced) {
|
|
968
|
+
toastWarning("Devices deleted, but configuration sync failed");
|
|
969
|
+
}
|
|
970
|
+
uiLog.info("Refreshing device list...");
|
|
971
|
+
const updatedList = await fetchDevices();
|
|
972
|
+
uiLog.info("Rendering devices:", updatedList.length);
|
|
973
|
+
renderDeviceList(updatedList);
|
|
974
|
+
const deletedCount = resp?.deletedCount || list.length;
|
|
975
|
+
uiLog.info(`\u2713 Removed ${deletedCount} device(s) successfully`);
|
|
976
|
+
toastSuccess(`Removed ${deletedCount} device(s) successfully`);
|
|
977
|
+
} catch (e) {
|
|
978
|
+
uiLog.error("Delete all error:", e);
|
|
979
|
+
toastError(e instanceof Error ? e.message : "Failed to delete all devices");
|
|
980
|
+
} finally {
|
|
981
|
+
hideBusyUi();
|
|
982
|
+
}
|
|
983
|
+
}
|
|
708
984
|
var init_devices_delete = __esm({
|
|
709
985
|
"src/homebridge-ui/public/js/devices-delete.ts"() {
|
|
710
986
|
"use strict";
|
|
@@ -719,12 +995,104 @@ var init_devices_delete = __esm({
|
|
|
719
995
|
// src/homebridge-ui/public/js/render.ts
|
|
720
996
|
var render_exports = {};
|
|
721
997
|
__export(render_exports, {
|
|
998
|
+
filterDevices: () => filterDevices,
|
|
999
|
+
getDiscoveryPreferences: () => getDiscoveryPreferences,
|
|
1000
|
+
getRssiSignalQuality: () => getRssiSignalQuality,
|
|
722
1001
|
renderBadge: () => renderBadge,
|
|
723
1002
|
renderConnectionBadge: () => renderConnectionBadge,
|
|
724
1003
|
renderDeviceList: () => renderDeviceList,
|
|
725
1004
|
renderDiscoveredDevices: () => renderDiscoveredDevices,
|
|
726
|
-
renderIRBadge: () => renderIRBadge
|
|
1005
|
+
renderIRBadge: () => renderIRBadge,
|
|
1006
|
+
renderSignalBars: () => renderSignalBars,
|
|
1007
|
+
renderSignalQualityBadge: () => renderSignalQualityBadge,
|
|
1008
|
+
setDiscoveryPreferences: () => setDiscoveryPreferences,
|
|
1009
|
+
sortDevices: () => sortDevices
|
|
727
1010
|
});
|
|
1011
|
+
function getRssiSignalQuality(rssi) {
|
|
1012
|
+
if (!rssi || rssi === 0) {
|
|
1013
|
+
return {
|
|
1014
|
+
level: "unknown",
|
|
1015
|
+
color: "#999",
|
|
1016
|
+
bgColor: "#f5f5f5",
|
|
1017
|
+
description: "Signal strength unknown",
|
|
1018
|
+
bars: 0
|
|
1019
|
+
};
|
|
1020
|
+
}
|
|
1021
|
+
const dbm = Math.floor(rssi);
|
|
1022
|
+
if (dbm > -60) {
|
|
1023
|
+
return {
|
|
1024
|
+
level: "excellent",
|
|
1025
|
+
color: "#34a853",
|
|
1026
|
+
bgColor: "#e8f5e9",
|
|
1027
|
+
description: `Excellent (${dbm} dBm)`,
|
|
1028
|
+
bars: 4
|
|
1029
|
+
};
|
|
1030
|
+
} else if (dbm > -75) {
|
|
1031
|
+
return {
|
|
1032
|
+
level: "good",
|
|
1033
|
+
color: "#fbbc04",
|
|
1034
|
+
bgColor: "#fffde7",
|
|
1035
|
+
description: `Good (${dbm} dBm)`,
|
|
1036
|
+
bars: 3
|
|
1037
|
+
};
|
|
1038
|
+
} else if (dbm > -85) {
|
|
1039
|
+
return {
|
|
1040
|
+
level: "fair",
|
|
1041
|
+
color: "#ff9800",
|
|
1042
|
+
bgColor: "#fff3e0",
|
|
1043
|
+
description: `Fair (${dbm} dBm)`,
|
|
1044
|
+
bars: 2
|
|
1045
|
+
};
|
|
1046
|
+
} else {
|
|
1047
|
+
return {
|
|
1048
|
+
level: "poor",
|
|
1049
|
+
color: "#ea4335",
|
|
1050
|
+
bgColor: "#ffebee",
|
|
1051
|
+
description: `Poor (${dbm} dBm) - unreliable`,
|
|
1052
|
+
bars: 1
|
|
1053
|
+
};
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
1056
|
+
function renderSignalBars(rssi) {
|
|
1057
|
+
const quality = getRssiSignalQuality(rssi);
|
|
1058
|
+
const container = document.createElement("span");
|
|
1059
|
+
container.style.display = "inline-flex";
|
|
1060
|
+
container.style.gap = "2px";
|
|
1061
|
+
container.style.alignItems = "center";
|
|
1062
|
+
container.style.marginLeft = "8px";
|
|
1063
|
+
container.style.fontSize = "12px";
|
|
1064
|
+
for (let i = 1; i <= 4; i++) {
|
|
1065
|
+
const bar = document.createElement("span");
|
|
1066
|
+
bar.style.height = `${i * 3}px`;
|
|
1067
|
+
bar.style.width = "3px";
|
|
1068
|
+
bar.style.borderRadius = "1px";
|
|
1069
|
+
bar.style.border = `1px solid ${quality.color}`;
|
|
1070
|
+
if (i <= quality.bars) {
|
|
1071
|
+
bar.style.backgroundColor = quality.color;
|
|
1072
|
+
} else {
|
|
1073
|
+
bar.style.backgroundColor = "transparent";
|
|
1074
|
+
}
|
|
1075
|
+
container.appendChild(bar);
|
|
1076
|
+
}
|
|
1077
|
+
container.title = quality.description;
|
|
1078
|
+
return container;
|
|
1079
|
+
}
|
|
1080
|
+
function renderSignalQualityBadge(rssi) {
|
|
1081
|
+
const quality = getRssiSignalQuality(rssi);
|
|
1082
|
+
const badge = document.createElement("span");
|
|
1083
|
+
badge.textContent = quality.level.charAt(0).toUpperCase() + quality.level.slice(1);
|
|
1084
|
+
badge.style.cssText = `
|
|
1085
|
+
background: ${quality.color};
|
|
1086
|
+
color: white;
|
|
1087
|
+
padding: 2px 6px;
|
|
1088
|
+
border-radius: 3px;
|
|
1089
|
+
font-size: 10px;
|
|
1090
|
+
font-weight: 600;
|
|
1091
|
+
margin-left: 8px;
|
|
1092
|
+
`;
|
|
1093
|
+
badge.title = quality.description;
|
|
1094
|
+
return badge;
|
|
1095
|
+
}
|
|
728
1096
|
function renderBadge(text, style) {
|
|
729
1097
|
const badge = document.createElement("span");
|
|
730
1098
|
badge.textContent = text;
|
|
@@ -753,47 +1121,70 @@ function renderIRBadge() {
|
|
|
753
1121
|
}
|
|
754
1122
|
async function renderDiscoveredDevices(devices) {
|
|
755
1123
|
const ul = document.createElement("ul");
|
|
756
|
-
ul.style.maxHeight = "
|
|
1124
|
+
ul.style.maxHeight = "400px";
|
|
757
1125
|
ul.style.overflowY = "auto";
|
|
1126
|
+
ul.style.marginTop = "12px";
|
|
1127
|
+
ul.style.padding = "0";
|
|
1128
|
+
ul.style.listStyle = "none";
|
|
758
1129
|
const { addDeviceToConfig: addDeviceToConfig3 } = await Promise.resolve().then(() => (init_discovery(), discovery_exports));
|
|
759
1130
|
for (const d of devices) {
|
|
760
1131
|
const li = document.createElement("li");
|
|
761
1132
|
li.className = "device-item";
|
|
1133
|
+
li.style.display = "flex";
|
|
1134
|
+
li.style.alignItems = "center";
|
|
1135
|
+
li.style.justifyContent = "space-between";
|
|
1136
|
+
li.style.padding = "10px 12px";
|
|
1137
|
+
li.style.marginBottom = "6px";
|
|
1138
|
+
li.style.borderRadius = "6px";
|
|
1139
|
+
li.style.transition = "all 0.2s ease";
|
|
762
1140
|
const info = document.createElement("div");
|
|
763
1141
|
info.style.flex = "1";
|
|
1142
|
+
const nameContainer = document.createElement("div");
|
|
1143
|
+
nameContainer.style.display = "flex";
|
|
1144
|
+
nameContainer.style.alignItems = "center";
|
|
1145
|
+
nameContainer.style.marginBottom = "4px";
|
|
1146
|
+
nameContainer.style.flexWrap = "wrap";
|
|
1147
|
+
nameContainer.style.gap = "6px";
|
|
764
1148
|
const name = document.createElement("div");
|
|
765
1149
|
name.style.fontWeight = "500";
|
|
1150
|
+
name.style.fontSize = "14px";
|
|
766
1151
|
name.textContent = d.name || d.id;
|
|
1152
|
+
nameContainer.appendChild(name);
|
|
767
1153
|
if (d.connectionType) {
|
|
768
|
-
name.appendChild(document.createTextNode(" "));
|
|
769
1154
|
const badge = renderConnectionBadge(d.connectionType);
|
|
770
1155
|
if (badge) {
|
|
771
|
-
|
|
1156
|
+
nameContainer.appendChild(badge);
|
|
772
1157
|
}
|
|
773
1158
|
}
|
|
774
1159
|
if (d.isIR) {
|
|
775
|
-
|
|
776
|
-
|
|
1160
|
+
nameContainer.appendChild(renderIRBadge());
|
|
1161
|
+
}
|
|
1162
|
+
if (d.rssi !== void 0 && d.rssi !== null && d.rssi !== 0) {
|
|
1163
|
+
nameContainer.appendChild(renderSignalBars(d.rssi));
|
|
1164
|
+
nameContainer.appendChild(renderSignalQualityBadge(d.rssi));
|
|
777
1165
|
}
|
|
778
1166
|
const details = document.createElement("div");
|
|
779
|
-
details.style.fontSize = "
|
|
780
|
-
details.style.opacity = "0.
|
|
1167
|
+
details.style.fontSize = "11px";
|
|
1168
|
+
details.style.opacity = "0.7";
|
|
1169
|
+
details.style.marginTop = "4px";
|
|
1170
|
+
details.style.fontFamily = "monospace";
|
|
781
1171
|
let detailsText = `ID: ${d.id} | Type: ${d.type} | Model: ${d.model || "N/A"}`;
|
|
782
1172
|
if (d.hubDeviceId) {
|
|
783
1173
|
detailsText += ` | Hub: ${d.hubDeviceId}`;
|
|
784
1174
|
}
|
|
785
|
-
if (d.rssi) {
|
|
786
|
-
detailsText += ` | Signal: ${d.rssi} dBm`;
|
|
787
|
-
}
|
|
788
1175
|
if (d.address) {
|
|
789
1176
|
detailsText += ` | MAC: ${d.address}`;
|
|
790
1177
|
}
|
|
791
1178
|
details.textContent = detailsText;
|
|
792
|
-
info.appendChild(
|
|
1179
|
+
info.appendChild(nameContainer);
|
|
793
1180
|
info.appendChild(details);
|
|
794
1181
|
const addBtn = document.createElement("button");
|
|
795
1182
|
addBtn.textContent = "+ Add";
|
|
796
1183
|
addBtn.style.marginLeft = "8px";
|
|
1184
|
+
addBtn.style.padding = "6px 14px";
|
|
1185
|
+
addBtn.style.fontSize = "12px";
|
|
1186
|
+
addBtn.style.whiteSpace = "nowrap";
|
|
1187
|
+
addBtn.style.flexShrink = "0";
|
|
797
1188
|
addBtn.onclick = async () => {
|
|
798
1189
|
await addDeviceToConfig3(d);
|
|
799
1190
|
};
|
|
@@ -803,24 +1194,128 @@ async function renderDiscoveredDevices(devices) {
|
|
|
803
1194
|
}
|
|
804
1195
|
return ul;
|
|
805
1196
|
}
|
|
1197
|
+
function filterDevices(devices, connectionType = "all", searchQuery = "") {
|
|
1198
|
+
let filtered = [...devices];
|
|
1199
|
+
if (connectionType !== "all") {
|
|
1200
|
+
filtered = filtered.filter((d) => {
|
|
1201
|
+
if (connectionType === "ir") {
|
|
1202
|
+
return d.isIR === true;
|
|
1203
|
+
}
|
|
1204
|
+
if (connectionType === "ble") {
|
|
1205
|
+
return d.connectionType === "BLE" || d.connectionType?.includes("BLE");
|
|
1206
|
+
}
|
|
1207
|
+
if (connectionType === "api") {
|
|
1208
|
+
return d.connectionType === "OpenAPI" || d.connectionType === "API" || d.connectionType?.includes("API");
|
|
1209
|
+
}
|
|
1210
|
+
if (connectionType === "both") {
|
|
1211
|
+
return d.connectionType === "Both" || d.connectionType?.includes("Both");
|
|
1212
|
+
}
|
|
1213
|
+
return true;
|
|
1214
|
+
});
|
|
1215
|
+
}
|
|
1216
|
+
if (searchQuery.trim()) {
|
|
1217
|
+
const query = searchQuery.toLowerCase();
|
|
1218
|
+
filtered = filtered.filter((d) => {
|
|
1219
|
+
const name = (d.name || "").toLowerCase();
|
|
1220
|
+
const id = (d.id || "").toLowerCase();
|
|
1221
|
+
const type = (d.type || "").toLowerCase();
|
|
1222
|
+
const model = (d.model || "").toLowerCase();
|
|
1223
|
+
return name.includes(query) || id.includes(query) || type.includes(query) || model.includes(query);
|
|
1224
|
+
});
|
|
1225
|
+
}
|
|
1226
|
+
return filtered;
|
|
1227
|
+
}
|
|
1228
|
+
function sortDevices(devices, sortBy = "name") {
|
|
1229
|
+
const sorted = [...devices];
|
|
1230
|
+
switch (sortBy) {
|
|
1231
|
+
case "signal": {
|
|
1232
|
+
sorted.sort((a, b) => {
|
|
1233
|
+
const aRssi = a.rssi || 0;
|
|
1234
|
+
const bRssi = b.rssi || 0;
|
|
1235
|
+
return bRssi - aRssi;
|
|
1236
|
+
});
|
|
1237
|
+
break;
|
|
1238
|
+
}
|
|
1239
|
+
case "type": {
|
|
1240
|
+
sorted.sort((a, b) => {
|
|
1241
|
+
const aType = (a.type || "").localeCompare(b.type || "");
|
|
1242
|
+
return aType;
|
|
1243
|
+
});
|
|
1244
|
+
break;
|
|
1245
|
+
}
|
|
1246
|
+
case "connection": {
|
|
1247
|
+
const connectionOrder = {
|
|
1248
|
+
"Both": 0,
|
|
1249
|
+
"BLE": 1,
|
|
1250
|
+
"OpenAPI": 2,
|
|
1251
|
+
"API": 2,
|
|
1252
|
+
"Unknown": 3
|
|
1253
|
+
};
|
|
1254
|
+
sorted.sort((a, b) => {
|
|
1255
|
+
const aOrder = connectionOrder[a.connectionType || "Unknown"] ?? 3;
|
|
1256
|
+
const bOrder = connectionOrder[b.connectionType || "Unknown"] ?? 3;
|
|
1257
|
+
return aOrder - bOrder;
|
|
1258
|
+
});
|
|
1259
|
+
break;
|
|
1260
|
+
}
|
|
1261
|
+
case "name":
|
|
1262
|
+
default: {
|
|
1263
|
+
sorted.sort((a, b) => (a.name || a.id || "").localeCompare(b.name || b.id || ""));
|
|
1264
|
+
break;
|
|
1265
|
+
}
|
|
1266
|
+
}
|
|
1267
|
+
return sorted;
|
|
1268
|
+
}
|
|
1269
|
+
function getDiscoveryPreferences() {
|
|
1270
|
+
try {
|
|
1271
|
+
const stored = localStorage.getItem("discoveryPreferences");
|
|
1272
|
+
if (stored) {
|
|
1273
|
+
return JSON.parse(stored);
|
|
1274
|
+
}
|
|
1275
|
+
} catch (_e) {
|
|
1276
|
+
}
|
|
1277
|
+
return {
|
|
1278
|
+
connectionType: "all",
|
|
1279
|
+
sortBy: "name",
|
|
1280
|
+
searchQuery: ""
|
|
1281
|
+
};
|
|
1282
|
+
}
|
|
1283
|
+
function setDiscoveryPreferences(preferences) {
|
|
1284
|
+
try {
|
|
1285
|
+
localStorage.setItem("discoveryPreferences", JSON.stringify(preferences));
|
|
1286
|
+
} catch (_e) {
|
|
1287
|
+
}
|
|
1288
|
+
}
|
|
806
1289
|
function renderDeviceList(list) {
|
|
807
1290
|
const ul = document.getElementById("devices");
|
|
808
1291
|
const status = document.getElementById("status");
|
|
1292
|
+
const removeAllContainer = document.getElementById("removeAllContainer");
|
|
809
1293
|
if (!ul || !status) {
|
|
810
1294
|
return;
|
|
811
1295
|
}
|
|
812
1296
|
if (!list.length) {
|
|
813
1297
|
status.textContent = "No devices found in config.";
|
|
814
1298
|
ul.innerHTML = "";
|
|
1299
|
+
if (removeAllContainer) {
|
|
1300
|
+
removeAllContainer.style.display = "none";
|
|
1301
|
+
}
|
|
815
1302
|
return;
|
|
816
1303
|
}
|
|
817
1304
|
status.textContent = `Found ${list.length} device(s)`;
|
|
818
1305
|
ul.innerHTML = "";
|
|
1306
|
+
if (removeAllContainer) {
|
|
1307
|
+
removeAllContainer.style.display = "block";
|
|
1308
|
+
}
|
|
819
1309
|
for (const d of list) {
|
|
820
1310
|
const li = document.createElement("li");
|
|
821
1311
|
li.className = "device-item";
|
|
822
1312
|
const info = document.createElement("div");
|
|
823
1313
|
info.style.flex = "1";
|
|
1314
|
+
const nameContainer = document.createElement("div");
|
|
1315
|
+
nameContainer.style.display = "flex";
|
|
1316
|
+
nameContainer.style.alignItems = "center";
|
|
1317
|
+
nameContainer.style.flexWrap = "wrap";
|
|
1318
|
+
nameContainer.style.gap = "6px";
|
|
824
1319
|
const name = document.createElement("div");
|
|
825
1320
|
name.style.fontWeight = "500";
|
|
826
1321
|
name.textContent = d.name || d.id;
|
|
@@ -829,6 +1324,12 @@ function renderDeviceList(list) {
|
|
|
829
1324
|
code.style.fontSize = "12px";
|
|
830
1325
|
code.style.opacity = "0.75";
|
|
831
1326
|
code.style.marginLeft = "8px";
|
|
1327
|
+
nameContainer.appendChild(name);
|
|
1328
|
+
nameContainer.appendChild(code);
|
|
1329
|
+
if (d.rssi !== void 0 && d.rssi !== null && d.rssi !== 0) {
|
|
1330
|
+
nameContainer.appendChild(renderSignalBars(d.rssi));
|
|
1331
|
+
nameContainer.appendChild(renderSignalQualityBadge(d.rssi));
|
|
1332
|
+
}
|
|
832
1333
|
const meta = document.createElement("div");
|
|
833
1334
|
meta.style.opacity = "0.75";
|
|
834
1335
|
meta.style.marginTop = "4px";
|
|
@@ -837,8 +1338,7 @@ function renderDeviceList(list) {
|
|
|
837
1338
|
const connText = d.connectionPreference ? `conn: ${d.connectionPreference}` : "";
|
|
838
1339
|
const roomText = d.room ? `room: ${d.room}` : "";
|
|
839
1340
|
meta.textContent = [typeText, connText, roomText].filter(Boolean).join(" | ");
|
|
840
|
-
info.appendChild(
|
|
841
|
-
info.appendChild(code);
|
|
1341
|
+
info.appendChild(nameContainer);
|
|
842
1342
|
info.appendChild(meta);
|
|
843
1343
|
const buttons = document.createElement("div");
|
|
844
1344
|
buttons.style.display = "flex";
|
|
@@ -895,6 +1395,7 @@ var init_render = __esm({
|
|
|
895
1395
|
var devices_exports = {};
|
|
896
1396
|
__export(devices_exports, {
|
|
897
1397
|
addDeviceToConfig: () => addDeviceToConfig2,
|
|
1398
|
+
initRemoveAllButton: () => initRemoveAllButton,
|
|
898
1399
|
loadConfiguredDevices: () => loadConfiguredDevices
|
|
899
1400
|
});
|
|
900
1401
|
async function addDeviceToConfig2(device, options = {}) {
|
|
@@ -948,6 +1449,16 @@ async function addDeviceToConfig2(device, options = {}) {
|
|
|
948
1449
|
hideBusyUi();
|
|
949
1450
|
}
|
|
950
1451
|
}
|
|
1452
|
+
async function initRemoveAllButton() {
|
|
1453
|
+
const removeAllBtn = document.getElementById("removeAllBtn");
|
|
1454
|
+
if (!removeAllBtn) {
|
|
1455
|
+
return;
|
|
1456
|
+
}
|
|
1457
|
+
removeAllBtn.addEventListener("click", async () => {
|
|
1458
|
+
const { deleteAllDevicesFromConfig: deleteAllDevicesFromConfig2 } = await Promise.resolve().then(() => (init_devices_delete(), devices_delete_exports));
|
|
1459
|
+
await deleteAllDevicesFromConfig2();
|
|
1460
|
+
});
|
|
1461
|
+
}
|
|
951
1462
|
async function loadConfiguredDevices() {
|
|
952
1463
|
const list = await fetchDevices();
|
|
953
1464
|
renderDeviceList(list);
|
|
@@ -1060,6 +1571,7 @@ window.discoverDevices = discoverDevices2;
|
|
|
1060
1571
|
async function init() {
|
|
1061
1572
|
await loadCredentialStatus();
|
|
1062
1573
|
const list = await loadConfiguredDevices();
|
|
1574
|
+
await initRemoveAllButton();
|
|
1063
1575
|
}
|
|
1064
1576
|
if (document.readyState === "loading") {
|
|
1065
1577
|
document.addEventListener("DOMContentLoaded", init);
|