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

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 (33) 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 +6 -6
  5. package/dist/initialData/getSupportedAreas.js +12 -8
  6. package/dist/model/RoomMap.js +7 -6
  7. package/dist/platform.js +2 -2
  8. package/dist/platformRunner.js +22 -3
  9. package/dist/roborockCommunication/Zenum/additionalPropCode.js +4 -0
  10. package/dist/roborockCommunication/Zmodel/mapInfo.js +18 -16
  11. package/dist/roborockCommunication/broadcast/messageProcessor.js +2 -3
  12. package/dist/roborockCommunication/index.js +1 -0
  13. package/dist/roborockService.js +1 -1
  14. package/matterbridge-roborock-vacuum-plugin.config.json +1 -1
  15. package/matterbridge-roborock-vacuum-plugin.schema.json +1 -1
  16. package/package.json +1 -1
  17. package/src/helper.ts +6 -7
  18. package/src/initialData/getSupportedAreas.ts +28 -20
  19. package/src/model/RoomMap.ts +17 -13
  20. package/src/platform.ts +2 -2
  21. package/src/platformRunner.ts +27 -4
  22. package/src/roborockCommunication/Zenum/additionalPropCode.ts +3 -0
  23. package/src/roborockCommunication/Zmodel/map.ts +1 -1
  24. package/src/roborockCommunication/Zmodel/mapInfo.ts +36 -18
  25. package/src/roborockCommunication/Zmodel/multipleMap.ts +2 -2
  26. package/src/roborockCommunication/broadcast/messageProcessor.ts +2 -4
  27. package/src/roborockCommunication/index.ts +1 -0
  28. package/src/roborockService.ts +4 -1
  29. package/src/tests/helper.test.ts +113 -0
  30. package/src/tests/platformRunner2.test.ts +39 -6
  31. package/src/tests/platformRunner3.test.ts +54 -0
  32. package/src/tests/roborockCommunication/broadcast/messageProcessor.test.ts +2 -7
  33. package/src/tests/roborockService.test.ts +11 -1
@@ -1,6 +1,10 @@
1
1
  name: Build, lint and test
2
2
 
3
- on: [push, pull_request]
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+ pull_request:
4
8
 
5
9
  jobs:
6
10
  publish:
@@ -1,6 +1,10 @@
1
1
  name: Analyze code coverage
2
2
 
3
- on: [push, pull_request]
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+ - dev
4
8
 
5
9
  jobs:
6
10
  publish:
package/CHANGELOG.md ADDED
@@ -0,0 +1,62 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ ---
6
+
7
+ ## [1.1.0-rc15] - 2025-07-26
8
+ ### Changed
9
+ - Refactor codebase.
10
+ - Correct LocalNetworkClient connecting process.
11
+ - Update and fix unit tests.
12
+ - Various lint fixes.
13
+
14
+ ## [1.1.0-rc14] - 2025-07-26
15
+ ### Added
16
+ - Initialize sync device status via MQTT.
17
+
18
+ ## [1.1.0-rc13] - 2025-07-25
19
+ ### Added
20
+ - More unit tests.
21
+ - Increased unit test coverage.
22
+
23
+ ### Fixed
24
+ - Several bugs related to socket connection issues.
25
+ - Improve reconnect logic and cleaning status reporting.
26
+
27
+ ## [1.1.0-rc11] - 2025-07-22
28
+ ### Fixed
29
+ - Unable to get room from `get_room_mapping`.
30
+ - Can not get room from `get_multi_maps_list`.
31
+
32
+ ## [1.1.0-rc10] - 2025-07-22
33
+ ### Fixed
34
+ - Stop retrying to connect socket when exceeding retry count.
35
+ - Lint and unit test updates.
36
+
37
+ ## [1.1.0-rc09] - 2025-07-20
38
+ ### Fixed
39
+ - Do not re-connect MQTT unnecessarily.
40
+ - Reconnect when socket or MQTT is unexpectedly disconnected.
41
+
42
+ ### Enhanced
43
+ - Save user data for subsequent logins.
44
+ - Unit test improvements.
45
+
46
+ ## [1.1.0-rc07] - 2025-07-17
47
+ ### Changed
48
+ - Enable server mode fixes.
49
+ - Improved error logging when plugin can't connect to device.
50
+
51
+ ## [1.1.0-rc06] - 2025-07-16
52
+ ### Fixed
53
+ - Can not start plugin when server mode is disabled.
54
+
55
+ ## [1.1.0-rc04] - 2025-07-16
56
+ ### Added
57
+ - Unit tests.
58
+ - GitHub Actions improvements.
59
+
60
+ ---
61
+
62
+ **Note:** This changelog includes only the most recent 30 commits. For the full commit history, view more on [GitHub](https://github.com/RinDevJunior/matterbridge-roborock-vacuum-plugin/commits?sort=updated&direction=desc).
package/dist/helper.js CHANGED
@@ -43,9 +43,9 @@ export async function getRoomMap(duid, platform) {
43
43
  }
44
44
  if (robot.roomInfo === undefined) {
45
45
  const mapInfo = await platform.roborockService.getMapInformation(robot.device.duid);
46
- if (mapInfo && mapInfo.maps && mapInfo.maps.length > 0) {
47
- platform.log.error(`getRoomMap - mapInfo: ${debugStringify(mapInfo.maps)}`);
48
- const roomDataMap = mapInfo.maps[0].rooms.map((r) => [r.id, parseInt(r.iot_name_id), r.tag]);
46
+ if (mapInfo && mapInfo.allRooms && mapInfo.allRooms.length > 0) {
47
+ platform.log.error(`getRoomMap - mapInfo: ${debugStringify(mapInfo.allRooms)}`);
48
+ const roomDataMap = mapInfo.allRooms.map((r) => [r.id, parseInt(r.iot_name_id), r.tag]);
49
49
  robot.roomInfo = new RoomMap(roomDataMap, rooms);
50
50
  }
51
51
  }
@@ -54,7 +54,7 @@ export async function getRoomMap(duid, platform) {
54
54
  export async function getRoomMapFromDevice(device, platform) {
55
55
  const rooms = device?.rooms ?? [];
56
56
  platform.log.notice('-------------------------------------------0--------------------------------------------------------');
57
- platform.log.notice(`getRoomMapFromDevice: ${debugStringify(rooms)}`);
57
+ platform.log.notice(`getRoomMapFromDevice - device.rooms: ${debugStringify(rooms)}`);
58
58
  if (device && platform.roborockService) {
59
59
  const roomData = await platform.roborockService.getRoomMappings(device.duid);
60
60
  if (roomData !== undefined && roomData.length > 0) {
@@ -66,8 +66,8 @@ export async function getRoomMapFromDevice(device, platform) {
66
66
  }
67
67
  const mapInfo = await platform.roborockService.getMapInformation(device.duid);
68
68
  platform.log.notice(`getRoomMapFromDevice - mapInfo: ${mapInfo ? debugStringify(mapInfo) : 'undefined'}`);
69
- if (mapInfo && mapInfo.maps && mapInfo.maps.length > 0) {
70
- const roomDataMap = mapInfo.maps[0].rooms.map((r) => [r.id, parseInt(r.iot_name_id), r.tag]);
69
+ if (mapInfo && mapInfo.allRooms && mapInfo.allRooms.length > 0) {
70
+ const roomDataMap = mapInfo.allRooms.map((r) => [r.id, parseInt(r.iot_name_id), r.tag]);
71
71
  const roomMap = new RoomMap(roomDataMap, rooms);
72
72
  platform.log.notice(`getRoomMapFromDevice - roomMap: ${debugStringify(roomMap)}`);
73
73
  platform.log.notice('-------------------------------------------2--------------------------------------------------------');
@@ -3,11 +3,13 @@ import { randomInt } from 'node:crypto';
3
3
  export function getSupportedAreas(vacuumRooms, roomMap, log) {
4
4
  log?.debug('getSupportedAreas-vacuum room', debugStringify(vacuumRooms));
5
5
  log?.debug('getSupportedAreas-roomMap', roomMap ? debugStringify(roomMap) : 'undefined');
6
- if (!vacuumRooms || vacuumRooms.length === 0 || !roomMap?.rooms || roomMap.rooms.length == 0) {
7
- if (!vacuumRooms || vacuumRooms.length === 0) {
6
+ const noVacuumRooms = !vacuumRooms || vacuumRooms.length === 0;
7
+ const noRoomMap = !roomMap?.rooms || roomMap.rooms.length === 0;
8
+ if (noVacuumRooms || noRoomMap) {
9
+ if (noVacuumRooms) {
8
10
  log?.error('No rooms found');
9
11
  }
10
- if (!roomMap || !roomMap.rooms || roomMap.rooms.length == 0) {
12
+ if (noRoomMap) {
11
13
  log?.error('No room map found');
12
14
  }
13
15
  return [
@@ -26,12 +28,13 @@ export function getSupportedAreas(vacuumRooms, roomMap, log) {
26
28
  ];
27
29
  }
28
30
  const supportedAreas = roomMap.rooms.map((room) => {
31
+ const locationName = room.displayName ?? vacuumRooms.find((r) => r.id === room.globalId || r.id === room.id)?.name ?? `Unknown Room ${randomInt(1000, 9999)}`;
29
32
  return {
30
33
  areaId: room.id,
31
34
  mapId: null,
32
35
  areaInfo: {
33
36
  locationInfo: {
34
- locationName: room.displayName ?? vacuumRooms.find((r) => r.id == room.globalId || r.id == room.id)?.name ?? `Unknown Room ${randomInt(1000, 9999)}`,
37
+ locationName,
35
38
  floorNumber: null,
36
39
  areaType: null,
37
40
  },
@@ -41,8 +44,8 @@ export function getSupportedAreas(vacuumRooms, roomMap, log) {
41
44
  });
42
45
  log?.debug('getSupportedAreas - supportedAreas', debugStringify(supportedAreas));
43
46
  const duplicated = findDuplicatedAreaIds(supportedAreas, log);
44
- return duplicated
45
- ? [
47
+ if (duplicated) {
48
+ return [
46
49
  {
47
50
  areaId: 2,
48
51
  mapId: null,
@@ -55,8 +58,9 @@ export function getSupportedAreas(vacuumRooms, roomMap, log) {
55
58
  landmarkInfo: null,
56
59
  },
57
60
  },
58
- ]
59
- : supportedAreas;
61
+ ];
62
+ }
63
+ return supportedAreas;
60
64
  }
61
65
  function findDuplicatedAreaIds(areas, log) {
62
66
  const seen = new Set();
@@ -1,12 +1,13 @@
1
1
  export class RoomMap {
2
- rooms = [];
2
+ rooms;
3
3
  constructor(roomData, rooms) {
4
- this.rooms = roomData.map((entry) => {
4
+ this.rooms = roomData.map(([id, globalId, altId]) => {
5
+ const room = rooms.find((r) => Number(r.id) === Number(globalId) || Number(r.id) === Number(id));
5
6
  return {
6
- id: entry[0],
7
- globalId: Number(entry[1]),
8
- displayName: rooms.find((r) => Number(r.id) == Number(entry[1]))?.name,
9
- alternativeId: `${entry[0]}${entry[2]}`,
7
+ id,
8
+ globalId: globalId !== undefined ? Number(globalId) : undefined,
9
+ displayName: room?.name,
10
+ alternativeId: `${id}${altId}`,
10
11
  };
11
12
  });
12
13
  }
package/dist/platform.js CHANGED
@@ -159,8 +159,8 @@ export class RoborockMatterbridgePlatform extends MatterbridgeDynamicPlatform {
159
159
  if (vacuum.rooms === undefined || vacuum.rooms.length === 0) {
160
160
  this.log.notice(`Fetching map information for device: ${vacuum.name} (${vacuum.duid}) to get rooms`);
161
161
  const map_info = await this.roborockService.getMapInformation(vacuum.duid);
162
- const rooms = map_info?.maps?.[0]?.rooms ?? [];
163
- vacuum.rooms = rooms.map((room) => ({ id: room.id, name: room.displayName }));
162
+ const rooms = map_info?.allRooms ?? [];
163
+ vacuum.rooms = rooms.map((room) => ({ id: room.globalId, name: room.displayName }));
164
164
  }
165
165
  const roomMap = await getRoomMapFromDevice(vacuum, this);
166
166
  this.log.debug('Initializing - roomMap: ', debugStringify(roomMap));
@@ -1,11 +1,12 @@
1
1
  import { RvcRunMode, PowerSource, ServiceArea, RvcOperationalState, RvcCleanMode } from 'matterbridge/matter/clusters';
2
- import { getRoomMap, getVacuumProperty, isStatusUpdate } from './helper.js';
2
+ import { getRoomMap, getRoomMapFromDevice, getVacuumProperty, isStatusUpdate } from './helper.js';
3
3
  import { getRunningMode } from './initialData/getSupportedRunModes.js';
4
4
  import { state_to_matter_operational_status, state_to_matter_state } from './share/function.js';
5
- import { getBatteryState, getBatteryStatus, getOperationalErrorState } from './initialData/index.js';
5
+ import { getBatteryState, getBatteryStatus, getOperationalErrorState, getSupportedAreas } from './initialData/index.js';
6
6
  import { NotifyMessageTypes } from './notifyMessageTypes.js';
7
7
  import { Protocol } from './roborockCommunication/broadcast/model/protocol.js';
8
8
  import { hasDockingStationError, parseDockingStationStatus } from './model/DockingStationStatus.js';
9
+ import { AdditionalPropCode } from './roborockCommunication/index.js';
9
10
  import { OperationStatusCode } from './roborockCommunication/Zenum/operationStatusCode.js';
10
11
  import { getCurrentCleanModeFunc } from './share/runtimeHelper.js';
11
12
  import { debugStringify } from 'matterbridge/logger';
@@ -217,7 +218,25 @@ export class PlatformRunner {
217
218
  });
218
219
  break;
219
220
  }
220
- case Protocol.additional_props:
221
+ case Protocol.additional_props: {
222
+ platform.log.notice(`Received additional properties for robot ${duid}: ${debugStringify(data)}`);
223
+ const propCode = data.dps[Protocol.additional_props];
224
+ platform.log.debug(`DPS for additional properties: ${propCode}, AdditionalPropCode: ${AdditionalPropCode[propCode]}`);
225
+ if (propCode === AdditionalPropCode.map_change) {
226
+ platform.log.notice('------------------------ get roomData ----------------------------');
227
+ const roomMap = await getRoomMapFromDevice(robot.device, platform);
228
+ platform.log.notice('------------------------ Room map updated ------------------------');
229
+ const supportedAreas = getSupportedAreas(robot.device.rooms, roomMap, platform.log);
230
+ platform.log.notice(`Supported areas: ${debugStringify(supportedAreas)}`);
231
+ platform.log.notice('------------------------ Supported areas updated ------------------');
232
+ platform.roborockService?.setSupportedAreas(duid, supportedAreas);
233
+ platform.roborockService?.setSelectedAreas(duid, []);
234
+ robot.updateAttribute(ServiceArea.Cluster.id, 'supportedAreas', supportedAreas, platform.log);
235
+ robot.updateAttribute(ServiceArea.Cluster.id, 'selectedAreas', [], platform.log);
236
+ robot.updateAttribute(ServiceArea.Cluster.id, 'currentArea', null, platform.log);
237
+ }
238
+ break;
239
+ }
221
240
  case Protocol.back_type: {
222
241
  break;
223
242
  }
@@ -0,0 +1,4 @@
1
+ export var AdditionalPropCode;
2
+ (function (AdditionalPropCode) {
3
+ AdditionalPropCode[AdditionalPropCode["map_change"] = 4] = "map_change";
4
+ })(AdditionalPropCode || (AdditionalPropCode = {}));
@@ -1,28 +1,30 @@
1
1
  import decodeComponent from '../helper/nameDecoder.js';
2
2
  export class MapInfo {
3
3
  maps = [];
4
+ allRooms = [];
4
5
  constructor(multimap) {
5
- multimap.map_info.forEach((map) => {
6
- this.maps.push({
7
- id: map.mapFlag,
8
- name: decodeComponent(map.name)?.toLowerCase(),
9
- rooms: map.rooms && map.rooms.length > 0
10
- ? map.rooms.map((room) => {
11
- return {
12
- id: room.id,
13
- iot_name_id: room.iot_name_id,
14
- tag: room.tag,
15
- displayName: room.iot_name,
16
- };
17
- })
18
- : [],
19
- });
6
+ this.maps = multimap.map_info.map((mapInfo) => {
7
+ const rooms = mapInfo.rooms.map((room) => ({
8
+ id: room.id,
9
+ globalId: parseInt(room.iot_name_id),
10
+ iot_name_id: room.iot_name_id,
11
+ tag: room.tag,
12
+ displayName: room.iot_name,
13
+ mapId: mapInfo.mapFlag,
14
+ }));
15
+ this.allRooms.push(...rooms);
16
+ return {
17
+ id: mapInfo.mapFlag,
18
+ name: decodeComponent(mapInfo.name),
19
+ rooms,
20
+ };
20
21
  });
22
+ this.allRooms = this.allRooms.filter((room, index, self) => index === self.findIndex((r) => r.globalId === room.globalId));
21
23
  }
22
24
  getById(id) {
23
25
  return this.maps.find((m) => m.id === id)?.name;
24
26
  }
25
27
  getByName(name) {
26
- return this.maps.find((m) => m.name === name.toLowerCase())?.id;
28
+ return this.maps.find((m) => m.name?.toLowerCase() === name.toLowerCase())?.id;
27
29
  }
28
30
  }
@@ -1,5 +1,4 @@
1
1
  import { debugStringify } from 'matterbridge/logger';
2
- import { RoomInfo } from '../Zmodel/roomInfo.js';
3
2
  import { SimpleMessageListener } from './listener/index.js';
4
3
  import { RequestMessage } from './model/requestMessage.js';
5
4
  import { DeviceStatus } from '../Zmodel/deviceStatus.js';
@@ -40,9 +39,9 @@ export class MessageProcessor {
40
39
  }
41
40
  return undefined;
42
41
  }
43
- async getRooms(duid, rooms) {
42
+ async getRooms(duid) {
44
43
  const request = new RequestMessage({ method: 'get_room_mapping' });
45
- return this.client.get(duid, request).then((response) => new RoomInfo(rooms, response ?? []));
44
+ return this.client.get(duid, request);
46
45
  }
47
46
  async gotoDock(duid) {
48
47
  const request = new RequestMessage({ method: 'app_charge' });
@@ -8,4 +8,5 @@ export { ClientRouter } from './broadcast/clientRouter.js';
8
8
  export { DeviceStatus } from './Zmodel/deviceStatus.js';
9
9
  export { ResponseMessage } from './broadcast/model/responseMessage.js';
10
10
  export { MapInfo } from './Zmodel/mapInfo.js';
11
+ export { AdditionalPropCode } from './Zenum/additionalPropCode.js';
11
12
  export { Scene } from './Zmodel/scene.js';
@@ -351,7 +351,7 @@ export default class RoborockService {
351
351
  assert(this.iotApi !== undefined);
352
352
  return this.iotApi.startScene(sceneId);
353
353
  }
354
- getRoomMappings(duid) {
354
+ async getRoomMappings(duid) {
355
355
  if (!this.messageClient) {
356
356
  this.logger.warn('messageClient not initialized. Waititing for next execution');
357
357
  return Promise.resolve(undefined);
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "matterbridge-roborock-vacuum-plugin",
3
3
  "type": "DynamicPlatform",
4
- "version": "1.1.0-rc15",
4
+ "version": "1.1.0-rc16",
5
5
  "whiteList": [],
6
6
  "blackList": [],
7
7
  "useInterval": true,
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "title": "Matterbridge Roborock Vacuum Plugin",
3
- "description": "matterbridge-roborock-vacuum-plugin v. 1.1.0-rc15 by https://github.com/RinDevJunior",
3
+ "description": "matterbridge-roborock-vacuum-plugin v. 1.1.0-rc16 by https://github.com/RinDevJunior",
4
4
  "type": "object",
5
5
  "required": ["username", "password"],
6
6
  "properties": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "matterbridge-roborock-vacuum-plugin",
3
- "version": "1.1.0-rc15",
3
+ "version": "1.1.0-rc16",
4
4
  "description": "Matterbridge Roborock Vacuum Plugin",
5
5
  "author": "https://github.com/RinDevJunior",
6
6
  "license": "MIT",
package/src/helper.ts CHANGED
@@ -47,7 +47,6 @@ export async function getRoomMap(duid: string, platform: RoborockMatterbridgePla
47
47
  if (platform.roborockService === undefined) return undefined;
48
48
 
49
49
  const rooms = robot.device.rooms ?? [];
50
- // if (platform.robot?.device === undefined || platform.roborockService === undefined) return undefined;
51
50
  if (robot.roomInfo === undefined) {
52
51
  const roomData = await platform.roborockService.getRoomMappings(robot.device.duid);
53
52
  if (roomData !== undefined && roomData.length > 0) {
@@ -58,10 +57,10 @@ export async function getRoomMap(duid: string, platform: RoborockMatterbridgePla
58
57
 
59
58
  if (robot.roomInfo === undefined) {
60
59
  const mapInfo = await platform.roborockService.getMapInformation(robot.device.duid);
61
- if (mapInfo && mapInfo.maps && mapInfo.maps.length > 0) {
62
- platform.log.error(`getRoomMap - mapInfo: ${debugStringify(mapInfo.maps)}`);
60
+ if (mapInfo && mapInfo.allRooms && mapInfo.allRooms.length > 0) {
61
+ platform.log.error(`getRoomMap - mapInfo: ${debugStringify(mapInfo.allRooms)}`);
63
62
 
64
- const roomDataMap = mapInfo.maps[0].rooms.map((r) => [r.id, parseInt(r.iot_name_id), r.tag] as [number, number, number]);
63
+ const roomDataMap = mapInfo.allRooms.map((r) => [r.id, parseInt(r.iot_name_id), r.tag] as [number, number, number]);
65
64
  robot.roomInfo = new RoomMap(roomDataMap, rooms);
66
65
  }
67
66
  }
@@ -73,7 +72,7 @@ export async function getRoomMapFromDevice(device: Device, platform: RoborockMat
73
72
  const rooms = device?.rooms ?? [];
74
73
 
75
74
  platform.log.notice('-------------------------------------------0--------------------------------------------------------');
76
- platform.log.notice(`getRoomMapFromDevice: ${debugStringify(rooms)}`);
75
+ platform.log.notice(`getRoomMapFromDevice - device.rooms: ${debugStringify(rooms)}`);
77
76
 
78
77
  if (device && platform.roborockService) {
79
78
  const roomData = await platform.roborockService.getRoomMappings(device.duid);
@@ -90,8 +89,8 @@ export async function getRoomMapFromDevice(device: Device, platform: RoborockMat
90
89
  const mapInfo = await platform.roborockService.getMapInformation(device.duid);
91
90
  platform.log.notice(`getRoomMapFromDevice - mapInfo: ${mapInfo ? debugStringify(mapInfo) : 'undefined'}`);
92
91
 
93
- if (mapInfo && mapInfo.maps && mapInfo.maps.length > 0) {
94
- const roomDataMap = mapInfo.maps[0].rooms.map((r) => [r.id, parseInt(r.iot_name_id), r.tag] as [number, number, number]);
92
+ if (mapInfo && mapInfo.allRooms && mapInfo.allRooms.length > 0) {
93
+ const roomDataMap = mapInfo.allRooms.map((r) => [r.id, parseInt(r.iot_name_id), r.tag] as [number, number, number]);
95
94
 
96
95
  const roomMap = new RoomMap(roomDataMap, rooms);
97
96
 
@@ -8,15 +8,16 @@ export function getSupportedAreas(vacuumRooms: Room[], roomMap: RoomMap | undefi
8
8
  log?.debug('getSupportedAreas-vacuum room', debugStringify(vacuumRooms));
9
9
  log?.debug('getSupportedAreas-roomMap', roomMap ? debugStringify(roomMap) : 'undefined');
10
10
 
11
- if (!vacuumRooms || vacuumRooms.length === 0 || !roomMap?.rooms || roomMap.rooms.length == 0) {
12
- if (!vacuumRooms || vacuumRooms.length === 0) {
11
+ const noVacuumRooms = !vacuumRooms || vacuumRooms.length === 0;
12
+ const noRoomMap = !roomMap?.rooms || roomMap.rooms.length === 0;
13
+
14
+ if (noVacuumRooms || noRoomMap) {
15
+ if (noVacuumRooms) {
13
16
  log?.error('No rooms found');
14
17
  }
15
-
16
- if (!roomMap || !roomMap.rooms || roomMap.rooms.length == 0) {
18
+ if (noRoomMap) {
17
19
  log?.error('No room map found');
18
20
  }
19
-
20
21
  return [
21
22
  {
22
23
  areaId: 1,
@@ -34,12 +35,14 @@ export function getSupportedAreas(vacuumRooms: Room[], roomMap: RoomMap | undefi
34
35
  }
35
36
 
36
37
  const supportedAreas: ServiceArea.Area[] = roomMap.rooms.map((room) => {
38
+ const locationName = room.displayName ?? vacuumRooms.find((r) => r.id === room.globalId || r.id === room.id)?.name ?? `Unknown Room ${randomInt(1000, 9999)}`;
39
+
37
40
  return {
38
41
  areaId: room.id,
39
42
  mapId: null,
40
43
  areaInfo: {
41
44
  locationInfo: {
42
- locationName: room.displayName ?? vacuumRooms.find((r) => r.id == room.globalId || r.id == room.id)?.name ?? `Unknown Room ${randomInt(1000, 9999)}`,
45
+ locationName,
43
46
  floorNumber: null,
44
47
  areaType: null,
45
48
  },
@@ -52,27 +55,30 @@ export function getSupportedAreas(vacuumRooms: Room[], roomMap: RoomMap | undefi
52
55
 
53
56
  const duplicated = findDuplicatedAreaIds(supportedAreas, log);
54
57
 
55
- return duplicated
56
- ? [
57
- {
58
- areaId: 2,
59
- mapId: null,
60
- areaInfo: {
61
- locationInfo: {
62
- locationName: 'Unknown',
63
- floorNumber: null,
64
- areaType: null,
65
- },
66
- landmarkInfo: null,
58
+ if (duplicated) {
59
+ return [
60
+ {
61
+ areaId: 2,
62
+ mapId: null,
63
+ areaInfo: {
64
+ locationInfo: {
65
+ locationName: 'Unknown',
66
+ floorNumber: null,
67
+ areaType: null,
67
68
  },
69
+ landmarkInfo: null,
68
70
  },
69
- ]
70
- : supportedAreas;
71
+ },
72
+ ];
73
+ }
74
+
75
+ return supportedAreas;
71
76
  }
72
77
 
73
78
  function findDuplicatedAreaIds(areas: ServiceArea.Area[], log?: AnsiLogger): boolean {
74
79
  const seen = new Set<number>();
75
80
  const duplicates: number[] = [];
81
+
76
82
  for (const area of areas) {
77
83
  if (seen.has(area.areaId)) {
78
84
  duplicates.push(area.areaId);
@@ -80,9 +86,11 @@ function findDuplicatedAreaIds(areas: ServiceArea.Area[], log?: AnsiLogger): boo
80
86
  seen.add(area.areaId);
81
87
  }
82
88
  }
89
+
83
90
  if (duplicates.length > 0 && log) {
84
91
  const duplicated = areas.filter((x) => duplicates.includes(x.areaId));
85
92
  log.error(`Duplicated areaId(s) found: ${debugStringify(duplicated)}`);
86
93
  }
94
+
87
95
  return duplicates.length > 0;
88
96
  }
@@ -17,30 +17,34 @@ roomMap = {
17
17
 
18
18
  import { Room } from '../roborockCommunication/Zmodel/room.js';
19
19
 
20
+ export interface RoomMapEntry {
21
+ id: number;
22
+ globalId: number | undefined;
23
+ displayName?: string;
24
+ alternativeId: string;
25
+ }
26
+
20
27
  export class RoomMap {
21
- readonly rooms: {
22
- id: number;
23
- globalId: number | undefined;
24
- displayName: string | undefined;
25
- alternativeId: string;
26
- }[] = [];
28
+ readonly rooms: RoomMapEntry[];
27
29
 
28
30
  constructor(roomData: number[][], rooms: Room[]) {
29
- this.rooms = roomData.map((entry) => {
31
+ this.rooms = roomData.map(([id, globalId, altId]) => {
32
+ const room = rooms.find((r) => Number(r.id) === Number(globalId) || Number(r.id) === Number(id));
30
33
  return {
31
- id: entry[0],
32
- globalId: Number(entry[1]),
33
- displayName: rooms.find((r) => Number(r.id) == Number(entry[1]))?.name,
34
- alternativeId: `${entry[0]}${entry[2]}`,
34
+ id,
35
+ globalId: globalId !== undefined ? Number(globalId) : undefined,
36
+ displayName: room?.name,
37
+ alternativeId: `${id}${altId}`,
35
38
  };
36
39
  });
37
40
  }
38
41
 
42
+ // Optionally, add utility methods for clarity
39
43
  // getGlobalId(id: number): number | undefined {
40
- // return this.rooms.find((r) => Number(r.id) == Number(id))?.globalId;
44
+ // return this.rooms.find((r) => r.id === id)?.globalId;
41
45
  // }
42
46
 
43
47
  // getRoomId(globalId: number): number | undefined {
44
- // return this.rooms.find((r) => Number(r.globalId) == Number(globalId))?.id;
48
+ // return this.rooms.find((r) => r.globalId === globalId)?.id;
45
49
  // }
46
50
  }
package/src/platform.ts CHANGED
@@ -227,8 +227,8 @@ export class RoborockMatterbridgePlatform extends MatterbridgeDynamicPlatform {
227
227
  if (vacuum.rooms === undefined || vacuum.rooms.length === 0) {
228
228
  this.log.notice(`Fetching map information for device: ${vacuum.name} (${vacuum.duid}) to get rooms`);
229
229
  const map_info = await this.roborockService.getMapInformation(vacuum.duid);
230
- const rooms = map_info?.maps?.[0]?.rooms ?? [];
231
- vacuum.rooms = rooms.map((room) => ({ id: room.id, name: room.displayName }) as Room);
230
+ const rooms = map_info?.allRooms ?? [];
231
+ vacuum.rooms = rooms.map((room) => ({ id: room.globalId, name: room.displayName }) as Room);
232
232
  }
233
233
 
234
234
  const roomMap = await getRoomMapFromDevice(vacuum, this);
@@ -1,17 +1,17 @@
1
1
  import { RvcRunMode, PowerSource, ServiceArea, RvcOperationalState, RvcCleanMode } from 'matterbridge/matter/clusters';
2
- import { getRoomMap, getVacuumProperty, isStatusUpdate } from './helper.js';
2
+ import { getRoomMap, getRoomMapFromDevice, getVacuumProperty, isStatusUpdate } from './helper.js';
3
3
  import { getRunningMode } from './initialData/getSupportedRunModes.js';
4
4
  import { CloudMessageModel } from './model/CloudMessageModel.js';
5
5
  import { RoborockMatterbridgePlatform } from './platform.js';
6
6
  import { state_to_matter_operational_status, state_to_matter_state } from './share/function.js';
7
- import { getBatteryState, getBatteryStatus, getOperationalErrorState } from './initialData/index.js';
7
+ import { getBatteryState, getBatteryStatus, getOperationalErrorState, getSupportedAreas } from './initialData/index.js';
8
8
  import { NotifyMessageTypes } from './notifyMessageTypes.js';
9
9
  import { CloudMessageResult } from './roborockCommunication/Zmodel/messageResult.js';
10
10
  import { Protocol } from './roborockCommunication/broadcast/model/protocol.js';
11
11
  import { DpsPayload } from './roborockCommunication/broadcast/model/dps.js';
12
12
  import { RoborockVacuumCleaner } from './rvc.js';
13
13
  import { hasDockingStationError, parseDockingStationStatus } from './model/DockingStationStatus.js';
14
- import { BatteryMessage, Device, DeviceErrorMessage, DeviceStatusNotify, Home } from './roborockCommunication/index.js';
14
+ import { AdditionalPropCode, BatteryMessage, Device, DeviceErrorMessage, DeviceStatusNotify, Home } from './roborockCommunication/index.js';
15
15
  import { OperationStatusCode } from './roborockCommunication/Zenum/operationStatusCode.js';
16
16
  import { getCurrentCleanModeFunc } from './share/runtimeHelper.js';
17
17
  import { debugStringify } from 'matterbridge/logger';
@@ -280,7 +280,30 @@ export class PlatformRunner {
280
280
  });
281
281
  break; // Do nothing, handled in local message
282
282
  }
283
- case Protocol.additional_props:
283
+ case Protocol.additional_props: {
284
+ platform.log.notice(`Received additional properties for robot ${duid}: ${debugStringify(data)}`);
285
+ const propCode = data.dps[Protocol.additional_props] as number;
286
+ platform.log.debug(`DPS for additional properties: ${propCode}, AdditionalPropCode: ${AdditionalPropCode[propCode]}`);
287
+
288
+ if (propCode === AdditionalPropCode.map_change) {
289
+ platform.log.notice('------------------------ get roomData ----------------------------');
290
+
291
+ const roomMap = await getRoomMapFromDevice(robot.device, platform);
292
+
293
+ platform.log.notice('------------------------ Room map updated ------------------------');
294
+ const supportedAreas = getSupportedAreas(robot.device.rooms, roomMap, platform.log);
295
+
296
+ platform.log.notice(`Supported areas: ${debugStringify(supportedAreas)}`);
297
+ platform.log.notice('------------------------ Supported areas updated ------------------');
298
+
299
+ platform.roborockService?.setSupportedAreas(duid, supportedAreas);
300
+ platform.roborockService?.setSelectedAreas(duid, []);
301
+ robot.updateAttribute(ServiceArea.Cluster.id, 'supportedAreas', supportedAreas, platform.log);
302
+ robot.updateAttribute(ServiceArea.Cluster.id, 'selectedAreas', [], platform.log);
303
+ robot.updateAttribute(ServiceArea.Cluster.id, 'currentArea', null, platform.log);
304
+ }
305
+ break;
306
+ }
284
307
  case Protocol.back_type: {
285
308
  // TODO: check if this is needed
286
309
  break;
@@ -0,0 +1,3 @@
1
+ export enum AdditionalPropCode {
2
+ map_change = 4,
3
+ }
@@ -1,4 +1,4 @@
1
- export interface Map {
1
+ export interface MapInformation {
2
2
  mapFlag: number;
3
3
  add_time: number;
4
4
  length: number;
@@ -2,27 +2,45 @@ import decodeComponent from '../helper/nameDecoder.js';
2
2
  import { RoomInformation } from './map.js';
3
3
  import type { MultipleMap } from './multipleMap.js';
4
4
 
5
+ export interface MapRoom {
6
+ id: number;
7
+ iot_name_id: string;
8
+ tag: number;
9
+ globalId?: number;
10
+ displayName: string;
11
+ mapId?: number;
12
+ }
13
+
14
+ export interface MapEntry {
15
+ id: number;
16
+ name: string | undefined;
17
+ rooms: MapRoom[];
18
+ }
19
+
5
20
  export class MapInfo {
6
- readonly maps: { id: number; name: string | undefined; rooms: { id: number; iot_name_id: string; tag: number; displayName: string }[] }[] = [];
21
+ public readonly maps: MapEntry[] = [];
22
+ public readonly allRooms: MapRoom[] = [];
7
23
 
8
24
  constructor(multimap: MultipleMap) {
9
- multimap.map_info.forEach((map) => {
10
- this.maps.push({
11
- id: map.mapFlag,
12
- name: decodeComponent(map.name)?.toLowerCase(),
13
- rooms:
14
- map.rooms && map.rooms.length > 0
15
- ? map.rooms.map((room: RoomInformation) => {
16
- return {
17
- id: room.id,
18
- iot_name_id: room.iot_name_id,
19
- tag: room.tag,
20
- displayName: room.iot_name,
21
- };
22
- })
23
- : [],
24
- });
25
+ this.maps = multimap.map_info.map((mapInfo) => {
26
+ const rooms: MapRoom[] = mapInfo.rooms.map((room: RoomInformation) => ({
27
+ id: room.id,
28
+ globalId: parseInt(room.iot_name_id),
29
+ iot_name_id: room.iot_name_id,
30
+ tag: room.tag,
31
+ displayName: room.iot_name,
32
+ mapId: mapInfo.mapFlag,
33
+ }));
34
+
35
+ this.allRooms.push(...rooms);
36
+ return {
37
+ id: mapInfo.mapFlag,
38
+ name: decodeComponent(mapInfo.name),
39
+ rooms,
40
+ };
25
41
  });
42
+
43
+ this.allRooms = this.allRooms.filter((room, index, self) => index === self.findIndex((r) => r.globalId === room.globalId));
26
44
  }
27
45
 
28
46
  getById(id: number): string | undefined {
@@ -30,6 +48,6 @@ export class MapInfo {
30
48
  }
31
49
 
32
50
  getByName(name: string): number | undefined {
33
- return this.maps.find((m) => m.name === name.toLowerCase())?.id;
51
+ return this.maps.find((m) => m.name?.toLowerCase() === name.toLowerCase())?.id;
34
52
  }
35
53
  }
@@ -1,8 +1,8 @@
1
- import { Map } from './map.js';
1
+ import { MapInformation } from './map.js';
2
2
 
3
3
  export interface MultipleMap {
4
4
  max_multi_map: number;
5
5
  max_bak_map: number;
6
6
  multi_map_count: number;
7
- map_info: Map[];
7
+ map_info: MapInformation[];
8
8
  }
@@ -1,11 +1,9 @@
1
1
  import { AnsiLogger, debugStringify } from 'matterbridge/logger';
2
2
  import { CloudMessageResult } from '../Zmodel/messageResult.js';
3
- import { RoomInfo } from '../Zmodel/roomInfo.js';
4
3
  import { AbstractMessageHandler } from './listener/abstractMessageHandler.js';
5
4
  import { SimpleMessageListener } from './listener/index.js';
6
5
  import { RequestMessage } from './model/requestMessage.js';
7
6
  import { DeviceStatus } from '../Zmodel/deviceStatus.js';
8
- import { Room } from '../Zmodel/room.js';
9
7
  import { Client } from './client.js';
10
8
  import { NetworkInfo } from '../Zmodel/networkInfo.js';
11
9
 
@@ -58,9 +56,9 @@ export class MessageProcessor {
58
56
  return undefined;
59
57
  }
60
58
 
61
- public async getRooms(duid: string, rooms: Room[]): Promise<RoomInfo> {
59
+ public async getRooms(duid: string): Promise<number[][] | undefined> {
62
60
  const request = new RequestMessage({ method: 'get_room_mapping' });
63
- return this.client.get<number[][] | undefined>(duid, request).then((response) => new RoomInfo(rooms, response ?? []));
61
+ return this.client.get<number[][] | undefined>(duid, request); // .then((response) => new RoomInfo(rooms, response ?? []));
64
62
  }
65
63
 
66
64
  public async gotoDock(duid: string): Promise<void> {
@@ -8,6 +8,7 @@ export { ClientRouter } from './broadcast/clientRouter.js';
8
8
  export { DeviceStatus } from './Zmodel/deviceStatus.js';
9
9
  export { ResponseMessage } from './broadcast/model/responseMessage.js';
10
10
  export { MapInfo } from './Zmodel/mapInfo.js';
11
+ export { AdditionalPropCode } from './Zenum/additionalPropCode.js';
11
12
 
12
13
  export { Scene } from './Zmodel/scene.js';
13
14
 
@@ -375,6 +375,7 @@ export default class RoborockService {
375
375
  },
376
376
  };
377
377
  }) as Device[];
378
+
378
379
  return result;
379
380
  }
380
381
 
@@ -444,12 +445,14 @@ export default class RoborockService {
444
445
  return this.iotApi.startScene(sceneId);
445
446
  }
446
447
 
447
- public getRoomMappings(duid: string): Promise<number[][] | undefined> {
448
+ public async getRoomMappings(duid: string): Promise<number[][] | undefined> {
448
449
  if (!this.messageClient) {
449
450
  this.logger.warn('messageClient not initialized. Waititing for next execution');
450
451
  return Promise.resolve(undefined);
451
452
  }
452
453
  return this.messageClient.get(duid, new RequestMessage({ method: 'get_room_mapping', secure: this.isRequestSecure(duid) }));
454
+
455
+ // return await this.getMessageProcessor(duid)?.getRooms(duid);
453
456
  }
454
457
 
455
458
  public async initializeMessageClient(username: string, device: Device, userdata: UserData): Promise<void> {
@@ -0,0 +1,113 @@
1
+ import { getRoomMapFromDevice } from '../helper';
2
+ import { RoomMap } from '../model/RoomMap';
3
+
4
+ // Mocks
5
+ // const mockLog = {
6
+ // notice: (message: string, ...arg: unknown[]) => console.log('NOTICE:', message, ...arg),
7
+ // error: (message: string, ...arg: unknown[]) => console.error('ERROR:', message, ...arg),
8
+ // debug: (message: string, ...arg: unknown[]) => console.debug('DEBUG:', message, ...arg),
9
+ // info: (message: string, ...arg: unknown[]) => console.info('INFO:', message, ...arg),
10
+ // warn: (message: string, ...arg: unknown[]) => console.warn('WARN:', message, ...arg),
11
+ // verbose: (message: string, ...arg: unknown[]) => console.log('VERBOSE:', message, ...arg),
12
+ // };
13
+
14
+ const mockLog = {
15
+ notice: jest.fn(),
16
+ error: jest.fn(),
17
+ debug: jest.fn(),
18
+ info: jest.fn(),
19
+ warn: jest.fn(),
20
+ verbose: jest.fn(),
21
+ };
22
+
23
+ const mockRoborockService = {
24
+ getRoomMappings: jest.fn(),
25
+ getMapInformation: jest.fn(),
26
+ };
27
+
28
+ const mockPlatform = {
29
+ log: mockLog,
30
+ roborockService: mockRoborockService,
31
+ };
32
+
33
+ describe('getRoomMapFromDevice', () => {
34
+ beforeEach(() => {
35
+ jest.clearAllMocks();
36
+ });
37
+
38
+ // it('returns RoomMap from getRoomMappings if available', async () => {
39
+ // const device = {
40
+ // duid: '123',
41
+ // rooms: [
42
+ // {
43
+ // 'id': 12461114,
44
+ // 'name': 'Guest bedroom',
45
+ // },
46
+ // {
47
+ // 'id': 12461111,
48
+ // 'name': 'Balcony',
49
+ // },
50
+ // {
51
+ // 'id': 12461109,
52
+ // 'name': 'Master bedroom',
53
+ // },
54
+ // {
55
+ // 'id': 11100849,
56
+ // 'name': 'Study',
57
+ // },
58
+ // {
59
+ // 'id': 11100847,
60
+ // 'name': 'Bedroom',
61
+ // },
62
+ // {
63
+ // 'id': 11100845,
64
+ // 'name': 'Kitchen',
65
+ // },
66
+ // {
67
+ // 'id': 11100842,
68
+ // 'name': 'Living room',
69
+ // },
70
+ // ],
71
+ // };
72
+ // mockRoborockService.getRoomMappings.mockResolvedValue([
73
+ // [1, '11100842', 6],
74
+ // [2, '12461114', 3],
75
+ // [3, '12461109', 2],
76
+ // [4, '12461111', 7],
77
+ // ]);
78
+ // mockRoborockService.getMapInformation.mockResolvedValue(undefined);
79
+
80
+ // const result = await getRoomMapFromDevice(device as any, mockPlatform as any);
81
+
82
+ // //console.log('Result:', result);
83
+ // expect(result).toBeInstanceOf(RoomMap);
84
+ // expect(mockRoborockService.getRoomMappings).toHaveBeenCalledWith('123');
85
+ // expect(result.rooms.length).toBeGreaterThan(0);
86
+ // });
87
+
88
+ it('returns RoomMap from getRoomMappings if available', async () => {
89
+ const device = {
90
+ duid: '123',
91
+ rooms: [
92
+ { id: 1, name: 'Kitchen' },
93
+ { id: 2, name: 'Study' },
94
+ { id: 3, name: 'Living room' },
95
+ { id: 4, name: 'Bedroom' },
96
+ ],
97
+ };
98
+ mockRoborockService.getRoomMappings.mockResolvedValue([
99
+ [1, '11100845', 14],
100
+ [2, '11100849', 9],
101
+ [3, '11100842', 6],
102
+ [4, '11100847', 1],
103
+ ]);
104
+ mockRoborockService.getMapInformation.mockResolvedValue(undefined);
105
+
106
+ const result = await getRoomMapFromDevice(device as any, mockPlatform as any);
107
+
108
+ // console.log('Result:', result);
109
+ expect(result).toBeInstanceOf(RoomMap);
110
+ expect(mockRoborockService.getRoomMappings).toHaveBeenCalledWith('123');
111
+ expect(result.rooms.length).toBeGreaterThan(0);
112
+ });
113
+ });
@@ -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
 
@@ -83,6 +116,6 @@ describe('PlatformRunner.getRoomMapFromDevice', () => {
83
116
  const result = await getRoomMapFromDevice(device as any, platform);
84
117
 
85
118
  expect(result).toBeInstanceOf(RoomMap);
86
- expect(result.rooms.length).toEqual(4);
119
+ expect(result.rooms.length).toEqual(7);
87
120
  });
88
121
  });
@@ -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 () => {
@@ -55,7 +55,17 @@ describe('RoborockService - startClean', () => {
55
55
  });
56
56
 
57
57
  it('should return MapInfo if response contains maps', async () => {
58
- const mapData = [{ map_info: [{}] }] as MultipleMap[];
58
+ const mapData = [
59
+ {
60
+ map_info: [
61
+ {
62
+ rooms: [{ id: 1, iot_name_id: 'room1', tag: 0, iot_name: 'Living Room' }],
63
+ mapFlag: 1,
64
+ name: 'Living Room Map',
65
+ },
66
+ ],
67
+ },
68
+ ] as MultipleMap[];
59
69
  mockMessageClient = {
60
70
  get: jest.fn(),
61
71
  };