matterbridge-roborock-vacuum-plugin 1.0.7-rc03 → 1.0.7

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.
@@ -3,11 +3,11 @@ import { setCommandHandlerA187 } from './behaviors/roborock.vacuum/QREVO_EDGE_5V
3
3
  import { setDefaultCommandHandler } from './behaviors/roborock.vacuum/default/default.js';
4
4
  import { DeviceModel } from './roborockCommunication/Zmodel/deviceModel.js';
5
5
  import { setCommandHandlerA27 } from './behaviors/roborock.vacuum/S7_MAXV/a27.js';
6
- export function configurateBehavior(model, duid, roborockService, logger) {
6
+ export function configurateBehavior(model, duid, roborockService, cleanModeSettings, logger) {
7
7
  switch (model) {
8
8
  case DeviceModel.QREVO_EDGE_5V1: {
9
9
  const deviceHandler = new BehaviorDeviceGeneric(logger);
10
- setCommandHandlerA187(duid, deviceHandler, logger, roborockService);
10
+ setCommandHandlerA187(duid, deviceHandler, logger, roborockService, cleanModeSettings);
11
11
  return deviceHandler;
12
12
  }
13
13
  case DeviceModel.S7_MAXV: {
@@ -27,15 +27,16 @@ export var MopWaterFlowA187;
27
27
  MopWaterFlowA187[MopWaterFlowA187["Medium"] = 202] = "Medium";
28
28
  MopWaterFlowA187[MopWaterFlowA187["High"] = 203] = "High";
29
29
  MopWaterFlowA187[MopWaterFlowA187["Custom"] = 204] = "Custom";
30
+ MopWaterFlowA187[MopWaterFlowA187["CustomizeWithDistanceOff"] = 207] = "CustomizeWithDistanceOff";
30
31
  MopWaterFlowA187[MopWaterFlowA187["Smart"] = 209] = "Smart";
31
32
  })(MopWaterFlowA187 || (MopWaterFlowA187 = {}));
32
33
  export var MopRouteA187;
33
34
  (function (MopRouteA187) {
34
- MopRouteA187[MopRouteA187["Fast"] = 304] = "Fast";
35
35
  MopRouteA187[MopRouteA187["Standard"] = 300] = "Standard";
36
36
  MopRouteA187[MopRouteA187["Deep"] = 301] = "Deep";
37
37
  MopRouteA187[MopRouteA187["Custom"] = 302] = "Custom";
38
38
  MopRouteA187[MopRouteA187["DeepPlus"] = 303] = "DeepPlus";
39
+ MopRouteA187[MopRouteA187["Fast"] = 304] = "Fast";
39
40
  MopRouteA187[MopRouteA187["Smart"] = 306] = "Smart";
40
41
  })(MopRouteA187 || (MopRouteA187 = {}));
41
42
  const RvcRunMode = {
@@ -51,13 +52,13 @@ const RvcCleanMode = {
51
52
  [8]: 'Custom',
52
53
  };
53
54
  const CleanSetting = {
54
- [4]: { suctionPower: 0, waterFlow: 0, mopRoute: MopRouteA187.Smart },
55
- [5]: { suctionPower: VacuumSuctionPowerA187.Off, waterFlow: MopWaterFlowA187.Medium, mopRoute: MopRouteA187.Custom },
56
- [6]: { suctionPower: VacuumSuctionPowerA187.Balanced, waterFlow: MopWaterFlowA187.Off, mopRoute: MopRouteA187.Custom },
57
- [7]: { suctionPower: VacuumSuctionPowerA187.Balanced, waterFlow: MopWaterFlowA187.Medium, mopRoute: MopRouteA187.Custom },
58
- [8]: { suctionPower: VacuumSuctionPowerA187.Custom, waterFlow: MopWaterFlowA187.Custom, mopRoute: MopRouteA187.Custom },
55
+ [4]: { suctionPower: 0, waterFlow: 0, distance_off: 0, mopRoute: MopRouteA187.Smart },
56
+ [5]: { suctionPower: VacuumSuctionPowerA187.Off, waterFlow: MopWaterFlowA187.Medium, distance_off: 0, mopRoute: MopRouteA187.Custom },
57
+ [6]: { suctionPower: VacuumSuctionPowerA187.Balanced, waterFlow: MopWaterFlowA187.Off, distance_off: 0, mopRoute: MopRouteA187.Custom },
58
+ [7]: { suctionPower: VacuumSuctionPowerA187.Balanced, waterFlow: MopWaterFlowA187.Medium, distance_off: 0, mopRoute: MopRouteA187.Custom },
59
+ [8]: { suctionPower: VacuumSuctionPowerA187.Custom, waterFlow: MopWaterFlowA187.Custom, distance_off: 0, mopRoute: MopRouteA187.Custom },
59
60
  };
60
- export function setCommandHandlerA187(duid, handler, logger, roborockService) {
61
+ export function setCommandHandlerA187(duid, handler, logger, roborockService, cleanModeSettings) {
61
62
  handler.setCommandHandler('changeToMode', async (newMode) => {
62
63
  const activity = RvcRunMode[newMode] || RvcCleanMode[newMode];
63
64
  switch (activity) {
@@ -67,20 +68,64 @@ export function setCommandHandlerA187(duid, handler, logger, roborockService) {
67
68
  return;
68
69
  }
69
70
  case 'Smart Plan':
70
- case 'Mop':
71
- case 'Vacuum':
72
- case 'Vac & Mop':
73
71
  case 'Custom': {
74
72
  const setting = CleanSetting[newMode];
75
- logger.notice(`BehaviorA187-ChangeCleanMode to: ${activity}, code: ${debugStringify(setting)}`);
73
+ logger.notice(`BehaviorA187-ChangeCleanMode to: ${activity}, setting: ${debugStringify(setting)}`);
76
74
  await roborockService.changeCleanMode(duid, setting);
77
75
  return;
78
76
  }
77
+ case 'Mop':
78
+ case 'Vacuum':
79
+ case 'Vac & Mop': {
80
+ const setting = cleanModeSettings ? getSettingFromCleanMode(activity, cleanModeSettings) : CleanSetting[newMode];
81
+ logger.notice(`BehaviorA187-ChangeCleanMode to: ${activity}, setting: ${debugStringify(setting ?? {})}`);
82
+ if (setting) {
83
+ await roborockService.changeCleanMode(duid, setting);
84
+ }
85
+ return;
86
+ }
79
87
  default:
80
88
  logger.notice('BehaviorA187-changeToMode-Unknown: ', newMode);
81
89
  return;
82
90
  }
83
91
  });
92
+ const getSettingFromCleanMode = (activity, cleanModeSettings) => {
93
+ switch (activity) {
94
+ case 'Mop': {
95
+ const mopSetting = cleanModeSettings?.mopping;
96
+ const waterFlow = MopWaterFlowA187[mopSetting?.waterFlowMode] ?? MopWaterFlowA187.Medium;
97
+ const distance_off = waterFlow == MopWaterFlowA187.CustomizeWithDistanceOff ? 210 - 5 * (mopSetting?.distanceOff ?? 25) : 0;
98
+ return {
99
+ suctionPower: VacuumSuctionPowerA187.Off,
100
+ waterFlow,
101
+ distance_off,
102
+ mopRoute: MopRouteA187[mopSetting?.mopRouteMode] ?? MopRouteA187.Standard,
103
+ };
104
+ }
105
+ case 'Vacuum': {
106
+ const vacuumSetting = cleanModeSettings?.vacuuming;
107
+ return {
108
+ suctionPower: VacuumSuctionPowerA187[vacuumSetting?.fanMode] ?? VacuumSuctionPowerA187.Balanced,
109
+ waterFlow: MopWaterFlowA187.Off,
110
+ distance_off: 0,
111
+ mopRoute: MopRouteA187[vacuumSetting?.mopRouteMode] ?? MopRouteA187.Standard,
112
+ };
113
+ }
114
+ case 'Vac & Mop': {
115
+ const vacmopSetting = cleanModeSettings?.vacmop;
116
+ const waterFlow = MopWaterFlowA187[vacmopSetting?.waterFlowMode] ?? MopWaterFlowA187.Medium;
117
+ const distance_off = waterFlow == MopWaterFlowA187.CustomizeWithDistanceOff ? 210 - 5 * (vacmopSetting?.distanceOff ?? 25) : 0;
118
+ return {
119
+ suctionPower: VacuumSuctionPowerA187[vacmopSetting?.fanMode] ?? VacuumSuctionPowerA187.Balanced,
120
+ waterFlow,
121
+ distance_off,
122
+ mopRoute: MopRouteA187[vacmopSetting?.mopRouteMode] ?? MopRouteA187.Standard,
123
+ };
124
+ }
125
+ default:
126
+ return undefined;
127
+ }
128
+ };
84
129
  handler.setCommandHandler('selectAreas', async (newAreas) => {
85
130
  logger.notice(`BehaviorA187-selectAreas: ${newAreas}`);
86
131
  roborockService.setSelectedAreas(duid, newAreas ?? []);
@@ -0,0 +1,5 @@
1
+ export class CleanModeSettings {
2
+ vacuuming;
3
+ mopping;
4
+ vacmop;
5
+ }
package/dist/platform.js CHANGED
@@ -91,6 +91,11 @@ export class RoborockMatterbridgePlatform extends MatterbridgeDynamicPlatform {
91
91
  }
92
92
  async onConfigure() {
93
93
  await super.onConfigure();
94
+ if (this.config.enableExperimentalFeature) {
95
+ const cleanModeSettings = this.config.cleanModeSettings;
96
+ this.log.notice(`Experimental Feature has been enable`);
97
+ this.log.notice(`cleanModeSettings ${debugStringify(cleanModeSettings)}`);
98
+ }
94
99
  const self = this;
95
100
  this.rvcInterval = setInterval(async () => {
96
101
  self.platformRunner?.requestHomeData();
@@ -108,11 +113,15 @@ export class RoborockMatterbridgePlatform extends MatterbridgeDynamicPlatform {
108
113
  this.log.error('Initializing: No supported devices found');
109
114
  return;
110
115
  }
116
+ let cleanModeSettings = undefined;
117
+ if (this.config.enableExperimentalFeature) {
118
+ cleanModeSettings = this.config.cleanModeSettings;
119
+ }
111
120
  const self = this;
112
121
  await this.roborockService.initializeMessageClientForLocal(vacuum);
113
122
  const roomMap = await this.platformRunner.getRoomMapFromDevice(vacuum);
114
123
  this.log.debug('Initializing - roomMap: ', debugStringify(roomMap));
115
- const behaviorHandler = configurateBehavior(vacuum.data.model, vacuum.duid, this.roborockService, this.log);
124
+ const behaviorHandler = configurateBehavior(vacuum.data.model, vacuum.duid, this.roborockService, cleanModeSettings, this.log);
116
125
  this.roborockService.setSupportedAreas(vacuum.duid, getSupportedAreas(vacuum.rooms, roomMap, this.log));
117
126
  this.robot = new RoborockVacuumCleaner(username, vacuum, roomMap, this.log);
118
127
  this.robot.configurateHandler(behaviorHandler);
@@ -63,24 +63,29 @@ export class MessageProcessor {
63
63
  const request = new RequestMessage(def);
64
64
  return this.client.send(duid, request);
65
65
  }
66
- async getCustomMessage(duid, def) {
67
- const response = this.client.get(duid, def);
68
- this.logger?.warn('XXXXXXX: ', debugStringify(response));
69
- return response;
66
+ getCustomMessage(duid, def) {
67
+ return this.client.get(duid, def);
70
68
  }
71
69
  async findMyRobot(duid) {
72
70
  const request = new RequestMessage({ method: 'find_me' });
73
71
  return this.client.send(duid, request);
74
72
  }
75
- async changeCleanMode(duid, suctionPower, waterFlow, mopRoute) {
76
- this.logger?.notice(`Change clean mode for ${duid} to suctionPower: ${suctionPower}, waterFlow: ${waterFlow}`);
73
+ async changeCleanMode(duid, suctionPower, waterFlow, mopRoute, distance_off) {
74
+ this.logger?.notice(`Change clean mode for ${duid} to suctionPower: ${suctionPower}, waterFlow: ${waterFlow}, mopRoute: ${mopRoute}, distance_off: ${distance_off}`);
75
+ const currentMopMode = await this.getCustomMessage(duid, new RequestMessage({ method: 'get_custom_mode' }));
76
+ if (currentMopMode == 110) {
77
+ await this.client.send(duid, new RequestMessage({ method: 'set_mop_mode', params: [302] }));
78
+ }
77
79
  if (mopRoute && mopRoute != 0) {
78
80
  await this.client.send(duid, new RequestMessage({ method: 'set_mop_mode', params: [mopRoute] }));
79
81
  }
80
82
  if (suctionPower && suctionPower != 0) {
81
83
  await this.client.send(duid, new RequestMessage({ method: 'set_custom_mode', params: [suctionPower] }));
82
84
  }
83
- if (waterFlow && waterFlow != 0) {
85
+ if (waterFlow && waterFlow == 207 && distance_off && distance_off != 0) {
86
+ await this.client.send(duid, new RequestMessage({ method: 'set_water_box_custom_mode', params: { 'water_box_mode': waterFlow, 'distance_off': distance_off } }));
87
+ }
88
+ else if (waterFlow && waterFlow != 0) {
84
89
  await this.client.send(duid, new RequestMessage({ method: 'set_water_box_custom_mode', params: [waterFlow] }));
85
90
  }
86
91
  }
@@ -48,9 +48,9 @@ export default class RoborockService {
48
48
  getSupportedAreas(duid) {
49
49
  return this.supportedAreas.get(duid);
50
50
  }
51
- async changeCleanMode(duid, { suctionPower, waterFlow, mopRoute }) {
51
+ async changeCleanMode(duid, { suctionPower, waterFlow, distance_off, mopRoute }) {
52
52
  this.logger.notice('changeCleanMode');
53
- return this.messageProcessor?.changeCleanMode(duid, suctionPower, waterFlow, mopRoute);
53
+ return this.messageProcessor?.changeCleanMode(duid, suctionPower, waterFlow, mopRoute, distance_off);
54
54
  }
55
55
  async startClean(duid) {
56
56
  const areas = this.supportedAreas.get(duid);
@@ -89,6 +89,9 @@ export default class RoborockService {
89
89
  this.logger.debug('customGetInSecure-message', method);
90
90
  return this.getMessageProcessor()?.getCustomMessage(duid, new RequestMessage({ method, secure: true }));
91
91
  }
92
+ async customSend(duid, request) {
93
+ return this.getMessageProcessor()?.sendCustomMessage(duid, request);
94
+ }
92
95
  stopService() {
93
96
  if (this.messageClient) {
94
97
  this.messageClient.disconnect();
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "matterbridge-roborock-vacuum-plugin",
3
3
  "type": "DynamicPlatform",
4
- "version": "1.0.7-rc03",
4
+ "version": "1.0.7",
5
5
  "whiteList": [],
6
6
  "blackList": [],
7
7
  "useInterval": true,
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "title": "Matterbridge Roborock Vacuum Plugin",
3
- "description": "matterbridge-roborock-vacuum-plugin v. 1.0.7-rc03 by https://github.com/RinDevJunior",
3
+ "description": "matterbridge-roborock-vacuum-plugin v. 1.0.7 by https://github.com/RinDevJunior",
4
4
  "type": "object",
5
- "required": [],
5
+ "required": ["username", "password"],
6
6
  "properties": {
7
7
  "name": {
8
8
  "description": "Plugin name",
@@ -17,7 +17,7 @@
17
17
  "ui:widget": "hidden"
18
18
  },
19
19
  "whiteList": {
20
- "description": "(ONLY SET IF YOU HAVE MULTIPLE VACUUMS) Only THE FIRST ONE device with DUID in the list will be exposed. If the list is empty, THE FIRST device will be exposed.",
20
+ "description": "(ONLY SET IF YOU HAVE MULTIPLE VACUUMS) Only THE FIRST ONE device with <Name>-<duid> in the list will be exposed. If the list is empty, THE FIRST device will be exposed.",
21
21
  "type": "array",
22
22
  "items": {
23
23
  "type": "string"
@@ -31,7 +31,8 @@
31
31
  },
32
32
  "password": {
33
33
  "description": "Roborock password (required for the plugin to work)",
34
- "type": "string"
34
+ "type": "string",
35
+ "ui:widget": "password"
35
36
  },
36
37
  "refreshInterval": {
37
38
  "description": "Refresh interval in seconds (default: 60)",
@@ -43,6 +44,103 @@
43
44
  "type": "boolean",
44
45
  "default": false
45
46
  },
47
+ "cleanModeSettings": {
48
+ "description": "Configure default cleaning parameters for vacuum.",
49
+ "type": "object",
50
+ "properties": {
51
+ "vacuuming": {
52
+ "description": "Vacuuming only",
53
+ "type": "object",
54
+ "properties": {
55
+ "fanMode": {
56
+ "type": "string",
57
+ "description": "Suction power mode to use in Vacuuming only mode (e.g., 'Quiet', 'Balanced', 'Turbo', 'Max', 'MaxPlus').",
58
+ "default": "Balanced"
59
+ },
60
+ "mopRouteMode": {
61
+ "type": "string",
62
+ "description": "Mop route intensity to use in Vacuuming only mode (e.g., 'Standard', 'Deep', 'DeepPlus', 'Fast').",
63
+ "default": "Standard"
64
+ }
65
+ }
66
+ },
67
+ "mopping": {
68
+ "description": "Mopping only",
69
+ "type": "object",
70
+ "properties": {
71
+ "waterFlowMode": {
72
+ "type": "string",
73
+ "description": "Water flow mode to use in Mopping only mode (e.g., 'Low', 'Medium', 'High', 'CustomizeWithDistanceOff').",
74
+ "default": "Medium"
75
+ },
76
+ "mopRouteMode": {
77
+ "type": "string",
78
+ "description": "Mop route intensity to use in Mopping only mode (e.g., 'Standard', 'Deep', 'DeepPlus', 'Fast').",
79
+ "default": "Standard"
80
+ }
81
+ },
82
+ "allOf": [
83
+ {
84
+ "if": {
85
+ "properties": {
86
+ "waterFlowMode": { "const": "CustomizeWithDistanceOff" }
87
+ }
88
+ },
89
+ "then": {
90
+ "properties": {
91
+ "distanceOff": {
92
+ "type": "number",
93
+ "description": "Distance Off - Default = 25",
94
+ "default": 25
95
+ }
96
+ },
97
+ "required": ["distanceOff"]
98
+ }
99
+ }
100
+ ]
101
+ },
102
+ "vacmop": {
103
+ "description": "Vacuuming and Mopping",
104
+ "type": "object",
105
+ "properties": {
106
+ "fanMode": {
107
+ "type": "string",
108
+ "description": "Suction power mode to use in Vacuuming and Mopping mode (e.g., 'Quiet', 'Balanced', 'Turbo', 'Max', 'MaxPlus').",
109
+ "default": "Balanced"
110
+ },
111
+ "waterFlowMode": {
112
+ "type": "string",
113
+ "description": "Water flow mode to use in Vacuuming and Mopping mode (e.g., 'Low', 'Medium', 'High', 'CustomizeWithDistanceOff').",
114
+ "default": "Medium"
115
+ },
116
+ "mopRouteMode": {
117
+ "type": "string",
118
+ "description": "Mop route intensity to use in Vacuuming and Mopping mode (e.g., 'Standard', 'Deep', 'DeepPlus', 'Fast').",
119
+ "default": "Standard"
120
+ }
121
+ },
122
+ "allOf": [
123
+ {
124
+ "if": {
125
+ "properties": {
126
+ "waterFlowMode": { "const": "CustomizeWithDistanceOff" }
127
+ }
128
+ },
129
+ "then": {
130
+ "properties": {
131
+ "distanceOff": {
132
+ "type": "number",
133
+ "description": "Distance Off - Default = 25",
134
+ "default": 25
135
+ }
136
+ },
137
+ "required": ["distanceOff"]
138
+ }
139
+ }
140
+ ]
141
+ }
142
+ }
143
+ },
46
144
  "debug": {
47
145
  "description": "Enable the debug for the plugin (development only)",
48
146
  "type": "boolean",
@@ -53,5 +151,20 @@
53
151
  "type": "boolean",
54
152
  "default": false
55
153
  }
56
- }
154
+ },
155
+ "dependencies": {
156
+ "cleanModeSettings": ["enableExperimentalFeature"]
157
+ },
158
+ "allOf": [
159
+ {
160
+ "if": {
161
+ "properties": { "enableExperimentalFeature": { "const": false } }
162
+ },
163
+ "then": {
164
+ "properties": {
165
+ "cleanModeSettings": false
166
+ }
167
+ }
168
+ }
169
+ ]
57
170
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "matterbridge-roborock-vacuum-plugin",
3
- "version": "1.0.7-rc03",
3
+ "version": "1.0.7",
4
4
  "description": "Matterbridge Roborock Vacuum Plugin",
5
5
  "author": "https://github.com/RinDevJunior",
6
6
  "license": "MIT",
@@ -5,14 +5,21 @@ import RoborockService from './roborockService.js';
5
5
  import { DefaultEndpointCommands, setDefaultCommandHandler } from './behaviors/roborock.vacuum/default/default.js';
6
6
  import { DeviceModel } from './roborockCommunication/Zmodel/deviceModel.js';
7
7
  import { EndpointCommandsA27, setCommandHandlerA27 } from './behaviors/roborock.vacuum/S7_MAXV/a27.js';
8
+ import { CleanModeSettings } from './model/CleanModeSettings.js';
8
9
 
9
10
  export type BehaviorFactoryResult = BehaviorDeviceGeneric<DefaultEndpointCommands> | BehaviorDeviceGeneric<EndpointCommandsA187>;
10
11
 
11
- export function configurateBehavior(model: string, duid: string, roborockService: RoborockService, logger: AnsiLogger): BehaviorFactoryResult {
12
+ export function configurateBehavior(
13
+ model: string,
14
+ duid: string,
15
+ roborockService: RoborockService,
16
+ cleanModeSettings: CleanModeSettings | undefined,
17
+ logger: AnsiLogger,
18
+ ): BehaviorFactoryResult {
12
19
  switch (model) {
13
20
  case DeviceModel.QREVO_EDGE_5V1: {
14
21
  const deviceHandler = new BehaviorDeviceGeneric<EndpointCommandsA187>(logger);
15
- setCommandHandlerA187(duid, deviceHandler, logger, roborockService);
22
+ setCommandHandlerA187(duid, deviceHandler, logger, roborockService, cleanModeSettings);
16
23
  return deviceHandler;
17
24
  }
18
25
 
@@ -0,0 +1,201 @@
1
+ import { setCommandHandlerA187, VacuumSuctionPowerA187, MopWaterFlowA187, MopRouteA187 } from './a187';
2
+ import { BehaviorDeviceGeneric } from '../../BehaviorDeviceGeneric';
3
+ import { AnsiLogger } from 'matterbridge/logger';
4
+ import RoborockService from '../../../roborockService';
5
+ import { CleanModeSettings } from '../../../model/CleanModeSettings';
6
+ import { jest } from '@jest/globals';
7
+ import { distance } from 'jimp';
8
+
9
+ describe('setCommandHandlerA187', () => {
10
+ let handler: BehaviorDeviceGeneric<any>;
11
+ let logger: AnsiLogger;
12
+ let roborockService: jest.Mocked<RoborockService>;
13
+ let cleanModeSettings: CleanModeSettings;
14
+ const duid = 'test-duid';
15
+
16
+ beforeEach(() => {
17
+ handler = {
18
+ setCommandHandler: jest.fn(),
19
+ } as any;
20
+
21
+ logger = {
22
+ notice: jest.fn(),
23
+ warn: jest.fn(),
24
+ } as any;
25
+
26
+ roborockService = {
27
+ startClean: jest.fn(),
28
+ changeCleanMode: jest.fn(),
29
+ setSelectedAreas: jest.fn(),
30
+ pauseClean: jest.fn(),
31
+ resumeClean: jest.fn(),
32
+ stopAndGoHome: jest.fn(),
33
+ playSoundToLocate: jest.fn(),
34
+ } as any;
35
+
36
+ cleanModeSettings = {
37
+ vacuuming: { fanMode: 'Max', mopRouteMode: 'DeepPlus' },
38
+ mopping: { waterFlowMode: 'High', mopRouteMode: 'Fast', distanceOff: 25 },
39
+ vacmop: {
40
+ fanMode: 'Turbo',
41
+ waterFlowMode: 'Low',
42
+ mopRouteMode: 'Deep',
43
+ distanceOff: 25,
44
+ },
45
+ };
46
+ });
47
+
48
+ it('should set all command handlers', () => {
49
+ setCommandHandlerA187(duid, handler, logger, roborockService, cleanModeSettings);
50
+ expect(handler.setCommandHandler).toHaveBeenCalledWith('changeToMode', expect.any(Function));
51
+ expect(handler.setCommandHandler).toHaveBeenCalledWith('selectAreas', expect.any(Function));
52
+ expect(handler.setCommandHandler).toHaveBeenCalledWith('pause', expect.any(Function));
53
+ expect(handler.setCommandHandler).toHaveBeenCalledWith('resume', expect.any(Function));
54
+ expect(handler.setCommandHandler).toHaveBeenCalledWith('goHome', expect.any(Function));
55
+ expect(handler.setCommandHandler).toHaveBeenCalledWith('PlaySoundToLocate', expect.any(Function));
56
+ });
57
+
58
+ it('should call startClean for Cleaning mode', async () => {
59
+ setCommandHandlerA187(duid, handler, logger, roborockService, cleanModeSettings);
60
+ const [[, changeToModeHandler]] = (handler.setCommandHandler as jest.Mock).mock.calls.filter(([cmd]) => cmd === 'changeToMode');
61
+ await (changeToModeHandler as (mode: number) => Promise<void>)(2); // 2 = Cleaning
62
+ expect(roborockService.startClean).toHaveBeenCalledWith(duid);
63
+ });
64
+
65
+ it('should call changeCleanMode for Smart Plan', async () => {
66
+ setCommandHandlerA187(duid, handler, logger, roborockService, cleanModeSettings);
67
+ const [[, changeToModeHandler]] = (handler.setCommandHandler as jest.Mock).mock.calls.filter(([cmd]) => cmd === 'changeToMode');
68
+ await (changeToModeHandler as (mode: number) => Promise<void>)(4); // 4 = Smart Plan
69
+ expect(roborockService.changeCleanMode).toHaveBeenCalledWith(duid, {
70
+ suctionPower: 0,
71
+ waterFlow: 0,
72
+ distance_off: 0,
73
+ mopRoute: MopRouteA187.Smart,
74
+ });
75
+ });
76
+
77
+ it('should call changeCleanMode for Mop with correct values', async () => {
78
+ setCommandHandlerA187(duid, handler, logger, roborockService, cleanModeSettings);
79
+ const [[, changeToModeHandler]] = (handler.setCommandHandler as jest.Mock).mock.calls.filter(([cmd]) => cmd === 'changeToMode');
80
+ await (changeToModeHandler as (mode: number) => Promise<void>)(5); // 5 = Mop
81
+ //mopping: { waterFlowMode: 'High', mopRouteMode: 'Fast', distanceOff: 85 },
82
+ expect(roborockService.changeCleanMode).toHaveBeenCalledWith(duid, {
83
+ suctionPower: VacuumSuctionPowerA187.Off,
84
+ waterFlow: MopWaterFlowA187.High,
85
+ mopRoute: MopRouteA187.Fast,
86
+ distance_off: 0,
87
+ });
88
+ });
89
+
90
+ it('should call changeCleanMode for Mop with correct values', async () => {
91
+ setCommandHandlerA187(duid, handler, logger, roborockService, {
92
+ vacuuming: { fanMode: 'Max', mopRouteMode: 'DeepPlus' },
93
+ mopping: { waterFlowMode: 'CustomizeWithDistanceOff', mopRouteMode: 'Fast', distanceOff: 25 },
94
+ vacmop: {
95
+ fanMode: 'Turbo',
96
+ waterFlowMode: 'Low',
97
+ mopRouteMode: 'Deep',
98
+ distanceOff: 25,
99
+ },
100
+ });
101
+ const [[, changeToModeHandler]] = (handler.setCommandHandler as jest.Mock).mock.calls.filter(([cmd]) => cmd === 'changeToMode');
102
+ await (changeToModeHandler as (mode: number) => Promise<void>)(5); // 5 = Mop
103
+ //vacuuming: { fanMode: 'Max', mopRouteMode: 'DeepPlus' },
104
+ expect(roborockService.changeCleanMode).toHaveBeenCalledWith(duid, {
105
+ suctionPower: VacuumSuctionPowerA187.Off,
106
+ waterFlow: MopWaterFlowA187.CustomizeWithDistanceOff,
107
+ mopRoute: MopRouteA187.Fast,
108
+ distance_off: 85,
109
+ });
110
+ });
111
+
112
+ it('should call changeCleanMode for Vacuum with correct values', async () => {
113
+ setCommandHandlerA187(duid, handler, logger, roborockService, cleanModeSettings);
114
+ const [[, changeToModeHandler]] = (handler.setCommandHandler as jest.Mock).mock.calls.filter(([cmd]) => cmd === 'changeToMode');
115
+ await (changeToModeHandler as (mode: number) => Promise<void>)(6); // 6 = Vacuum
116
+ //vacuuming: { fanMode: 'Max', mopRouteMode: 'DeepPlus' },
117
+ expect(roborockService.changeCleanMode).toHaveBeenCalledWith(duid, {
118
+ suctionPower: VacuumSuctionPowerA187.Max,
119
+ waterFlow: MopWaterFlowA187.Off,
120
+ mopRoute: MopRouteA187.DeepPlus,
121
+ distance_off: 0,
122
+ });
123
+ });
124
+
125
+ it('should call changeCleanMode for Vac & Mop with correct values', async () => {
126
+ setCommandHandlerA187(duid, handler, logger, roborockService, cleanModeSettings);
127
+ const [[, changeToModeHandler]] = (handler.setCommandHandler as jest.Mock).mock.calls.filter(([cmd]) => cmd === 'changeToMode');
128
+ await (changeToModeHandler as (mode: number) => Promise<void>)(7); // 7 = Vac & Mop
129
+ /*
130
+ vacmop: {
131
+ fanMode: 'Turbo',
132
+ waterFlowMode: 'Low',
133
+ mopRouteMode: 'Deep',
134
+ distanceOff: 85,
135
+ },
136
+ */
137
+
138
+ expect(roborockService.changeCleanMode).toHaveBeenCalledWith(duid, {
139
+ suctionPower: VacuumSuctionPowerA187.Turbo,
140
+ waterFlow: MopWaterFlowA187.Low,
141
+ mopRoute: MopRouteA187.Deep,
142
+ distance_off: 0,
143
+ });
144
+ });
145
+
146
+ it('should call changeCleanMode for Vac & Mop with correct values', async () => {
147
+ setCommandHandlerA187(duid, handler, logger, roborockService, {
148
+ vacuuming: { fanMode: 'Max', mopRouteMode: 'DeepPlus' },
149
+ mopping: { waterFlowMode: 'CustomizeWithDistanceOff', mopRouteMode: 'Fast', distanceOff: 25 },
150
+ vacmop: {
151
+ fanMode: 'Turbo',
152
+ waterFlowMode: 'CustomizeWithDistanceOff',
153
+ mopRouteMode: 'Deep',
154
+ distanceOff: 25,
155
+ },
156
+ });
157
+ const [[, changeToModeHandler]] = (handler.setCommandHandler as jest.Mock).mock.calls.filter(([cmd]) => cmd === 'changeToMode');
158
+ await (changeToModeHandler as (mode: number) => Promise<void>)(7); // 7 = Vac & Mop
159
+ expect(roborockService.changeCleanMode).toHaveBeenCalledWith(duid, {
160
+ suctionPower: VacuumSuctionPowerA187.Turbo,
161
+ waterFlow: MopWaterFlowA187.CustomizeWithDistanceOff,
162
+ mopRoute: MopRouteA187.Deep,
163
+ distance_off: 85,
164
+ });
165
+ });
166
+
167
+ it('should call setSelectedAreas', async () => {
168
+ setCommandHandlerA187(duid, handler, logger, roborockService, cleanModeSettings);
169
+ const [[, selectAreasHandler]] = (handler.setCommandHandler as jest.Mock).mock.calls.filter(([cmd]) => cmd === 'selectAreas');
170
+ await (selectAreasHandler as (areas: number[]) => Promise<void>)([1, 2, 3]);
171
+ expect(roborockService.setSelectedAreas).toHaveBeenCalledWith(duid, [1, 2, 3]);
172
+ });
173
+
174
+ it('should call pauseClean', async () => {
175
+ setCommandHandlerA187(duid, handler, logger, roborockService, cleanModeSettings);
176
+ const [[, pauseHandler]] = (handler.setCommandHandler as jest.Mock).mock.calls.filter(([cmd]) => cmd === 'pause');
177
+ await (pauseHandler as () => Promise<void>)();
178
+ expect(roborockService.pauseClean).toHaveBeenCalledWith(duid);
179
+ });
180
+
181
+ it('should call resumeClean', async () => {
182
+ setCommandHandlerA187(duid, handler, logger, roborockService, cleanModeSettings);
183
+ const [[, resumeHandler]] = (handler.setCommandHandler as jest.Mock).mock.calls.filter(([cmd]) => cmd === 'resume');
184
+ await (resumeHandler as () => Promise<void>)();
185
+ expect(roborockService.resumeClean).toHaveBeenCalledWith(duid);
186
+ });
187
+
188
+ it('should call stopAndGoHome', async () => {
189
+ setCommandHandlerA187(duid, handler, logger, roborockService, cleanModeSettings);
190
+ const [[, goHomeHandler]] = (handler.setCommandHandler as jest.Mock).mock.calls.filter(([cmd]) => cmd === 'goHome');
191
+ await (goHomeHandler as () => Promise<void>)();
192
+ expect(roborockService.stopAndGoHome).toHaveBeenCalledWith(duid);
193
+ });
194
+
195
+ it('should call playSoundToLocate', async () => {
196
+ setCommandHandlerA187(duid, handler, logger, roborockService, cleanModeSettings);
197
+ const [[, playSoundHandler]] = (handler.setCommandHandler as jest.Mock).mock.calls.filter(([cmd]) => cmd === 'PlaySoundToLocate');
198
+ await (playSoundHandler as (arg: number) => Promise<void>)(1);
199
+ expect(roborockService.playSoundToLocate).toHaveBeenCalledWith(duid);
200
+ });
201
+ });
@@ -2,6 +2,7 @@ import { MaybePromise } from 'matterbridge/matter';
2
2
  import { AnsiLogger, debugStringify } from 'matterbridge/logger';
3
3
  import { BehaviorDeviceGeneric, BehaviorRoborock, DeviceCommands } from '../../BehaviorDeviceGeneric.js';
4
4
  import RoborockService from '../../../roborockService.js';
5
+ import { CleanModeSettings } from '../../../model/CleanModeSettings.js';
5
6
 
6
7
  export interface EndpointCommandsA187 extends DeviceCommands {
7
8
  selectAreas: (newAreas: any) => MaybePromise;
@@ -41,16 +42,17 @@ export enum MopWaterFlowA187 {
41
42
  Medium = 202,
42
43
  High = 203,
43
44
  Custom = 204,
45
+ CustomizeWithDistanceOff = 207,
44
46
  Smart = 209,
45
47
  }
46
48
 
47
49
  //set_mop_mode
48
50
  export enum MopRouteA187 {
49
- Fast = 304,
50
51
  Standard = 300,
51
52
  Deep = 301,
52
53
  Custom = 302,
53
54
  DeepPlus = 303,
55
+ Fast = 304,
54
56
  Smart = 306,
55
57
  }
56
58
 
@@ -67,15 +69,21 @@ const RvcCleanMode: Record<number, string> = {
67
69
  [8]: 'Custom',
68
70
  };
69
71
 
70
- const CleanSetting: Record<number, { suctionPower: number; waterFlow: number; mopRoute: number }> = {
71
- [4]: { suctionPower: 0, waterFlow: 0, mopRoute: MopRouteA187.Smart }, //'Smart Plan'
72
- [5]: { suctionPower: VacuumSuctionPowerA187.Off, waterFlow: MopWaterFlowA187.Medium, mopRoute: MopRouteA187.Custom }, //'Mop'
73
- [6]: { suctionPower: VacuumSuctionPowerA187.Balanced, waterFlow: MopWaterFlowA187.Off, mopRoute: MopRouteA187.Custom }, //'Vacuum'
74
- [7]: { suctionPower: VacuumSuctionPowerA187.Balanced, waterFlow: MopWaterFlowA187.Medium, mopRoute: MopRouteA187.Custom }, //'Vac & Mop'
75
- [8]: { suctionPower: VacuumSuctionPowerA187.Custom, waterFlow: MopWaterFlowA187.Custom, mopRoute: MopRouteA187.Custom }, // 'Custom'
72
+ const CleanSetting: Record<number, { suctionPower: number; waterFlow: number; distance_off: number; mopRoute: number }> = {
73
+ [4]: { suctionPower: 0, waterFlow: 0, distance_off: 0, mopRoute: MopRouteA187.Smart }, //'Smart Plan'
74
+ [5]: { suctionPower: VacuumSuctionPowerA187.Off, waterFlow: MopWaterFlowA187.Medium, distance_off: 0, mopRoute: MopRouteA187.Custom }, //'Mop'
75
+ [6]: { suctionPower: VacuumSuctionPowerA187.Balanced, waterFlow: MopWaterFlowA187.Off, distance_off: 0, mopRoute: MopRouteA187.Custom }, //'Vacuum'
76
+ [7]: { suctionPower: VacuumSuctionPowerA187.Balanced, waterFlow: MopWaterFlowA187.Medium, distance_off: 0, mopRoute: MopRouteA187.Custom }, //'Vac & Mop'
77
+ [8]: { suctionPower: VacuumSuctionPowerA187.Custom, waterFlow: MopWaterFlowA187.Custom, distance_off: 0, mopRoute: MopRouteA187.Custom }, // 'Custom'
76
78
  };
77
79
 
78
- export function setCommandHandlerA187(duid: string, handler: BehaviorDeviceGeneric<DeviceCommands>, logger: AnsiLogger, roborockService: RoborockService): void {
80
+ export function setCommandHandlerA187(
81
+ duid: string,
82
+ handler: BehaviorDeviceGeneric<DeviceCommands>,
83
+ logger: AnsiLogger,
84
+ roborockService: RoborockService,
85
+ cleanModeSettings: CleanModeSettings | undefined,
86
+ ): void {
79
87
  handler.setCommandHandler('changeToMode', async (newMode: number) => {
80
88
  const activity = RvcRunMode[newMode] || RvcCleanMode[newMode];
81
89
  switch (activity) {
@@ -85,21 +93,69 @@ export function setCommandHandlerA187(duid: string, handler: BehaviorDeviceGener
85
93
  return;
86
94
  }
87
95
  case 'Smart Plan':
88
- case 'Mop':
89
- case 'Vacuum':
90
- case 'Vac & Mop':
91
96
  case 'Custom': {
92
97
  const setting = CleanSetting[newMode];
93
- logger.notice(`BehaviorA187-ChangeCleanMode to: ${activity}, code: ${debugStringify(setting)}`);
98
+ logger.notice(`BehaviorA187-ChangeCleanMode to: ${activity}, setting: ${debugStringify(setting)}`);
94
99
  await roborockService.changeCleanMode(duid, setting);
95
100
  return;
96
101
  }
102
+ case 'Mop':
103
+ case 'Vacuum':
104
+ case 'Vac & Mop': {
105
+ const setting = cleanModeSettings ? getSettingFromCleanMode(activity, cleanModeSettings) : CleanSetting[newMode];
106
+ logger.notice(`BehaviorA187-ChangeCleanMode to: ${activity}, setting: ${debugStringify(setting ?? {})}`);
107
+ if (setting) {
108
+ await roborockService.changeCleanMode(duid, setting);
109
+ }
110
+ return;
111
+ }
97
112
  default:
98
113
  logger.notice('BehaviorA187-changeToMode-Unknown: ', newMode);
99
114
  return;
100
115
  }
101
116
  });
102
117
 
118
+ const getSettingFromCleanMode = (
119
+ activity: string,
120
+ cleanModeSettings?: CleanModeSettings,
121
+ ): { suctionPower: number; waterFlow: number; distance_off: number; mopRoute: number } | undefined => {
122
+ switch (activity) {
123
+ case 'Mop': {
124
+ const mopSetting = cleanModeSettings?.mopping;
125
+ const waterFlow = MopWaterFlowA187[mopSetting?.waterFlowMode as keyof typeof MopWaterFlowA187] ?? MopWaterFlowA187.Medium;
126
+ const distance_off = waterFlow == MopWaterFlowA187.CustomizeWithDistanceOff ? 210 - 5 * (mopSetting?.distanceOff ?? 25) : 0;
127
+ return {
128
+ suctionPower: VacuumSuctionPowerA187.Off,
129
+ waterFlow,
130
+ distance_off,
131
+ mopRoute: MopRouteA187[mopSetting?.mopRouteMode as keyof typeof MopRouteA187] ?? MopRouteA187.Standard,
132
+ };
133
+ }
134
+ case 'Vacuum': {
135
+ const vacuumSetting = cleanModeSettings?.vacuuming;
136
+ return {
137
+ suctionPower: VacuumSuctionPowerA187[vacuumSetting?.fanMode as keyof typeof VacuumSuctionPowerA187] ?? VacuumSuctionPowerA187.Balanced,
138
+ waterFlow: MopWaterFlowA187.Off,
139
+ distance_off: 0,
140
+ mopRoute: MopRouteA187[vacuumSetting?.mopRouteMode as keyof typeof MopRouteA187] ?? MopRouteA187.Standard,
141
+ };
142
+ }
143
+ case 'Vac & Mop': {
144
+ const vacmopSetting = cleanModeSettings?.vacmop;
145
+ const waterFlow = MopWaterFlowA187[vacmopSetting?.waterFlowMode as keyof typeof MopWaterFlowA187] ?? MopWaterFlowA187.Medium;
146
+ const distance_off = waterFlow == MopWaterFlowA187.CustomizeWithDistanceOff ? 210 - 5 * (vacmopSetting?.distanceOff ?? 25) : 0;
147
+ return {
148
+ suctionPower: VacuumSuctionPowerA187[vacmopSetting?.fanMode as keyof typeof VacuumSuctionPowerA187] ?? VacuumSuctionPowerA187.Balanced,
149
+ waterFlow,
150
+ distance_off,
151
+ mopRoute: MopRouteA187[vacmopSetting?.mopRouteMode as keyof typeof MopRouteA187] ?? MopRouteA187.Standard,
152
+ };
153
+ }
154
+ default:
155
+ return undefined;
156
+ }
157
+ };
158
+
103
159
  handler.setCommandHandler('selectAreas', async (newAreas: number[]) => {
104
160
  logger.notice(`BehaviorA187-selectAreas: ${newAreas}`);
105
161
  roborockService.setSelectedAreas(duid, newAreas ?? []);
@@ -0,0 +1,17 @@
1
+ export class CleanModeSettings {
2
+ vacuuming?: {
3
+ fanMode?: string;
4
+ mopRouteMode?: string;
5
+ };
6
+ mopping?: {
7
+ waterFlowMode?: string;
8
+ distanceOff?: number;
9
+ mopRouteMode?: string;
10
+ };
11
+ vacmop?: {
12
+ fanMode?: string;
13
+ waterFlowMode?: string;
14
+ distanceOff?: number;
15
+ mopRouteMode?: string;
16
+ };
17
+ }
package/src/platform.ts CHANGED
@@ -12,6 +12,7 @@ import { configurateBehavior } from './behaviorFactory.js';
12
12
  import { NotifyMessageTypes } from './notifyMessageTypes.js';
13
13
  import { Device, RoborockAuthenticateApi, RoborockIoTApi } from './roborockCommunication/index.js';
14
14
  import { getSupportedAreas } from './initialData/index.js';
15
+ import { CleanModeSettings } from './model/CleanModeSettings.js';
15
16
 
16
17
  export class RoborockMatterbridgePlatform extends MatterbridgeDynamicPlatform {
17
18
  robot: RoborockVacuumCleaner | undefined;
@@ -117,6 +118,13 @@ export class RoborockMatterbridgePlatform extends MatterbridgeDynamicPlatform {
117
118
 
118
119
  override async onConfigure() {
119
120
  await super.onConfigure();
121
+ if (this.config.enableExperimentalFeature) {
122
+ const cleanModeSettings = this.config.cleanModeSettings as CleanModeSettings;
123
+
124
+ this.log.notice(`Experimental Feature has been enable`);
125
+ this.log.notice(`cleanModeSettings ${debugStringify(cleanModeSettings)}`);
126
+ }
127
+
120
128
  const self = this;
121
129
  this.rvcInterval = setInterval(
122
130
  async () => {
@@ -138,6 +146,10 @@ export class RoborockMatterbridgePlatform extends MatterbridgeDynamicPlatform {
138
146
  this.log.error('Initializing: No supported devices found');
139
147
  return;
140
148
  }
149
+ let cleanModeSettings: CleanModeSettings | undefined = undefined;
150
+ if (this.config.enableExperimentalFeature) {
151
+ cleanModeSettings = this.config.cleanModeSettings as CleanModeSettings;
152
+ }
141
153
 
142
154
  const self = this;
143
155
  await this.roborockService.initializeMessageClientForLocal(vacuum);
@@ -145,7 +157,7 @@ export class RoborockMatterbridgePlatform extends MatterbridgeDynamicPlatform {
145
157
 
146
158
  this.log.debug('Initializing - roomMap: ', debugStringify(roomMap));
147
159
 
148
- const behaviorHandler = configurateBehavior(vacuum.data.model, vacuum.duid, this.roborockService, this.log);
160
+ const behaviorHandler = configurateBehavior(vacuum.data.model, vacuum.duid, this.roborockService, cleanModeSettings, this.log);
149
161
 
150
162
  this.roborockService.setSupportedAreas(vacuum.duid, getSupportedAreas(vacuum.rooms, roomMap, this.log));
151
163
  this.robot = new RoborockVacuumCleaner(username, vacuum, roomMap, this.log);
@@ -90,10 +90,8 @@ export class MessageProcessor {
90
90
  return this.client.send(duid, request);
91
91
  }
92
92
 
93
- public async getCustomMessage(duid: string, def: RequestMessage): Promise<any> {
94
- const response = this.client.get<any>(duid, def);
95
- this.logger?.warn('XXXXXXX: ', debugStringify(response));
96
- return response;
93
+ public getCustomMessage(duid: string, def: RequestMessage): Promise<any> {
94
+ return this.client.get<any>(duid, def);
97
95
  }
98
96
 
99
97
  public async findMyRobot(duid: string): Promise<void> {
@@ -101,8 +99,16 @@ export class MessageProcessor {
101
99
  return this.client.send(duid, request);
102
100
  }
103
101
 
104
- public async changeCleanMode(duid: string, suctionPower: number, waterFlow: number, mopRoute: number): Promise<void> {
105
- this.logger?.notice(`Change clean mode for ${duid} to suctionPower: ${suctionPower}, waterFlow: ${waterFlow}`);
102
+ public async changeCleanMode(duid: string, suctionPower: number, waterFlow: number, mopRoute: number, distance_off: number): Promise<void> {
103
+ this.logger?.notice(`Change clean mode for ${duid} to suctionPower: ${suctionPower}, waterFlow: ${waterFlow}, mopRoute: ${mopRoute}, distance_off: ${distance_off}`);
104
+
105
+ const currentMopMode = await this.getCustomMessage(duid, new RequestMessage({ method: 'get_custom_mode' }));
106
+
107
+ //110 == AI/Smart Plan
108
+ //302 == Custom
109
+ if (currentMopMode == 110) {
110
+ await this.client.send(duid, new RequestMessage({ method: 'set_mop_mode', params: [302] }));
111
+ }
106
112
 
107
113
  if (mopRoute && mopRoute != 0) {
108
114
  await this.client.send(duid, new RequestMessage({ method: 'set_mop_mode', params: [mopRoute] }));
@@ -112,7 +118,10 @@ export class MessageProcessor {
112
118
  await this.client.send(duid, new RequestMessage({ method: 'set_custom_mode', params: [suctionPower] }));
113
119
  }
114
120
 
115
- if (waterFlow && waterFlow != 0) {
121
+ //207 == CustomizeWithDistanceOff
122
+ if (waterFlow && waterFlow == 207 && distance_off && distance_off != 0) {
123
+ await this.client.send(duid, new RequestMessage({ method: 'set_water_box_custom_mode', params: { 'water_box_mode': waterFlow, 'distance_off': distance_off } }));
124
+ } else if (waterFlow && waterFlow != 0) {
116
125
  await this.client.send(duid, new RequestMessage({ method: 'set_water_box_custom_mode', params: [waterFlow] }));
117
126
  }
118
127
  }
@@ -84,9 +84,12 @@ export default class RoborockService {
84
84
  return this.supportedAreas.get(duid);
85
85
  }
86
86
 
87
- public async changeCleanMode(duid: string, { suctionPower, waterFlow, mopRoute }: { suctionPower: number; waterFlow: number; mopRoute: number }): Promise<void> {
87
+ public async changeCleanMode(
88
+ duid: string,
89
+ { suctionPower, waterFlow, distance_off, mopRoute }: { suctionPower: number; waterFlow: number; distance_off: number; mopRoute: number },
90
+ ): Promise<void> {
88
91
  this.logger.notice('changeCleanMode');
89
- return this.messageProcessor?.changeCleanMode(duid, suctionPower, waterFlow, mopRoute);
92
+ return this.messageProcessor?.changeCleanMode(duid, suctionPower, waterFlow, mopRoute, distance_off);
90
93
  }
91
94
 
92
95
  public async startClean(duid: string): Promise<void> {
@@ -133,6 +136,10 @@ export default class RoborockService {
133
136
  return this.getMessageProcessor()?.getCustomMessage(duid, new RequestMessage({ method, secure: true }));
134
137
  }
135
138
 
139
+ public async customSend(duid: string, request: any): Promise<void> {
140
+ return this.getMessageProcessor()?.sendCustomMessage(duid, request);
141
+ }
142
+
136
143
  public stopService(): void {
137
144
  if (this.messageClient) {
138
145
  this.messageClient.disconnect();