matterbridge-roborock-vacuum-plugin-regions 1.1.1-jb.1

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 (261) hide show
  1. package/.github/workflows/build.yml +56 -0
  2. package/.github/workflows/coverage.yml +59 -0
  3. package/.github/workflows/publish.yml +37 -0
  4. package/.tarignore +5 -0
  5. package/CHANGELOG.md +62 -0
  6. package/LICENSE +202 -0
  7. package/README.md +135 -0
  8. package/README_CLEANMODE.md +29 -0
  9. package/README_DEV.md +75 -0
  10. package/README_REPORT_ISSUE.md +34 -0
  11. package/README_SUPPORTED.md +67 -0
  12. package/dist/behaviorFactory.js +26 -0
  13. package/dist/behaviors/BehaviorDeviceGeneric.js +22 -0
  14. package/dist/behaviors/roborock.vacuum/default/default.js +183 -0
  15. package/dist/behaviors/roborock.vacuum/default/initalData.js +143 -0
  16. package/dist/behaviors/roborock.vacuum/default/runtimes.js +21 -0
  17. package/dist/behaviors/roborock.vacuum/smart/initalData.js +18 -0
  18. package/dist/behaviors/roborock.vacuum/smart/runtimes.js +11 -0
  19. package/dist/behaviors/roborock.vacuum/smart/smart.js +119 -0
  20. package/dist/clientManager.js +17 -0
  21. package/dist/helper.js +76 -0
  22. package/dist/index.js +4 -0
  23. package/dist/initialData/getBatteryStatus.js +24 -0
  24. package/dist/initialData/getOperationalStates.js +82 -0
  25. package/dist/initialData/getSupportedAreas.js +120 -0
  26. package/dist/initialData/getSupportedCleanModes.js +17 -0
  27. package/dist/initialData/getSupportedRunModes.js +11 -0
  28. package/dist/initialData/getSupportedScenes.js +26 -0
  29. package/dist/initialData/index.js +6 -0
  30. package/dist/model/CloudMessageModel.js +1 -0
  31. package/dist/model/DockingStationStatus.js +26 -0
  32. package/dist/model/ExperimentalFeatureSetting.js +30 -0
  33. package/dist/model/RoomMap.js +19 -0
  34. package/dist/model/roomIndexMap.js +21 -0
  35. package/dist/notifyMessageTypes.js +9 -0
  36. package/dist/platform.js +312 -0
  37. package/dist/platformRunner.js +90 -0
  38. package/dist/roborockCommunication/RESTAPI/roborockAuthenticateApi.js +213 -0
  39. package/dist/roborockCommunication/RESTAPI/roborockIoTApi.js +95 -0
  40. package/dist/roborockCommunication/Zenum/additionalPropCode.js +4 -0
  41. package/dist/roborockCommunication/Zenum/authenticateResponseCode.js +7 -0
  42. package/dist/roborockCommunication/Zenum/dockType.js +4 -0
  43. package/dist/roborockCommunication/Zenum/operationStatusCode.js +44 -0
  44. package/dist/roborockCommunication/Zenum/vacuumAndDockErrorCode.js +68 -0
  45. package/dist/roborockCommunication/Zmodel/apiResponse.js +1 -0
  46. package/dist/roborockCommunication/Zmodel/authenticateFlowState.js +1 -0
  47. package/dist/roborockCommunication/Zmodel/authenticateResponse.js +1 -0
  48. package/dist/roborockCommunication/Zmodel/baseURL.js +1 -0
  49. package/dist/roborockCommunication/Zmodel/batteryMessage.js +1 -0
  50. package/dist/roborockCommunication/Zmodel/device.js +1 -0
  51. package/dist/roborockCommunication/Zmodel/deviceModel.js +28 -0
  52. package/dist/roborockCommunication/Zmodel/deviceSchema.js +1 -0
  53. package/dist/roborockCommunication/Zmodel/deviceStatus.js +22 -0
  54. package/dist/roborockCommunication/Zmodel/dockInfo.js +6 -0
  55. package/dist/roborockCommunication/Zmodel/home.js +1 -0
  56. package/dist/roborockCommunication/Zmodel/homeInfo.js +1 -0
  57. package/dist/roborockCommunication/Zmodel/map.js +1 -0
  58. package/dist/roborockCommunication/Zmodel/mapInfo.js +29 -0
  59. package/dist/roborockCommunication/Zmodel/messageResult.js +7 -0
  60. package/dist/roborockCommunication/Zmodel/multipleMap.js +1 -0
  61. package/dist/roborockCommunication/Zmodel/networkInfo.js +1 -0
  62. package/dist/roborockCommunication/Zmodel/product.js +1 -0
  63. package/dist/roborockCommunication/Zmodel/room.js +1 -0
  64. package/dist/roborockCommunication/Zmodel/roomInfo.js +22 -0
  65. package/dist/roborockCommunication/Zmodel/scene.js +16 -0
  66. package/dist/roborockCommunication/Zmodel/userData.js +1 -0
  67. package/dist/roborockCommunication/Zmodel/vacuumError.js +27 -0
  68. package/dist/roborockCommunication/broadcast/abstractClient.js +55 -0
  69. package/dist/roborockCommunication/broadcast/client/LocalNetworkClient.js +174 -0
  70. package/dist/roborockCommunication/broadcast/client/LocalNetworkUDPClient.js +129 -0
  71. package/dist/roborockCommunication/broadcast/client/MQTTClient.js +139 -0
  72. package/dist/roborockCommunication/broadcast/client.js +1 -0
  73. package/dist/roborockCommunication/broadcast/clientRouter.js +82 -0
  74. package/dist/roborockCommunication/broadcast/listener/abstractConnectionListener.js +1 -0
  75. package/dist/roborockCommunication/broadcast/listener/abstractMessageHandler.js +1 -0
  76. package/dist/roborockCommunication/broadcast/listener/abstractMessageListener.js +1 -0
  77. package/dist/roborockCommunication/broadcast/listener/implementation/chainedConnectionListener.js +26 -0
  78. package/dist/roborockCommunication/broadcast/listener/implementation/chainedMessageListener.js +11 -0
  79. package/dist/roborockCommunication/broadcast/listener/implementation/connectionStateListener.js +43 -0
  80. package/dist/roborockCommunication/broadcast/listener/implementation/generalSyncMessageListener.js +28 -0
  81. package/dist/roborockCommunication/broadcast/listener/implementation/simpleMessageListener.js +27 -0
  82. package/dist/roborockCommunication/broadcast/listener/implementation/syncMessageListener.js +33 -0
  83. package/dist/roborockCommunication/broadcast/listener/index.js +1 -0
  84. package/dist/roborockCommunication/broadcast/messageProcessor.js +148 -0
  85. package/dist/roborockCommunication/broadcast/model/contentMessage.js +1 -0
  86. package/dist/roborockCommunication/broadcast/model/dps.js +1 -0
  87. package/dist/roborockCommunication/broadcast/model/headerMessage.js +1 -0
  88. package/dist/roborockCommunication/broadcast/model/messageContext.js +37 -0
  89. package/dist/roborockCommunication/broadcast/model/protocol.js +28 -0
  90. package/dist/roborockCommunication/broadcast/model/requestMessage.js +38 -0
  91. package/dist/roborockCommunication/broadcast/model/responseMessage.js +14 -0
  92. package/dist/roborockCommunication/helper/chunkBuffer.js +17 -0
  93. package/dist/roborockCommunication/helper/cryptoHelper.js +23 -0
  94. package/dist/roborockCommunication/helper/messageDeserializer.js +98 -0
  95. package/dist/roborockCommunication/helper/messageSerializer.js +84 -0
  96. package/dist/roborockCommunication/helper/nameDecoder.js +66 -0
  97. package/dist/roborockCommunication/helper/sequence.js +16 -0
  98. package/dist/roborockCommunication/index.js +13 -0
  99. package/dist/roborockService.js +494 -0
  100. package/dist/runtimes/handleCloudMessage.js +110 -0
  101. package/dist/runtimes/handleHomeDataMessage.js +57 -0
  102. package/dist/runtimes/handleLocalMessage.js +169 -0
  103. package/dist/rvc.js +51 -0
  104. package/dist/settings.js +1 -0
  105. package/dist/share/function.js +93 -0
  106. package/dist/share/runtimeHelper.js +17 -0
  107. package/dist/tests/testData/mockData.js +359 -0
  108. package/eslint.config.js +80 -0
  109. package/jest.config.js +22 -0
  110. package/jest.setup.js +2 -0
  111. package/logo.png +0 -0
  112. package/matterbridge-roborock-vacuum-plugin.config.json +46 -0
  113. package/matterbridge-roborock-vacuum-plugin.schema.json +293 -0
  114. package/misc/status.md +119 -0
  115. package/package.json +111 -0
  116. package/prettier.config.js +49 -0
  117. package/screenshot/IMG_1.PNG +0 -0
  118. package/screenshot/IMG_2.PNG +0 -0
  119. package/screenshot/IMG_3.PNG +0 -0
  120. package/screenshot/IMG_4.PNG +0 -0
  121. package/screenshot/IMG_5.PNG +0 -0
  122. package/screenshot/IMG_6.PNG +0 -0
  123. package/screenshot/IMG_7.PNG +0 -0
  124. package/src/behaviorFactory.ts +41 -0
  125. package/src/behaviors/BehaviorDeviceGeneric.ts +31 -0
  126. package/src/behaviors/roborock.vacuum/default/default.ts +238 -0
  127. package/src/behaviors/roborock.vacuum/default/initalData.ts +152 -0
  128. package/src/behaviors/roborock.vacuum/default/runtimes.ts +23 -0
  129. package/src/behaviors/roborock.vacuum/smart/initalData.ts +20 -0
  130. package/src/behaviors/roborock.vacuum/smart/runtimes.ts +15 -0
  131. package/src/behaviors/roborock.vacuum/smart/smart.ts +159 -0
  132. package/src/clientManager.ts +23 -0
  133. package/src/helper.ts +97 -0
  134. package/src/index.ts +16 -0
  135. package/src/initialData/getBatteryStatus.ts +26 -0
  136. package/src/initialData/getOperationalStates.ts +94 -0
  137. package/src/initialData/getSupportedAreas.ts +162 -0
  138. package/src/initialData/getSupportedCleanModes.ts +22 -0
  139. package/src/initialData/getSupportedRunModes.ts +14 -0
  140. package/src/initialData/getSupportedScenes.ts +32 -0
  141. package/src/initialData/index.ts +6 -0
  142. package/src/model/CloudMessageModel.ts +11 -0
  143. package/src/model/DockingStationStatus.ts +41 -0
  144. package/src/model/ExperimentalFeatureSetting.ts +77 -0
  145. package/src/model/RoomMap.ts +38 -0
  146. package/src/model/roomIndexMap.ts +26 -0
  147. package/src/notifyMessageTypes.ts +8 -0
  148. package/src/platform.ts +424 -0
  149. package/src/platformRunner.ts +103 -0
  150. package/src/roborockCommunication/RESTAPI/roborockAuthenticateApi.ts +302 -0
  151. package/src/roborockCommunication/RESTAPI/roborockIoTApi.ts +107 -0
  152. package/src/roborockCommunication/Zenum/additionalPropCode.ts +3 -0
  153. package/src/roborockCommunication/Zenum/authenticateResponseCode.ts +6 -0
  154. package/src/roborockCommunication/Zenum/dockType.ts +3 -0
  155. package/src/roborockCommunication/Zenum/operationStatusCode.ts +43 -0
  156. package/src/roborockCommunication/Zenum/vacuumAndDockErrorCode.ts +68 -0
  157. package/src/roborockCommunication/Zmodel/apiResponse.ts +3 -0
  158. package/src/roborockCommunication/Zmodel/authenticateFlowState.ts +6 -0
  159. package/src/roborockCommunication/Zmodel/authenticateResponse.ts +5 -0
  160. package/src/roborockCommunication/Zmodel/baseURL.ts +5 -0
  161. package/src/roborockCommunication/Zmodel/batteryMessage.ts +16 -0
  162. package/src/roborockCommunication/Zmodel/device.ts +50 -0
  163. package/src/roborockCommunication/Zmodel/deviceModel.ts +27 -0
  164. package/src/roborockCommunication/Zmodel/deviceSchema.ts +8 -0
  165. package/src/roborockCommunication/Zmodel/deviceStatus.ts +30 -0
  166. package/src/roborockCommunication/Zmodel/dockInfo.ts +9 -0
  167. package/src/roborockCommunication/Zmodel/home.ts +13 -0
  168. package/src/roborockCommunication/Zmodel/homeInfo.ts +5 -0
  169. package/src/roborockCommunication/Zmodel/map.ts +20 -0
  170. package/src/roborockCommunication/Zmodel/mapInfo.ts +54 -0
  171. package/src/roborockCommunication/Zmodel/messageResult.ts +75 -0
  172. package/src/roborockCommunication/Zmodel/multipleMap.ts +8 -0
  173. package/src/roborockCommunication/Zmodel/networkInfo.ts +7 -0
  174. package/src/roborockCommunication/Zmodel/product.ts +9 -0
  175. package/src/roborockCommunication/Zmodel/room.ts +4 -0
  176. package/src/roborockCommunication/Zmodel/roomInfo.ts +30 -0
  177. package/src/roborockCommunication/Zmodel/scene.ts +44 -0
  178. package/src/roborockCommunication/Zmodel/userData.ts +26 -0
  179. package/src/roborockCommunication/Zmodel/vacuumError.ts +35 -0
  180. package/src/roborockCommunication/broadcast/abstractClient.ts +80 -0
  181. package/src/roborockCommunication/broadcast/client/LocalNetworkClient.ts +218 -0
  182. package/src/roborockCommunication/broadcast/client/LocalNetworkUDPClient.ts +157 -0
  183. package/src/roborockCommunication/broadcast/client/MQTTClient.ts +174 -0
  184. package/src/roborockCommunication/broadcast/client.ts +19 -0
  185. package/src/roborockCommunication/broadcast/clientRouter.ts +104 -0
  186. package/src/roborockCommunication/broadcast/listener/abstractConnectionListener.ts +6 -0
  187. package/src/roborockCommunication/broadcast/listener/abstractMessageHandler.ts +11 -0
  188. package/src/roborockCommunication/broadcast/listener/abstractMessageListener.ts +5 -0
  189. package/src/roborockCommunication/broadcast/listener/implementation/chainedConnectionListener.ts +33 -0
  190. package/src/roborockCommunication/broadcast/listener/implementation/chainedMessageListener.ts +16 -0
  191. package/src/roborockCommunication/broadcast/listener/implementation/connectionStateListener.ts +57 -0
  192. package/src/roborockCommunication/broadcast/listener/implementation/generalSyncMessageListener.ts +38 -0
  193. package/src/roborockCommunication/broadcast/listener/implementation/simpleMessageListener.ts +37 -0
  194. package/src/roborockCommunication/broadcast/listener/implementation/syncMessageListener.ts +50 -0
  195. package/src/roborockCommunication/broadcast/listener/index.ts +3 -0
  196. package/src/roborockCommunication/broadcast/messageProcessor.ts +184 -0
  197. package/src/roborockCommunication/broadcast/model/contentMessage.ts +5 -0
  198. package/src/roborockCommunication/broadcast/model/dps.ts +19 -0
  199. package/src/roborockCommunication/broadcast/model/headerMessage.ts +7 -0
  200. package/src/roborockCommunication/broadcast/model/messageContext.ts +53 -0
  201. package/src/roborockCommunication/broadcast/model/protocol.ts +28 -0
  202. package/src/roborockCommunication/broadcast/model/requestMessage.ts +51 -0
  203. package/src/roborockCommunication/broadcast/model/responseMessage.ts +19 -0
  204. package/src/roborockCommunication/helper/chunkBuffer.ts +18 -0
  205. package/src/roborockCommunication/helper/cryptoHelper.ts +30 -0
  206. package/src/roborockCommunication/helper/messageDeserializer.ts +119 -0
  207. package/src/roborockCommunication/helper/messageSerializer.ts +101 -0
  208. package/src/roborockCommunication/helper/nameDecoder.ts +78 -0
  209. package/src/roborockCommunication/helper/sequence.ts +18 -0
  210. package/src/roborockCommunication/index.ts +25 -0
  211. package/src/roborockService.ts +657 -0
  212. package/src/runtimes/handleCloudMessage.ts +134 -0
  213. package/src/runtimes/handleHomeDataMessage.ts +67 -0
  214. package/src/runtimes/handleLocalMessage.ts +209 -0
  215. package/src/rvc.ts +97 -0
  216. package/src/settings.ts +1 -0
  217. package/src/share/function.ts +103 -0
  218. package/src/share/runtimeHelper.ts +23 -0
  219. package/src/tests/behaviors/roborock.vacuum/default/default.test.ts +134 -0
  220. package/src/tests/behaviors/roborock.vacuum/smart/runtimes.test.ts +64 -0
  221. package/src/tests/behaviors/roborock.vacuum/smart/smart.test.ts +215 -0
  222. package/src/tests/helper.test.ts +162 -0
  223. package/src/tests/initialData/getSupportedAreas.test.ts +181 -0
  224. package/src/tests/model/DockingStationStatus.test.ts +39 -0
  225. package/src/tests/platformRunner.test.ts +188 -0
  226. package/src/tests/platformRunner2.test.ts +228 -0
  227. package/src/tests/platformRunner3.test.ts +46 -0
  228. package/src/tests/roborockCommunication/RESTAPI/roborockAuthenticateApi.test.ts +144 -0
  229. package/src/tests/roborockCommunication/RESTAPI/roborockIoTApi.test.ts +106 -0
  230. package/src/tests/roborockCommunication/broadcast/client/LocalNetworkClient.test.ts +189 -0
  231. package/src/tests/roborockCommunication/broadcast/client/MQTTClient.test.ts +208 -0
  232. package/src/tests/roborockCommunication/broadcast/clientRouter.test.ts +168 -0
  233. package/src/tests/roborockCommunication/broadcast/listener/implementation/chainedConnectionListener.test.ts +59 -0
  234. package/src/tests/roborockCommunication/broadcast/listener/implementation/chainedMessageListener.test.ts +46 -0
  235. package/src/tests/roborockCommunication/broadcast/listener/implementation/simpleMessageListener.test.ts +71 -0
  236. package/src/tests/roborockCommunication/broadcast/listener/implementation/syncMessageListener.test.ts +86 -0
  237. package/src/tests/roborockCommunication/broadcast/messageProcessor.test.ts +126 -0
  238. package/src/tests/roborockService.setSelectedAreas.test.ts +61 -0
  239. package/src/tests/roborockService.test.ts +517 -0
  240. package/src/tests/roborockService2.test.ts +69 -0
  241. package/src/tests/roborockService3.test.ts +133 -0
  242. package/src/tests/roborockService4.test.ts +76 -0
  243. package/src/tests/roborockService5.test.ts +79 -0
  244. package/src/tests/runtimes/handleCloudMessage.test.ts +200 -0
  245. package/src/tests/runtimes/handleHomeDataMessage.test.ts +54 -0
  246. package/src/tests/runtimes/handleLocalMessage.test.ts +227 -0
  247. package/src/tests/testData/mockData.ts +370 -0
  248. package/src/tests/testData/mockHomeData-a187.json +286 -0
  249. package/tsconfig.jest.json +21 -0
  250. package/tsconfig.json +37 -0
  251. package/tsconfig.production.json +19 -0
  252. package/tslint.json +9 -0
  253. package/web-for-testing/README.md +47 -0
  254. package/web-for-testing/nodemon.json +7 -0
  255. package/web-for-testing/package-lock.json +6600 -0
  256. package/web-for-testing/package.json +36 -0
  257. package/web-for-testing/src/app.ts +194 -0
  258. package/web-for-testing/tsconfig-ext.json +19 -0
  259. package/web-for-testing/tsconfig.json +23 -0
  260. package/web-for-testing/views/index.ejs +172 -0
  261. package/web-for-testing/watch.mjs +93 -0
@@ -0,0 +1,181 @@
1
+ import { getSupportedAreas } from '../../initialData/getSupportedAreas';
2
+ import { RoomMap } from '../../model/RoomMap';
3
+ import { Room } from '../../roborockCommunication/Zmodel/room';
4
+
5
+ const mockLogger = {
6
+ debug: jest.fn(),
7
+ error: jest.fn(),
8
+ notice: jest.fn(),
9
+ };
10
+
11
+ describe('getSupportedAreas', () => {
12
+ beforeEach(() => {
13
+ jest.clearAllMocks();
14
+ });
15
+
16
+ it('returns default area when rooms and roomMap are empty', () => {
17
+ const { supportedAreas } = getSupportedAreas(
18
+ [
19
+ { id: 2775739, name: 'Garage' },
20
+ { id: 1474466, name: 'Outside' },
21
+ { id: 1459590, name: 'AAA room' },
22
+ { id: 1459587, name: 'BBB’s Room' },
23
+ { id: 1459580, name: 'CCC’s room' },
24
+ { id: 1458155, name: 'Dining room' },
25
+ { id: 1457889, name: 'Bathroom' },
26
+ { id: 1457888, name: 'Living room' },
27
+ { id: 991195, name: 'Downstairs Bathroom' },
28
+ { id: 991190, name: 'Garage Entryway' },
29
+ { id: 991187, name: 'TV Room' },
30
+ { id: 991185, name: 'Bedroom' },
31
+ { id: 612205, name: 'DDD’s Room' },
32
+ { id: 612204, name: 'Upstairs Bathroom' },
33
+ { id: 612202, name: 'Hallway' },
34
+ { id: 612200, name: 'EEE Room' },
35
+ { id: 609148, name: 'Dining Room' },
36
+ { id: 609146, name: 'Kitchen' },
37
+ { id: 609145, name: 'Living Room' },
38
+ ],
39
+ {
40
+ rooms: [
41
+ { id: 16, globalId: 609146, displayName: 'Kitchen' },
42
+ { id: 17, globalId: 1457889, displayName: 'Bathroom' },
43
+ { id: 18, globalId: 612202, displayName: 'Hallway' },
44
+ { id: 19, globalId: 1458155, displayName: 'Dining room' },
45
+ { id: 20, globalId: 1457888, displayName: 'Living room' },
46
+ { id: 21, globalId: 1459580, displayName: 'BBB’s room' },
47
+ { id: 22, globalId: 612205, displayName: 'CCC’s Room' },
48
+ { id: 23, globalId: 1474466, displayName: 'Outside' },
49
+ ],
50
+ } as RoomMap,
51
+ false, // enableMultipleMap
52
+ mockLogger as any,
53
+ );
54
+
55
+ expect(supportedAreas.length).toEqual(8);
56
+ });
57
+
58
+ it('returns default area when rooms and roomMap are empty', () => {
59
+ const { supportedAreas } = getSupportedAreas(
60
+ [
61
+ { id: 11453731, name: 'Living room' },
62
+ { id: 11453727, name: 'Kitchen' },
63
+ { id: 11453415, name: 'Bathroom' },
64
+ { id: 11453409, name: 'Emile’s Room' },
65
+ { id: 11453404, name: 'Nadia’s Room' },
66
+ { id: 11453398, name: 'Hallway' },
67
+ { id: 11453391, name: 'Dining room' },
68
+ { id: 11453384, name: 'Outside' },
69
+ ],
70
+ {
71
+ rooms: [
72
+ { id: 16, globalId: 2775739, displayName: undefined },
73
+ { id: 17, globalId: 991195, displayName: undefined },
74
+ { id: 18, globalId: 991187, displayName: undefined },
75
+ { id: 19, globalId: 991185, displayName: undefined },
76
+ { id: 20, globalId: 991190, displayName: undefined },
77
+ ],
78
+ } as RoomMap,
79
+ false, // enableMultipleMap
80
+ mockLogger as any,
81
+ );
82
+
83
+ expect(supportedAreas.length).toEqual(5);
84
+ });
85
+
86
+ it('returns default area when rooms and roomMap are empty', () => {
87
+ const { supportedAreas } = getSupportedAreas(
88
+ [
89
+ { id: 11453731, name: 'Living room' },
90
+ { id: 11453727, name: 'Kitchen' },
91
+ { id: 11453415, name: 'Bathroom' },
92
+ { id: 11453409, name: 'Emile’s Room' },
93
+ { id: 11453404, name: 'Nadia’s Room' },
94
+ { id: 11453398, name: 'Hallway' },
95
+ { id: 11453391, name: 'Dining room' },
96
+ { id: 11453384, name: 'Outside' },
97
+ ],
98
+ {
99
+ rooms: [
100
+ { id: 16, globalId: 2775739, displayName: undefined },
101
+ { id: 17, globalId: 991195, displayName: undefined },
102
+ { id: 18, globalId: 991187, displayName: undefined },
103
+ { id: 19, globalId: 991185, displayName: undefined },
104
+ { id: 20, globalId: 991190, displayName: undefined },
105
+ ],
106
+ } as RoomMap,
107
+ false, // enableMultipleMap
108
+ mockLogger as any,
109
+ );
110
+
111
+ expect(supportedAreas.length).toEqual(5);
112
+ });
113
+
114
+ it('returns default area when rooms and roomMap are empty', () => {
115
+ const vacuumRooms: Room[] = [
116
+ { id: 11100845, name: 'Kitchen' },
117
+ { id: 11100849, name: 'Study' },
118
+ { id: 11100842, name: 'Living room' },
119
+ { id: 11100847, name: 'Bedroom' },
120
+ { id: 12461114, name: 'Guest bedroom' },
121
+ { id: 12461109, name: 'Master bedroom' },
122
+ { id: 12461111, name: 'Balcony' },
123
+ ];
124
+ const roomMap: RoomMap = {
125
+ rooms: [
126
+ { id: 16, globalId: 2775739, displayName: undefined, alternativeId: '161' },
127
+ { id: 17, globalId: 991195, displayName: undefined, alternativeId: '171' },
128
+ { id: 18, globalId: 991187, displayName: undefined, alternativeId: '181' },
129
+ { id: 19, globalId: 991185, displayName: undefined, alternativeId: '191' },
130
+ { id: 20, globalId: 991190, displayName: undefined, alternativeId: '201' },
131
+ ],
132
+ };
133
+ const { supportedAreas } = getSupportedAreas(vacuumRooms, roomMap, false, mockLogger as any);
134
+
135
+ expect(supportedAreas.length).toEqual(5);
136
+ });
137
+
138
+ it('returns default area when rooms and roomMap are empty', () => {
139
+ const vacuumRooms: Room[] = [
140
+ { id: 11100845, name: 'Kitchen' },
141
+ { id: 11100849, name: 'Study' },
142
+ { id: 11100842, name: 'Living room' },
143
+ { id: 11100847, name: 'Bedroom' },
144
+ { id: 11100842, name: 'Living room' },
145
+ { id: 12461114, name: 'Guest bedroom' },
146
+ { id: 12461109, name: 'Master bedroom' },
147
+ { id: 12461111, name: 'Balcony' },
148
+ ];
149
+ const roomMap: RoomMap = {
150
+ rooms: [
151
+ { id: 1, globalId: 11100845, displayName: 'Kitchen', alternativeId: '114', mapId: 0 },
152
+ { id: 2, globalId: 11100849, displayName: 'Study', alternativeId: '29', mapId: 0 },
153
+ { id: 3, globalId: 11100842, displayName: 'Living room', alternativeId: '36', mapId: 0 },
154
+ { id: 4, globalId: 11100847, displayName: 'Bedroom', alternativeId: '41', mapId: 0 },
155
+ { id: 1, globalId: 11100842, displayName: 'Living room', alternativeId: '16', mapId: 1 },
156
+ { id: 2, globalId: 12461114, displayName: 'Guest bedroom', alternativeId: '23', mapId: 1 },
157
+ { id: 3, globalId: 12461109, displayName: 'Master bedroom', alternativeId: '32', mapId: 1 },
158
+ { id: 4, globalId: 12461111, displayName: 'Balcony', alternativeId: '47', mapId: 1 },
159
+ ],
160
+ mapInfo: [
161
+ {
162
+ id: 0,
163
+ name: 'First Map',
164
+ },
165
+ {
166
+ id: 1,
167
+ name: 'Second Map',
168
+ },
169
+ ],
170
+ };
171
+
172
+ const mockLogger1 = {
173
+ debug: jest.fn(),
174
+ notice: jest.fn(),
175
+ error: jest.fn(),
176
+ };
177
+ const { supportedAreas, supportedMaps } = getSupportedAreas(vacuumRooms, roomMap, true, mockLogger1 as any);
178
+ expect(supportedAreas.length).toEqual(8);
179
+ expect(supportedMaps.length).toEqual(2);
180
+ });
181
+ });
@@ -0,0 +1,39 @@
1
+ import { parseDockingStationStatus, hasDockingStationError, DockingStationStatusType, DockingStationStatus } from '../../model/DockingStationStatus';
2
+
3
+ describe('DockingStationStatus', () => {
4
+ it('should parse docking station status correctly', () => {
5
+ const dss = 2729;
6
+ const status = parseDockingStationStatus(dss);
7
+
8
+ expect(status.cleanFluidStatus).toBe(2);
9
+ expect(status.waterBoxFilterStatus).toBe(2);
10
+ expect(status.dustBagStatus).toBe(2);
11
+ expect(status.dirtyWaterBoxStatus).toBe(2);
12
+ expect(status.clearWaterBoxStatus).toBe(2);
13
+ expect(status.isUpdownWaterReady).toBe(1);
14
+ });
15
+
16
+ it('should detect error in any status field', () => {
17
+ const status: DockingStationStatus = {
18
+ cleanFluidStatus: DockingStationStatusType.OK,
19
+ waterBoxFilterStatus: DockingStationStatusType.OK,
20
+ dustBagStatus: DockingStationStatusType.Error,
21
+ dirtyWaterBoxStatus: DockingStationStatusType.OK,
22
+ clearWaterBoxStatus: DockingStationStatusType.OK,
23
+ isUpdownWaterReady: DockingStationStatusType.OK,
24
+ };
25
+ expect(hasDockingStationError(status)).toBe(true);
26
+ });
27
+
28
+ it('should return true if error in any field', () => {
29
+ const status: DockingStationStatus = {
30
+ cleanFluidStatus: 2,
31
+ waterBoxFilterStatus: 2,
32
+ dustBagStatus: 2,
33
+ dirtyWaterBoxStatus: 2,
34
+ clearWaterBoxStatus: 1, // This means there is a problem with the clear water box
35
+ isUpdownWaterReady: 1, // This means there is a problem with the updown water
36
+ };
37
+ expect(hasDockingStationError(status)).toBe(true);
38
+ });
39
+ });
@@ -0,0 +1,188 @@
1
+ import { PlatformRunner } from '../platformRunner';
2
+ import { NotifyMessageTypes } from '../notifyMessageTypes';
3
+ import { RoborockMatterbridgePlatform } from '../platform';
4
+ import { Home } from '../roborockCommunication';
5
+ import fs from 'node:fs';
6
+ import { fileURLToPath } from 'node:url';
7
+ import path from 'node:path';
8
+ import { RoborockVacuumCleaner } from '../rvc';
9
+ import * as initialDataIndex from '../initialData/index';
10
+ import { RvcOperationalState } from 'matterbridge/matter/clusters';
11
+
12
+ const __filename = fileURLToPath(import.meta.url);
13
+ const __dirname = path.dirname(__filename);
14
+ let getOperationalErrorState = jest.fn().mockReturnValue(2);
15
+
16
+ jest.mock('./src/initialData/index', () => ({
17
+ ...initialDataIndex,
18
+ getOperationalErrorState,
19
+ }));
20
+
21
+ describe('PlatformRunner.updateRobot', () => {
22
+ let platform: RoborockMatterbridgePlatform;
23
+ let runner: PlatformRunner;
24
+ let robotMock: any;
25
+
26
+ beforeEach(() => {
27
+ robotMock = {
28
+ updateAttribute: jest.fn(),
29
+ getAttribute: jest.fn(),
30
+ device: {
31
+ data: { model: 'test-model' },
32
+ duid: '123456',
33
+ rooms: [],
34
+ },
35
+ serialNumber: '123456',
36
+ dockStationStatus: undefined,
37
+ roomInfo: undefined,
38
+ };
39
+
40
+ const robots = new Map<string, RoborockVacuumCleaner>();
41
+ robots.set('123456', robotMock);
42
+
43
+ platform = {
44
+ robots: robots,
45
+ log: {
46
+ error: jest.fn(),
47
+ debug: jest.fn(),
48
+ notice: jest.fn(),
49
+ },
50
+ enableExperimentalFeature: undefined,
51
+ } as unknown as RoborockMatterbridgePlatform;
52
+
53
+ runner = new PlatformRunner(platform);
54
+ });
55
+
56
+ it('should set correct attributes from homeData', async () => {
57
+ const homeDataPath = path.join(__dirname, 'testData', 'mockHomeData-a187.json');
58
+ const homeData: Home = JSON.parse(fs.readFileSync(homeDataPath, 'utf-8'));
59
+
60
+ await runner.updateRobot(NotifyMessageTypes.HomeData, homeData);
61
+
62
+ const calls = robotMock.updateAttribute.mock.calls;
63
+
64
+ expect(calls).toEqual(
65
+ expect.arrayContaining([
66
+ expect.arrayContaining([
67
+ expect.any(String), // Cluster.id
68
+ 'batPercentRemaining',
69
+ expect.any(Number),
70
+ expect.any(Object),
71
+ ]),
72
+ expect.arrayContaining([expect.any(String), 'currentMode', expect.any(Number), expect.any(Object)]),
73
+ expect.arrayContaining([expect.any(String), 'operationalState', expect.any(Number), expect.any(Object)]),
74
+ expect.arrayContaining([expect.any(String), 'batChargeState', expect.any(Number), expect.any(Object)]),
75
+ ]),
76
+ );
77
+ expect(platform.log.error).not.toHaveBeenCalled();
78
+ });
79
+
80
+ it('should log error if robot not found in homeData', async () => {
81
+ const homeData: Home = {
82
+ devices: [{ duid: 'notfound', data: {}, productId: 1 }],
83
+ products: [],
84
+ } as any;
85
+
86
+ await runner.updateRobot(NotifyMessageTypes.HomeData, homeData);
87
+
88
+ expect(platform.log.error).not.toHaveBeenCalledWith(expect.stringContaining('Error5'));
89
+ // No updateAttribute should be called
90
+ expect(robotMock.updateAttribute).not.toHaveBeenCalled();
91
+ });
92
+
93
+ it('should log error if device data is undefined', async () => {
94
+ robotMock.device.data = undefined;
95
+ const homeData: Home = {
96
+ devices: [{ duid: '123456', data: {}, productId: 1 }],
97
+ products: [],
98
+ } as any;
99
+
100
+ await runner.updateRobot(NotifyMessageTypes.HomeData, homeData);
101
+
102
+ expect(platform.log.error).toHaveBeenCalledWith('Device not found in home data');
103
+ expect(robotMock.updateAttribute).not.toHaveBeenCalled();
104
+ });
105
+
106
+ it('should not update attributes if state or matterState is missing', async () => {
107
+ robotMock.device.data = { model: 'test-model' };
108
+ robotMock.serialNumber = '123456';
109
+ const homeData: Home = {
110
+ devices: [
111
+ {
112
+ duid: '123456',
113
+ data: {
114
+ model: 'test-model',
115
+ },
116
+ productId: 1,
117
+ deviceStatus: {
118
+ 122: 50,
119
+ },
120
+ },
121
+ ],
122
+ products: [
123
+ {
124
+ id: 1,
125
+ model: 'test-model',
126
+ schema: [
127
+ {
128
+ id: 122,
129
+ name: '设备电量',
130
+ code: 'battery',
131
+ mode: 'ro',
132
+ type: 'ENUM',
133
+ property: '{"range": [""]}',
134
+ },
135
+ ],
136
+ },
137
+ ],
138
+ } as any;
139
+
140
+ await runner.updateRobot(NotifyMessageTypes.HomeData, homeData);
141
+
142
+ // Only battery attributes should be set, not currentMode/operationalState
143
+ expect(robotMock.updateAttribute).toHaveBeenCalledWith(expect.any(Number), 'batPercentRemaining', 100, expect.any(Object));
144
+ expect(robotMock.updateAttribute).toHaveBeenCalledWith(expect.any(Number), 'batChargeLevel', expect.any(Number), expect.any(Object));
145
+ expect(robotMock.updateAttribute).not.toHaveBeenCalledWith(expect.any(Number), 'currentMode', expect.anything(), expect.any(Object));
146
+ expect(robotMock.updateAttribute).not.toHaveBeenCalledWith(expect.any(Number), 'operationalState', expect.anything(), expect.any(Object));
147
+ });
148
+
149
+ it('should handle NotifyMessageTypes.BatteryUpdate', async () => {
150
+ const batteryMessage = { percentage: 60 };
151
+ robotMock.device.data = { model: 'test-model' };
152
+ robotMock.serialNumber = '123456';
153
+ await runner['updateFromMQTTMessage'](NotifyMessageTypes.BatteryUpdate, batteryMessage, '123456');
154
+
155
+ expect(robotMock.updateAttribute).toHaveBeenCalledWith(expect.any(Number), 'batPercentRemaining', 120, expect.any(Object));
156
+ expect(robotMock.updateAttribute).toHaveBeenCalledWith(expect.any(Number), 'batChargeLevel', expect.any(Number), expect.any(Object));
157
+ });
158
+
159
+ it('should handle NotifyMessageTypes.ErrorOccurred', async () => {
160
+ const errorMessage = { errorCode: 1 };
161
+ robotMock.device.data = { model: 'test-model' };
162
+ robotMock.serialNumber = '123456';
163
+ getOperationalErrorState = jest.fn().mockReturnValue(RvcOperationalState.OperationalState.Error);
164
+
165
+ await runner['updateFromMQTTMessage'](NotifyMessageTypes.ErrorOccurred, errorMessage, '123456');
166
+
167
+ expect(platform.log.error).toHaveBeenCalledWith('Error occurred: 1');
168
+ expect(robotMock.updateAttribute).toHaveBeenCalledWith(expect.any(Number), 'operationalState', 3, expect.any(Object));
169
+ });
170
+
171
+ it('should log error if robot not found for MQTT message', async () => {
172
+ await runner['updateFromMQTTMessage'](NotifyMessageTypes.BatteryUpdate, { percentage: 50 }, 'notfound');
173
+ expect(platform.log.error).toHaveBeenCalledWith('Error1: Robot with DUID notfound not found');
174
+ });
175
+
176
+ it('should log error if device data is undefined for MQTT message', async () => {
177
+ robotMock.device.data = undefined;
178
+ await runner['updateFromMQTTMessage'](NotifyMessageTypes.BatteryUpdate, { percentage: 50 }, '123456');
179
+ expect(platform.log.error).toHaveBeenCalledWith('Device data is undefined');
180
+ });
181
+
182
+ it('should log error if robot serial number is undefined for MQTT message', async () => {
183
+ robotMock.serialNumber = undefined;
184
+ robotMock.device.data = { model: 'test-model' };
185
+ await runner['updateFromMQTTMessage'](NotifyMessageTypes.BatteryUpdate, { percentage: 50 }, '123456');
186
+ expect(platform.log.error).toHaveBeenCalledWith('Robot serial number is undefined');
187
+ });
188
+ });
@@ -0,0 +1,228 @@
1
+ import { getRoomMapFromDevice } from '../helper';
2
+ import { RoomMap } from '../model/RoomMap';
3
+ import { MapInfo } from '../roborockCommunication';
4
+
5
+ describe('PlatformRunner.getRoomMapFromDevice', () => {
6
+ let platform: any;
7
+
8
+ beforeEach(() => {
9
+ platform = {
10
+ log: {
11
+ error: jest.fn(),
12
+ debug: jest.fn(),
13
+ notice: jest.fn(),
14
+ },
15
+ roborockService: {
16
+ getRoomMappings: jest.fn(),
17
+ getMapInformation: jest.fn(),
18
+ },
19
+ };
20
+ });
21
+
22
+ it('returns RoomMap with roomData from getRoomMappings if available', async () => {
23
+ const device = {
24
+ duid: 'duid1',
25
+ rooms: [
26
+ { id: 1, name: 'Kitchen' },
27
+ { id: 2, name: 'Study' },
28
+ { id: 3, name: 'Living room' },
29
+ { id: 4, name: 'Bedroom' },
30
+ ],
31
+ };
32
+ const roomData = [
33
+ [1, '11100845', 14],
34
+ [2, '11100849', 9],
35
+ [3, '11100842', 6],
36
+ [4, '11100847', 1],
37
+ ];
38
+
39
+ platform.roborockService.getRoomMappings.mockResolvedValue(roomData);
40
+ platform.roborockService.getMapInformation.mockResolvedValue(undefined);
41
+
42
+ const result = await getRoomMapFromDevice(device as any, platform);
43
+
44
+ expect(result).toBeInstanceOf(RoomMap);
45
+ expect(result.rooms.length).toEqual(4);
46
+ });
47
+
48
+ it('returns RoomMap with roomData from getMapInformation if available', async () => {
49
+ const device = {
50
+ duid: 'duid1',
51
+ rooms: [
52
+ { id: 1, name: 'Kitchen' },
53
+ { id: 2, name: 'Study' },
54
+ { id: 3, name: 'Living room' },
55
+ { id: 4, name: 'Bedroom' },
56
+ ],
57
+ };
58
+
59
+ const mapInfo = new MapInfo({
60
+ max_multi_map: 1,
61
+ max_bak_map: 1,
62
+ multi_map_count: 1,
63
+ map_info: [
64
+ {
65
+ mapFlag: 0,
66
+ add_time: 1753511673,
67
+ length: 9,
68
+ name: 'First Map',
69
+ bak_maps: [{ mapFlag: 4, add_time: 1753578164 }],
70
+ rooms: [
71
+ { id: 1, tag: 14, iot_name_id: '11100845', iot_name: 'Kitchen' },
72
+ { id: 2, tag: 9, iot_name_id: '11100849', iot_name: 'Study' },
73
+ {
74
+ id: 3,
75
+ tag: 6,
76
+ iot_name_id: '11100842',
77
+ iot_name: 'Living room',
78
+ },
79
+ { id: 4, tag: 1, iot_name_id: '11100847', iot_name: 'Bedroom' },
80
+ ],
81
+ },
82
+ {
83
+ mapFlag: 1,
84
+ add_time: 1753579596,
85
+ length: 10,
86
+ name: 'Second Map',
87
+ bak_maps: [{ mapFlag: 5, add_time: 1753578579 }],
88
+ rooms: [
89
+ {
90
+ id: 1,
91
+ tag: 6,
92
+ iot_name_id: '11100842',
93
+ iot_name: 'Living room',
94
+ },
95
+ {
96
+ id: 2,
97
+ tag: 3,
98
+ iot_name_id: '12461114',
99
+ iot_name: 'Guest bedroom',
100
+ },
101
+ {
102
+ id: 3,
103
+ tag: 2,
104
+ iot_name_id: '12461109',
105
+ iot_name: 'Master bedroom',
106
+ },
107
+ { id: 4, tag: 7, iot_name_id: '12461111', iot_name: 'Balcony' },
108
+ ],
109
+ },
110
+ ],
111
+ });
112
+
113
+ platform.roborockService.getRoomMappings.mockResolvedValue(undefined);
114
+ platform.roborockService.getMapInformation.mockResolvedValue(mapInfo);
115
+
116
+ const result = await getRoomMapFromDevice(device as any, platform);
117
+ expect(result).toBeInstanceOf(RoomMap);
118
+ expect(result.rooms.length).toEqual(4);
119
+
120
+ platform.enableExperimentalFeature = {
121
+ enableExperimentalFeature: true,
122
+ advancedFeature: {
123
+ enableMultipleMap: true,
124
+ },
125
+ };
126
+
127
+ const result1 = await getRoomMapFromDevice(device as any, platform);
128
+ expect(result1).toBeInstanceOf(RoomMap);
129
+ expect(result1.rooms.length).toEqual(8);
130
+ });
131
+
132
+ it('returns RoomMap with empty roomData from getMapInformation if available', async () => {
133
+ const device = {
134
+ duid: 'duid1',
135
+ rooms: [
136
+ { id: 1, name: 'Kitchen' },
137
+ { id: 2, name: 'Study' },
138
+ { id: 3, name: 'Living room' },
139
+ { id: 4, name: 'Bedroom' },
140
+ ],
141
+ };
142
+
143
+ const mapInfo = new MapInfo({
144
+ max_multi_map: 4,
145
+ max_bak_map: 0,
146
+ multi_map_count: 1,
147
+ map_info: [
148
+ {
149
+ mapFlag: 0,
150
+ add_time: 1753731408,
151
+ length: 0,
152
+ name: '',
153
+ bak_maps: [],
154
+ },
155
+ ],
156
+ });
157
+
158
+ platform.roborockService.getRoomMappings.mockResolvedValue(undefined);
159
+ platform.roborockService.getMapInformation.mockResolvedValue(mapInfo);
160
+
161
+ const result = await getRoomMapFromDevice(device as any, platform);
162
+ expect(result).toBeInstanceOf(RoomMap);
163
+ expect(result.rooms.length).toEqual(0);
164
+
165
+ platform.enableExperimentalFeature = {
166
+ enableExperimentalFeature: true,
167
+ advancedFeature: {
168
+ enableMultipleMap: true,
169
+ },
170
+ };
171
+
172
+ const result1 = await getRoomMapFromDevice(device as any, platform);
173
+ expect(result1).toBeInstanceOf(RoomMap);
174
+ expect(result1.rooms.length).toEqual(0);
175
+ });
176
+
177
+ it('returns RoomMap with roomData from getMapInformation if available', async () => {
178
+ const device = {
179
+ duid: 'duid1',
180
+ rooms: [
181
+ { id: 1, name: 'Kitchen' },
182
+ { id: 2, name: 'Study' },
183
+ { id: 3, name: 'Living room' },
184
+ { id: 4, name: 'Bedroom' },
185
+ ],
186
+ };
187
+
188
+ const mapInfo = new MapInfo({
189
+ max_multi_map: 4,
190
+ max_bak_map: 0,
191
+ multi_map_count: 1,
192
+ map_info: [
193
+ {
194
+ mapFlag: 0,
195
+ add_time: 1753731408,
196
+ length: 0,
197
+ name: '',
198
+ bak_maps: [],
199
+ },
200
+ ],
201
+ });
202
+
203
+ const roomData = [
204
+ [1, '11100845', 14],
205
+ [2, '11100849', 9],
206
+ [3, '11100842', 6],
207
+ [4, '11100847', 1],
208
+ ];
209
+
210
+ platform.roborockService.getRoomMappings.mockResolvedValue(roomData);
211
+ platform.roborockService.getMapInformation.mockResolvedValue(mapInfo);
212
+
213
+ const result = await getRoomMapFromDevice(device as any, platform);
214
+ expect(result).toBeInstanceOf(RoomMap);
215
+ expect(result.rooms.length).toEqual(4);
216
+
217
+ platform.enableExperimentalFeature = {
218
+ enableExperimentalFeature: true,
219
+ advancedFeature: {
220
+ enableMultipleMap: true,
221
+ },
222
+ };
223
+
224
+ const result1 = await getRoomMapFromDevice(device as any, platform);
225
+ expect(result1).toBeInstanceOf(RoomMap);
226
+ expect(result1.rooms.length).toEqual(4);
227
+ });
228
+ });
@@ -0,0 +1,46 @@
1
+ import { PlatformRunner } from '../platformRunner';
2
+ import { NotifyMessageTypes } from '../notifyMessageTypes';
3
+ import { RoborockMatterbridgePlatform } from '../platform';
4
+ import { RoborockVacuumCleaner } from '../rvc';
5
+
6
+ describe('PlatformRunner.updateRobot', () => {
7
+ let platform: RoborockMatterbridgePlatform;
8
+ let runner: PlatformRunner;
9
+ let robotMock: any;
10
+
11
+ beforeEach(() => {
12
+ robotMock = {
13
+ updateAttribute: jest.fn(),
14
+ getAttribute: jest.fn(),
15
+ device: {
16
+ data: { model: 'test-model' },
17
+ duid: '123456',
18
+ rooms: [],
19
+ },
20
+ serialNumber: '123456',
21
+ dockStationStatus: undefined,
22
+ roomInfo: undefined,
23
+ };
24
+
25
+ const robots = new Map<string, RoborockVacuumCleaner>();
26
+ robots.set('123456', robotMock);
27
+
28
+ platform = {
29
+ robots: robots,
30
+ log: {
31
+ error: jest.fn(),
32
+ debug: jest.fn(),
33
+ notice: jest.fn(),
34
+ },
35
+ enableExperimentalFeature: undefined,
36
+ } as unknown as RoborockMatterbridgePlatform;
37
+
38
+ runner = new PlatformRunner(platform);
39
+ });
40
+
41
+ it('should handle unknown message types gracefully', async () => {
42
+ const mapUpdated = { duid: '123456', dps: { 128: 4 } };
43
+ await runner['updateFromMQTTMessage'](NotifyMessageTypes.CloudMessage, mapUpdated, '123456');
44
+ expect(platform.log.notice).toHaveBeenCalled();
45
+ });
46
+ });