@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.
Files changed (48) hide show
  1. package/.github/workflows/release.yml +64 -15
  2. package/dist/SwitchBotMatterPlatform.d.ts +1 -0
  3. package/dist/SwitchBotMatterPlatform.d.ts.map +1 -1
  4. package/dist/SwitchBotMatterPlatform.js +23 -0
  5. package/dist/SwitchBotMatterPlatform.js.map +1 -1
  6. package/dist/homebridge-ui/endpoints/config.d.ts.map +1 -1
  7. package/dist/homebridge-ui/endpoints/config.js +9 -2
  8. package/dist/homebridge-ui/endpoints/config.js.map +1 -1
  9. package/dist/homebridge-ui/homebridge-ui/endpoints/config.js +9 -2
  10. package/dist/homebridge-ui/homebridge-ui/endpoints/config.js.map +1 -1
  11. package/dist/homebridge-ui/homebridge-ui/utils/config-parser.js +8 -0
  12. package/dist/homebridge-ui/homebridge-ui/utils/config-parser.js.map +1 -1
  13. package/dist/homebridge-ui/homebridge-ui/utils/v4-detection.js +23 -0
  14. package/dist/homebridge-ui/homebridge-ui/utils/v4-detection.js.map +1 -0
  15. package/dist/homebridge-ui/public/css/styles.css +61 -1
  16. package/dist/homebridge-ui/public/index.html +25 -2
  17. package/dist/homebridge-ui/public/js/api.d.ts +10 -0
  18. package/dist/homebridge-ui/public/js/api.d.ts.map +1 -1
  19. package/dist/homebridge-ui/public/js/api.js +30 -0
  20. package/dist/homebridge-ui/public/js/api.js.map +1 -1
  21. package/dist/homebridge-ui/public/js/api.ts +30 -0
  22. package/dist/homebridge-ui/public/js/app.js +159 -109
  23. package/dist/homebridge-ui/public/js/app.js.map +4 -4
  24. package/dist/homebridge-ui/public/js/app.ts +22 -4
  25. package/dist/homebridge-ui/utils/config-parser.d.ts +8 -0
  26. package/dist/homebridge-ui/utils/config-parser.d.ts.map +1 -1
  27. package/dist/homebridge-ui/utils/config-parser.js +8 -0
  28. package/dist/homebridge-ui/utils/config-parser.js.map +1 -1
  29. package/dist/homebridge-ui/utils/v4-detection.d.ts +12 -0
  30. package/dist/homebridge-ui/utils/v4-detection.d.ts.map +1 -0
  31. package/dist/homebridge-ui/utils/v4-detection.js +23 -0
  32. package/dist/homebridge-ui/utils/v4-detection.js.map +1 -0
  33. package/docs/assets/icons.js +1 -1
  34. package/docs/assets/icons.svg +1 -1
  35. package/docs/assets/main.js +3 -3
  36. package/docs/variables/default.html +1 -1
  37. package/package.json +6 -6
  38. package/scripts/build-ui.js +30 -0
  39. package/src/SwitchBotMatterPlatform.ts +26 -0
  40. package/src/homebridge-ui/endpoints/config.ts +10 -2
  41. package/src/homebridge-ui/public/css/styles.css +61 -1
  42. package/src/homebridge-ui/public/index.html +23 -0
  43. package/src/homebridge-ui/public/js/api.ts +30 -0
  44. package/src/homebridge-ui/public/js/app.ts +22 -4
  45. package/src/homebridge-ui/utils/config-parser.ts +9 -0
  46. package/src/homebridge-ui/utils/v4-detection.ts +23 -0
  47. package/test/homebridge-ui/config-parser.spec.ts +61 -0
  48. 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
- (window as any).loadCredentialStatus = loadCredentialStatus;
6
- (window as any).saveCredentials = saveCredentials;
7
- (window as any).discoverDevices = discoverDevices
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
  })