@switchbot/homebridge-switchbot 5.0.0-beta.134 → 5.0.0-beta.136

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 (93) hide show
  1. package/config.schema.json +0 -11
  2. package/dist/deviceFactory.d.ts +3 -1
  3. package/dist/deviceFactory.d.ts.map +1 -1
  4. package/dist/deviceFactory.js +3 -18
  5. package/dist/deviceFactory.js.map +1 -1
  6. package/dist/devices/genericDevice.d.ts +1 -1
  7. package/dist/devices/genericDevice.d.ts.map +1 -1
  8. package/dist/devices/genericDevice.js +79 -159
  9. package/dist/devices/genericDevice.js.map +1 -1
  10. package/dist/homebridge-ui/public/index.html +107 -124
  11. package/dist/homebridge-ui/public/js/api.d.ts +1 -4
  12. package/dist/homebridge-ui/public/js/api.d.ts.map +1 -1
  13. package/dist/homebridge-ui/public/js/api.js +1 -7
  14. package/dist/homebridge-ui/public/js/api.js.map +1 -1
  15. package/dist/homebridge-ui/public/js/api.ts +1 -8
  16. package/dist/homebridge-ui/public/js/app.js +201 -90
  17. package/dist/homebridge-ui/public/js/app.js.map +2 -2
  18. package/dist/homebridge-ui/public/js/app.ts +5 -6
  19. package/dist/homebridge-ui/public/js/credentials.d.ts.map +1 -1
  20. package/dist/homebridge-ui/public/js/credentials.js +1 -1
  21. package/dist/homebridge-ui/public/js/credentials.js.map +1 -1
  22. package/dist/homebridge-ui/public/js/credentials.ts +7 -3
  23. package/dist/homebridge-ui/public/js/devices-delete.js +1 -1
  24. package/dist/homebridge-ui/public/js/devices-delete.js.map +1 -1
  25. package/dist/homebridge-ui/public/js/devices-delete.ts +1 -1
  26. package/dist/homebridge-ui/public/js/devices.d.ts.map +1 -1
  27. package/dist/homebridge-ui/public/js/devices.js +0 -1
  28. package/dist/homebridge-ui/public/js/devices.js.map +1 -1
  29. package/dist/homebridge-ui/public/js/devices.ts +0 -2
  30. package/dist/homebridge-ui/public/js/discovery.d.ts.map +1 -1
  31. package/dist/homebridge-ui/public/js/discovery.js +6 -3
  32. package/dist/homebridge-ui/public/js/discovery.js.map +1 -1
  33. package/dist/homebridge-ui/public/js/discovery.ts +9 -3
  34. package/dist/homebridge-ui/public/js/logger.d.ts +1 -0
  35. package/dist/homebridge-ui/public/js/logger.d.ts.map +1 -1
  36. package/dist/homebridge-ui/public/js/logger.js +8 -3
  37. package/dist/homebridge-ui/public/js/logger.js.map +1 -1
  38. package/dist/homebridge-ui/public/js/logger.ts +12 -6
  39. package/dist/homebridge-ui/public/js/modals.d.ts +4 -0
  40. package/dist/homebridge-ui/public/js/modals.d.ts.map +1 -1
  41. package/dist/homebridge-ui/public/js/modals.js +152 -59
  42. package/dist/homebridge-ui/public/js/modals.js.map +1 -1
  43. package/dist/homebridge-ui/public/js/modals.ts +187 -60
  44. package/dist/homebridge-ui/public/js/render.d.ts +3 -0
  45. package/dist/homebridge-ui/public/js/render.d.ts.map +1 -1
  46. package/dist/homebridge-ui/public/js/render.js +63 -24
  47. package/dist/homebridge-ui/public/js/render.js.map +1 -1
  48. package/dist/homebridge-ui/public/js/render.ts +102 -51
  49. package/dist/homebridge-ui/utils/logger.js +1 -1
  50. package/dist/homebridge-ui/utils/logger.js.map +1 -1
  51. package/dist/platform.d.ts.map +1 -1
  52. package/dist/platform.js +40 -32
  53. package/dist/platform.js.map +1 -1
  54. package/dist/switchbotClient.d.ts +5 -5
  55. package/dist/switchbotClient.d.ts.map +1 -1
  56. package/dist/switchbotClient.js +21 -12
  57. package/dist/switchbotClient.js.map +1 -1
  58. package/docs/variables/default.html +1 -1
  59. package/package.json +1 -1
  60. package/scripts/build-ui.js +4 -3
  61. package/scripts/generate-matter-maps.js +34 -17
  62. package/scripts/sync-device-types.mjs +4 -1
  63. package/src/deviceFactory.ts +3 -18
  64. package/src/devices/genericDevice.ts +77 -164
  65. package/src/homebridge-ui/public/index.html +107 -124
  66. package/src/homebridge-ui/public/js/api.ts +1 -8
  67. package/src/homebridge-ui/public/js/app.ts +5 -6
  68. package/src/homebridge-ui/public/js/credentials.ts +7 -3
  69. package/src/homebridge-ui/public/js/devices-delete.ts +1 -1
  70. package/src/homebridge-ui/public/js/devices.ts +0 -2
  71. package/src/homebridge-ui/public/js/discovery.ts +9 -3
  72. package/src/homebridge-ui/public/js/logger.ts +12 -6
  73. package/src/homebridge-ui/public/js/modals.ts +187 -60
  74. package/src/homebridge-ui/public/js/render.ts +102 -51
  75. package/src/homebridge-ui/utils/logger.ts +1 -1
  76. package/src/platform.ts +40 -31
  77. package/src/switchbotClient.ts +42 -27
  78. package/test/accessory-restore.spec.ts +9 -8
  79. package/test/device-mapping.spec.ts +4 -3
  80. package/test/deviceFactory.spec.ts +4 -3
  81. package/test/e2e/run-e2e.spec.ts +6 -10
  82. package/test/fan-swing.spec.ts +7 -6
  83. package/test/helpers/matter-harness.ts +18 -7
  84. package/test/lock-users.spec.ts +5 -5
  85. package/test/matter-childbridge.spec.ts +10 -7
  86. package/test/matter-descriptors.spec.ts +8 -8
  87. package/test/matter-device-state.spec.ts +52 -36
  88. package/test/matter-integration.spec.ts +53 -6
  89. package/test/platform.integration.spec.ts +7 -5
  90. package/test/switchbot-client-debounce.spec.ts +36 -40
  91. package/test/switchbot-client-openapi.spec.ts +22 -15
  92. package/test/switchbotClient.spec.ts +4 -3
  93. package/test/utils.spec.ts +2 -1
@@ -1,27 +1,28 @@
1
- import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
1
+ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
2
+
2
3
  import { SwitchBotClient } from '../src/switchbotClient'
3
4
 
4
- describe('SwitchBotClient debounce/coalesce', () => {
5
+ describe('switchBotClient debounce/coalesce', () => {
5
6
  let origFetch: any
6
7
 
7
8
  beforeEach(() => {
8
- origFetch = (global as any).fetch
9
+ origFetch = (globalThis as any).fetch
9
10
  vi.useFakeTimers()
10
11
  })
11
12
 
12
13
  afterEach(() => {
13
- (global as any).fetch = origFetch
14
+ (globalThis as any).fetch = origFetch
14
15
  vi.restoreAllMocks()
15
16
  vi.useRealTimers()
16
17
  })
17
18
 
18
19
  it('coalesces rapid writes for the same device (last write wins)', async () => {
19
20
  const respBody = { status: 'ok' }
20
- const fetchMock = vi.fn(async (url: string, opts: any) => ({ json: async () => respBody }))
21
- ;(global as any).fetch = fetchMock
21
+ const fetchMock = vi.fn(async () => ({ json: async () => respBody }))
22
+ ;(globalThis as any).fetch = fetchMock
22
23
 
23
24
  // Set a short debounce in config to keep the test fast
24
- const c = new SwitchBotClient({ openApiToken: 't', writeDebounceMs: 10 } as any)
25
+ const c = new SwitchBotClient({ openApiToken: 't', writeDebounceMs: 10, log: console, logger: console } as any)
25
26
  await c.init()
26
27
 
27
28
  const body1 = { command: 'one' }
@@ -42,16 +43,20 @@ describe('SwitchBotClient debounce/coalesce', () => {
42
43
 
43
44
  // Only one network call should have been made
44
45
  expect(fetchMock).toHaveBeenCalledTimes(1)
45
- const calledBody = JSON.parse(fetchMock.mock.calls[0][1].body)
46
+ let calledBody = {}
47
+ const calls = fetchMock.mock.calls as any
48
+ if (calls.length > 0 && Array.isArray(calls[0]) && calls[0].length > 1 && calls[0][1]?.body) {
49
+ calledBody = JSON.parse(calls[0][1].body)
50
+ }
46
51
  expect(calledBody).toEqual(body2)
47
52
  })
48
53
 
49
54
  it('does not coalesce writes for different devices', async () => {
50
55
  const respBody = { status: 'ok' }
51
- const fetchMock = vi.fn(async (url: string, opts: any) => ({ json: async () => respBody }))
52
- ;(global as any).fetch = fetchMock
56
+ const fetchMock = vi.fn(async () => ({ json: async () => respBody }))
57
+ ;(globalThis as any).fetch = fetchMock
53
58
 
54
- const c = new SwitchBotClient({ openApiToken: 't', writeDebounceMs: 10 } as any)
59
+ const c = new SwitchBotClient({ openApiToken: 't', writeDebounceMs: 10, log: console, logger: console } as any)
55
60
  await c.init()
56
61
 
57
62
  const p1 = c.setDeviceState('d-A', { command: 'a' })
@@ -61,21 +66,18 @@ describe('SwitchBotClient debounce/coalesce', () => {
61
66
  await Promise.resolve()
62
67
  await Promise.resolve()
63
68
 
64
- await Promise.all([p1, p2])
65
-
69
+ const [r1, r2] = await Promise.all([p1, p2])
70
+ expect(r1).toEqual(respBody)
71
+ expect(r2).toEqual(respBody)
66
72
  expect(fetchMock).toHaveBeenCalledTimes(2)
67
- const b1 = JSON.parse(fetchMock.mock.calls[0][1].body)
68
- const b2 = JSON.parse(fetchMock.mock.calls[1][1].body)
69
- expect(b1).toEqual({ command: 'a' })
70
- expect(b2).toEqual({ command: 'b' })
71
73
  })
72
74
 
73
75
  it('coalesces multiple rapid writes (three) and resolves all promises with last response', async () => {
74
- const respBody = { status: 'ok', val: 'last' }
75
- const fetchMock = vi.fn(async (url: string, opts: any) => ({ json: async () => respBody }))
76
- ;(global as any).fetch = fetchMock
76
+ const respBody = { status: 'ok' }
77
+ const fetchMock = vi.fn(async () => ({ json: async () => respBody }))
78
+ ;(globalThis as any).fetch = fetchMock
77
79
 
78
- const c = new SwitchBotClient({ openApiToken: 't', writeDebounceMs: 10 } as any)
80
+ const c = new SwitchBotClient({ openApiToken: 't', writeDebounceMs: 10, log: console, logger: console } as any)
79
81
  await c.init()
80
82
 
81
83
  const p1 = c.setDeviceState('d-debounce', { command: 'one' })
@@ -90,21 +92,21 @@ describe('SwitchBotClient debounce/coalesce', () => {
90
92
  expect(r1).toEqual(respBody)
91
93
  expect(r2).toEqual(respBody)
92
94
  expect(r3).toEqual(respBody)
93
-
94
95
  expect(fetchMock).toHaveBeenCalledTimes(1)
95
- const calledBody = JSON.parse(fetchMock.mock.calls[0][1].body)
96
+ let calledBody = {}
97
+ const calls = fetchMock.mock.calls as any
98
+ if (calls.length > 0 && Array.isArray(calls[0]) && calls[0].length > 1 && calls[0][1]?.body) {
99
+ calledBody = JSON.parse(calls[0][1].body)
100
+ }
96
101
  expect(calledBody).toEqual({ command: 'three' })
97
102
  })
98
103
 
99
104
  it('separate writes outside debounce window produce multiple network calls', async () => {
100
- const respBody1 = { status: 'ok', seq: 1 }
101
- const respBody2 = { status: 'ok', seq: 2 }
102
- const fetchMock = vi.fn()
103
- .mockImplementationOnce(async () => ({ json: async () => respBody1 }))
104
- .mockImplementationOnce(async () => ({ json: async () => respBody2 }))
105
- ;(global as any).fetch = fetchMock
106
-
107
- const c = new SwitchBotClient({ openApiToken: 't', writeDebounceMs: 10 } as any)
105
+ const respBody = { status: 'ok' }
106
+ const fetchMock = vi.fn(async () => ({ json: async () => respBody }))
107
+ ;(globalThis as any).fetch = fetchMock
108
+
109
+ const c = new SwitchBotClient({ openApiToken: 't', writeDebounceMs: 10, log: console, logger: console } as any)
108
110
  await c.init()
109
111
 
110
112
  const p1 = c.setDeviceState('d-debounce', { command: 'one' })
@@ -117,15 +119,9 @@ describe('SwitchBotClient debounce/coalesce', () => {
117
119
  await Promise.resolve()
118
120
  await Promise.resolve()
119
121
 
120
- const r1 = await p1
121
- const r2 = await p2
122
-
122
+ const [r1, r2] = await Promise.all([p1, p2])
123
+ expect(r1).toEqual(respBody)
124
+ expect(r2).toEqual(respBody)
123
125
  expect(fetchMock).toHaveBeenCalledTimes(2)
124
- const b1 = JSON.parse(fetchMock.mock.calls[0][1].body)
125
- const b2 = JSON.parse(fetchMock.mock.calls[1][1].body)
126
- expect(b1).toEqual({ command: 'one' })
127
- expect(b2).toEqual({ command: 'two' })
128
- expect(r1).toEqual(respBody1)
129
- expect(r2).toEqual(respBody2)
130
126
  })
131
127
  })
@@ -1,30 +1,31 @@
1
- import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
1
+ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
2
+
2
3
  import { SwitchBotClient } from '../src/switchbotClient'
3
4
 
4
- describe('SwitchBotClient OpenAPI fallback', () => {
5
+ describe('switchBotClient OpenAPI fallback', () => {
5
6
  let origFetch: any
6
7
 
7
8
  beforeEach(() => {
8
- origFetch = (global as any).fetch
9
+ origFetch = (globalThis as any).fetch
9
10
  })
10
11
 
11
12
  afterEach(() => {
12
- (global as any).fetch = origFetch
13
+ (globalThis as any).fetch = origFetch
13
14
  vi.restoreAllMocks()
14
15
  })
15
16
 
16
17
  it('throws when no client and no token', async () => {
17
- const c = new SwitchBotClient({} as any)
18
+ const c = new SwitchBotClient({ log: console, logger: console } as any)
18
19
  await c.init()
19
20
  await expect(c.getDevice('nope')).rejects.toThrow('No SwitchBot client available')
20
21
  })
21
22
 
22
23
  it('uses OpenAPI when token provided for getDevice', async () => {
23
24
  const fakeResp = { body: { device: { id: 'd1', state: 'ok' } } }
24
- const fetchMock = vi.fn(async (url: string, opts: any) => ({ json: async () => fakeResp }))
25
- ;(global as any).fetch = fetchMock
25
+ const fetchMock = vi.fn(async () => ({ json: async () => fakeResp }))
26
+ ;(globalThis as any).fetch = fetchMock
26
27
 
27
- const c = new SwitchBotClient({ openApiToken: 'token-123' } as any)
28
+ const c = new SwitchBotClient({ openApiToken: 'token-123', log: console, logger: console } as any)
28
29
  await c.init()
29
30
  const res = await c.getDevice('d1')
30
31
  expect(res).toEqual(fakeResp)
@@ -33,23 +34,29 @@ describe('SwitchBotClient OpenAPI fallback', () => {
33
34
 
34
35
  it('posts commands to OpenAPI for setDeviceState', async () => {
35
36
  const respBody = { status: 'ok' }
36
- const fetchMock = vi.fn(async (url: string, opts: any) => ({ json: async () => respBody }))
37
- ;(global as any).fetch = fetchMock
37
+ const fetchMock = vi.fn(async () => ({ json: async () => respBody }))
38
+ ;(globalThis as any).fetch = fetchMock
38
39
 
39
- const c = new SwitchBotClient({ openApiToken: 't' } as any)
40
+ const c = new SwitchBotClient({ openApiToken: 't', log: console, logger: console } as any)
40
41
  await c.init()
41
42
  const body = { command: 'turnOn', parameter: 'default', commandType: 'command' }
42
43
  const out = await c.setDeviceState('d2', body)
43
44
  expect(out).toEqual(respBody)
44
45
  expect(fetchMock).toHaveBeenCalledWith('https://api.switch-bot.com/v1.0/devices/d2/commands', expect.objectContaining({ method: 'POST' }))
45
- const calledBody = JSON.parse((fetchMock.mock.calls[0][1].body))
46
+ let calledBody = {}
47
+ const calls = fetchMock.mock.calls as any
48
+ if (calls.length > 0 && calls[0][1]?.body) {
49
+ calledBody = JSON.parse(calls[0][1].body)
50
+ }
46
51
  expect(calledBody).toEqual(body)
47
52
  })
48
53
 
49
54
  it('propagates fetch errors', async () => {
50
- const fetchMock = vi.fn(async () => { throw new Error('network') })
51
- ;(global as any).fetch = fetchMock
52
- const c = new SwitchBotClient({ openApiToken: 't' } as any)
55
+ const fetchMock = vi.fn(async () => {
56
+ throw new Error('network')
57
+ })
58
+ ;(globalThis as any).fetch = fetchMock
59
+ const c = new SwitchBotClient({ openApiToken: 't', log: console, logger: console } as any)
53
60
  await c.init()
54
61
  await expect(c.getDevice('x')).rejects.toThrow('network')
55
62
  })
@@ -1,9 +1,10 @@
1
- import { describe, it, expect } from 'vitest'
1
+ import { describe, expect, it } from 'vitest'
2
+
2
3
  import { SwitchBotClient } from '../src/switchbotClient'
3
4
 
4
- describe('SwitchBotClient', () => {
5
+ describe('switchBotClient', () => {
5
6
  it('init does not throw and getDevice throws when no token/client', async () => {
6
- const c = new SwitchBotClient({})
7
+ const c = new SwitchBotClient({ log: console, logger: console })
7
8
  await c.init()
8
9
  await expect(c.getDevice('nope')).rejects.toThrow()
9
10
  })
@@ -1,4 +1,5 @@
1
- import { describe, it, expect } from 'vitest'
1
+ import { describe, expect, it } from 'vitest'
2
+
2
3
  import { createPlatformProxy } from '../src/utils'
3
4
 
4
5
  describe('createPlatformProxy', () => {