matterbridge-lg-thinq-plugin 0.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 (210) hide show
  1. package/.claude/CLAUDE.md +43 -0
  2. package/.claude/agents/compiler.md +91 -0
  3. package/.claude/agents/direct-executor.md +98 -0
  4. package/.claude/agents/documenter.md +78 -0
  5. package/.claude/agents/finalizer.md +233 -0
  6. package/.claude/agents/implementer.md +112 -0
  7. package/.claude/agents/investigator.md +100 -0
  8. package/.claude/agents/release-manager.md +203 -0
  9. package/.claude/agents/reviewer.md +131 -0
  10. package/.claude/agents/technical-architect.md +403 -0
  11. package/.claude/agents/test-writer.md +159 -0
  12. package/.claude/agents/wiki-manager.md +147 -0
  13. package/.claude/instructions/agent-prompts.md +90 -0
  14. package/.claude/instructions/shared-rules.md +11 -0
  15. package/.claude/instructions/team-orchestrator-policy.md +165 -0
  16. package/.claude/memory.md +186 -0
  17. package/.claude/output-styles/engineer-manager.md +38 -0
  18. package/.claude/settings.json +40 -0
  19. package/.claude/settings.local.json +30 -0
  20. package/.claude/skills/load-policy/SKILL.md +13 -0
  21. package/.claude/skills/ref-idea/SKILL.md +44 -0
  22. package/.claude/skills/status-of/SKILL.md +38 -0
  23. package/.claude/skills/workspace-console/SKILL.md +49 -0
  24. package/.claude/statusline-command.sh +130 -0
  25. package/.claude/statusline.md +53 -0
  26. package/.claude/templates/approval-packet.template.html +171 -0
  27. package/.claude/templates/reference-workspaces.md +16 -0
  28. package/.claude/tools/cli-runner-server.cjs +220 -0
  29. package/.claude/tools/discord-send-server.cjs +188 -0
  30. package/.claude/tools/github-release-server.cjs +181 -0
  31. package/.claude/tools/glob-grep-server.cjs +267 -0
  32. package/.claude/tools/read-log-server.cjs +175 -0
  33. package/.github/codeql/codeql-config.yml +14 -0
  34. package/.github/workflows/build.yml +54 -0
  35. package/.github/workflows/codeql.yml +59 -0
  36. package/.github/workflows/coverage.yml +70 -0
  37. package/.github/workflows/publish.yml +103 -0
  38. package/.mcp.json +34 -0
  39. package/CHANGELOG.md +195 -0
  40. package/LICENSE +21 -0
  41. package/dist/cli/commands/devices.js +52 -0
  42. package/dist/cli/commands/energy.js +122 -0
  43. package/dist/cli/commands/login.js +54 -0
  44. package/dist/cli/help.js +50 -0
  45. package/dist/cli/main.js +56 -0
  46. package/dist/cli/session.js +13 -0
  47. package/dist/cli/types.js +1 -0
  48. package/dist/cli/utils.js +29 -0
  49. package/dist/cli.js +2 -0
  50. package/dist/constants/index.js +1 -0
  51. package/dist/core/domain/entities/ThinqDevice.js +43 -0
  52. package/dist/core/domain/value-objects/AirConditionerCapabilities.js +22 -0
  53. package/dist/core/domain/value-objects/ThinqFilterState.js +35 -0
  54. package/dist/core/domain/value-objects/ThinqSnapshot.js +125 -0
  55. package/dist/errors/AuthenticationError.js +26 -0
  56. package/dist/errors/BaseError.js +23 -0
  57. package/dist/errors/index.js +2 -0
  58. package/dist/model/LgThinkqPluginPlatformConfig.js +23 -0
  59. package/dist/model/LgThinqPluginPlatformConfig.js +23 -0
  60. package/dist/module.js +212 -0
  61. package/dist/platform/deviceRegistry.js +26 -0
  62. package/dist/platform/platformConfigManager.js +89 -0
  63. package/dist/platform/platformState.js +9 -0
  64. package/dist/platform/thinq/thinqAirConditionerCommandHandlers.js +190 -0
  65. package/dist/platform/thinq/thinqAirConditionerEndpointFactory.js +53 -0
  66. package/dist/platform/thinq/thinqAirConditionerFilterResetCommandHandler.js +54 -0
  67. package/dist/platform/thinq/thinqAirConditionerFilterStateSync.js +19 -0
  68. package/dist/platform/thinq/thinqAirConditionerStateSync.js +205 -0
  69. package/dist/platform/thinq/thinqDeviceConfigReconciler.js +26 -0
  70. package/dist/platform/thinq/thinqDeviceConfigurator.js +124 -0
  71. package/dist/platform/thinq/thinqDeviceDiscovery.js +33 -0
  72. package/dist/platform/thinq/thinqWasherCommandHandlers.js +54 -0
  73. package/dist/platform/thinq/thinqWasherEndpointFactory.js +6 -0
  74. package/dist/platform/thinq/thinqWasherStateSync.js +31 -0
  75. package/dist/platform/thinq/thinqWasherStopCommandResolver.js +38 -0
  76. package/dist/services/authentication/AccountAuthStrategy.js +135 -0
  77. package/dist/services/authentication/AuthContext.js +1 -0
  78. package/dist/services/authentication/AuthenticationCoordinator.js +29 -0
  79. package/dist/services/authentication/IAuthStrategy.js +1 -0
  80. package/dist/services/authentication/TokenAuthStrategy.js +27 -0
  81. package/dist/services/authentication/UserDataRepository.js +32 -0
  82. package/dist/services/thinq/gateway.js +61 -0
  83. package/dist/services/thinq/mqtt/mqttCertificate.js +63 -0
  84. package/dist/services/thinq/mqtt/mqttConnectionEvents.js +37 -0
  85. package/dist/services/thinq/mqtt/mqttKeyRepository.js +33 -0
  86. package/dist/services/thinq/mqtt/mqttRetry.js +25 -0
  87. package/dist/services/thinq/mqtt/thinqMqttListener.js +97 -0
  88. package/dist/services/thinq/serviceContainer.js +73 -0
  89. package/dist/services/thinq/session.js +48 -0
  90. package/dist/services/thinq/thinqApiClient.js +258 -0
  91. package/dist/services/thinq/thinqDeviceService.js +75 -0
  92. package/dist/services/thinq/thinqFilterMonitoringService.js +40 -0
  93. package/dist/settings.js +9 -0
  94. package/dist/tests/helpers/testUtils.js +87 -0
  95. package/dist/utils/mask.js +39 -0
  96. package/docs/README_CLI.md +166 -0
  97. package/eslint.config.js +194 -0
  98. package/matterbridge-lg-thinq-plugin.config.json +27 -0
  99. package/matterbridge-lg-thinq-plugin.schema.json +520 -0
  100. package/package.json +107 -0
  101. package/prettier.config.js +49 -0
  102. package/scripts/parse-junit-failures.mjs +45 -0
  103. package/scripts/parse-tsc-failures.mjs +48 -0
  104. package/scripts/run-build-local-summary.mjs +29 -0
  105. package/scripts/run-format-summary.mjs +36 -0
  106. package/scripts/run-lint-fix-summary.mjs +38 -0
  107. package/scripts/run-precommit-summary.mjs +94 -0
  108. package/src/cli/commands/devices.ts +67 -0
  109. package/src/cli/commands/energy.ts +160 -0
  110. package/src/cli/commands/login.ts +74 -0
  111. package/src/cli/help.ts +53 -0
  112. package/src/cli/main.ts +66 -0
  113. package/src/cli/session.ts +15 -0
  114. package/src/cli/types.ts +10 -0
  115. package/src/cli/utils.ts +30 -0
  116. package/src/cli.ts +3 -0
  117. package/src/constants/index.ts +8 -0
  118. package/src/core/domain/entities/ThinqDevice.test.ts +224 -0
  119. package/src/core/domain/entities/ThinqDevice.ts +74 -0
  120. package/src/core/domain/value-objects/AirConditionerCapabilities.ts +50 -0
  121. package/src/core/domain/value-objects/ThinqFilterState.ts +50 -0
  122. package/src/core/domain/value-objects/ThinqSnapshot.ts +162 -0
  123. package/src/errors/AuthenticationError.ts +37 -0
  124. package/src/errors/BaseError.ts +38 -0
  125. package/src/errors/index.ts +24 -0
  126. package/src/model/LgThinqPluginPlatformConfig.ts +94 -0
  127. package/src/module.ts +298 -0
  128. package/src/platform/deviceRegistry.ts +58 -0
  129. package/src/platform/platformConfigManager.ts +133 -0
  130. package/src/platform/platformState.ts +16 -0
  131. package/src/platform/thinq/thinqAirConditionerCommandHandlers.ts +291 -0
  132. package/src/platform/thinq/thinqAirConditionerEndpointFactory.ts +145 -0
  133. package/src/platform/thinq/thinqAirConditionerFilterResetCommandHandler.ts +96 -0
  134. package/src/platform/thinq/thinqAirConditionerFilterStateSync.ts +42 -0
  135. package/src/platform/thinq/thinqAirConditionerStateSync.ts +343 -0
  136. package/src/platform/thinq/thinqDeviceConfigReconciler.ts +42 -0
  137. package/src/platform/thinq/thinqDeviceConfigurator.test.ts +90 -0
  138. package/src/platform/thinq/thinqDeviceConfigurator.ts +191 -0
  139. package/src/platform/thinq/thinqDeviceDiscovery.test.ts +173 -0
  140. package/src/platform/thinq/thinqDeviceDiscovery.ts +51 -0
  141. package/src/platform/thinq/thinqWasherCommandHandlers.ts +120 -0
  142. package/src/platform/thinq/thinqWasherEndpointFactory.ts +37 -0
  143. package/src/platform/thinq/thinqWasherStateSync.ts +60 -0
  144. package/src/platform/thinq/thinqWasherStopCommandResolver.ts +62 -0
  145. package/src/services/authentication/AccountAuthStrategy.test.ts +358 -0
  146. package/src/services/authentication/AccountAuthStrategy.ts +209 -0
  147. package/src/services/authentication/AuthContext.ts +19 -0
  148. package/src/services/authentication/AuthenticationCoordinator.test.ts +217 -0
  149. package/src/services/authentication/AuthenticationCoordinator.ts +59 -0
  150. package/src/services/authentication/IAuthStrategy.ts +10 -0
  151. package/src/services/authentication/TokenAuthStrategy.test.ts +167 -0
  152. package/src/services/authentication/TokenAuthStrategy.ts +40 -0
  153. package/src/services/authentication/UserDataRepository.test.ts +149 -0
  154. package/src/services/authentication/UserDataRepository.ts +46 -0
  155. package/src/services/thinq/gateway.ts +86 -0
  156. package/src/services/thinq/mqtt/mqttCertificate.test.ts +344 -0
  157. package/src/services/thinq/mqtt/mqttCertificate.ts +102 -0
  158. package/src/services/thinq/mqtt/mqttConnectionEvents.test.ts +276 -0
  159. package/src/services/thinq/mqtt/mqttConnectionEvents.ts +76 -0
  160. package/src/services/thinq/mqtt/mqttKeyRepository.test.ts +164 -0
  161. package/src/services/thinq/mqtt/mqttKeyRepository.ts +43 -0
  162. package/src/services/thinq/mqtt/mqttRetry.test.ts +239 -0
  163. package/src/services/thinq/mqtt/mqttRetry.ts +43 -0
  164. package/src/services/thinq/mqtt/thinqMqttListener.test.ts +349 -0
  165. package/src/services/thinq/mqtt/thinqMqttListener.ts +120 -0
  166. package/src/services/thinq/serviceContainer.test.ts +203 -0
  167. package/src/services/thinq/serviceContainer.ts +105 -0
  168. package/src/services/thinq/session.test.ts +287 -0
  169. package/src/services/thinq/session.ts +69 -0
  170. package/src/services/thinq/thinqApiClient.test.ts +702 -0
  171. package/src/services/thinq/thinqApiClient.ts +418 -0
  172. package/src/services/thinq/thinqDeviceService.test.ts +415 -0
  173. package/src/services/thinq/thinqDeviceService.ts +101 -0
  174. package/src/services/thinq/thinqFilterMonitoringService.test.ts +264 -0
  175. package/src/services/thinq/thinqFilterMonitoringService.ts +53 -0
  176. package/src/settings.ts +18 -0
  177. package/src/tests/cli/commands/devices.test.ts +293 -0
  178. package/src/tests/cli/commands/login.test.ts +240 -0
  179. package/src/tests/cli/help.test.ts +46 -0
  180. package/src/tests/cli/main.test.ts +214 -0
  181. package/src/tests/cli/session.test.ts +198 -0
  182. package/src/tests/cli/utils.test.ts +194 -0
  183. package/src/tests/core/domain/value-objects/AirConditionerCapabilities.test.ts +685 -0
  184. package/src/tests/core/domain/value-objects/ThinqFilterState.test.ts +210 -0
  185. package/src/tests/core/domain/value-objects/ThinqSnapshot.test.ts +1035 -0
  186. package/src/tests/helpers/testUtils.ts +118 -0
  187. package/src/tests/model/LgThinqPluginPlatformConfig.test.ts +81 -0
  188. package/src/tests/platform/deviceRegistry.test.ts +177 -0
  189. package/src/tests/platform/platformConfigManager.test.ts +790 -0
  190. package/src/tests/platform/platformState.test.ts +41 -0
  191. package/src/tests/platform/thinq/thinqAirConditionerCommandHandlers.test.ts +879 -0
  192. package/src/tests/platform/thinq/thinqAirConditionerEndpointFactory.test.ts +993 -0
  193. package/src/tests/platform/thinq/thinqAirConditionerFilterResetCommandHandler.test.ts +508 -0
  194. package/src/tests/platform/thinq/thinqAirConditionerFilterStateSync.test.ts +212 -0
  195. package/src/tests/platform/thinq/thinqAirConditionerStateSync.test.ts +1586 -0
  196. package/src/tests/platform/thinq/thinqDeviceConfigReconciler.test.ts +269 -0
  197. package/src/tests/platform/thinq/thinqDeviceConfigurator.test.ts +492 -0
  198. package/src/tests/platform/thinq/thinqWasherCommandHandlers.test.ts +594 -0
  199. package/src/tests/platform/thinq/thinqWasherEndpointFactory.test.ts +84 -0
  200. package/src/tests/platform/thinq/thinqWasherStateSync.test.ts +373 -0
  201. package/src/tests/platform/thinq/thinqWasherStopCommandResolver.test.ts +392 -0
  202. package/src/tests/settings.test.ts +33 -0
  203. package/src/utils/mask.ts +50 -0
  204. package/tsconfig.base.json +25 -0
  205. package/tsconfig.json +9 -0
  206. package/tsconfig.production.json +13 -0
  207. package/tsconfig.vitest.json +5 -0
  208. package/vite.config.ts +43 -0
  209. package/wiki/memory-archive.md +12 -0
  210. package/wiki/reference-workspaces.md +23 -0
@@ -0,0 +1,269 @@
1
+ import { describe, expect, it } from 'vitest';
2
+
3
+ import type { ThinqAirConditionerDevice, ThinqWasherDevice } from '../../../core/domain/entities/ThinqDevice.js';
4
+ import type { ThinqDeviceConfigEntry } from '../../../model/LgThinqPluginPlatformConfig.js';
5
+ import { reconcileThinqDeviceConfigEntries } from '../../../platform/thinq/thinqDeviceConfigReconciler.js';
6
+ import { asPartial } from '../../helpers/testUtils.js';
7
+
8
+ describe('reconcileThinqDeviceConfigEntries', () => {
9
+ describe('empty configDevices with discovered AC device', () => {
10
+ it('should push new AC entry and return true', () => {
11
+ // Arrange
12
+ const configDevices: ThinqDeviceConfigEntry[] = [];
13
+ const discovered = asPartial<ThinqAirConditionerDevice>({
14
+ id: 'ac-001',
15
+ type: 'AC',
16
+ });
17
+
18
+ // Act
19
+ const changed = reconcileThinqDeviceConfigEntries(configDevices, [discovered]);
20
+
21
+ // Assert
22
+ expect(changed).toBe(true);
23
+ expect(configDevices).toHaveLength(1);
24
+ expect(configDevices[0]).toEqual({
25
+ deviceId: 'ac-001',
26
+ deviceType: 'AC',
27
+ });
28
+ });
29
+ });
30
+
31
+ describe('empty configDevices with discovered washer device', () => {
32
+ it('should push new washer entry and return true', () => {
33
+ // Arrange
34
+ const configDevices: ThinqDeviceConfigEntry[] = [];
35
+ const discovered = asPartial<ThinqWasherDevice>({
36
+ id: 'washer-001',
37
+ type: 'WASHER',
38
+ });
39
+
40
+ // Act
41
+ const changed = reconcileThinqDeviceConfigEntries(configDevices, [discovered]);
42
+
43
+ // Assert
44
+ expect(changed).toBe(true);
45
+ expect(configDevices).toHaveLength(1);
46
+ expect(configDevices[0]).toEqual({
47
+ deviceId: 'washer-001',
48
+ deviceType: 'WASHER',
49
+ });
50
+ });
51
+ });
52
+
53
+ describe('existing entry with correct deviceType', () => {
54
+ it('should not mutate the entry and return false', () => {
55
+ // Arrange
56
+ const existing: ThinqDeviceConfigEntry = {
57
+ deviceId: 'ac-001',
58
+ deviceType: 'AC',
59
+ capabilities: {
60
+ supportsHeat: true,
61
+ supportsFanSpeedControl: false,
62
+ },
63
+ productName: 'Custom AC',
64
+ };
65
+ const configDevices = [existing];
66
+ const originalReference = configDevices[0];
67
+ const discovered = asPartial<ThinqAirConditionerDevice>({
68
+ id: 'ac-001',
69
+ type: 'AC',
70
+ });
71
+
72
+ // Act
73
+ const changed = reconcileThinqDeviceConfigEntries(configDevices, [discovered]);
74
+
75
+ // Assert
76
+ expect(changed).toBe(false);
77
+ expect(configDevices[0]).toBe(originalReference);
78
+ expect(configDevices[0]).toEqual(existing);
79
+ expect(configDevices[0].capabilities).toEqual({
80
+ supportsHeat: true,
81
+ supportsFanSpeedControl: false,
82
+ });
83
+ });
84
+ });
85
+
86
+ describe('existing entry with missing or stale deviceType', () => {
87
+ it('should backfill missing deviceType and return true', () => {
88
+ // Arrange
89
+ const existing: ThinqDeviceConfigEntry = {
90
+ deviceId: 'ac-001',
91
+ capabilities: {
92
+ supportsHeat: true,
93
+ },
94
+ };
95
+ const configDevices = [existing];
96
+ const discovered = asPartial<ThinqAirConditionerDevice>({
97
+ id: 'ac-001',
98
+ type: 'AC',
99
+ });
100
+
101
+ // Act
102
+ const changed = reconcileThinqDeviceConfigEntries(configDevices, [discovered]);
103
+
104
+ // Assert
105
+ expect(changed).toBe(true);
106
+ expect(configDevices[0].deviceType).toBe('AC');
107
+ expect(configDevices[0].capabilities).toEqual({
108
+ supportsHeat: true,
109
+ });
110
+ });
111
+
112
+ it('should correct stale deviceType and return true', () => {
113
+ // Arrange
114
+ const existing: ThinqDeviceConfigEntry = {
115
+ deviceId: 'device-001',
116
+ deviceType: 'WASHER',
117
+ productName: 'Old Value',
118
+ };
119
+ const configDevices = [existing];
120
+ const discovered = asPartial<ThinqAirConditionerDevice>({
121
+ id: 'device-001',
122
+ type: 'AC',
123
+ });
124
+
125
+ // Act
126
+ const changed = reconcileThinqDeviceConfigEntries(configDevices, [discovered]);
127
+
128
+ // Assert
129
+ expect(changed).toBe(true);
130
+ expect(configDevices[0].deviceType).toBe('AC');
131
+ expect(configDevices[0].productName).toBe('Old Value');
132
+ });
133
+ });
134
+
135
+ describe('mixed input: existing correct + new device', () => {
136
+ it('should add new device without touching existing entry', () => {
137
+ // Arrange
138
+ const existing: ThinqDeviceConfigEntry = {
139
+ deviceId: 'ac-001',
140
+ deviceType: 'AC',
141
+ capabilities: { supportsHeat: true },
142
+ };
143
+ const configDevices = [existing];
144
+ const existingReference = configDevices[0];
145
+ const discovered = [
146
+ asPartial<ThinqAirConditionerDevice>({
147
+ id: 'ac-001',
148
+ type: 'AC',
149
+ }),
150
+ asPartial<ThinqWasherDevice>({
151
+ id: 'washer-001',
152
+ type: 'WASHER',
153
+ }),
154
+ ];
155
+
156
+ // Act
157
+ const changed = reconcileThinqDeviceConfigEntries(configDevices, discovered);
158
+
159
+ // Assert
160
+ expect(changed).toBe(true);
161
+ expect(configDevices).toHaveLength(2);
162
+ expect(configDevices[0]).toBe(existingReference);
163
+ expect(configDevices[0].capabilities).toEqual({ supportsHeat: true });
164
+ expect(configDevices[1]).toEqual({
165
+ deviceId: 'washer-001',
166
+ deviceType: 'WASHER',
167
+ });
168
+ });
169
+ });
170
+
171
+ describe('discovered device that is neither AC nor washer', () => {
172
+ it('should skip unrecognized device type silently', () => {
173
+ // Arrange
174
+ const configDevices: ThinqDeviceConfigEntry[] = [];
175
+ const discovered = asPartial<{ id: string; type: string }>({
176
+ id: 'dryer-001',
177
+ type: 'DRYER',
178
+ });
179
+
180
+ // Act
181
+ const changed = reconcileThinqDeviceConfigEntries(configDevices, [discovered as any]);
182
+
183
+ // Assert
184
+ expect(changed).toBe(false);
185
+ expect(configDevices).toHaveLength(0);
186
+ });
187
+ });
188
+
189
+ describe('multiple discovered devices, only some new', () => {
190
+ it('should add only new devices and skip existing ones', () => {
191
+ // Arrange
192
+ const existing: ThinqDeviceConfigEntry = {
193
+ deviceId: 'ac-001',
194
+ deviceType: 'AC',
195
+ };
196
+ const configDevices = [existing];
197
+ const discovered = [
198
+ asPartial<ThinqAirConditionerDevice>({
199
+ id: 'ac-001',
200
+ type: 'AC',
201
+ }),
202
+ asPartial<ThinqWasherDevice>({
203
+ id: 'washer-001',
204
+ type: 'WASHER',
205
+ }),
206
+ asPartial<ThinqWasherDevice>({
207
+ id: 'washer-002',
208
+ type: 'WASHER',
209
+ }),
210
+ ];
211
+
212
+ // Act
213
+ const changed = reconcileThinqDeviceConfigEntries(configDevices, discovered);
214
+
215
+ // Assert
216
+ expect(changed).toBe(true);
217
+ expect(configDevices).toHaveLength(3);
218
+ expect(configDevices[0].deviceId).toBe('ac-001');
219
+ expect(configDevices[1].deviceId).toBe('washer-001');
220
+ expect(configDevices[2].deviceId).toBe('washer-002');
221
+ });
222
+ });
223
+
224
+ describe('empty discovered devices list', () => {
225
+ it('should not modify configDevices and return false', () => {
226
+ // Arrange
227
+ const existing: ThinqDeviceConfigEntry = {
228
+ deviceId: 'ac-001',
229
+ deviceType: 'AC',
230
+ };
231
+ const configDevices = [existing];
232
+
233
+ // Act
234
+ const changed = reconcileThinqDeviceConfigEntries(configDevices, []);
235
+
236
+ // Assert
237
+ expect(changed).toBe(false);
238
+ expect(configDevices).toHaveLength(1);
239
+ expect(configDevices[0]).toEqual(existing);
240
+ });
241
+ });
242
+
243
+ describe('no changes needed', () => {
244
+ it('should return false when all devices already have correct types', () => {
245
+ // Arrange
246
+ const configDevices: ThinqDeviceConfigEntry[] = [
247
+ { deviceId: 'ac-001', deviceType: 'AC' },
248
+ { deviceId: 'washer-001', deviceType: 'WASHER' },
249
+ ];
250
+ const discovered = [
251
+ asPartial<ThinqAirConditionerDevice>({
252
+ id: 'ac-001',
253
+ type: 'AC',
254
+ }),
255
+ asPartial<ThinqWasherDevice>({
256
+ id: 'washer-001',
257
+ type: 'WASHER',
258
+ }),
259
+ ];
260
+
261
+ // Act
262
+ const changed = reconcileThinqDeviceConfigEntries(configDevices, discovered);
263
+
264
+ // Assert
265
+ expect(changed).toBe(false);
266
+ expect(configDevices).toHaveLength(2);
267
+ });
268
+ });
269
+ });