@switchbot/homebridge-switchbot 5.1.0-beta.0 → 5.1.0-beta.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.
- package/.github/workflows/release.yml +64 -15
- package/dist/SwitchBotMatterPlatform.d.ts +1 -0
- package/dist/SwitchBotMatterPlatform.d.ts.map +1 -1
- package/dist/SwitchBotMatterPlatform.js +23 -0
- package/dist/SwitchBotMatterPlatform.js.map +1 -1
- package/dist/homebridge-ui/endpoints/config.d.ts.map +1 -1
- package/dist/homebridge-ui/endpoints/config.js +9 -2
- package/dist/homebridge-ui/endpoints/config.js.map +1 -1
- package/dist/homebridge-ui/homebridge-ui/endpoints/config.js +9 -2
- package/dist/homebridge-ui/homebridge-ui/endpoints/config.js.map +1 -1
- package/dist/homebridge-ui/homebridge-ui/utils/config-parser.js +8 -0
- package/dist/homebridge-ui/homebridge-ui/utils/config-parser.js.map +1 -1
- package/dist/homebridge-ui/homebridge-ui/utils/v4-detection.js +23 -0
- package/dist/homebridge-ui/homebridge-ui/utils/v4-detection.js.map +1 -0
- package/dist/homebridge-ui/public/css/styles.css +61 -1
- package/dist/homebridge-ui/public/index.html +25 -2
- package/dist/homebridge-ui/public/js/api.d.ts +10 -0
- package/dist/homebridge-ui/public/js/api.d.ts.map +1 -1
- package/dist/homebridge-ui/public/js/api.js +30 -0
- package/dist/homebridge-ui/public/js/api.js.map +1 -1
- package/dist/homebridge-ui/public/js/api.ts +30 -0
- package/dist/homebridge-ui/public/js/app.js +159 -109
- package/dist/homebridge-ui/public/js/app.js.map +4 -4
- package/dist/homebridge-ui/public/js/app.ts +22 -4
- package/dist/homebridge-ui/utils/config-parser.d.ts +8 -0
- package/dist/homebridge-ui/utils/config-parser.d.ts.map +1 -1
- package/dist/homebridge-ui/utils/config-parser.js +8 -0
- package/dist/homebridge-ui/utils/config-parser.js.map +1 -1
- package/dist/homebridge-ui/utils/v4-detection.d.ts +12 -0
- package/dist/homebridge-ui/utils/v4-detection.d.ts.map +1 -0
- package/dist/homebridge-ui/utils/v4-detection.js +23 -0
- package/dist/homebridge-ui/utils/v4-detection.js.map +1 -0
- package/docs/assets/icons.js +1 -1
- package/docs/assets/icons.svg +1 -1
- package/docs/assets/main.js +3 -3
- package/docs/variables/default.html +1 -1
- package/package.json +6 -6
- package/scripts/build-ui.js +30 -0
- package/src/SwitchBotMatterPlatform.ts +26 -0
- package/src/homebridge-ui/endpoints/config.ts +10 -2
- package/src/homebridge-ui/public/css/styles.css +61 -1
- package/src/homebridge-ui/public/index.html +23 -0
- package/src/homebridge-ui/public/js/api.ts +30 -0
- package/src/homebridge-ui/public/js/app.ts +22 -4
- package/src/homebridge-ui/utils/config-parser.ts +9 -0
- package/src/homebridge-ui/utils/v4-detection.ts +23 -0
- package/test/homebridge-ui/config-parser.spec.ts +61 -0
- package/test/platform/matter-integration.spec.ts +50 -0
|
@@ -1,13 +1,31 @@
|
|
|
1
|
+
import { detectV4Config } from './api.js'
|
|
1
2
|
import { loadCredentialStatus, saveCredentials } from './credentials.js'
|
|
2
3
|
import { initRemoveAllButton, loadConfiguredDevices } from './devices.js'
|
|
3
|
-
import { discoverDevices, initializeDiscoverySettings } from './discovery.js'
|
|
4
|
+
import { discoverDevices, initializeDiscoverySettings } from './discovery.js'
|
|
4
5
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
(window as any).
|
|
6
|
+
;
|
|
7
|
+
|
|
8
|
+
(window as any).loadCredentialStatus = loadCredentialStatus
|
|
9
|
+
;(window as any).saveCredentials = saveCredentials
|
|
10
|
+
;(window as any).discoverDevices = discoverDevices
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Show or hide the v4 config incompatibility warning banner.
|
|
14
|
+
*/
|
|
15
|
+
async function checkV4Config(): Promise<void> {
|
|
16
|
+
const banner = document.getElementById('v4ConfigWarning')
|
|
17
|
+
if (!banner) {
|
|
18
|
+
return
|
|
19
|
+
}
|
|
20
|
+
const isV4 = await detectV4Config()
|
|
21
|
+
if (isV4) {
|
|
22
|
+
banner.style.display = 'block'
|
|
23
|
+
}
|
|
24
|
+
}
|
|
8
25
|
|
|
9
26
|
// Initialize on page load
|
|
10
27
|
async function init(): Promise<void> {
|
|
28
|
+
await checkV4Config()
|
|
11
29
|
await loadCredentialStatus()
|
|
12
30
|
await initializeDiscoverySettings()
|
|
13
31
|
await loadConfiguredDevices()
|
|
@@ -57,6 +57,15 @@ export function getAllDevices(platform: any): any[] {
|
|
|
57
57
|
})
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
+
/**
|
|
61
|
+
* Detect whether a platform config block is in the legacy v4 format.
|
|
62
|
+
*
|
|
63
|
+
* v4 configs stored credentials under a `credentials` sub-object and devices
|
|
64
|
+
* under `options.devices`. v5+ flattened these to `openApiToken`,
|
|
65
|
+
* `openApiSecret`, and a root-level `devices` array.
|
|
66
|
+
*/
|
|
67
|
+
export { isV4Config } from './v4-detection.js'
|
|
68
|
+
|
|
60
69
|
/**
|
|
61
70
|
* Get credential from platform config
|
|
62
71
|
*/
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Detect whether a platform config block is in the legacy v4 format.
|
|
3
|
+
*
|
|
4
|
+
* v4 configs stored credentials under a `credentials` sub-object and devices
|
|
5
|
+
* under `options.devices`. v5+ flattened these to `openApiToken`,
|
|
6
|
+
* `openApiSecret`, and a root-level `devices` array.
|
|
7
|
+
*
|
|
8
|
+
* This helper has no Node.js-specific dependencies so it can be imported from
|
|
9
|
+
* both the Homebridge server-side code and the browser-side plugin UI bundle.
|
|
10
|
+
*/
|
|
11
|
+
export function isV4Config(platform: any): boolean {
|
|
12
|
+
if (!platform || typeof platform !== 'object') {
|
|
13
|
+
return false
|
|
14
|
+
}
|
|
15
|
+
// Presence of a `credentials` sub-object is the clearest v4 signal
|
|
16
|
+
const hasCredentials = platform.credentials !== undefined && typeof platform.credentials === 'object'
|
|
17
|
+
// Presence of `options.devices` is the other v4 signal
|
|
18
|
+
const hasOptionsDevices
|
|
19
|
+
= platform.options !== undefined
|
|
20
|
+
&& typeof platform.options === 'object'
|
|
21
|
+
&& Array.isArray(platform.options.devices)
|
|
22
|
+
return hasCredentials || hasOptionsDevices
|
|
23
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
|
|
3
|
+
import { isV4Config } from '../../src/homebridge-ui/utils/v4-detection.js'
|
|
4
|
+
|
|
5
|
+
describe('isV4Config', () => {
|
|
6
|
+
it('returns false for null/undefined input', () => {
|
|
7
|
+
expect(isV4Config(null)).toBe(false)
|
|
8
|
+
expect(isV4Config(undefined)).toBe(false)
|
|
9
|
+
})
|
|
10
|
+
|
|
11
|
+
it('returns false for an empty v5 config block', () => {
|
|
12
|
+
expect(isV4Config({ platform: 'SwitchBot', devices: [] })).toBe(false)
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
it('returns false for a v5 config with credentials omitted', () => {
|
|
16
|
+
expect(isV4Config({ platform: 'SwitchBot', openApiToken: 'tok', openApiSecret: 'sec', devices: [] })).toBe(false)
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
it('returns true when a credentials sub-object is present (v4 token location)', () => {
|
|
20
|
+
const v4 = {
|
|
21
|
+
platform: 'SwitchBot',
|
|
22
|
+
credentials: { token: 'tok', secret: 'sec', notice: 'Keep your token a secret!' },
|
|
23
|
+
options: { devices: [] },
|
|
24
|
+
}
|
|
25
|
+
expect(isV4Config(v4)).toBe(true)
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
it('returns true when options.devices array is present without credentials (partial v4)', () => {
|
|
29
|
+
const partialV4 = {
|
|
30
|
+
platform: 'SwitchBot',
|
|
31
|
+
options: {
|
|
32
|
+
devices: [{ deviceId: 'aabbcc', configDeviceName: 'Curtain', configDeviceType: 'Curtain', connectionType: 'BLE' }],
|
|
33
|
+
},
|
|
34
|
+
}
|
|
35
|
+
expect(isV4Config(partialV4)).toBe(true)
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
it('returns true for the exact config from the bug report', () => {
|
|
39
|
+
const bugReportConfig = {
|
|
40
|
+
name: 'SwitchBot',
|
|
41
|
+
credentials: { notice: 'Keep your token a secret!' },
|
|
42
|
+
options: {
|
|
43
|
+
devices: [
|
|
44
|
+
{
|
|
45
|
+
deviceId: 'xxxxxxxx',
|
|
46
|
+
configDeviceName: 'Curtain',
|
|
47
|
+
configDeviceType: 'Curtain',
|
|
48
|
+
connectionType: 'BLE',
|
|
49
|
+
hide_lightsensor: false,
|
|
50
|
+
set_min: 96,
|
|
51
|
+
set_max: 12,
|
|
52
|
+
maxRetry: 0,
|
|
53
|
+
logging: 'standard',
|
|
54
|
+
},
|
|
55
|
+
],
|
|
56
|
+
},
|
|
57
|
+
platform: 'SwitchBot',
|
|
58
|
+
}
|
|
59
|
+
expect(isV4Config(bugReportConfig)).toBe(true)
|
|
60
|
+
})
|
|
61
|
+
})
|
|
@@ -30,4 +30,54 @@ describe('matter integration platform', () => {
|
|
|
30
30
|
const platform = new TestMatterPlatform(logger, config, api)
|
|
31
31
|
expect(platform.getDevices()).toEqual([])
|
|
32
32
|
})
|
|
33
|
+
|
|
34
|
+
it('should publish per-device external Matter accessories via the Homebridge external event', async () => {
|
|
35
|
+
const pendingExternalRegistrations = new Map()
|
|
36
|
+
const apiWithMatter: any = {
|
|
37
|
+
matter: {
|
|
38
|
+
uuid: { generate: (value: string) => `m-${value}` },
|
|
39
|
+
registerPlatformAccessories: vi.fn(async () => {}),
|
|
40
|
+
},
|
|
41
|
+
hap: { uuid: { generate: (value: string) => `h-${value}` } },
|
|
42
|
+
isMatterAvailable: () => true,
|
|
43
|
+
isMatterEnabled: () => true,
|
|
44
|
+
on: vi.fn(),
|
|
45
|
+
emit: null as any,
|
|
46
|
+
_pendingExternalRegistrations: pendingExternalRegistrations,
|
|
47
|
+
}
|
|
48
|
+
const emit = vi.fn((event: string, accessories: any[], registrationId: string) => {
|
|
49
|
+
if (event === 'publishExternalMatterAccessories') {
|
|
50
|
+
const resolve = pendingExternalRegistrations.get(registrationId)
|
|
51
|
+
resolve?.()
|
|
52
|
+
}
|
|
53
|
+
return true
|
|
54
|
+
})
|
|
55
|
+
apiWithMatter.emit = emit
|
|
56
|
+
const platform = new TestMatterPlatform(logger, config, apiWithMatter)
|
|
57
|
+
const created = {
|
|
58
|
+
createAccessory: vi.fn(async () => ({
|
|
59
|
+
name: 'Curtain3',
|
|
60
|
+
manufacturer: 'SwitchBot',
|
|
61
|
+
model: 'Curtain3',
|
|
62
|
+
serialNumber: 'serial-1',
|
|
63
|
+
deviceType: { deviceType: 514 },
|
|
64
|
+
clusters: { windowCovering: { currentPositionLiftPercent100ths: 0 } },
|
|
65
|
+
handlers: {},
|
|
66
|
+
})),
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
await platform.registerMatterAccessories([
|
|
70
|
+
{
|
|
71
|
+
created,
|
|
72
|
+
d: { id: 'device-1', name: 'Curtain3' },
|
|
73
|
+
type: 'curtain',
|
|
74
|
+
useMatter: true,
|
|
75
|
+
matterAvailable: true,
|
|
76
|
+
externalPublishProtocol: 'matter',
|
|
77
|
+
},
|
|
78
|
+
])
|
|
79
|
+
|
|
80
|
+
expect(emit).toHaveBeenCalledWith('publishExternalMatterAccessories', expect.any(Array), expect.any(String))
|
|
81
|
+
expect(apiWithMatter.matter.registerPlatformAccessories).not.toHaveBeenCalled()
|
|
82
|
+
})
|
|
33
83
|
})
|