matterbridge-roborock-vacuum-plugin 1.1.0-rc15 → 1.1.0-rc17

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 (43) hide show
  1. package/.github/workflows/build.yml +5 -1
  2. package/.github/workflows/coverage.yml +5 -1
  3. package/CHANGELOG.md +62 -0
  4. package/dist/helper.js +24 -21
  5. package/dist/initialData/getSupportedAreas.js +91 -48
  6. package/dist/model/ExperimentalFeatureSetting.js +1 -0
  7. package/dist/model/RoomMap.js +13 -7
  8. package/dist/model/roomIndexMap.js +17 -0
  9. package/dist/platform.js +7 -5
  10. package/dist/platformRunner.js +32 -5
  11. package/dist/roborockCommunication/Zenum/additionalPropCode.js +4 -0
  12. package/dist/roborockCommunication/Zmodel/mapInfo.js +17 -16
  13. package/dist/roborockCommunication/broadcast/messageProcessor.js +2 -3
  14. package/dist/roborockCommunication/index.js +1 -0
  15. package/dist/roborockService.js +11 -2
  16. package/dist/rvc.js +5 -2
  17. package/matterbridge-roborock-vacuum-plugin.config.json +4 -3
  18. package/matterbridge-roborock-vacuum-plugin.schema.json +42 -10
  19. package/package.json +1 -1
  20. package/src/helper.ts +28 -26
  21. package/src/initialData/getSupportedAreas.ts +115 -45
  22. package/src/model/ExperimentalFeatureSetting.ts +2 -0
  23. package/src/model/RoomMap.ts +33 -15
  24. package/src/model/roomIndexMap.ts +20 -0
  25. package/src/platform.ts +8 -5
  26. package/src/platformRunner.ts +41 -6
  27. package/src/roborockCommunication/Zenum/additionalPropCode.ts +3 -0
  28. package/src/roborockCommunication/Zmodel/map.ts +2 -2
  29. package/src/roborockCommunication/Zmodel/mapInfo.ts +37 -18
  30. package/src/roborockCommunication/Zmodel/multipleMap.ts +2 -2
  31. package/src/roborockCommunication/broadcast/messageProcessor.ts +2 -4
  32. package/src/roborockCommunication/index.ts +1 -0
  33. package/src/roborockService.ts +20 -2
  34. package/src/rvc.ts +7 -2
  35. package/src/tests/helper.test.ts +113 -0
  36. package/src/tests/initialData/getSupportedAreas.test.ts +80 -7
  37. package/src/tests/platformRunner2.test.ts +145 -5
  38. package/src/tests/platformRunner3.test.ts +54 -0
  39. package/src/tests/roborockCommunication/broadcast/messageProcessor.test.ts +2 -7
  40. package/src/tests/roborockService.test.ts +22 -1
  41. package/web-for-testing/package-lock.json +14 -12
  42. package/web-for-testing/package.json +1 -1
  43. package/web-for-testing/src/accountStore.ts +0 -10
@@ -63,17 +63,50 @@ describe('PlatformRunner.getRoomMapFromDevice', () => {
63
63
  map_info: [
64
64
  {
65
65
  mapFlag: 0,
66
- add_time: 1753281130,
67
- length: 0,
68
- name: '',
69
- bak_maps: [{ mapFlag: 4, add_time: 1753187168 }],
66
+ add_time: 1753511673,
67
+ length: 9,
68
+ name: 'First Map',
69
+ bak_maps: [{ mapFlag: 4, add_time: 1753578164 }],
70
70
  rooms: [
71
71
  { id: 1, tag: 14, iot_name_id: '11100845', iot_name: 'Kitchen' },
72
72
  { id: 2, tag: 9, iot_name_id: '11100849', iot_name: 'Study' },
73
- { id: 3, tag: 6, iot_name_id: '11100842', iot_name: 'Living room' },
73
+ {
74
+ id: 3,
75
+ tag: 6,
76
+ iot_name_id: '11100842',
77
+ iot_name: 'Living room',
78
+ },
74
79
  { id: 4, tag: 1, iot_name_id: '11100847', iot_name: 'Bedroom' },
75
80
  ],
76
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
+ },
77
110
  ],
78
111
  });
79
112
 
@@ -81,8 +114,115 @@ describe('PlatformRunner.getRoomMapFromDevice', () => {
81
114
  platform.roborockService.getMapInformation.mockResolvedValue(mapInfo);
82
115
 
83
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
+ });
84
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);
85
214
  expect(result).toBeInstanceOf(RoomMap);
86
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);
87
227
  });
88
228
  });
@@ -0,0 +1,54 @@
1
+ import { PlatformRunner } from '../platformRunner';
2
+ import { NotifyMessageTypes } from '../notifyMessageTypes';
3
+ import { RoborockMatterbridgePlatform } from '../platform';
4
+ import { RoborockVacuumCleaner } from '../rvc';
5
+ import * as initialDataIndex from '../initialData/index';
6
+
7
+ const getOperationalErrorState = jest.fn().mockReturnValue(2);
8
+
9
+ jest.mock('./src/initialData/index', () => ({
10
+ ...initialDataIndex,
11
+ getOperationalErrorState,
12
+ }));
13
+
14
+ describe('PlatformRunner.updateRobot', () => {
15
+ let platform: RoborockMatterbridgePlatform;
16
+ let runner: PlatformRunner;
17
+ let robotMock: any;
18
+
19
+ beforeEach(() => {
20
+ robotMock = {
21
+ updateAttribute: jest.fn(),
22
+ getAttribute: jest.fn(),
23
+ device: {
24
+ data: { model: 'test-model' },
25
+ duid: '123456',
26
+ rooms: [],
27
+ },
28
+ serialNumber: '123456',
29
+ dockStationStatus: undefined,
30
+ roomInfo: undefined,
31
+ };
32
+
33
+ const robots = new Map<string, RoborockVacuumCleaner>();
34
+ robots.set('123456', robotMock);
35
+
36
+ platform = {
37
+ robots: robots,
38
+ log: {
39
+ error: jest.fn(),
40
+ debug: jest.fn(),
41
+ notice: jest.fn(),
42
+ },
43
+ enableExperimentalFeature: undefined,
44
+ } as unknown as RoborockMatterbridgePlatform;
45
+
46
+ runner = new PlatformRunner(platform);
47
+ });
48
+
49
+ it('should handle unknown message types gracefully', async () => {
50
+ const mapUpdated = { duid: '123456', dps: { 128: 4 } };
51
+ await runner['updateFromMQTTMessage'](NotifyMessageTypes.CloudMessage, mapUpdated, '123456');
52
+ expect(platform.log.notice).toHaveBeenCalled();
53
+ });
54
+ });
@@ -1,6 +1,5 @@
1
1
  import { MessageProcessor } from '../../../roborockCommunication/broadcast/messageProcessor';
2
2
  import { DeviceStatus } from '../../../roborockCommunication/Zmodel/deviceStatus';
3
- import { RoomInfo } from '../../../roborockCommunication/Zmodel/roomInfo';
4
3
 
5
4
  describe('MessageProcessor', () => {
6
5
  let mockClient: any;
@@ -51,13 +50,9 @@ describe('MessageProcessor', () => {
51
50
  });
52
51
 
53
52
  it('getRooms should return RoomInfo', async () => {
54
- const rooms = [
55
- { id: 1, name: 'Room1' },
56
- { id: 2, name: 'Room2' },
57
- ];
58
53
  mockClient.get.mockResolvedValue([[1, 2]]);
59
- const result = await processor.getRooms('duid', rooms);
60
- expect(result).toBeInstanceOf(RoomInfo);
54
+ const result = await processor.getRooms('duid');
55
+ expect(result).not.toBeUndefined();
61
56
  });
62
57
 
63
58
  it('gotoDock should call client.send', async () => {
@@ -3,6 +3,7 @@ import { ServiceArea } from 'matterbridge/matter/clusters';
3
3
  import RoborockService from '../roborockService';
4
4
  import { MessageProcessor } from '../roborockCommunication/broadcast/messageProcessor';
5
5
  import { Device, MultipleMap, RequestMessage } from '../roborockCommunication';
6
+ import { RoomIndexMap } from '../model/roomIndexMap';
6
7
 
7
8
  describe('RoborockService - startClean', () => {
8
9
  let roborockService: RoborockService;
@@ -55,7 +56,17 @@ describe('RoborockService - startClean', () => {
55
56
  });
56
57
 
57
58
  it('should return MapInfo if response contains maps', async () => {
58
- const mapData = [{ map_info: [{}] }] as MultipleMap[];
59
+ const mapData = [
60
+ {
61
+ map_info: [
62
+ {
63
+ rooms: [{ id: 1, iot_name_id: 'room1', tag: 0, iot_name: 'Living Room' }],
64
+ mapFlag: 1,
65
+ name: 'Living Room Map',
66
+ },
67
+ ],
68
+ },
69
+ ] as MultipleMap[];
59
70
  mockMessageClient = {
60
71
  get: jest.fn(),
61
72
  };
@@ -272,7 +283,17 @@ describe('RoborockService - basic setters/getters', () => {
272
283
  });
273
284
 
274
285
  it('setSelectedAreas should set selected areas', () => {
286
+ roborockService.setSupportedAreaIndexMap(
287
+ 'duid',
288
+ new RoomIndexMap(
289
+ new Map([
290
+ [1, { roomId: 1, mapId: 0 }],
291
+ [2, { roomId: 2, mapId: 1 }],
292
+ ]),
293
+ ),
294
+ );
275
295
  roborockService.setSelectedAreas('duid', [1, 2]);
296
+
276
297
  expect(roborockService['selectedAreas'].get('duid')).toEqual([1, 2]);
277
298
  expect(mockLogger.debug).toHaveBeenCalledWith('RoborockService - setSelectedAreas', [1, 2]);
278
299
  });
@@ -12,7 +12,7 @@
12
12
  "binary-parser": "^2.2.1",
13
13
  "ejs": "^3.1.10",
14
14
  "express": "^5.1.0",
15
- "matterbridge": "^3.1.4",
15
+ "matterbridge": "^3.1.8",
16
16
  "node-ansi-logger": "^3.0.1"
17
17
  },
18
18
  "devDependencies": {
@@ -2351,9 +2351,9 @@
2351
2351
  "license": "MIT"
2352
2352
  },
2353
2353
  "node_modules/matterbridge": {
2354
- "version": "3.1.4",
2355
- "resolved": "https://registry.npmjs.org/matterbridge/-/matterbridge-3.1.4.tgz",
2356
- "integrity": "sha512-r9Bhy+YMzeneR0hihEeKn4BpFSVJ7oXuKDVLpWPz4fN/51ziBkKIGRARJLPY5HEwHlvUbf9oU3Z4fkQhq70O7w==",
2354
+ "version": "3.1.8",
2355
+ "resolved": "https://registry.npmjs.org/matterbridge/-/matterbridge-3.1.8.tgz",
2356
+ "integrity": "sha512-KYGSw088++v8UBr2+iCewrfuNGbZpr9AzhbNbVpaXGzGkxaonUQ7397ZQ4MRMTmdpsEteOJb4Hcx4IJtXUuZdA==",
2357
2357
  "hasShrinkwrap": true,
2358
2358
  "license": "Apache-2.0",
2359
2359
  "dependencies": {
@@ -2361,13 +2361,15 @@
2361
2361
  "archiver": "7.0.1",
2362
2362
  "express": "5.1.0",
2363
2363
  "glob": "11.0.3",
2364
- "multer": "2.0.1",
2364
+ "multer": "2.0.2",
2365
2365
  "node-ansi-logger": "3.1.1",
2366
2366
  "node-persist-manager": "2.0.0",
2367
2367
  "ws": "8.18.3"
2368
2368
  },
2369
2369
  "bin": {
2370
- "matterbridge": "bin/matterbridge.js"
2370
+ "matterbridge": "bin/matterbridge.js",
2371
+ "mb_coap": "bin/mb_coap.js",
2372
+ "mb_mdns": "bin/mb_mdns.js"
2371
2373
  },
2372
2374
  "engines": {
2373
2375
  "node": ">=18.0.0 <19.0.0 || >=20.0.0 <21.0.0 || >=22.0.0 <23.0.0 || >=24.0.0 <25.0.0"
@@ -2499,9 +2501,9 @@
2499
2501
  }
2500
2502
  },
2501
2503
  "node_modules/matterbridge/node_modules/@noble/curves": {
2502
- "version": "1.9.2",
2503
- "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.9.2.tgz",
2504
- "integrity": "sha512-HxngEd2XUcg9xi20JkwlLCtYwfoFw4JGkuZpT+WlsPD4gB/cxkvTD8fSsoAnphGZhFdZYKeQIPCuFlWPm1uE0g==",
2504
+ "version": "1.9.4",
2505
+ "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.9.4.tgz",
2506
+ "integrity": "sha512-2bKONnuM53lINoDrSmK8qP8W271ms7pygDhZt4SiLOoLwBtoHqeCFi6RG42V8zd3mLHuJFhU/Bmaqo4nX0/kBw==",
2505
2507
  "license": "MIT",
2506
2508
  "dependencies": {
2507
2509
  "@noble/hashes": "1.8.0"
@@ -3627,9 +3629,9 @@
3627
3629
  "license": "MIT"
3628
3630
  },
3629
3631
  "node_modules/matterbridge/node_modules/multer": {
3630
- "version": "2.0.1",
3631
- "resolved": "https://registry.npmjs.org/multer/-/multer-2.0.1.tgz",
3632
- "integrity": "sha512-Ug8bXeTIUlxurg8xLTEskKShvcKDZALo1THEX5E41pYCD2sCVub5/kIRIGqWNoqV6szyLyQKV6mD4QUrWE5GCQ==",
3632
+ "version": "2.0.2",
3633
+ "resolved": "https://registry.npmjs.org/multer/-/multer-2.0.2.tgz",
3634
+ "integrity": "sha512-u7f2xaZ/UG8oLXHvtF/oWTRvT44p9ecwBBqTwgJVq0+4BW1g8OW01TyMEGWBHbyMOYVHXslaut7qEQ1meATXgw==",
3633
3635
  "license": "MIT",
3634
3636
  "dependencies": {
3635
3637
  "append-field": "^1.0.0",
@@ -21,7 +21,7 @@
21
21
  "binary-parser": "^2.2.1",
22
22
  "ejs": "^3.1.10",
23
23
  "express": "^5.1.0",
24
- "matterbridge": "^3.1.4",
24
+ "matterbridge": "^3.1.8",
25
25
  "node-ansi-logger": "^3.0.1"
26
26
  },
27
27
  "devDependencies": {
@@ -1,10 +0,0 @@
1
- import { UserData } from './ext/roborockCommunication/index.js';
2
-
3
- export function getAccountStore(): Map<string, UserData> {
4
- const accountStore = new Map<string, UserData>();
5
-
6
- // Initialize with a default user data if needed
7
- // accountStore.set('defaultUser', new UserData());
8
-
9
- return accountStore;
10
- }