matterbridge-roborock-vacuum-plugin 1.1.0-rc14 → 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 (39) 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 +62 -0
  5. package/dist/initialData/getSupportedAreas.js +12 -8
  6. package/dist/model/RoomMap.js +8 -7
  7. package/dist/platform.js +4 -4
  8. package/dist/platformRunner.js +24 -68
  9. package/dist/roborockCommunication/Zenum/additionalPropCode.js +4 -0
  10. package/dist/roborockCommunication/Zmodel/mapInfo.js +18 -16
  11. package/dist/roborockCommunication/broadcast/client/LocalNetworkClient.js +2 -2
  12. package/dist/roborockCommunication/broadcast/listener/implementation/syncMessageListener.js +0 -4
  13. package/dist/roborockCommunication/broadcast/messageProcessor.js +3 -4
  14. package/dist/roborockCommunication/index.js +1 -0
  15. package/dist/roborockService.js +1 -1
  16. package/matterbridge-roborock-vacuum-plugin.config.json +1 -1
  17. package/matterbridge-roborock-vacuum-plugin.schema.json +1 -1
  18. package/package.json +1 -1
  19. package/src/helper.ts +82 -0
  20. package/src/initialData/getSupportedAreas.ts +29 -21
  21. package/src/model/RoomMap.ts +18 -14
  22. package/src/platform.ts +4 -4
  23. package/src/platformRunner.ts +29 -89
  24. package/src/roborockCommunication/Zenum/additionalPropCode.ts +3 -0
  25. package/src/roborockCommunication/Zmodel/map.ts +1 -1
  26. package/src/roborockCommunication/Zmodel/mapInfo.ts +36 -18
  27. package/src/roborockCommunication/Zmodel/multipleMap.ts +2 -2
  28. package/src/roborockCommunication/broadcast/client/LocalNetworkClient.ts +2 -3
  29. package/src/roborockCommunication/broadcast/listener/implementation/syncMessageListener.ts +4 -4
  30. package/src/roborockCommunication/broadcast/messageProcessor.ts +4 -6
  31. package/src/roborockCommunication/index.ts +1 -0
  32. package/src/roborockService.ts +4 -1
  33. package/src/rvc.ts +1 -1
  34. package/src/tests/helper.test.ts +113 -0
  35. package/src/tests/platformRunner2.test.ts +43 -13
  36. package/src/tests/platformRunner3.test.ts +54 -0
  37. package/src/tests/roborockCommunication/broadcast/listener/implementation/syncMessageListener.test.ts +3 -3
  38. package/src/tests/roborockCommunication/broadcast/messageProcessor.test.ts +3 -8
  39. package/src/tests/roborockService.test.ts +11 -1
@@ -1,6 +1,6 @@
1
1
  import { AnsiLogger, debugStringify } from 'matterbridge/logger';
2
2
  import { ServiceArea } from 'matterbridge/matter/clusters';
3
- import RoomMap from '../model/RoomMap.js';
3
+ import { RoomMap } from '../model/RoomMap.js';
4
4
  import { Room } from '../roborockCommunication/Zmodel/room.js';
5
5
  import { randomInt } from 'node:crypto';
6
6
 
@@ -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 default class RoomMap {
21
- readonly rooms: {
22
- id: number;
23
- globalId: number | undefined;
24
- displayName: string | undefined;
25
- alternativeId: string;
26
- }[] = [];
20
+ export interface RoomMapEntry {
21
+ id: number;
22
+ globalId: number | undefined;
23
+ displayName?: string;
24
+ alternativeId: string;
25
+ }
26
+
27
+ export class RoomMap {
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
@@ -4,7 +4,7 @@ import { AnsiLogger, debugStringify, LogLevel } from 'matterbridge/logger';
4
4
  import RoborockService from './roborockService.js';
5
5
  import { PLUGIN_NAME } from './settings.js';
6
6
  import ClientManager from './clientManager.js';
7
- import { isSupportedDevice } from './helper.js';
7
+ import { getRoomMapFromDevice, isSupportedDevice } from './helper.js';
8
8
  import { PlatformRunner } from './platformRunner.js';
9
9
  import { RoborockVacuumCleaner } from './rvc.js';
10
10
  import { configurateBehavior } from './behaviorFactory.js';
@@ -227,11 +227,11 @@ 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
- const roomMap = await this.platformRunner.getRoomMapFromDevice(vacuum);
234
+ const roomMap = await getRoomMapFromDevice(vacuum, this);
235
235
 
236
236
  this.log.debug('Initializing - roomMap: ', debugStringify(roomMap));
237
237
 
@@ -1,18 +1,17 @@
1
1
  import { RvcRunMode, PowerSource, ServiceArea, RvcOperationalState, RvcCleanMode } from 'matterbridge/matter/clusters';
2
- import { getVacuumProperty } 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 RoomMap from './model/RoomMap.js';
8
- import { getBatteryState, getBatteryStatus, getOperationalErrorState } from './initialData/index.js';
7
+ import { getBatteryState, getBatteryStatus, getOperationalErrorState, getSupportedAreas } from './initialData/index.js';
9
8
  import { NotifyMessageTypes } from './notifyMessageTypes.js';
10
9
  import { CloudMessageResult } from './roborockCommunication/Zmodel/messageResult.js';
11
10
  import { Protocol } from './roborockCommunication/broadcast/model/protocol.js';
12
11
  import { DpsPayload } from './roborockCommunication/broadcast/model/dps.js';
13
12
  import { RoborockVacuumCleaner } from './rvc.js';
14
13
  import { hasDockingStationError, parseDockingStationStatus } from './model/DockingStationStatus.js';
15
- import { BatteryMessage, Device, DeviceErrorMessage, DeviceStatusNotify, Home } from './roborockCommunication/index.js';
14
+ import { AdditionalPropCode, BatteryMessage, Device, DeviceErrorMessage, DeviceStatusNotify, Home } from './roborockCommunication/index.js';
16
15
  import { OperationStatusCode } from './roborockCommunication/Zenum/operationStatusCode.js';
17
16
  import { getCurrentCleanModeFunc } from './share/runtimeHelper.js';
18
17
  import { debugStringify } from 'matterbridge/logger';
@@ -41,76 +40,6 @@ export class PlatformRunner {
41
40
  await this.updateRobot(NotifyMessageTypes.HomeData, homeData);
42
41
  }
43
42
 
44
- public async getRoomMapFromDevice(device: Device): Promise<RoomMap> {
45
- const platform = this.platform;
46
- const rooms = device?.rooms ?? [];
47
-
48
- platform.log.notice('-------------------------------------------0--------------------------------------------------------');
49
- platform.log.notice(`getRoomMapFromDevice: ${debugStringify(rooms)}`);
50
-
51
- if (device && platform.roborockService) {
52
- const roomData = await platform.roborockService.getRoomMappings(device.duid);
53
- if (roomData !== undefined && roomData.length > 0) {
54
- platform.log.notice(`getRoomMapFromDevice - roomData: ${debugStringify(roomData ?? [])}`);
55
-
56
- const roomMap = new RoomMap(roomData ?? [], rooms);
57
-
58
- platform.log.notice(`getRoomMapFromDevice - roomMap: ${debugStringify(roomMap)}`);
59
- platform.log.notice('-------------------------------------------1--------------------------------------------------------');
60
- return roomMap;
61
- }
62
-
63
- const mapInfo = await platform.roborockService.getMapInformation(device.duid);
64
- platform.log.notice(`getRoomMapFromDevice - mapInfo: ${mapInfo ? debugStringify(mapInfo) : 'undefined'}`);
65
-
66
- if (mapInfo && mapInfo.maps && mapInfo.maps.length > 0) {
67
- const roomDataMap = mapInfo.maps[0].rooms.map((r) => [r.id, parseInt(r.iot_name_id), r.tag] as [number, number, number]);
68
-
69
- const roomMap = new RoomMap(roomDataMap, rooms);
70
-
71
- platform.log.notice(`getRoomMapFromDevice - roomMap: ${debugStringify(roomMap)}`);
72
- platform.log.notice('-------------------------------------------2--------------------------------------------------------');
73
- return roomMap;
74
- }
75
- }
76
-
77
- return new RoomMap([], rooms);
78
- }
79
-
80
- private async getRoomMap(duid: string): Promise<RoomMap | undefined> {
81
- const platform = this.platform;
82
-
83
- const robot = platform.robots.get(duid);
84
- if (robot === undefined) {
85
- platform.log.error(`Error6: Robot with DUID ${duid} not found`);
86
- return undefined;
87
- }
88
-
89
- if (platform.roborockService === undefined) return undefined;
90
-
91
- const rooms = robot.device.rooms ?? [];
92
- // if (platform.robot?.device === undefined || platform.roborockService === undefined) return undefined;
93
- if (robot.roomInfo === undefined) {
94
- const roomData = await platform.roborockService.getRoomMappings(robot.device.duid);
95
- if (roomData !== undefined && roomData.length > 0) {
96
- robot.roomInfo = new RoomMap(roomData ?? [], rooms);
97
- return robot.roomInfo;
98
- }
99
- }
100
-
101
- if (robot.roomInfo === undefined) {
102
- const mapInfo = await platform.roborockService.getMapInformation(robot.device.duid);
103
- if (mapInfo && mapInfo.maps && mapInfo.maps.length > 0) {
104
- platform.log.error(`getRoomMap - mapInfo: ${debugStringify(mapInfo.maps)}`);
105
-
106
- const roomDataMap = mapInfo.maps[0].rooms.map((r) => [r.id, parseInt(r.iot_name_id), r.tag] as [number, number, number]);
107
- robot.roomInfo = new RoomMap(roomDataMap, rooms);
108
- }
109
- }
110
-
111
- return robot.roomInfo;
112
- }
113
-
114
43
  private async updateFromMQTTMessage(messageSource: NotifyMessageTypes, messageData: unknown, duid = '', tracked = false): Promise<void> {
115
44
  const platform = this.platform;
116
45
  duid = duid || (messageData as DeviceStatusNotify)?.duid || '';
@@ -191,7 +120,7 @@ export class PlatformRunner {
191
120
  robot.updateAttribute(ServiceArea.Cluster.id, 'selectedAreas', [], platform.log);
192
121
  } else {
193
122
  const currentMappedAreas = this.platform.roborockService?.getSupportedAreas(duid);
194
- const roomMap = await this.getRoomMap(duid);
123
+ const roomMap = await getRoomMap(duid, this.platform);
195
124
 
196
125
  // Get current room from segment_id
197
126
  const segment_id = data.cleaning_info?.segment_id ?? -1;
@@ -311,7 +240,7 @@ export class PlatformRunner {
311
240
  case Protocol.rpc_response: {
312
241
  const response = data.dps[messageType] as DpsPayload;
313
242
  // ignore network info
314
- if (!self.isStatusUpdate(response.result)) {
243
+ if (!isStatusUpdate(response.result)) {
315
244
  platform.log.debug('Ignore message:', debugStringify(data));
316
245
  return;
317
246
  }
@@ -351,7 +280,30 @@ export class PlatformRunner {
351
280
  });
352
281
  break; // Do nothing, handled in local message
353
282
  }
354
- 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
+ }
355
307
  case Protocol.back_type: {
356
308
  // TODO: check if this is needed
357
309
  break;
@@ -398,18 +350,6 @@ export class PlatformRunner {
398
350
  return undefined;
399
351
  }
400
352
 
401
- private isStatusUpdate(result: unknown): boolean {
402
- return (
403
- Array.isArray(result) &&
404
- result.length > 0 &&
405
- typeof result[0] === 'object' &&
406
- result[0] !== null &&
407
- 'msg_ver' in result[0] &&
408
- (result[0] as CloudMessageResult).msg_ver !== undefined &&
409
- (result[0] as CloudMessageResult).msg_ver !== null
410
- );
411
- }
412
-
413
353
  private updateFromHomeData(homeData: Home): void {
414
354
  const platform = this.platform;
415
355
 
@@ -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
  }
@@ -80,12 +80,11 @@ export class LocalNetworkClient extends AbstractClient {
80
80
  private async onConnect(): Promise<void> {
81
81
  this.logger.debug(` [LocalNetworkClient]: ${this.duid} connected to ${this.ip}`);
82
82
  this.logger.debug(` [LocalNetworkClient]: ${this.duid} socket writable: ${this.socket?.writable}, readable: ${this.socket?.readable}`);
83
+ this.connected = true;
84
+ this.retryCount = 0;
83
85
 
84
86
  await this.sendHelloMessage();
85
87
  this.pingInterval = setInterval(this.sendPingRequest.bind(this), 5000);
86
-
87
- this.connected = true;
88
- this.retryCount = 0;
89
88
  await this.connectionListeners.onConnected(this.duid);
90
89
  }
91
90
 
@@ -28,10 +28,10 @@ export class SyncMessageListener implements AbstractMessageListener {
28
28
  const messageId = dps.id;
29
29
 
30
30
  const responseHandler = this.pending.get(messageId);
31
- const result = dps.result as Record<string, unknown>;
32
- if (result && result.length == 1 && result[0] == 'ok') {
33
- return;
34
- }
31
+ // const result = dps.result as Record<string, unknown>;
32
+ // if (result && result.length == 1 && result[0] == 'ok') {
33
+ // return;
34
+ // }
35
35
 
36
36
  if (responseHandler) {
37
37
  responseHandler(dps.result as ResponseMessage);
@@ -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> {
@@ -105,9 +103,9 @@ export class MessageProcessor {
105
103
  return this.client.get(duid, def);
106
104
  }
107
105
 
108
- public async findMyRobot(duid: string): Promise<void> {
106
+ public async findMyRobot(duid: string): Promise<unknown> {
109
107
  const request = new RequestMessage({ method: 'find_me' });
110
- return this.client.send(duid, request);
108
+ return this.client.get(duid, request);
111
109
  }
112
110
 
113
111
  public async getCleanModeData(duid: string): Promise<{ suctionPower: number; waterFlow: number; distance_off: number; mopRoute: number }> {
@@ -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> {
package/src/rvc.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { RoboticVacuumCleaner } from 'matterbridge/devices';
2
- import RoomMap from './model/RoomMap.js';
2
+ import { RoomMap } from './model/RoomMap.js';
3
3
  import { Device } from './roborockCommunication/index.js';
4
4
  import { getOperationalStates, getSupportedAreas, getSupportedCleanModes, getSupportedRunModes } from './initialData/index.js';
5
5
  import { AnsiLogger } from 'matterbridge/logger';
@@ -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
+ });