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.
Files changed (42) hide show
  1. package/README.md +1 -1
  2. package/dist/helper.js +62 -0
  3. package/dist/model/RoomMap.js +1 -1
  4. package/dist/platform.js +4 -4
  5. package/dist/platformRunner.js +3 -66
  6. package/dist/roborockCommunication/broadcast/client/LocalNetworkClient.js +25 -25
  7. package/dist/roborockCommunication/broadcast/client/MQTTClient.js +1 -2
  8. package/dist/roborockCommunication/broadcast/clientRouter.js +1 -1
  9. package/dist/roborockCommunication/broadcast/listener/implementation/connectionStateListener.js +2 -2
  10. package/dist/roborockCommunication/broadcast/listener/implementation/syncMessageListener.js +0 -4
  11. package/dist/roborockCommunication/broadcast/messageProcessor.js +10 -1
  12. package/dist/roborockCommunication/broadcast/model/protocol.js +9 -0
  13. package/dist/roborockService.js +20 -1
  14. package/matterbridge-roborock-vacuum-plugin.config.json +1 -1
  15. package/matterbridge-roborock-vacuum-plugin.schema.json +1 -1
  16. package/misc/status.md +119 -0
  17. package/package.json +1 -1
  18. package/src/helper.ts +83 -0
  19. package/src/initialData/getSupportedAreas.ts +1 -1
  20. package/src/model/RoomMap.ts +1 -1
  21. package/src/platform.ts +4 -4
  22. package/src/platformRunner.ts +3 -86
  23. package/src/roborockCommunication/broadcast/client/LocalNetworkClient.ts +32 -31
  24. package/src/roborockCommunication/broadcast/client/MQTTClient.ts +2 -2
  25. package/src/roborockCommunication/broadcast/clientRouter.ts +1 -1
  26. package/src/roborockCommunication/broadcast/listener/implementation/connectionStateListener.ts +2 -3
  27. package/src/roborockCommunication/broadcast/listener/implementation/syncMessageListener.ts +4 -4
  28. package/src/roborockCommunication/broadcast/messageProcessor.ts +14 -7
  29. package/src/roborockCommunication/broadcast/model/protocol.ts +9 -0
  30. package/src/roborockService.ts +21 -1
  31. package/src/rvc.ts +1 -1
  32. package/src/tests/platformRunner2.test.ts +4 -7
  33. package/src/tests/roborockCommunication/RESTAPI/roborockAuthenticateApi.test.ts +136 -0
  34. package/src/tests/roborockCommunication/RESTAPI/roborockIoTApi.test.ts +106 -0
  35. package/src/tests/roborockCommunication/broadcast/client/LocalNetworkClient.test.ts +3 -5
  36. package/src/tests/roborockCommunication/broadcast/clientRouter.test.ts +168 -0
  37. package/src/tests/roborockCommunication/broadcast/listener/implementation/syncMessageListener.test.ts +3 -3
  38. package/src/tests/roborockCommunication/broadcast/messageProcessor.test.ts +131 -0
  39. package/src/tests/roborockService.test.ts +102 -2
  40. package/src/tests/roborockService3.test.ts +133 -0
  41. package/src/tests/roborockService4.test.ts +76 -0
  42. package/src/tests/roborockService5.test.ts +79 -0
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,82 @@ 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 (platform.robot?.device === undefined || platform.roborockService === undefined) return undefined;
51
+ if (robot.roomInfo === undefined) {
52
+ const roomData = await platform.roborockService.getRoomMappings(robot.device.duid);
53
+ if (roomData !== undefined && roomData.length > 0) {
54
+ robot.roomInfo = new RoomMap(roomData ?? [], rooms);
55
+ return robot.roomInfo;
56
+ }
57
+ }
58
+
59
+ if (robot.roomInfo === undefined) {
60
+ const mapInfo = await platform.roborockService.getMapInformation(robot.device.duid);
61
+ if (mapInfo && mapInfo.maps && mapInfo.maps.length > 0) {
62
+ platform.log.error(`getRoomMap - mapInfo: ${debugStringify(mapInfo.maps)}`);
63
+
64
+ const roomDataMap = mapInfo.maps[0].rooms.map((r) => [r.id, parseInt(r.iot_name_id), r.tag] as [number, number, number]);
65
+ robot.roomInfo = new RoomMap(roomDataMap, rooms);
66
+ }
67
+ }
68
+
69
+ return robot.roomInfo;
70
+ }
71
+
72
+ export async function getRoomMapFromDevice(device: Device, platform: RoborockMatterbridgePlatform): Promise<RoomMap> {
73
+ const rooms = device?.rooms ?? [];
74
+
75
+ platform.log.notice('-------------------------------------------0--------------------------------------------------------');
76
+ platform.log.notice(`getRoomMapFromDevice: ${debugStringify(rooms)}`);
77
+
78
+ if (device && platform.roborockService) {
79
+ const roomData = await platform.roborockService.getRoomMappings(device.duid);
80
+ if (roomData !== undefined && roomData.length > 0) {
81
+ platform.log.notice(`getRoomMapFromDevice - roomData: ${debugStringify(roomData ?? [])}`);
82
+
83
+ const roomMap = new RoomMap(roomData ?? [], rooms);
84
+
85
+ platform.log.notice(`getRoomMapFromDevice - roomMap: ${debugStringify(roomMap)}`);
86
+ platform.log.notice('-------------------------------------------1--------------------------------------------------------');
87
+ return roomMap;
88
+ }
89
+
90
+ const mapInfo = await platform.roborockService.getMapInformation(device.duid);
91
+ platform.log.notice(`getRoomMapFromDevice - mapInfo: ${mapInfo ? debugStringify(mapInfo) : 'undefined'}`);
92
+
93
+ if (mapInfo && mapInfo.maps && mapInfo.maps.length > 0) {
94
+ const roomDataMap = mapInfo.maps[0].rooms.map((r) => [r.id, parseInt(r.iot_name_id), r.tag] as [number, number, number]);
95
+
96
+ const roomMap = new RoomMap(roomDataMap, rooms);
97
+
98
+ platform.log.notice(`getRoomMapFromDevice - roomMap: ${debugStringify(roomMap)}`);
99
+ platform.log.notice('-------------------------------------------2--------------------------------------------------------');
100
+ return roomMap;
101
+ }
102
+ }
103
+
104
+ return new RoomMap([], rooms);
105
+ }
@@ -1,6 +1,6 @@
1
1
  import { AnsiLogger, debugStringify } from 'matterbridge/logger';
2
2
  import { ServiceArea } from 'matterbridge/matter/clusters';
3
- import RoomMap from '../model/RoomMap.js';
3
+ import { RoomMap } from '../model/RoomMap.js';
4
4
  import { Room } from '../roborockCommunication/Zmodel/room.js';
5
5
  import { randomInt } from 'node:crypto';
6
6
 
@@ -17,7 +17,7 @@ roomMap = {
17
17
 
18
18
  import { Room } from '../roborockCommunication/Zmodel/room.js';
19
19
 
20
- export default class RoomMap {
20
+ export class RoomMap {
21
21
  readonly rooms: {
22
22
  id: number;
23
23
  globalId: number | undefined;
package/src/platform.ts CHANGED
@@ -4,7 +4,7 @@ import { AnsiLogger, debugStringify, LogLevel } 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';
@@ -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.6')) {
36
+ if (this.verifyMatterbridgeVersion === undefined || typeof this.verifyMatterbridgeVersion !== 'function' || !this.verifyMatterbridgeVersion('3.1.7')) {
37
37
  throw new Error(
38
- `This plugin requires Matterbridge version >= "3.1.6". Please update Matterbridge from ${this.matterbridge.matterbridgeVersion} to the latest version in the frontend.`,
38
+ `This plugin requires Matterbridge version >= "3.1.7". 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);
@@ -231,7 +231,7 @@ export class RoborockMatterbridgePlatform extends MatterbridgeDynamicPlatform {
231
231
  vacuum.rooms = rooms.map((room) => ({ id: room.id, name: room.displayName }) as Room);
232
232
  }
233
233
 
234
- const roomMap = await this.platformRunner.getRoomMapFromDevice(vacuum);
234
+ const roomMap = await getRoomMapFromDevice(vacuum, this);
235
235
 
236
236
  this.log.debug('Initializing - roomMap: ', debugStringify(roomMap));
237
237
 
@@ -1,10 +1,9 @@
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 { CloudMessageModel } from './model/CloudMessageModel.js';
5
5
  import { RoborockMatterbridgePlatform } from './platform.js';
6
6
  import { state_to_matter_operational_status, state_to_matter_state } from './share/function.js';
7
- import RoomMap from './model/RoomMap.js';
8
7
  import { getBatteryState, getBatteryStatus, getOperationalErrorState } from './initialData/index.js';
9
8
  import { NotifyMessageTypes } from './notifyMessageTypes.js';
10
9
  import { CloudMessageResult } from './roborockCommunication/Zmodel/messageResult.js';
@@ -41,76 +40,6 @@ export class PlatformRunner {
41
40
  await this.updateRobot(NotifyMessageTypes.HomeData, homeData);
42
41
  }
43
42
 
44
- public async getRoomMapFromDevice(device: Device): Promise<RoomMap> {
45
- const platform = this.platform;
46
- const rooms = device?.rooms ?? [];
47
-
48
- platform.log.notice('-------------------------------------------0--------------------------------------------------------');
49
- platform.log.notice(`getRoomMapFromDevice: ${debugStringify(rooms)}`);
50
-
51
- if (device && platform.roborockService) {
52
- const roomData = await platform.roborockService.getRoomMappings(device.duid);
53
- if (roomData !== undefined && roomData.length > 0) {
54
- platform.log.notice(`getRoomMapFromDevice - roomData: ${debugStringify(roomData ?? [])}`);
55
-
56
- const roomMap = new RoomMap(roomData ?? [], rooms);
57
-
58
- platform.log.notice(`getRoomMapFromDevice - roomMap: ${debugStringify(roomMap)}`);
59
- platform.log.notice('-------------------------------------------1--------------------------------------------------------');
60
- return roomMap;
61
- }
62
-
63
- const mapInfo = await platform.roborockService.getMapInformation(device.duid);
64
- platform.log.notice(`getRoomMapFromDevice - mapInfo: ${mapInfo ? debugStringify(mapInfo) : 'undefined'}`);
65
-
66
- if (mapInfo && mapInfo.maps && mapInfo.maps.length > 0) {
67
- const roomDataMap = mapInfo.maps[0].rooms.map((r) => [r.id, parseInt(r.iot_name_id), r.tag] as [number, number, number]);
68
-
69
- const roomMap = new RoomMap(roomDataMap, rooms);
70
-
71
- platform.log.notice(`getRoomMapFromDevice - roomMap: ${debugStringify(roomMap)}`);
72
- platform.log.notice('-------------------------------------------2--------------------------------------------------------');
73
- return roomMap;
74
- }
75
- }
76
-
77
- return new RoomMap([], rooms);
78
- }
79
-
80
- private async getRoomMap(duid: string): Promise<RoomMap | undefined> {
81
- const platform = this.platform;
82
-
83
- const robot = platform.robots.get(duid);
84
- if (robot === undefined) {
85
- platform.log.error(`Error6: Robot with DUID ${duid} not found`);
86
- return undefined;
87
- }
88
-
89
- if (platform.roborockService === undefined) return undefined;
90
-
91
- const rooms = robot.device.rooms ?? [];
92
- // if (platform.robot?.device === undefined || platform.roborockService === undefined) return undefined;
93
- if (robot.roomInfo === undefined) {
94
- const roomData = await platform.roborockService.getRoomMappings(robot.device.duid);
95
- if (roomData !== undefined && roomData.length > 0) {
96
- robot.roomInfo = new RoomMap(roomData ?? [], rooms);
97
- return robot.roomInfo;
98
- }
99
- }
100
-
101
- if (robot.roomInfo === undefined) {
102
- const mapInfo = await platform.roborockService.getMapInformation(robot.device.duid);
103
- if (mapInfo && mapInfo.maps && mapInfo.maps.length > 0) {
104
- platform.log.error(`getRoomMap - mapInfo: ${debugStringify(mapInfo.maps)}`);
105
-
106
- const roomDataMap = mapInfo.maps[0].rooms.map((r) => [r.id, parseInt(r.iot_name_id), r.tag] as [number, number, number]);
107
- robot.roomInfo = new RoomMap(roomDataMap, rooms);
108
- }
109
- }
110
-
111
- return robot.roomInfo;
112
- }
113
-
114
43
  private async updateFromMQTTMessage(messageSource: NotifyMessageTypes, messageData: unknown, duid = '', tracked = false): Promise<void> {
115
44
  const platform = this.platform;
116
45
  duid = duid || (messageData as DeviceStatusNotify)?.duid || '';
@@ -191,7 +120,7 @@ export class PlatformRunner {
191
120
  robot.updateAttribute(ServiceArea.Cluster.id, 'selectedAreas', [], platform.log);
192
121
  } else {
193
122
  const currentMappedAreas = this.platform.roborockService?.getSupportedAreas(duid);
194
- const roomMap = await this.getRoomMap(duid);
123
+ const roomMap = await getRoomMap(duid, this.platform);
195
124
 
196
125
  // Get current room from segment_id
197
126
  const segment_id = data.cleaning_info?.segment_id ?? -1;
@@ -311,7 +240,7 @@ export class PlatformRunner {
311
240
  case Protocol.rpc_response: {
312
241
  const response = data.dps[messageType] as DpsPayload;
313
242
  // ignore network info
314
- if (!self.isStatusUpdate(response.result)) {
243
+ if (!isStatusUpdate(response.result)) {
315
244
  platform.log.debug('Ignore message:', debugStringify(data));
316
245
  return;
317
246
  }
@@ -398,18 +327,6 @@ export class PlatformRunner {
398
327
  return undefined;
399
328
  }
400
329
 
401
- private isStatusUpdate(result: unknown): boolean {
402
- return (
403
- Array.isArray(result) &&
404
- result.length > 0 &&
405
- typeof result[0] === 'object' &&
406
- result[0] !== null &&
407
- 'msg_ver' in result[0] &&
408
- (result[0] as CloudMessageResult).msg_ver !== undefined &&
409
- (result[0] as CloudMessageResult).msg_ver !== null
410
- );
411
- }
412
-
413
330
  private updateFromHomeData(homeData: Home): void {
414
331
  const platform = this.platform;
415
332
 
@@ -16,6 +16,7 @@ export class LocalNetworkClient extends AbstractClient {
16
16
  private buffer: ChunkBuffer = new ChunkBuffer();
17
17
  private messageIdSeq: Sequence;
18
18
  private pingInterval?: NodeJS.Timeout;
19
+ private keepConnectionAliveInterval: NodeJS.Timeout | undefined = undefined;
19
20
  duid: string;
20
21
  ip: string;
21
22
 
@@ -45,6 +46,8 @@ export class LocalNetworkClient extends AbstractClient {
45
46
  // Data event listener
46
47
  this.socket.on('data', this.onMessage.bind(this));
47
48
  this.socket.connect(58867, this.ip);
49
+
50
+ this.keepConnectionAlive();
48
51
  }
49
52
 
50
53
  public async disconnect(): Promise<void> {
@@ -75,8 +78,8 @@ export class LocalNetworkClient extends AbstractClient {
75
78
  }
76
79
 
77
80
  private async onConnect(): Promise<void> {
78
- this.logger.debug(`LocalNetworkClient: ${this.duid} connected to ${this.ip}`);
79
- this.logger.debug(`LocalNetworkClient: ${this.duid} socket writable: ${this.socket?.writable}, readable: ${this.socket?.readable}`);
81
+ this.logger.debug(` [LocalNetworkClient]: ${this.duid} connected to ${this.ip}`);
82
+ this.logger.debug(` [LocalNetworkClient]: ${this.duid} socket writable: ${this.socket?.writable}, readable: ${this.socket?.readable}`);
80
83
  this.connected = true;
81
84
  this.retryCount = 0;
82
85
 
@@ -85,25 +88,8 @@ export class LocalNetworkClient extends AbstractClient {
85
88
  await this.connectionListeners.onConnected(this.duid);
86
89
  }
87
90
 
88
- private async onEnd(): Promise<void> {
89
- await this.destroySocket('Socket has ended.');
90
- await this.connectionListeners.onDisconnected(this.duid, 'Socket has ended.');
91
- }
92
-
93
91
  private async onDisconnect(hadError: boolean): Promise<void> {
94
- await this.destroySocket(`Socket disconnected. Had error: ${hadError}`);
95
-
96
- if (!hadError) {
97
- await this.connectionListeners.onDisconnected(this.duid, 'Socket disconnected. Had no error.');
98
- }
99
- }
100
-
101
- private async onTimeout(): Promise<void> {
102
- this.logger.error(`LocalNetworkClient: Socket for ${this.duid} timed out.`);
103
- }
104
-
105
- private async destroySocket(message: string): Promise<void> {
106
- this.logger.error(`LocalNetworkClient: Destroying socket for ${this.duid} due to: ${message}`);
92
+ this.logger.info(` [LocalNetworkClient]: ${this.duid} socket disconnected. Had error: ${hadError}`);
107
93
  this.connected = false;
108
94
 
109
95
  if (this.socket) {
@@ -113,22 +99,20 @@ export class LocalNetworkClient extends AbstractClient {
113
99
  if (this.pingInterval) {
114
100
  clearInterval(this.pingInterval);
115
101
  }
102
+ await this.connectionListeners.onDisconnected(this.duid, 'Socket disconnected. Had no error.');
116
103
  }
117
104
 
118
105
  private async onError(error: Error): Promise<void> {
119
- this.logger.error('LocalNetworkClient: Socket connection error: ' + error.message);
120
- this.connected = false;
121
-
122
- if (this.socket) {
123
- this.socket.destroy();
124
- this.socket = undefined;
125
- }
106
+ this.logger.error(` [LocalNetworkClient]: Socket error for ${this.duid}: ${error.message}`);
107
+ await this.connectionListeners.onError(this.duid, error.message);
108
+ }
126
109
 
127
- if (this.pingInterval) {
128
- clearInterval(this.pingInterval);
129
- }
110
+ private async onTimeout(): Promise<void> {
111
+ this.logger.error(` [LocalNetworkClient]: Socket for ${this.duid} timed out.`);
112
+ }
130
113
 
131
- await this.connectionListeners.onError(this.duid, error.message);
114
+ private async onEnd(): Promise<void> {
115
+ this.logger.debug(` [LocalNetworkClient]: ${this.duid} socket ended.`);
132
116
  }
133
117
 
134
118
  private async onMessage(message: Buffer): Promise<void> {
@@ -212,4 +196,21 @@ export class LocalNetworkClient extends AbstractClient {
212
196
  });
213
197
  await this.send(this.duid, request);
214
198
  }
199
+
200
+ private keepConnectionAlive(): void {
201
+ if (this.keepConnectionAliveInterval) {
202
+ clearTimeout(this.keepConnectionAliveInterval);
203
+ this.keepConnectionAliveInterval.unref();
204
+ }
205
+
206
+ this.keepConnectionAliveInterval = setInterval(
207
+ () => {
208
+ if (this.socket === undefined || !this.connected || !this.socket.writable || this.socket.readable) {
209
+ this.logger.debug(` [LocalNetworkClient]: ${this.duid} socket is not writable or readable, reconnecting...`);
210
+ this.connect();
211
+ }
212
+ },
213
+ 60 * 60 * 1000,
214
+ );
215
+ }
215
216
  }
@@ -36,8 +36,8 @@ export class MQTTClient extends AbstractClient {
36
36
  username: this.mqttUsername,
37
37
  password: this.mqttPassword,
38
38
  keepalive: 30,
39
- log: (...args: unknown[]) => {
40
- this.logger.debug(`MQTTClient args: ${debugStringify(args)}`);
39
+ log: () => {
40
+ // ...args: unknown[] this.logger.debug(`MQTTClient args: ${debugStringify(args)}`);
41
41
  },
42
42
  });
43
43
 
@@ -15,9 +15,9 @@ export class ClientRouter implements Client {
15
15
  protected readonly messageListeners = new ChainedMessageListener();
16
16
 
17
17
  private readonly context: MessageContext;
18
- private readonly mqttClient: MQTTClient;
19
18
  private readonly localClients = new Map<string, LocalNetworkClient>();
20
19
  private readonly logger: AnsiLogger;
20
+ private mqttClient: MQTTClient;
21
21
 
22
22
  public constructor(logger: AnsiLogger, userdata: UserData) {
23
23
  this.context = new MessageContext(userdata);
@@ -43,11 +43,10 @@ export class ConnectionStateListener implements AbstractConnectionListener {
43
43
  return;
44
44
  }
45
45
 
46
- this.logger.info(`Re-registering device with DUID ${duid} to ${this.clientName}`);
47
-
48
46
  setTimeout(() => {
47
+ this.logger.info(`Re-registering device with DUID ${duid} to ${this.clientName}`);
49
48
  this.client.connect();
50
- }, 3000);
49
+ }, 10000);
51
50
 
52
51
  this.client.isInDisconnectingStep = false;
53
52
  }
@@ -28,10 +28,10 @@ export class SyncMessageListener implements AbstractMessageListener {
28
28
  const messageId = dps.id;
29
29
 
30
30
  const responseHandler = this.pending.get(messageId);
31
- const result = dps.result as Record<string, unknown>;
32
- if (result && result.length == 1 && result[0] == 'ok') {
33
- return;
34
- }
31
+ // const result = dps.result as Record<string, unknown>;
32
+ // if (result && result.length == 1 && result[0] == 'ok') {
33
+ // return;
34
+ // }
35
35
 
36
36
  if (responseHandler) {
37
37
  responseHandler(dps.result as ResponseMessage);
@@ -34,11 +34,6 @@ export class MessageProcessor {
34
34
  return await this.client.get(duid, request);
35
35
  }
36
36
 
37
- // public async getDeviceStatus(duid: string): Promise<DeviceStatus> {
38
- // const request = new RequestMessage({ method: 'get_status' });
39
- // return this.client.get<CloudMessageResult[]>(duid, request).then((response) => new DeviceStatus(response[0]));
40
- // }
41
-
42
37
  public async getDeviceStatus(duid: string): Promise<DeviceStatus | undefined> {
43
38
  const request = new RequestMessage({ method: 'get_status' });
44
39
  const response = await this.client.get<CloudMessageResult[]>(duid, request);
@@ -51,6 +46,18 @@ export class MessageProcessor {
51
46
  return undefined;
52
47
  }
53
48
 
49
+ public async getDeviceStatusOverMQTT(duid: string): Promise<DeviceStatus | undefined> {
50
+ const request = new RequestMessage({ method: 'get_status', secure: true });
51
+ const response = await this.client.get<CloudMessageResult[]>(duid, request);
52
+
53
+ if (response) {
54
+ this.logger?.debug('MQTT - Device status: ', debugStringify(response));
55
+ return new DeviceStatus(response);
56
+ }
57
+
58
+ return undefined;
59
+ }
60
+
54
61
  public async getRooms(duid: string, rooms: Room[]): Promise<RoomInfo> {
55
62
  const request = new RequestMessage({ method: 'get_room_mapping' });
56
63
  return this.client.get<number[][] | undefined>(duid, request).then((response) => new RoomInfo(rooms, response ?? []));
@@ -98,9 +105,9 @@ export class MessageProcessor {
98
105
  return this.client.get(duid, def);
99
106
  }
100
107
 
101
- public async findMyRobot(duid: string): Promise<void> {
108
+ public async findMyRobot(duid: string): Promise<unknown> {
102
109
  const request = new RequestMessage({ method: 'find_me' });
103
- return this.client.send(duid, request);
110
+ return this.client.get(duid, request);
104
111
  }
105
112
 
106
113
  public async getCleanModeData(duid: string): Promise<{ suctionPower: number; waterFlow: number; distance_off: number; mopRoute: number }> {
@@ -12,8 +12,17 @@ export enum Protocol {
12
12
  battery = 122,
13
13
  suction_power = 123,
14
14
  water_box_mode = 124,
15
+ main_brush_work_time = 125,
16
+ side_brush_work_time = 126,
17
+ filter_work_time = 127,
15
18
  additional_props = 128,
19
+ task_complete = 130,
20
+ task_cancel_low_power = 131,
21
+ task_cancel_in_motion = 132,
22
+ charge_status = 133,
23
+ drying_status = 134,
16
24
  back_type = 139, // WTF is this
17
25
  map_response = 301,
26
+ some_thing_happened_when_socket_closed = 500,
18
27
  }
19
28
  // "deviceStatus":{"120":0,"121":8,"122":100,"123":110,"124":209,"125":99,"126":96,"127":97,"128":0,"133":1,"134":1,"135":0,"139":0}
@@ -274,7 +274,7 @@ export default class RoborockService {
274
274
  await messageProcessor.getDeviceStatus(device.duid).then((response: DeviceStatus | undefined) => {
275
275
  if (self.deviceNotify && response) {
276
276
  const message = { duid: device.duid, ...response.errorStatus, ...response.message } as DeviceStatusNotify;
277
- self.logger.debug('Device status update', debugStringify(message));
277
+ self.logger.debug('Socket - Device status update', debugStringify(message));
278
278
  self.deviceNotify(NotifyMessageTypes.LocalMessage, message);
279
279
  }
280
280
  });
@@ -284,6 +284,26 @@ export default class RoborockService {
284
284
  }, this.refreshInterval * 1000);
285
285
  }
286
286
 
287
+ public activateDeviceNotifyOverMQTT(device: Device): void {
288
+ // eslint-disable-next-line @typescript-eslint/no-this-alias
289
+ const self = this;
290
+ this.logger.notice('Requesting device info for device over MQTT', device.duid);
291
+ const messageProcessor = this.getMessageProcessor(device.duid);
292
+ this.requestDeviceStatusInterval = setInterval(async () => {
293
+ if (messageProcessor) {
294
+ await messageProcessor.getDeviceStatusOverMQTT(device.duid).then((response: DeviceStatus | undefined) => {
295
+ if (self.deviceNotify && response) {
296
+ const message = { duid: device.duid, ...response.errorStatus, ...response.message } as DeviceStatusNotify;
297
+ self.logger.debug('MQTT - Device status update', debugStringify(message));
298
+ self.deviceNotify(NotifyMessageTypes.LocalMessage, message);
299
+ }
300
+ });
301
+ } else {
302
+ self.logger.error('Local client not initialized');
303
+ }
304
+ }, this.refreshInterval * 500);
305
+ }
306
+
287
307
  public async listDevices(username: string): Promise<Device[]> {
288
308
  assert(this.iotApi !== undefined);
289
309
  assert(this.userdata !== undefined);
package/src/rvc.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { RoboticVacuumCleaner } from 'matterbridge/devices';
2
- import RoomMap from './model/RoomMap.js';
2
+ import { RoomMap } from './model/RoomMap.js';
3
3
  import { Device } from './roborockCommunication/index.js';
4
4
  import { getOperationalStates, getSupportedAreas, getSupportedCleanModes, getSupportedRunModes } from './initialData/index.js';
5
5
  import { AnsiLogger } from 'matterbridge/logger';
@@ -1,11 +1,9 @@
1
- import RoomMap from '../model/RoomMap';
2
- import { RoborockMatterbridgePlatform } from '../platform';
3
- import { PlatformRunner } from '../platformRunner';
1
+ import { getRoomMapFromDevice } from '../helper';
2
+ import { RoomMap } from '../model/RoomMap';
4
3
  import { MapInfo } from '../roborockCommunication';
5
4
 
6
5
  describe('PlatformRunner.getRoomMapFromDevice', () => {
7
6
  let platform: any;
8
- let runner: PlatformRunner;
9
7
 
10
8
  beforeEach(() => {
11
9
  platform = {
@@ -19,7 +17,6 @@ describe('PlatformRunner.getRoomMapFromDevice', () => {
19
17
  getMapInformation: jest.fn(),
20
18
  },
21
19
  };
22
- runner = new PlatformRunner(platform as RoborockMatterbridgePlatform);
23
20
  });
24
21
 
25
22
  it('returns RoomMap with roomData from getRoomMappings if available', async () => {
@@ -42,7 +39,7 @@ describe('PlatformRunner.getRoomMapFromDevice', () => {
42
39
  platform.roborockService.getRoomMappings.mockResolvedValue(roomData);
43
40
  platform.roborockService.getMapInformation.mockResolvedValue(undefined);
44
41
 
45
- const result = await runner.getRoomMapFromDevice(device as any);
42
+ const result = await getRoomMapFromDevice(device as any, platform);
46
43
 
47
44
  expect(result).toBeInstanceOf(RoomMap);
48
45
  expect(result.rooms.length).toEqual(4);
@@ -83,7 +80,7 @@ describe('PlatformRunner.getRoomMapFromDevice', () => {
83
80
  platform.roborockService.getRoomMappings.mockResolvedValue(undefined);
84
81
  platform.roborockService.getMapInformation.mockResolvedValue(mapInfo);
85
82
 
86
- const result = await runner.getRoomMapFromDevice(device as any);
83
+ const result = await getRoomMapFromDevice(device as any, platform);
87
84
 
88
85
  expect(result).toBeInstanceOf(RoomMap);
89
86
  expect(result.rooms.length).toEqual(4);