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,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
@@ -1,3 +1,5 @@
1
+ import { debugStringify } from 'matterbridge/logger';
2
+ import { RoomMap } from './model/RoomMap.js';
1
3
  export function getVacuumProperty(device, property) {
2
4
  if (device) {
3
5
  const schemas = device.schema;
@@ -14,3 +16,63 @@ export function getVacuumProperty(device, property) {
14
16
  export function isSupportedDevice(model) {
15
17
  return model.startsWith('roborock.vacuum.');
16
18
  }
19
+ export function isStatusUpdate(result) {
20
+ return (Array.isArray(result) &&
21
+ result.length > 0 &&
22
+ typeof result[0] === 'object' &&
23
+ result[0] !== null &&
24
+ 'msg_ver' in result[0] &&
25
+ result[0].msg_ver !== undefined &&
26
+ result[0].msg_ver !== null);
27
+ }
28
+ export async function getRoomMap(duid, platform) {
29
+ const robot = platform.robots.get(duid);
30
+ if (robot === undefined) {
31
+ platform.log.error(`Error6: Robot with DUID ${duid} not found`);
32
+ return undefined;
33
+ }
34
+ if (platform.roborockService === undefined)
35
+ return undefined;
36
+ const rooms = robot.device.rooms ?? [];
37
+ if (robot.roomInfo === undefined) {
38
+ const roomData = await platform.roborockService.getRoomMappings(robot.device.duid);
39
+ if (roomData !== undefined && roomData.length > 0) {
40
+ robot.roomInfo = new RoomMap(roomData ?? [], rooms);
41
+ return robot.roomInfo;
42
+ }
43
+ }
44
+ if (robot.roomInfo === undefined) {
45
+ const mapInfo = await platform.roborockService.getMapInformation(robot.device.duid);
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
+ robot.roomInfo = new RoomMap(roomDataMap, rooms);
50
+ }
51
+ }
52
+ return robot.roomInfo;
53
+ }
54
+ export async function getRoomMapFromDevice(device, platform) {
55
+ const rooms = device?.rooms ?? [];
56
+ platform.log.notice('-------------------------------------------0--------------------------------------------------------');
57
+ platform.log.notice(`getRoomMapFromDevice - device.rooms: ${debugStringify(rooms)}`);
58
+ if (device && platform.roborockService) {
59
+ const roomData = await platform.roborockService.getRoomMappings(device.duid);
60
+ if (roomData !== undefined && roomData.length > 0) {
61
+ platform.log.notice(`getRoomMapFromDevice - roomData: ${debugStringify(roomData ?? [])}`);
62
+ const roomMap = new RoomMap(roomData ?? [], rooms);
63
+ platform.log.notice(`getRoomMapFromDevice - roomMap: ${debugStringify(roomMap)}`);
64
+ platform.log.notice('-------------------------------------------1--------------------------------------------------------');
65
+ return roomMap;
66
+ }
67
+ const mapInfo = await platform.roborockService.getMapInformation(device.duid);
68
+ platform.log.notice(`getRoomMapFromDevice - mapInfo: ${mapInfo ? debugStringify(mapInfo) : 'undefined'}`);
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
+ const roomMap = new RoomMap(roomDataMap, rooms);
72
+ platform.log.notice(`getRoomMapFromDevice - roomMap: ${debugStringify(roomMap)}`);
73
+ platform.log.notice('-------------------------------------------2--------------------------------------------------------');
74
+ return roomMap;
75
+ }
76
+ }
77
+ return new RoomMap([], rooms);
78
+ }
@@ -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
- export default class RoomMap {
2
- rooms = [];
1
+ export class RoomMap {
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
@@ -4,7 +4,7 @@ import { debugStringify } 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';
@@ -159,10 +159,10 @@ 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
- const roomMap = await this.platformRunner.getRoomMapFromDevice(vacuum);
165
+ const roomMap = await getRoomMapFromDevice(vacuum, this);
166
166
  this.log.debug('Initializing - roomMap: ', debugStringify(roomMap));
167
167
  const behaviorHandler = configurateBehavior(vacuum.data.model, vacuum.duid, this.roborockService, this.cleanModeSettings, this.enableExperimentalFeature?.advancedFeature?.forceRunAtDefault ?? false, this.log);
168
168
  const supportedAreas = getSupportedAreas(vacuum.rooms, roomMap, this.log);
@@ -1,12 +1,12 @@
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 { state_to_matter_operational_status, state_to_matter_state } from './share/function.js';
5
- import RoomMap from './model/RoomMap.js';
6
- import { getBatteryState, getBatteryStatus, getOperationalErrorState } from './initialData/index.js';
5
+ import { getBatteryState, getBatteryStatus, getOperationalErrorState, getSupportedAreas } from './initialData/index.js';
7
6
  import { NotifyMessageTypes } from './notifyMessageTypes.js';
8
7
  import { Protocol } from './roborockCommunication/broadcast/model/protocol.js';
9
8
  import { hasDockingStationError, parseDockingStationStatus } from './model/DockingStationStatus.js';
9
+ import { AdditionalPropCode } from './roborockCommunication/index.js';
10
10
  import { OperationStatusCode } from './roborockCommunication/Zenum/operationStatusCode.js';
11
11
  import { getCurrentCleanModeFunc } from './share/runtimeHelper.js';
12
12
  import { debugStringify } from 'matterbridge/logger';
@@ -32,59 +32,6 @@ export class PlatformRunner {
32
32
  const homeData = await platform.roborockService.getHomeDataForUpdating(platform.rrHomeId);
33
33
  await this.updateRobot(NotifyMessageTypes.HomeData, homeData);
34
34
  }
35
- async getRoomMapFromDevice(device) {
36
- const platform = this.platform;
37
- const rooms = device?.rooms ?? [];
38
- platform.log.notice('-------------------------------------------0--------------------------------------------------------');
39
- platform.log.notice(`getRoomMapFromDevice: ${debugStringify(rooms)}`);
40
- if (device && platform.roborockService) {
41
- const roomData = await platform.roborockService.getRoomMappings(device.duid);
42
- if (roomData !== undefined && roomData.length > 0) {
43
- platform.log.notice(`getRoomMapFromDevice - roomData: ${debugStringify(roomData ?? [])}`);
44
- const roomMap = new RoomMap(roomData ?? [], rooms);
45
- platform.log.notice(`getRoomMapFromDevice - roomMap: ${debugStringify(roomMap)}`);
46
- platform.log.notice('-------------------------------------------1--------------------------------------------------------');
47
- return roomMap;
48
- }
49
- const mapInfo = await platform.roborockService.getMapInformation(device.duid);
50
- platform.log.notice(`getRoomMapFromDevice - mapInfo: ${mapInfo ? debugStringify(mapInfo) : 'undefined'}`);
51
- if (mapInfo && mapInfo.maps && mapInfo.maps.length > 0) {
52
- const roomDataMap = mapInfo.maps[0].rooms.map((r) => [r.id, parseInt(r.iot_name_id), r.tag]);
53
- const roomMap = new RoomMap(roomDataMap, rooms);
54
- platform.log.notice(`getRoomMapFromDevice - roomMap: ${debugStringify(roomMap)}`);
55
- platform.log.notice('-------------------------------------------2--------------------------------------------------------');
56
- return roomMap;
57
- }
58
- }
59
- return new RoomMap([], rooms);
60
- }
61
- async getRoomMap(duid) {
62
- const platform = this.platform;
63
- const robot = platform.robots.get(duid);
64
- if (robot === undefined) {
65
- platform.log.error(`Error6: Robot with DUID ${duid} not found`);
66
- return undefined;
67
- }
68
- if (platform.roborockService === undefined)
69
- return undefined;
70
- const rooms = robot.device.rooms ?? [];
71
- if (robot.roomInfo === undefined) {
72
- const roomData = await platform.roborockService.getRoomMappings(robot.device.duid);
73
- if (roomData !== undefined && roomData.length > 0) {
74
- robot.roomInfo = new RoomMap(roomData ?? [], rooms);
75
- return robot.roomInfo;
76
- }
77
- }
78
- if (robot.roomInfo === undefined) {
79
- const mapInfo = await platform.roborockService.getMapInformation(robot.device.duid);
80
- if (mapInfo && mapInfo.maps && mapInfo.maps.length > 0) {
81
- platform.log.error(`getRoomMap - mapInfo: ${debugStringify(mapInfo.maps)}`);
82
- const roomDataMap = mapInfo.maps[0].rooms.map((r) => [r.id, parseInt(r.iot_name_id), r.tag]);
83
- robot.roomInfo = new RoomMap(roomDataMap, rooms);
84
- }
85
- }
86
- return robot.roomInfo;
87
- }
88
35
  async updateFromMQTTMessage(messageSource, messageData, duid = '', tracked = false) {
89
36
  const platform = this.platform;
90
37
  duid = duid || messageData?.duid || '';
@@ -146,7 +93,7 @@ export class PlatformRunner {
146
93
  }
147
94
  else {
148
95
  const currentMappedAreas = this.platform.roborockService?.getSupportedAreas(duid);
149
- const roomMap = await this.getRoomMap(duid);
96
+ const roomMap = await getRoomMap(duid, this.platform);
150
97
  const segment_id = data.cleaning_info?.segment_id ?? -1;
151
98
  const target_segment_id = data.cleaning_info?.target_segment_id ?? -1;
152
99
  let target_room_id = roomMap?.rooms.find((x) => x.id === segment_id || x.alternativeId === segment_id.toString())?.id ?? -1;
@@ -237,7 +184,7 @@ export class PlatformRunner {
237
184
  }
238
185
  case Protocol.rpc_response: {
239
186
  const response = data.dps[messageType];
240
- if (!self.isStatusUpdate(response.result)) {
187
+ if (!isStatusUpdate(response.result)) {
241
188
  platform.log.debug('Ignore message:', debugStringify(data));
242
189
  return;
243
190
  }
@@ -271,7 +218,25 @@ export class PlatformRunner {
271
218
  });
272
219
  break;
273
220
  }
274
- 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
+ }
275
240
  case Protocol.back_type: {
276
241
  break;
277
242
  }
@@ -309,15 +274,6 @@ export class PlatformRunner {
309
274
  }
310
275
  return undefined;
311
276
  }
312
- isStatusUpdate(result) {
313
- return (Array.isArray(result) &&
314
- result.length > 0 &&
315
- typeof result[0] === 'object' &&
316
- result[0] !== null &&
317
- 'msg_ver' in result[0] &&
318
- result[0].msg_ver !== undefined &&
319
- result[0].msg_ver !== null);
320
- }
321
277
  updateFromHomeData(homeData) {
322
278
  const platform = this.platform;
323
279
  if (platform.robots.size === 0)
@@ -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
  }
@@ -61,10 +61,10 @@ export class LocalNetworkClient extends AbstractClient {
61
61
  async onConnect() {
62
62
  this.logger.debug(` [LocalNetworkClient]: ${this.duid} connected to ${this.ip}`);
63
63
  this.logger.debug(` [LocalNetworkClient]: ${this.duid} socket writable: ${this.socket?.writable}, readable: ${this.socket?.readable}`);
64
- await this.sendHelloMessage();
65
- this.pingInterval = setInterval(this.sendPingRequest.bind(this), 5000);
66
64
  this.connected = true;
67
65
  this.retryCount = 0;
66
+ await this.sendHelloMessage();
67
+ this.pingInterval = setInterval(this.sendPingRequest.bind(this), 5000);
68
68
  await this.connectionListeners.onConnected(this.duid);
69
69
  }
70
70
  async onDisconnect(hadError) {
@@ -18,10 +18,6 @@ export class SyncMessageListener {
18
18
  const dps = message.get(Protocol.rpc_response);
19
19
  const messageId = dps.id;
20
20
  const responseHandler = this.pending.get(messageId);
21
- const result = dps.result;
22
- if (result && result.length == 1 && result[0] == 'ok') {
23
- return;
24
- }
25
21
  if (responseHandler) {
26
22
  responseHandler(dps.result);
27
23
  }
@@ -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' });
@@ -80,7 +79,7 @@ export class MessageProcessor {
80
79
  }
81
80
  async findMyRobot(duid) {
82
81
  const request = new RequestMessage({ method: 'find_me' });
83
- return this.client.send(duid, request);
82
+ return this.client.get(duid, request);
84
83
  }
85
84
  async getCleanModeData(duid) {
86
85
  const currentMopMode = await this.getCustomMessage(duid, new RequestMessage({ method: 'get_mop_mode' }));
@@ -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-rc14",
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-rc14 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-rc14",
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
@@ -1,4 +1,8 @@
1
+ import { debugStringify } from 'matterbridge/logger';
2
+ import { RoomMap } from './model/RoomMap.js';
3
+ import { RoborockMatterbridgePlatform } from './platform.js';
1
4
  import { Device } from './roborockCommunication/index.js';
5
+ import { CloudMessageResult } from './roborockCommunication/Zmodel/messageResult.js';
2
6
 
3
7
  export function getVacuumProperty(device: Device, property: string): number | undefined {
4
8
  if (device) {
@@ -20,3 +24,81 @@ export function getVacuumProperty(device: Device, property: string): number | un
20
24
  export function isSupportedDevice(model: string): boolean {
21
25
  return model.startsWith('roborock.vacuum.');
22
26
  }
27
+
28
+ export function isStatusUpdate(result: unknown): boolean {
29
+ return (
30
+ Array.isArray(result) &&
31
+ result.length > 0 &&
32
+ typeof result[0] === 'object' &&
33
+ result[0] !== null &&
34
+ 'msg_ver' in result[0] &&
35
+ (result[0] as CloudMessageResult).msg_ver !== undefined &&
36
+ (result[0] as CloudMessageResult).msg_ver !== null
37
+ );
38
+ }
39
+
40
+ export async function getRoomMap(duid: string, platform: RoborockMatterbridgePlatform): Promise<RoomMap | undefined> {
41
+ const robot = platform.robots.get(duid);
42
+ if (robot === undefined) {
43
+ platform.log.error(`Error6: Robot with DUID ${duid} not found`);
44
+ return undefined;
45
+ }
46
+
47
+ if (platform.roborockService === undefined) return undefined;
48
+
49
+ const rooms = robot.device.rooms ?? [];
50
+ if (robot.roomInfo === undefined) {
51
+ const roomData = await platform.roborockService.getRoomMappings(robot.device.duid);
52
+ if (roomData !== undefined && roomData.length > 0) {
53
+ robot.roomInfo = new RoomMap(roomData ?? [], rooms);
54
+ return robot.roomInfo;
55
+ }
56
+ }
57
+
58
+ if (robot.roomInfo === undefined) {
59
+ const mapInfo = await platform.roborockService.getMapInformation(robot.device.duid);
60
+ if (mapInfo && mapInfo.allRooms && mapInfo.allRooms.length > 0) {
61
+ platform.log.error(`getRoomMap - mapInfo: ${debugStringify(mapInfo.allRooms)}`);
62
+
63
+ const roomDataMap = mapInfo.allRooms.map((r) => [r.id, parseInt(r.iot_name_id), r.tag] as [number, number, number]);
64
+ robot.roomInfo = new RoomMap(roomDataMap, rooms);
65
+ }
66
+ }
67
+
68
+ return robot.roomInfo;
69
+ }
70
+
71
+ export async function getRoomMapFromDevice(device: Device, platform: RoborockMatterbridgePlatform): Promise<RoomMap> {
72
+ const rooms = device?.rooms ?? [];
73
+
74
+ platform.log.notice('-------------------------------------------0--------------------------------------------------------');
75
+ platform.log.notice(`getRoomMapFromDevice - device.rooms: ${debugStringify(rooms)}`);
76
+
77
+ if (device && platform.roborockService) {
78
+ const roomData = await platform.roborockService.getRoomMappings(device.duid);
79
+ if (roomData !== undefined && roomData.length > 0) {
80
+ platform.log.notice(`getRoomMapFromDevice - roomData: ${debugStringify(roomData ?? [])}`);
81
+
82
+ const roomMap = new RoomMap(roomData ?? [], rooms);
83
+
84
+ platform.log.notice(`getRoomMapFromDevice - roomMap: ${debugStringify(roomMap)}`);
85
+ platform.log.notice('-------------------------------------------1--------------------------------------------------------');
86
+ return roomMap;
87
+ }
88
+
89
+ const mapInfo = await platform.roborockService.getMapInformation(device.duid);
90
+ platform.log.notice(`getRoomMapFromDevice - mapInfo: ${mapInfo ? debugStringify(mapInfo) : 'undefined'}`);
91
+
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]);
94
+
95
+ const roomMap = new RoomMap(roomDataMap, rooms);
96
+
97
+ platform.log.notice(`getRoomMapFromDevice - roomMap: ${debugStringify(roomMap)}`);
98
+ platform.log.notice('-------------------------------------------2--------------------------------------------------------');
99
+ return roomMap;
100
+ }
101
+ }
102
+
103
+ return new RoomMap([], rooms);
104
+ }