@switchbot/homebridge-switchbot 5.1.0-beta.8 → 5.1.0

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 (130) hide show
  1. package/.github/copilot-instructions.md +0 -63
  2. package/.github/scripts/normalize-changelog-header.cjs +1 -1
  3. package/.github/scripts/npm-version-script-esm-auto.js +1 -1
  4. package/.github/scripts/resolve-version-bump-esm.js +1 -1
  5. package/CHANGELOG.md +10 -9
  6. package/MIGRATION.md +0 -5
  7. package/README.md +0 -25
  8. package/config.schema.json +0 -11
  9. package/dist/{Platform.HAP.d.ts → SwitchBotHAPPlatform.d.ts} +16 -2
  10. package/dist/SwitchBotHAPPlatform.d.ts.map +1 -0
  11. package/dist/{Platform.HAP.js → SwitchBotHAPPlatform.js} +111 -40
  12. package/dist/SwitchBotHAPPlatform.js.map +1 -0
  13. package/dist/{Platform.Matter.d.ts → SwitchBotMatterPlatform.d.ts} +2 -4
  14. package/dist/SwitchBotMatterPlatform.d.ts.map +1 -0
  15. package/dist/{Platform.Matter.js → SwitchBotMatterPlatform.js} +6 -62
  16. package/dist/SwitchBotMatterPlatform.js.map +1 -0
  17. package/dist/deviceFactory.d.ts.map +1 -1
  18. package/dist/deviceFactory.js +5 -15
  19. package/dist/deviceFactory.js.map +1 -1
  20. package/dist/devices/genericDevice.d.ts +63 -5
  21. package/dist/devices/genericDevice.d.ts.map +1 -1
  22. package/dist/devices/genericDevice.js +251 -11
  23. package/dist/devices/genericDevice.js.map +1 -1
  24. package/dist/homebridge-ui/endpoints/config.d.ts.map +1 -1
  25. package/dist/homebridge-ui/endpoints/config.js +2 -9
  26. package/dist/homebridge-ui/endpoints/config.js.map +1 -1
  27. package/dist/homebridge-ui/homebridge-ui/endpoints/config.js +2 -9
  28. package/dist/homebridge-ui/homebridge-ui/endpoints/config.js.map +1 -1
  29. package/dist/homebridge-ui/homebridge-ui/utils/config-parser.js +0 -8
  30. package/dist/homebridge-ui/homebridge-ui/utils/config-parser.js.map +1 -1
  31. package/dist/homebridge-ui/public/css/styles.css +1 -61
  32. package/dist/homebridge-ui/public/index.html +2 -61
  33. package/dist/homebridge-ui/public/js/advanced-settings.d.ts.map +1 -1
  34. package/dist/homebridge-ui/public/js/advanced-settings.js +3 -4
  35. package/dist/homebridge-ui/public/js/advanced-settings.js.map +1 -1
  36. package/dist/homebridge-ui/public/js/advanced-settings.ts +3 -4
  37. package/dist/homebridge-ui/public/js/api.d.ts +0 -16
  38. package/dist/homebridge-ui/public/js/api.d.ts.map +1 -1
  39. package/dist/homebridge-ui/public/js/api.js +0 -48
  40. package/dist/homebridge-ui/public/js/api.js.map +1 -1
  41. package/dist/homebridge-ui/public/js/api.ts +1 -61
  42. package/dist/homebridge-ui/public/js/app.js +115 -267
  43. package/dist/homebridge-ui/public/js/app.js.map +4 -4
  44. package/dist/homebridge-ui/public/js/app.ts +4 -47
  45. package/dist/homebridge-ui/public/js/credentials.d.ts.map +1 -1
  46. package/dist/homebridge-ui/public/js/credentials.js +5 -11
  47. package/dist/homebridge-ui/public/js/credentials.js.map +1 -1
  48. package/dist/homebridge-ui/public/js/credentials.ts +5 -11
  49. package/dist/homebridge-ui/public/js/devices.d.ts.map +1 -1
  50. package/dist/homebridge-ui/public/js/devices.js +0 -6
  51. package/dist/homebridge-ui/public/js/devices.js.map +1 -1
  52. package/dist/homebridge-ui/public/js/devices.ts +0 -6
  53. package/dist/homebridge-ui/public/js/modals.d.ts +0 -1
  54. package/dist/homebridge-ui/public/js/modals.d.ts.map +1 -1
  55. package/dist/homebridge-ui/public/js/modals.js +0 -53
  56. package/dist/homebridge-ui/public/js/modals.js.map +1 -1
  57. package/dist/homebridge-ui/public/js/modals.ts +0 -58
  58. package/dist/homebridge-ui/public/js/render.d.ts.map +1 -1
  59. package/dist/homebridge-ui/public/js/render.js +1 -2
  60. package/dist/homebridge-ui/public/js/render.js.map +1 -1
  61. package/dist/homebridge-ui/public/js/render.ts +1 -2
  62. package/dist/homebridge-ui/settings.js.map +1 -1
  63. package/dist/homebridge-ui/utils/config-parser.d.ts +0 -8
  64. package/dist/homebridge-ui/utils/config-parser.d.ts.map +1 -1
  65. package/dist/homebridge-ui/utils/config-parser.js +0 -8
  66. package/dist/homebridge-ui/utils/config-parser.js.map +1 -1
  67. package/dist/index.js +2 -2
  68. package/dist/index.js.map +1 -1
  69. package/dist/settings.d.ts +0 -11
  70. package/dist/settings.d.ts.map +1 -1
  71. package/dist/settings.js.map +1 -1
  72. package/dist/utils.d.ts +0 -5
  73. package/dist/utils.d.ts.map +1 -1
  74. package/dist/utils.js +0 -27
  75. package/dist/utils.js.map +1 -1
  76. package/docs/assets/highlight.css +15 -15
  77. package/docs/index.html +5 -22
  78. package/docs/variables/default.html +1 -1
  79. package/package.json +15 -9
  80. package/scripts/build-ui.js +0 -30
  81. package/src/{Platform.HAP.ts → SwitchBotHAPPlatform.ts} +112 -39
  82. package/src/{Platform.Matter.ts → SwitchBotMatterPlatform.ts} +8 -63
  83. package/src/deviceFactory.ts +5 -14
  84. package/src/devices/genericDevice.ts +271 -12
  85. package/src/homebridge-ui/endpoints/config.ts +2 -10
  86. package/src/homebridge-ui/public/css/styles.css +1 -61
  87. package/src/homebridge-ui/public/index.html +0 -59
  88. package/src/homebridge-ui/public/js/advanced-settings.ts +3 -4
  89. package/src/homebridge-ui/public/js/api.ts +1 -61
  90. package/src/homebridge-ui/public/js/app.ts +4 -47
  91. package/src/homebridge-ui/public/js/credentials.ts +5 -11
  92. package/src/homebridge-ui/public/js/devices.ts +0 -6
  93. package/src/homebridge-ui/public/js/modals.ts +0 -58
  94. package/src/homebridge-ui/public/js/render.ts +1 -2
  95. package/src/homebridge-ui/utils/config-parser.ts +0 -9
  96. package/src/index.ts +2 -2
  97. package/src/settings.ts +0 -12
  98. package/src/utils.ts +0 -31
  99. package/test/device/deviceFactory.spec.ts +9 -0
  100. package/test/device/getstate-cache.spec.ts +62 -0
  101. package/test/device/getstate-returns-status.spec.ts +89 -0
  102. package/test/device/water-detector-battery.spec.ts +81 -0
  103. package/test/device/water-detector-leak.spec.ts +43 -0
  104. package/test/hap-push-updates.spec.ts +150 -0
  105. package/test/platform/accessory-restore.spec.ts +2 -2
  106. package/test/platform/matter-childbridge.spec.ts +1 -1
  107. package/test/platform/matter-integration.spec.ts +1 -51
  108. package/test/platform/platform-edge.spec.ts +2 -2
  109. package/test/platform/platform.integration.spec.ts +1 -1
  110. package/test/utils/utils.spec.ts +0 -15
  111. package/.github/npm-version-script-esm.js +0 -97
  112. package/dist/Platform.HAP.d.ts.map +0 -1
  113. package/dist/Platform.HAP.js.map +0 -1
  114. package/dist/Platform.Matter.d.ts.map +0 -1
  115. package/dist/Platform.Matter.js.map +0 -1
  116. package/dist/homebridge-ui/homebridge-ui/utils/v4-detection.js +0 -23
  117. package/dist/homebridge-ui/homebridge-ui/utils/v4-detection.js.map +0 -1
  118. package/dist/homebridge-ui/public/js/legacy-view.d.ts +0 -3
  119. package/dist/homebridge-ui/public/js/legacy-view.d.ts.map +0 -1
  120. package/dist/homebridge-ui/public/js/legacy-view.js +0 -43
  121. package/dist/homebridge-ui/public/js/legacy-view.js.map +0 -1
  122. package/dist/homebridge-ui/public/js/legacy-view.ts +0 -99
  123. package/dist/homebridge-ui/utils/v4-detection.d.ts +0 -12
  124. package/dist/homebridge-ui/utils/v4-detection.d.ts.map +0 -1
  125. package/dist/homebridge-ui/utils/v4-detection.js +0 -23
  126. package/dist/homebridge-ui/utils/v4-detection.js.map +0 -1
  127. package/src/homebridge-ui/public/js/legacy-view.js +0 -43
  128. package/src/homebridge-ui/public/js/legacy-view.ts +0 -99
  129. package/src/homebridge-ui/utils/v4-detection.ts +0 -23
  130. package/test/homebridge-ui/config-parser.spec.ts +0 -61
@@ -1,43 +0,0 @@
1
- import { disableLegacyView, enableLegacyView } from './legacy-view.js'
2
-
3
- // Toggle button visibility and view containers
4
- function updateViewButtons() {
5
- const legacyBtn = document.getElementById('legacyViewBtn')
6
- const dynamicBtn = document.getElementById('dynamicViewBtn')
7
- const legacyContainer = document.getElementById('legacyViewContainer')
8
- if (legacyContainer && legacyContainer.style.display === 'block') {
9
- if (legacyBtn) {
10
- legacyBtn.style.display = 'none'
11
- }
12
- if (dynamicBtn) {
13
- dynamicBtn.style.display = 'inline-block'
14
- }
15
- } else {
16
- if (legacyBtn) {
17
- legacyBtn.style.display = 'inline-block'
18
- }
19
- if (dynamicBtn) {
20
- dynamicBtn.style.display = 'none'
21
- }
22
- }
23
- }
24
-
25
- document.addEventListener('DOMContentLoaded', () => {
26
- const legacyBtn = document.getElementById('legacyViewBtn')
27
- const dynamicBtn = document.getElementById('dynamicViewBtn')
28
- if (legacyBtn) {
29
- legacyBtn.addEventListener('click', () => {
30
- console.log('[SwitchBot UI] Legacy View button clicked')
31
- enableLegacyView()
32
- updateViewButtons()
33
- })
34
- }
35
- if (dynamicBtn) {
36
- dynamicBtn.addEventListener('click', () => {
37
- console.log('[SwitchBot UI] Dynamic View button clicked')
38
- disableLegacyView()
39
- updateViewButtons()
40
- })
41
- }
42
- updateViewButtons()
43
- })
@@ -1 +0,0 @@
1
- {"version":3,"file":"legacy-view.js","sourceRoot":"","sources":["../../../../src/homebridge-ui/public/js/legacy-view.ts"],"names":[],"mappings":"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"}
@@ -1,99 +0,0 @@
1
- // legacy-view.ts
2
- // Renders the config UI from config.schema.json and toggles between legacy and dynamic UI
3
-
4
- import schema from '../../../../config.schema.json'
5
-
6
- function renderLegacyView() {
7
- const container = document.getElementById('legacyConfigForm')
8
- if (!container) {
9
- const legacyContainer = document.getElementById('legacyViewContainer')
10
- if (legacyContainer) {
11
- legacyContainer.innerHTML = '<div style="color:red;font-weight:bold">[SwitchBot UI] Error: legacyConfigForm container not found</div>'
12
- }
13
- console.error('[SwitchBot UI] Error: legacyConfigForm container not found')
14
- return
15
- }
16
- container.innerHTML = ''
17
- const { properties } = schema.schema
18
- const layout = schema.layout || []
19
- const form = document.createElement('form')
20
- for (const section of layout) {
21
- const fieldset = document.createElement('fieldset')
22
- if (section.title) {
23
- const legend = document.createElement('legend')
24
- legend.textContent = section.title
25
- fieldset.appendChild(legend)
26
- }
27
- for (const key of section.items) {
28
- const prop = properties[key]
29
- if (!prop) {
30
- continue
31
- }
32
- if (prop.type === 'array' && prop.items && prop.items.properties) {
33
- // Render array of devices as a table
34
- const table = document.createElement('table')
35
- table.style.marginBottom = '12px'
36
- const thead = document.createElement('thead')
37
- const headerRow = document.createElement('tr')
38
- for (const colKey of Object.keys(prop.items.properties)) {
39
- const th = document.createElement('th')
40
- th.textContent = prop.items.properties[colKey].title || colKey
41
- headerRow.appendChild(th)
42
- }
43
- thead.appendChild(headerRow)
44
- table.appendChild(thead)
45
- // No data rows (read-only legacy view)
46
- fieldset.appendChild(table)
47
- continue
48
- }
49
- const label = document.createElement('label')
50
- label.textContent = prop.title || key
51
- label.style.display = 'block'
52
- let input: HTMLElement
53
- if (prop.enum) {
54
- const select = document.createElement('select')
55
- select.name = key
56
- for (const opt of prop.enum) {
57
- const option = document.createElement('option')
58
- option.value = opt
59
- option.textContent = opt
60
- select.appendChild(option)
61
- }
62
- input = select
63
- } else if (prop.type === 'boolean') {
64
- const checkbox = document.createElement('input')
65
- checkbox.type = 'checkbox'
66
- checkbox.name = key
67
- if (prop.default) {
68
- checkbox.checked = true
69
- }
70
- input = checkbox
71
- } else {
72
- const text = document.createElement('input')
73
- text.name = key
74
- text.type = prop.format === 'password' ? 'password' : 'text'
75
- if (prop.default) {
76
- text.value = prop.default
77
- }
78
- input = text
79
- }
80
- label.appendChild(input)
81
- fieldset.appendChild(label)
82
- }
83
- form.appendChild(fieldset)
84
- }
85
- container.appendChild(form)
86
- }
87
-
88
- export function enableLegacyView() {
89
- document.getElementById('dynamicUiContainer')!.style.display = 'none'
90
- document.getElementById('legacyViewContainer')!.style.display = 'block'
91
- renderLegacyView()
92
- }
93
-
94
- export function disableLegacyView() {
95
- document.getElementById('dynamicUiContainer')!.style.display = 'block'
96
- document.getElementById('legacyViewContainer')!.style.display = 'none'
97
- }
98
-
99
- // (window assignment removed; handled in .js entry point)
@@ -1,12 +0,0 @@
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 declare function isV4Config(platform: any): boolean;
12
- //# sourceMappingURL=v4-detection.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"v4-detection.d.ts","sourceRoot":"","sources":["../../../src/homebridge-ui/utils/v4-detection.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,wBAAgB,UAAU,CAAC,QAAQ,EAAE,GAAG,GAAG,OAAO,CAYjD"}
@@ -1,23 +0,0 @@
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) {
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 = platform.options !== undefined
19
- && typeof platform.options === 'object'
20
- && Array.isArray(platform.options.devices);
21
- return hasCredentials || hasOptionsDevices;
22
- }
23
- //# sourceMappingURL=v4-detection.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"v4-detection.js","sourceRoot":"","sources":["../../../src/homebridge-ui/utils/v4-detection.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,MAAM,UAAU,UAAU,CAAC,QAAa;IACtC,IAAI,CAAC,QAAQ,IAAI,OAAO,QAAQ,KAAK,QAAQ,EAAE,CAAC;QAC9C,OAAO,KAAK,CAAA;IACd,CAAC;IACD,mEAAmE;IACnE,MAAM,cAAc,GAAG,QAAQ,CAAC,WAAW,KAAK,SAAS,IAAI,OAAO,QAAQ,CAAC,WAAW,KAAK,QAAQ,CAAA;IACrG,uDAAuD;IACvD,MAAM,iBAAiB,GACnB,QAAQ,CAAC,OAAO,KAAK,SAAS;WAC7B,OAAO,QAAQ,CAAC,OAAO,KAAK,QAAQ;WACpC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,OAAO,CAAC,CAAA;IAC5C,OAAO,cAAc,IAAI,iBAAiB,CAAA;AAC5C,CAAC"}
@@ -1,43 +0,0 @@
1
- import { disableLegacyView, enableLegacyView } from './legacy-view.js'
2
-
3
- // Toggle button visibility and view containers
4
- function updateViewButtons() {
5
- const legacyBtn = document.getElementById('legacyViewBtn')
6
- const dynamicBtn = document.getElementById('dynamicViewBtn')
7
- const legacyContainer = document.getElementById('legacyViewContainer')
8
- if (legacyContainer && legacyContainer.style.display === 'block') {
9
- if (legacyBtn) {
10
- legacyBtn.style.display = 'none'
11
- }
12
- if (dynamicBtn) {
13
- dynamicBtn.style.display = 'inline-block'
14
- }
15
- } else {
16
- if (legacyBtn) {
17
- legacyBtn.style.display = 'inline-block'
18
- }
19
- if (dynamicBtn) {
20
- dynamicBtn.style.display = 'none'
21
- }
22
- }
23
- }
24
-
25
- document.addEventListener('DOMContentLoaded', () => {
26
- const legacyBtn = document.getElementById('legacyViewBtn')
27
- const dynamicBtn = document.getElementById('dynamicViewBtn')
28
- if (legacyBtn) {
29
- legacyBtn.addEventListener('click', () => {
30
- console.log('[SwitchBot UI] Legacy View button clicked')
31
- enableLegacyView()
32
- updateViewButtons()
33
- })
34
- }
35
- if (dynamicBtn) {
36
- dynamicBtn.addEventListener('click', () => {
37
- console.log('[SwitchBot UI] Dynamic View button clicked')
38
- disableLegacyView()
39
- updateViewButtons()
40
- })
41
- }
42
- updateViewButtons()
43
- })
@@ -1,99 +0,0 @@
1
- // legacy-view.ts
2
- // Renders the config UI from config.schema.json and toggles between legacy and dynamic UI
3
-
4
- import schema from '../../../../config.schema.json'
5
-
6
- function renderLegacyView() {
7
- const container = document.getElementById('legacyConfigForm')
8
- if (!container) {
9
- const legacyContainer = document.getElementById('legacyViewContainer')
10
- if (legacyContainer) {
11
- legacyContainer.innerHTML = '<div style="color:red;font-weight:bold">[SwitchBot UI] Error: legacyConfigForm container not found</div>'
12
- }
13
- console.error('[SwitchBot UI] Error: legacyConfigForm container not found')
14
- return
15
- }
16
- container.innerHTML = ''
17
- const { properties } = schema.schema
18
- const layout = schema.layout || []
19
- const form = document.createElement('form')
20
- for (const section of layout) {
21
- const fieldset = document.createElement('fieldset')
22
- if (section.title) {
23
- const legend = document.createElement('legend')
24
- legend.textContent = section.title
25
- fieldset.appendChild(legend)
26
- }
27
- for (const key of section.items) {
28
- const prop = properties[key]
29
- if (!prop) {
30
- continue
31
- }
32
- if (prop.type === 'array' && prop.items && prop.items.properties) {
33
- // Render array of devices as a table
34
- const table = document.createElement('table')
35
- table.style.marginBottom = '12px'
36
- const thead = document.createElement('thead')
37
- const headerRow = document.createElement('tr')
38
- for (const colKey of Object.keys(prop.items.properties)) {
39
- const th = document.createElement('th')
40
- th.textContent = prop.items.properties[colKey].title || colKey
41
- headerRow.appendChild(th)
42
- }
43
- thead.appendChild(headerRow)
44
- table.appendChild(thead)
45
- // No data rows (read-only legacy view)
46
- fieldset.appendChild(table)
47
- continue
48
- }
49
- const label = document.createElement('label')
50
- label.textContent = prop.title || key
51
- label.style.display = 'block'
52
- let input: HTMLElement
53
- if (prop.enum) {
54
- const select = document.createElement('select')
55
- select.name = key
56
- for (const opt of prop.enum) {
57
- const option = document.createElement('option')
58
- option.value = opt
59
- option.textContent = opt
60
- select.appendChild(option)
61
- }
62
- input = select
63
- } else if (prop.type === 'boolean') {
64
- const checkbox = document.createElement('input')
65
- checkbox.type = 'checkbox'
66
- checkbox.name = key
67
- if (prop.default) {
68
- checkbox.checked = true
69
- }
70
- input = checkbox
71
- } else {
72
- const text = document.createElement('input')
73
- text.name = key
74
- text.type = prop.format === 'password' ? 'password' : 'text'
75
- if (prop.default) {
76
- text.value = prop.default
77
- }
78
- input = text
79
- }
80
- label.appendChild(input)
81
- fieldset.appendChild(label)
82
- }
83
- form.appendChild(fieldset)
84
- }
85
- container.appendChild(form)
86
- }
87
-
88
- export function enableLegacyView() {
89
- document.getElementById('dynamicUiContainer')!.style.display = 'none'
90
- document.getElementById('legacyViewContainer')!.style.display = 'block'
91
- renderLegacyView()
92
- }
93
-
94
- export function disableLegacyView() {
95
- document.getElementById('dynamicUiContainer')!.style.display = 'block'
96
- document.getElementById('legacyViewContainer')!.style.display = 'none'
97
- }
98
-
99
- // (window assignment removed; handled in .js entry point)
@@ -1,23 +0,0 @@
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
- }
@@ -1,61 +0,0 @@
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
- })