matterbridge-roborock-vacuum-plugin 1.1.0-rc13 → 1.1.0-rc15
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/README.md +1 -1
- package/dist/helper.js +62 -0
- package/dist/model/RoomMap.js +1 -1
- package/dist/platform.js +4 -4
- package/dist/platformRunner.js +3 -66
- package/dist/roborockCommunication/broadcast/client/LocalNetworkClient.js +25 -25
- package/dist/roborockCommunication/broadcast/client/MQTTClient.js +1 -2
- package/dist/roborockCommunication/broadcast/clientRouter.js +1 -1
- package/dist/roborockCommunication/broadcast/listener/implementation/connectionStateListener.js +2 -2
- package/dist/roborockCommunication/broadcast/listener/implementation/syncMessageListener.js +0 -4
- package/dist/roborockCommunication/broadcast/messageProcessor.js +10 -1
- package/dist/roborockCommunication/broadcast/model/protocol.js +9 -0
- package/dist/roborockService.js +20 -1
- package/matterbridge-roborock-vacuum-plugin.config.json +1 -1
- package/matterbridge-roborock-vacuum-plugin.schema.json +1 -1
- package/misc/status.md +119 -0
- package/package.json +1 -1
- package/src/helper.ts +83 -0
- package/src/initialData/getSupportedAreas.ts +1 -1
- package/src/model/RoomMap.ts +1 -1
- package/src/platform.ts +4 -4
- package/src/platformRunner.ts +3 -86
- package/src/roborockCommunication/broadcast/client/LocalNetworkClient.ts +32 -31
- package/src/roborockCommunication/broadcast/client/MQTTClient.ts +2 -2
- package/src/roborockCommunication/broadcast/clientRouter.ts +1 -1
- package/src/roborockCommunication/broadcast/listener/implementation/connectionStateListener.ts +2 -3
- package/src/roborockCommunication/broadcast/listener/implementation/syncMessageListener.ts +4 -4
- package/src/roborockCommunication/broadcast/messageProcessor.ts +14 -7
- package/src/roborockCommunication/broadcast/model/protocol.ts +9 -0
- package/src/roborockService.ts +21 -1
- package/src/rvc.ts +1 -1
- package/src/tests/platformRunner2.test.ts +4 -7
- package/src/tests/roborockCommunication/RESTAPI/roborockAuthenticateApi.test.ts +136 -0
- package/src/tests/roborockCommunication/RESTAPI/roborockIoTApi.test.ts +106 -0
- package/src/tests/roborockCommunication/broadcast/client/LocalNetworkClient.test.ts +3 -5
- package/src/tests/roborockCommunication/broadcast/clientRouter.test.ts +168 -0
- package/src/tests/roborockCommunication/broadcast/listener/implementation/syncMessageListener.test.ts +3 -3
- package/src/tests/roborockCommunication/broadcast/messageProcessor.test.ts +131 -0
- package/src/tests/roborockService.test.ts +102 -2
- package/src/tests/roborockService3.test.ts +133 -0
- package/src/tests/roborockService4.test.ts +76 -0
- package/src/tests/roborockService5.test.ts +79 -0
package/README.md
CHANGED
|
@@ -69,7 +69,7 @@ To get the **DUID** for your devices, you have two options:
|
|
|
69
69
|
### 🚧 Project Status
|
|
70
70
|
|
|
71
71
|
- **Under active development**
|
|
72
|
-
- Requires **`matterbridge@3.1.
|
|
72
|
+
- Requires **`matterbridge@3.1.7`**
|
|
73
73
|
- ⚠️ **Known Issue:**
|
|
74
74
|
+ Vacuum may appear as **two devices** in Apple Home
|
|
75
75
|
+ Error: Wrong CRC32 2241274590, expected 0 -> this is normal error and not impact to plugin functionality
|
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.maps && mapInfo.maps.length > 0) {
|
|
47
|
+
platform.log.error(`getRoomMap - mapInfo: ${debugStringify(mapInfo.maps)}`);
|
|
48
|
+
const roomDataMap = mapInfo.maps[0].rooms.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: ${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.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
|
+
}
|
|
77
|
+
return new RoomMap([], rooms);
|
|
78
|
+
}
|
package/dist/model/RoomMap.js
CHANGED
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';
|
|
@@ -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.7')) {
|
|
30
|
+
throw new Error(`This plugin requires Matterbridge version >= "3.1.7". 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)
|
|
@@ -162,7 +162,7 @@ export class RoborockMatterbridgePlatform extends MatterbridgeDynamicPlatform {
|
|
|
162
162
|
const rooms = map_info?.maps?.[0]?.rooms ?? [];
|
|
163
163
|
vacuum.rooms = rooms.map((room) => ({ id: room.id, 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,8 +1,7 @@
|
|
|
1
1
|
import { RvcRunMode, PowerSource, ServiceArea, RvcOperationalState, RvcCleanMode } from 'matterbridge/matter/clusters';
|
|
2
|
-
import { getVacuumProperty } from './helper.js';
|
|
2
|
+
import { getRoomMap, getVacuumProperty, isStatusUpdate } from './helper.js';
|
|
3
3
|
import { getRunningMode } from './initialData/getSupportedRunModes.js';
|
|
4
4
|
import { state_to_matter_operational_status, state_to_matter_state } from './share/function.js';
|
|
5
|
-
import RoomMap from './model/RoomMap.js';
|
|
6
5
|
import { getBatteryState, getBatteryStatus, getOperationalErrorState } from './initialData/index.js';
|
|
7
6
|
import { NotifyMessageTypes } from './notifyMessageTypes.js';
|
|
8
7
|
import { Protocol } from './roborockCommunication/broadcast/model/protocol.js';
|
|
@@ -32,59 +31,6 @@ export class PlatformRunner {
|
|
|
32
31
|
const homeData = await platform.roborockService.getHomeDataForUpdating(platform.rrHomeId);
|
|
33
32
|
await this.updateRobot(NotifyMessageTypes.HomeData, homeData);
|
|
34
33
|
}
|
|
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
34
|
async updateFromMQTTMessage(messageSource, messageData, duid = '', tracked = false) {
|
|
89
35
|
const platform = this.platform;
|
|
90
36
|
duid = duid || messageData?.duid || '';
|
|
@@ -146,7 +92,7 @@ export class PlatformRunner {
|
|
|
146
92
|
}
|
|
147
93
|
else {
|
|
148
94
|
const currentMappedAreas = this.platform.roborockService?.getSupportedAreas(duid);
|
|
149
|
-
const roomMap = await
|
|
95
|
+
const roomMap = await getRoomMap(duid, this.platform);
|
|
150
96
|
const segment_id = data.cleaning_info?.segment_id ?? -1;
|
|
151
97
|
const target_segment_id = data.cleaning_info?.target_segment_id ?? -1;
|
|
152
98
|
let target_room_id = roomMap?.rooms.find((x) => x.id === segment_id || x.alternativeId === segment_id.toString())?.id ?? -1;
|
|
@@ -237,7 +183,7 @@ export class PlatformRunner {
|
|
|
237
183
|
}
|
|
238
184
|
case Protocol.rpc_response: {
|
|
239
185
|
const response = data.dps[messageType];
|
|
240
|
-
if (!
|
|
186
|
+
if (!isStatusUpdate(response.result)) {
|
|
241
187
|
platform.log.debug('Ignore message:', debugStringify(data));
|
|
242
188
|
return;
|
|
243
189
|
}
|
|
@@ -309,15 +255,6 @@ export class PlatformRunner {
|
|
|
309
255
|
}
|
|
310
256
|
return undefined;
|
|
311
257
|
}
|
|
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
258
|
updateFromHomeData(homeData) {
|
|
322
259
|
const platform = this.platform;
|
|
323
260
|
if (platform.robots.size === 0)
|
|
@@ -13,6 +13,7 @@ export class LocalNetworkClient extends AbstractClient {
|
|
|
13
13
|
buffer = new ChunkBuffer();
|
|
14
14
|
messageIdSeq;
|
|
15
15
|
pingInterval;
|
|
16
|
+
keepConnectionAliveInterval = undefined;
|
|
16
17
|
duid;
|
|
17
18
|
ip;
|
|
18
19
|
constructor(logger, context, duid, ip) {
|
|
@@ -34,6 +35,7 @@ export class LocalNetworkClient extends AbstractClient {
|
|
|
34
35
|
this.socket.on('timeout', this.onTimeout.bind(this));
|
|
35
36
|
this.socket.on('data', this.onMessage.bind(this));
|
|
36
37
|
this.socket.connect(58867, this.ip);
|
|
38
|
+
this.keepConnectionAlive();
|
|
37
39
|
}
|
|
38
40
|
async disconnect() {
|
|
39
41
|
if (!this.socket) {
|
|
@@ -57,29 +59,16 @@ export class LocalNetworkClient extends AbstractClient {
|
|
|
57
59
|
this.socket.write(this.wrapWithLengthData(message.buffer));
|
|
58
60
|
}
|
|
59
61
|
async onConnect() {
|
|
60
|
-
this.logger.debug(`LocalNetworkClient: ${this.duid} connected to ${this.ip}`);
|
|
61
|
-
this.logger.debug(`LocalNetworkClient: ${this.duid} socket writable: ${this.socket?.writable}, readable: ${this.socket?.readable}`);
|
|
62
|
+
this.logger.debug(` [LocalNetworkClient]: ${this.duid} connected to ${this.ip}`);
|
|
63
|
+
this.logger.debug(` [LocalNetworkClient]: ${this.duid} socket writable: ${this.socket?.writable}, readable: ${this.socket?.readable}`);
|
|
62
64
|
this.connected = true;
|
|
63
65
|
this.retryCount = 0;
|
|
64
66
|
await this.sendHelloMessage();
|
|
65
67
|
this.pingInterval = setInterval(this.sendPingRequest.bind(this), 5000);
|
|
66
68
|
await this.connectionListeners.onConnected(this.duid);
|
|
67
69
|
}
|
|
68
|
-
async onEnd() {
|
|
69
|
-
await this.destroySocket('Socket has ended.');
|
|
70
|
-
await this.connectionListeners.onDisconnected(this.duid, 'Socket has ended.');
|
|
71
|
-
}
|
|
72
70
|
async onDisconnect(hadError) {
|
|
73
|
-
|
|
74
|
-
if (!hadError) {
|
|
75
|
-
await this.connectionListeners.onDisconnected(this.duid, 'Socket disconnected. Had no error.');
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
async onTimeout() {
|
|
79
|
-
this.logger.error(`LocalNetworkClient: Socket for ${this.duid} timed out.`);
|
|
80
|
-
}
|
|
81
|
-
async destroySocket(message) {
|
|
82
|
-
this.logger.error(`LocalNetworkClient: Destroying socket for ${this.duid} due to: ${message}`);
|
|
71
|
+
this.logger.info(` [LocalNetworkClient]: ${this.duid} socket disconnected. Had error: ${hadError}`);
|
|
83
72
|
this.connected = false;
|
|
84
73
|
if (this.socket) {
|
|
85
74
|
this.socket.destroy();
|
|
@@ -88,19 +77,18 @@ export class LocalNetworkClient extends AbstractClient {
|
|
|
88
77
|
if (this.pingInterval) {
|
|
89
78
|
clearInterval(this.pingInterval);
|
|
90
79
|
}
|
|
80
|
+
await this.connectionListeners.onDisconnected(this.duid, 'Socket disconnected. Had no error.');
|
|
91
81
|
}
|
|
92
82
|
async onError(error) {
|
|
93
|
-
this.logger.error(
|
|
94
|
-
this.connected = false;
|
|
95
|
-
if (this.socket) {
|
|
96
|
-
this.socket.destroy();
|
|
97
|
-
this.socket = undefined;
|
|
98
|
-
}
|
|
99
|
-
if (this.pingInterval) {
|
|
100
|
-
clearInterval(this.pingInterval);
|
|
101
|
-
}
|
|
83
|
+
this.logger.error(` [LocalNetworkClient]: Socket error for ${this.duid}: ${error.message}`);
|
|
102
84
|
await this.connectionListeners.onError(this.duid, error.message);
|
|
103
85
|
}
|
|
86
|
+
async onTimeout() {
|
|
87
|
+
this.logger.error(` [LocalNetworkClient]: Socket for ${this.duid} timed out.`);
|
|
88
|
+
}
|
|
89
|
+
async onEnd() {
|
|
90
|
+
this.logger.debug(` [LocalNetworkClient]: ${this.duid} socket ended.`);
|
|
91
|
+
}
|
|
104
92
|
async onMessage(message) {
|
|
105
93
|
if (!this.socket) {
|
|
106
94
|
return;
|
|
@@ -170,4 +158,16 @@ export class LocalNetworkClient extends AbstractClient {
|
|
|
170
158
|
});
|
|
171
159
|
await this.send(this.duid, request);
|
|
172
160
|
}
|
|
161
|
+
keepConnectionAlive() {
|
|
162
|
+
if (this.keepConnectionAliveInterval) {
|
|
163
|
+
clearTimeout(this.keepConnectionAliveInterval);
|
|
164
|
+
this.keepConnectionAliveInterval.unref();
|
|
165
|
+
}
|
|
166
|
+
this.keepConnectionAliveInterval = setInterval(() => {
|
|
167
|
+
if (this.socket === undefined || !this.connected || !this.socket.writable || this.socket.readable) {
|
|
168
|
+
this.logger.debug(` [LocalNetworkClient]: ${this.duid} socket is not writable or readable, reconnecting...`);
|
|
169
|
+
this.connect();
|
|
170
|
+
}
|
|
171
|
+
}, 60 * 60 * 1000);
|
|
172
|
+
}
|
|
173
173
|
}
|
|
@@ -26,8 +26,7 @@ export class MQTTClient extends AbstractClient {
|
|
|
26
26
|
username: this.mqttUsername,
|
|
27
27
|
password: this.mqttPassword,
|
|
28
28
|
keepalive: 30,
|
|
29
|
-
log: (
|
|
30
|
-
this.logger.debug(`MQTTClient args: ${debugStringify(args)}`);
|
|
29
|
+
log: () => {
|
|
31
30
|
},
|
|
32
31
|
});
|
|
33
32
|
this.mqttClient.on('connect', this.onConnect.bind(this));
|
|
@@ -7,9 +7,9 @@ export class ClientRouter {
|
|
|
7
7
|
connectionListeners = new ChainedConnectionListener();
|
|
8
8
|
messageListeners = new ChainedMessageListener();
|
|
9
9
|
context;
|
|
10
|
-
mqttClient;
|
|
11
10
|
localClients = new Map();
|
|
12
11
|
logger;
|
|
12
|
+
mqttClient;
|
|
13
13
|
constructor(logger, userdata) {
|
|
14
14
|
this.context = new MessageContext(userdata);
|
|
15
15
|
this.logger = logger;
|
package/dist/roborockCommunication/broadcast/listener/implementation/connectionStateListener.js
CHANGED
|
@@ -31,10 +31,10 @@ export class ConnectionStateListener {
|
|
|
31
31
|
this.logger.info(`Device with DUID ${duid} is in disconnecting step, skipping re-registration.`);
|
|
32
32
|
return;
|
|
33
33
|
}
|
|
34
|
-
this.logger.info(`Re-registering device with DUID ${duid} to ${this.clientName}`);
|
|
35
34
|
setTimeout(() => {
|
|
35
|
+
this.logger.info(`Re-registering device with DUID ${duid} to ${this.clientName}`);
|
|
36
36
|
this.client.connect();
|
|
37
|
-
},
|
|
37
|
+
}, 10000);
|
|
38
38
|
this.client.isInDisconnectingStep = false;
|
|
39
39
|
}
|
|
40
40
|
async onError(duid, message) {
|
|
@@ -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
|
}
|
|
@@ -31,6 +31,15 @@ export class MessageProcessor {
|
|
|
31
31
|
}
|
|
32
32
|
return undefined;
|
|
33
33
|
}
|
|
34
|
+
async getDeviceStatusOverMQTT(duid) {
|
|
35
|
+
const request = new RequestMessage({ method: 'get_status', secure: true });
|
|
36
|
+
const response = await this.client.get(duid, request);
|
|
37
|
+
if (response) {
|
|
38
|
+
this.logger?.debug('MQTT - Device status: ', debugStringify(response));
|
|
39
|
+
return new DeviceStatus(response);
|
|
40
|
+
}
|
|
41
|
+
return undefined;
|
|
42
|
+
}
|
|
34
43
|
async getRooms(duid, rooms) {
|
|
35
44
|
const request = new RequestMessage({ method: 'get_room_mapping' });
|
|
36
45
|
return this.client.get(duid, request).then((response) => new RoomInfo(rooms, response ?? []));
|
|
@@ -71,7 +80,7 @@ export class MessageProcessor {
|
|
|
71
80
|
}
|
|
72
81
|
async findMyRobot(duid) {
|
|
73
82
|
const request = new RequestMessage({ method: 'find_me' });
|
|
74
|
-
return this.client.
|
|
83
|
+
return this.client.get(duid, request);
|
|
75
84
|
}
|
|
76
85
|
async getCleanModeData(duid) {
|
|
77
86
|
const currentMopMode = await this.getCustomMessage(duid, new RequestMessage({ method: 'get_mop_mode' }));
|
|
@@ -13,7 +13,16 @@ export var Protocol;
|
|
|
13
13
|
Protocol[Protocol["battery"] = 122] = "battery";
|
|
14
14
|
Protocol[Protocol["suction_power"] = 123] = "suction_power";
|
|
15
15
|
Protocol[Protocol["water_box_mode"] = 124] = "water_box_mode";
|
|
16
|
+
Protocol[Protocol["main_brush_work_time"] = 125] = "main_brush_work_time";
|
|
17
|
+
Protocol[Protocol["side_brush_work_time"] = 126] = "side_brush_work_time";
|
|
18
|
+
Protocol[Protocol["filter_work_time"] = 127] = "filter_work_time";
|
|
16
19
|
Protocol[Protocol["additional_props"] = 128] = "additional_props";
|
|
20
|
+
Protocol[Protocol["task_complete"] = 130] = "task_complete";
|
|
21
|
+
Protocol[Protocol["task_cancel_low_power"] = 131] = "task_cancel_low_power";
|
|
22
|
+
Protocol[Protocol["task_cancel_in_motion"] = 132] = "task_cancel_in_motion";
|
|
23
|
+
Protocol[Protocol["charge_status"] = 133] = "charge_status";
|
|
24
|
+
Protocol[Protocol["drying_status"] = 134] = "drying_status";
|
|
17
25
|
Protocol[Protocol["back_type"] = 139] = "back_type";
|
|
18
26
|
Protocol[Protocol["map_response"] = 301] = "map_response";
|
|
27
|
+
Protocol[Protocol["some_thing_happened_when_socket_closed"] = 500] = "some_thing_happened_when_socket_closed";
|
|
19
28
|
})(Protocol || (Protocol = {}));
|
package/dist/roborockService.js
CHANGED
|
@@ -201,7 +201,7 @@ export default class RoborockService {
|
|
|
201
201
|
await messageProcessor.getDeviceStatus(device.duid).then((response) => {
|
|
202
202
|
if (self.deviceNotify && response) {
|
|
203
203
|
const message = { duid: device.duid, ...response.errorStatus, ...response.message };
|
|
204
|
-
self.logger.debug('Device status update', debugStringify(message));
|
|
204
|
+
self.logger.debug('Socket - Device status update', debugStringify(message));
|
|
205
205
|
self.deviceNotify(NotifyMessageTypes.LocalMessage, message);
|
|
206
206
|
}
|
|
207
207
|
});
|
|
@@ -211,6 +211,25 @@ export default class RoborockService {
|
|
|
211
211
|
}
|
|
212
212
|
}, this.refreshInterval * 1000);
|
|
213
213
|
}
|
|
214
|
+
activateDeviceNotifyOverMQTT(device) {
|
|
215
|
+
const self = this;
|
|
216
|
+
this.logger.notice('Requesting device info for device over MQTT', device.duid);
|
|
217
|
+
const messageProcessor = this.getMessageProcessor(device.duid);
|
|
218
|
+
this.requestDeviceStatusInterval = setInterval(async () => {
|
|
219
|
+
if (messageProcessor) {
|
|
220
|
+
await messageProcessor.getDeviceStatusOverMQTT(device.duid).then((response) => {
|
|
221
|
+
if (self.deviceNotify && response) {
|
|
222
|
+
const message = { duid: device.duid, ...response.errorStatus, ...response.message };
|
|
223
|
+
self.logger.debug('MQTT - Device status update', debugStringify(message));
|
|
224
|
+
self.deviceNotify(NotifyMessageTypes.LocalMessage, message);
|
|
225
|
+
}
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
else {
|
|
229
|
+
self.logger.error('Local client not initialized');
|
|
230
|
+
}
|
|
231
|
+
}, this.refreshInterval * 500);
|
|
232
|
+
}
|
|
214
233
|
async listDevices(username) {
|
|
215
234
|
assert(this.iotApi !== undefined);
|
|
216
235
|
assert(this.userdata !== 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-rc15 by https://github.com/RinDevJunior",
|
|
4
4
|
"type": "object",
|
|
5
5
|
"required": ["username", "password"],
|
|
6
6
|
"properties": {
|
package/misc/status.md
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# Status Message
|
|
2
|
+
|
|
3
|
+
### Response
|
|
4
|
+
|
|
5
|
+
| Key | Example | Description | Only available for |
|
|
6
|
+
| --------------------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------ |
|
|
7
|
+
| `battery` | _100_ | Battery level (in %) | |
|
|
8
|
+
| `clean_area` | _140000_ | Total area (in cm²) | |
|
|
9
|
+
| `clean_time` | _15_ | Total cleaning time (in s) | |
|
|
10
|
+
| `dnd_enabled` | _0_ | Is 'Do Not Disturb' enabled (0=disabled, 1=enabled) | |
|
|
11
|
+
| `error_code` | _0_ | Error code (see [list](#error-codes) below) | |
|
|
12
|
+
| `fan_power` | _102_ | Fan power, corresponds to the values in [Custom Mode](../README_CLEANMODE.md) | |
|
|
13
|
+
| `in_cleaning` | _0_ | Is device cleaning | |
|
|
14
|
+
| `in_fresh_state` | _1_ | ? | |
|
|
15
|
+
| `in_returning` | _0_ | Is returning to dock (0=no, 1=yes) | |
|
|
16
|
+
| `is_locating` | _0_ | ? | |
|
|
17
|
+
| `lab_status` | _1_ | ? | |
|
|
18
|
+
| `lock_status` | _0_ | ? | |
|
|
19
|
+
| `map_present` | _1_ | Is map present | |
|
|
20
|
+
| `map_status` | _3_ | ? | |
|
|
21
|
+
| `mop_forbidden_enable` | _0_ | ? | |
|
|
22
|
+
| `msg_seq` | _52_ | Message sequence increments with each request | |
|
|
23
|
+
| `msg_ver` | _2_ | Message version (seems always 4 and 2 for s6) see below for other examples | |
|
|
24
|
+
| `state` | _8_ | Status code (see [list](#status-codes) below) | |
|
|
25
|
+
| `water_box_carriage_status` | _0_ | Is carriage mounted (0=no, 1=yes) | |
|
|
26
|
+
| `water_box_mode` | _204_ | Water quantity control, | |
|
|
27
|
+
| `water_box_status` | _1_ | Is water tank mounted (0=no, 1=yes) | |
|
|
28
|
+
|
|
29
|
+
#### Example
|
|
30
|
+
|
|
31
|
+
```json
|
|
32
|
+
{
|
|
33
|
+
"result": [{
|
|
34
|
+
"msg_ver": 2,
|
|
35
|
+
"msg_seq": 52,
|
|
36
|
+
"state": 8,
|
|
37
|
+
"battery": 100,
|
|
38
|
+
"clean_time": 15,
|
|
39
|
+
"clean_area": 140000,
|
|
40
|
+
"error_code": 0,
|
|
41
|
+
"map_present": 1,
|
|
42
|
+
"in_cleaning": 0,
|
|
43
|
+
"in_returning": 0,
|
|
44
|
+
"in_fresh_state": 1,
|
|
45
|
+
"lab_status": 1,
|
|
46
|
+
"water_box_status": 1,
|
|
47
|
+
"fan_power": 102,
|
|
48
|
+
"dnd_enabled": 0,
|
|
49
|
+
"map_status": 3,
|
|
50
|
+
"is_locating": 0,
|
|
51
|
+
"lock_status": 0,
|
|
52
|
+
"water_box_mode": 204,
|
|
53
|
+
"water_box_carriage_status": 0,
|
|
54
|
+
"mop_forbidden_enable": 0
|
|
55
|
+
}
|
|
56
|
+
],
|
|
57
|
+
"id": 96
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Codes
|
|
62
|
+
|
|
63
|
+
### Status Codes
|
|
64
|
+
|
|
65
|
+
| Code | Description |
|
|
66
|
+
| ---- | -------------- |
|
|
67
|
+
| 0 | Unknown |
|
|
68
|
+
| 1 | Initiating |
|
|
69
|
+
| 2 | Sleeping |
|
|
70
|
+
| 3 | Idle |
|
|
71
|
+
| 4 | Remote Control |
|
|
72
|
+
| 5 | Cleaning |
|
|
73
|
+
| 6 | Returning Dock |
|
|
74
|
+
| 7 | Manual Mode |
|
|
75
|
+
| 8 | Charging |
|
|
76
|
+
| 9 | Charging Error |
|
|
77
|
+
| 10 | Paused |
|
|
78
|
+
| 11 | Spot Cleaning |
|
|
79
|
+
| 12 | In Error |
|
|
80
|
+
| 13 | Shutting Down |
|
|
81
|
+
| 14 | Updating |
|
|
82
|
+
| 15 | Docking |
|
|
83
|
+
| 16 | Go To |
|
|
84
|
+
| 17 | Zone Clean |
|
|
85
|
+
| 18 | Room Clean |
|
|
86
|
+
| 100 | Fully Charged |
|
|
87
|
+
|
|
88
|
+
### Error Codes
|
|
89
|
+
|
|
90
|
+
| Code | Description |
|
|
91
|
+
| ---- | ------------------------------------- |
|
|
92
|
+
| 0 | No error |
|
|
93
|
+
| 1 | Laser sensor fault |
|
|
94
|
+
| 2 | Collision sensor fault |
|
|
95
|
+
| 3 | Wheel floating |
|
|
96
|
+
| 4 | Cliff sensor fault |
|
|
97
|
+
| 5 | Main brush blocked |
|
|
98
|
+
| 6 | Side brush blocked |
|
|
99
|
+
| 7 | Wheel blocked |
|
|
100
|
+
| 8 | Device stuck |
|
|
101
|
+
| 9 | Dust bin missing |
|
|
102
|
+
| 10 | Filter blocked |
|
|
103
|
+
| 11 | Magnetic field detected |
|
|
104
|
+
| 12 | Low battery |
|
|
105
|
+
| 13 | Charging problem |
|
|
106
|
+
| 14 | Battery failure |
|
|
107
|
+
| 15 | Wall sensor fault |
|
|
108
|
+
| 16 | Uneven surface |
|
|
109
|
+
| 17 | Side brush failure |
|
|
110
|
+
| 18 | Suction fan failure |
|
|
111
|
+
| 19 | Unpowered charging station |
|
|
112
|
+
| 20 | Unknown Error |
|
|
113
|
+
| 21 | Laser pressure sensor problem |
|
|
114
|
+
| 22 | Charge sensor problem |
|
|
115
|
+
| 23 | Dock problem |
|
|
116
|
+
| 24 | No-go zone or invisible wall detected |
|
|
117
|
+
| 254 | Bin full |
|
|
118
|
+
| 255 | Internal error |
|
|
119
|
+
| -1 | Unknown Error |
|