matterbridge-roborock-vacuum-plugin 1.1.0-rc15 → 1.1.0-rc17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/build.yml +5 -1
- package/.github/workflows/coverage.yml +5 -1
- package/CHANGELOG.md +62 -0
- package/dist/helper.js +24 -21
- package/dist/initialData/getSupportedAreas.js +91 -48
- package/dist/model/ExperimentalFeatureSetting.js +1 -0
- package/dist/model/RoomMap.js +13 -7
- package/dist/model/roomIndexMap.js +17 -0
- package/dist/platform.js +7 -5
- package/dist/platformRunner.js +32 -5
- package/dist/roborockCommunication/Zenum/additionalPropCode.js +4 -0
- package/dist/roborockCommunication/Zmodel/mapInfo.js +17 -16
- package/dist/roborockCommunication/broadcast/messageProcessor.js +2 -3
- package/dist/roborockCommunication/index.js +1 -0
- package/dist/roborockService.js +11 -2
- package/dist/rvc.js +5 -2
- package/matterbridge-roborock-vacuum-plugin.config.json +4 -3
- package/matterbridge-roborock-vacuum-plugin.schema.json +42 -10
- package/package.json +1 -1
- package/src/helper.ts +28 -26
- package/src/initialData/getSupportedAreas.ts +115 -45
- package/src/model/ExperimentalFeatureSetting.ts +2 -0
- package/src/model/RoomMap.ts +33 -15
- package/src/model/roomIndexMap.ts +20 -0
- package/src/platform.ts +8 -5
- package/src/platformRunner.ts +41 -6
- package/src/roborockCommunication/Zenum/additionalPropCode.ts +3 -0
- package/src/roborockCommunication/Zmodel/map.ts +2 -2
- package/src/roborockCommunication/Zmodel/mapInfo.ts +37 -18
- package/src/roborockCommunication/Zmodel/multipleMap.ts +2 -2
- package/src/roborockCommunication/broadcast/messageProcessor.ts +2 -4
- package/src/roborockCommunication/index.ts +1 -0
- package/src/roborockService.ts +20 -2
- package/src/rvc.ts +7 -2
- package/src/tests/helper.test.ts +113 -0
- package/src/tests/initialData/getSupportedAreas.test.ts +80 -7
- package/src/tests/platformRunner2.test.ts +145 -5
- package/src/tests/platformRunner3.test.ts +54 -0
- package/src/tests/roborockCommunication/broadcast/messageProcessor.test.ts +2 -7
- package/src/tests/roborockService.test.ts +22 -1
- package/web-for-testing/package-lock.json +14 -12
- package/web-for-testing/package.json +1 -1
- package/web-for-testing/src/accountStore.ts +0 -10
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
|
@@ -27,6 +27,7 @@ export function isStatusUpdate(result) {
|
|
|
27
27
|
}
|
|
28
28
|
export async function getRoomMap(duid, platform) {
|
|
29
29
|
const robot = platform.robots.get(duid);
|
|
30
|
+
const enableMultipleMap = (platform.enableExperimentalFeature?.enableExperimentalFeature && platform.enableExperimentalFeature?.advancedFeature.enableMultipleMap) ?? false;
|
|
30
31
|
if (robot === undefined) {
|
|
31
32
|
platform.log.error(`Error6: Robot with DUID ${duid} not found`);
|
|
32
33
|
return undefined;
|
|
@@ -35,44 +36,46 @@ export async function getRoomMap(duid, platform) {
|
|
|
35
36
|
return undefined;
|
|
36
37
|
const rooms = robot.device.rooms ?? [];
|
|
37
38
|
if (robot.roomInfo === undefined) {
|
|
38
|
-
const
|
|
39
|
-
if (
|
|
40
|
-
|
|
41
|
-
|
|
39
|
+
const mapInfo = await platform.roborockService.getMapInformation(robot.device.duid);
|
|
40
|
+
if (mapInfo && mapInfo.allRooms && mapInfo.allRooms.length > 0) {
|
|
41
|
+
platform.log.info(`getRoomMap - mapInfo: ${debugStringify(mapInfo.allRooms)}`);
|
|
42
|
+
robot.roomInfo = new RoomMap(mapInfo.allRooms, rooms, mapInfo.maps, enableMultipleMap);
|
|
42
43
|
}
|
|
43
44
|
}
|
|
44
45
|
if (robot.roomInfo === undefined) {
|
|
45
|
-
const
|
|
46
|
-
if (
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
robot.roomInfo
|
|
46
|
+
const roomData = await platform.roborockService.getRoomMappings(robot.device.duid);
|
|
47
|
+
if (roomData !== undefined && roomData.length > 0) {
|
|
48
|
+
const roomDataMap = roomData.map((r) => ({ id: r[0], iot_name_id: String(r[1]), globalId: r[1], tag: r[2], mapId: 0, displayName: undefined }));
|
|
49
|
+
robot.roomInfo = new RoomMap(roomDataMap, rooms, [], enableMultipleMap);
|
|
50
|
+
return robot.roomInfo;
|
|
50
51
|
}
|
|
51
52
|
}
|
|
52
53
|
return robot.roomInfo;
|
|
53
54
|
}
|
|
54
55
|
export async function getRoomMapFromDevice(device, platform) {
|
|
55
56
|
const rooms = device?.rooms ?? [];
|
|
57
|
+
const enableMultipleMap = (platform.enableExperimentalFeature?.enableExperimentalFeature && platform.enableExperimentalFeature?.advancedFeature.enableMultipleMap) ?? false;
|
|
56
58
|
platform.log.notice('-------------------------------------------0--------------------------------------------------------');
|
|
57
|
-
platform.log.notice(`getRoomMapFromDevice: ${debugStringify(rooms)}`);
|
|
59
|
+
platform.log.notice(`getRoomMapFromDevice - device.rooms: ${debugStringify(rooms)}`);
|
|
58
60
|
if (device && platform.roborockService) {
|
|
61
|
+
const mapInfo = await platform.roborockService.getMapInformation(device.duid);
|
|
62
|
+
platform.log.notice(`getRoomMapFromDevice - mapInfo: ${mapInfo ? debugStringify(mapInfo) : 'undefined'}`);
|
|
63
|
+
if (mapInfo && mapInfo.allRooms && mapInfo.allRooms.length > 0) {
|
|
64
|
+
const roomDataMap = mapInfo.allRooms;
|
|
65
|
+
const roomMap = new RoomMap(roomDataMap, rooms, mapInfo.maps, enableMultipleMap);
|
|
66
|
+
platform.log.notice(`getRoomMapFromDevice - roomMap: ${debugStringify(roomMap)}`);
|
|
67
|
+
platform.log.notice('-------------------------------------------2--------------------------------------------------------');
|
|
68
|
+
return roomMap;
|
|
69
|
+
}
|
|
59
70
|
const roomData = await platform.roborockService.getRoomMappings(device.duid);
|
|
60
71
|
if (roomData !== undefined && roomData.length > 0) {
|
|
61
72
|
platform.log.notice(`getRoomMapFromDevice - roomData: ${debugStringify(roomData ?? [])}`);
|
|
62
|
-
const
|
|
73
|
+
const roomDataMap = roomData.map((r) => ({ id: r[0], iot_name_id: String(r[1]), globalId: r[1], tag: r[2], mapId: 0, displayName: undefined }));
|
|
74
|
+
const roomMap = new RoomMap(roomDataMap ?? [], rooms, [], enableMultipleMap);
|
|
63
75
|
platform.log.notice(`getRoomMapFromDevice - roomMap: ${debugStringify(roomMap)}`);
|
|
64
76
|
platform.log.notice('-------------------------------------------1--------------------------------------------------------');
|
|
65
77
|
return roomMap;
|
|
66
78
|
}
|
|
67
|
-
const mapInfo = await platform.roborockService.getMapInformation(device.duid);
|
|
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]);
|
|
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
79
|
}
|
|
77
|
-
return new RoomMap([], rooms);
|
|
80
|
+
return new RoomMap([], rooms, [], enableMultipleMap);
|
|
78
81
|
}
|
|
@@ -1,77 +1,120 @@
|
|
|
1
1
|
import { debugStringify } from 'matterbridge/logger';
|
|
2
2
|
import { randomInt } from 'node:crypto';
|
|
3
|
-
|
|
3
|
+
import { RoomIndexMap } from '../model/roomIndexMap.js';
|
|
4
|
+
export function getSupportedAreas(vacuumRooms, roomMap, enableMultipleMap = false, log) {
|
|
4
5
|
log?.debug('getSupportedAreas-vacuum room', debugStringify(vacuumRooms));
|
|
5
6
|
log?.debug('getSupportedAreas-roomMap', roomMap ? debugStringify(roomMap) : 'undefined');
|
|
6
|
-
|
|
7
|
-
|
|
7
|
+
const noVacuumRooms = !vacuumRooms || vacuumRooms.length === 0;
|
|
8
|
+
const noRoomMap = !roomMap?.rooms || roomMap.rooms.length === 0;
|
|
9
|
+
if (noVacuumRooms || noRoomMap) {
|
|
10
|
+
if (noVacuumRooms) {
|
|
8
11
|
log?.error('No rooms found');
|
|
9
12
|
}
|
|
10
|
-
if (
|
|
13
|
+
if (noRoomMap) {
|
|
11
14
|
log?.error('No room map found');
|
|
12
15
|
}
|
|
13
|
-
return
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
16
|
+
return {
|
|
17
|
+
supportedAreas: [
|
|
18
|
+
{
|
|
19
|
+
areaId: 1,
|
|
20
|
+
mapId: null,
|
|
21
|
+
areaInfo: {
|
|
22
|
+
locationInfo: {
|
|
23
|
+
locationName: 'Unknown',
|
|
24
|
+
floorNumber: null,
|
|
25
|
+
areaType: null,
|
|
26
|
+
},
|
|
27
|
+
landmarkInfo: null,
|
|
22
28
|
},
|
|
23
|
-
landmarkInfo: null,
|
|
24
29
|
},
|
|
25
|
-
|
|
26
|
-
|
|
30
|
+
],
|
|
31
|
+
supportedMaps: [],
|
|
32
|
+
roomIndexMap: new RoomIndexMap(new Map([[1, { roomId: 1, mapId: null }]])),
|
|
33
|
+
};
|
|
27
34
|
}
|
|
28
|
-
const supportedAreas =
|
|
35
|
+
const { supportedAreas, indexMap } = processValidData(enableMultipleMap, vacuumRooms, roomMap);
|
|
36
|
+
const duplicated = findDuplicatedAreaIds(supportedAreas, log);
|
|
37
|
+
if (duplicated) {
|
|
29
38
|
return {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
39
|
+
supportedAreas: [
|
|
40
|
+
{
|
|
41
|
+
areaId: 2,
|
|
42
|
+
mapId: null,
|
|
43
|
+
areaInfo: {
|
|
44
|
+
locationInfo: {
|
|
45
|
+
locationName: 'Unknown - Duplicated Areas Found',
|
|
46
|
+
floorNumber: null,
|
|
47
|
+
areaType: null,
|
|
48
|
+
},
|
|
49
|
+
landmarkInfo: null,
|
|
50
|
+
},
|
|
37
51
|
},
|
|
38
|
-
|
|
39
|
-
|
|
52
|
+
],
|
|
53
|
+
supportedMaps: [],
|
|
54
|
+
roomIndexMap: new RoomIndexMap(new Map([[2, { roomId: 2, mapId: null }]])),
|
|
40
55
|
};
|
|
41
|
-
}
|
|
56
|
+
}
|
|
57
|
+
const supportedMaps = getSupportedMaps(enableMultipleMap, supportedAreas, roomMap);
|
|
42
58
|
log?.debug('getSupportedAreas - supportedAreas', debugStringify(supportedAreas));
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
locationInfo: {
|
|
51
|
-
locationName: 'Unknown',
|
|
52
|
-
floorNumber: null,
|
|
53
|
-
areaType: null,
|
|
54
|
-
},
|
|
55
|
-
landmarkInfo: null,
|
|
56
|
-
},
|
|
57
|
-
},
|
|
58
|
-
]
|
|
59
|
-
: supportedAreas;
|
|
59
|
+
log?.debug('getSupportedAreas - supportedMaps', debugStringify(supportedMaps));
|
|
60
|
+
const roomIndexMap = new RoomIndexMap(indexMap);
|
|
61
|
+
return {
|
|
62
|
+
supportedAreas,
|
|
63
|
+
supportedMaps,
|
|
64
|
+
roomIndexMap,
|
|
65
|
+
};
|
|
60
66
|
}
|
|
61
67
|
function findDuplicatedAreaIds(areas, log) {
|
|
62
68
|
const seen = new Set();
|
|
63
69
|
const duplicates = [];
|
|
64
70
|
for (const area of areas) {
|
|
65
|
-
|
|
66
|
-
|
|
71
|
+
const key = `${area.areaId}=${area.mapId}`;
|
|
72
|
+
if (seen.has(key)) {
|
|
73
|
+
duplicates.push({ areaId: area.areaId, mapId: area.mapId ?? 0 });
|
|
67
74
|
}
|
|
68
75
|
else {
|
|
69
|
-
seen.add(
|
|
76
|
+
seen.add(key);
|
|
70
77
|
}
|
|
71
78
|
}
|
|
72
79
|
if (duplicates.length > 0 && log) {
|
|
73
|
-
const duplicated = areas.filter((
|
|
80
|
+
const duplicated = areas.filter(({ areaId, mapId }) => duplicates.some((y) => y.areaId === areaId && y.mapId === (mapId ?? 0)));
|
|
74
81
|
log.error(`Duplicated areaId(s) found: ${debugStringify(duplicated)}`);
|
|
75
82
|
}
|
|
76
83
|
return duplicates.length > 0;
|
|
77
84
|
}
|
|
85
|
+
function processValidData(enableMultipleMap, vacuumRooms, roomMap) {
|
|
86
|
+
const indexMap = new Map();
|
|
87
|
+
const supportedAreas = roomMap?.rooms !== undefined && roomMap.rooms.length > 0
|
|
88
|
+
? roomMap.rooms.map((room, index) => {
|
|
89
|
+
const locationName = room.displayName ?? vacuumRooms.find((r) => r.id === room.globalId || r.id === room.id)?.name ?? `Unknown Room ${randomInt(1000, 9999)}`;
|
|
90
|
+
const areaId = index + 100;
|
|
91
|
+
const mapId = enableMultipleMap ? (room.mapId ?? null) : null;
|
|
92
|
+
indexMap.set(areaId, { roomId: room.id, mapId: room.mapId ?? null });
|
|
93
|
+
return {
|
|
94
|
+
areaId: areaId,
|
|
95
|
+
mapId: mapId,
|
|
96
|
+
areaInfo: {
|
|
97
|
+
locationInfo: {
|
|
98
|
+
locationName: locationName,
|
|
99
|
+
floorNumber: room.mapId ?? null,
|
|
100
|
+
areaType: null,
|
|
101
|
+
},
|
|
102
|
+
landmarkInfo: null,
|
|
103
|
+
},
|
|
104
|
+
};
|
|
105
|
+
})
|
|
106
|
+
: [];
|
|
107
|
+
return {
|
|
108
|
+
supportedAreas,
|
|
109
|
+
indexMap,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
function getSupportedMaps(enableMultipleMap, supportedAreas, roomMap) {
|
|
113
|
+
if (enableMultipleMap) {
|
|
114
|
+
return (roomMap?.mapInfo?.map((map) => ({
|
|
115
|
+
mapId: map.id,
|
|
116
|
+
name: map.name ?? `Map ${map.id}`,
|
|
117
|
+
})) ?? []);
|
|
118
|
+
}
|
|
119
|
+
return [];
|
|
120
|
+
}
|
package/dist/model/RoomMap.js
CHANGED
|
@@ -1,13 +1,19 @@
|
|
|
1
1
|
export class RoomMap {
|
|
2
|
-
rooms
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
rooms;
|
|
3
|
+
mapInfo;
|
|
4
|
+
constructor(roomData, rooms, mapInfo, enableMultipleMap) {
|
|
5
|
+
const mapid = mapInfo[0]?.id ?? 0;
|
|
6
|
+
const roomDataTmp = enableMultipleMap ? roomData : roomData.filter((room) => room.mapId === undefined || room.mapId === mapid);
|
|
7
|
+
this.rooms = roomDataTmp.map(({ id, globalId, tag, mapId }) => {
|
|
8
|
+
const room = rooms.find((r) => Number(r.id) === Number(globalId) || Number(r.id) === Number(id));
|
|
5
9
|
return {
|
|
6
|
-
id
|
|
7
|
-
globalId: Number(
|
|
8
|
-
displayName:
|
|
9
|
-
alternativeId: `${
|
|
10
|
+
id,
|
|
11
|
+
globalId: globalId !== undefined ? Number(globalId) : undefined,
|
|
12
|
+
displayName: room?.name,
|
|
13
|
+
alternativeId: `${id}${tag}`,
|
|
14
|
+
mapId,
|
|
10
15
|
};
|
|
11
16
|
});
|
|
17
|
+
this.mapInfo = mapInfo;
|
|
12
18
|
}
|
|
13
19
|
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export class RoomIndexMap {
|
|
2
|
+
indexMap;
|
|
3
|
+
roomMap;
|
|
4
|
+
constructor(roomMap) {
|
|
5
|
+
this.indexMap = roomMap;
|
|
6
|
+
this.roomMap = new Map();
|
|
7
|
+
for (const [areaId, { roomId }] of roomMap.entries()) {
|
|
8
|
+
this.roomMap.set(roomId, areaId);
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
getAreaId(roomId) {
|
|
12
|
+
return this.roomMap.get(roomId);
|
|
13
|
+
}
|
|
14
|
+
getRoomId(areaId) {
|
|
15
|
+
return this.indexMap.get(areaId)?.roomId;
|
|
16
|
+
}
|
|
17
|
+
}
|
package/dist/platform.js
CHANGED
|
@@ -26,8 +26,8 @@ export class RoborockMatterbridgePlatform extends MatterbridgeDynamicPlatform {
|
|
|
26
26
|
rrHomeId;
|
|
27
27
|
constructor(matterbridge, log, config) {
|
|
28
28
|
super(matterbridge, log, config);
|
|
29
|
-
if (this.verifyMatterbridgeVersion === undefined || typeof this.verifyMatterbridgeVersion !== 'function' || !this.verifyMatterbridgeVersion('3.1.
|
|
30
|
-
throw new Error(`This plugin requires Matterbridge version >= "3.1.
|
|
29
|
+
if (this.verifyMatterbridgeVersion === undefined || typeof this.verifyMatterbridgeVersion !== 'function' || !this.verifyMatterbridgeVersion('3.1.8')) {
|
|
30
|
+
throw new Error(`This plugin requires Matterbridge version >= "3.1.8". Please update Matterbridge from ${this.matterbridge.matterbridgeVersion} to the latest version in the frontend.`);
|
|
31
31
|
}
|
|
32
32
|
this.log.info('Initializing platform:', this.config.name);
|
|
33
33
|
if (config.whiteList === undefined)
|
|
@@ -159,14 +159,16 @@ 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?.
|
|
163
|
-
vacuum.rooms = rooms.map((room) => ({ id: room.
|
|
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));
|
|
167
167
|
const behaviorHandler = configurateBehavior(vacuum.data.model, vacuum.duid, this.roborockService, this.cleanModeSettings, this.enableExperimentalFeature?.advancedFeature?.forceRunAtDefault ?? false, this.log);
|
|
168
|
-
const
|
|
168
|
+
const enableMultipleMap = this.enableExperimentalFeature?.enableExperimentalFeature && this.enableExperimentalFeature.advancedFeature?.enableMultipleMap;
|
|
169
|
+
const { supportedAreas, roomIndexMap } = getSupportedAreas(vacuum.rooms, roomMap, enableMultipleMap, this.log);
|
|
169
170
|
this.roborockService.setSupportedAreas(vacuum.duid, supportedAreas);
|
|
171
|
+
this.roborockService.setSupportedAreaIndexMap(vacuum.duid, roomIndexMap);
|
|
170
172
|
let routineAsRoom = [];
|
|
171
173
|
if (this.enableExperimentalFeature?.enableExperimentalFeature && this.enableExperimentalFeature.advancedFeature?.showRoutinesAsRoom) {
|
|
172
174
|
routineAsRoom = getSupportedScenes(vacuum.scenes, this.log);
|
package/dist/platformRunner.js
CHANGED
|
@@ -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';
|
|
@@ -92,6 +93,7 @@ export class PlatformRunner {
|
|
|
92
93
|
}
|
|
93
94
|
else {
|
|
94
95
|
const currentMappedAreas = this.platform.roborockService?.getSupportedAreas(duid);
|
|
96
|
+
const roomIndexMap = this.platform.roborockService?.getSupportedAreasIndexMap(duid);
|
|
95
97
|
const roomMap = await getRoomMap(duid, this.platform);
|
|
96
98
|
const segment_id = data.cleaning_info?.segment_id ?? -1;
|
|
97
99
|
const target_segment_id = data.cleaning_info?.target_segment_id ?? -1;
|
|
@@ -101,7 +103,8 @@ export class PlatformRunner {
|
|
|
101
103
|
if (segment_id !== -1 && isMappedArea) {
|
|
102
104
|
this.platform.log.debug(`RoomMap: ${roomMap ? debugStringify(roomMap) : 'undefined'}`);
|
|
103
105
|
this.platform.log.debug(`Part1: CurrentRoom: ${segment_id}, room name: ${roomMap?.rooms.find((x) => x.id === segment_id || x.alternativeId === segment_id.toString())?.displayName ?? 'unknown'}`);
|
|
104
|
-
|
|
106
|
+
const areaId = roomIndexMap?.getAreaId(segment_id) ?? null;
|
|
107
|
+
robot.updateAttribute(ServiceArea.Cluster.id, 'currentArea', areaId, platform.log);
|
|
105
108
|
}
|
|
106
109
|
if (segment_id == -1) {
|
|
107
110
|
const isTargetMappedArea = currentMappedAreas?.some((x) => x.areaId == target_segment_id);
|
|
@@ -110,7 +113,8 @@ export class PlatformRunner {
|
|
|
110
113
|
if (target_segment_id !== -1 && isTargetMappedArea) {
|
|
111
114
|
this.platform.log.debug(`RoomMap: ${roomMap ? debugStringify(roomMap) : 'undefined'}`);
|
|
112
115
|
this.platform.log.debug(`Part2: TargetRoom: ${target_segment_id}, room name: ${roomMap?.rooms.find((x) => x.id === target_segment_id || x.alternativeId === segment_id.toString())?.displayName ?? 'unknown'}`);
|
|
113
|
-
|
|
116
|
+
const areaId = roomIndexMap?.getAreaId(target_segment_id) ?? null;
|
|
117
|
+
robot.updateAttribute(ServiceArea.Cluster.id, 'currentArea', areaId, platform.log);
|
|
114
118
|
}
|
|
115
119
|
}
|
|
116
120
|
if (target_segment_id == -1 && segment_id == -1) {
|
|
@@ -217,7 +221,30 @@ export class PlatformRunner {
|
|
|
217
221
|
});
|
|
218
222
|
break;
|
|
219
223
|
}
|
|
220
|
-
case Protocol.additional_props:
|
|
224
|
+
case Protocol.additional_props: {
|
|
225
|
+
platform.log.notice(`Received additional properties for robot ${duid}: ${debugStringify(data)}`);
|
|
226
|
+
const propCode = data.dps[Protocol.additional_props];
|
|
227
|
+
platform.log.debug(`DPS for additional properties: ${propCode}, AdditionalPropCode: ${AdditionalPropCode[propCode]}`);
|
|
228
|
+
const enableMultipleMap = (platform.enableExperimentalFeature?.enableExperimentalFeature && platform.enableExperimentalFeature?.advancedFeature?.enableMultipleMap) ?? false;
|
|
229
|
+
if (propCode === AdditionalPropCode.map_change) {
|
|
230
|
+
platform.log.notice('------------------------ get roomData ----------------------------');
|
|
231
|
+
const roomMap = await getRoomMapFromDevice(robot.device, platform);
|
|
232
|
+
platform.log.notice('------------------------ Room map updated ------------------------');
|
|
233
|
+
const { supportedAreas, supportedMaps, roomIndexMap } = getSupportedAreas(robot.device.rooms, roomMap, enableMultipleMap, platform.log);
|
|
234
|
+
platform.log.notice(`Supported areas: ${debugStringify(supportedAreas)}`);
|
|
235
|
+
platform.log.notice('------------------------ Supported areas updated ------------------');
|
|
236
|
+
platform.roborockService?.setSupportedAreas(duid, supportedAreas);
|
|
237
|
+
platform.roborockService?.setSelectedAreas(duid, []);
|
|
238
|
+
robot.updateAttribute(ServiceArea.Cluster.id, 'supportedAreas', supportedAreas, platform.log);
|
|
239
|
+
robot.updateAttribute(ServiceArea.Cluster.id, 'selectedAreas', [], platform.log);
|
|
240
|
+
robot.updateAttribute(ServiceArea.Cluster.id, 'currentArea', null, platform.log);
|
|
241
|
+
if (enableMultipleMap) {
|
|
242
|
+
platform.roborockService?.setSupportedAreaIndexMap(duid, roomIndexMap);
|
|
243
|
+
robot.updateAttribute(ServiceArea.Cluster.id, 'supportedMaps', supportedMaps, platform.log);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
break;
|
|
247
|
+
}
|
|
221
248
|
case Protocol.back_type: {
|
|
222
249
|
break;
|
|
223
250
|
}
|
|
@@ -1,28 +1,29 @@
|
|
|
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.
|
|
6
|
-
|
|
7
|
-
id:
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
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
|
});
|
|
21
22
|
}
|
|
22
23
|
getById(id) {
|
|
23
24
|
return this.maps.find((m) => m.id === id)?.name;
|
|
24
25
|
}
|
|
25
26
|
getByName(name) {
|
|
26
|
-
return this.maps.find((m) => m.name === name.toLowerCase())?.id;
|
|
27
|
+
return this.maps.find((m) => m.name?.toLowerCase() === name.toLowerCase())?.id;
|
|
27
28
|
}
|
|
28
29
|
}
|
|
@@ -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
|
|
42
|
+
async getRooms(duid) {
|
|
44
43
|
const request = new RequestMessage({ method: 'get_room_mapping' });
|
|
45
|
-
return this.client.get(duid, request)
|
|
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';
|
package/dist/roborockService.js
CHANGED
|
@@ -22,6 +22,7 @@ export default class RoborockService {
|
|
|
22
22
|
supportedAreas = new Map();
|
|
23
23
|
supportedRoutines = new Map();
|
|
24
24
|
selectedAreas = new Map();
|
|
25
|
+
supportedAreaIndexMaps = new Map();
|
|
25
26
|
vacuumNeedAPIV3 = ['roborock.vacuum.ss07'];
|
|
26
27
|
constructor(authenticateApiSupplier = (logger) => new RoborockAuthenticateApi(logger), iotApiSupplier = (logger, ud) => new RoborockIoTApi(ud, logger), refreshInterval, clientManager, logger) {
|
|
27
28
|
this.logger = logger;
|
|
@@ -52,7 +53,9 @@ export default class RoborockService {
|
|
|
52
53
|
}
|
|
53
54
|
setSelectedAreas(duid, selectedAreas) {
|
|
54
55
|
this.logger.debug('RoborockService - setSelectedAreas', selectedAreas);
|
|
55
|
-
this.
|
|
56
|
+
const roomIds = selectedAreas.map((areaId) => this.supportedAreaIndexMaps.get(duid)?.getRoomId(areaId)) ?? [];
|
|
57
|
+
this.logger.debug('RoborockService - setSelectedAreas - roomIds', roomIds);
|
|
58
|
+
this.selectedAreas.set(duid, roomIds.filter((id) => id !== undefined).map((id) => id));
|
|
56
59
|
}
|
|
57
60
|
getSelectedAreas(duid) {
|
|
58
61
|
return this.selectedAreas.get(duid) ?? [];
|
|
@@ -60,12 +63,18 @@ export default class RoborockService {
|
|
|
60
63
|
setSupportedAreas(duid, supportedAreas) {
|
|
61
64
|
this.supportedAreas.set(duid, supportedAreas);
|
|
62
65
|
}
|
|
66
|
+
setSupportedAreaIndexMap(duid, indexMap) {
|
|
67
|
+
this.supportedAreaIndexMaps.set(duid, indexMap);
|
|
68
|
+
}
|
|
63
69
|
setSupportedScenes(duid, routineAsRooms) {
|
|
64
70
|
this.supportedRoutines.set(duid, routineAsRooms);
|
|
65
71
|
}
|
|
66
72
|
getSupportedAreas(duid) {
|
|
67
73
|
return this.supportedAreas.get(duid);
|
|
68
74
|
}
|
|
75
|
+
getSupportedAreasIndexMap(duid) {
|
|
76
|
+
return this.supportedAreaIndexMaps.get(duid);
|
|
77
|
+
}
|
|
69
78
|
async getCleanModeData(duid) {
|
|
70
79
|
this.logger.notice('RoborockService - getCleanModeData');
|
|
71
80
|
const data = await this.getMessageProcessor(duid)?.getCleanModeData(duid);
|
|
@@ -351,7 +360,7 @@ export default class RoborockService {
|
|
|
351
360
|
assert(this.iotApi !== undefined);
|
|
352
361
|
return this.iotApi.startScene(sceneId);
|
|
353
362
|
}
|
|
354
|
-
getRoomMappings(duid) {
|
|
363
|
+
async getRoomMappings(duid) {
|
|
355
364
|
if (!this.messageClient) {
|
|
356
365
|
this.logger.warn('messageClient not initialized. Waititing for next execution');
|
|
357
366
|
return Promise.resolve(undefined);
|
package/dist/rvc.js
CHANGED
|
@@ -9,14 +9,17 @@ export class RoborockVacuumCleaner extends RoboticVacuumCleaner {
|
|
|
9
9
|
constructor(username, device, roomMap, routineAsRoom, enableExperimentalFeature, log) {
|
|
10
10
|
const cleanModes = getSupportedCleanModes(device.data.model, enableExperimentalFeature);
|
|
11
11
|
const supportedRunModes = getSupportedRunModes();
|
|
12
|
-
const
|
|
12
|
+
const enableMultipleMap = enableExperimentalFeature?.enableExperimentalFeature && enableExperimentalFeature?.advancedFeature?.enableMultipleMap;
|
|
13
|
+
const { supportedAreas, supportedMaps } = getSupportedAreas(device.rooms, roomMap, enableMultipleMap, log);
|
|
14
|
+
const supportedAreaAndRoutines = [...supportedAreas, ...routineAsRoom];
|
|
13
15
|
const deviceName = `${device.name}-${device.duid}`.replace(/\s+/g, '');
|
|
14
16
|
log.debug(`Creating RoborockVacuumCleaner for device: ${deviceName}, model: ${device.data.model}, forceRunAtDefault: ${enableExperimentalFeature?.advancedFeature?.forceRunAtDefault}`);
|
|
15
17
|
log.debug(`Supported Clean Modes: ${JSON.stringify(cleanModes)}`);
|
|
16
18
|
log.debug(`Supported Run Modes: ${JSON.stringify(supportedRunModes)}`);
|
|
17
19
|
log.debug(`Supported Areas: ${JSON.stringify(supportedAreas)}`);
|
|
20
|
+
log.debug(`Supported Maps: ${JSON.stringify(supportedMaps)}`);
|
|
18
21
|
const bridgeMode = enableExperimentalFeature?.enableExperimentalFeature && enableExperimentalFeature?.advancedFeature?.enableServerMode ? 'server' : undefined;
|
|
19
|
-
super(deviceName, device.duid, bridgeMode, supportedRunModes[0].mode, supportedRunModes, cleanModes[0].mode, cleanModes, undefined, undefined, RvcOperationalState.OperationalState.Docked, getOperationalStates(),
|
|
22
|
+
super(deviceName, device.duid, bridgeMode, supportedRunModes[0].mode, supportedRunModes, cleanModes[0].mode, cleanModes, undefined, undefined, RvcOperationalState.OperationalState.Docked, getOperationalStates(), supportedAreaAndRoutines, undefined, supportedAreas[0].areaId, supportedMaps);
|
|
20
23
|
this.username = username;
|
|
21
24
|
this.device = device;
|
|
22
25
|
}
|