matterbridge-roborock-vacuum-plugin 1.1.0-rc16 → 1.1.0-rc18
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/dist/helper.js +23 -20
- package/dist/initialData/getSupportedAreas.js +85 -46
- package/dist/model/ExperimentalFeatureSetting.js +1 -0
- package/dist/model/RoomMap.js +8 -3
- package/dist/model/roomIndexMap.js +17 -0
- package/dist/platform.js +5 -3
- package/dist/platformRunner.js +13 -3
- package/dist/roborockCommunication/Zmodel/mapInfo.js +2 -3
- package/dist/roborockService.js +10 -1
- 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 +27 -24
- package/src/initialData/getSupportedAreas.ts +112 -50
- package/src/model/ExperimentalFeatureSetting.ts +2 -0
- package/src/model/RoomMap.ts +17 -3
- package/src/model/roomIndexMap.ts +20 -0
- package/src/platform.ts +6 -3
- package/src/platformRunner.ts +16 -3
- package/src/roborockCommunication/Zmodel/map.ts +1 -1
- package/src/roborockCommunication/Zmodel/mapInfo.ts +11 -10
- package/src/roborockService.ts +16 -1
- package/src/rvc.ts +7 -2
- package/src/tests/helper.test.ts +47 -47
- package/src/tests/initialData/getSupportedAreas.test.ts +80 -7
- package/src/tests/platformRunner2.test.ts +108 -1
- package/src/tests/roborockService.test.ts +11 -0
- 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/src/helper.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { RoomMap } from './model/RoomMap.js';
|
|
|
3
3
|
import { RoborockMatterbridgePlatform } from './platform.js';
|
|
4
4
|
import { Device } from './roborockCommunication/index.js';
|
|
5
5
|
import { CloudMessageResult } from './roborockCommunication/Zmodel/messageResult.js';
|
|
6
|
+
import { MapRoom } from './roborockCommunication/Zmodel/mapInfo.js';
|
|
6
7
|
|
|
7
8
|
export function getVacuumProperty(device: Device, property: string): number | undefined {
|
|
8
9
|
if (device) {
|
|
@@ -39,6 +40,7 @@ export function isStatusUpdate(result: unknown): boolean {
|
|
|
39
40
|
|
|
40
41
|
export async function getRoomMap(duid: string, platform: RoborockMatterbridgePlatform): Promise<RoomMap | undefined> {
|
|
41
42
|
const robot = platform.robots.get(duid);
|
|
43
|
+
const enableMultipleMap = (platform.enableExperimentalFeature?.enableExperimentalFeature && platform.enableExperimentalFeature?.advancedFeature.enableMultipleMap) ?? false;
|
|
42
44
|
if (robot === undefined) {
|
|
43
45
|
platform.log.error(`Error6: Robot with DUID ${duid} not found`);
|
|
44
46
|
return undefined;
|
|
@@ -47,21 +49,21 @@ export async function getRoomMap(duid: string, platform: RoborockMatterbridgePla
|
|
|
47
49
|
if (platform.roborockService === undefined) return undefined;
|
|
48
50
|
|
|
49
51
|
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
52
|
|
|
58
53
|
if (robot.roomInfo === undefined) {
|
|
59
54
|
const mapInfo = await platform.roborockService.getMapInformation(robot.device.duid);
|
|
60
55
|
if (mapInfo && mapInfo.allRooms && mapInfo.allRooms.length > 0) {
|
|
61
|
-
platform.log.
|
|
56
|
+
platform.log.info(`getRoomMap - mapInfo: ${debugStringify(mapInfo.allRooms)}`);
|
|
57
|
+
robot.roomInfo = new RoomMap(mapInfo.allRooms, rooms, mapInfo.maps, enableMultipleMap);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
62
60
|
|
|
63
|
-
|
|
64
|
-
|
|
61
|
+
if (robot.roomInfo === undefined) {
|
|
62
|
+
const roomData = await platform.roborockService.getRoomMappings(robot.device.duid);
|
|
63
|
+
if (roomData !== undefined && roomData.length > 0) {
|
|
64
|
+
const roomDataMap: MapRoom[] = roomData.map((r) => ({ id: r[0], iot_name_id: String(r[1]), globalId: r[1], tag: r[2], mapId: 0, displayName: undefined }));
|
|
65
|
+
robot.roomInfo = new RoomMap(roomDataMap, rooms, [], enableMultipleMap);
|
|
66
|
+
return robot.roomInfo;
|
|
65
67
|
}
|
|
66
68
|
}
|
|
67
69
|
|
|
@@ -70,35 +72,36 @@ export async function getRoomMap(duid: string, platform: RoborockMatterbridgePla
|
|
|
70
72
|
|
|
71
73
|
export async function getRoomMapFromDevice(device: Device, platform: RoborockMatterbridgePlatform): Promise<RoomMap> {
|
|
72
74
|
const rooms = device?.rooms ?? [];
|
|
75
|
+
const enableMultipleMap = (platform.enableExperimentalFeature?.enableExperimentalFeature && platform.enableExperimentalFeature?.advancedFeature.enableMultipleMap) ?? false;
|
|
73
76
|
|
|
74
77
|
platform.log.notice('-------------------------------------------0--------------------------------------------------------');
|
|
75
78
|
platform.log.notice(`getRoomMapFromDevice - device.rooms: ${debugStringify(rooms)}`);
|
|
76
79
|
|
|
77
80
|
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
81
|
const mapInfo = await platform.roborockService.getMapInformation(device.duid);
|
|
90
82
|
platform.log.notice(`getRoomMapFromDevice - mapInfo: ${mapInfo ? debugStringify(mapInfo) : 'undefined'}`);
|
|
91
83
|
|
|
92
84
|
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]);
|
|
85
|
+
const roomDataMap = mapInfo.allRooms; // .map((r) => [r.id, parseInt(r.iot_name_id), r.tag, r.mapId] as [number, number, number, number]);
|
|
94
86
|
|
|
95
|
-
const roomMap = new RoomMap(roomDataMap, rooms);
|
|
87
|
+
const roomMap = new RoomMap(roomDataMap, rooms, mapInfo.maps, enableMultipleMap);
|
|
96
88
|
|
|
97
89
|
platform.log.notice(`getRoomMapFromDevice - roomMap: ${debugStringify(roomMap)}`);
|
|
98
90
|
platform.log.notice('-------------------------------------------2--------------------------------------------------------');
|
|
99
91
|
return roomMap;
|
|
100
92
|
}
|
|
93
|
+
|
|
94
|
+
const roomData = await platform.roborockService.getRoomMappings(device.duid);
|
|
95
|
+
if (roomData !== undefined && roomData.length > 0) {
|
|
96
|
+
platform.log.notice(`getRoomMapFromDevice - roomData: ${debugStringify(roomData ?? [])}`);
|
|
97
|
+
const roomDataMap: MapRoom[] = roomData.map((r) => ({ id: r[0], iot_name_id: String(r[1]), globalId: r[1], tag: r[2], mapId: 0, displayName: undefined }));
|
|
98
|
+
const roomMap = new RoomMap(roomDataMap ?? [], rooms, [], enableMultipleMap);
|
|
99
|
+
|
|
100
|
+
platform.log.notice(`getRoomMapFromDevice - roomMap: ${debugStringify(roomMap)}`);
|
|
101
|
+
platform.log.notice('-------------------------------------------1--------------------------------------------------------');
|
|
102
|
+
return roomMap;
|
|
103
|
+
}
|
|
101
104
|
}
|
|
102
105
|
|
|
103
|
-
return new RoomMap([], rooms);
|
|
106
|
+
return new RoomMap([], rooms, [], enableMultipleMap);
|
|
104
107
|
}
|
|
@@ -3,8 +3,14 @@ import { ServiceArea } from 'matterbridge/matter/clusters';
|
|
|
3
3
|
import { RoomMap } from '../model/RoomMap.js';
|
|
4
4
|
import { Room } from '../roborockCommunication/Zmodel/room.js';
|
|
5
5
|
import { randomInt } from 'node:crypto';
|
|
6
|
+
import { RoomIndexMap } from '../model/roomIndexMap.js';
|
|
6
7
|
|
|
7
|
-
export function getSupportedAreas(
|
|
8
|
+
export function getSupportedAreas(
|
|
9
|
+
vacuumRooms: Room[],
|
|
10
|
+
roomMap: RoomMap | undefined,
|
|
11
|
+
enableMultipleMap = false,
|
|
12
|
+
log?: AnsiLogger,
|
|
13
|
+
): { supportedAreas: ServiceArea.Area[]; supportedMaps: ServiceArea.Map[]; roomIndexMap: RoomIndexMap } {
|
|
8
14
|
log?.debug('getSupportedAreas-vacuum room', debugStringify(vacuumRooms));
|
|
9
15
|
log?.debug('getSupportedAreas-roomMap', roomMap ? debugStringify(roomMap) : 'undefined');
|
|
10
16
|
|
|
@@ -18,79 +24,135 @@ export function getSupportedAreas(vacuumRooms: Room[], roomMap: RoomMap | undefi
|
|
|
18
24
|
if (noRoomMap) {
|
|
19
25
|
log?.error('No room map found');
|
|
20
26
|
}
|
|
21
|
-
return [
|
|
22
|
-
{
|
|
23
|
-
areaId: 1,
|
|
24
|
-
mapId: null,
|
|
25
|
-
areaInfo: {
|
|
26
|
-
locationInfo: {
|
|
27
|
-
locationName: 'Unknown',
|
|
28
|
-
floorNumber: null,
|
|
29
|
-
areaType: null,
|
|
30
|
-
},
|
|
31
|
-
landmarkInfo: null,
|
|
32
|
-
},
|
|
33
|
-
},
|
|
34
|
-
];
|
|
35
|
-
}
|
|
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
27
|
|
|
40
28
|
return {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
29
|
+
supportedAreas: [
|
|
30
|
+
{
|
|
31
|
+
areaId: 1,
|
|
32
|
+
mapId: null,
|
|
33
|
+
areaInfo: {
|
|
34
|
+
locationInfo: {
|
|
35
|
+
locationName: 'Unknown',
|
|
36
|
+
floorNumber: null,
|
|
37
|
+
areaType: null,
|
|
38
|
+
},
|
|
39
|
+
landmarkInfo: null,
|
|
40
|
+
},
|
|
48
41
|
},
|
|
49
|
-
|
|
50
|
-
|
|
42
|
+
],
|
|
43
|
+
supportedMaps: [],
|
|
44
|
+
roomIndexMap: new RoomIndexMap(new Map([[1, { roomId: 1, mapId: null }]])),
|
|
51
45
|
};
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
log?.debug('getSupportedAreas - supportedAreas', debugStringify(supportedAreas));
|
|
46
|
+
}
|
|
55
47
|
|
|
48
|
+
const { supportedAreas, indexMap } = processValidData(enableMultipleMap, vacuumRooms, roomMap);
|
|
56
49
|
const duplicated = findDuplicatedAreaIds(supportedAreas, log);
|
|
57
50
|
|
|
58
51
|
if (duplicated) {
|
|
59
|
-
return
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
52
|
+
return {
|
|
53
|
+
supportedAreas: [
|
|
54
|
+
{
|
|
55
|
+
areaId: 2,
|
|
56
|
+
mapId: null,
|
|
57
|
+
areaInfo: {
|
|
58
|
+
locationInfo: {
|
|
59
|
+
locationName: 'Unknown - Duplicated Areas Found',
|
|
60
|
+
floorNumber: null,
|
|
61
|
+
areaType: null,
|
|
62
|
+
},
|
|
63
|
+
landmarkInfo: null,
|
|
68
64
|
},
|
|
69
|
-
landmarkInfo: null,
|
|
70
65
|
},
|
|
71
|
-
|
|
72
|
-
|
|
66
|
+
],
|
|
67
|
+
supportedMaps: [],
|
|
68
|
+
roomIndexMap: new RoomIndexMap(new Map([[2, { roomId: 2, mapId: null }]])),
|
|
69
|
+
};
|
|
73
70
|
}
|
|
74
71
|
|
|
75
|
-
|
|
72
|
+
const supportedMaps = getSupportedMaps(enableMultipleMap, supportedAreas, roomMap);
|
|
73
|
+
|
|
74
|
+
log?.debug('getSupportedAreas - supportedAreas', debugStringify(supportedAreas));
|
|
75
|
+
log?.debug('getSupportedAreas - supportedMaps', debugStringify(supportedMaps));
|
|
76
|
+
const roomIndexMap = new RoomIndexMap(indexMap);
|
|
77
|
+
return {
|
|
78
|
+
supportedAreas,
|
|
79
|
+
supportedMaps,
|
|
80
|
+
roomIndexMap,
|
|
81
|
+
};
|
|
76
82
|
}
|
|
77
83
|
|
|
78
84
|
function findDuplicatedAreaIds(areas: ServiceArea.Area[], log?: AnsiLogger): boolean {
|
|
79
|
-
const seen = new Set<
|
|
80
|
-
const duplicates: number[] = [];
|
|
85
|
+
const seen = new Set<string>();
|
|
86
|
+
const duplicates: { areaId: number; mapId: number }[] = [];
|
|
81
87
|
|
|
82
88
|
for (const area of areas) {
|
|
83
|
-
|
|
84
|
-
|
|
89
|
+
const key = `${area.areaId}=${area.mapId}`;
|
|
90
|
+
if (seen.has(key)) {
|
|
91
|
+
duplicates.push({ areaId: area.areaId, mapId: area.mapId ?? 0 });
|
|
85
92
|
} else {
|
|
86
|
-
seen.add(
|
|
93
|
+
seen.add(key);
|
|
87
94
|
}
|
|
88
95
|
}
|
|
89
96
|
|
|
90
97
|
if (duplicates.length > 0 && log) {
|
|
91
|
-
const duplicated = areas.filter((
|
|
98
|
+
const duplicated = areas.filter(({ areaId, mapId }) => duplicates.some((y) => y.areaId === areaId && y.mapId === (mapId ?? 0)));
|
|
92
99
|
log.error(`Duplicated areaId(s) found: ${debugStringify(duplicated)}`);
|
|
93
100
|
}
|
|
94
101
|
|
|
95
102
|
return duplicates.length > 0;
|
|
96
103
|
}
|
|
104
|
+
|
|
105
|
+
function processValidData(
|
|
106
|
+
enableMultipleMap: boolean,
|
|
107
|
+
vacuumRooms: Room[],
|
|
108
|
+
roomMap?: RoomMap,
|
|
109
|
+
): { supportedAreas: ServiceArea.Area[]; indexMap: Map<number, { roomId: number; mapId: number | null }> } {
|
|
110
|
+
const indexMap = new Map<number, { roomId: number; mapId: number | null }>();
|
|
111
|
+
const supportedAreas: ServiceArea.Area[] =
|
|
112
|
+
roomMap?.rooms !== undefined && roomMap.rooms.length > 0
|
|
113
|
+
? roomMap.rooms.map((room, index) => {
|
|
114
|
+
const locationName = room.displayName ?? vacuumRooms.find((r) => r.id === room.globalId || r.id === room.id)?.name ?? `Unknown Room ${randomInt(1000, 9999)}`;
|
|
115
|
+
|
|
116
|
+
const areaId = enableMultipleMap ? index + 100 : room.id;
|
|
117
|
+
const mapId = enableMultipleMap ? (room.mapId ?? null) : null;
|
|
118
|
+
|
|
119
|
+
indexMap.set(areaId, { roomId: room.id, mapId: room.mapId ?? null });
|
|
120
|
+
|
|
121
|
+
return {
|
|
122
|
+
areaId: areaId,
|
|
123
|
+
mapId: mapId,
|
|
124
|
+
areaInfo: {
|
|
125
|
+
locationInfo: {
|
|
126
|
+
locationName: locationName, // `${locationName} - (${areaId}) - (${room.id}) - (${room.globalId})`,
|
|
127
|
+
floorNumber: room.mapId ?? null,
|
|
128
|
+
areaType: null,
|
|
129
|
+
},
|
|
130
|
+
landmarkInfo: null,
|
|
131
|
+
},
|
|
132
|
+
};
|
|
133
|
+
})
|
|
134
|
+
: [];
|
|
135
|
+
|
|
136
|
+
return {
|
|
137
|
+
supportedAreas,
|
|
138
|
+
indexMap,
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function getSupportedMaps(enableMultipleMap: boolean, supportedAreas: ServiceArea.Area[], roomMap?: RoomMap): ServiceArea.Map[] {
|
|
143
|
+
if (enableMultipleMap) {
|
|
144
|
+
return (
|
|
145
|
+
roomMap?.mapInfo?.map((map) => ({
|
|
146
|
+
mapId: map.id,
|
|
147
|
+
name: map.name ?? `Map ${map.id}`,
|
|
148
|
+
})) ?? []
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return [];
|
|
153
|
+
|
|
154
|
+
// return supportedAreas.map((area) => ({
|
|
155
|
+
// mapId: area.mapId ?? 0,
|
|
156
|
+
// name: `Map ${area.mapId ?? 0}`,
|
|
157
|
+
// }));
|
|
158
|
+
}
|
|
@@ -7,6 +7,7 @@ export interface ExperimentalFeatureSetting {
|
|
|
7
7
|
useVacationModeToSendVacuumToDock: boolean;
|
|
8
8
|
enableServerMode: boolean;
|
|
9
9
|
alwaysExecuteAuthentication: boolean;
|
|
10
|
+
enableMultipleMap: boolean;
|
|
10
11
|
};
|
|
11
12
|
cleanModeSettings: CleanModeSettings;
|
|
12
13
|
}
|
|
@@ -40,6 +41,7 @@ export function createDefaultExperimentalFeatureSetting(): ExperimentalFeatureSe
|
|
|
40
41
|
useVacationModeToSendVacuumToDock: false,
|
|
41
42
|
enableServerMode: false,
|
|
42
43
|
alwaysExecuteAuthentication: false,
|
|
44
|
+
enableMultipleMap: false,
|
|
43
45
|
},
|
|
44
46
|
cleanModeSettings: {
|
|
45
47
|
enableCleanModeMapping: false,
|
package/src/model/RoomMap.ts
CHANGED
|
@@ -15,6 +15,7 @@ roomMap = {
|
|
|
15
15
|
};
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
|
+
import { MapRoom } from '../roborockCommunication/Zmodel/mapInfo.js';
|
|
18
19
|
import { Room } from '../roborockCommunication/Zmodel/room.js';
|
|
19
20
|
|
|
20
21
|
export interface RoomMapEntry {
|
|
@@ -22,21 +23,34 @@ export interface RoomMapEntry {
|
|
|
22
23
|
globalId: number | undefined;
|
|
23
24
|
displayName?: string;
|
|
24
25
|
alternativeId: string;
|
|
26
|
+
mapId?: number;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface MapInfo {
|
|
30
|
+
id: number;
|
|
31
|
+
name: string | undefined;
|
|
25
32
|
}
|
|
26
33
|
|
|
27
34
|
export class RoomMap {
|
|
28
35
|
readonly rooms: RoomMapEntry[];
|
|
36
|
+
readonly mapInfo?: MapInfo[];
|
|
29
37
|
|
|
30
|
-
constructor(roomData:
|
|
31
|
-
|
|
38
|
+
constructor(roomData: MapRoom[], rooms: Room[], mapInfo: MapInfo[], enableMultipleMap: boolean) {
|
|
39
|
+
const mapid = mapInfo[0]?.id ?? 0;
|
|
40
|
+
const roomDataTmp = enableMultipleMap ? roomData : roomData.filter((room) => room.mapId === undefined || room.mapId === mapid);
|
|
41
|
+
|
|
42
|
+
this.rooms = roomDataTmp.map(({ id, globalId, tag, mapId }) => {
|
|
32
43
|
const room = rooms.find((r) => Number(r.id) === Number(globalId) || Number(r.id) === Number(id));
|
|
33
44
|
return {
|
|
34
45
|
id,
|
|
35
46
|
globalId: globalId !== undefined ? Number(globalId) : undefined,
|
|
36
47
|
displayName: room?.name,
|
|
37
|
-
alternativeId: `${id}${
|
|
48
|
+
alternativeId: `${id}${tag}`,
|
|
49
|
+
mapId,
|
|
38
50
|
};
|
|
39
51
|
});
|
|
52
|
+
|
|
53
|
+
this.mapInfo = mapInfo;
|
|
40
54
|
}
|
|
41
55
|
|
|
42
56
|
// Optionally, add utility methods for clarity
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export class RoomIndexMap {
|
|
2
|
+
private indexMap: Map<number, { roomId: number; mapId: number | null }>;
|
|
3
|
+
private roomMap: Map<number, number>;
|
|
4
|
+
|
|
5
|
+
constructor(roomMap: Map<number, { roomId: number; mapId: number | null }>) {
|
|
6
|
+
this.indexMap = roomMap;
|
|
7
|
+
this.roomMap = new Map();
|
|
8
|
+
for (const [areaId, { roomId }] of roomMap.entries()) {
|
|
9
|
+
this.roomMap.set(roomId, areaId);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
public getAreaId(roomId: number): number | undefined {
|
|
14
|
+
return this.roomMap.get(roomId);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
public getRoomId(areaId: number): number | undefined {
|
|
18
|
+
return this.indexMap.get(areaId)?.roomId;
|
|
19
|
+
}
|
|
20
|
+
}
|
package/src/platform.ts
CHANGED
|
@@ -33,9 +33,9 @@ export class RoborockMatterbridgePlatform extends MatterbridgeDynamicPlatform {
|
|
|
33
33
|
super(matterbridge, log, config);
|
|
34
34
|
|
|
35
35
|
// Verify that Matterbridge is the correct version
|
|
36
|
-
if (this.verifyMatterbridgeVersion === undefined || typeof this.verifyMatterbridgeVersion !== 'function' || !this.verifyMatterbridgeVersion('3.1.
|
|
36
|
+
if (this.verifyMatterbridgeVersion === undefined || typeof this.verifyMatterbridgeVersion !== 'function' || !this.verifyMatterbridgeVersion('3.1.8')) {
|
|
37
37
|
throw new Error(
|
|
38
|
-
`This plugin requires Matterbridge version >= "3.1.
|
|
38
|
+
`This plugin requires Matterbridge version >= "3.1.8". Please update Matterbridge from ${this.matterbridge.matterbridgeVersion} to the latest version in the frontend.`,
|
|
39
39
|
);
|
|
40
40
|
}
|
|
41
41
|
this.log.info('Initializing platform:', this.config.name);
|
|
@@ -244,8 +244,11 @@ export class RoborockMatterbridgePlatform extends MatterbridgeDynamicPlatform {
|
|
|
244
244
|
this.log,
|
|
245
245
|
);
|
|
246
246
|
|
|
247
|
-
const
|
|
247
|
+
const enableMultipleMap = this.enableExperimentalFeature?.enableExperimentalFeature && this.enableExperimentalFeature.advancedFeature?.enableMultipleMap;
|
|
248
|
+
|
|
249
|
+
const { supportedAreas, roomIndexMap } = getSupportedAreas(vacuum.rooms, roomMap, enableMultipleMap, this.log);
|
|
248
250
|
this.roborockService.setSupportedAreas(vacuum.duid, supportedAreas);
|
|
251
|
+
this.roborockService.setSupportedAreaIndexMap(vacuum.duid, roomIndexMap);
|
|
249
252
|
|
|
250
253
|
let routineAsRoom: ServiceArea.Area[] = [];
|
|
251
254
|
if (this.enableExperimentalFeature?.enableExperimentalFeature && this.enableExperimentalFeature.advancedFeature?.showRoutinesAsRoom) {
|
package/src/platformRunner.ts
CHANGED
|
@@ -120,6 +120,7 @@ export class PlatformRunner {
|
|
|
120
120
|
robot.updateAttribute(ServiceArea.Cluster.id, 'selectedAreas', [], platform.log);
|
|
121
121
|
} else {
|
|
122
122
|
const currentMappedAreas = this.platform.roborockService?.getSupportedAreas(duid);
|
|
123
|
+
const roomIndexMap = this.platform.roborockService?.getSupportedAreasIndexMap(duid);
|
|
123
124
|
const roomMap = await getRoomMap(duid, this.platform);
|
|
124
125
|
|
|
125
126
|
// Get current room from segment_id
|
|
@@ -135,7 +136,10 @@ export class PlatformRunner {
|
|
|
135
136
|
this.platform.log.debug(
|
|
136
137
|
`Part1: CurrentRoom: ${segment_id}, room name: ${roomMap?.rooms.find((x) => x.id === segment_id || x.alternativeId === segment_id.toString())?.displayName ?? 'unknown'}`,
|
|
137
138
|
);
|
|
138
|
-
|
|
139
|
+
|
|
140
|
+
const areaId = roomIndexMap?.getAreaId(segment_id) ?? segment_id;
|
|
141
|
+
this.platform.log.notice(`AreaId: ${areaId}, segment_id: ${segment_id}`);
|
|
142
|
+
robot.updateAttribute(ServiceArea.Cluster.id, 'currentArea', areaId, platform.log);
|
|
139
143
|
}
|
|
140
144
|
|
|
141
145
|
if (segment_id == -1) {
|
|
@@ -148,7 +152,9 @@ export class PlatformRunner {
|
|
|
148
152
|
this.platform.log.debug(
|
|
149
153
|
`Part2: TargetRoom: ${target_segment_id}, room name: ${roomMap?.rooms.find((x) => x.id === target_segment_id || x.alternativeId === segment_id.toString())?.displayName ?? 'unknown'}`,
|
|
150
154
|
);
|
|
151
|
-
|
|
155
|
+
const areaId = roomIndexMap?.getAreaId(target_segment_id) ?? target_room_id;
|
|
156
|
+
this.platform.log.notice(`AreaId: ${areaId}, target_segment_id: ${target_segment_id}`);
|
|
157
|
+
robot.updateAttribute(ServiceArea.Cluster.id, 'currentArea', areaId, platform.log);
|
|
152
158
|
}
|
|
153
159
|
}
|
|
154
160
|
|
|
@@ -284,6 +290,8 @@ export class PlatformRunner {
|
|
|
284
290
|
platform.log.notice(`Received additional properties for robot ${duid}: ${debugStringify(data)}`);
|
|
285
291
|
const propCode = data.dps[Protocol.additional_props] as number;
|
|
286
292
|
platform.log.debug(`DPS for additional properties: ${propCode}, AdditionalPropCode: ${AdditionalPropCode[propCode]}`);
|
|
293
|
+
const enableMultipleMap =
|
|
294
|
+
(platform.enableExperimentalFeature?.enableExperimentalFeature && platform.enableExperimentalFeature?.advancedFeature?.enableMultipleMap) ?? false;
|
|
287
295
|
|
|
288
296
|
if (propCode === AdditionalPropCode.map_change) {
|
|
289
297
|
platform.log.notice('------------------------ get roomData ----------------------------');
|
|
@@ -291,7 +299,7 @@ export class PlatformRunner {
|
|
|
291
299
|
const roomMap = await getRoomMapFromDevice(robot.device, platform);
|
|
292
300
|
|
|
293
301
|
platform.log.notice('------------------------ Room map updated ------------------------');
|
|
294
|
-
const supportedAreas = getSupportedAreas(robot.device.rooms, roomMap, platform.log);
|
|
302
|
+
const { supportedAreas, supportedMaps, roomIndexMap } = getSupportedAreas(robot.device.rooms, roomMap, enableMultipleMap, platform.log);
|
|
295
303
|
|
|
296
304
|
platform.log.notice(`Supported areas: ${debugStringify(supportedAreas)}`);
|
|
297
305
|
platform.log.notice('------------------------ Supported areas updated ------------------');
|
|
@@ -301,6 +309,11 @@ export class PlatformRunner {
|
|
|
301
309
|
robot.updateAttribute(ServiceArea.Cluster.id, 'supportedAreas', supportedAreas, platform.log);
|
|
302
310
|
robot.updateAttribute(ServiceArea.Cluster.id, 'selectedAreas', [], platform.log);
|
|
303
311
|
robot.updateAttribute(ServiceArea.Cluster.id, 'currentArea', null, platform.log);
|
|
312
|
+
|
|
313
|
+
if (enableMultipleMap) {
|
|
314
|
+
platform.roborockService?.setSupportedAreaIndexMap(duid, roomIndexMap);
|
|
315
|
+
robot.updateAttribute(ServiceArea.Cluster.id, 'supportedMaps', supportedMaps, platform.log);
|
|
316
|
+
}
|
|
304
317
|
}
|
|
305
318
|
break;
|
|
306
319
|
}
|
|
@@ -7,7 +7,7 @@ export interface MapRoom {
|
|
|
7
7
|
iot_name_id: string;
|
|
8
8
|
tag: number;
|
|
9
9
|
globalId?: number;
|
|
10
|
-
displayName
|
|
10
|
+
displayName?: string;
|
|
11
11
|
mapId?: number;
|
|
12
12
|
}
|
|
13
13
|
|
|
@@ -23,14 +23,15 @@ export class MapInfo {
|
|
|
23
23
|
|
|
24
24
|
constructor(multimap: MultipleMap) {
|
|
25
25
|
this.maps = multimap.map_info.map((mapInfo) => {
|
|
26
|
-
const rooms: MapRoom[] =
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
26
|
+
const rooms: MapRoom[] =
|
|
27
|
+
mapInfo.rooms?.map((room: RoomInformation) => ({
|
|
28
|
+
id: room.id,
|
|
29
|
+
globalId: parseInt(room.iot_name_id),
|
|
30
|
+
iot_name_id: room.iot_name_id,
|
|
31
|
+
tag: room.tag,
|
|
32
|
+
displayName: room.iot_name,
|
|
33
|
+
mapId: mapInfo.mapFlag,
|
|
34
|
+
})) ?? [];
|
|
34
35
|
|
|
35
36
|
this.allRooms.push(...rooms);
|
|
36
37
|
return {
|
|
@@ -40,7 +41,7 @@ export class MapInfo {
|
|
|
40
41
|
};
|
|
41
42
|
});
|
|
42
43
|
|
|
43
|
-
this.allRooms = this.allRooms.filter((room, index, self) => index === self.findIndex((r) => r.globalId === room.globalId));
|
|
44
|
+
// this.allRooms = this.allRooms.filter((room, index, self) => index === self.findIndex((r) => r.globalId === room.globalId));
|
|
44
45
|
}
|
|
45
46
|
|
|
46
47
|
getById(id: number): string | undefined {
|
package/src/roborockService.ts
CHANGED
|
@@ -24,6 +24,7 @@ import {
|
|
|
24
24
|
import type { AbstractMessageHandler, AbstractMessageListener, BatteryMessage, DeviceErrorMessage, DeviceStatusNotify, MultipleMap } from './roborockCommunication/index.js';
|
|
25
25
|
import { ServiceArea } from 'matterbridge/matter/clusters';
|
|
26
26
|
import { LocalNetworkClient } from './roborockCommunication/broadcast/client/LocalNetworkClient.js';
|
|
27
|
+
import { RoomIndexMap } from './model/roomIndexMap.js';
|
|
27
28
|
export type Factory<A, T> = (logger: AnsiLogger, arg: A) => T;
|
|
28
29
|
|
|
29
30
|
export default class RoborockService {
|
|
@@ -48,6 +49,7 @@ export default class RoborockService {
|
|
|
48
49
|
private supportedAreas = new Map<string, ServiceArea.Area[]>();
|
|
49
50
|
private supportedRoutines = new Map<string, ServiceArea.Area[]>();
|
|
50
51
|
private selectedAreas = new Map<string, number[]>();
|
|
52
|
+
private supportedAreaIndexMaps = new Map<string, RoomIndexMap>();
|
|
51
53
|
|
|
52
54
|
private readonly vacuumNeedAPIV3 = ['roborock.vacuum.ss07'];
|
|
53
55
|
|
|
@@ -95,7 +97,12 @@ export default class RoborockService {
|
|
|
95
97
|
|
|
96
98
|
public setSelectedAreas(duid: string, selectedAreas: number[]): void {
|
|
97
99
|
this.logger.debug('RoborockService - setSelectedAreas', selectedAreas);
|
|
98
|
-
this.
|
|
100
|
+
const roomIds = selectedAreas.map((areaId) => this.supportedAreaIndexMaps.get(duid)?.getRoomId(areaId)) ?? [];
|
|
101
|
+
this.logger.debug('RoborockService - setSelectedAreas - roomIds', roomIds);
|
|
102
|
+
this.selectedAreas.set(
|
|
103
|
+
duid,
|
|
104
|
+
roomIds.filter((id) => id !== undefined).map((id) => id),
|
|
105
|
+
);
|
|
99
106
|
}
|
|
100
107
|
|
|
101
108
|
public getSelectedAreas(duid: string): number[] {
|
|
@@ -106,6 +113,10 @@ export default class RoborockService {
|
|
|
106
113
|
this.supportedAreas.set(duid, supportedAreas);
|
|
107
114
|
}
|
|
108
115
|
|
|
116
|
+
public setSupportedAreaIndexMap(duid: string, indexMap: RoomIndexMap): void {
|
|
117
|
+
this.supportedAreaIndexMaps.set(duid, indexMap);
|
|
118
|
+
}
|
|
119
|
+
|
|
109
120
|
public setSupportedScenes(duid: string, routineAsRooms: ServiceArea.Area[]) {
|
|
110
121
|
this.supportedRoutines.set(duid, routineAsRooms);
|
|
111
122
|
}
|
|
@@ -114,6 +125,10 @@ export default class RoborockService {
|
|
|
114
125
|
return this.supportedAreas.get(duid);
|
|
115
126
|
}
|
|
116
127
|
|
|
128
|
+
public getSupportedAreasIndexMap(duid: string): RoomIndexMap | undefined {
|
|
129
|
+
return this.supportedAreaIndexMaps.get(duid);
|
|
130
|
+
}
|
|
131
|
+
|
|
117
132
|
public async getCleanModeData(duid: string): Promise<{ suctionPower: number; waterFlow: number; distance_off: number; mopRoute: number }> {
|
|
118
133
|
this.logger.notice('RoborockService - getCleanModeData');
|
|
119
134
|
const data = await this.getMessageProcessor(duid)?.getCleanModeData(duid);
|
package/src/rvc.ts
CHANGED
|
@@ -24,7 +24,10 @@ export class RoborockVacuumCleaner extends RoboticVacuumCleaner {
|
|
|
24
24
|
) {
|
|
25
25
|
const cleanModes = getSupportedCleanModes(device.data.model, enableExperimentalFeature);
|
|
26
26
|
const supportedRunModes = getSupportedRunModes();
|
|
27
|
-
const
|
|
27
|
+
const enableMultipleMap = enableExperimentalFeature?.enableExperimentalFeature && enableExperimentalFeature?.advancedFeature?.enableMultipleMap;
|
|
28
|
+
|
|
29
|
+
const { supportedAreas, supportedMaps } = getSupportedAreas(device.rooms, roomMap, enableMultipleMap, log);
|
|
30
|
+
const supportedAreaAndRoutines = [...supportedAreas, ...routineAsRoom];
|
|
28
31
|
const deviceName = `${device.name}-${device.duid}`.replace(/\s+/g, '');
|
|
29
32
|
|
|
30
33
|
log.debug(
|
|
@@ -33,6 +36,7 @@ export class RoborockVacuumCleaner extends RoboticVacuumCleaner {
|
|
|
33
36
|
log.debug(`Supported Clean Modes: ${JSON.stringify(cleanModes)}`);
|
|
34
37
|
log.debug(`Supported Run Modes: ${JSON.stringify(supportedRunModes)}`);
|
|
35
38
|
log.debug(`Supported Areas: ${JSON.stringify(supportedAreas)}`);
|
|
39
|
+
log.debug(`Supported Maps: ${JSON.stringify(supportedMaps)}`);
|
|
36
40
|
|
|
37
41
|
const bridgeMode = enableExperimentalFeature?.enableExperimentalFeature && enableExperimentalFeature?.advancedFeature?.enableServerMode ? 'server' : undefined;
|
|
38
42
|
super(
|
|
@@ -47,9 +51,10 @@ export class RoborockVacuumCleaner extends RoboticVacuumCleaner {
|
|
|
47
51
|
undefined, // phaseList
|
|
48
52
|
RvcOperationalState.OperationalState.Docked, // operationalState
|
|
49
53
|
getOperationalStates(), // operationalStateList
|
|
50
|
-
|
|
54
|
+
supportedAreaAndRoutines, // supportedAreas
|
|
51
55
|
undefined, // selectedAreas
|
|
52
56
|
supportedAreas[0].areaId, // currentArea
|
|
57
|
+
supportedMaps, // supportedMaps
|
|
53
58
|
);
|
|
54
59
|
|
|
55
60
|
this.username = username;
|