@switchbot/homebridge-switchbot 5.0.1 → 5.0.2

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.
@@ -5,6 +5,22 @@ const SPACES_REGEX = /\s/g
5
5
  const CAMELCASE_REGEX = /([A-Z])/g
6
6
  const FIRST_CHAR_REGEX = /^./
7
7
 
8
+ async function copyTextWithFallback(text: string): Promise<void> {
9
+ try {
10
+ await navigator.clipboard.writeText(text)
11
+ } catch {
12
+ const textarea = document.createElement('textarea')
13
+ textarea.value = text
14
+ textarea.setAttribute('readonly', '')
15
+ textarea.style.cssText = 'position:fixed;left:-9999px;opacity:0;pointer-events:none'
16
+ document.body.appendChild(textarea)
17
+ textarea.select()
18
+ textarea.setSelectionRange(0, textarea.value.length)
19
+ document.execCommand('copy')
20
+ textarea.remove()
21
+ }
22
+ }
23
+
8
24
  /**
9
25
  * Get RSSI signal quality level and color based on dBm value
10
26
  * @param rssi Signal strength in dBm (typically -30 to -90)
@@ -994,7 +1010,8 @@ export function renderDeviceList(list: any[]): void {
994
1010
  meta.style.fontSize = '10px'
995
1011
  meta.style.fontFamily = 'monospace'
996
1012
 
997
- const id = `ID: ${d.deviceId || d.id}`
1013
+ const deviceIdentifier = d.deviceId || d.id
1014
+ const id = `ID: ${deviceIdentifier}`
998
1015
  const typeText = d.configDeviceType || d.type ? `Type: ${d.configDeviceType || d.type}` : ''
999
1016
  const connText = d.connectionPreference ? `Conn: ${d.connectionPreference}` : ''
1000
1017
  const roomText = d.room ? `Room: ${d.room}` : ''
@@ -1029,7 +1046,9 @@ export function renderDeviceList(list: any[]): void {
1029
1046
  copyBtn.style.fontSize = '11px'
1030
1047
  copyBtn.addEventListener('click', async () => {
1031
1048
  try {
1032
- await navigator.clipboard.writeText(d.id)
1049
+ if (deviceIdentifier) {
1050
+ await copyTextWithFallback(deviceIdentifier)
1051
+ }
1033
1052
  copyBtn.textContent = 'Copied'
1034
1053
  copyBtn.classList.add('success')
1035
1054
  setTimeout(() => {
@@ -1058,8 +1077,8 @@ export function renderDeviceList(list: any[]): void {
1058
1077
 
1059
1078
  buttons.appendChild(editBtn)
1060
1079
  buttons.appendChild(copyBtn)
1061
- buttons.appendChild(createConnectionTestControls(d))
1062
1080
  buttons.appendChild(deleteBtn)
1081
+ buttons.appendChild(createConnectionTestControls(d))
1063
1082
 
1064
1083
  li.appendChild(info)
1065
1084
  li.appendChild(buttons)
@@ -1,3 +1,3 @@
1
1
  <!DOCTYPE html><html class="default" lang="en" data-base="../"><head><meta charset="utf-8"/><meta http-equiv="x-ua-compatible" content="IE=edge"/><title>default | @switchbot/homebridge-switchbot</title><meta name="description" content="Documentation for @switchbot/homebridge-switchbot"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="stylesheet" href="../assets/style.css"/><link rel="stylesheet" href="../assets/highlight.css"/><script defer src="../assets/main.js"></script><script async src="../assets/icons.js" id="tsd-icons-script"></script><script async src="../assets/search.js" id="tsd-search-script"></script><script async src="../assets/navigation.js" id="tsd-nav-script"></script></head><body><script>document.documentElement.dataset.theme = localStorage.getItem("tsd-theme") || "os";document.body.style.display="none";setTimeout(() => window.app?app.showPage():document.body.style.removeProperty("display"),500)</script><header class="tsd-page-toolbar"><div class="tsd-toolbar-contents container"><a href="../index.html" class="title">@switchbot/homebridge-switchbot</a><div id="tsd-toolbar-links"></div><button id="tsd-search-trigger" class="tsd-widget" aria-label="Search"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-search"></use></svg></button><dialog id="tsd-search" aria-label="Search"><input role="combobox" id="tsd-search-input" aria-controls="tsd-search-results" aria-autocomplete="list" aria-expanded="true" autocapitalize="off" autocomplete="off" placeholder="Search the docs" maxLength="100"/><ul role="listbox" id="tsd-search-results"></ul><div id="tsd-search-status" aria-live="polite" aria-atomic="true"><div>Preparing search index...</div></div></dialog><a href="#" class="tsd-widget menu" id="tsd-toolbar-menu-trigger" data-toggle="menu" aria-label="Menu"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-menu"></use></svg></a></div></header><div class="container container-main"><div class="col-content"><div class="tsd-page-title"><ul class="tsd-breadcrumb" aria-label="Breadcrumb"><li><a href="" aria-current="page">default</a></li></ul><h1>Variable default</h1></div><div class="tsd-signature"><span class="tsd-kind-variable">default</span><span class="tsd-signature-symbol">:</span> <span class="tsd-signature-symbol">(</span><span class="tsd-kind-parameter">api</span><span class="tsd-signature-symbol">:</span> <span class="tsd-signature-type">API</span><span class="tsd-signature-symbol">)</span> <span class="tsd-signature-symbol">=&gt;</span> <span class="tsd-signature-type">void</span></div><div class="tsd-comment tsd-typography"><p>Registers the SwitchBot platform with Homebridge.</p>
2
2
  </div><div class="tsd-type-declaration"><h4>Type Declaration</h4><ul class="tsd-parameters"><li class="tsd-parameter-signature"><ul class="tsd-signatures"><li class="tsd-signature" id="__type"><span class="tsd-signature-symbol">(</span><span class="tsd-kind-parameter">api</span><span class="tsd-signature-symbol">:</span> <span class="tsd-signature-type">API</span><span class="tsd-signature-symbol">)</span><span class="tsd-signature-symbol">:</span> <span class="tsd-signature-type">void</span></li><li class="tsd-description"><div class="tsd-parameters"><h4 class="tsd-parameters-title">Parameters</h4><ul class="tsd-parameter-list"><li><span><span class="tsd-kind-parameter">api</span>: <span class="tsd-signature-type">API</span></span><div class="tsd-comment tsd-typography"><p>The Homebridge API instance.</p>
3
- </div></li></ul></div><h4 class="tsd-returns-title">Returns <span class="tsd-signature-type">void</span></h4></li></ul></li></ul></div><aside class="tsd-sources"><ul><li>Defined in <a href="https://github.com/OpenWonderLabs/homebridge-switchbot/blob/d950b8418492383290fb3f20b573b22faa39f964/src/index.ts#L22">index.ts:22</a></li></ul></aside></div><div class="col-sidebar"><div class="page-menu"><div class="tsd-navigation settings"><details class="tsd-accordion"><summary class="tsd-accordion-summary"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg><h3>Settings</h3></summary><div class="tsd-accordion-details"><div class="tsd-filter-visibility"><span class="settings-label">Member Visibility</span><ul id="tsd-filter-options"><li class="tsd-filter-item"><label class="tsd-filter-input"><input type="checkbox" id="tsd-filter-inherited" name="inherited" checked/><svg width="32" height="32" viewBox="0 0 32 32" aria-hidden="true"><rect class="tsd-checkbox-background" width="30" height="30" x="1" y="1" rx="6" fill="none"></rect><path class="tsd-checkbox-checkmark" d="M8.35422 16.8214L13.2143 21.75L24.6458 10.25" stroke="none" stroke-width="3.5" stroke-linejoin="round" fill="none"></path></svg><span>Inherited</span></label></li></ul></div><div class="tsd-theme-toggle"><label class="settings-label" for="tsd-theme">Theme</label><select id="tsd-theme"><option value="os">OS</option><option value="light">Light</option><option value="dark">Dark</option></select></div></div></details></div></div><div class="site-menu"><nav class="tsd-navigation"><a href="../modules.html">@switchbot/homebridge-switchbot</a><ul class="tsd-small-nested-navigation" id="tsd-nav-container"><li>Loading...</li></ul></nav></div></div></div><footer></footer><div class="overlay"></div></body></html>
3
+ </div></li></ul></div><h4 class="tsd-returns-title">Returns <span class="tsd-signature-type">void</span></h4></li></ul></li></ul></div><aside class="tsd-sources"><ul><li>Defined in <a href="https://github.com/OpenWonderLabs/homebridge-switchbot/blob/beddb5beda694522535549f5627e37285d89cc52/src/index.ts#L22">index.ts:22</a></li></ul></aside></div><div class="col-sidebar"><div class="page-menu"><div class="tsd-navigation settings"><details class="tsd-accordion"><summary class="tsd-accordion-summary"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg><h3>Settings</h3></summary><div class="tsd-accordion-details"><div class="tsd-filter-visibility"><span class="settings-label">Member Visibility</span><ul id="tsd-filter-options"><li class="tsd-filter-item"><label class="tsd-filter-input"><input type="checkbox" id="tsd-filter-inherited" name="inherited" checked/><svg width="32" height="32" viewBox="0 0 32 32" aria-hidden="true"><rect class="tsd-checkbox-background" width="30" height="30" x="1" y="1" rx="6" fill="none"></rect><path class="tsd-checkbox-checkmark" d="M8.35422 16.8214L13.2143 21.75L24.6458 10.25" stroke="none" stroke-width="3.5" stroke-linejoin="round" fill="none"></path></svg><span>Inherited</span></label></li></ul></div><div class="tsd-theme-toggle"><label class="settings-label" for="tsd-theme">Theme</label><select id="tsd-theme"><option value="os">OS</option><option value="light">Light</option><option value="dark">Dark</option></select></div></div></details></div></div><div class="site-menu"><nav class="tsd-navigation"><a href="../modules.html">@switchbot/homebridge-switchbot</a><ul class="tsd-small-nested-navigation" id="tsd-nav-container"><li>Loading...</li></ul></nav></div></div></div><footer></footer><div class="overlay"></div></body></html>
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@switchbot/homebridge-switchbot",
3
3
  "displayName": "SwitchBot",
4
4
  "type": "module",
5
- "version": "5.0.1",
5
+ "version": "5.0.2",
6
6
  "description": "The SwitchBot plugin allows you to access your SwitchBot device(s) from HomeKit.",
7
7
  "author": "SwitchBot <support@wondertechlabs.com> (https://github.com/SwitchBot)",
8
8
  "contributors": [
@@ -81,10 +81,6 @@
81
81
  <button id="discoverBtn" onclick="discoverDevices()" style="margin-bottom: 8px">
82
82
  🔍 Discover
83
83
  </button>
84
- <button id="cancelDiscoverBtn"
85
- style="margin-left: 6px; margin-bottom: 8px; display: none; padding: 6px 10px; font-size: 11px" class="secondary">
86
- Cancel
87
- </button>
88
84
  <div id="discoverStatus" style="margin-bottom: 6px; font-size: 11px"></div>
89
85
  <div id="discoverDevicesFound" style="margin-bottom: 6px; font-size: 11px; display: none"></div>
90
86
  <div id="discoverPhaseProgress" class="discover-phase-progress" style="display: none">
@@ -394,7 +394,6 @@ function setDiscoveryGroupExpanded(groupKey: string, expanded: boolean): void {
394
394
 
395
395
  export async function discoverDevices(): Promise<void> {
396
396
  const btn = document.getElementById('discoverBtn') as HTMLButtonElement
397
- const cancelBtn = document.getElementById('cancelDiscoverBtn') as HTMLButtonElement | null
398
397
  const status = document.getElementById('discoverStatus')
399
398
  const phaseProgress = document.getElementById('discoverPhaseProgress') as HTMLElement | null
400
399
  const phaseFill = document.getElementById('discoverPhaseFill') as HTMLElement | null
@@ -424,7 +423,6 @@ export async function discoverDevices(): Promise<void> {
424
423
  const startedAt = Date.now()
425
424
  let phaseStartedAt = startedAt
426
425
  let phase = 'Preparing discovery...'
427
- let cancelled = false
428
426
 
429
427
  // --- Real-time RSSI polling additions ---
430
428
  // (bleScanDurationSeconds is now only used in bleSettings below)
@@ -936,32 +934,8 @@ export async function discoverDevices(): Promise<void> {
936
934
  devicesFoundDisplay.classList.remove('discovery-scanning-pulse')
937
935
  }
938
936
 
939
- if (cancelBtn) {
940
- cancelBtn.style.display = 'inline-block'
941
- cancelBtn.disabled = false
942
- cancelBtn.onclick = () => {
943
- cancelled = true
944
- const totalSeconds = Math.max(0, Math.floor((Date.now() - startedAt) / 1000))
945
- status.textContent = `Discovery cancelled (${totalSeconds}s total)`
946
- if (phaseProgress) {
947
- phaseProgress.style.display = 'none'
948
- }
949
- if (devicesFoundDisplay) {
950
- devicesFoundDisplay.style.display = 'none'
951
- devicesFoundDisplay.classList.remove('discovery-scanning-pulse')
952
- }
953
- cancelBtn.style.display = 'none'
954
- btn.disabled = false
955
- btn.textContent = '🔍 Discover Devices'
956
- hideBusyUi()
957
- }
958
- }
959
-
960
937
  if (bleSettings.bleEnabled) {
961
938
  const bleDevicesRaw = await apiDiscoverDevices('ble', bleSettings)
962
- if (cancelled) {
963
- return
964
- }
965
939
 
966
940
  discoveredDevices = dedupeById(bleDevicesRaw)
967
941
  uiLog.info('BLE discover response:', bleDevicesRaw)
@@ -988,9 +962,6 @@ export async function discoverDevices(): Promise<void> {
988
962
 
989
963
  try {
990
964
  const openApiDevicesRaw = await apiDiscoverDevices('openapi')
991
- if (cancelled) {
992
- return
993
- }
994
965
 
995
966
  uiLog.info('OpenAPI discover response:', openApiDevicesRaw)
996
967
  discoveredDevices = mergeDiscoveredDevices(discoveredDevices, openApiDevicesRaw)
@@ -1096,9 +1067,6 @@ export async function discoverDevices(): Promise<void> {
1096
1067
  setDiscoveryCache(discoveredDevices)
1097
1068
  updateLastScannedStatus()
1098
1069
  } catch (e) {
1099
- if (cancelled) {
1100
- return
1101
- }
1102
1070
  uiLog.error('Discovery error:', e)
1103
1071
  status.textContent = `Error: ${e instanceof Error ? e.message : 'Discovery failed'}`
1104
1072
  status.classList.add('error')
@@ -1123,11 +1091,6 @@ export async function discoverDevices(): Promise<void> {
1123
1091
  }
1124
1092
  btn.disabled = false
1125
1093
  btn.textContent = '🔍 Discover Devices'
1126
- if (cancelBtn) {
1127
- cancelBtn.disabled = false
1128
- cancelBtn.style.display = 'none'
1129
- cancelBtn.onclick = null
1130
- }
1131
1094
  }
1132
1095
  }
1133
1096
 
@@ -5,6 +5,22 @@ const SPACES_REGEX = /\s/g
5
5
  const CAMELCASE_REGEX = /([A-Z])/g
6
6
  const FIRST_CHAR_REGEX = /^./
7
7
 
8
+ async function copyTextWithFallback(text: string): Promise<void> {
9
+ try {
10
+ await navigator.clipboard.writeText(text)
11
+ } catch {
12
+ const textarea = document.createElement('textarea')
13
+ textarea.value = text
14
+ textarea.setAttribute('readonly', '')
15
+ textarea.style.cssText = 'position:fixed;left:-9999px;opacity:0;pointer-events:none'
16
+ document.body.appendChild(textarea)
17
+ textarea.select()
18
+ textarea.setSelectionRange(0, textarea.value.length)
19
+ document.execCommand('copy')
20
+ textarea.remove()
21
+ }
22
+ }
23
+
8
24
  /**
9
25
  * Get RSSI signal quality level and color based on dBm value
10
26
  * @param rssi Signal strength in dBm (typically -30 to -90)
@@ -994,7 +1010,8 @@ export function renderDeviceList(list: any[]): void {
994
1010
  meta.style.fontSize = '10px'
995
1011
  meta.style.fontFamily = 'monospace'
996
1012
 
997
- const id = `ID: ${d.deviceId || d.id}`
1013
+ const deviceIdentifier = d.deviceId || d.id
1014
+ const id = `ID: ${deviceIdentifier}`
998
1015
  const typeText = d.configDeviceType || d.type ? `Type: ${d.configDeviceType || d.type}` : ''
999
1016
  const connText = d.connectionPreference ? `Conn: ${d.connectionPreference}` : ''
1000
1017
  const roomText = d.room ? `Room: ${d.room}` : ''
@@ -1029,7 +1046,9 @@ export function renderDeviceList(list: any[]): void {
1029
1046
  copyBtn.style.fontSize = '11px'
1030
1047
  copyBtn.addEventListener('click', async () => {
1031
1048
  try {
1032
- await navigator.clipboard.writeText(d.id)
1049
+ if (deviceIdentifier) {
1050
+ await copyTextWithFallback(deviceIdentifier)
1051
+ }
1033
1052
  copyBtn.textContent = 'Copied'
1034
1053
  copyBtn.classList.add('success')
1035
1054
  setTimeout(() => {
@@ -1058,8 +1077,8 @@ export function renderDeviceList(list: any[]): void {
1058
1077
 
1059
1078
  buttons.appendChild(editBtn)
1060
1079
  buttons.appendChild(copyBtn)
1061
- buttons.appendChild(createConnectionTestControls(d))
1062
1080
  buttons.appendChild(deleteBtn)
1081
+ buttons.appendChild(createConnectionTestControls(d))
1063
1082
 
1064
1083
  li.appendChild(info)
1065
1084
  li.appendChild(buttons)