matterbridge-roborock-vacuum-plugin 1.0.8-rc02 → 1.0.8-rc03
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/build.yml +47 -0
- package/.github/workflows/coverage.yml +50 -0
- package/README.md +6 -0
- package/dist/behaviors/BehaviorDeviceGeneric.js +0 -9
- package/dist/behaviors/roborock.vacuum/QREVO_EDGE_5V1/a187.js +2 -9
- package/dist/behaviors/roborock.vacuum/S7_MAXV/a27.js +2 -9
- package/dist/behaviors/roborock.vacuum/S8_PRO_ULTRA/a51.js +2 -9
- package/dist/behaviors/roborock.vacuum/default/default.js +2 -9
- package/dist/initialData/getSupportedAreas.js +1 -1
- package/dist/initialData/getSupportedScenes.js +2 -2
- package/dist/platform.js +2 -20
- package/dist/platformRunner.js +29 -22
- package/dist/roborockCommunication/RESTAPI/roborockAuthenticateApi.js +1 -1
- package/dist/roborockCommunication/RESTAPI/roborockIoTApi.js +1 -1
- package/dist/roborockCommunication/Zmodel/batteryMessage.js +1 -0
- package/dist/roborockCommunication/Zmodel/deviceModel.js +1 -1
- package/dist/roborockCommunication/broadcast/abstractClient.js +1 -1
- package/dist/roborockCommunication/broadcast/client/MQTTClient.js +3 -2
- package/dist/roborockCommunication/broadcast/clientRouter.js +2 -2
- package/dist/roborockCommunication/broadcast/listener/implementation/syncMessageListener.js +2 -1
- package/dist/roborockCommunication/broadcast/model/messageContext.js +1 -1
- package/dist/roborockCommunication/broadcast/model/requestMessage.js +1 -1
- package/dist/roborockCommunication/helper/cryptoHelper.js +21 -25
- package/dist/roborockCommunication/helper/messageDeserializer.js +3 -3
- package/dist/roborockCommunication/helper/messageSerializer.js +3 -3
- package/dist/roborockService.js +2 -1
- package/dist/rvc.js +1 -1
- package/dist/share/function.js +0 -2
- package/eslint.config.js +65 -61
- package/jest.config.js +20 -20
- package/jest.setup.js +1 -1
- package/matterbridge-roborock-vacuum-plugin.config.json +26 -1
- package/matterbridge-roborock-vacuum-plugin.schema.json +6 -1
- package/package.json +4 -2
- package/prettier.config.js +18 -18
- package/src/behaviors/BehaviorDeviceGeneric.ts +5 -13
- package/src/behaviors/roborock.vacuum/QREVO_EDGE_5V1/a187.ts +16 -18
- package/src/behaviors/roborock.vacuum/QREVO_EDGE_5V1/runtimes.ts +1 -1
- package/src/behaviors/roborock.vacuum/S7_MAXV/a27.ts +15 -17
- package/src/behaviors/roborock.vacuum/S7_MAXV/runtimes.ts +1 -1
- package/src/behaviors/roborock.vacuum/S8_PRO_ULTRA/a51.ts +14 -16
- package/src/behaviors/roborock.vacuum/S8_PRO_ULTRA/runtimes.ts +1 -1
- package/src/behaviors/roborock.vacuum/default/default.ts +13 -15
- package/src/behaviors/roborock.vacuum/default/runtimes.ts +1 -1
- package/src/clientManager.ts +1 -1
- package/src/initialData/getSupportedAreas.ts +1 -1
- package/src/initialData/getSupportedScenes.ts +2 -2
- package/src/model/CloudMessageModel.ts +8 -8
- package/src/model/DockingStationStatus.ts +3 -3
- package/src/model/ExperimentalFeatureSetting.ts +1 -0
- package/src/platform.ts +4 -23
- package/src/platformRunner.ts +51 -40
- package/src/roborockCommunication/RESTAPI/roborockAuthenticateApi.ts +2 -2
- package/src/roborockCommunication/RESTAPI/roborockIoTApi.ts +4 -4
- package/src/roborockCommunication/Zenum/operationStatusCode.ts +1 -1
- package/src/roborockCommunication/Zmodel/batteryMessage.ts +14 -0
- package/src/roborockCommunication/Zmodel/deviceModel.ts +1 -1
- package/src/roborockCommunication/Zmodel/deviceSchema.ts +1 -1
- package/src/roborockCommunication/Zmodel/messageResult.ts +1 -1
- package/src/roborockCommunication/Zmodel/room.ts +1 -1
- package/src/roborockCommunication/Zmodel/scene.ts +2 -2
- package/src/roborockCommunication/broadcast/abstractClient.ts +4 -3
- package/src/roborockCommunication/broadcast/client/LocalNetworkClient.ts +2 -2
- package/src/roborockCommunication/broadcast/client/MQTTClient.ts +6 -6
- package/src/roborockCommunication/broadcast/clientRouter.ts +2 -2
- package/src/roborockCommunication/broadcast/listener/implementation/simpleMessageListener.ts +3 -3
- package/src/roborockCommunication/broadcast/listener/implementation/syncMessageListener.ts +5 -4
- package/src/roborockCommunication/broadcast/messageProcessor.ts +3 -3
- package/src/roborockCommunication/broadcast/model/dps.ts +2 -2
- package/src/roborockCommunication/broadcast/model/messageContext.ts +2 -2
- package/src/roborockCommunication/broadcast/model/protocol.ts +2 -2
- package/src/roborockCommunication/broadcast/model/requestMessage.ts +5 -5
- package/src/roborockCommunication/helper/cryptoHelper.ts +22 -26
- package/src/roborockCommunication/helper/messageDeserializer.ts +6 -7
- package/src/roborockCommunication/helper/messageSerializer.ts +3 -3
- package/src/roborockCommunication/helper/nameDecoder.ts +3 -3
- package/src/roborockCommunication/index.ts +1 -0
- package/src/roborockService.ts +37 -27
- package/src/rvc.ts +14 -14
- package/src/share/function.ts +0 -2
- package/src/tests/behaviors/roborock.vacuum/QREVO_EDGE_5V1/a187.test.ts +5 -5
- package/src/tests/behaviors/roborock.vacuum/S7_MAXV/a27.test.ts +2 -2
- package/src/tests/model/DockingStationStatus.test.ts +39 -0
- package/src/tests/platformRunner.test.ts +57 -0
- package/src/tests/testData/mockHomeData-a187.json +286 -0
- package/dist/extensions/AxiosStaticExtensions.js +0 -22
- package/dist/extensions/index.js +0 -1
- package/src/behaviors/roborock.vacuum/QREVO_EDGE_5V1/runtimes.test.ts +0 -79
- package/src/behaviors/roborock.vacuum/S7_MAXV/a27.test.ts +0 -134
- package/src/extensions/AxiosStaticExtensions.ts +0 -35
- package/src/extensions/index.ts +0 -1
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
name: Build, lint and test
|
|
2
|
+
|
|
3
|
+
on: [push, pull_request]
|
|
4
|
+
|
|
5
|
+
jobs:
|
|
6
|
+
publish:
|
|
7
|
+
runs-on: ubuntu-latest
|
|
8
|
+
|
|
9
|
+
strategy:
|
|
10
|
+
fail-fast: false
|
|
11
|
+
matrix:
|
|
12
|
+
node-version: [18.x, 20.x, 22.x, 24.x]
|
|
13
|
+
os: [ubuntu-latest, windows-latest, macos-latest]
|
|
14
|
+
|
|
15
|
+
steps:
|
|
16
|
+
- name: Checkout code
|
|
17
|
+
uses: actions/checkout@v4
|
|
18
|
+
|
|
19
|
+
- name: Use Node.js ${{ matrix.node-version }}
|
|
20
|
+
uses: actions/setup-node@v4
|
|
21
|
+
with:
|
|
22
|
+
node-version: ${{ matrix.node-version }}
|
|
23
|
+
registry-url: 'https://registry.npmjs.org/'
|
|
24
|
+
|
|
25
|
+
- name: Clean cache
|
|
26
|
+
run: npm cache clean --force
|
|
27
|
+
|
|
28
|
+
- name: Verify Node.js version
|
|
29
|
+
run: node -v
|
|
30
|
+
|
|
31
|
+
- name: Verify Npm version
|
|
32
|
+
run: npm -v
|
|
33
|
+
|
|
34
|
+
- name: Install dependencies
|
|
35
|
+
run: npm ci
|
|
36
|
+
|
|
37
|
+
- name: Install dependencies
|
|
38
|
+
run: npm run precondition && npm run deepClean && npm install && npm link matterbridge
|
|
39
|
+
|
|
40
|
+
- name: Lint the project
|
|
41
|
+
run: npm run lint
|
|
42
|
+
|
|
43
|
+
- name: Test the project
|
|
44
|
+
run: npm run test
|
|
45
|
+
|
|
46
|
+
- name: Build (optional)
|
|
47
|
+
run: npm run build
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
name: Analyze code coverage
|
|
2
|
+
|
|
3
|
+
on: [push, pull_request]
|
|
4
|
+
|
|
5
|
+
jobs:
|
|
6
|
+
publish:
|
|
7
|
+
runs-on: ubuntu-latest
|
|
8
|
+
|
|
9
|
+
steps:
|
|
10
|
+
- name: Checkout code
|
|
11
|
+
uses: actions/checkout@v4
|
|
12
|
+
|
|
13
|
+
- name: Use Node.js
|
|
14
|
+
uses: actions/setup-node@v4
|
|
15
|
+
with:
|
|
16
|
+
node-version: '20'
|
|
17
|
+
registry-url: 'https://registry.npmjs.org/'
|
|
18
|
+
|
|
19
|
+
- name: Clean cache
|
|
20
|
+
run: npm cache clean --force
|
|
21
|
+
|
|
22
|
+
- name: Verify Node.js version
|
|
23
|
+
run: node -v
|
|
24
|
+
|
|
25
|
+
- name: Verify Npm version
|
|
26
|
+
run: npm -v
|
|
27
|
+
|
|
28
|
+
- name: Install dependencies
|
|
29
|
+
run: npm ci
|
|
30
|
+
|
|
31
|
+
- name: Install dependencies
|
|
32
|
+
run: npm run precondition && npm run deepClean && npm install && npm link matterbridge
|
|
33
|
+
|
|
34
|
+
- name: Lint the project
|
|
35
|
+
run: npm run lint
|
|
36
|
+
|
|
37
|
+
- name: Test the project
|
|
38
|
+
run: npm run test
|
|
39
|
+
|
|
40
|
+
- name: Test with coverage
|
|
41
|
+
run: npm run test -- --coverage
|
|
42
|
+
|
|
43
|
+
- name: Upload coverage reports to Codecov
|
|
44
|
+
uses: codecov/codecov-action@v5
|
|
45
|
+
with:
|
|
46
|
+
token: ${{ secrets.CODECOV_TOKEN }}
|
|
47
|
+
slug: RinDevJunior/matterbridge-roborock-vacuum-plugin
|
|
48
|
+
|
|
49
|
+
- name: Build (optional)
|
|
50
|
+
run: npm run build
|
package/README.md
CHANGED
|
@@ -11,6 +11,12 @@
|
|
|
11
11
|
<a href="https://www.npmjs.com/package/matterbridge-roborock-vacuum-plugin">
|
|
12
12
|
<img src="https://img.shields.io/npm/dt/matterbridge-roborock-vacuum-plugin.svg" alt="npm downloads" />
|
|
13
13
|
</a>
|
|
14
|
+
<a href="https://github.com/RinDevJunior/matterbridge-roborock-vacuum-plugin/actions/workflows/publish.yml/badge.svg">
|
|
15
|
+
<img src="https://github.com/RinDevJunior/matterbridge-roborock-vacuum-plugin/actions/workflows/publish.yml/badge.svg" alt="nodejs ci" />
|
|
16
|
+
</a>
|
|
17
|
+
<a href="https://codecov.io/gh/RinDevJunior/matterbridge-roborock-vacuum-plugin">
|
|
18
|
+
<img src="https://codecov.io/gh/RinDevJunior/matterbridge-roborock-vacuum-plugin/branch/main/graph/badge.svg" alt="codecov" />
|
|
19
|
+
</a>
|
|
14
20
|
<a href="https://www.npmjs.com/package/matterbridge">
|
|
15
21
|
<img src="https://img.shields.io/badge/powered%20by-matterbridge-blue" alt="powered by Matterbridge" />
|
|
16
22
|
</a>
|
|
@@ -17,15 +17,6 @@ export class BehaviorDeviceGeneric {
|
|
|
17
17
|
await handler(...args);
|
|
18
18
|
}
|
|
19
19
|
}
|
|
20
|
-
export class BehaviorGeneric extends Behavior {
|
|
21
|
-
static id;
|
|
22
|
-
}
|
|
23
20
|
export class BehaviorRoborock extends Behavior {
|
|
24
21
|
static id = 'roborock.vacuum.axx';
|
|
25
22
|
}
|
|
26
|
-
(function (BehaviorRoborock) {
|
|
27
|
-
class State {
|
|
28
|
-
device;
|
|
29
|
-
}
|
|
30
|
-
BehaviorRoborock.State = State;
|
|
31
|
-
})(BehaviorRoborock || (BehaviorRoborock = {}));
|
|
@@ -2,13 +2,6 @@ import { debugStringify } from 'matterbridge/logger';
|
|
|
2
2
|
import { BehaviorRoborock } from '../../BehaviorDeviceGeneric.js';
|
|
3
3
|
export class BehaviorA187 extends BehaviorRoborock {
|
|
4
4
|
}
|
|
5
|
-
export var BehaviorRoborockA187;
|
|
6
|
-
(function (BehaviorRoborockA187) {
|
|
7
|
-
class State {
|
|
8
|
-
device;
|
|
9
|
-
}
|
|
10
|
-
BehaviorRoborockA187.State = State;
|
|
11
|
-
})(BehaviorRoborockA187 || (BehaviorRoborockA187 = {}));
|
|
12
5
|
export var VacuumSuctionPowerA187;
|
|
13
6
|
(function (VacuumSuctionPowerA187) {
|
|
14
7
|
VacuumSuctionPowerA187[VacuumSuctionPowerA187["Quiet"] = 101] = "Quiet";
|
|
@@ -142,8 +135,8 @@ export function setCommandHandlerA187(duid, handler, logger, roborockService, cl
|
|
|
142
135
|
logger.notice('BehaviorA187-GoHome');
|
|
143
136
|
await roborockService.stopAndGoHome(duid);
|
|
144
137
|
});
|
|
145
|
-
handler.setCommandHandler('
|
|
146
|
-
logger.notice('BehaviorA187-
|
|
138
|
+
handler.setCommandHandler('playSoundToLocate', async () => {
|
|
139
|
+
logger.notice('BehaviorA187-playSoundToLocate');
|
|
147
140
|
await roborockService.playSoundToLocate(duid);
|
|
148
141
|
});
|
|
149
142
|
}
|
|
@@ -2,13 +2,6 @@ import { debugStringify } from 'matterbridge/logger';
|
|
|
2
2
|
import { BehaviorRoborock } from '../../BehaviorDeviceGeneric.js';
|
|
3
3
|
export class BehaviorA27 extends BehaviorRoborock {
|
|
4
4
|
}
|
|
5
|
-
export var BehaviorRoborockA27;
|
|
6
|
-
(function (BehaviorRoborockA27) {
|
|
7
|
-
class State {
|
|
8
|
-
device;
|
|
9
|
-
}
|
|
10
|
-
BehaviorRoborockA27.State = State;
|
|
11
|
-
})(BehaviorRoborockA27 || (BehaviorRoborockA27 = {}));
|
|
12
5
|
export var VacuumSuctionPowerA27;
|
|
13
6
|
(function (VacuumSuctionPowerA27) {
|
|
14
7
|
VacuumSuctionPowerA27[VacuumSuctionPowerA27["Quiet"] = 101] = "Quiet";
|
|
@@ -133,8 +126,8 @@ export function setCommandHandlerA27(duid, handler, logger, roborockService, cle
|
|
|
133
126
|
logger.notice('BehaviorA27-GoHome');
|
|
134
127
|
await roborockService.stopAndGoHome(duid);
|
|
135
128
|
});
|
|
136
|
-
handler.setCommandHandler('
|
|
137
|
-
logger.notice('BehaviorA27-
|
|
129
|
+
handler.setCommandHandler('playSoundToLocate', async () => {
|
|
130
|
+
logger.notice('BehaviorA27-playSoundToLocate');
|
|
138
131
|
await roborockService.playSoundToLocate(duid);
|
|
139
132
|
});
|
|
140
133
|
}
|
|
@@ -2,13 +2,6 @@ import { debugStringify } from 'matterbridge/logger';
|
|
|
2
2
|
import { BehaviorRoborock } from '../../BehaviorDeviceGeneric.js';
|
|
3
3
|
export class BehaviorA51 extends BehaviorRoborock {
|
|
4
4
|
}
|
|
5
|
-
export var BehaviorRoborockA51;
|
|
6
|
-
(function (BehaviorRoborockA51) {
|
|
7
|
-
class State {
|
|
8
|
-
device;
|
|
9
|
-
}
|
|
10
|
-
BehaviorRoborockA51.State = State;
|
|
11
|
-
})(BehaviorRoborockA51 || (BehaviorRoborockA51 = {}));
|
|
12
5
|
export var VacuumSuctionPowerA51;
|
|
13
6
|
(function (VacuumSuctionPowerA51) {
|
|
14
7
|
VacuumSuctionPowerA51[VacuumSuctionPowerA51["Quiet"] = 101] = "Quiet";
|
|
@@ -97,8 +90,8 @@ export function setCommandHandlerA51(duid, handler, logger, roborockService, cle
|
|
|
97
90
|
logger.notice('BehaviorA51-GoHome');
|
|
98
91
|
await roborockService.stopAndGoHome(duid);
|
|
99
92
|
});
|
|
100
|
-
handler.setCommandHandler('
|
|
101
|
-
logger.notice('BehaviorA51-
|
|
93
|
+
handler.setCommandHandler('playSoundToLocate', async () => {
|
|
94
|
+
logger.notice('BehaviorA51-playSoundToLocate');
|
|
102
95
|
await roborockService.playSoundToLocate(duid);
|
|
103
96
|
});
|
|
104
97
|
const getSettingFromCleanMode = (activity, cleanModeSettings) => {
|
|
@@ -2,13 +2,6 @@ import { debugStringify } from 'matterbridge/logger';
|
|
|
2
2
|
import { BehaviorRoborock } from '../../BehaviorDeviceGeneric.js';
|
|
3
3
|
export class DefaultBehavior extends BehaviorRoborock {
|
|
4
4
|
}
|
|
5
|
-
export var DefaultBehaviorRoborock;
|
|
6
|
-
(function (DefaultBehaviorRoborock) {
|
|
7
|
-
class State {
|
|
8
|
-
device;
|
|
9
|
-
}
|
|
10
|
-
DefaultBehaviorRoborock.State = State;
|
|
11
|
-
})(DefaultBehaviorRoborock || (DefaultBehaviorRoborock = {}));
|
|
12
5
|
export var VacuumSuctionPower;
|
|
13
6
|
(function (VacuumSuctionPower) {
|
|
14
7
|
VacuumSuctionPower[VacuumSuctionPower["Quiet"] = 101] = "Quiet";
|
|
@@ -97,8 +90,8 @@ export function setDefaultCommandHandler(duid, handler, logger, roborockService,
|
|
|
97
90
|
logger.notice('DefaultBehavior-GoHome');
|
|
98
91
|
await roborockService.stopAndGoHome(duid);
|
|
99
92
|
});
|
|
100
|
-
handler.setCommandHandler('
|
|
101
|
-
logger.notice('DefaultBehavior-
|
|
93
|
+
handler.setCommandHandler('playSoundToLocate', async () => {
|
|
94
|
+
logger.notice('DefaultBehavior-playSoundToLocate');
|
|
102
95
|
await roborockService.playSoundToLocate(duid);
|
|
103
96
|
});
|
|
104
97
|
const getSettingFromCleanMode = (activity, cleanModeSettings) => {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { debugStringify } from 'matterbridge/logger';
|
|
2
|
-
import { randomInt } from 'crypto';
|
|
2
|
+
import { randomInt } from 'node:crypto';
|
|
3
3
|
export function getSupportedScenes(scenes, log) {
|
|
4
4
|
log?.debug('getSupportedScenes', debugStringify(scenes));
|
|
5
5
|
if (!scenes || scenes.length === 0) {
|
|
@@ -10,7 +10,7 @@ export function getSupportedScenes(scenes, log) {
|
|
|
10
10
|
.filter((s) => s.enabled && s.id)
|
|
11
11
|
.map((scene) => {
|
|
12
12
|
return {
|
|
13
|
-
areaId: scene.id + randomInt(5000, 9000),
|
|
13
|
+
areaId: scene.id ?? 0 + randomInt(5000, 9000),
|
|
14
14
|
mapId: null,
|
|
15
15
|
areaInfo: {
|
|
16
16
|
locationInfo: {
|
package/dist/platform.js
CHANGED
|
@@ -38,7 +38,6 @@ export class RoborockMatterbridgePlatform extends MatterbridgeDynamicPlatform {
|
|
|
38
38
|
}
|
|
39
39
|
async onStart(reason) {
|
|
40
40
|
this.log.notice('onStart called with reason:', reason ?? 'none');
|
|
41
|
-
const self = this;
|
|
42
41
|
await this.ready;
|
|
43
42
|
await this.clearSelect();
|
|
44
43
|
if (this.config.username === undefined || this.config.password === undefined) {
|
|
@@ -46,24 +45,6 @@ export class RoborockMatterbridgePlatform extends MatterbridgeDynamicPlatform {
|
|
|
46
45
|
return;
|
|
47
46
|
}
|
|
48
47
|
const axiosInstance = axios.default ?? axios;
|
|
49
|
-
axiosInstance.interceptors.request.use((request) => {
|
|
50
|
-
self.log.debug('Axios Request:', {
|
|
51
|
-
method: request.method,
|
|
52
|
-
url: request.url,
|
|
53
|
-
data: request.data,
|
|
54
|
-
headers: request.headers,
|
|
55
|
-
});
|
|
56
|
-
return request;
|
|
57
|
-
});
|
|
58
|
-
axiosInstance.interceptors.response.use((response) => {
|
|
59
|
-
self.log.debug('Axios Response:', {
|
|
60
|
-
status: response.status,
|
|
61
|
-
data: response.data,
|
|
62
|
-
headers: response.headers,
|
|
63
|
-
url: response.config.url,
|
|
64
|
-
});
|
|
65
|
-
return response;
|
|
66
|
-
});
|
|
67
48
|
this.enableExperimentalFeature = this.config.enableExperimental;
|
|
68
49
|
if (this.enableExperimentalFeature?.enableExperimentalFeature && this.enableExperimentalFeature?.cleanModeSettings?.enableCleanModeMapping) {
|
|
69
50
|
this.cleanModeSettings = this.enableExperimentalFeature.cleanModeSettings;
|
|
@@ -144,7 +125,8 @@ export class RoborockMatterbridgePlatform extends MatterbridgeDynamicPlatform {
|
|
|
144
125
|
async onShutdown(reason) {
|
|
145
126
|
await super.onShutdown(reason);
|
|
146
127
|
this.log.notice('onShutdown called with reason:', reason ?? 'none');
|
|
147
|
-
|
|
128
|
+
if (this.rvcInterval)
|
|
129
|
+
clearInterval(this.rvcInterval);
|
|
148
130
|
if (this.roborockService)
|
|
149
131
|
this.roborockService.stopService();
|
|
150
132
|
if (this.config.unregisterOnShutdown === true)
|
package/dist/platformRunner.js
CHANGED
|
@@ -30,7 +30,7 @@ export class PlatformRunner {
|
|
|
30
30
|
if (platform.roborockService === undefined)
|
|
31
31
|
return;
|
|
32
32
|
const homeData = await platform.roborockService.getHomeDataForUpdating(platform.robot.rrHomeId);
|
|
33
|
-
await
|
|
33
|
+
await this.updateRobot(NotifyMessageTypes.HomeData, homeData);
|
|
34
34
|
}
|
|
35
35
|
async getRoomMapFromDevice(device) {
|
|
36
36
|
const platform = this.platform;
|
|
@@ -76,24 +76,25 @@ export class PlatformRunner {
|
|
|
76
76
|
}
|
|
77
77
|
switch (messageSource) {
|
|
78
78
|
case NotifyMessageTypes.ErrorOccurred: {
|
|
79
|
-
const
|
|
80
|
-
const operationalStateId = getOperationalErrorState(errorCode);
|
|
79
|
+
const message = messageData;
|
|
80
|
+
const operationalStateId = getOperationalErrorState(message.errorCode);
|
|
81
81
|
if (operationalStateId) {
|
|
82
|
-
platform.log.error(`Error occurred: ${errorCode}`);
|
|
82
|
+
platform.log.error(`Error occurred: ${message.errorCode}`);
|
|
83
83
|
platform.robot.updateAttribute(RvcOperationalState.Cluster.id, 'operationalState', operationalStateId, platform.log);
|
|
84
84
|
}
|
|
85
85
|
break;
|
|
86
86
|
}
|
|
87
87
|
case NotifyMessageTypes.BatteryUpdate: {
|
|
88
|
-
const
|
|
89
|
-
|
|
88
|
+
const message = messageData;
|
|
89
|
+
const batteryLevel = message.percentage;
|
|
90
|
+
if (batteryLevel) {
|
|
90
91
|
platform.robot.updateAttribute(PowerSource.Cluster.id, 'batPercentRemaining', batteryLevel * 2, platform.log);
|
|
91
92
|
platform.robot.updateAttribute(PowerSource.Cluster.id, 'batChargeLevel', getBatteryStatus(batteryLevel), platform.log);
|
|
92
93
|
}
|
|
93
94
|
break;
|
|
94
95
|
}
|
|
95
96
|
case NotifyMessageTypes.LocalMessage: {
|
|
96
|
-
const data = messageData
|
|
97
|
+
const data = messageData;
|
|
97
98
|
if (data) {
|
|
98
99
|
const state = state_to_matter_state(data.state);
|
|
99
100
|
if (state) {
|
|
@@ -123,10 +124,7 @@ export class PlatformRunner {
|
|
|
123
124
|
break;
|
|
124
125
|
}
|
|
125
126
|
case NotifyMessageTypes.CloudMessage: {
|
|
126
|
-
|
|
127
|
-
if (!data) {
|
|
128
|
-
data = messageData;
|
|
129
|
-
}
|
|
127
|
+
const data = messageData;
|
|
130
128
|
if (!data)
|
|
131
129
|
return;
|
|
132
130
|
this.handlerCloudMessage(data, duid, deviceData.model);
|
|
@@ -146,11 +144,11 @@ export class PlatformRunner {
|
|
|
146
144
|
const status = Number(data.dps[messageType]);
|
|
147
145
|
const matterState = state_to_matter_state(status);
|
|
148
146
|
if (matterState) {
|
|
149
|
-
platform.robot
|
|
147
|
+
platform.robot?.updateAttribute(RvcRunMode.Cluster.id, 'currentMode', getRunningMode(model, matterState), platform.log);
|
|
150
148
|
}
|
|
151
149
|
const operationalStateId = state_to_matter_operational_status(status);
|
|
152
150
|
if (operationalStateId) {
|
|
153
|
-
platform.robot
|
|
151
|
+
platform.robot?.updateAttribute(RvcOperationalState.Cluster.id, 'operationalState', operationalStateId, platform.log);
|
|
154
152
|
}
|
|
155
153
|
break;
|
|
156
154
|
}
|
|
@@ -160,9 +158,12 @@ export class PlatformRunner {
|
|
|
160
158
|
platform.log.debug('Ignore message:', debugStringify(data));
|
|
161
159
|
return;
|
|
162
160
|
}
|
|
163
|
-
|
|
161
|
+
let roboStatus;
|
|
162
|
+
if (Array.isArray(response.result) && response.result.length > 0) {
|
|
163
|
+
roboStatus = response.result[0];
|
|
164
|
+
}
|
|
164
165
|
if (roboStatus) {
|
|
165
|
-
const message = { duid: duid,
|
|
166
|
+
const message = { duid: duid, ...roboStatus };
|
|
166
167
|
platform.log.debug('rpc_response:', debugStringify(message));
|
|
167
168
|
await self.updateFromMQTTMessage(NotifyMessageTypes.LocalMessage, message, true);
|
|
168
169
|
}
|
|
@@ -172,7 +173,7 @@ export class PlatformRunner {
|
|
|
172
173
|
const fanPower = data.dps[messageType];
|
|
173
174
|
const currentCleanMode = getCurrentCleanModeFromFanPowerFunc(model)(fanPower);
|
|
174
175
|
if (currentCleanMode) {
|
|
175
|
-
platform.robot
|
|
176
|
+
platform.robot?.updateAttribute(RvcCleanMode.Cluster.id, 'currentMode', currentCleanMode, platform.log);
|
|
176
177
|
}
|
|
177
178
|
break;
|
|
178
179
|
}
|
|
@@ -180,7 +181,7 @@ export class PlatformRunner {
|
|
|
180
181
|
const water_box_mode = data.dps[messageType];
|
|
181
182
|
const currentCleanMode = getCurrentCleanModeFromWaterBoxModeFunc(model)(water_box_mode);
|
|
182
183
|
if (currentCleanMode) {
|
|
183
|
-
platform.robot
|
|
184
|
+
platform.robot?.updateAttribute(RvcCleanMode.Cluster.id, 'currentMode', currentCleanMode, platform.log);
|
|
184
185
|
}
|
|
185
186
|
break;
|
|
186
187
|
}
|
|
@@ -197,7 +198,10 @@ export class PlatformRunner {
|
|
|
197
198
|
}
|
|
198
199
|
async processAdditionalProps(robot, message) {
|
|
199
200
|
const platform = this.platform;
|
|
200
|
-
if (platform.
|
|
201
|
+
if (platform.enableExperimentalFeature &&
|
|
202
|
+
platform.enableExperimentalFeature.enableExperimentalFeature &&
|
|
203
|
+
platform.enableExperimentalFeature.advancedFeature.includeDockStationStatus &&
|
|
204
|
+
message.dss !== undefined) {
|
|
201
205
|
const dss = parseDockingStationStatus(message.dss);
|
|
202
206
|
this.platform.log.debug('DockingStationStatus:', debugStringify(dss));
|
|
203
207
|
const currentOperationState = robot.getAttribute(RvcOperationalState.Cluster.id, 'operationalState');
|
|
@@ -205,12 +209,15 @@ export class PlatformRunner {
|
|
|
205
209
|
robot.updateAttribute(RvcOperationalState.Cluster.id, 'operationalState', RvcOperationalState.OperationalState.Error, platform.log);
|
|
206
210
|
}
|
|
207
211
|
}
|
|
208
|
-
if (message.fan_power !== undefined) {
|
|
209
|
-
const fanPower = message.fan_power;
|
|
210
|
-
}
|
|
211
212
|
}
|
|
212
213
|
isStatusUpdate(result) {
|
|
213
|
-
return
|
|
214
|
+
return (Array.isArray(result) &&
|
|
215
|
+
result.length > 0 &&
|
|
216
|
+
typeof result[0] === 'object' &&
|
|
217
|
+
result[0] !== null &&
|
|
218
|
+
'msg_ver' in result[0] &&
|
|
219
|
+
result[0].msg_ver !== undefined &&
|
|
220
|
+
result[0].msg_ver !== null);
|
|
214
221
|
}
|
|
215
222
|
updateFromHomeData(homeData) {
|
|
216
223
|
const platform = this.platform;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -8,8 +8,8 @@ export var DeviceModel;
|
|
|
8
8
|
DeviceModel["S6_MAXV"] = "roborock.vacuum.a10";
|
|
9
9
|
DeviceModel["S6_PURE"] = "roborock.vacuum.a08";
|
|
10
10
|
DeviceModel["Q7"] = "roborock.vacuum.a40";
|
|
11
|
+
DeviceModel["Q7_PLUS"] = "roborock.vacuum.axx";
|
|
11
12
|
DeviceModel["Q7_MAX"] = "roborock.vacuum.a38";
|
|
12
|
-
DeviceModel["Q7_PLUS"] = "roborock.vacuum.a40";
|
|
13
13
|
DeviceModel["S7"] = "roborock.vacuum.a15";
|
|
14
14
|
DeviceModel["S7_MAXV"] = "roborock.vacuum.a27";
|
|
15
15
|
DeviceModel["S7_MAXV_ULTRA"] = "roborock.vacuum.a65";
|
|
@@ -22,7 +22,7 @@ export class AbstractClient {
|
|
|
22
22
|
}
|
|
23
23
|
async get(duid, request) {
|
|
24
24
|
return new Promise((resolve, reject) => {
|
|
25
|
-
this.syncMessageListener.waitFor(request.messageId, resolve, reject);
|
|
25
|
+
this.syncMessageListener.waitFor(request.messageId, (response) => resolve(response), reject);
|
|
26
26
|
this.send(duid, request);
|
|
27
27
|
});
|
|
28
28
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import mqtt from 'mqtt';
|
|
2
|
-
import
|
|
2
|
+
import * as CryptoUtils from '../../helper/cryptoHelper.js';
|
|
3
3
|
import { AbstractClient } from '../abstractClient.js';
|
|
4
4
|
import { debugStringify } from 'matterbridge/logger';
|
|
5
5
|
export class MQTTClient extends AbstractClient {
|
|
@@ -23,6 +23,7 @@ export class MQTTClient extends AbstractClient {
|
|
|
23
23
|
password: this.mqttPassword,
|
|
24
24
|
keepalive: 30,
|
|
25
25
|
log: (...args) => {
|
|
26
|
+
this.logger.debug('MQTTClient args:: ' + args[0]);
|
|
26
27
|
},
|
|
27
28
|
});
|
|
28
29
|
this.client.on('connect', this.onConnect.bind(this));
|
|
@@ -67,7 +68,7 @@ export class MQTTClient extends AbstractClient {
|
|
|
67
68
|
}
|
|
68
69
|
this.client.subscribe('rr/m/o/' + this.rriot.u + '/' + this.mqttUsername + '/#', this.onSubscribe.bind(this));
|
|
69
70
|
}
|
|
70
|
-
async onSubscribe(err
|
|
71
|
+
async onSubscribe(err) {
|
|
71
72
|
if (!err) {
|
|
72
73
|
return;
|
|
73
74
|
}
|
|
@@ -41,13 +41,13 @@ export class ClientRouter {
|
|
|
41
41
|
}
|
|
42
42
|
connect() {
|
|
43
43
|
this.mqttClient.connect();
|
|
44
|
-
this.localClients.forEach((client
|
|
44
|
+
this.localClients.forEach((client) => {
|
|
45
45
|
client.connect();
|
|
46
46
|
});
|
|
47
47
|
}
|
|
48
48
|
async disconnect() {
|
|
49
49
|
await this.mqttClient.disconnect();
|
|
50
|
-
this.localClients.forEach((client
|
|
50
|
+
this.localClients.forEach((client) => {
|
|
51
51
|
client.disconnect();
|
|
52
52
|
});
|
|
53
53
|
}
|
|
@@ -17,7 +17,8 @@ export class SyncMessageListener {
|
|
|
17
17
|
const dps = message.get(Protocol.rpc_response);
|
|
18
18
|
const messageId = dps.id;
|
|
19
19
|
const responseHandler = this.pending.get(messageId);
|
|
20
|
-
|
|
20
|
+
const result = dps.result;
|
|
21
|
+
if (result && result.length == 1 && result[0] == 'ok') {
|
|
21
22
|
return;
|
|
22
23
|
}
|
|
23
24
|
if (responseHandler) {
|
|
@@ -1,27 +1,23 @@
|
|
|
1
|
-
import crypto from 'crypto';
|
|
2
|
-
export
|
|
3
|
-
|
|
4
|
-
return crypto.createHash('md5').update(str).digest();
|
|
5
|
-
}
|
|
6
|
-
static md5hex(str) {
|
|
7
|
-
return crypto.createHash('md5').update(str).digest('hex');
|
|
8
|
-
}
|
|
9
|
-
static decryptECB(encrypted, aesKey) {
|
|
10
|
-
const decipher = crypto.createDecipheriv('aes-128-ecb', aesKey, null);
|
|
11
|
-
decipher.setAutoPadding(false);
|
|
12
|
-
let decrypted = decipher.update(encrypted, 'binary', 'utf8');
|
|
13
|
-
decrypted += decipher.final('utf8');
|
|
14
|
-
return this.removePadding(decrypted);
|
|
15
|
-
}
|
|
16
|
-
static removePadding(str) {
|
|
17
|
-
const paddingLength = str.charCodeAt(str.length - 1);
|
|
18
|
-
return str.slice(0, -paddingLength);
|
|
19
|
-
}
|
|
1
|
+
import crypto from 'node:crypto';
|
|
2
|
+
export function md5bin(str) {
|
|
3
|
+
return crypto.createHash('md5').update(str).digest();
|
|
20
4
|
}
|
|
21
|
-
export
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
5
|
+
export function md5hex(str) {
|
|
6
|
+
return crypto.createHash('md5').update(str).digest('hex');
|
|
7
|
+
}
|
|
8
|
+
export function decryptECB(encrypted, aesKey) {
|
|
9
|
+
const decipher = crypto.createDecipheriv('aes-128-ecb', aesKey, null);
|
|
10
|
+
decipher.setAutoPadding(false);
|
|
11
|
+
let decrypted = decipher.update(encrypted, 'binary', 'utf8');
|
|
12
|
+
decrypted += decipher.final('utf8');
|
|
13
|
+
return removePadding(decrypted);
|
|
14
|
+
}
|
|
15
|
+
function removePadding(str) {
|
|
16
|
+
const paddingLength = str.charCodeAt(str.length - 1);
|
|
17
|
+
return str.slice(0, -paddingLength);
|
|
18
|
+
}
|
|
19
|
+
export const SALT = 'TXdfu$jyZ#TZHsg4';
|
|
20
|
+
export function encodeTimestamp(timestamp) {
|
|
21
|
+
const hex = timestamp.toString(16).padStart(8, '0').split('');
|
|
22
|
+
return [5, 6, 3, 7, 1, 2, 0, 4].map((idx) => hex[idx]).join('');
|
|
27
23
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import crypto from 'crypto';
|
|
1
|
+
import crypto from 'node:crypto';
|
|
2
2
|
import CRC32 from 'crc-32';
|
|
3
3
|
import { Parser } from 'binary-parser';
|
|
4
4
|
import { ResponseMessage } from '../broadcast/model/responseMessage.js';
|
|
5
|
-
import
|
|
5
|
+
import * as CryptoUtils from './cryptoHelper.js';
|
|
6
6
|
import { Protocol } from '../broadcast/model/protocol.js';
|
|
7
7
|
export class MessageDeserializer {
|
|
8
8
|
context;
|
|
@@ -43,7 +43,7 @@ export class MessageDeserializer {
|
|
|
43
43
|
}
|
|
44
44
|
const data = this.messageParser.parse(message);
|
|
45
45
|
if (version == '1.0') {
|
|
46
|
-
const aesKey = CryptoUtils.md5bin(
|
|
46
|
+
const aesKey = CryptoUtils.md5bin(CryptoUtils.encodeTimestamp(data.timestamp) + localKey + CryptoUtils.SALT);
|
|
47
47
|
const decipher = crypto.createDecipheriv('aes-128-ecb', aesKey, null);
|
|
48
48
|
data.payload = Buffer.concat([decipher.update(data.payload), decipher.final()]);
|
|
49
49
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import
|
|
2
|
-
import crypto from 'crypto';
|
|
1
|
+
import * as CryptoUtils from './cryptoHelper.js';
|
|
2
|
+
import crypto from 'node:crypto';
|
|
3
3
|
import CRC32 from 'crc-32';
|
|
4
4
|
export class MessageSerializer {
|
|
5
5
|
context;
|
|
@@ -44,7 +44,7 @@ export class MessageSerializer {
|
|
|
44
44
|
const payload = JSON.stringify(payloadData);
|
|
45
45
|
let encrypted;
|
|
46
46
|
if (version == '1.0') {
|
|
47
|
-
const aesKey = CryptoUtils.md5bin(
|
|
47
|
+
const aesKey = CryptoUtils.md5bin(CryptoUtils.encodeTimestamp(payloadData.t) + localKey + CryptoUtils.SALT);
|
|
48
48
|
const cipher = crypto.createCipheriv('aes-128-ecb', aesKey, null);
|
|
49
49
|
encrypted = Buffer.concat([cipher.update(payload), cipher.final()]);
|
|
50
50
|
}
|
package/dist/roborockService.js
CHANGED
|
@@ -78,7 +78,8 @@ export default class RoborockService {
|
|
|
78
78
|
}
|
|
79
79
|
else if (rt.length === 1) {
|
|
80
80
|
this.logger.debug('RoborockService - startScene', debugStringify({ duid, rooms }));
|
|
81
|
-
|
|
81
|
+
await this.iotApi?.startScene(rt[0]);
|
|
82
|
+
return;
|
|
82
83
|
}
|
|
83
84
|
else if (rooms.length == supportedRooms.length || rooms.length === 0 || supportedRooms.length === 0) {
|
|
84
85
|
this.logger.debug('RoborockService - startGlobalClean');
|
package/dist/rvc.js
CHANGED
|
@@ -18,7 +18,7 @@ export class RoborockVacuumCleaner extends RoboticVacuumCleaner {
|
|
|
18
18
|
}
|
|
19
19
|
configurateHandler(behaviorHandler) {
|
|
20
20
|
this.addCommandHandler('identify', async ({ request: { identifyTime } }) => {
|
|
21
|
-
behaviorHandler.executeCommand('
|
|
21
|
+
behaviorHandler.executeCommand('playSoundToLocate', identifyTime);
|
|
22
22
|
});
|
|
23
23
|
this.addCommandHandler('selectAreas', async ({ request }) => {
|
|
24
24
|
this.log.info(`XXXXXX - Selecting areas: ${request.newAreas.join(', ')}`);
|