matterbridge-roborock-vacuum-plugin 1.0.8-rc01 → 1.0.8-rc02
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/publish.yml +4 -1
- package/README.md +2 -0
- package/README_SUPPORTED.md +54 -0
- package/dist/behaviorFactory.js +7 -1
- package/dist/behaviors/roborock.vacuum/S8_PRO_ULTRA/a51.js +139 -0
- package/dist/behaviors/roborock.vacuum/S8_PRO_ULTRA/initalData.js +76 -0
- package/dist/behaviors/roborock.vacuum/S8_PRO_ULTRA/runtimes.js +33 -0
- package/dist/behaviors/roborock.vacuum/default/default.js +86 -5
- package/dist/behaviors/roborock.vacuum/default/initalData.js +15 -5
- package/dist/behaviors/roborock.vacuum/default/runtimes.js +33 -0
- package/dist/initialData/getSupportedScenes.js +26 -0
- package/dist/initialData/index.js +1 -0
- package/dist/model/ExperimentalFeatureSetting.js +1 -0
- package/dist/platform.js +16 -12
- package/dist/platformRunner.js +4 -3
- package/dist/roborockCommunication/RESTAPI/roborockIoTApi.js +14 -3
- package/dist/roborockCommunication/Zmodel/scene.js +16 -0
- package/dist/roborockCommunication/helper/messageDeserializer.js +4 -2
- package/dist/roborockCommunication/index.js +1 -0
- package/dist/roborockService.js +58 -19
- package/dist/rvc.js +2 -2
- package/dist/share/runtimeHelper.js +14 -3
- package/jest.config.js +2 -0
- package/jest.setup.js +2 -0
- package/matterbridge-roborock-vacuum-plugin.config.json +2 -2
- package/matterbridge-roborock-vacuum-plugin.schema.json +181 -104
- package/package.json +1 -1
- package/src/behaviorFactory.ts +9 -2
- package/src/behaviors/roborock.vacuum/QREVO_EDGE_5V1/a187.ts +2 -2
- package/src/behaviors/roborock.vacuum/S7_MAXV/a27.ts +2 -2
- package/src/behaviors/roborock.vacuum/S7_MAXV/initalData.ts +0 -1
- package/src/behaviors/roborock.vacuum/S8_PRO_ULTRA/a51.ts +173 -0
- package/src/behaviors/roborock.vacuum/S8_PRO_ULTRA/initalData.ts +79 -0
- package/src/behaviors/roborock.vacuum/S8_PRO_ULTRA/runtimes.ts +26 -0
- package/src/behaviors/roborock.vacuum/default/default.ts +101 -6
- package/src/behaviors/roborock.vacuum/default/initalData.ts +15 -5
- package/src/behaviors/roborock.vacuum/default/runtimes.ts +26 -0
- package/src/initialData/getSupportedScenes.ts +32 -0
- package/src/initialData/index.ts +1 -0
- package/src/model/ExperimentalFeatureSetting.ts +26 -0
- package/src/platform.ts +21 -15
- package/src/platformRunner.ts +6 -4
- package/src/roborockCommunication/RESTAPI/roborockIoTApi.ts +20 -7
- package/src/roborockCommunication/Zmodel/device.ts +3 -0
- package/src/roborockCommunication/Zmodel/scene.ts +42 -0
- package/src/roborockCommunication/broadcast/listener/index.ts +2 -2
- package/src/roborockCommunication/helper/messageDeserializer.ts +5 -1
- package/src/roborockCommunication/index.ts +10 -6
- package/src/roborockService.ts +74 -23
- package/src/rvc.ts +2 -2
- package/src/share/runtimeHelper.ts +14 -3
- package/src/{behaviors → tests/behaviors}/roborock.vacuum/QREVO_EDGE_5V1/a187.test.ts +7 -6
- package/src/tests/behaviors/roborock.vacuum/QREVO_EDGE_5V1/runtimes.test.ts +83 -0
- package/src/tests/behaviors/roborock.vacuum/S7_MAXV/a27.test.ts +134 -0
- package/src/{initialData → tests/initialData}/getSupportedAreas.test.ts +2 -3
- package/src/tests/roborockService.test.ts +155 -0
- package/tsconfig.jest.json +4 -1
- package/dist/model/CleanModeSettings.js +0 -5
- package/src/model/CleanModeSettings.ts +0 -17
package/README.md
CHANGED
|
@@ -68,6 +68,8 @@ To get the **DUID** for your devices, you have two options:
|
|
|
68
68
|
|
|
69
69
|
---
|
|
70
70
|
|
|
71
|
+
**➡️ [See Supported & Tested Roborock Devices](./README_SUPPORTED.md)**
|
|
72
|
+
|
|
71
73
|
### 📦 Prerequisites
|
|
72
74
|
|
|
73
75
|
- A working installation of [Matterbridge](https://github.com/Luligu/matterbridge)
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# Supported & Tested Roborock Device Models
|
|
2
|
+
|
|
3
|
+
## ✅ Tested Devices
|
|
4
|
+
|
|
5
|
+
| Device Name | Model Enum Key | Model String | Thank |
|
|
6
|
+
|----------------------------|------------------------|-------------------------------|------------------|
|
|
7
|
+
| Roborock Qrevo Edge 5V1 | `QREVO_EDGE_5V1` | `roborock.vacuum.a187` | |
|
|
8
|
+
| Roborock S8 Pro Ultra | `S8_PRO_ULTRA` | `roborock.vacuum.a70` | |
|
|
9
|
+
| Roborock S7 MaxV | `S7_MAXV` | `roborock.vacuum.a27` | |
|
|
10
|
+
|
|
11
|
+
These devices have been fully tested and are confirmed to work as expected.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## ⚠️ Other Supported Devices
|
|
16
|
+
|
|
17
|
+
All other models listed in the code are supported, but **may have some limitations**.
|
|
18
|
+
|
|
19
|
+
**Reason:**
|
|
20
|
+
I do not own these devices for direct testing.
|
|
21
|
+
|
|
22
|
+
If you have one of these models, please try it out and let me know your results!
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
### List of Other Models that I know the mode string
|
|
27
|
+
|
|
28
|
+
- Q5 (`roborock.vacuum.a34`)
|
|
29
|
+
- Q5_PRO (`roborock.vacuum.a72`)
|
|
30
|
+
- S5 (`roborock.vacuum.s5`)
|
|
31
|
+
- S5_MAX (`roborock.vacuum.s5e`)
|
|
32
|
+
- S6 (`roborock.vacuum.s6`)
|
|
33
|
+
- S6_MAXV (`roborock.vacuum.a10`)
|
|
34
|
+
- S6_PURE (`roborock.vacuum.a08`)
|
|
35
|
+
- Q7 (`roborock.vacuum.a40`)
|
|
36
|
+
- Q7_MAX (`roborock.vacuum.a38`)
|
|
37
|
+
- Q7_PLUS (`roborock.vacuum.a40`)
|
|
38
|
+
- S7 (`roborock.vacuum.a15`)
|
|
39
|
+
- S7_MAXV_ULTRA (`roborock.vacuum.a65`)
|
|
40
|
+
- S7_PRO_ULTRA (`roborock.vacuum.a62`)
|
|
41
|
+
- Q8_MAX (`roborock.vacuum.a73`)
|
|
42
|
+
- S8 (`roborock.vacuum.a51`)
|
|
43
|
+
- S8_MAXV_ULTRA (`roborock.vacuum.a97`)
|
|
44
|
+
- QREVO_MASTER (`roborock.vacuum.a117`)
|
|
45
|
+
- QREVO_CURV (`roborock.vacuum.a135`)
|
|
46
|
+
- QREVO_S (`roborock.vacuum.a104`)
|
|
47
|
+
- QREVO_PRO (`roborock.vacuum.a101`)
|
|
48
|
+
- QREVO_MAXV (`roborock.vacuum.a87`)
|
|
49
|
+
- QREVO_EDGE_5AE (`roborock.vacuum.xxxx`)
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
> **Note:**
|
|
54
|
+
> If you have a device not listed above, feel free to try it and report your experience!
|
package/dist/behaviorFactory.js
CHANGED
|
@@ -3,6 +3,7 @@ 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
|
+
import { setCommandHandlerA51 } from './behaviors/roborock.vacuum/S8_PRO_ULTRA/a51.js';
|
|
6
7
|
export function configurateBehavior(model, duid, roborockService, cleanModeSettings, logger) {
|
|
7
8
|
switch (model) {
|
|
8
9
|
case DeviceModel.QREVO_EDGE_5V1: {
|
|
@@ -15,9 +16,14 @@ export function configurateBehavior(model, duid, roborockService, cleanModeSetti
|
|
|
15
16
|
setCommandHandlerA27(duid, deviceHandler, logger, roborockService, cleanModeSettings);
|
|
16
17
|
return deviceHandler;
|
|
17
18
|
}
|
|
19
|
+
case DeviceModel.S8_PRO_ULTRA: {
|
|
20
|
+
const deviceHandler = new BehaviorDeviceGeneric(logger);
|
|
21
|
+
setCommandHandlerA51(duid, deviceHandler, logger, roborockService, cleanModeSettings);
|
|
22
|
+
return deviceHandler;
|
|
23
|
+
}
|
|
18
24
|
default: {
|
|
19
25
|
const deviceHandler = new BehaviorDeviceGeneric(logger);
|
|
20
|
-
setDefaultCommandHandler(duid, deviceHandler, logger, roborockService);
|
|
26
|
+
setDefaultCommandHandler(duid, deviceHandler, logger, roborockService, cleanModeSettings);
|
|
21
27
|
return deviceHandler;
|
|
22
28
|
}
|
|
23
29
|
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { debugStringify } from 'matterbridge/logger';
|
|
2
|
+
import { BehaviorRoborock } from '../../BehaviorDeviceGeneric.js';
|
|
3
|
+
export class BehaviorA51 extends BehaviorRoborock {
|
|
4
|
+
}
|
|
5
|
+
export var BehaviorRoborockA51;
|
|
6
|
+
(function (BehaviorRoborockA51) {
|
|
7
|
+
class State {
|
|
8
|
+
device;
|
|
9
|
+
}
|
|
10
|
+
BehaviorRoborockA51.State = State;
|
|
11
|
+
})(BehaviorRoborockA51 || (BehaviorRoborockA51 = {}));
|
|
12
|
+
export var VacuumSuctionPowerA51;
|
|
13
|
+
(function (VacuumSuctionPowerA51) {
|
|
14
|
+
VacuumSuctionPowerA51[VacuumSuctionPowerA51["Quiet"] = 101] = "Quiet";
|
|
15
|
+
VacuumSuctionPowerA51[VacuumSuctionPowerA51["Balanced"] = 102] = "Balanced";
|
|
16
|
+
VacuumSuctionPowerA51[VacuumSuctionPowerA51["Turbo"] = 103] = "Turbo";
|
|
17
|
+
VacuumSuctionPowerA51[VacuumSuctionPowerA51["Max"] = 104] = "Max";
|
|
18
|
+
VacuumSuctionPowerA51[VacuumSuctionPowerA51["Off"] = 105] = "Off";
|
|
19
|
+
VacuumSuctionPowerA51[VacuumSuctionPowerA51["Custom"] = 106] = "Custom";
|
|
20
|
+
VacuumSuctionPowerA51[VacuumSuctionPowerA51["MaxPlus"] = 108] = "MaxPlus";
|
|
21
|
+
})(VacuumSuctionPowerA51 || (VacuumSuctionPowerA51 = {}));
|
|
22
|
+
export var MopWaterFlowA51;
|
|
23
|
+
(function (MopWaterFlowA51) {
|
|
24
|
+
MopWaterFlowA51[MopWaterFlowA51["Off"] = 200] = "Off";
|
|
25
|
+
MopWaterFlowA51[MopWaterFlowA51["Low"] = 201] = "Low";
|
|
26
|
+
MopWaterFlowA51[MopWaterFlowA51["Medium"] = 202] = "Medium";
|
|
27
|
+
MopWaterFlowA51[MopWaterFlowA51["High"] = 203] = "High";
|
|
28
|
+
MopWaterFlowA51[MopWaterFlowA51["Custom"] = 204] = "Custom";
|
|
29
|
+
})(MopWaterFlowA51 || (MopWaterFlowA51 = {}));
|
|
30
|
+
export var MopRouteA51;
|
|
31
|
+
(function (MopRouteA51) {
|
|
32
|
+
MopRouteA51[MopRouteA51["Standard"] = 300] = "Standard";
|
|
33
|
+
MopRouteA51[MopRouteA51["Deep"] = 301] = "Deep";
|
|
34
|
+
MopRouteA51[MopRouteA51["Custom"] = 302] = "Custom";
|
|
35
|
+
MopRouteA51[MopRouteA51["DeepPlus"] = 303] = "DeepPlus";
|
|
36
|
+
MopRouteA51[MopRouteA51["Fast"] = 304] = "Fast";
|
|
37
|
+
})(MopRouteA51 || (MopRouteA51 = {}));
|
|
38
|
+
const RvcRunMode = {
|
|
39
|
+
[1]: 'Idle',
|
|
40
|
+
[2]: 'Cleaning',
|
|
41
|
+
[3]: 'Mapping',
|
|
42
|
+
};
|
|
43
|
+
const RvcCleanMode = {
|
|
44
|
+
[5]: 'Mop',
|
|
45
|
+
[6]: 'Vacuum',
|
|
46
|
+
[7]: 'Vac & Mop',
|
|
47
|
+
[8]: 'Custom',
|
|
48
|
+
};
|
|
49
|
+
const CleanSetting = {
|
|
50
|
+
[5]: { suctionPower: VacuumSuctionPowerA51.Off, waterFlow: MopWaterFlowA51.Medium, distance_off: 0, mopRoute: MopRouteA51.Standard },
|
|
51
|
+
[6]: { suctionPower: VacuumSuctionPowerA51.Balanced, waterFlow: MopWaterFlowA51.Off, distance_off: 0, mopRoute: MopRouteA51.Standard },
|
|
52
|
+
[7]: { suctionPower: VacuumSuctionPowerA51.Balanced, waterFlow: MopWaterFlowA51.Medium, distance_off: 0, mopRoute: MopRouteA51.Standard },
|
|
53
|
+
[8]: { suctionPower: VacuumSuctionPowerA51.Custom, waterFlow: MopWaterFlowA51.Custom, distance_off: 0, mopRoute: MopRouteA51.Custom },
|
|
54
|
+
};
|
|
55
|
+
export function setCommandHandlerA51(duid, handler, logger, roborockService, cleanModeSettings) {
|
|
56
|
+
handler.setCommandHandler('changeToMode', async (newMode) => {
|
|
57
|
+
const activity = RvcRunMode[newMode] || RvcCleanMode[newMode];
|
|
58
|
+
switch (activity) {
|
|
59
|
+
case 'Cleaning': {
|
|
60
|
+
await roborockService.startClean(duid);
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
case 'Mop':
|
|
64
|
+
case 'Vacuum':
|
|
65
|
+
case 'Vac & Mop': {
|
|
66
|
+
const setting = cleanModeSettings ? getSettingFromCleanMode(activity, cleanModeSettings) : CleanSetting[newMode];
|
|
67
|
+
logger.notice(`BehaviorA51-ChangeCleanMode to: ${activity}, setting: ${debugStringify(setting ?? {})}`);
|
|
68
|
+
if (setting) {
|
|
69
|
+
await roborockService.changeCleanMode(duid, setting);
|
|
70
|
+
}
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
case 'Custom': {
|
|
74
|
+
const setting = CleanSetting[newMode];
|
|
75
|
+
logger.notice(`BehaviorA51-ChangeCleanMode to: ${activity}, setting: ${debugStringify(setting)}`);
|
|
76
|
+
await roborockService.changeCleanMode(duid, setting);
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
default:
|
|
80
|
+
logger.notice('BehaviorA51-changeToMode-Unknown: ', newMode);
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
handler.setCommandHandler('selectAreas', async (newAreas) => {
|
|
85
|
+
logger.notice(`BehaviorA51-selectAreas: ${newAreas}`);
|
|
86
|
+
roborockService.setSelectedAreas(duid, newAreas ?? []);
|
|
87
|
+
});
|
|
88
|
+
handler.setCommandHandler('pause', async () => {
|
|
89
|
+
logger.notice('BehaviorA51-Pause');
|
|
90
|
+
await roborockService.pauseClean(duid);
|
|
91
|
+
});
|
|
92
|
+
handler.setCommandHandler('resume', async () => {
|
|
93
|
+
logger.notice('BehaviorA51-Resume');
|
|
94
|
+
await roborockService.resumeClean(duid);
|
|
95
|
+
});
|
|
96
|
+
handler.setCommandHandler('goHome', async () => {
|
|
97
|
+
logger.notice('BehaviorA51-GoHome');
|
|
98
|
+
await roborockService.stopAndGoHome(duid);
|
|
99
|
+
});
|
|
100
|
+
handler.setCommandHandler('PlaySoundToLocate', async (identifyTime) => {
|
|
101
|
+
logger.notice('BehaviorA51-PlaySoundToLocate');
|
|
102
|
+
await roborockService.playSoundToLocate(duid);
|
|
103
|
+
});
|
|
104
|
+
const getSettingFromCleanMode = (activity, cleanModeSettings) => {
|
|
105
|
+
switch (activity) {
|
|
106
|
+
case 'Mop': {
|
|
107
|
+
const mopSetting = cleanModeSettings?.mopping;
|
|
108
|
+
const waterFlow = MopWaterFlowA51[mopSetting?.waterFlowMode] ?? MopWaterFlowA51.Medium;
|
|
109
|
+
return {
|
|
110
|
+
suctionPower: VacuumSuctionPowerA51.Off,
|
|
111
|
+
waterFlow,
|
|
112
|
+
distance_off: 0,
|
|
113
|
+
mopRoute: MopRouteA51[mopSetting?.mopRouteMode] ?? MopRouteA51.Standard,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
case 'Vacuum': {
|
|
117
|
+
const vacuumSetting = cleanModeSettings?.vacuuming;
|
|
118
|
+
return {
|
|
119
|
+
suctionPower: VacuumSuctionPowerA51[vacuumSetting?.fanMode] ?? VacuumSuctionPowerA51.Balanced,
|
|
120
|
+
waterFlow: MopWaterFlowA51.Off,
|
|
121
|
+
distance_off: 0,
|
|
122
|
+
mopRoute: MopRouteA51[vacuumSetting?.mopRouteMode] ?? MopRouteA51.Standard,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
case 'Vac & Mop': {
|
|
126
|
+
const vacmopSetting = cleanModeSettings?.vacmop;
|
|
127
|
+
const waterFlow = MopWaterFlowA51[vacmopSetting?.waterFlowMode] ?? MopWaterFlowA51.Medium;
|
|
128
|
+
return {
|
|
129
|
+
suctionPower: VacuumSuctionPowerA51[vacmopSetting?.fanMode] ?? VacuumSuctionPowerA51.Balanced,
|
|
130
|
+
waterFlow,
|
|
131
|
+
distance_off: 0,
|
|
132
|
+
mopRoute: MopRouteA51[vacmopSetting?.mopRouteMode] ?? MopRouteA51.Standard,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
default:
|
|
136
|
+
return undefined;
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { RvcCleanMode, RvcOperationalState, RvcRunMode } from 'matterbridge/matter/clusters';
|
|
2
|
+
export function getDefaultSupportedRunModes() {
|
|
3
|
+
return [
|
|
4
|
+
{
|
|
5
|
+
label: 'Idle',
|
|
6
|
+
mode: 1,
|
|
7
|
+
modeTags: [{ value: RvcRunMode.ModeTag.Idle }],
|
|
8
|
+
},
|
|
9
|
+
{
|
|
10
|
+
label: 'Cleaning',
|
|
11
|
+
mode: 2,
|
|
12
|
+
modeTags: [{ value: RvcRunMode.ModeTag.Cleaning }],
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
label: 'Mapping',
|
|
16
|
+
mode: 3,
|
|
17
|
+
modeTags: [{ value: RvcRunMode.ModeTag.Mapping }],
|
|
18
|
+
},
|
|
19
|
+
];
|
|
20
|
+
}
|
|
21
|
+
export function getDefaultSupportedCleanModes() {
|
|
22
|
+
return [
|
|
23
|
+
{
|
|
24
|
+
label: 'Mop',
|
|
25
|
+
mode: 5,
|
|
26
|
+
modeTags: [{ value: RvcCleanMode.ModeTag.Mop }, { value: RvcCleanMode.ModeTag.Auto }],
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
label: 'Vacuum',
|
|
30
|
+
mode: 6,
|
|
31
|
+
modeTags: [{ value: RvcCleanMode.ModeTag.Vacuum }, { value: RvcCleanMode.ModeTag.Auto }],
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
label: 'Mop & Vacuum',
|
|
35
|
+
mode: 7,
|
|
36
|
+
modeTags: [{ value: RvcCleanMode.ModeTag.Mop }, { value: RvcCleanMode.ModeTag.Vacuum }, { value: RvcCleanMode.ModeTag.DeepClean }],
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
label: 'Custom',
|
|
40
|
+
mode: 8,
|
|
41
|
+
modeTags: [{ value: RvcCleanMode.ModeTag.Mop }, { value: RvcCleanMode.ModeTag.Vacuum }, { value: RvcCleanMode.ModeTag.Quick }],
|
|
42
|
+
},
|
|
43
|
+
];
|
|
44
|
+
}
|
|
45
|
+
export function getDefaultOperationalStates() {
|
|
46
|
+
return [
|
|
47
|
+
{
|
|
48
|
+
operationalStateId: RvcOperationalState.OperationalState.Stopped,
|
|
49
|
+
operationalStateLabel: 'Stopped',
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
operationalStateId: RvcOperationalState.OperationalState.Running,
|
|
53
|
+
operationalStateLabel: 'Running',
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
operationalStateId: RvcOperationalState.OperationalState.Paused,
|
|
57
|
+
operationalStateLabel: 'Paused',
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
operationalStateId: RvcOperationalState.OperationalState.Error,
|
|
61
|
+
operationalStateLabel: 'Error',
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
operationalStateId: RvcOperationalState.OperationalState.SeekingCharger,
|
|
65
|
+
operationalStateLabel: 'SeekingCharger',
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
operationalStateId: RvcOperationalState.OperationalState.Charging,
|
|
69
|
+
operationalStateLabel: 'Charging',
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
operationalStateId: RvcOperationalState.OperationalState.Docked,
|
|
73
|
+
operationalStateLabel: 'Docked',
|
|
74
|
+
},
|
|
75
|
+
];
|
|
76
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { MopWaterFlowA51, VacuumSuctionPowerA51 } from './a51.js';
|
|
2
|
+
export function getCurrentCleanModeA51(fan_power, water_box_mode) {
|
|
3
|
+
if (!fan_power || !water_box_mode)
|
|
4
|
+
return undefined;
|
|
5
|
+
if (fan_power == VacuumSuctionPowerA51.Custom || water_box_mode == MopWaterFlowA51.Custom)
|
|
6
|
+
return 8;
|
|
7
|
+
if (fan_power == VacuumSuctionPowerA51.Off)
|
|
8
|
+
return 5;
|
|
9
|
+
if (water_box_mode == MopWaterFlowA51.Off)
|
|
10
|
+
return 6;
|
|
11
|
+
else
|
|
12
|
+
return 7;
|
|
13
|
+
}
|
|
14
|
+
export function getCurrentCleanModeFromFanPowerA51(fan_power) {
|
|
15
|
+
if (!fan_power)
|
|
16
|
+
return undefined;
|
|
17
|
+
if (fan_power == VacuumSuctionPowerA51.Custom)
|
|
18
|
+
return 8;
|
|
19
|
+
if (fan_power == VacuumSuctionPowerA51.Off)
|
|
20
|
+
return 5;
|
|
21
|
+
else
|
|
22
|
+
return undefined;
|
|
23
|
+
}
|
|
24
|
+
export function getCurrentCleanModeFromWaterBoxModeA51(water_box_mode) {
|
|
25
|
+
if (!water_box_mode)
|
|
26
|
+
return undefined;
|
|
27
|
+
if (water_box_mode == MopWaterFlowA51.Custom)
|
|
28
|
+
return 8;
|
|
29
|
+
if (water_box_mode == MopWaterFlowA51.Off)
|
|
30
|
+
return 6;
|
|
31
|
+
else
|
|
32
|
+
return undefined;
|
|
33
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { debugStringify } from 'matterbridge/logger';
|
|
1
2
|
import { BehaviorRoborock } from '../../BehaviorDeviceGeneric.js';
|
|
2
3
|
export class DefaultBehavior extends BehaviorRoborock {
|
|
3
4
|
}
|
|
@@ -8,16 +9,50 @@ export var DefaultBehaviorRoborock;
|
|
|
8
9
|
}
|
|
9
10
|
DefaultBehaviorRoborock.State = State;
|
|
10
11
|
})(DefaultBehaviorRoborock || (DefaultBehaviorRoborock = {}));
|
|
12
|
+
export var VacuumSuctionPower;
|
|
13
|
+
(function (VacuumSuctionPower) {
|
|
14
|
+
VacuumSuctionPower[VacuumSuctionPower["Quiet"] = 101] = "Quiet";
|
|
15
|
+
VacuumSuctionPower[VacuumSuctionPower["Balanced"] = 102] = "Balanced";
|
|
16
|
+
VacuumSuctionPower[VacuumSuctionPower["Turbo"] = 103] = "Turbo";
|
|
17
|
+
VacuumSuctionPower[VacuumSuctionPower["Max"] = 104] = "Max";
|
|
18
|
+
VacuumSuctionPower[VacuumSuctionPower["Off"] = 105] = "Off";
|
|
19
|
+
VacuumSuctionPower[VacuumSuctionPower["Custom"] = 106] = "Custom";
|
|
20
|
+
VacuumSuctionPower[VacuumSuctionPower["MaxPlus"] = 108] = "MaxPlus";
|
|
21
|
+
})(VacuumSuctionPower || (VacuumSuctionPower = {}));
|
|
22
|
+
export var MopWaterFlow;
|
|
23
|
+
(function (MopWaterFlow) {
|
|
24
|
+
MopWaterFlow[MopWaterFlow["Off"] = 200] = "Off";
|
|
25
|
+
MopWaterFlow[MopWaterFlow["Low"] = 201] = "Low";
|
|
26
|
+
MopWaterFlow[MopWaterFlow["Medium"] = 202] = "Medium";
|
|
27
|
+
MopWaterFlow[MopWaterFlow["High"] = 203] = "High";
|
|
28
|
+
MopWaterFlow[MopWaterFlow["Custom"] = 204] = "Custom";
|
|
29
|
+
})(MopWaterFlow || (MopWaterFlow = {}));
|
|
30
|
+
export var MopRoute;
|
|
31
|
+
(function (MopRoute) {
|
|
32
|
+
MopRoute[MopRoute["Standard"] = 300] = "Standard";
|
|
33
|
+
MopRoute[MopRoute["Deep"] = 301] = "Deep";
|
|
34
|
+
MopRoute[MopRoute["Custom"] = 302] = "Custom";
|
|
35
|
+
MopRoute[MopRoute["DeepPlus"] = 303] = "DeepPlus";
|
|
36
|
+
MopRoute[MopRoute["Fast"] = 304] = "Fast";
|
|
37
|
+
})(MopRoute || (MopRoute = {}));
|
|
11
38
|
const RvcRunMode = {
|
|
12
39
|
[1]: 'Idle',
|
|
13
40
|
[2]: 'Cleaning',
|
|
14
41
|
[3]: 'Mapping',
|
|
15
42
|
};
|
|
16
43
|
const RvcCleanMode = {
|
|
17
|
-
[
|
|
18
|
-
[
|
|
44
|
+
[5]: 'Mop',
|
|
45
|
+
[6]: 'Vacuum',
|
|
46
|
+
[7]: 'Vac & Mop',
|
|
47
|
+
[8]: 'Custom',
|
|
19
48
|
};
|
|
20
|
-
|
|
49
|
+
const CleanSetting = {
|
|
50
|
+
[5]: { suctionPower: VacuumSuctionPower.Off, waterFlow: MopWaterFlow.Medium, distance_off: 0, mopRoute: MopRoute.Standard },
|
|
51
|
+
[6]: { suctionPower: VacuumSuctionPower.Balanced, waterFlow: MopWaterFlow.Off, distance_off: 0, mopRoute: MopRoute.Standard },
|
|
52
|
+
[7]: { suctionPower: VacuumSuctionPower.Balanced, waterFlow: MopWaterFlow.Medium, distance_off: 0, mopRoute: MopRoute.Standard },
|
|
53
|
+
[8]: { suctionPower: VacuumSuctionPower.Custom, waterFlow: MopWaterFlow.Custom, distance_off: 0, mopRoute: MopRoute.Custom },
|
|
54
|
+
};
|
|
55
|
+
export function setDefaultCommandHandler(duid, handler, logger, roborockService, cleanModeSettings) {
|
|
21
56
|
handler.setCommandHandler('changeToMode', async (newMode) => {
|
|
22
57
|
const activity = RvcRunMode[newMode] || RvcCleanMode[newMode];
|
|
23
58
|
switch (activity) {
|
|
@@ -26,8 +61,19 @@ export function setDefaultCommandHandler(duid, handler, logger, roborockService)
|
|
|
26
61
|
return;
|
|
27
62
|
}
|
|
28
63
|
case 'Mop':
|
|
29
|
-
case 'Vacuum':
|
|
30
|
-
|
|
64
|
+
case 'Vacuum':
|
|
65
|
+
case 'Vac & Mop': {
|
|
66
|
+
const setting = cleanModeSettings ? getSettingFromCleanMode(activity, cleanModeSettings) : CleanSetting[newMode];
|
|
67
|
+
logger.notice(`DefaultBehavior-ChangeCleanMode to: ${activity}, setting: ${debugStringify(setting ?? {})}`);
|
|
68
|
+
if (setting) {
|
|
69
|
+
await roborockService.changeCleanMode(duid, setting);
|
|
70
|
+
}
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
case 'Custom': {
|
|
74
|
+
const setting = CleanSetting[newMode];
|
|
75
|
+
logger.notice(`DefaultBehavior-ChangeCleanMode to: ${activity}, setting: ${debugStringify(setting)}`);
|
|
76
|
+
await roborockService.changeCleanMode(duid, setting);
|
|
31
77
|
return;
|
|
32
78
|
}
|
|
33
79
|
default:
|
|
@@ -55,4 +101,39 @@ export function setDefaultCommandHandler(duid, handler, logger, roborockService)
|
|
|
55
101
|
logger.notice('DefaultBehavior-PlaySoundToLocate');
|
|
56
102
|
await roborockService.playSoundToLocate(duid);
|
|
57
103
|
});
|
|
104
|
+
const getSettingFromCleanMode = (activity, cleanModeSettings) => {
|
|
105
|
+
switch (activity) {
|
|
106
|
+
case 'Mop': {
|
|
107
|
+
const mopSetting = cleanModeSettings?.mopping;
|
|
108
|
+
const waterFlow = MopWaterFlow[mopSetting?.waterFlowMode] ?? MopWaterFlow.Medium;
|
|
109
|
+
return {
|
|
110
|
+
suctionPower: VacuumSuctionPower.Off,
|
|
111
|
+
waterFlow,
|
|
112
|
+
distance_off: 0,
|
|
113
|
+
mopRoute: MopRoute[mopSetting?.mopRouteMode] ?? MopRoute.Standard,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
case 'Vacuum': {
|
|
117
|
+
const vacuumSetting = cleanModeSettings?.vacuuming;
|
|
118
|
+
return {
|
|
119
|
+
suctionPower: VacuumSuctionPower[vacuumSetting?.fanMode] ?? VacuumSuctionPower.Balanced,
|
|
120
|
+
waterFlow: MopWaterFlow.Off,
|
|
121
|
+
distance_off: 0,
|
|
122
|
+
mopRoute: MopRoute[vacuumSetting?.mopRouteMode] ?? MopRoute.Standard,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
case 'Vac & Mop': {
|
|
126
|
+
const vacmopSetting = cleanModeSettings?.vacmop;
|
|
127
|
+
const waterFlow = MopWaterFlow[vacmopSetting?.waterFlowMode] ?? MopWaterFlow.Medium;
|
|
128
|
+
return {
|
|
129
|
+
suctionPower: VacuumSuctionPower[vacmopSetting?.fanMode] ?? VacuumSuctionPower.Balanced,
|
|
130
|
+
waterFlow,
|
|
131
|
+
distance_off: 0,
|
|
132
|
+
mopRoute: MopRoute[vacmopSetting?.mopRouteMode] ?? MopRoute.Standard,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
default:
|
|
136
|
+
return undefined;
|
|
137
|
+
}
|
|
138
|
+
};
|
|
58
139
|
}
|
|
@@ -20,15 +20,25 @@ export function getDefaultSupportedRunModes() {
|
|
|
20
20
|
}
|
|
21
21
|
export function getDefaultSupportedCleanModes() {
|
|
22
22
|
return [
|
|
23
|
+
{
|
|
24
|
+
label: 'Mop',
|
|
25
|
+
mode: 5,
|
|
26
|
+
modeTags: [{ value: RvcCleanMode.ModeTag.Mop }, { value: RvcCleanMode.ModeTag.Auto }],
|
|
27
|
+
},
|
|
23
28
|
{
|
|
24
29
|
label: 'Vacuum',
|
|
25
|
-
mode:
|
|
26
|
-
modeTags: [{ value: RvcCleanMode.ModeTag.Vacuum }],
|
|
30
|
+
mode: 6,
|
|
31
|
+
modeTags: [{ value: RvcCleanMode.ModeTag.Vacuum }, { value: RvcCleanMode.ModeTag.Auto }],
|
|
27
32
|
},
|
|
28
33
|
{
|
|
29
|
-
label: 'Mop',
|
|
30
|
-
mode:
|
|
31
|
-
modeTags: [{ value: RvcCleanMode.ModeTag.Mop }],
|
|
34
|
+
label: 'Mop & Vacuum',
|
|
35
|
+
mode: 7,
|
|
36
|
+
modeTags: [{ value: RvcCleanMode.ModeTag.Mop }, { value: RvcCleanMode.ModeTag.Vacuum }, { value: RvcCleanMode.ModeTag.DeepClean }],
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
label: 'Custom',
|
|
40
|
+
mode: 8,
|
|
41
|
+
modeTags: [{ value: RvcCleanMode.ModeTag.Mop }, { value: RvcCleanMode.ModeTag.Vacuum }, { value: RvcCleanMode.ModeTag.Quick }],
|
|
32
42
|
},
|
|
33
43
|
];
|
|
34
44
|
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { MopWaterFlow, VacuumSuctionPower } from './default.js';
|
|
2
|
+
export function getCurrentCleanModeDefault(fan_power, water_box_mode) {
|
|
3
|
+
if (!fan_power || !water_box_mode)
|
|
4
|
+
return undefined;
|
|
5
|
+
if (fan_power == VacuumSuctionPower.Custom || water_box_mode == MopWaterFlow.Custom)
|
|
6
|
+
return 8;
|
|
7
|
+
if (fan_power == VacuumSuctionPower.Off)
|
|
8
|
+
return 5;
|
|
9
|
+
if (water_box_mode == MopWaterFlow.Off)
|
|
10
|
+
return 6;
|
|
11
|
+
else
|
|
12
|
+
return 7;
|
|
13
|
+
}
|
|
14
|
+
export function getCurrentCleanModeFromFanPowerDefault(fan_power) {
|
|
15
|
+
if (!fan_power)
|
|
16
|
+
return undefined;
|
|
17
|
+
if (fan_power == VacuumSuctionPower.Custom)
|
|
18
|
+
return 8;
|
|
19
|
+
if (fan_power == VacuumSuctionPower.Off)
|
|
20
|
+
return 5;
|
|
21
|
+
else
|
|
22
|
+
return undefined;
|
|
23
|
+
}
|
|
24
|
+
export function getCurrentCleanModeFromWaterBoxModeDefault(water_box_mode) {
|
|
25
|
+
if (!water_box_mode)
|
|
26
|
+
return undefined;
|
|
27
|
+
if (water_box_mode == MopWaterFlow.Custom)
|
|
28
|
+
return 8;
|
|
29
|
+
if (water_box_mode == MopWaterFlow.Off)
|
|
30
|
+
return 6;
|
|
31
|
+
else
|
|
32
|
+
return undefined;
|
|
33
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { debugStringify } from 'matterbridge/logger';
|
|
2
|
+
import { randomInt } from 'crypto';
|
|
3
|
+
export function getSupportedScenes(scenes, log) {
|
|
4
|
+
log?.debug('getSupportedScenes', debugStringify(scenes));
|
|
5
|
+
if (!scenes || scenes.length === 0) {
|
|
6
|
+
log?.error('No scenes found');
|
|
7
|
+
return [];
|
|
8
|
+
}
|
|
9
|
+
const supportedAreas = scenes
|
|
10
|
+
.filter((s) => s.enabled && s.id)
|
|
11
|
+
.map((scene) => {
|
|
12
|
+
return {
|
|
13
|
+
areaId: scene.id + randomInt(5000, 9000),
|
|
14
|
+
mapId: null,
|
|
15
|
+
areaInfo: {
|
|
16
|
+
locationInfo: {
|
|
17
|
+
locationName: `Scene: ${scene.name}`,
|
|
18
|
+
floorNumber: null,
|
|
19
|
+
areaType: null,
|
|
20
|
+
},
|
|
21
|
+
landmarkInfo: null,
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
});
|
|
25
|
+
return supportedAreas;
|
|
26
|
+
}
|
|
@@ -3,3 +3,4 @@ export { getOperationalStates, getOperationalErrorState } from './getOperational
|
|
|
3
3
|
export { getSupportedCleanModes } from './getSupportedCleanModes.js';
|
|
4
4
|
export { getSupportedAreas } from './getSupportedAreas.js';
|
|
5
5
|
export { getBatteryStatus, getBatteryState } from './getBatteryStatus.js';
|
|
6
|
+
export { getSupportedScenes } from './getSupportedScenes.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/platform.js
CHANGED
|
@@ -10,7 +10,7 @@ import { PlatformRunner } from './platformRunner.js';
|
|
|
10
10
|
import { RoborockVacuumCleaner } from './rvc.js';
|
|
11
11
|
import { configurateBehavior } from './behaviorFactory.js';
|
|
12
12
|
import { RoborockAuthenticateApi, RoborockIoTApi } from './roborockCommunication/index.js';
|
|
13
|
-
import { getSupportedAreas } from './initialData/index.js';
|
|
13
|
+
import { getSupportedAreas, getSupportedScenes } from './initialData/index.js';
|
|
14
14
|
export class RoborockMatterbridgePlatform extends MatterbridgeDynamicPlatform {
|
|
15
15
|
robot;
|
|
16
16
|
rvcInterval;
|
|
@@ -19,6 +19,8 @@ export class RoborockMatterbridgePlatform extends MatterbridgeDynamicPlatform {
|
|
|
19
19
|
platformRunner;
|
|
20
20
|
devices;
|
|
21
21
|
serialNumber;
|
|
22
|
+
cleanModeSettings;
|
|
23
|
+
enableExperimentalFeature;
|
|
22
24
|
constructor(matterbridge, log, config) {
|
|
23
25
|
super(matterbridge, log, config);
|
|
24
26
|
if (this.verifyMatterbridgeVersion === undefined || typeof this.verifyMatterbridgeVersion !== 'function' || !this.verifyMatterbridgeVersion('3.0.4')) {
|
|
@@ -62,6 +64,12 @@ export class RoborockMatterbridgePlatform extends MatterbridgeDynamicPlatform {
|
|
|
62
64
|
});
|
|
63
65
|
return response;
|
|
64
66
|
});
|
|
67
|
+
this.enableExperimentalFeature = this.config.enableExperimental;
|
|
68
|
+
if (this.enableExperimentalFeature?.enableExperimentalFeature && this.enableExperimentalFeature?.cleanModeSettings?.enableCleanModeMapping) {
|
|
69
|
+
this.cleanModeSettings = this.enableExperimentalFeature.cleanModeSettings;
|
|
70
|
+
this.log.notice(`Experimental Feature has been enable`);
|
|
71
|
+
this.log.notice(`cleanModeSettings ${debugStringify(this.cleanModeSettings)}`);
|
|
72
|
+
}
|
|
65
73
|
this.platformRunner = new PlatformRunner(this);
|
|
66
74
|
this.roborockService = new RoborockService(() => new RoborockAuthenticateApi(this.log, axiosInstance), (logger, ud) => new RoborockIoTApi(ud, logger), this.config.refreshInterval ?? 60, this.clientManager, this.log);
|
|
67
75
|
const username = this.config.username;
|
|
@@ -91,11 +99,6 @@ export class RoborockMatterbridgePlatform extends MatterbridgeDynamicPlatform {
|
|
|
91
99
|
}
|
|
92
100
|
async onConfigure() {
|
|
93
101
|
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
|
-
}
|
|
99
102
|
const self = this;
|
|
100
103
|
this.rvcInterval = setInterval(async () => {
|
|
101
104
|
self.platformRunner?.requestHomeData();
|
|
@@ -113,17 +116,18 @@ export class RoborockMatterbridgePlatform extends MatterbridgeDynamicPlatform {
|
|
|
113
116
|
this.log.error('Initializing: No supported devices found');
|
|
114
117
|
return;
|
|
115
118
|
}
|
|
116
|
-
let cleanModeSettings = undefined;
|
|
117
|
-
if (this.config.enableExperimentalFeature) {
|
|
118
|
-
cleanModeSettings = this.config.cleanModeSettings;
|
|
119
|
-
}
|
|
120
119
|
const self = this;
|
|
121
120
|
await this.roborockService.initializeMessageClientForLocal(vacuum);
|
|
122
121
|
const roomMap = await this.platformRunner.getRoomMapFromDevice(vacuum);
|
|
123
122
|
this.log.debug('Initializing - roomMap: ', debugStringify(roomMap));
|
|
124
|
-
const behaviorHandler = configurateBehavior(vacuum.data.model, vacuum.duid, this.roborockService, cleanModeSettings, this.log);
|
|
123
|
+
const behaviorHandler = configurateBehavior(vacuum.data.model, vacuum.duid, this.roborockService, this.cleanModeSettings, this.log);
|
|
125
124
|
this.roborockService.setSupportedAreas(vacuum.duid, getSupportedAreas(vacuum.rooms, roomMap, this.log));
|
|
126
|
-
|
|
125
|
+
let routineAsRoom = [];
|
|
126
|
+
if (this.enableExperimentalFeature?.enableExperimentalFeature && this.enableExperimentalFeature.advancedFeature?.showRoutinesAsRoom) {
|
|
127
|
+
routineAsRoom = getSupportedScenes(vacuum.scenes, this.log);
|
|
128
|
+
this.roborockService.setSupportedScenes(vacuum.duid, routineAsRoom);
|
|
129
|
+
}
|
|
130
|
+
this.robot = new RoborockVacuumCleaner(username, vacuum, roomMap, routineAsRoom, this.log);
|
|
127
131
|
this.robot.configurateHandler(behaviorHandler);
|
|
128
132
|
this.log.info('vacuum:', debugStringify(vacuum));
|
|
129
133
|
this.setSelectDevice(this.robot.serialNumber ?? '', this.robot.deviceName ?? '', undefined, 'hub');
|
package/dist/platformRunner.js
CHANGED
|
@@ -157,7 +157,7 @@ export class PlatformRunner {
|
|
|
157
157
|
case Protocol.rpc_response: {
|
|
158
158
|
const response = data.dps[messageType];
|
|
159
159
|
if (!self.isStatusUpdate(response.result)) {
|
|
160
|
-
platform.log.
|
|
160
|
+
platform.log.debug('Ignore message:', debugStringify(data));
|
|
161
161
|
return;
|
|
162
162
|
}
|
|
163
163
|
const roboStatus = response.result[0];
|
|
@@ -200,8 +200,9 @@ export class PlatformRunner {
|
|
|
200
200
|
if (platform.config.enableExperimentalFeature && message.dss !== undefined) {
|
|
201
201
|
const dss = parseDockingStationStatus(message.dss);
|
|
202
202
|
this.platform.log.debug('DockingStationStatus:', debugStringify(dss));
|
|
203
|
-
|
|
204
|
-
|
|
203
|
+
const currentOperationState = robot.getAttribute(RvcOperationalState.Cluster.id, 'operationalState');
|
|
204
|
+
if (dss && hasDockingStationError(dss) && currentOperationState !== RvcOperationalState.OperationalState.Running) {
|
|
205
|
+
robot.updateAttribute(RvcOperationalState.Cluster.id, 'operationalState', RvcOperationalState.OperationalState.Error, platform.log);
|
|
205
206
|
}
|
|
206
207
|
}
|
|
207
208
|
if (message.fan_power !== undefined) {
|