@switchbot/homebridge-switchbot 5.1.0-beta.2 → 5.1.0-beta.3

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 (34) hide show
  1. package/.github/scripts/normalize-changelog-header.cjs +86 -0
  2. package/.github/scripts/npm-version-script-esm-auto.js +201 -0
  3. package/.github/scripts/resolve-version-bump-esm.js +118 -0
  4. package/.github/workflows/release.yml +4 -4
  5. package/CHANGELOG.md +18 -0
  6. package/dist/homebridge-ui/public/css/styles.css +1 -1
  7. package/dist/homebridge-ui/public/index.html +1 -5
  8. package/dist/homebridge-ui/public/js/api.d.ts +1 -0
  9. package/dist/homebridge-ui/public/js/api.d.ts.map +1 -1
  10. package/dist/homebridge-ui/public/js/api.js +14 -1
  11. package/dist/homebridge-ui/public/js/api.js.map +1 -1
  12. package/dist/homebridge-ui/public/js/api.ts +16 -1
  13. package/dist/homebridge-ui/public/js/app.js +54 -71
  14. package/dist/homebridge-ui/public/js/app.js.map +2 -2
  15. package/dist/homebridge-ui/public/js/discovery.d.ts.map +1 -1
  16. package/dist/homebridge-ui/public/js/discovery.js +0 -36
  17. package/dist/homebridge-ui/public/js/discovery.js.map +1 -1
  18. package/dist/homebridge-ui/public/js/discovery.ts +0 -37
  19. package/dist/homebridge-ui/public/js/render.d.ts.map +1 -1
  20. package/dist/homebridge-ui/public/js/render.js +38 -31
  21. package/dist/homebridge-ui/public/js/render.js.map +1 -1
  22. package/dist/homebridge-ui/public/js/render.ts +39 -34
  23. package/dist/homebridge-ui/public/js/toast.d.ts.map +1 -1
  24. package/dist/homebridge-ui/public/js/toast.js +2 -3
  25. package/dist/homebridge-ui/public/js/toast.js.map +1 -1
  26. package/dist/homebridge-ui/public/js/toast.ts +2 -3
  27. package/docs/variables/default.html +1 -1
  28. package/package.json +1 -1
  29. package/src/homebridge-ui/public/css/styles.css +1 -1
  30. package/src/homebridge-ui/public/index.html +0 -4
  31. package/src/homebridge-ui/public/js/api.ts +16 -1
  32. package/src/homebridge-ui/public/js/discovery.ts +0 -37
  33. package/src/homebridge-ui/public/js/render.ts +39 -34
  34. package/src/homebridge-ui/public/js/toast.ts +2 -3
@@ -299,10 +299,9 @@ function showToast(method, message, title = "SwitchBot") {
299
299
  try {
300
300
  const hb = typeof window !== "undefined" ? window.homebridge : void 0;
301
301
  const toast = hb && typeof hb.toast === "object" ? hb.toast : void 0;
302
- const fn = toast && typeof toast[method] === "function" ? toast[method] : void 0;
303
- if (fn) {
302
+ if (toast && typeof toast[method] === "function") {
304
303
  try {
305
- fn(message, title);
304
+ toast[method](message, title);
306
305
  return;
307
306
  } catch (err) {
308
307
  uiLog.warn(`Toast ${method} threw:`, err);
@@ -346,6 +345,7 @@ __export(api_exports, {
346
345
  fetchBluetoothStatus: () => fetchBluetoothStatus,
347
346
  fetchCredentialStatus: () => fetchCredentialStatus,
348
347
  fetchDevices: () => fetchDevices,
348
+ normalizeBulkAddDevicesResponse: () => normalizeBulkAddDevicesResponse,
349
349
  saveCredentials: () => saveCredentials,
350
350
  syncParentPluginConfigFromDisk: () => syncParentPluginConfigFromDisk,
351
351
  testDeviceConnection: () => testDeviceConnection,
@@ -563,7 +563,20 @@ async function addDevicesInBulk(devices) {
563
563
  if (!resp || resp.success === false) {
564
564
  throw new Error(resp?.data?.message || "Bulk add failed");
565
565
  }
566
- return resp.data || resp;
566
+ return normalizeBulkAddDevicesResponse(resp);
567
+ }
568
+ function normalizeBulkAddDevicesResponse(resp) {
569
+ const payload = resp?.data && typeof resp.data === "object" ? resp.data : resp;
570
+ const addedCount = Number(payload?.addedCount ?? payload?.added ?? 0);
571
+ const skippedCount = Number(payload?.skippedCount ?? payload?.skipped ?? 0);
572
+ const updatedCount = Number(payload?.updatedCount ?? payload?.updated ?? 0);
573
+ return {
574
+ ...payload,
575
+ success: resp?.success ?? true,
576
+ addedCount,
577
+ skippedCount,
578
+ updatedCount
579
+ };
567
580
  }
568
581
  async function updateDevice(deviceId, configDeviceName, configDeviceType, options) {
569
582
  if (typeof homebridge.getPluginConfig !== "function" || typeof homebridge.updatePluginConfig !== "function") {
@@ -999,7 +1012,6 @@ function setDiscoveryGroupExpanded(groupKey, expanded) {
999
1012
  }
1000
1013
  async function discoverDevices2() {
1001
1014
  const btn = document.getElementById("discoverBtn");
1002
- const cancelBtn = document.getElementById("cancelDiscoverBtn");
1003
1015
  const status = document.getElementById("discoverStatus");
1004
1016
  const phaseProgress = document.getElementById("discoverPhaseProgress");
1005
1017
  const phaseFill = document.getElementById("discoverPhaseFill");
@@ -1027,7 +1039,6 @@ async function discoverDevices2() {
1027
1039
  const startedAt = Date.now();
1028
1040
  let phaseStartedAt = startedAt;
1029
1041
  let phase = "Preparing discovery...";
1030
- let cancelled = false;
1031
1042
  const setPhase = (nextPhase) => {
1032
1043
  phase = nextPhase;
1033
1044
  phaseStartedAt = Date.now();
@@ -1464,31 +1475,8 @@ async function discoverDevices2() {
1464
1475
  devicesFoundDisplay.style.display = "none";
1465
1476
  devicesFoundDisplay.classList.remove("discovery-scanning-pulse");
1466
1477
  }
1467
- if (cancelBtn) {
1468
- cancelBtn.style.display = "inline-block";
1469
- cancelBtn.disabled = false;
1470
- cancelBtn.onclick = () => {
1471
- cancelled = true;
1472
- const totalSeconds = Math.max(0, Math.floor((Date.now() - startedAt) / 1e3));
1473
- status.textContent = `Discovery cancelled (${totalSeconds}s total)`;
1474
- if (phaseProgress) {
1475
- phaseProgress.style.display = "none";
1476
- }
1477
- if (devicesFoundDisplay) {
1478
- devicesFoundDisplay.style.display = "none";
1479
- devicesFoundDisplay.classList.remove("discovery-scanning-pulse");
1480
- }
1481
- cancelBtn.style.display = "none";
1482
- btn.disabled = false;
1483
- btn.textContent = "\u{1F50D} Discover Devices";
1484
- hideBusyUi();
1485
- };
1486
- }
1487
1478
  if (bleSettings.bleEnabled) {
1488
1479
  const bleDevicesRaw = await discoverDevices("ble", bleSettings);
1489
- if (cancelled) {
1490
- return;
1491
- }
1492
1480
  discoveredDevices = dedupeById(bleDevicesRaw);
1493
1481
  uiLog.info("BLE discover response:", bleDevicesRaw);
1494
1482
  if (devicesFoundDisplay && bleDevicesRaw.length > 0) {
@@ -1509,9 +1497,6 @@ async function discoverDevices2() {
1509
1497
  renderProgress();
1510
1498
  try {
1511
1499
  const openApiDevicesRaw = await discoverDevices("openapi");
1512
- if (cancelled) {
1513
- return;
1514
- }
1515
1500
  uiLog.info("OpenAPI discover response:", openApiDevicesRaw);
1516
1501
  discoveredDevices = mergeDiscoveredDevices(discoveredDevices, openApiDevicesRaw);
1517
1502
  if (devicesFoundDisplay && discoveredDevices.length > 0) {
@@ -1591,9 +1576,6 @@ async function discoverDevices2() {
1591
1576
  setDiscoveryCache(discoveredDevices);
1592
1577
  updateLastScannedStatus();
1593
1578
  } catch (e) {
1594
- if (cancelled) {
1595
- return;
1596
- }
1597
1579
  uiLog.error("Discovery error:", e);
1598
1580
  status.textContent = `Error: ${e instanceof Error ? e.message : "Discovery failed"}`;
1599
1581
  status.classList.add("error");
@@ -1618,11 +1600,6 @@ async function discoverDevices2() {
1618
1600
  }
1619
1601
  btn.disabled = false;
1620
1602
  btn.textContent = "\u{1F50D} Discover Devices";
1621
- if (cancelBtn) {
1622
- cancelBtn.disabled = false;
1623
- cancelBtn.style.display = "none";
1624
- cancelBtn.onclick = null;
1625
- }
1626
1603
  }
1627
1604
  }
1628
1605
  async function updateDiscoveryView(allDevices, preferences, groupBy, hideAdded, selectedIds) {
@@ -2750,6 +2727,21 @@ __export(render_exports, {
2750
2727
  setDiscoveryPreferences: () => setDiscoveryPreferences,
2751
2728
  sortDevices: () => sortDevices
2752
2729
  });
2730
+ async function copyTextWithFallback(text) {
2731
+ try {
2732
+ await navigator.clipboard.writeText(text);
2733
+ } catch {
2734
+ const textarea = document.createElement("textarea");
2735
+ textarea.value = text;
2736
+ textarea.setAttribute("readonly", "");
2737
+ textarea.style.cssText = "position:fixed;left:-9999px;opacity:0;pointer-events:none";
2738
+ document.body.appendChild(textarea);
2739
+ textarea.select();
2740
+ textarea.setSelectionRange(0, textarea.value.length);
2741
+ document.execCommand("copy");
2742
+ textarea.remove();
2743
+ }
2744
+ }
2753
2745
  function getRssiSignalQuality(rssi) {
2754
2746
  if (!rssi || rssi === 0) {
2755
2747
  return {
@@ -2830,7 +2822,6 @@ function renderSignalQualityBadge(rssi) {
2830
2822
  border-radius: 3px;
2831
2823
  font-size: 10px;
2832
2824
  font-weight: 600;
2833
- margin-left: 8px;
2834
2825
  `;
2835
2826
  badge.title = quality.description;
2836
2827
  return badge;
@@ -3475,6 +3466,7 @@ function renderDeviceList(list) {
3475
3466
  }
3476
3467
  status.textContent = `Found ${list.length} device(s)`;
3477
3468
  ul.classList.add("device-grid");
3469
+ ul.style.padding = "0";
3478
3470
  ul.innerHTML = "";
3479
3471
  if (removeAllContainer) {
3480
3472
  removeAllContainer.style.display = "block";
@@ -3486,6 +3478,7 @@ function renderDeviceList(list) {
3486
3478
  li.style.display = "flex";
3487
3479
  li.style.flexDirection = "column";
3488
3480
  li.style.alignItems = "stretch";
3481
+ li.style.padding = "5px 8px";
3489
3482
  li.style.marginBottom = "0";
3490
3483
  const info = document.createElement("div");
3491
3484
  info.style.flex = "1 1 auto";
@@ -3493,13 +3486,14 @@ function renderDeviceList(list) {
3493
3486
  info.style.minWidth = "0";
3494
3487
  const nameContainer = document.createElement("div");
3495
3488
  nameContainer.style.display = "flex";
3496
- nameContainer.style.flexDirection = "column";
3497
- nameContainer.style.alignItems = "flex-start";
3498
- nameContainer.style.gap = "0";
3489
+ nameContainer.style.alignItems = "center";
3490
+ nameContainer.style.marginBottom = "0";
3491
+ nameContainer.style.flexWrap = "wrap";
3492
+ nameContainer.style.gap = "4px";
3499
3493
  const name = document.createElement("div");
3500
3494
  name.style.fontWeight = "500";
3501
3495
  name.style.fontSize = "13px";
3502
- name.textContent = d.name || d.id;
3496
+ name.textContent = d.configDeviceName || d.name || d.id;
3503
3497
  const expandedDetails = document.createElement("div");
3504
3498
  expandedDetails.style.display = "none";
3505
3499
  expandedDetails.appendChild(renderDeviceDetailsPanel(d));
@@ -3517,36 +3511,23 @@ function renderDeviceList(list) {
3517
3511
  expandedDetails.style.display = isHidden ? "block" : "none";
3518
3512
  expandBtn.style.transform = isHidden ? "rotate(180deg)" : "rotate(0deg)";
3519
3513
  };
3520
- const code = document.createElement("code");
3521
- code.textContent = d.id;
3522
- code.style.fontSize = "10px";
3523
- code.style.opacity = "0.75";
3524
- code.style.marginLeft = "0";
3525
- code.style.whiteSpace = "normal";
3526
- code.style.overflowWrap = "anywhere";
3527
- code.style.wordBreak = "break-word";
3528
- code.style.maxWidth = "100%";
3529
- const headerRow = document.createElement("div");
3530
- headerRow.style.display = "inline-flex";
3531
- headerRow.style.alignItems = "center";
3532
- headerRow.style.gap = "4px";
3533
- headerRow.appendChild(name);
3534
- headerRow.appendChild(expandBtn);
3535
- nameContainer.appendChild(headerRow);
3536
- nameContainer.appendChild(code);
3514
+ nameContainer.appendChild(name);
3515
+ nameContainer.appendChild(expandBtn);
3537
3516
  if (d.rssi !== void 0 && d.rssi !== null && d.rssi !== 0) {
3538
3517
  nameContainer.appendChild(renderSignalBars(d.rssi));
3539
3518
  nameContainer.appendChild(renderSignalQualityBadge(d.rssi));
3540
3519
  }
3541
3520
  const meta = document.createElement("div");
3542
- meta.style.opacity = "0.75";
3543
- meta.style.marginTop = "0";
3544
- meta.style.fontSize = "11px";
3545
- const typeText = d.type ? `type: ${d.type}` : "";
3546
- const connText = d.connectionPreference ? `conn: ${d.connectionPreference}` : "";
3521
+ meta.style.opacity = "0.7";
3522
+ meta.style.fontSize = "10px";
3523
+ meta.style.fontFamily = "monospace";
3524
+ const deviceIdentifier = d.deviceId || d.id;
3525
+ const id = `ID: ${deviceIdentifier}`;
3526
+ const typeText = d.configDeviceType || d.type ? `Type: ${d.configDeviceType || d.type}` : "";
3527
+ const connText = d.connectionPreference ? `Conn: ${d.connectionPreference}` : "";
3547
3528
  const extText = d.externalPublishProtocol && d.externalPublishProtocol !== "none" ? `external: ${d.externalPublishProtocol}` : "";
3548
- const roomText = d.room ? `room: ${d.room}` : "";
3549
- meta.textContent = [typeText, connText, extText, roomText].filter(Boolean).join(" | ");
3529
+ const roomText = d.room ? `Room: ${d.room}` : "";
3530
+ meta.textContent = [id, typeText, connText, extText, roomText].filter(Boolean).join(" | ");
3550
3531
  info.appendChild(nameContainer);
3551
3532
  info.appendChild(meta);
3552
3533
  info.appendChild(expandedDetails);
@@ -3573,7 +3554,9 @@ function renderDeviceList(list) {
3573
3554
  copyBtn.style.fontSize = "11px";
3574
3555
  copyBtn.addEventListener("click", async () => {
3575
3556
  try {
3576
- await navigator.clipboard.writeText(d.id);
3557
+ if (deviceIdentifier) {
3558
+ await copyTextWithFallback(deviceIdentifier);
3559
+ }
3577
3560
  copyBtn.textContent = "Copied";
3578
3561
  copyBtn.classList.add("success");
3579
3562
  setTimeout(() => {
@@ -3600,8 +3583,8 @@ function renderDeviceList(list) {
3600
3583
  };
3601
3584
  buttons.appendChild(editBtn);
3602
3585
  buttons.appendChild(copyBtn);
3603
- buttons.appendChild(createConnectionTestControls(d));
3604
3586
  buttons.appendChild(deleteBtn);
3587
+ buttons.appendChild(createConnectionTestControls(d));
3605
3588
  li.appendChild(info);
3606
3589
  li.appendChild(buttons);
3607
3590
  ul.appendChild(li);