@switchbot/homebridge-switchbot 5.1.0-beta.1 → 5.1.0-beta.10
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/.github/copilot-instructions.md +63 -0
- package/.github/scripts/normalize-changelog-header.cjs +86 -0
- package/.github/scripts/npm-version-script-esm-auto.js +201 -0
- package/.github/scripts/resolve-version-bump-esm.js +118 -0
- package/.github/workflows/release.yml +62 -13
- package/CHANGELOG.md +18 -0
- package/dist/{SwitchBotHAPPlatform.d.ts → Platform.HAP.d.ts} +2 -2
- package/dist/Platform.HAP.d.ts.map +1 -0
- package/dist/{SwitchBotHAPPlatform.js → Platform.HAP.js} +4 -2
- package/dist/Platform.HAP.js.map +1 -0
- package/dist/{SwitchBotMatterPlatform.d.ts → Platform.Matter.d.ts} +2 -2
- package/dist/Platform.Matter.d.ts.map +1 -0
- package/dist/{SwitchBotMatterPlatform.js → Platform.Matter.js} +1 -1
- package/dist/Platform.Matter.js.map +1 -0
- package/dist/homebridge-ui/public/css/styles.css +1 -1
- package/dist/homebridge-ui/public/index.html +38 -6
- package/dist/homebridge-ui/public/js/advanced-settings.d.ts.map +1 -1
- package/dist/homebridge-ui/public/js/advanced-settings.js +4 -3
- package/dist/homebridge-ui/public/js/advanced-settings.js.map +1 -1
- package/dist/homebridge-ui/public/js/advanced-settings.ts +4 -3
- package/dist/homebridge-ui/public/js/api.d.ts +1 -0
- package/dist/homebridge-ui/public/js/api.d.ts.map +1 -1
- package/dist/homebridge-ui/public/js/api.js +14 -1
- package/dist/homebridge-ui/public/js/api.js.map +1 -1
- package/dist/homebridge-ui/public/js/api.ts +16 -1
- package/dist/homebridge-ui/public/js/app.js +85 -76
- package/dist/homebridge-ui/public/js/app.js.map +2 -2
- package/dist/homebridge-ui/public/js/app.ts +25 -0
- package/dist/homebridge-ui/public/js/credentials.d.ts.map +1 -1
- package/dist/homebridge-ui/public/js/credentials.js +11 -5
- package/dist/homebridge-ui/public/js/credentials.js.map +1 -1
- package/dist/homebridge-ui/public/js/credentials.ts +11 -5
- package/dist/homebridge-ui/public/js/discovery.d.ts.map +1 -1
- package/dist/homebridge-ui/public/js/discovery.js +0 -36
- package/dist/homebridge-ui/public/js/discovery.js.map +1 -1
- package/dist/homebridge-ui/public/js/discovery.ts +0 -37
- package/dist/homebridge-ui/public/js/legacy-view.d.ts +3 -0
- package/dist/homebridge-ui/public/js/legacy-view.d.ts.map +1 -0
- package/dist/homebridge-ui/public/js/legacy-view.js +152 -0
- package/dist/homebridge-ui/public/js/legacy-view.js.map +1 -0
- package/dist/homebridge-ui/public/js/legacy-view.ts +107 -0
- package/dist/homebridge-ui/public/js/render.d.ts.map +1 -1
- package/dist/homebridge-ui/public/js/render.js +38 -31
- package/dist/homebridge-ui/public/js/render.js.map +1 -1
- package/dist/homebridge-ui/public/js/render.ts +39 -34
- package/dist/homebridge-ui/public/js/toast.d.ts.map +1 -1
- package/dist/homebridge-ui/public/js/toast.js +2 -3
- package/dist/homebridge-ui/public/js/toast.js.map +1 -1
- package/dist/homebridge-ui/public/js/toast.ts +2 -3
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/docs/variables/default.html +1 -1
- package/package.json +9 -15
- package/src/{SwitchBotHAPPlatform.ts → Platform.HAP.ts} +5 -2
- package/src/{SwitchBotMatterPlatform.ts → Platform.Matter.ts} +2 -1
- package/src/homebridge-ui/public/css/styles.css +1 -1
- package/src/homebridge-ui/public/index.html +36 -4
- package/src/homebridge-ui/public/js/advanced-settings.ts +4 -3
- package/src/homebridge-ui/public/js/api.ts +16 -1
- package/src/homebridge-ui/public/js/app.ts +25 -0
- package/src/homebridge-ui/public/js/credentials.ts +11 -5
- package/src/homebridge-ui/public/js/discovery.ts +0 -37
- package/src/homebridge-ui/public/js/legacy-view.js +152 -0
- package/src/homebridge-ui/public/js/legacy-view.ts +107 -0
- package/src/homebridge-ui/public/js/render.ts +39 -34
- package/src/homebridge-ui/public/js/toast.ts +2 -3
- package/src/index.ts +2 -2
- package/test/platform/accessory-restore.spec.ts +2 -2
- package/test/platform/matter-childbridge.spec.ts +1 -1
- package/test/platform/matter-integration.spec.ts +1 -1
- package/test/platform/platform-edge.spec.ts +2 -2
- package/test/platform/platform.integration.spec.ts +1 -1
- package/dist/SwitchBotHAPPlatform.d.ts.map +0 -1
- package/dist/SwitchBotHAPPlatform.js.map +0 -1
- package/dist/SwitchBotMatterPlatform.d.ts.map +0 -1
- package/dist/SwitchBotMatterPlatform.js.map +0 -1
|
@@ -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)
|
|
@@ -119,7 +135,6 @@ export function renderSignalQualityBadge(rssi: number | undefined): HTMLElement
|
|
|
119
135
|
border-radius: 3px;
|
|
120
136
|
font-size: 10px;
|
|
121
137
|
font-weight: 600;
|
|
122
|
-
margin-left: 8px;
|
|
123
138
|
`
|
|
124
139
|
badge.title = quality.description
|
|
125
140
|
|
|
@@ -927,6 +942,7 @@ export function renderDeviceList(list: any[]): void {
|
|
|
927
942
|
|
|
928
943
|
status.textContent = `Found ${list.length} device(s)`
|
|
929
944
|
ul.classList.add('device-grid')
|
|
945
|
+
ul.style.padding = '0'
|
|
930
946
|
ul.innerHTML = ''
|
|
931
947
|
|
|
932
948
|
// Show remove all button when devices exist
|
|
@@ -941,6 +957,7 @@ export function renderDeviceList(list: any[]): void {
|
|
|
941
957
|
li.style.display = 'flex'
|
|
942
958
|
li.style.flexDirection = 'column'
|
|
943
959
|
li.style.alignItems = 'stretch'
|
|
960
|
+
li.style.padding = '5px 8px'
|
|
944
961
|
li.style.marginBottom = '0'
|
|
945
962
|
|
|
946
963
|
const info = document.createElement('div')
|
|
@@ -950,14 +967,15 @@ export function renderDeviceList(list: any[]): void {
|
|
|
950
967
|
|
|
951
968
|
const nameContainer = document.createElement('div')
|
|
952
969
|
nameContainer.style.display = 'flex'
|
|
953
|
-
nameContainer.style.
|
|
954
|
-
nameContainer.style.
|
|
955
|
-
nameContainer.style.
|
|
970
|
+
nameContainer.style.alignItems = 'center'
|
|
971
|
+
nameContainer.style.marginBottom = '0'
|
|
972
|
+
nameContainer.style.flexWrap = 'wrap'
|
|
973
|
+
nameContainer.style.gap = '4px'
|
|
956
974
|
|
|
957
975
|
const name = document.createElement('div')
|
|
958
976
|
name.style.fontWeight = '500'
|
|
959
977
|
name.style.fontSize = '13px'
|
|
960
|
-
name.textContent = d.name || d.id
|
|
978
|
+
name.textContent = d.configDeviceName || d.name || d.id
|
|
961
979
|
|
|
962
980
|
const expandedDetails = document.createElement('div')
|
|
963
981
|
expandedDetails.style.display = 'none'
|
|
@@ -978,25 +996,8 @@ export function renderDeviceList(list: any[]): void {
|
|
|
978
996
|
expandBtn.style.transform = isHidden ? 'rotate(180deg)' : 'rotate(0deg)'
|
|
979
997
|
}
|
|
980
998
|
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
code.style.fontSize = '10px'
|
|
984
|
-
code.style.opacity = '0.75'
|
|
985
|
-
code.style.marginLeft = '0'
|
|
986
|
-
code.style.whiteSpace = 'normal'
|
|
987
|
-
code.style.overflowWrap = 'anywhere'
|
|
988
|
-
code.style.wordBreak = 'break-word'
|
|
989
|
-
code.style.maxWidth = '100%'
|
|
990
|
-
|
|
991
|
-
const headerRow = document.createElement('div')
|
|
992
|
-
headerRow.style.display = 'inline-flex'
|
|
993
|
-
headerRow.style.alignItems = 'center'
|
|
994
|
-
headerRow.style.gap = '4px'
|
|
995
|
-
headerRow.appendChild(name)
|
|
996
|
-
headerRow.appendChild(expandBtn)
|
|
997
|
-
|
|
998
|
-
nameContainer.appendChild(headerRow)
|
|
999
|
-
nameContainer.appendChild(code)
|
|
999
|
+
nameContainer.appendChild(name)
|
|
1000
|
+
nameContainer.appendChild(expandBtn)
|
|
1000
1001
|
|
|
1001
1002
|
// Add signal strength visualization if RSSI is available
|
|
1002
1003
|
if (d.rssi !== undefined && d.rssi !== null && d.rssi !== 0) {
|
|
@@ -1005,15 +1006,17 @@ export function renderDeviceList(list: any[]): void {
|
|
|
1005
1006
|
}
|
|
1006
1007
|
|
|
1007
1008
|
const meta = document.createElement('div')
|
|
1008
|
-
meta.style.opacity = '0.
|
|
1009
|
-
meta.style.
|
|
1010
|
-
meta.style.
|
|
1011
|
-
|
|
1012
|
-
const
|
|
1013
|
-
const
|
|
1009
|
+
meta.style.opacity = '0.7'
|
|
1010
|
+
meta.style.fontSize = '10px'
|
|
1011
|
+
meta.style.fontFamily = 'monospace'
|
|
1012
|
+
|
|
1013
|
+
const deviceIdentifier = d.deviceId || d.id
|
|
1014
|
+
const id = `ID: ${deviceIdentifier}`
|
|
1015
|
+
const typeText = d.configDeviceType || d.type ? `Type: ${d.configDeviceType || d.type}` : ''
|
|
1016
|
+
const connText = d.connectionPreference ? `Conn: ${d.connectionPreference}` : ''
|
|
1014
1017
|
const extText = d.externalPublishProtocol && d.externalPublishProtocol !== 'none' ? `external: ${d.externalPublishProtocol}` : ''
|
|
1015
|
-
const roomText = d.room ? `
|
|
1016
|
-
meta.textContent = [typeText, connText, extText, roomText].filter(Boolean).join(' | ')
|
|
1018
|
+
const roomText = d.room ? `Room: ${d.room}` : ''
|
|
1019
|
+
meta.textContent = [id, typeText, connText, extText, roomText].filter(Boolean).join(' | ')
|
|
1017
1020
|
|
|
1018
1021
|
info.appendChild(nameContainer)
|
|
1019
1022
|
info.appendChild(meta)
|
|
@@ -1044,7 +1047,9 @@ export function renderDeviceList(list: any[]): void {
|
|
|
1044
1047
|
copyBtn.style.fontSize = '11px'
|
|
1045
1048
|
copyBtn.addEventListener('click', async () => {
|
|
1046
1049
|
try {
|
|
1047
|
-
|
|
1050
|
+
if (deviceIdentifier) {
|
|
1051
|
+
await copyTextWithFallback(deviceIdentifier)
|
|
1052
|
+
}
|
|
1048
1053
|
copyBtn.textContent = 'Copied'
|
|
1049
1054
|
copyBtn.classList.add('success')
|
|
1050
1055
|
setTimeout(() => {
|
|
@@ -1073,8 +1078,8 @@ export function renderDeviceList(list: any[]): void {
|
|
|
1073
1078
|
|
|
1074
1079
|
buttons.appendChild(editBtn)
|
|
1075
1080
|
buttons.appendChild(copyBtn)
|
|
1076
|
-
buttons.appendChild(createConnectionTestControls(d))
|
|
1077
1081
|
buttons.appendChild(deleteBtn)
|
|
1082
|
+
buttons.appendChild(createConnectionTestControls(d))
|
|
1078
1083
|
|
|
1079
1084
|
li.appendChild(info)
|
|
1080
1085
|
li.appendChild(buttons)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"toast.d.ts","sourceRoot":"","sources":["../../../../src/homebridge-ui/public/js/toast.ts"],"names":[],"mappings":"AAAA,OAAO,YAAY,CAAA;
|
|
1
|
+
{"version":3,"file":"toast.d.ts","sourceRoot":"","sources":["../../../../src/homebridge-ui/public/js/toast.ts"],"names":[],"mappings":"AAAA,OAAO,YAAY,CAAA;AA4BnB,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAElE;AAED,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAEhE;AAED,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAElE;AAED,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAE/D"}
|
|
@@ -5,10 +5,9 @@ function showToast(method, message, title = 'SwitchBot') {
|
|
|
5
5
|
// Defensive: check for window and homebridge existence
|
|
6
6
|
const hb = typeof window !== 'undefined' ? window.homebridge : undefined;
|
|
7
7
|
const toast = hb && typeof hb.toast === 'object' ? hb.toast : undefined;
|
|
8
|
-
|
|
9
|
-
if (fn) {
|
|
8
|
+
if (toast && typeof toast[method] === 'function') {
|
|
10
9
|
try {
|
|
11
|
-
|
|
10
|
+
toast[method](message, title);
|
|
12
11
|
return;
|
|
13
12
|
}
|
|
14
13
|
catch (err) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"toast.js","sourceRoot":"","sources":["../../../../src/homebridge-ui/public/js/toast.ts"],"names":[],"mappings":"AAAA,OAAO,YAAY,CAAA;AACnB,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAA;AAEnC,SAAS,SAAS,CAChB,MAAgD,EAChD,OAAe,EACf,KAAK,GAAG,WAAW;IAEnB,IAAI,CAAC;QACH,uDAAuD;QACvD,MAAM,EAAE,GAAG,OAAO,MAAM,KAAK,WAAW,CAAC,CAAC,CAAE,MAAc,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,CAAA;QACjF,MAAM,KAAK,GAAG,EAAE,IAAI,OAAO,EAAE,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAA;QACvE,
|
|
1
|
+
{"version":3,"file":"toast.js","sourceRoot":"","sources":["../../../../src/homebridge-ui/public/js/toast.ts"],"names":[],"mappings":"AAAA,OAAO,YAAY,CAAA;AACnB,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAA;AAEnC,SAAS,SAAS,CAChB,MAAgD,EAChD,OAAe,EACf,KAAK,GAAG,WAAW;IAEnB,IAAI,CAAC;QACH,uDAAuD;QACvD,MAAM,EAAE,GAAG,OAAO,MAAM,KAAK,WAAW,CAAC,CAAC,CAAE,MAAc,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,CAAA;QACjF,MAAM,KAAK,GAAG,EAAE,IAAI,OAAO,EAAE,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAA;QACvE,IAAI,KAAK,IAAI,OAAO,KAAK,CAAC,MAAM,CAAC,KAAK,UAAU,EAAE,CAAC;YACjD,IAAI,CAAC;gBACH,KAAK,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,KAAK,CAAC,CAAA;gBAC7B,OAAM;YACR,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,KAAK,CAAC,IAAI,CAAC,SAAS,MAAM,SAAS,EAAE,GAAG,CAAC,CAAA;YAC3C,CAAC;QACH,CAAC;QACD,2BAA2B;QAC3B,KAAK,CAAC,IAAI,CAAC,UAAU,MAAM,KAAK,KAAK,MAAM,OAAO,EAAE,CAAC,CAAA;IACvD,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,KAAK,CAAC,IAAI,CAAC,SAAS,MAAM,eAAe,EAAE,CAAC,CAAC,CAAA;QAC7C,KAAK,CAAC,IAAI,CAAC,UAAU,MAAM,KAAK,KAAK,MAAM,OAAO,EAAE,CAAC,CAAA;IACvD,CAAC;AACH,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,OAAe,EAAE,KAAc;IAC1D,SAAS,CAAC,SAAS,EAAE,OAAO,EAAE,KAAK,CAAC,CAAA;AACtC,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,OAAe,EAAE,KAAc;IACxD,SAAS,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,CAAC,CAAA;AACpC,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,OAAe,EAAE,KAAc;IAC1D,SAAS,CAAC,SAAS,EAAE,OAAO,EAAE,KAAK,CAAC,CAAA;AACtC,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,OAAe,EAAE,KAAc;IACvD,SAAS,CAAC,MAAM,EAAE,OAAO,EAAE,KAAK,CAAC,CAAA;AACnC,CAAC"}
|
|
@@ -10,10 +10,9 @@ function showToast(
|
|
|
10
10
|
// Defensive: check for window and homebridge existence
|
|
11
11
|
const hb = typeof window !== 'undefined' ? (window as any).homebridge : undefined
|
|
12
12
|
const toast = hb && typeof hb.toast === 'object' ? hb.toast : undefined
|
|
13
|
-
|
|
14
|
-
if (fn) {
|
|
13
|
+
if (toast && typeof toast[method] === 'function') {
|
|
15
14
|
try {
|
|
16
|
-
|
|
15
|
+
toast[method](message, title)
|
|
17
16
|
return
|
|
18
17
|
} catch (err) {
|
|
19
18
|
uiLog.warn(`Toast ${method} threw:`, err)
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import { SwitchBotHAPPlatform } from './Platform.HAP.js';
|
|
2
|
+
import { SwitchBotMatterPlatform } from './Platform.Matter.js';
|
|
1
3
|
import { PLATFORM_NAME, PLUGIN_NAME } from './settings.js';
|
|
2
|
-
import { SwitchBotHAPPlatform } from './SwitchBotHAPPlatform.js';
|
|
3
|
-
import { SwitchBotMatterPlatform } from './SwitchBotMatterPlatform.js';
|
|
4
4
|
import { createPlatformProxy } from './utils.js';
|
|
5
5
|
/**
|
|
6
6
|
* Registers the SwitchBot platform with Homebridge.
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAA;AACxD,OAAO,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAA;AAC9D,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAC1D,OAAO,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAA;AAEhD;;;;;;;;;GASG;AACH,eAAe,CAAC,GAAQ,EAAQ,EAAE;IAChC,MAAM,SAAS,GAAG,mBAAmB,CAAC,oBAAoB,EAAE,uBAAuB,CAAC,CAAA;IACpF,GAAG,CAAC,gBAAgB,CAAC,WAAW,EAAE,aAAa,EAAE,SAAgB,CAAC,CAAA;AACpE,CAAC,CAAA"}
|
|
@@ -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">=></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/
|
|
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/232580f7c1a983dfd8db64d7c7cac73920a648db/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.1.0-beta.
|
|
5
|
+
"version": "5.1.0-beta.10",
|
|
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": [
|
|
@@ -32,6 +32,7 @@
|
|
|
32
32
|
},
|
|
33
33
|
"keywords": [
|
|
34
34
|
"homebridge-plugin",
|
|
35
|
+
"homebridge-matter",
|
|
35
36
|
"switchbot",
|
|
36
37
|
"lights",
|
|
37
38
|
"fan",
|
|
@@ -92,28 +93,21 @@
|
|
|
92
93
|
},
|
|
93
94
|
"dependencies": {
|
|
94
95
|
"@homebridge/plugin-ui-utils": "^2.2.3",
|
|
95
|
-
"
|
|
96
|
-
"node-switchbot": "^4.0.1"
|
|
96
|
+
"node-switchbot": "^4.0.2"
|
|
97
97
|
},
|
|
98
98
|
"devDependencies": {
|
|
99
99
|
"@antfu/eslint-config": "^8.2.0",
|
|
100
|
-
"@types/debug": "^4.1.13",
|
|
101
|
-
"@types/fs-extra": "^11.0.4",
|
|
102
100
|
"@types/node": "^25.6.0",
|
|
103
|
-
"@
|
|
104
|
-
"@types/source-map-support": "^0.5.10",
|
|
105
|
-
"@vitest/coverage-v8": "^4.1.4",
|
|
101
|
+
"@vitest/coverage-v8": "^4.1.5",
|
|
106
102
|
"esbuild": "^0.28.0",
|
|
107
|
-
"eslint": "^10.
|
|
103
|
+
"eslint": "^10.3.0",
|
|
108
104
|
"eslint-plugin-format": "^2.0.1",
|
|
109
|
-
"eslint-plugin-
|
|
110
|
-
"
|
|
111
|
-
"homebridge": "^2.0.0-beta.89",
|
|
112
|
-
"homebridge-config-ui-x": "^5.21.0",
|
|
105
|
+
"eslint-plugin-perfectionist": "^5.9.0",
|
|
106
|
+
"homebridge": "^2.0.1",
|
|
113
107
|
"nodemon": "^3.1.14",
|
|
114
108
|
"shx": "^0.4.0",
|
|
115
109
|
"typedoc": "^0.28.19",
|
|
116
|
-
"typescript": "^6.0.
|
|
117
|
-
"vitest": "^4.1.
|
|
110
|
+
"typescript": "^6.0.3",
|
|
111
|
+
"vitest": "^4.1.5"
|
|
118
112
|
}
|
|
119
113
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import type { SwitchBotPluginConfig } from './settings.js'
|
|
2
1
|
import type { API, Logger, PlatformConfig } from 'homebridge'
|
|
3
2
|
|
|
3
|
+
import type { SwitchBotPluginConfig } from './settings.js'
|
|
4
|
+
|
|
4
5
|
import { createDevice } from './deviceFactory.js'
|
|
5
6
|
import { PLATFORM_NAME, PLUGIN_NAME } from './settings.js'
|
|
6
7
|
import { SwitchBotClient } from './switchbotClient.js'
|
|
@@ -214,7 +215,9 @@ export class SwitchBotHAPPlatform {
|
|
|
214
215
|
}
|
|
215
216
|
}
|
|
216
217
|
if (!accessory) {
|
|
217
|
-
|
|
218
|
+
// Use the PlatformAccessory constructor from the HAP API
|
|
219
|
+
const PlatformAccessory = (this.api as any).hap?.PlatformAccessory || (this.api as any).platformAccessory
|
|
220
|
+
accessory = new PlatformAccessory(d.name || type, uuid)
|
|
218
221
|
try {
|
|
219
222
|
accessory.context = accessory.context || {}
|
|
220
223
|
accessory.context.deviceId = d.id
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import type { SwitchBotPluginConfig } from './settings.js'
|
|
2
1
|
import type { API, Logger, PlatformConfig } from 'homebridge'
|
|
3
2
|
|
|
3
|
+
import type { SwitchBotPluginConfig } from './settings.js'
|
|
4
|
+
|
|
4
5
|
import { createDevice } from './deviceFactory.js'
|
|
5
6
|
import { PLATFORM_NAME, PLUGIN_NAME } from './settings.js'
|
|
6
7
|
import { SwitchBotClient } from './switchbotClient.js'
|
|
@@ -9,8 +9,34 @@
|
|
|
9
9
|
</head>
|
|
10
10
|
|
|
11
11
|
<body>
|
|
12
|
+
|
|
12
13
|
<h1>🤖 SwitchBot Configuration</h1>
|
|
13
14
|
|
|
15
|
+
<!-- ...existing code... -->
|
|
16
|
+
|
|
17
|
+
<!-- Centered legacy view button at the bottom -->
|
|
18
|
+
<div class="centered-toggle-btn">
|
|
19
|
+
<button id="legacyViewBtn">Legacy View</button>
|
|
20
|
+
<button id="dynamicViewBtn" style="display: none;">Dynamic View</button>
|
|
21
|
+
</div>
|
|
22
|
+
<style>
|
|
23
|
+
.centered-toggle-btn {
|
|
24
|
+
display: flex;
|
|
25
|
+
flex-direction: row;
|
|
26
|
+
justify-content: center;
|
|
27
|
+
align-items: center;
|
|
28
|
+
margin-top: 32px;
|
|
29
|
+
margin-bottom: 12px;
|
|
30
|
+
gap: 12px;
|
|
31
|
+
}
|
|
32
|
+
#legacyViewBtn, #dynamicViewBtn {
|
|
33
|
+
font-size: 12px;
|
|
34
|
+
padding: 4px 10px;
|
|
35
|
+
}
|
|
36
|
+
</style>
|
|
37
|
+
|
|
38
|
+
<div id="dynamicUiContainer">
|
|
39
|
+
|
|
14
40
|
<!-- v4 config incompatibility warning – shown dynamically by app.js -->
|
|
15
41
|
<div id="v4ConfigWarning" class="v4-config-warning" style="display: none">
|
|
16
42
|
<strong>⚠️ Incompatible v4 Configuration Detected</strong>
|
|
@@ -104,10 +130,6 @@
|
|
|
104
130
|
<button id="discoverBtn" onclick="discoverDevices()" style="margin-bottom: 8px">
|
|
105
131
|
🔍 Discover
|
|
106
132
|
</button>
|
|
107
|
-
<button id="cancelDiscoverBtn"
|
|
108
|
-
style="margin-left: 6px; margin-bottom: 8px; display: none; padding: 6px 10px; font-size: 11px" class="secondary">
|
|
109
|
-
Cancel
|
|
110
|
-
</button>
|
|
111
133
|
<div id="discoverStatus" style="margin-bottom: 6px; font-size: 11px"></div>
|
|
112
134
|
<div id="discoverDevicesFound" style="margin-bottom: 6px; font-size: 11px; display: none"></div>
|
|
113
135
|
<div id="discoverPhaseProgress" class="discover-phase-progress" style="display: none">
|
|
@@ -231,8 +253,18 @@
|
|
|
231
253
|
</form>
|
|
232
254
|
</div>
|
|
233
255
|
|
|
256
|
+
</div> <!-- end dynamicUiContainer -->
|
|
257
|
+
|
|
258
|
+
<div id="legacyViewContainer" style="display: none;">
|
|
259
|
+
<div class="card compact">
|
|
260
|
+
<h2>Legacy Config View</h2>
|
|
261
|
+
<div id="legacyConfigForm"></div>
|
|
262
|
+
</div>
|
|
263
|
+
</div>
|
|
264
|
+
|
|
234
265
|
<script type="module" src="js/app.js"></script>
|
|
235
266
|
<script type="module" src="js/advanced-settings.js"></script>
|
|
267
|
+
<script type="module" src="js/legacy-view.js"></script>
|
|
236
268
|
</body>
|
|
237
269
|
|
|
238
270
|
</html>
|
|
@@ -46,10 +46,11 @@ async function saveAdvancedSettings(): Promise<void> {
|
|
|
46
46
|
if (!Array.isArray(pluginConfigBlocks) || !pluginConfigBlocks.length) {
|
|
47
47
|
throw new Error('No plugin config blocks returned from Homebridge')
|
|
48
48
|
}
|
|
49
|
-
// Find the SwitchBot config block
|
|
50
|
-
|
|
49
|
+
// Find or add the SwitchBot config block
|
|
50
|
+
let idx = pluginConfigBlocks.findIndex(c => (c.platform || c.name || '').toLowerCase().includes('switchbot'))
|
|
51
51
|
if (idx === -1) {
|
|
52
|
-
|
|
52
|
+
pluginConfigBlocks.push({ platform: 'SwitchBot' })
|
|
53
|
+
idx = pluginConfigBlocks.length - 1
|
|
53
54
|
}
|
|
54
55
|
const config = pluginConfigBlocks[idx]
|
|
55
56
|
// Update config values from UI
|
|
@@ -300,7 +300,22 @@ export async function addDevicesInBulk(
|
|
|
300
300
|
if (!resp || resp.success === false) {
|
|
301
301
|
throw new Error(resp?.data?.message || 'Bulk add failed')
|
|
302
302
|
}
|
|
303
|
-
return resp
|
|
303
|
+
return normalizeBulkAddDevicesResponse(resp)
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
export function normalizeBulkAddDevicesResponse(resp: any): any {
|
|
307
|
+
const payload = resp?.data && typeof resp.data === 'object' ? resp.data : resp
|
|
308
|
+
const addedCount = Number(payload?.addedCount ?? payload?.added ?? 0)
|
|
309
|
+
const skippedCount = Number(payload?.skippedCount ?? payload?.skipped ?? 0)
|
|
310
|
+
const updatedCount = Number(payload?.updatedCount ?? payload?.updated ?? 0)
|
|
311
|
+
|
|
312
|
+
return {
|
|
313
|
+
...payload,
|
|
314
|
+
success: resp?.success ?? true,
|
|
315
|
+
addedCount,
|
|
316
|
+
skippedCount,
|
|
317
|
+
updatedCount,
|
|
318
|
+
}
|
|
304
319
|
}
|
|
305
320
|
|
|
306
321
|
export async function updateDevice(
|
|
@@ -3,6 +3,29 @@ import { loadCredentialStatus, saveCredentials } from './credentials.js'
|
|
|
3
3
|
import { initRemoveAllButton, loadConfiguredDevices } from './devices.js'
|
|
4
4
|
import { discoverDevices, initializeDiscoverySettings } from './discovery.js'
|
|
5
5
|
|
|
6
|
+
// Ensure SwitchBot config block exists before any UI actions
|
|
7
|
+
async function ensureSwitchBotConfigBlock(): Promise<void> {
|
|
8
|
+
try {
|
|
9
|
+
if (typeof homebridge.getPluginConfig !== 'function' || typeof homebridge.updatePluginConfig !== 'function') {
|
|
10
|
+
return
|
|
11
|
+
}
|
|
12
|
+
let configArr = await homebridge.getPluginConfig()
|
|
13
|
+
if (!Array.isArray(configArr)) {
|
|
14
|
+
configArr = []
|
|
15
|
+
}
|
|
16
|
+
const idx = configArr.findIndex(c => (c.platform || c.name || '').toLowerCase().includes('switchbot'))
|
|
17
|
+
if (idx === -1) {
|
|
18
|
+
configArr.push({ platform: 'SwitchBot' })
|
|
19
|
+
await homebridge.updatePluginConfig(configArr)
|
|
20
|
+
if (typeof homebridge.savePluginConfig === 'function') {
|
|
21
|
+
await homebridge.savePluginConfig()
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
} catch (_e) {
|
|
25
|
+
// Ignore errors
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
6
29
|
;
|
|
7
30
|
|
|
8
31
|
(window as any).loadCredentialStatus = loadCredentialStatus
|
|
@@ -24,7 +47,9 @@ async function checkV4Config(): Promise<void> {
|
|
|
24
47
|
}
|
|
25
48
|
|
|
26
49
|
// Initialize on page load
|
|
50
|
+
|
|
27
51
|
async function init(): Promise<void> {
|
|
52
|
+
await ensureSwitchBotConfigBlock()
|
|
28
53
|
await checkV4Config()
|
|
29
54
|
await loadCredentialStatus()
|
|
30
55
|
await initializeDiscoverySettings()
|
|
@@ -63,14 +63,20 @@ export async function saveCredentials(): Promise<void> {
|
|
|
63
63
|
if (typeof homebridge.getPluginConfig !== 'function' || typeof homebridge.updatePluginConfig !== 'function') {
|
|
64
64
|
throw new TypeError('Homebridge UI API not available')
|
|
65
65
|
}
|
|
66
|
-
|
|
67
|
-
if (!Array.isArray(configArr)
|
|
68
|
-
|
|
66
|
+
let configArr = await homebridge.getPluginConfig()
|
|
67
|
+
if (!Array.isArray(configArr)) {
|
|
68
|
+
configArr = []
|
|
69
|
+
}
|
|
70
|
+
let idx = configArr.findIndex(c => (c.platform || c.name || '').toLowerCase() === 'switchbot')
|
|
71
|
+
if (idx === -1) {
|
|
72
|
+
// Add new SwitchBot platform config
|
|
73
|
+
configArr.push({ platform: 'SwitchBot' })
|
|
74
|
+
idx = configArr.length - 1
|
|
69
75
|
}
|
|
70
|
-
const config = configArr[
|
|
76
|
+
const config = configArr[idx]
|
|
71
77
|
config.openApiToken = token
|
|
72
78
|
config.openApiSecret = secret
|
|
73
|
-
await homebridge.updatePluginConfig(
|
|
79
|
+
await homebridge.updatePluginConfig(configArr)
|
|
74
80
|
if (typeof homebridge.savePluginConfig === 'function') {
|
|
75
81
|
await homebridge.savePluginConfig()
|
|
76
82
|
}
|
|
@@ -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
|
|