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,36 @@
1
+ {
2
+ "name": "web",
3
+ "version": "1.0.0",
4
+ "description": "",
5
+ "main": "/src/app.ts",
6
+ "scripts": {
7
+ "copy-src": "cd src && rm -rf ext && cd .. && cpx '../src/**/!(*.test).ts' src/ext/",
8
+ "build": "rm -rf dist && tsc",
9
+ "start": "node dist/app.js",
10
+ "dev": "node watch.mjs",
11
+ "dont-run:dev:simple": "npm run build && node dist/app.js",
12
+ "dont-run:dev:watch": "while true; do npm run build && node dist/app.js; echo 'Restarting in 2 seconds...'; sleep 2; done",
13
+ "dont-run:dev:build": "nodemon --watch src --ext ts --exec \"npm run build && node dist/app.js\"",
14
+ "dont-run:dev:macos": "osascript -e 'tell application \"Terminal\" to do script \"cd /Users/rinnguyen/Data/vscode/matterbridge-roborock-vacuum-plugin/webui && npm run dev\"'"
15
+ },
16
+ "keywords": [],
17
+ "author": "",
18
+ "license": "ISC",
19
+ "type": "module",
20
+ "dependencies": {
21
+ "binary-parser": "^2.2.1",
22
+ "ejs": "^3.1.10",
23
+ "express": "^5.1.0",
24
+ "matterbridge": "^3.1.8",
25
+ "node-ansi-logger": "^3.0.1"
26
+ },
27
+ "devDependencies": {
28
+ "@types/express": "^5.0.2",
29
+ "@types/node": "^22.15.21",
30
+ "cpx": "^1.5.0",
31
+ "nodemon": "^3.1.10",
32
+ "ts-node": "^10.9.2",
33
+ "tsx": "^4.19.4",
34
+ "typescript": "^5.8.3"
35
+ }
36
+ }
@@ -0,0 +1,194 @@
1
+ import express, { Request, Response } from 'express';
2
+ import RoborockService from './ext/roborockService.js';
3
+ import ClientManager from './ext/clientManager.js';
4
+ import { AnsiLogger, LogLevel } from 'node-ansi-logger';
5
+ import { Device, RequestMessage, RoborockAuthenticateApi, RoborockIoTApi, UserData } from './ext/roborockCommunication/index.js';
6
+ import axios from 'axios';
7
+ import { Socket } from 'net';
8
+ import { getAccountStore } from './accountStore.js';
9
+
10
+ // Handle unhandled promise rejections
11
+ process.on('unhandledRejection', (reason, promise) => {
12
+ console.error('Unhandled Promise Rejection at:', promise, 'reason:', reason);
13
+ // Application specific logging, throwing an error, or other logic here
14
+ });
15
+
16
+ // Handle uncaught exceptions
17
+ process.on('uncaughtException', (error) => {
18
+ console.error('Uncaught Exception:', error);
19
+ // Application specific logging, throwing an error, or other logic here
20
+ });
21
+
22
+ const app = express();
23
+ const port = 3000;
24
+ const log = new AnsiLogger({ logName: 'Main', logLevel: LogLevel.DEBUG });
25
+
26
+ var roborockService: RoborockService;
27
+ var clientManager: ClientManager;
28
+ var userData: UserData;
29
+ const devices: Device[] = [];
30
+ var usname: string;
31
+ var connected = true;
32
+ var connectedDUID = '';
33
+ var isConnected = false;
34
+
35
+ //--------------------------
36
+ app.set('view engine', 'ejs');
37
+ app.use(express.static('public'));
38
+ app.use(express.json());
39
+
40
+ app.get('/', (req: Request, res: Response) => {
41
+ res.render('index', { title: 'My Express WebUI' });
42
+ });
43
+
44
+ app.post('/login', async (req: Request, res: Response) => {
45
+ const { username, password } = req.body;
46
+ console.warn(`Login attempt with username: ${username}, password: ${password}`);
47
+ if (!username) {
48
+ res.json({ error: 'Username are required' });
49
+ return;
50
+ }
51
+ axios.interceptors.request.use((request: any) => {
52
+ log.notice('Axios Request:', {
53
+ method: request.method,
54
+ url: request.url,
55
+ data: request.data,
56
+ headers: request.headers,
57
+ });
58
+ return request;
59
+ });
60
+
61
+ axios.interceptors.response.use((response: any) => {
62
+ log.notice('Axios Response:', {
63
+ status: response.status,
64
+ data: response.data,
65
+ headers: response.headers,
66
+ url: response.config.url,
67
+ });
68
+ return response;
69
+ });
70
+
71
+ clientManager = new ClientManager(log);
72
+ roborockService = new RoborockService(
73
+ () => new RoborockAuthenticateApi(log, axios),
74
+ (logger, ud) => new RoborockIoTApi(ud, logger),
75
+ 60,
76
+ clientManager,
77
+ log,
78
+ );
79
+
80
+ const userDataMap = getAccountStore();
81
+ const selectedUserData = userDataMap.get(username);
82
+ console.warn(`Selected user data for ${username}:`, JSON.stringify(selectedUserData));
83
+
84
+ try {
85
+ userData = await roborockService.loginWithPassword(
86
+ username,
87
+ password,
88
+ async () => {
89
+ return password.length > 0 ? undefined : selectedUserData;
90
+ },
91
+ async (userData) => {
92
+ console.warn('User data received:', JSON.stringify(userData));
93
+ },
94
+ );
95
+
96
+ const dvs = await roborockService.listDevices(username);
97
+
98
+ devices.push(...dvs);
99
+
100
+ isConnected = false;
101
+
102
+ if (userData) {
103
+ usname = username;
104
+ res.json({ success: true, devices: dvs, userData });
105
+ } else {
106
+ res.json({ success: false, error: 'Invalid username or password' });
107
+ }
108
+ } catch (error) {
109
+ res.json({ success: false, error });
110
+ }
111
+ });
112
+
113
+ app.post('/run', async (req: Request, res: Response) => {
114
+ const { duid, command, props, secure, userData1, device1 } = req.body;
115
+ if (!duid || !command) {
116
+ res.json({ error: 'Nothing to do' });
117
+ return;
118
+ }
119
+ let device = devices.find((x) => x.duid == duid);
120
+ if (!device) {
121
+ res.json({ result: `device not found` });
122
+ return;
123
+ }
124
+
125
+ if (!isConnected) {
126
+ if (connected && duid != connectedDUID && roborockService) {
127
+ roborockService.stopService();
128
+ }
129
+
130
+ await roborockService.initializeMessageClient(usname, device, userData);
131
+
132
+ roborockService.setDeviceNotify(async (messageSource, homeData) => {
133
+ console.warn(`${messageSource}: ${JSON.stringify(homeData)}`);
134
+ });
135
+ await roborockService.initializeMessageClientForLocal(device);
136
+
137
+ roborockService.activateDeviceNotify(device);
138
+
139
+ connected = true;
140
+ connectedDUID = duid;
141
+ isConnected = true;
142
+ }
143
+
144
+ // const map_info = await roborockService.getMapInformation(duid);
145
+ // const rooms = map_info?.maps?.[0]?.rooms ?? [];
146
+ // console.warn(`Rooms: ${JSON.stringify(rooms)}`);
147
+
148
+ //const xx = await roborockService.getRoomIdFromMap(duid);
149
+ //console.warn(`Room ID: ${xx}`);
150
+
151
+ const data = await roborockService.customGet(duid, new RequestMessage({ method: command, params: props, secure }));
152
+ res.json({ command, data, secure: false });
153
+ });
154
+
155
+ app.post('/try-connect', async (req: Request, res: Response) => {
156
+ const ip = req.body.ip ?? '192.168.202.65';
157
+ const port = 58867;
158
+ const socket = new Socket();
159
+ socket.on('connect', () => {
160
+ console.log(ip, 'Connected!');
161
+ setTimeout(() => socket.end(), 3000);
162
+ });
163
+ socket.on('error', (err) => {
164
+ console.error(ip, 'Error:', err);
165
+ });
166
+ socket.connect(port, ip);
167
+ res.json({ result: `ok` });
168
+ });
169
+
170
+ app.post('/disconnect', async (req: Request, resp: Response) => {
171
+ roborockService.stopService();
172
+
173
+ resp.json({
174
+ success: true,
175
+ });
176
+ });
177
+
178
+ app.post('/call-api', async (req: Request, resp: Response) => {
179
+ const { api } = req.body;
180
+ if (!api) {
181
+ resp.json({ error: 'API is required' });
182
+ return;
183
+ }
184
+
185
+ const result = await roborockService.getCustomAPI(api);
186
+ resp.json({
187
+ success: true,
188
+ result,
189
+ });
190
+ });
191
+
192
+ app.listen(port, () => {
193
+ console.log(`WebUI listening at http://localhost:${port}`);
194
+ });
@@ -0,0 +1,19 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ESNext",
4
+ "lib": [
5
+ "ESNext"
6
+ ],
7
+ "module": "NodeNext",
8
+ "moduleResolution": "NodeNext",
9
+ "esModuleInterop": true,
10
+ "moduleDetection": "force",
11
+ "forceConsistentCasingInFileNames": true,
12
+ "strict": true,
13
+ "skipLibCheck": true,
14
+ "outDir": "dist"
15
+ },
16
+ "include": [
17
+ "src/**/*.ts"
18
+ ]
19
+ }
@@ -0,0 +1,23 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ESNext",
4
+ "lib": [
5
+ "ESNext"
6
+ ],
7
+ "module": "NodeNext",
8
+ "moduleResolution": "NodeNext",
9
+ "esModuleInterop": true,
10
+ "moduleDetection": "force",
11
+ "forceConsistentCasingInFileNames": true,
12
+ "strict": true,
13
+ "skipLibCheck": true,
14
+ "outDir": "dist",
15
+ "sourceMap": true,
16
+ "inlineSources": true,
17
+ "declaration": true,
18
+ "declarationMap": true
19
+ },
20
+ "include": [
21
+ "src/**/*.ts"
22
+ ]
23
+ }
@@ -0,0 +1,172 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+
4
+ <head>
5
+ <title>
6
+ <%= title %>
7
+ </title>
8
+ <link rel="stylesheet" href="/style.css">
9
+ </head>
10
+
11
+ <body>
12
+ <h1>
13
+ <%= title %>
14
+ </h1>
15
+
16
+ <!-- Login Section -->
17
+ <section id="loginSection">
18
+ <h2>Login</h2>
19
+ <input type="text" id="username" placeholder="Username" value="abc@gmail.com" />
20
+ <input type="text" id="password" placeholder="Password" value="abc" />
21
+ <button id="loginBtn">Login</button>
22
+ <span id="loginStatus"></span>
23
+ <br><br>
24
+
25
+ </section>
26
+ <section id="tryToConnect">
27
+ <h2>Socket test</h2>
28
+ <br>
29
+ <label for="tryConnectIp">IP to try connect:</label>
30
+ <input type="text" id="tryConnectIp" placeholder="192.168.202.65" />
31
+ <button id="tryConnectBtn">Try to Connect</button>
32
+ <br>
33
+ </section>
34
+
35
+ <!-- Search Section -->
36
+ <section id="execute-command">
37
+ <h2>Execute Command</h2>
38
+ <input type="text" id="duid" placeholder="duid" />
39
+ <input type="text" id="command" placeholder="command" value="get_prop" />
40
+ <input type="text" id="props" placeholder="props" value='["battery"]' />
41
+ <label for="secure">Secure?: </label>
42
+ <input type="checkbox" id="secure" />
43
+ <button id="executeCommand">Submit</button>
44
+ <button id="disconnectBtn" style="margin-left:10px;">Disconnect</button>
45
+ <br>
46
+ </section>
47
+
48
+ <section id="execute-api">
49
+ <h2>Execute API</h2>
50
+ <input type="text" id="api" placeholder="api" value="user/homes/yourHomeId" />
51
+ <button id="executeAPI">Submit</button>
52
+ <br>
53
+ </section>
54
+ <section id="result">
55
+ <pre id="resultId" style="background:#f5f5f5;padding:10px;"></pre>
56
+ </section>
57
+ <script>
58
+ document.getElementById('executeAPI').onclick = async function () {
59
+ // Show spinner and clear result
60
+ let resultArea = document.getElementById('resultId');
61
+ // Remove old spinner if exists
62
+ let oldSpinner = document.getElementById('submitSpinner');
63
+ if (oldSpinner) oldSpinner.remove();
64
+ // Insert spinner above resultArea
65
+ let spinner = document.createElement('div');
66
+ spinner.id = 'submitSpinner';
67
+ spinner.innerHTML = 'šŸ”„ Processing...';
68
+ resultArea.parentNode.insertBefore(spinner, resultArea);
69
+ resultArea.textContent = '';
70
+ const api = document.getElementById('api').value;
71
+
72
+ const response = await fetch('/call-api', {
73
+ method: 'POST',
74
+ headers: { 'Content-Type': 'application/json' },
75
+ body: JSON.stringify({ api })
76
+ });
77
+ const data = await response.json();
78
+ spinner.remove();
79
+ resultArea.innerHTML =
80
+ 'result = <pre>' + JSON.stringify(data, null, 2) + '</pre><br>';
81
+ };
82
+
83
+ // Login handler
84
+ document.getElementById('loginBtn').onclick = async function () {
85
+ // Clear previous result and show spinner
86
+ const loginResult = document.getElementById('resultId');
87
+ loginResult.textContent = '';
88
+ loginResult.innerHTML = '<span id="loginSpinner">šŸ”„ Logging in...</span>';
89
+
90
+ const username = document.getElementById('username').value;
91
+ const password = document.getElementById('password').value;
92
+ const response = await fetch('/login', {
93
+ method: 'POST',
94
+ headers: { 'Content-Type': 'application/json' },
95
+ body: JSON.stringify({ username, password })
96
+ });
97
+ const data = await response.json();
98
+ document.getElementById('loginStatus').textContent = data.success ? 'Login successful!' : (data.error || 'Login failed');
99
+
100
+ // Hide spinner and show result
101
+ if (data.success) {
102
+ loginResult.innerHTML =
103
+ 'devices = <pre>' + JSON.stringify(data.devices, null, 2) + '</pre><br>' +
104
+ 'userData = <pre>' + JSON.stringify(data.userData, null, 2) + '</pre>';
105
+ } else {
106
+ loginResult.innerHTML =
107
+ 'error = <pre>' + JSON.stringify(data.error, null, 2) + '</pre>';
108
+ }
109
+ };
110
+
111
+ // Search handler
112
+ document.getElementById('executeCommand').onclick = async function () {
113
+ // Show spinner and clear result
114
+ let resultArea = document.getElementById('resultId');
115
+ // Remove old spinner if exists
116
+ let oldSpinner = document.getElementById('submitSpinner');
117
+ if (oldSpinner) oldSpinner.remove();
118
+ // Insert spinner above resultArea
119
+ let spinner = document.createElement('div');
120
+ spinner.id = 'submitSpinner';
121
+ spinner.innerHTML = 'šŸ”„ Processing...';
122
+ resultArea.parentNode.insertBefore(spinner, resultArea);
123
+ resultArea.textContent = '';
124
+
125
+ const duid = document.getElementById('duid').value;
126
+ const command = document.getElementById('command').value;
127
+ const props = document.getElementById('props').value;
128
+ const secure = document.getElementById('secure').checked;
129
+ const userData = document.getElementById('userDataField') ? document.getElementById('userDataField').value : '';
130
+ const device = document.getElementById('deviceField') ? document.getElementById('deviceField').value : '';
131
+
132
+ const response = await fetch('/run', {
133
+ method: 'POST',
134
+ headers: { 'Content-Type': 'application/json' },
135
+ body: JSON.stringify({ duid, command, props, secure, userData1: userData, device1: device })
136
+ });
137
+ const data = await response.json();
138
+ // Remove spinner after response
139
+ spinner.remove();
140
+ resultArea.innerHTML =
141
+ 'command = ' + data.command + '</pre><br>' +
142
+ 'secure = ' + data.secure + '</pre><br>' +
143
+ 'result = <pre>' + JSON.stringify(data.data, null, 2) + '</pre><br>';
144
+ };
145
+
146
+ document.getElementById('disconnectBtn').onclick = async function () {
147
+ const response = await fetch('/disconnect', {
148
+ method: 'POST',
149
+ headers: { 'Content-Type': 'application/json' }
150
+ });
151
+ const data = await response.json();
152
+ document.getElementById('resultId').textContent = data.success ? 'Disconnected!' : 'Disconnect failed!';
153
+ };
154
+
155
+ document.getElementById('tryConnectBtn').onclick = async function () {
156
+ const ip = document.getElementById('tryConnectIp').value;
157
+ if (!ip) {
158
+ document.getElementById('resultId').textContent = 'Please enter an IP address!';
159
+ return;
160
+ }
161
+ const response = await fetch('/try-connect', {
162
+ method: 'POST',
163
+ headers: { 'Content-Type': 'application/json' },
164
+ body: JSON.stringify({ ip })
165
+ });
166
+ const data = await response.json();
167
+ document.getElementById('resultId').textContent = data.result || data.error || '';
168
+ };
169
+ </script>
170
+ </body>
171
+
172
+ </html>
@@ -0,0 +1,93 @@
1
+ import { spawn } from 'child_process';
2
+ import { watch } from 'fs';
3
+ import path from 'path';
4
+ import { fileURLToPath } from 'url';
5
+
6
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
7
+
8
+ let isBuilding = false;
9
+ let currentProcess = null;
10
+
11
+ function debounce(func, wait) {
12
+ let timeout;
13
+ return function executedFunction(...args) {
14
+ const later = () => {
15
+ clearTimeout(timeout);
16
+ func(...args);
17
+ };
18
+ clearTimeout(timeout);
19
+ timeout = setTimeout(later, wait);
20
+ };
21
+ }
22
+
23
+ function killCurrentProcess() {
24
+ if (currentProcess && !currentProcess.killed) {
25
+ currentProcess.kill('SIGINT');
26
+ currentProcess = null;
27
+ }
28
+ }
29
+
30
+ function buildAndRun() {
31
+ if (isBuilding) return;
32
+
33
+ isBuilding = true;
34
+ killCurrentProcess();
35
+
36
+ console.log('\nšŸ”„ Building...');
37
+
38
+ const buildProcess = spawn('npm', ['run', 'build'], {
39
+ cwd: __dirname,
40
+ stdio: 'inherit',
41
+ shell: true,
42
+ });
43
+
44
+ buildProcess.on('close', (code) => {
45
+ isBuilding = false;
46
+ if (code === 0) {
47
+ console.log('āœ… Build successful, starting app...');
48
+ currentProcess = spawn('node', ['dist/app.js'], {
49
+ cwd: __dirname,
50
+ stdio: 'inherit',
51
+ shell: true,
52
+ });
53
+
54
+ currentProcess.on('close', (appCode) => {
55
+ if (appCode !== 0 && appCode !== null) {
56
+ console.log(`āŒ App exited with code ${appCode}`);
57
+ }
58
+ });
59
+ } else {
60
+ console.log(`āŒ Build failed with code ${code}`);
61
+ }
62
+ });
63
+ }
64
+
65
+ // Initial build and run
66
+ buildAndRun();
67
+
68
+ // Watch for file changes
69
+ const debouncedBuildAndRun = debounce(buildAndRun, 500);
70
+
71
+ console.log('šŸ‘€ Watching src/ directory for changes...');
72
+
73
+ const watcher = watch(path.join(__dirname, 'src'), { recursive: true }, (eventType, filename) => {
74
+ if (filename && filename.endsWith('.ts')) {
75
+ console.log(`šŸ“ File changed: ${filename}`);
76
+ debouncedBuildAndRun();
77
+ }
78
+ });
79
+
80
+ // Handle process termination
81
+ process.on('SIGINT', () => {
82
+ console.log('\nšŸ›‘ Stopping watcher...');
83
+ watcher.close();
84
+ killCurrentProcess();
85
+ process.exit(0);
86
+ });
87
+
88
+ process.on('SIGTERM', () => {
89
+ console.log('\nšŸ›‘ Stopping watcher...');
90
+ watcher.close();
91
+ killCurrentProcess();
92
+ process.exit(0);
93
+ });