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.
- package/.github/workflows/build.yml +5 -1
- package/.github/workflows/coverage.yml +5 -1
- package/CHANGELOG.md +62 -0
- package/dist/helper.js +62 -0
- package/dist/initialData/getSupportedAreas.js +12 -8
- package/dist/model/RoomMap.js +8 -7
- package/dist/platform.js +4 -4
- package/dist/platformRunner.js +24 -68
- package/dist/roborockCommunication/Zenum/additionalPropCode.js +4 -0
- package/dist/roborockCommunication/Zmodel/mapInfo.js +18 -16
- package/dist/roborockCommunication/broadcast/client/LocalNetworkClient.js +2 -2
- package/dist/roborockCommunication/broadcast/listener/implementation/syncMessageListener.js +0 -4
- package/dist/roborockCommunication/broadcast/messageProcessor.js +3 -4
- package/dist/roborockCommunication/index.js +1 -0
- package/dist/roborockService.js +1 -1
- package/matterbridge-roborock-vacuum-plugin.config.json +1 -1
- package/matterbridge-roborock-vacuum-plugin.schema.json +1 -1
- package/package.json +1 -1
- package/src/helper.ts +82 -0
- package/src/initialData/getSupportedAreas.ts +29 -21
- package/src/model/RoomMap.ts +18 -14
- package/src/platform.ts +4 -4
- package/src/platformRunner.ts +29 -89
- package/src/roborockCommunication/Zenum/additionalPropCode.ts +3 -0
- package/src/roborockCommunication/Zmodel/map.ts +1 -1
- package/src/roborockCommunication/Zmodel/mapInfo.ts +36 -18
- package/src/roborockCommunication/Zmodel/multipleMap.ts +2 -2
- package/src/roborockCommunication/broadcast/client/LocalNetworkClient.ts +2 -3
- package/src/roborockCommunication/broadcast/listener/implementation/syncMessageListener.ts +4 -4
- package/src/roborockCommunication/broadcast/messageProcessor.ts +4 -6
- package/src/roborockCommunication/index.ts +1 -0
- package/src/roborockService.ts +4 -1
- package/src/rvc.ts +1 -1
- package/src/tests/helper.test.ts +113 -0
- package/src/tests/platformRunner2.test.ts +43 -13
- package/src/tests/platformRunner3.test.ts +54 -0
- package/src/tests/roborockCommunication/broadcast/listener/implementation/syncMessageListener.test.ts +3 -3
- package/src/tests/roborockCommunication/broadcast/messageProcessor.test.ts +3 -8
- package/src/tests/roborockService.test.ts +11 -1
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
|
-
|
|
7
|
-
|
|
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 (
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
61
|
+
];
|
|
62
|
+
}
|
|
63
|
+
return supportedAreas;
|
|
60
64
|
}
|
|
61
65
|
function findDuplicatedAreaIds(areas, log) {
|
|
62
66
|
const seen = new Set();
|
package/dist/model/RoomMap.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
export
|
|
2
|
-
rooms
|
|
1
|
+
export class RoomMap {
|
|
2
|
+
rooms;
|
|
3
3
|
constructor(roomData, rooms) {
|
|
4
|
-
this.rooms = roomData.map((
|
|
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
|
|
7
|
-
globalId: Number(
|
|
8
|
-
displayName:
|
|
9
|
-
alternativeId: `${
|
|
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?.
|
|
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
|
-
const roomMap = await
|
|
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);
|
package/dist/platformRunner.js
CHANGED
|
@@ -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
|
|
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
|
|
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 (!
|
|
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)
|
|
@@ -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.
|
|
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
|
});
|
|
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
|
|
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' });
|
|
@@ -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.
|
|
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';
|
package/dist/roborockService.js
CHANGED
|
@@ -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,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"title": "Matterbridge Roborock Vacuum Plugin",
|
|
3
|
-
"description": "matterbridge-roborock-vacuum-plugin v. 1.1.0-
|
|
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
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
|
+
}
|