matterbridge-roborock-vacuum-plugin 1.0.8-rc07 → 1.0.8-rc09

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.
@@ -1,6 +1,7 @@
1
1
  import { RvcOperationalState } from 'matterbridge/matter/clusters';
2
2
  import { getDefaultOperationalStates } from '../behaviors/roborock.vacuum/default/initalData.js';
3
3
  import { VacuumErrorCode } from '../roborockCommunication/Zenum/vacuumAndDockErrorCode.js';
4
+ import { DockingStationStatusType, hasDockingStationError } from '../model/DockingStationStatus.js';
4
5
  export function getOperationalStates() {
5
6
  return getDefaultOperationalStates();
6
7
  }
@@ -13,3 +14,69 @@ export function getOperationalErrorState(errorCode) {
13
14
  }
14
15
  }
15
16
  }
17
+ export function getErrorFromErrorCode(errorCode) {
18
+ const operationalState = getOperationalErrorState(errorCode);
19
+ if (operationalState) {
20
+ return {
21
+ errorStateId: RvcOperationalState.ErrorState.NoError,
22
+ errorStateLabel: `${RvcOperationalState.ErrorState.NoError}`,
23
+ errorStateDetails: `Error code: ${errorCode}`,
24
+ };
25
+ }
26
+ return undefined;
27
+ }
28
+ export function getErrorFromDSS(status) {
29
+ if (!status) {
30
+ return {
31
+ errorStateId: RvcOperationalState.ErrorState.NoError,
32
+ errorStateLabel: 'No Docking Station Status',
33
+ errorStateDetails: 'Docking station status is not available.',
34
+ };
35
+ }
36
+ const hasError = hasDockingStationError(status);
37
+ if (hasError) {
38
+ if (status.cleanFluidStatus === DockingStationStatusType.Error) {
39
+ return {
40
+ errorStateId: RvcOperationalState.ErrorState.MopCleaningPadMissing,
41
+ errorStateLabel: 'Clean Fluid Error',
42
+ errorStateDetails: 'The clean fluid is not available or has an issue.',
43
+ };
44
+ }
45
+ if (status.waterBoxFilterStatus === DockingStationStatusType.Error) {
46
+ return {
47
+ errorStateId: RvcOperationalState.ErrorState.WaterTankEmpty,
48
+ errorStateLabel: 'Water Box Filter Error',
49
+ errorStateDetails: 'The water box filter is not available or has an issue.',
50
+ };
51
+ }
52
+ if (status.dustBagStatus === DockingStationStatusType.Error) {
53
+ return {
54
+ errorStateId: RvcOperationalState.ErrorState.DustBinMissing,
55
+ errorStateLabel: 'Dust Bag Error',
56
+ errorStateDetails: 'The dust bag is not available or has an issue.',
57
+ };
58
+ }
59
+ if (status.dirtyWaterBoxStatus === DockingStationStatusType.Error) {
60
+ return {
61
+ errorStateId: RvcOperationalState.ErrorState.WaterTankEmpty,
62
+ errorStateLabel: 'Dirty Water Box Error',
63
+ errorStateDetails: 'The dirty water box is not available or has an issue.',
64
+ };
65
+ }
66
+ if (status.clearWaterBoxStatus === DockingStationStatusType.Error) {
67
+ return {
68
+ errorStateId: RvcOperationalState.ErrorState.WaterTankEmpty,
69
+ errorStateLabel: 'Clear Water Box Error',
70
+ errorStateDetails: 'The clear water box is not available or has an issue.',
71
+ };
72
+ }
73
+ if (status.isUpdownWaterReady === DockingStationStatusType.Error) {
74
+ return {
75
+ errorStateId: RvcOperationalState.ErrorState.WaterTankMissing,
76
+ errorStateLabel: 'Updown Water Ready Error',
77
+ errorStateDetails: 'The updown water tank is not ready or has an issue.',
78
+ };
79
+ }
80
+ }
81
+ return undefined;
82
+ }
package/dist/platform.js CHANGED
@@ -25,8 +25,8 @@ export class RoborockMatterbridgePlatform extends MatterbridgeDynamicPlatform {
25
25
  persist;
26
26
  constructor(matterbridge, log, config) {
27
27
  super(matterbridge, log, config);
28
- if (this.verifyMatterbridgeVersion === undefined || typeof this.verifyMatterbridgeVersion !== 'function' || !this.verifyMatterbridgeVersion('3.0.5')) {
29
- throw new Error(`This plugin requires Matterbridge version >= "3.0.5". Please update Matterbridge from ${this.matterbridge.matterbridgeVersion} to the latest version in the frontend.`);
28
+ if (this.verifyMatterbridgeVersion === undefined || typeof this.verifyMatterbridgeVersion !== 'function' || !this.verifyMatterbridgeVersion('3.0.7')) {
29
+ throw new Error(`This plugin requires Matterbridge version >= "3.0.7". Please update Matterbridge from ${this.matterbridge.matterbridgeVersion} to the latest version in the frontend.`);
30
30
  }
31
31
  this.log.info('Initializing platform:', this.config.name);
32
32
  if (config.whiteList === undefined)
@@ -56,7 +56,7 @@ export class LocalNetworkClient extends AbstractClient {
56
56
  await this.connectionListeners.onConnected();
57
57
  }
58
58
  async onDisconnect() {
59
- this.logger.info('Socket has disconnected.');
59
+ this.logger.notice('LocalNetworkClient: Socket has disconnected.');
60
60
  this.connected = false;
61
61
  if (this.socket) {
62
62
  this.socket.destroy();
@@ -68,7 +68,7 @@ export class LocalNetworkClient extends AbstractClient {
68
68
  await this.connectionListeners.onDisconnected();
69
69
  }
70
70
  async onError(result) {
71
- this.logger.error('Socket connection error: ' + result);
71
+ this.logger.error('LocalNetworkClient: Socket connection error: ' + result);
72
72
  this.connected = false;
73
73
  if (this.socket) {
74
74
  this.socket.destroy();
@@ -158,7 +158,7 @@ export default class RoborockService {
158
158
  await this.messageProcessor.getDeviceStatus(device.duid).then((response) => {
159
159
  if (self.deviceNotify) {
160
160
  const message = { duid: device.duid, ...response.errorStatus, ...response.message };
161
- self.logger.debug('Device status update xxx', debugStringify(message));
161
+ self.logger.debug('Device status update', debugStringify(message));
162
162
  self.deviceNotify(NotifyMessageTypes.LocalMessage, message);
163
163
  }
164
164
  });
@@ -320,6 +320,8 @@ export default class RoborockService {
320
320
  self.deviceNotify(NotifyMessageTypes.BatteryUpdate, { duid: device.duid, percentage });
321
321
  }
322
322
  },
323
+ onStatusChanged: () => {
324
+ },
323
325
  });
324
326
  this.logger.debug('Local device', device.duid);
325
327
  try {
@@ -332,16 +334,21 @@ export default class RoborockService {
332
334
  this.logger.debug('initializing the local connection for this client towards ' + this.ip);
333
335
  this.localClient = this.messageClient.registerClient(device.duid, this.ip);
334
336
  this.localClient.connect();
335
- while (!this.localClient.isConnected()) {
337
+ let count = 0;
338
+ while (!this.localClient.isConnected() && count < 20) {
339
+ this.logger.debug('Keep waiting for local client to connect');
340
+ count++;
336
341
  await this.sleep(500);
337
342
  }
343
+ if (!this.localClient.isConnected()) {
344
+ throw new Error('Local client did not connect after 10 attempts, something is wrong');
345
+ }
338
346
  this.logger.debug('LocalClient connected');
339
347
  }
340
348
  }
341
349
  catch (error) {
342
350
  this.logger.error('Error requesting network info', error);
343
351
  }
344
- this.logger.debug('Local client connected');
345
352
  }
346
353
  sleep(ms) {
347
354
  return new Promise((resolve) => setTimeout(resolve, ms));
package/dist/rvc.js CHANGED
@@ -21,15 +21,18 @@ export class RoborockVacuumCleaner extends RoboticVacuumCleaner {
21
21
  this.rrHomeId = device.rrHomeId;
22
22
  }
23
23
  configurateHandler(behaviorHandler) {
24
- this.addCommandHandler('identify', async ({ request: { identifyTime } }) => {
25
- behaviorHandler.executeCommand('playSoundToLocate', identifyTime);
24
+ this.addCommandHandler('identify', async ({ request, cluster, attributes, endpoint }) => {
25
+ this.log.info(`Identify command received for endpoint ${endpoint}, cluster ${cluster}, attributes ${attributes}, request: ${JSON.stringify(request)}`);
26
+ behaviorHandler.executeCommand('playSoundToLocate', request.identifyTime ?? 0);
26
27
  });
27
28
  this.addCommandHandler('selectAreas', async ({ request }) => {
28
- this.log.info(`Selecting areas: ${request.newAreas.join(', ')}`);
29
- behaviorHandler.executeCommand('selectAreas', request.newAreas);
29
+ const { newAreas } = request;
30
+ this.log.info(`Selecting areas: ${newAreas?.join(', ')}`);
31
+ behaviorHandler.executeCommand('selectAreas', newAreas);
30
32
  });
31
33
  this.addCommandHandler('changeToMode', async ({ request }) => {
32
- behaviorHandler.executeCommand('changeToMode', request.newMode);
34
+ const { newMode } = request;
35
+ behaviorHandler.executeCommand('changeToMode', newMode);
33
36
  });
34
37
  this.addCommandHandler('pause', async () => {
35
38
  behaviorHandler.executeCommand('pause');
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "matterbridge-roborock-vacuum-plugin",
3
3
  "type": "DynamicPlatform",
4
- "version": "1.0.8-rc07",
4
+ "version": "1.0.8-rc09",
5
5
  "whiteList": [],
6
6
  "blackList": [],
7
7
  "useInterval": true,
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "title": "Matterbridge Roborock Vacuum Plugin",
3
- "description": "matterbridge-roborock-vacuum-plugin v. 1.0.8-rc07 by https://github.com/RinDevJunior",
3
+ "description": "matterbridge-roborock-vacuum-plugin v. 1.0.8-rc09 by https://github.com/RinDevJunior",
4
4
  "type": "object",
5
5
  "required": ["username", "password"],
6
6
  "properties": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "matterbridge-roborock-vacuum-plugin",
3
- "version": "1.0.8-rc07",
3
+ "version": "1.0.8-rc09",
4
4
  "description": "Matterbridge Roborock Vacuum Plugin",
5
5
  "author": "https://github.com/RinDevJunior",
6
6
  "license": "MIT",
@@ -1,6 +1,7 @@
1
1
  import { RvcOperationalState } from 'matterbridge/matter/clusters';
2
2
  import { getDefaultOperationalStates } from '../behaviors/roborock.vacuum/default/initalData.js';
3
3
  import { VacuumErrorCode } from '../roborockCommunication/Zenum/vacuumAndDockErrorCode.js';
4
+ import { DockingStationStatus, DockingStationStatusType, hasDockingStationError } from '../model/DockingStationStatus.js';
4
5
 
5
6
  export function getOperationalStates(): RvcOperationalState.OperationalStateStruct[] {
6
7
  return getDefaultOperationalStates();
@@ -15,3 +16,79 @@ export function getOperationalErrorState(errorCode: VacuumErrorCode): RvcOperati
15
16
  }
16
17
  }
17
18
  }
19
+
20
+ export function getErrorFromErrorCode(errorCode: VacuumErrorCode): RvcOperationalState.ErrorStateStruct | undefined {
21
+ const operationalState = getOperationalErrorState(errorCode);
22
+ if (operationalState) {
23
+ return {
24
+ errorStateId: RvcOperationalState.ErrorState.NoError,
25
+ errorStateLabel: `${RvcOperationalState.ErrorState.NoError}`,
26
+ errorStateDetails: `Error code: ${errorCode}`,
27
+ };
28
+ }
29
+ return undefined;
30
+ }
31
+
32
+ export function getErrorFromDSS(status: DockingStationStatus): RvcOperationalState.ErrorStateStruct | undefined {
33
+ if (!status) {
34
+ return {
35
+ errorStateId: RvcOperationalState.ErrorState.NoError,
36
+ errorStateLabel: 'No Docking Station Status',
37
+ errorStateDetails: 'Docking station status is not available.',
38
+ };
39
+ }
40
+
41
+ const hasError = hasDockingStationError(status);
42
+
43
+ if (hasError) {
44
+ if (status.cleanFluidStatus === DockingStationStatusType.Error) {
45
+ return {
46
+ errorStateId: RvcOperationalState.ErrorState.MopCleaningPadMissing,
47
+ errorStateLabel: 'Clean Fluid Error',
48
+ errorStateDetails: 'The clean fluid is not available or has an issue.',
49
+ };
50
+ }
51
+
52
+ if (status.waterBoxFilterStatus === DockingStationStatusType.Error) {
53
+ return {
54
+ errorStateId: RvcOperationalState.ErrorState.WaterTankEmpty,
55
+ errorStateLabel: 'Water Box Filter Error',
56
+ errorStateDetails: 'The water box filter is not available or has an issue.',
57
+ };
58
+ }
59
+
60
+ if (status.dustBagStatus === DockingStationStatusType.Error) {
61
+ return {
62
+ errorStateId: RvcOperationalState.ErrorState.DustBinMissing,
63
+ errorStateLabel: 'Dust Bag Error',
64
+ errorStateDetails: 'The dust bag is not available or has an issue.',
65
+ };
66
+ }
67
+
68
+ if (status.dirtyWaterBoxStatus === DockingStationStatusType.Error) {
69
+ return {
70
+ errorStateId: RvcOperationalState.ErrorState.WaterTankEmpty, // TODO: Check if this is correct
71
+ errorStateLabel: 'Dirty Water Box Error',
72
+ errorStateDetails: 'The dirty water box is not available or has an issue.',
73
+ };
74
+ }
75
+
76
+ if (status.clearWaterBoxStatus === DockingStationStatusType.Error) {
77
+ return {
78
+ errorStateId: RvcOperationalState.ErrorState.WaterTankEmpty,
79
+ errorStateLabel: 'Clear Water Box Error',
80
+ errorStateDetails: 'The clear water box is not available or has an issue.',
81
+ };
82
+ }
83
+
84
+ if (status.isUpdownWaterReady === DockingStationStatusType.Error) {
85
+ return {
86
+ errorStateId: RvcOperationalState.ErrorState.WaterTankMissing,
87
+ errorStateLabel: 'Updown Water Ready Error',
88
+ errorStateDetails: 'The updown water tank is not ready or has an issue.',
89
+ };
90
+ }
91
+ }
92
+
93
+ return undefined;
94
+ }
package/src/platform.ts CHANGED
@@ -32,9 +32,9 @@ export class RoborockMatterbridgePlatform extends MatterbridgeDynamicPlatform {
32
32
  super(matterbridge, log, config);
33
33
 
34
34
  // Verify that Matterbridge is the correct version
35
- if (this.verifyMatterbridgeVersion === undefined || typeof this.verifyMatterbridgeVersion !== 'function' || !this.verifyMatterbridgeVersion('3.0.5')) {
35
+ if (this.verifyMatterbridgeVersion === undefined || typeof this.verifyMatterbridgeVersion !== 'function' || !this.verifyMatterbridgeVersion('3.0.7')) {
36
36
  throw new Error(
37
- `This plugin requires Matterbridge version >= "3.0.5". Please update Matterbridge from ${this.matterbridge.matterbridgeVersion} to the latest version in the frontend.`,
37
+ `This plugin requires Matterbridge version >= "3.0.7". Please update Matterbridge from ${this.matterbridge.matterbridgeVersion} to the latest version in the frontend.`,
38
38
  );
39
39
  }
40
40
  this.log.info('Initializing platform:', this.config.name);
@@ -16,6 +16,7 @@ import { BatteryMessage, Device, DeviceErrorMessage, DeviceStatusNotify, Home }
16
16
  import { OperationStatusCode } from './roborockCommunication/Zenum/operationStatusCode.js';
17
17
  import { getCurrentCleanModeFunc } from './share/runtimeHelper.js';
18
18
  import { debugStringify } from 'matterbridge/logger';
19
+ // import { getErrorFromErrorCode } from './initialData/getOperationalStates.js';
19
20
 
20
21
  export class PlatformRunner {
21
22
  platform: RoborockMatterbridgePlatform;
@@ -95,6 +96,11 @@ export class PlatformRunner {
95
96
  if (operationalStateId) {
96
97
  platform.log.error(`Error occurred: ${message.errorCode}`);
97
98
  platform.robot.updateAttribute(RvcOperationalState.Cluster.id, 'operationalState', operationalStateId, platform.log);
99
+
100
+ // const errorState = getErrorFromErrorCode(message.errorCode);
101
+ // if (operationalStateId === RvcOperationalState.OperationalState.Error && errorState) {
102
+ // platform.robot.updateAttribute(RvcOperationalState.Cluster.id, 'operationalError', errorState, platform.log);
103
+ // }
98
104
  }
99
105
  break;
100
106
  }
@@ -67,7 +67,7 @@ export class LocalNetworkClient extends AbstractClient {
67
67
  }
68
68
 
69
69
  private async onDisconnect(): Promise<void> {
70
- this.logger.info('Socket has disconnected.');
70
+ this.logger.notice('LocalNetworkClient: Socket has disconnected.');
71
71
  this.connected = false;
72
72
 
73
73
  if (this.socket) {
@@ -82,7 +82,7 @@ export class LocalNetworkClient extends AbstractClient {
82
82
  }
83
83
 
84
84
  private async onError(result: Error): Promise<void> {
85
- this.logger.error('Socket connection error: ' + result);
85
+ this.logger.error('LocalNetworkClient: Socket connection error: ' + result);
86
86
  this.connected = false;
87
87
 
88
88
  if (this.socket) {
@@ -2,6 +2,7 @@ import { ResponseMessage } from '../../model/responseMessage.js';
2
2
  import { AbstractMessageListener } from '../index.js';
3
3
  import { Protocol } from '../../model/protocol.js';
4
4
  import { AbstractMessageHandler } from '../abstractMessageHandler.js';
5
+ // import { DeviceStatus } from '../../../Zmodel/deviceStatus.js';
5
6
 
6
7
  export class SimpleMessageListener implements AbstractMessageListener {
7
8
  private handler: AbstractMessageHandler | undefined;
@@ -29,6 +29,7 @@ import type {
29
29
  DeviceStatusNotify,
30
30
  } from './roborockCommunication/index.js';
31
31
  import { ServiceArea } from 'matterbridge/matter/clusters';
32
+ import { LocalNetworkClient } from './roborockCommunication/broadcast/client/LocalNetworkClient.js';
32
33
  export type Factory<A, T> = (logger: AnsiLogger, arg: A) => T;
33
34
 
34
35
  export default class RoborockService {
@@ -227,7 +228,7 @@ export default class RoborockService {
227
228
  await this.messageProcessor.getDeviceStatus(device.duid).then((response: DeviceStatus) => {
228
229
  if (self.deviceNotify) {
229
230
  const message: DeviceStatusNotify = { duid: device.duid, ...response.errorStatus, ...response.message } as DeviceStatusNotify;
230
- self.logger.debug('Device status update xxx', debugStringify(message));
231
+ self.logger.debug('Device status update', debugStringify(message));
231
232
  self.deviceNotify(NotifyMessageTypes.LocalMessage, message);
232
233
  }
233
234
  });
@@ -419,6 +420,14 @@ export default class RoborockService {
419
420
  self.deviceNotify(NotifyMessageTypes.BatteryUpdate, { duid: device.duid, percentage } as BatteryMessage);
420
421
  }
421
422
  },
423
+ onStatusChanged: () => {
424
+ // status: DeviceStatus
425
+ // if (self.deviceNotify) {
426
+ // const message: DeviceStatusNotify = { duid: device.duid, ...status.errorStatus, ...status.message } as DeviceStatusNotify;
427
+ // self.logger.debug('Device status update', debugStringify(message));
428
+ // self.deviceNotify(NotifyMessageTypes.LocalMessage, message);
429
+ // }
430
+ },
422
431
  } as AbstractMessageHandler);
423
432
 
424
433
  this.logger.debug('Local device', device.duid);
@@ -431,19 +440,25 @@ export default class RoborockService {
431
440
 
432
441
  if (this.ip) {
433
442
  this.logger.debug('initializing the local connection for this client towards ' + this.ip);
434
- this.localClient = this.messageClient.registerClient(device.duid, this.ip);
443
+ this.localClient = this.messageClient.registerClient(device.duid, this.ip) as LocalNetworkClient;
435
444
  this.localClient.connect();
436
445
 
437
- while (!this.localClient.isConnected()) {
446
+ let count = 0;
447
+ while (!this.localClient.isConnected() && count < 20) {
448
+ this.logger.debug('Keep waiting for local client to connect');
449
+ count++;
438
450
  await this.sleep(500);
439
451
  }
452
+
453
+ if (!this.localClient.isConnected()) {
454
+ throw new Error('Local client did not connect after 10 attempts, something is wrong');
455
+ }
456
+
440
457
  this.logger.debug('LocalClient connected');
441
458
  }
442
459
  } catch (error) {
443
460
  this.logger.error('Error requesting network info', error);
444
461
  }
445
-
446
- this.logger.debug('Local client connected');
447
462
  }
448
463
 
449
464
  private sleep(ms: number): Promise<void> {
package/src/rvc.ts CHANGED
@@ -56,17 +56,20 @@ export class RoborockVacuumCleaner extends RoboticVacuumCleaner {
56
56
  }
57
57
 
58
58
  public configurateHandler(behaviorHandler: BehaviorFactoryResult): void {
59
- this.addCommandHandler('identify', async ({ request: { identifyTime } }) => {
60
- behaviorHandler.executeCommand('playSoundToLocate', identifyTime as number);
59
+ this.addCommandHandler('identify', async ({ request, cluster, attributes, endpoint }) => {
60
+ this.log.info(`Identify command received for endpoint ${endpoint}, cluster ${cluster}, attributes ${attributes}, request: ${JSON.stringify(request)}`);
61
+ behaviorHandler.executeCommand('playSoundToLocate', (request as { identifyTime?: number }).identifyTime ?? 0);
61
62
  });
62
63
 
63
- this.addCommandHandler('selectAreas', async ({ request }: { request: ServiceArea.SelectAreasRequest }) => {
64
- this.log.info(`Selecting areas: ${request.newAreas.join(', ')}`);
65
- behaviorHandler.executeCommand('selectAreas', request.newAreas);
64
+ this.addCommandHandler('selectAreas', async ({ request }) => {
65
+ const { newAreas } = request as ServiceArea.SelectAreasRequest;
66
+ this.log.info(`Selecting areas: ${newAreas?.join(', ')}`);
67
+ behaviorHandler.executeCommand('selectAreas', newAreas);
66
68
  });
67
69
 
68
- this.addCommandHandler('changeToMode', async ({ request }: { request: ModeBase.ChangeToModeRequest }) => {
69
- behaviorHandler.executeCommand('changeToMode', request.newMode);
70
+ this.addCommandHandler('changeToMode', async ({ request }) => {
71
+ const { newMode } = request as ModeBase.ChangeToModeRequest;
72
+ behaviorHandler.executeCommand('changeToMode', newMode);
70
73
  });
71
74
 
72
75
  this.addCommandHandler('pause', async () => {