matterbridge-roborock-vacuum-plugin-regions 1.1.1-jb.1
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 +56 -0
- package/.github/workflows/coverage.yml +59 -0
- package/.github/workflows/publish.yml +37 -0
- package/.tarignore +5 -0
- package/CHANGELOG.md +62 -0
- package/LICENSE +202 -0
- package/README.md +135 -0
- package/README_CLEANMODE.md +29 -0
- package/README_DEV.md +75 -0
- package/README_REPORT_ISSUE.md +34 -0
- package/README_SUPPORTED.md +67 -0
- package/dist/behaviorFactory.js +26 -0
- package/dist/behaviors/BehaviorDeviceGeneric.js +22 -0
- package/dist/behaviors/roborock.vacuum/default/default.js +183 -0
- package/dist/behaviors/roborock.vacuum/default/initalData.js +143 -0
- package/dist/behaviors/roborock.vacuum/default/runtimes.js +21 -0
- package/dist/behaviors/roborock.vacuum/smart/initalData.js +18 -0
- package/dist/behaviors/roborock.vacuum/smart/runtimes.js +11 -0
- package/dist/behaviors/roborock.vacuum/smart/smart.js +119 -0
- package/dist/clientManager.js +17 -0
- package/dist/helper.js +76 -0
- package/dist/index.js +4 -0
- package/dist/initialData/getBatteryStatus.js +24 -0
- package/dist/initialData/getOperationalStates.js +82 -0
- package/dist/initialData/getSupportedAreas.js +120 -0
- package/dist/initialData/getSupportedCleanModes.js +17 -0
- package/dist/initialData/getSupportedRunModes.js +11 -0
- package/dist/initialData/getSupportedScenes.js +26 -0
- package/dist/initialData/index.js +6 -0
- package/dist/model/CloudMessageModel.js +1 -0
- package/dist/model/DockingStationStatus.js +26 -0
- package/dist/model/ExperimentalFeatureSetting.js +30 -0
- package/dist/model/RoomMap.js +19 -0
- package/dist/model/roomIndexMap.js +21 -0
- package/dist/notifyMessageTypes.js +9 -0
- package/dist/platform.js +312 -0
- package/dist/platformRunner.js +90 -0
- package/dist/roborockCommunication/RESTAPI/roborockAuthenticateApi.js +213 -0
- package/dist/roborockCommunication/RESTAPI/roborockIoTApi.js +95 -0
- package/dist/roborockCommunication/Zenum/additionalPropCode.js +4 -0
- package/dist/roborockCommunication/Zenum/authenticateResponseCode.js +7 -0
- package/dist/roborockCommunication/Zenum/dockType.js +4 -0
- package/dist/roborockCommunication/Zenum/operationStatusCode.js +44 -0
- package/dist/roborockCommunication/Zenum/vacuumAndDockErrorCode.js +68 -0
- package/dist/roborockCommunication/Zmodel/apiResponse.js +1 -0
- package/dist/roborockCommunication/Zmodel/authenticateFlowState.js +1 -0
- package/dist/roborockCommunication/Zmodel/authenticateResponse.js +1 -0
- package/dist/roborockCommunication/Zmodel/baseURL.js +1 -0
- package/dist/roborockCommunication/Zmodel/batteryMessage.js +1 -0
- package/dist/roborockCommunication/Zmodel/device.js +1 -0
- package/dist/roborockCommunication/Zmodel/deviceModel.js +28 -0
- package/dist/roborockCommunication/Zmodel/deviceSchema.js +1 -0
- package/dist/roborockCommunication/Zmodel/deviceStatus.js +22 -0
- package/dist/roborockCommunication/Zmodel/dockInfo.js +6 -0
- package/dist/roborockCommunication/Zmodel/home.js +1 -0
- package/dist/roborockCommunication/Zmodel/homeInfo.js +1 -0
- package/dist/roborockCommunication/Zmodel/map.js +1 -0
- package/dist/roborockCommunication/Zmodel/mapInfo.js +29 -0
- package/dist/roborockCommunication/Zmodel/messageResult.js +7 -0
- package/dist/roborockCommunication/Zmodel/multipleMap.js +1 -0
- package/dist/roborockCommunication/Zmodel/networkInfo.js +1 -0
- package/dist/roborockCommunication/Zmodel/product.js +1 -0
- package/dist/roborockCommunication/Zmodel/room.js +1 -0
- package/dist/roborockCommunication/Zmodel/roomInfo.js +22 -0
- package/dist/roborockCommunication/Zmodel/scene.js +16 -0
- package/dist/roborockCommunication/Zmodel/userData.js +1 -0
- package/dist/roborockCommunication/Zmodel/vacuumError.js +27 -0
- package/dist/roborockCommunication/broadcast/abstractClient.js +55 -0
- package/dist/roborockCommunication/broadcast/client/LocalNetworkClient.js +174 -0
- package/dist/roborockCommunication/broadcast/client/LocalNetworkUDPClient.js +129 -0
- package/dist/roborockCommunication/broadcast/client/MQTTClient.js +139 -0
- package/dist/roborockCommunication/broadcast/client.js +1 -0
- package/dist/roborockCommunication/broadcast/clientRouter.js +82 -0
- package/dist/roborockCommunication/broadcast/listener/abstractConnectionListener.js +1 -0
- package/dist/roborockCommunication/broadcast/listener/abstractMessageHandler.js +1 -0
- package/dist/roborockCommunication/broadcast/listener/abstractMessageListener.js +1 -0
- package/dist/roborockCommunication/broadcast/listener/implementation/chainedConnectionListener.js +26 -0
- package/dist/roborockCommunication/broadcast/listener/implementation/chainedMessageListener.js +11 -0
- package/dist/roborockCommunication/broadcast/listener/implementation/connectionStateListener.js +43 -0
- package/dist/roborockCommunication/broadcast/listener/implementation/generalSyncMessageListener.js +28 -0
- package/dist/roborockCommunication/broadcast/listener/implementation/simpleMessageListener.js +27 -0
- package/dist/roborockCommunication/broadcast/listener/implementation/syncMessageListener.js +33 -0
- package/dist/roborockCommunication/broadcast/listener/index.js +1 -0
- package/dist/roborockCommunication/broadcast/messageProcessor.js +148 -0
- package/dist/roborockCommunication/broadcast/model/contentMessage.js +1 -0
- package/dist/roborockCommunication/broadcast/model/dps.js +1 -0
- package/dist/roborockCommunication/broadcast/model/headerMessage.js +1 -0
- package/dist/roborockCommunication/broadcast/model/messageContext.js +37 -0
- package/dist/roborockCommunication/broadcast/model/protocol.js +28 -0
- package/dist/roborockCommunication/broadcast/model/requestMessage.js +38 -0
- package/dist/roborockCommunication/broadcast/model/responseMessage.js +14 -0
- package/dist/roborockCommunication/helper/chunkBuffer.js +17 -0
- package/dist/roborockCommunication/helper/cryptoHelper.js +23 -0
- package/dist/roborockCommunication/helper/messageDeserializer.js +98 -0
- package/dist/roborockCommunication/helper/messageSerializer.js +84 -0
- package/dist/roborockCommunication/helper/nameDecoder.js +66 -0
- package/dist/roborockCommunication/helper/sequence.js +16 -0
- package/dist/roborockCommunication/index.js +13 -0
- package/dist/roborockService.js +494 -0
- package/dist/runtimes/handleCloudMessage.js +110 -0
- package/dist/runtimes/handleHomeDataMessage.js +57 -0
- package/dist/runtimes/handleLocalMessage.js +169 -0
- package/dist/rvc.js +51 -0
- package/dist/settings.js +1 -0
- package/dist/share/function.js +93 -0
- package/dist/share/runtimeHelper.js +17 -0
- package/dist/tests/testData/mockData.js +359 -0
- package/eslint.config.js +80 -0
- package/jest.config.js +22 -0
- package/jest.setup.js +2 -0
- package/logo.png +0 -0
- package/matterbridge-roborock-vacuum-plugin.config.json +46 -0
- package/matterbridge-roborock-vacuum-plugin.schema.json +293 -0
- package/misc/status.md +119 -0
- package/package.json +111 -0
- package/prettier.config.js +49 -0
- package/screenshot/IMG_1.PNG +0 -0
- package/screenshot/IMG_2.PNG +0 -0
- package/screenshot/IMG_3.PNG +0 -0
- package/screenshot/IMG_4.PNG +0 -0
- package/screenshot/IMG_5.PNG +0 -0
- package/screenshot/IMG_6.PNG +0 -0
- package/screenshot/IMG_7.PNG +0 -0
- package/src/behaviorFactory.ts +41 -0
- package/src/behaviors/BehaviorDeviceGeneric.ts +31 -0
- package/src/behaviors/roborock.vacuum/default/default.ts +238 -0
- package/src/behaviors/roborock.vacuum/default/initalData.ts +152 -0
- package/src/behaviors/roborock.vacuum/default/runtimes.ts +23 -0
- package/src/behaviors/roborock.vacuum/smart/initalData.ts +20 -0
- package/src/behaviors/roborock.vacuum/smart/runtimes.ts +15 -0
- package/src/behaviors/roborock.vacuum/smart/smart.ts +159 -0
- package/src/clientManager.ts +23 -0
- package/src/helper.ts +97 -0
- package/src/index.ts +16 -0
- package/src/initialData/getBatteryStatus.ts +26 -0
- package/src/initialData/getOperationalStates.ts +94 -0
- package/src/initialData/getSupportedAreas.ts +162 -0
- package/src/initialData/getSupportedCleanModes.ts +22 -0
- package/src/initialData/getSupportedRunModes.ts +14 -0
- package/src/initialData/getSupportedScenes.ts +32 -0
- package/src/initialData/index.ts +6 -0
- package/src/model/CloudMessageModel.ts +11 -0
- package/src/model/DockingStationStatus.ts +41 -0
- package/src/model/ExperimentalFeatureSetting.ts +77 -0
- package/src/model/RoomMap.ts +38 -0
- package/src/model/roomIndexMap.ts +26 -0
- package/src/notifyMessageTypes.ts +8 -0
- package/src/platform.ts +424 -0
- package/src/platformRunner.ts +103 -0
- package/src/roborockCommunication/RESTAPI/roborockAuthenticateApi.ts +302 -0
- package/src/roborockCommunication/RESTAPI/roborockIoTApi.ts +107 -0
- package/src/roborockCommunication/Zenum/additionalPropCode.ts +3 -0
- package/src/roborockCommunication/Zenum/authenticateResponseCode.ts +6 -0
- package/src/roborockCommunication/Zenum/dockType.ts +3 -0
- package/src/roborockCommunication/Zenum/operationStatusCode.ts +43 -0
- package/src/roborockCommunication/Zenum/vacuumAndDockErrorCode.ts +68 -0
- package/src/roborockCommunication/Zmodel/apiResponse.ts +3 -0
- package/src/roborockCommunication/Zmodel/authenticateFlowState.ts +6 -0
- package/src/roborockCommunication/Zmodel/authenticateResponse.ts +5 -0
- package/src/roborockCommunication/Zmodel/baseURL.ts +5 -0
- package/src/roborockCommunication/Zmodel/batteryMessage.ts +16 -0
- package/src/roborockCommunication/Zmodel/device.ts +50 -0
- package/src/roborockCommunication/Zmodel/deviceModel.ts +27 -0
- package/src/roborockCommunication/Zmodel/deviceSchema.ts +8 -0
- package/src/roborockCommunication/Zmodel/deviceStatus.ts +30 -0
- package/src/roborockCommunication/Zmodel/dockInfo.ts +9 -0
- package/src/roborockCommunication/Zmodel/home.ts +13 -0
- package/src/roborockCommunication/Zmodel/homeInfo.ts +5 -0
- package/src/roborockCommunication/Zmodel/map.ts +20 -0
- package/src/roborockCommunication/Zmodel/mapInfo.ts +54 -0
- package/src/roborockCommunication/Zmodel/messageResult.ts +75 -0
- package/src/roborockCommunication/Zmodel/multipleMap.ts +8 -0
- package/src/roborockCommunication/Zmodel/networkInfo.ts +7 -0
- package/src/roborockCommunication/Zmodel/product.ts +9 -0
- package/src/roborockCommunication/Zmodel/room.ts +4 -0
- package/src/roborockCommunication/Zmodel/roomInfo.ts +30 -0
- package/src/roborockCommunication/Zmodel/scene.ts +44 -0
- package/src/roborockCommunication/Zmodel/userData.ts +26 -0
- package/src/roborockCommunication/Zmodel/vacuumError.ts +35 -0
- package/src/roborockCommunication/broadcast/abstractClient.ts +80 -0
- package/src/roborockCommunication/broadcast/client/LocalNetworkClient.ts +218 -0
- package/src/roborockCommunication/broadcast/client/LocalNetworkUDPClient.ts +157 -0
- package/src/roborockCommunication/broadcast/client/MQTTClient.ts +174 -0
- package/src/roborockCommunication/broadcast/client.ts +19 -0
- package/src/roborockCommunication/broadcast/clientRouter.ts +104 -0
- package/src/roborockCommunication/broadcast/listener/abstractConnectionListener.ts +6 -0
- package/src/roborockCommunication/broadcast/listener/abstractMessageHandler.ts +11 -0
- package/src/roborockCommunication/broadcast/listener/abstractMessageListener.ts +5 -0
- package/src/roborockCommunication/broadcast/listener/implementation/chainedConnectionListener.ts +33 -0
- package/src/roborockCommunication/broadcast/listener/implementation/chainedMessageListener.ts +16 -0
- package/src/roborockCommunication/broadcast/listener/implementation/connectionStateListener.ts +57 -0
- package/src/roborockCommunication/broadcast/listener/implementation/generalSyncMessageListener.ts +38 -0
- package/src/roborockCommunication/broadcast/listener/implementation/simpleMessageListener.ts +37 -0
- package/src/roborockCommunication/broadcast/listener/implementation/syncMessageListener.ts +50 -0
- package/src/roborockCommunication/broadcast/listener/index.ts +3 -0
- package/src/roborockCommunication/broadcast/messageProcessor.ts +184 -0
- package/src/roborockCommunication/broadcast/model/contentMessage.ts +5 -0
- package/src/roborockCommunication/broadcast/model/dps.ts +19 -0
- package/src/roborockCommunication/broadcast/model/headerMessage.ts +7 -0
- package/src/roborockCommunication/broadcast/model/messageContext.ts +53 -0
- package/src/roborockCommunication/broadcast/model/protocol.ts +28 -0
- package/src/roborockCommunication/broadcast/model/requestMessage.ts +51 -0
- package/src/roborockCommunication/broadcast/model/responseMessage.ts +19 -0
- package/src/roborockCommunication/helper/chunkBuffer.ts +18 -0
- package/src/roborockCommunication/helper/cryptoHelper.ts +30 -0
- package/src/roborockCommunication/helper/messageDeserializer.ts +119 -0
- package/src/roborockCommunication/helper/messageSerializer.ts +101 -0
- package/src/roborockCommunication/helper/nameDecoder.ts +78 -0
- package/src/roborockCommunication/helper/sequence.ts +18 -0
- package/src/roborockCommunication/index.ts +25 -0
- package/src/roborockService.ts +657 -0
- package/src/runtimes/handleCloudMessage.ts +134 -0
- package/src/runtimes/handleHomeDataMessage.ts +67 -0
- package/src/runtimes/handleLocalMessage.ts +209 -0
- package/src/rvc.ts +97 -0
- package/src/settings.ts +1 -0
- package/src/share/function.ts +103 -0
- package/src/share/runtimeHelper.ts +23 -0
- package/src/tests/behaviors/roborock.vacuum/default/default.test.ts +134 -0
- package/src/tests/behaviors/roborock.vacuum/smart/runtimes.test.ts +64 -0
- package/src/tests/behaviors/roborock.vacuum/smart/smart.test.ts +215 -0
- package/src/tests/helper.test.ts +162 -0
- package/src/tests/initialData/getSupportedAreas.test.ts +181 -0
- package/src/tests/model/DockingStationStatus.test.ts +39 -0
- package/src/tests/platformRunner.test.ts +188 -0
- package/src/tests/platformRunner2.test.ts +228 -0
- package/src/tests/platformRunner3.test.ts +46 -0
- package/src/tests/roborockCommunication/RESTAPI/roborockAuthenticateApi.test.ts +144 -0
- package/src/tests/roborockCommunication/RESTAPI/roborockIoTApi.test.ts +106 -0
- package/src/tests/roborockCommunication/broadcast/client/LocalNetworkClient.test.ts +189 -0
- package/src/tests/roborockCommunication/broadcast/client/MQTTClient.test.ts +208 -0
- package/src/tests/roborockCommunication/broadcast/clientRouter.test.ts +168 -0
- package/src/tests/roborockCommunication/broadcast/listener/implementation/chainedConnectionListener.test.ts +59 -0
- package/src/tests/roborockCommunication/broadcast/listener/implementation/chainedMessageListener.test.ts +46 -0
- package/src/tests/roborockCommunication/broadcast/listener/implementation/simpleMessageListener.test.ts +71 -0
- package/src/tests/roborockCommunication/broadcast/listener/implementation/syncMessageListener.test.ts +86 -0
- package/src/tests/roborockCommunication/broadcast/messageProcessor.test.ts +126 -0
- package/src/tests/roborockService.setSelectedAreas.test.ts +61 -0
- package/src/tests/roborockService.test.ts +517 -0
- package/src/tests/roborockService2.test.ts +69 -0
- package/src/tests/roborockService3.test.ts +133 -0
- package/src/tests/roborockService4.test.ts +76 -0
- package/src/tests/roborockService5.test.ts +79 -0
- package/src/tests/runtimes/handleCloudMessage.test.ts +200 -0
- package/src/tests/runtimes/handleHomeDataMessage.test.ts +54 -0
- package/src/tests/runtimes/handleLocalMessage.test.ts +227 -0
- package/src/tests/testData/mockData.ts +370 -0
- package/src/tests/testData/mockHomeData-a187.json +286 -0
- package/tsconfig.jest.json +21 -0
- package/tsconfig.json +37 -0
- package/tsconfig.production.json +19 -0
- package/tslint.json +9 -0
- package/web-for-testing/README.md +47 -0
- package/web-for-testing/nodemon.json +7 -0
- package/web-for-testing/package-lock.json +6600 -0
- package/web-for-testing/package.json +36 -0
- package/web-for-testing/src/app.ts +194 -0
- package/web-for-testing/tsconfig-ext.json +19 -0
- package/web-for-testing/tsconfig.json +23 -0
- package/web-for-testing/views/index.ejs +172 -0
- package/web-for-testing/watch.mjs +93 -0
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
import axios, { AxiosInstance, AxiosStatic } from 'axios';
|
|
2
|
+
import crypto from 'node:crypto';
|
|
3
|
+
import { AnsiLogger } from 'matterbridge/logger';
|
|
4
|
+
import { URLSearchParams } from 'node:url';
|
|
5
|
+
import { AuthenticateResponse } from '../Zmodel/authenticateResponse.js';
|
|
6
|
+
import { BaseUrl } from '../Zmodel/baseURL.js';
|
|
7
|
+
import { HomeInfo } from '../Zmodel/homeInfo.js';
|
|
8
|
+
import { UserData } from '../Zmodel/userData.js';
|
|
9
|
+
import { AuthenticateResponseCode } from '../Zenum/authenticateResponseCode.js';
|
|
10
|
+
|
|
11
|
+
export class RoborockAuthenticateApi {
|
|
12
|
+
private readonly logger: AnsiLogger;
|
|
13
|
+
private axiosFactory: AxiosStatic;
|
|
14
|
+
private deviceId: string;
|
|
15
|
+
private username?: string;
|
|
16
|
+
private authToken?: string;
|
|
17
|
+
// Cached values from base URL lookup for v4 login
|
|
18
|
+
private cachedBaseUrl?: string;
|
|
19
|
+
private cachedCountry?: string;
|
|
20
|
+
private cachedCountryCode?: string;
|
|
21
|
+
private configRegion?: string;
|
|
22
|
+
|
|
23
|
+
constructor(logger: AnsiLogger, axiosFactory: AxiosStatic = axios, deviceId?: string, configRegion?: string) {
|
|
24
|
+
this.deviceId = deviceId ?? crypto.randomUUID();
|
|
25
|
+
this.axiosFactory = axiosFactory;
|
|
26
|
+
this.logger = logger;
|
|
27
|
+
this.configRegion = configRegion;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
public async loginWithUserData(username: string, userData: UserData): Promise<UserData> {
|
|
31
|
+
this.loginWithAuthToken(username, userData.token);
|
|
32
|
+
return userData;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* @deprecated Use requestCodeV4 and loginWithCodeV4 instead
|
|
37
|
+
*/
|
|
38
|
+
public async loginWithPassword(username: string, password: string): Promise<UserData> {
|
|
39
|
+
const api = await this.getAPIFor(username);
|
|
40
|
+
const response = await api.post(
|
|
41
|
+
'api/v1/login',
|
|
42
|
+
new URLSearchParams({
|
|
43
|
+
username: username,
|
|
44
|
+
password: password,
|
|
45
|
+
needtwostepauth: 'false',
|
|
46
|
+
}).toString(),
|
|
47
|
+
);
|
|
48
|
+
return this.auth(username, response.data);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Request a verification code to be sent to the user's email
|
|
53
|
+
* @param email - The user's email address
|
|
54
|
+
* @throws Error if the account is not found, rate limited, or other API error
|
|
55
|
+
*/
|
|
56
|
+
public async requestCodeV4(email: string): Promise<void> {
|
|
57
|
+
const api = await this.getAPIFor(email);
|
|
58
|
+
const response = await api.post(
|
|
59
|
+
'api/v4/email/code/send',
|
|
60
|
+
new URLSearchParams({
|
|
61
|
+
email: email,
|
|
62
|
+
type: 'login',
|
|
63
|
+
platform: '',
|
|
64
|
+
}),
|
|
65
|
+
{
|
|
66
|
+
headers: {
|
|
67
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
const apiResponse: AuthenticateResponse<unknown> = response.data;
|
|
73
|
+
|
|
74
|
+
if (apiResponse.code === AuthenticateResponseCode.AccountNotFound) {
|
|
75
|
+
throw new Error(`Account not found for email: ${email}`);
|
|
76
|
+
}
|
|
77
|
+
if (apiResponse.code === AuthenticateResponseCode.RateLimited) {
|
|
78
|
+
throw new Error('Rate limited. Please wait before requesting another code.');
|
|
79
|
+
}
|
|
80
|
+
if (apiResponse.code !== AuthenticateResponseCode.Success && apiResponse.code !== undefined) {
|
|
81
|
+
throw new Error(`Failed to send verification code: ${apiResponse.msg} (code: ${apiResponse.code})`);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
this.logger.debug('Verification code requested successfully');
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Login with a verification code received via email
|
|
89
|
+
* @param email - The user's email address
|
|
90
|
+
* @param code - The 6-digit verification code
|
|
91
|
+
* @returns UserData on successful authentication
|
|
92
|
+
* @throws Error if the code is invalid, rate limited, or other API error
|
|
93
|
+
*/
|
|
94
|
+
public async loginWithCodeV4(email: string, code: string): Promise<UserData> {
|
|
95
|
+
const api = await this.getAPIFor(email);
|
|
96
|
+
|
|
97
|
+
// Generate x_mercy_ks (random 16-char alphanumeric string)
|
|
98
|
+
const xMercyKs = this.generateRandomString(16);
|
|
99
|
+
|
|
100
|
+
// Get signed key from API
|
|
101
|
+
const xMercyK = await this.signKeyV3(api, xMercyKs);
|
|
102
|
+
|
|
103
|
+
const response = await api.post('api/v4/auth/email/login/code', null, {
|
|
104
|
+
params: {
|
|
105
|
+
email: email,
|
|
106
|
+
code: code,
|
|
107
|
+
country: this.cachedCountry ?? '',
|
|
108
|
+
countryCode: this.cachedCountryCode ?? '',
|
|
109
|
+
majorVersion: '14',
|
|
110
|
+
minorVersion: '0',
|
|
111
|
+
},
|
|
112
|
+
headers: {
|
|
113
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
114
|
+
'x-mercy-ks': xMercyKs,
|
|
115
|
+
'x-mercy-k': xMercyK,
|
|
116
|
+
header_appversion: '4.54.02',
|
|
117
|
+
header_phonesystem: 'iOS',
|
|
118
|
+
header_phonemodel: 'iPhone16,1',
|
|
119
|
+
},
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
return this.authV4(email, response.data);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
public async getHomeDetails(): Promise<HomeInfo | undefined> {
|
|
126
|
+
if (!this.username || !this.authToken) {
|
|
127
|
+
return undefined;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const api = await this.getAPIFor(this.username);
|
|
131
|
+
const response = await api.get('api/v1/getHomeDetail');
|
|
132
|
+
|
|
133
|
+
const apiResponse: AuthenticateResponse<HomeInfo> = response.data;
|
|
134
|
+
if (!apiResponse.data) {
|
|
135
|
+
throw new Error('Failed to retrieve the home details');
|
|
136
|
+
}
|
|
137
|
+
return apiResponse.data;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Get cached country info from the last base URL lookup
|
|
142
|
+
*/
|
|
143
|
+
public getCachedCountryInfo(): { country?: string; countryCode?: string } {
|
|
144
|
+
return {
|
|
145
|
+
country: this.cachedCountry,
|
|
146
|
+
countryCode: this.cachedCountryCode,
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
private async getAPIFor(username: string): Promise<AxiosInstance> {
|
|
151
|
+
let baseUrl: string;
|
|
152
|
+
|
|
153
|
+
// Check if region is manually configured
|
|
154
|
+
if (this.configRegion) {
|
|
155
|
+
baseUrl = this.getBaseUrlForRegion(this.configRegion);
|
|
156
|
+
this.logger.info(`Using configured region: ${this.configRegion}`);
|
|
157
|
+
} else {
|
|
158
|
+
// Fall back to auto-detection
|
|
159
|
+
baseUrl = await this.getBaseUrl(username);
|
|
160
|
+
this.logger.info(`Auto-detected region from email`);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return this.apiForUser(username, baseUrl);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
private async getBaseUrl(username: string): Promise<string> {
|
|
167
|
+
if (this.cachedBaseUrl && this.username === username) {
|
|
168
|
+
return this.cachedBaseUrl;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const api = await this.apiForUser(username);
|
|
172
|
+
const response = await api.post(
|
|
173
|
+
'api/v1/getUrlByEmail',
|
|
174
|
+
new URLSearchParams({
|
|
175
|
+
email: username,
|
|
176
|
+
needtwostepauth: 'false',
|
|
177
|
+
}).toString(),
|
|
178
|
+
);
|
|
179
|
+
|
|
180
|
+
const apiResponse: AuthenticateResponse<BaseUrl> = response.data;
|
|
181
|
+
if (!apiResponse.data) {
|
|
182
|
+
throw new Error('Failed to retrieve base URL: ' + apiResponse.msg);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
this.cachedBaseUrl = apiResponse.data.url;
|
|
186
|
+
this.cachedCountry = apiResponse.data.country;
|
|
187
|
+
this.cachedCountryCode = apiResponse.data.countrycode;
|
|
188
|
+
this.username = username;
|
|
189
|
+
|
|
190
|
+
return apiResponse.data.url;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
private getBaseUrlForRegion(region: string): string {
|
|
194
|
+
const regionUrls: { [key: string]: string } = {
|
|
195
|
+
'eu': 'https://euiot.roborock.com',
|
|
196
|
+
'us': 'https://usiot.roborock.com',
|
|
197
|
+
'cn': 'https://iotcn.roborock.com',
|
|
198
|
+
};
|
|
199
|
+
return regionUrls[region.toLowerCase()] || regionUrls['us'];
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
private async apiForUser(username: string, baseUrl = 'https://usiot.roborock.com'): Promise<AxiosInstance> {
|
|
203
|
+
const instance = this.axiosFactory.create({
|
|
204
|
+
baseURL: baseUrl,
|
|
205
|
+
headers: {
|
|
206
|
+
header_clientid: crypto.createHash('md5').update(username).update(this.deviceId).digest('base64'),
|
|
207
|
+
Authorization: this.authToken,
|
|
208
|
+
header_clientlang: 'en',
|
|
209
|
+
},
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
instance.interceptors.request.use((config) => {
|
|
213
|
+
this.logger.debug('=== HTTP Request ===');
|
|
214
|
+
this.logger.debug(`URL: ${config.baseURL}/${config.url}`);
|
|
215
|
+
this.logger.debug(`Method: ${config.method?.toUpperCase()}`);
|
|
216
|
+
this.logger.debug(`Params: ${JSON.stringify(config.params)}`);
|
|
217
|
+
this.logger.debug(`Data: ${JSON.stringify(config.data)}`);
|
|
218
|
+
this.logger.debug(`Headers: ${JSON.stringify(config.headers)}`);
|
|
219
|
+
return config;
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
instance.interceptors.response.use(
|
|
223
|
+
(response) => {
|
|
224
|
+
this.logger.debug('=== HTTP Response ===');
|
|
225
|
+
this.logger.debug(`Status: ${response.status}`);
|
|
226
|
+
this.logger.debug(`Data: ${JSON.stringify(response.data)}`);
|
|
227
|
+
return response;
|
|
228
|
+
},
|
|
229
|
+
(error) => {
|
|
230
|
+
this.logger.debug('=== HTTP Error ===');
|
|
231
|
+
this.logger.debug(`Error: ${JSON.stringify(error.response?.data ?? error.message)}`);
|
|
232
|
+
return Promise.reject(error);
|
|
233
|
+
},
|
|
234
|
+
);
|
|
235
|
+
|
|
236
|
+
return instance;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
private auth(username: string, response: AuthenticateResponse<UserData>): UserData {
|
|
240
|
+
const userdata = response.data;
|
|
241
|
+
if (!userdata || !userdata.token) {
|
|
242
|
+
throw new Error('Authentication failed: ' + response.msg + ' code: ' + response.code);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
this.loginWithAuthToken(username, userdata.token);
|
|
246
|
+
return userdata;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Handle v4 authentication response with specific error code handling
|
|
251
|
+
*/
|
|
252
|
+
private authV4(email: string, response: AuthenticateResponse<UserData>): UserData {
|
|
253
|
+
if (response.code === AuthenticateResponseCode.InvalidCode) {
|
|
254
|
+
throw new Error('Invalid verification code. Please check and try again.');
|
|
255
|
+
}
|
|
256
|
+
if (response.code === AuthenticateResponseCode.RateLimited) {
|
|
257
|
+
throw new Error('Rate limited. Please wait before trying again.');
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
const userdata = response.data;
|
|
261
|
+
if (!userdata || !userdata.token) {
|
|
262
|
+
throw new Error('Authentication failed: ' + response.msg + ' code: ' + response.code);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
this.loginWithAuthToken(email, userdata.token);
|
|
266
|
+
return userdata;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
private loginWithAuthToken(username: string, token: string): void {
|
|
270
|
+
this.username = username;
|
|
271
|
+
this.authToken = token;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Generate a random alphanumeric string of specified length
|
|
276
|
+
*/
|
|
277
|
+
private generateRandomString(length: number): string {
|
|
278
|
+
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
|
279
|
+
let result = '';
|
|
280
|
+
const randomBytes = crypto.randomBytes(length);
|
|
281
|
+
for (let i = 0; i < length; i++) {
|
|
282
|
+
result += chars[randomBytes[i] % chars.length];
|
|
283
|
+
}
|
|
284
|
+
return result;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* Sign a key using the v3 API endpoint
|
|
289
|
+
*/
|
|
290
|
+
private async signKeyV3(api: AxiosInstance, s: string): Promise<string> {
|
|
291
|
+
const response = await api.post('api/v3/key/sign', null, {
|
|
292
|
+
params: { s },
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
const apiResponse: AuthenticateResponse<{ k: string }> = response.data;
|
|
296
|
+
if (!apiResponse.data?.k) {
|
|
297
|
+
throw new Error('Failed to sign key: ' + apiResponse.msg);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
return apiResponse.data.k;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import axios, { AxiosInstance } from 'axios';
|
|
2
|
+
import crypto from 'node:crypto';
|
|
3
|
+
import { AnsiLogger, debugStringify } from 'matterbridge/logger';
|
|
4
|
+
import { ApiResponse } from '../Zmodel/apiResponse.js';
|
|
5
|
+
import { Home } from '../Zmodel/home.js';
|
|
6
|
+
import { UserData } from '../Zmodel/userData.js';
|
|
7
|
+
import { Scene } from '../Zmodel/scene.js';
|
|
8
|
+
|
|
9
|
+
export class RoborockIoTApi {
|
|
10
|
+
logger: AnsiLogger;
|
|
11
|
+
private readonly api: AxiosInstance;
|
|
12
|
+
|
|
13
|
+
constructor(userdata: UserData, logger: AnsiLogger) {
|
|
14
|
+
this.logger = logger;
|
|
15
|
+
|
|
16
|
+
this.api = axios.create({ baseURL: userdata.rriot.r.a });
|
|
17
|
+
this.api.interceptors.request.use((config) => {
|
|
18
|
+
try {
|
|
19
|
+
const timestamp = Math.floor(Date.now() / 1000);
|
|
20
|
+
const nonce = crypto
|
|
21
|
+
.randomBytes(6)
|
|
22
|
+
.toString('base64')
|
|
23
|
+
.substring(0, 6)
|
|
24
|
+
.replace(/[+/]/g, (m) => (m === '+' ? 'X' : 'Y'));
|
|
25
|
+
const url = this.api ? new URL(this.api.getUri(config)).pathname : '';
|
|
26
|
+
const data = [userdata.rriot.u, userdata.rriot.s, nonce, timestamp, crypto.createHash('md5').update(url).digest('hex'), '', ''].join(':');
|
|
27
|
+
const hmac = crypto.createHmac('sha256', userdata.rriot.h).update(data).digest('base64');
|
|
28
|
+
config.headers['Authorization'] = `Hawk id="${userdata.rriot.u}", s="${userdata.rriot.s}", ts="${timestamp}", nonce="${nonce}", mac="${hmac}"`;
|
|
29
|
+
} catch (error) {
|
|
30
|
+
this.logger.error(`Failed to initialize RESTAPI ${error ? debugStringify(error) : 'undefined'}`);
|
|
31
|
+
}
|
|
32
|
+
return config;
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
public async getHome(homeId: number): Promise<Home | undefined> {
|
|
37
|
+
const result = await this.api.get(`user/homes/${homeId}`);
|
|
38
|
+
|
|
39
|
+
const apiResponse: ApiResponse<Home> = result.data;
|
|
40
|
+
if (apiResponse.result) {
|
|
41
|
+
return apiResponse.result;
|
|
42
|
+
} else {
|
|
43
|
+
this.logger.error('Failed to retrieve the home data');
|
|
44
|
+
return undefined;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
public async getHomev2(homeId: number): Promise<Home | undefined> {
|
|
49
|
+
const result = await this.api.get('v2/user/homes/' + homeId);
|
|
50
|
+
|
|
51
|
+
const apiResponse: ApiResponse<Home> = result.data;
|
|
52
|
+
if (apiResponse.result) {
|
|
53
|
+
return apiResponse.result;
|
|
54
|
+
} else {
|
|
55
|
+
this.logger.error('Failed to retrieve the home data');
|
|
56
|
+
return undefined;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
public async getHomev3(homeId: number): Promise<Home | undefined> {
|
|
61
|
+
const result = await this.api.get('v3/user/homes/' + homeId); // can be v3 also
|
|
62
|
+
|
|
63
|
+
const apiResponse: ApiResponse<Home> = result.data;
|
|
64
|
+
if (apiResponse.result) {
|
|
65
|
+
return apiResponse.result;
|
|
66
|
+
} else {
|
|
67
|
+
this.logger.error('Failed to retrieve the home data');
|
|
68
|
+
return undefined;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
public async getScenes(homeId: number): Promise<Scene[] | undefined> {
|
|
73
|
+
const result = await this.api.get('user/scene/home/' + homeId);
|
|
74
|
+
|
|
75
|
+
const apiResponse: ApiResponse<Scene[]> = result.data;
|
|
76
|
+
if (apiResponse.result) {
|
|
77
|
+
return apiResponse.result;
|
|
78
|
+
} else {
|
|
79
|
+
this.logger.error('Failed to retrieve scene');
|
|
80
|
+
return undefined;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
public async startScene(sceneId: number): Promise<unknown> {
|
|
85
|
+
const result = await this.api.post(`user/scene/${sceneId}/execute`);
|
|
86
|
+
const apiResponse: ApiResponse<unknown> = result.data;
|
|
87
|
+
|
|
88
|
+
if (apiResponse.result) {
|
|
89
|
+
return apiResponse.result;
|
|
90
|
+
} else {
|
|
91
|
+
this.logger.error('Failed to execute scene');
|
|
92
|
+
return undefined;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
public async getCustom(url: string): Promise<unknown> {
|
|
97
|
+
const result = await this.api.get(url);
|
|
98
|
+
const apiResponse: ApiResponse<unknown> = result.data;
|
|
99
|
+
|
|
100
|
+
if (apiResponse.result) {
|
|
101
|
+
return apiResponse.result;
|
|
102
|
+
} else {
|
|
103
|
+
this.logger.error('Failed to execute scene');
|
|
104
|
+
return undefined;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
export enum OperationStatusCode {
|
|
2
|
+
Unknown = 0,
|
|
3
|
+
Initiating = 1,
|
|
4
|
+
Sleeping = 2, // or ChargerDisconnected
|
|
5
|
+
Idle = 3,
|
|
6
|
+
RemoteControl = 4,
|
|
7
|
+
Cleaning = 5,
|
|
8
|
+
ReturningDock = 6,
|
|
9
|
+
ManualMode = 7,
|
|
10
|
+
Charging = 8,
|
|
11
|
+
ChargingError = 9,
|
|
12
|
+
Paused = 10,
|
|
13
|
+
SpotCleaning = 11,
|
|
14
|
+
InError = 12,
|
|
15
|
+
ShuttingDown = 13,
|
|
16
|
+
Updating = 14,
|
|
17
|
+
ReturnToDock = 15,
|
|
18
|
+
GoTo = 16,
|
|
19
|
+
ZoneClean = 17,
|
|
20
|
+
RoomClean = 18,
|
|
21
|
+
EmptyingDustContainer = 22,
|
|
22
|
+
WashingTheMop = 23,
|
|
23
|
+
WashingTheMop2 = 25,
|
|
24
|
+
GoingToWashTheMop = 26,
|
|
25
|
+
InCall = 28,
|
|
26
|
+
Mapping = 29,
|
|
27
|
+
SOMETHING_NEED_TO_FIGUREOUT = 30,
|
|
28
|
+
Patrol = 32,
|
|
29
|
+
FullyCharged = 100,
|
|
30
|
+
DeviceOffline = 101,
|
|
31
|
+
Locked = 103,
|
|
32
|
+
AirDryingStopping = 202,
|
|
33
|
+
RobotStatusMopping = 6301,
|
|
34
|
+
CleanMopCleaning = 6302,
|
|
35
|
+
CleanMopMopping = 6303,
|
|
36
|
+
RoomMopping = 6304,
|
|
37
|
+
RoomCleanMopCleaning = 6305,
|
|
38
|
+
RoomCleanMopMopping = 6306,
|
|
39
|
+
ZoneMopping = 6307,
|
|
40
|
+
ZoneCleanMopCleaning = 6308,
|
|
41
|
+
ZoneCleanMopMopping = 6309,
|
|
42
|
+
BackToDockWashingDuster = 6310,
|
|
43
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
export enum VacuumErrorCode {
|
|
2
|
+
None = 0,
|
|
3
|
+
LidarBlocked = 1,
|
|
4
|
+
BumperStuck = 2,
|
|
5
|
+
WheelsSuspended = 3,
|
|
6
|
+
CliffSensorError = 4,
|
|
7
|
+
MainBrushJammed = 5,
|
|
8
|
+
SideBrushJammed = 6,
|
|
9
|
+
WheelsJammed = 7,
|
|
10
|
+
RobotTrapped = 8,
|
|
11
|
+
NoDustbin = 9,
|
|
12
|
+
StrainerError = 10,
|
|
13
|
+
CompassError = 11,
|
|
14
|
+
LowBattery = 12,
|
|
15
|
+
ChargingError = 13,
|
|
16
|
+
BatteryError = 14,
|
|
17
|
+
WallSensorDirty = 15,
|
|
18
|
+
RobotTilted = 16,
|
|
19
|
+
SideBrushError = 17,
|
|
20
|
+
FanError = 18,
|
|
21
|
+
DockNotConnectedToPower = 19,
|
|
22
|
+
OpticalFlowSensorDirt = 20,
|
|
23
|
+
VerticalBumperPressed = 21,
|
|
24
|
+
DockLocatorError = 22,
|
|
25
|
+
ReturnToDockFail = 23,
|
|
26
|
+
NogoZoneDetected = 24,
|
|
27
|
+
CameraError = 25,
|
|
28
|
+
WallSensorError = 26,
|
|
29
|
+
VibrariseJammed = 27,
|
|
30
|
+
RobotOnCarpet = 28,
|
|
31
|
+
FilterBlocked = 29,
|
|
32
|
+
InvisibleWallDetected = 30,
|
|
33
|
+
CannotCrossCarpet = 31,
|
|
34
|
+
InternalError = 32,
|
|
35
|
+
CleanAutoEmptyDock = 34,
|
|
36
|
+
AutoEmptyDockVoltage = 35,
|
|
37
|
+
MoppingRollerJammed = 36, // Wash roller may be jammed
|
|
38
|
+
MoppingRollerNotLowered = 37, // wash roller not lowered properly
|
|
39
|
+
ClearWaterBoxHoare = 38,
|
|
40
|
+
DirtyWaterBoxHoare = 39,
|
|
41
|
+
SinkStrainerHoare = 40,
|
|
42
|
+
ClearWaterTankEmpty = 41,
|
|
43
|
+
ClearBrushInstalledProperly = 42, // Check that the water filter has been correctly installed
|
|
44
|
+
ClearBrushPositioningError = 43,
|
|
45
|
+
FilterScreenException = 44, // Clean the dock water filter
|
|
46
|
+
MoppingRollerJammed2 = 45, // Wash roller may be jammed
|
|
47
|
+
UpWaterException = 48,
|
|
48
|
+
DrainWaterException = 49,
|
|
49
|
+
TemperatureProtection = 51,
|
|
50
|
+
CleanCarouselException = 52,
|
|
51
|
+
CleanCarouselWaterFull = 53,
|
|
52
|
+
WaterCarriageDrop = 54,
|
|
53
|
+
CheckCleanCarouse = 55,
|
|
54
|
+
AudioError = 56,
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export enum DockErrorCode {
|
|
58
|
+
None = 0,
|
|
59
|
+
DuctBlockage = 34, // Duct blockage detected
|
|
60
|
+
WaterEmpty = 38, // Clean water tank empty
|
|
61
|
+
WasteWaterTankFull = 39, // Waste water tank full
|
|
62
|
+
MaintenanceBrushJammed = 42, // Maintenance brush jammed
|
|
63
|
+
DirtyTankLatchOpen = 44, // Dirty tank latch open
|
|
64
|
+
NoDustbin = 46, // No dustbin detected
|
|
65
|
+
CleaningTankFullOrBlocked = 53, // Cleaning tank full or blocked
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export const SUPPORTED_ATTACHMENTS = ['WATERTANK', 'MOP'];
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { DeviceStatus, VacuumErrorCode } from '../index.js';
|
|
2
|
+
import { CloudMessageResult } from './messageResult.js';
|
|
3
|
+
|
|
4
|
+
export interface BatteryMessage {
|
|
5
|
+
duid: string;
|
|
6
|
+
percentage: number;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface DeviceErrorMessage {
|
|
10
|
+
duid: string;
|
|
11
|
+
errorCode: VacuumErrorCode;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface DeviceStatusNotify extends DeviceStatus, CloudMessageResult {
|
|
15
|
+
duid: string;
|
|
16
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { DeviceSchema } from './deviceSchema.js';
|
|
2
|
+
import { Room } from './room.js';
|
|
3
|
+
import { Scene } from './scene.js';
|
|
4
|
+
import { UserData } from './userData.js';
|
|
5
|
+
|
|
6
|
+
export interface DeviceData {
|
|
7
|
+
id: string;
|
|
8
|
+
firmwareVersion: string;
|
|
9
|
+
serialNumber: string;
|
|
10
|
+
model: string;
|
|
11
|
+
category: string;
|
|
12
|
+
batteryLevel: number;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
interface DeviceInformation {
|
|
16
|
+
userData: UserData;
|
|
17
|
+
localKey: string;
|
|
18
|
+
pv: string;
|
|
19
|
+
model?: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface Device {
|
|
23
|
+
duid: string;
|
|
24
|
+
name: string;
|
|
25
|
+
sn: string;
|
|
26
|
+
serialNumber: string;
|
|
27
|
+
|
|
28
|
+
featureSet?: string;
|
|
29
|
+
newFeatureSet?: string;
|
|
30
|
+
silentOtaSwitch?: boolean;
|
|
31
|
+
|
|
32
|
+
activeTime: number;
|
|
33
|
+
createTime: number;
|
|
34
|
+
localKey: string;
|
|
35
|
+
|
|
36
|
+
/** The protocol version of the robot. */
|
|
37
|
+
pv: string;
|
|
38
|
+
online: boolean;
|
|
39
|
+
productId: string;
|
|
40
|
+
rrHomeId: number;
|
|
41
|
+
/** The firmware version of the robot. */
|
|
42
|
+
fv: string;
|
|
43
|
+
|
|
44
|
+
deviceStatus: Record<string, number>;
|
|
45
|
+
rooms: Room[];
|
|
46
|
+
schema: DeviceSchema[];
|
|
47
|
+
data: DeviceData;
|
|
48
|
+
store?: DeviceInformation;
|
|
49
|
+
scenes?: Scene[];
|
|
50
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export enum DeviceModel {
|
|
2
|
+
Q5 = 'roborock.vacuum.a34',
|
|
3
|
+
Q5_PRO = 'roborock.vacuum.a72',
|
|
4
|
+
S5 = 'roborock.vacuum.s5',
|
|
5
|
+
S5_MAX = 'roborock.vacuum.s5e',
|
|
6
|
+
S6 = 'roborock.vacuum.s6',
|
|
7
|
+
S6_MAXV = 'roborock.vacuum.a10',
|
|
8
|
+
S6_PURE = 'roborock.vacuum.a08',
|
|
9
|
+
Q7 = 'roborock.vacuum.a40',
|
|
10
|
+
Q7_PLUS = 'roborock.vacuum.axx', // TODO
|
|
11
|
+
Q7_MAX = 'roborock.vacuum.a38',
|
|
12
|
+
S7 = 'roborock.vacuum.a15',
|
|
13
|
+
S7_MAXV = 'roborock.vacuum.a27',
|
|
14
|
+
S7_MAXV_ULTRA = 'roborock.vacuum.a65',
|
|
15
|
+
S7_PRO_ULTRA = 'roborock.vacuum.a62',
|
|
16
|
+
Q8_MAX = 'roborock.vacuum.a73',
|
|
17
|
+
S8 = 'roborock.vacuum.a51',
|
|
18
|
+
S8_PRO_ULTRA = 'roborock.vacuum.a70',
|
|
19
|
+
S8_MAXV_ULTRA = 'roborock.vacuum.a97',
|
|
20
|
+
QREVO_MASTER = 'roborock.vacuum.a117',
|
|
21
|
+
QREVO_CURV = 'roborock.vacuum.a135',
|
|
22
|
+
QREVO_S = 'roborock.vacuum.a104',
|
|
23
|
+
QREVO_PRO = 'roborock.vacuum.a101',
|
|
24
|
+
QREVO_MAXV = 'roborock.vacuum.a87',
|
|
25
|
+
QREVO_EDGE_5V1 = 'roborock.vacuum.a187',
|
|
26
|
+
QREVO_EDGE_5AE = 'roborock.vacuum.xxxx',
|
|
27
|
+
}
|