matterbridge-roborock-vacuum-plugin 1.0.5
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 +34 -0
- package/.tarignore +5 -0
- package/LICENSE +202 -0
- package/README.md +34 -0
- package/README_DEV.md +69 -0
- package/bmc-button.svg +22 -0
- package/dist/appliances.js +6 -0
- package/dist/behaviorFactory.js +18 -0
- package/dist/behaviors/BehaviorDeviceGeneric.js +31 -0
- package/dist/behaviors/roborock.vacuum/QREVO_EDGE_5V1/a187.js +94 -0
- package/dist/behaviors/roborock.vacuum/QREVO_EDGE_5V1/initalData.js +81 -0
- package/dist/behaviors/roborock.vacuum/QREVO_EDGE_5V1/runtimes.js +39 -0
- package/dist/behaviors/roborock.vacuum/default/default.js +58 -0
- package/dist/behaviors/roborock.vacuum/default/initalData.js +66 -0
- package/dist/clientManager.js +17 -0
- package/dist/extensions/AxiosStaticExtensions.js +22 -0
- package/dist/extensions/index.js +1 -0
- package/dist/helper.js +16 -0
- package/dist/index.js +4 -0
- package/dist/initialData/getBatteryStatus.js +24 -0
- package/dist/initialData/getOperationalStates.js +22 -0
- package/dist/initialData/getSupportedAreas.js +69 -0
- package/dist/initialData/getSupportedCleanModes.js +11 -0
- package/dist/initialData/getSupportedRunModes.js +18 -0
- package/dist/initialData/index.js +5 -0
- package/dist/model/CloudMessageModel.js +1 -0
- package/dist/model/DockingStationStatus.js +24 -0
- package/dist/model/RoomMap.js +18 -0
- package/dist/notifyMessageTypes.js +9 -0
- package/dist/platform.js +146 -0
- package/dist/platformRunner.js +249 -0
- package/dist/roborockCommunication/RESTAPI/roborockAuthenticateApi.js +73 -0
- package/dist/roborockCommunication/RESTAPI/roborockIoTApi.js +61 -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/authenticateResponse.js +1 -0
- package/dist/roborockCommunication/Zmodel/baseURL.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/messageResult.js +7 -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/userData.js +1 -0
- package/dist/roborockCommunication/Zmodel/vacuumError.js +27 -0
- package/dist/roborockCommunication/broadcast/abstractClient.js +41 -0
- package/dist/roborockCommunication/broadcast/client/LocalNetworkClient.js +148 -0
- package/dist/roborockCommunication/broadcast/client/MQTTClient.js +101 -0
- package/dist/roborockCommunication/broadcast/client.js +1 -0
- package/dist/roborockCommunication/broadcast/clientRouter.js +79 -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 +21 -0
- package/dist/roborockCommunication/broadcast/listener/implementation/chainedMessageListener.js +11 -0
- package/dist/roborockCommunication/broadcast/listener/implementation/simpleMessageListener.js +27 -0
- package/dist/roborockCommunication/broadcast/listener/implementation/syncMessageListener.js +35 -0
- package/dist/roborockCommunication/broadcast/listener/index.js +1 -0
- package/dist/roborockCommunication/broadcast/messageProcessor.js +79 -0
- package/dist/roborockCommunication/broadcast/model/dps.js +1 -0
- package/dist/roborockCommunication/broadcast/model/messageContext.js +26 -0
- package/dist/roborockCommunication/broadcast/model/protocol.js +19 -0
- package/dist/roborockCommunication/broadcast/model/requestMessage.js +33 -0
- package/dist/roborockCommunication/broadcast/model/responseMessage.js +14 -0
- package/dist/roborockCommunication/helper/chunkBuffer.js +17 -0
- package/dist/roborockCommunication/helper/cryptoHelper.js +27 -0
- package/dist/roborockCommunication/helper/messageDeserializer.js +74 -0
- package/dist/roborockCommunication/helper/messageSerializer.js +70 -0
- package/dist/roborockCommunication/helper/nameDecoder.js +66 -0
- package/dist/roborockCommunication/helper/sequence.js +16 -0
- package/dist/roborockCommunication/index.js +9 -0
- package/dist/roborockService.js +300 -0
- package/dist/rvc.js +39 -0
- package/dist/settings.js +1 -0
- package/dist/share/function.js +96 -0
- package/dist/share/runtimeHelper.js +29 -0
- package/eslint.config.js +76 -0
- package/matterbridge-roborock-vacuum-plugin.config.json +11 -0
- package/matterbridge-roborock-vacuum-plugin.schema.json +57 -0
- package/package.json +43 -0
- package/prettier.config.js +49 -0
- package/src/appliances.ts +15 -0
- package/src/behaviorFactory.ts +24 -0
- package/src/behaviors/BehaviorDeviceGeneric.ts +39 -0
- package/src/behaviors/roborock.vacuum/QREVO_EDGE_5V1/a187.ts +117 -0
- package/src/behaviors/roborock.vacuum/QREVO_EDGE_5V1/initalData.ts +85 -0
- package/src/behaviors/roborock.vacuum/QREVO_EDGE_5V1/runtimes.ts +29 -0
- package/src/behaviors/roborock.vacuum/default/default.ts +78 -0
- package/src/behaviors/roborock.vacuum/default/initalData.ts +69 -0
- package/src/clientManager.ts +23 -0
- package/src/extensions/AxiosStaticExtensions.ts +35 -0
- package/src/extensions/index.ts +1 -0
- package/src/helper.ts +22 -0
- package/src/index.ts +16 -0
- package/src/initialData/getBatteryStatus.ts +26 -0
- package/src/initialData/getOperationalStates.ts +24 -0
- package/src/initialData/getSupportedAreas.ts +95 -0
- package/src/initialData/getSupportedCleanModes.ts +13 -0
- package/src/initialData/getSupportedRunModes.ts +21 -0
- package/src/initialData/index.ts +5 -0
- package/src/model/CloudMessageModel.ts +13 -0
- package/src/model/DockingStationStatus.ts +37 -0
- package/src/model/RoomMap.ts +44 -0
- package/src/notifyMessageTypes.ts +8 -0
- package/src/platform.ts +192 -0
- package/src/platformRunner.ts +292 -0
- package/src/roborockCommunication/RESTAPI/roborockAuthenticateApi.ts +98 -0
- package/src/roborockCommunication/RESTAPI/roborockIoTApi.ts +70 -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/authenticateResponse.ts +5 -0
- package/src/roborockCommunication/Zmodel/baseURL.ts +5 -0
- package/src/roborockCommunication/Zmodel/device.ts +39 -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/messageResult.ts +72 -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 +25 -0
- package/src/roborockCommunication/Zmodel/userData.ts +23 -0
- package/src/roborockCommunication/Zmodel/vacuumError.ts +35 -0
- package/src/roborockCommunication/broadcast/abstractClient.ts +61 -0
- package/src/roborockCommunication/broadcast/client/LocalNetworkClient.ts +177 -0
- package/src/roborockCommunication/broadcast/client/MQTTClient.ts +129 -0
- package/src/roborockCommunication/broadcast/client.ts +19 -0
- package/src/roborockCommunication/broadcast/clientRouter.ts +100 -0
- package/src/roborockCommunication/broadcast/listener/abstractConnectionListener.ts +5 -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 +26 -0
- package/src/roborockCommunication/broadcast/listener/implementation/chainedMessageListener.ts +16 -0
- package/src/roborockCommunication/broadcast/listener/implementation/simpleMessageListener.ts +37 -0
- package/src/roborockCommunication/broadcast/listener/implementation/syncMessageListener.ts +48 -0
- package/src/roborockCommunication/broadcast/listener/index.ts +3 -0
- package/src/roborockCommunication/broadcast/messageProcessor.ts +110 -0
- package/src/roborockCommunication/broadcast/model/dps.ts +17 -0
- package/src/roborockCommunication/broadcast/model/messageContext.ts +34 -0
- package/src/roborockCommunication/broadcast/model/protocol.ts +19 -0
- package/src/roborockCommunication/broadcast/model/requestMessage.ts +44 -0
- package/src/roborockCommunication/broadcast/model/responseMessage.ts +19 -0
- package/src/roborockCommunication/helper/chunkBuffer.ts +18 -0
- package/src/roborockCommunication/helper/cryptoHelper.ts +34 -0
- package/src/roborockCommunication/helper/messageDeserializer.ts +99 -0
- package/src/roborockCommunication/helper/messageSerializer.ts +82 -0
- package/src/roborockCommunication/helper/nameDecoder.ts +78 -0
- package/src/roborockCommunication/helper/sequence.ts +18 -0
- package/src/roborockCommunication/index.ts +15 -0
- package/src/roborockService.ts +379 -0
- package/src/rvc.ts +66 -0
- package/src/settings.ts +1 -0
- package/src/share/function.ts +106 -0
- package/src/share/runtimeHelper.ts +35 -0
- package/tsconfig.json +37 -0
- package/tsconfig.production.json +19 -0
- package/tslint.json +9 -0
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { AnsiLogger } from 'matterbridge/logger';
|
|
2
|
+
import { ServiceArea } from 'matterbridge/matter/clusters';
|
|
3
|
+
import RoomMap from '../model/RoomMap.js';
|
|
4
|
+
import { Room } from '../roborockCommunication/Zmodel/room.js';
|
|
5
|
+
|
|
6
|
+
/*
|
|
7
|
+
rooms = [
|
|
8
|
+
{ id: 123456, name: 'Study' },
|
|
9
|
+
{ id: 123457, name: 'Bedroom' },
|
|
10
|
+
{ id: 123458, name: 'Kitchen' },
|
|
11
|
+
{ id: 123459, name: 'Living room' }
|
|
12
|
+
]
|
|
13
|
+
roomMap = {
|
|
14
|
+
rooms: [
|
|
15
|
+
{ id: 1, globalId: "123456", displayName: undefined },
|
|
16
|
+
{ id: 2, globalId: "123457", displayName: undefined },
|
|
17
|
+
{ id: 3, globalId: "123458", displayName: undefined },
|
|
18
|
+
{ id: 4, globalId: "123459", displayName: undefined },
|
|
19
|
+
],
|
|
20
|
+
};
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
export function getSupportedAreas(rooms: Room[], roomMap: RoomMap | undefined, log?: AnsiLogger): ServiceArea.Area[] {
|
|
24
|
+
log?.debug('getSupportedAreas', JSON.stringify(rooms));
|
|
25
|
+
log?.debug('getSupportedAreas', JSON.stringify(roomMap));
|
|
26
|
+
|
|
27
|
+
if (!rooms || rooms.length === 0 || !roomMap?.rooms || roomMap.rooms.length == 0) {
|
|
28
|
+
log?.error('No rooms found');
|
|
29
|
+
return [
|
|
30
|
+
{
|
|
31
|
+
areaId: 1,
|
|
32
|
+
mapId: null,
|
|
33
|
+
areaInfo: {
|
|
34
|
+
locationInfo: {
|
|
35
|
+
locationName: 'Unknown',
|
|
36
|
+
floorNumber: null,
|
|
37
|
+
areaType: null,
|
|
38
|
+
},
|
|
39
|
+
landmarkInfo: null,
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
];
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const supportedAreas: ServiceArea.Area[] = rooms.map((room, index) => {
|
|
46
|
+
return {
|
|
47
|
+
areaId: roomMap.getRoomId(room.id) ?? index + 1,
|
|
48
|
+
mapId: null,
|
|
49
|
+
areaInfo: {
|
|
50
|
+
locationInfo: {
|
|
51
|
+
locationName: room.name,
|
|
52
|
+
floorNumber: null,
|
|
53
|
+
areaType: null,
|
|
54
|
+
},
|
|
55
|
+
landmarkInfo: null,
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
const duplicated = findDuplicatedAreaIds(supportedAreas, log);
|
|
61
|
+
|
|
62
|
+
return duplicated
|
|
63
|
+
? [
|
|
64
|
+
{
|
|
65
|
+
areaId: 1,
|
|
66
|
+
mapId: null,
|
|
67
|
+
areaInfo: {
|
|
68
|
+
locationInfo: {
|
|
69
|
+
locationName: 'Unknown',
|
|
70
|
+
floorNumber: null,
|
|
71
|
+
areaType: null,
|
|
72
|
+
},
|
|
73
|
+
landmarkInfo: null,
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
]
|
|
77
|
+
: supportedAreas;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function findDuplicatedAreaIds(areas: ServiceArea.Area[], log?: AnsiLogger): boolean {
|
|
81
|
+
const seen = new Set<number>();
|
|
82
|
+
const duplicates: number[] = [];
|
|
83
|
+
for (const area of areas) {
|
|
84
|
+
if (seen.has(area.areaId)) {
|
|
85
|
+
duplicates.push(area.areaId);
|
|
86
|
+
} else {
|
|
87
|
+
seen.add(area.areaId);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
if (duplicates.length > 0 && log) {
|
|
91
|
+
const duplicated = areas.filter((x) => duplicates.includes(x.areaId));
|
|
92
|
+
log.error(`Duplicated areaId(s) found: ${JSON.stringify(duplicated)}`);
|
|
93
|
+
}
|
|
94
|
+
return duplicates.length > 0;
|
|
95
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { RvcCleanMode } from 'matterbridge/matter/clusters';
|
|
2
|
+
import { getSupportedCleanModesA187 } from '../behaviors/roborock.vacuum/QREVO_EDGE_5V1/initalData.js';
|
|
3
|
+
import { getDefaultSupportedCleanModes } from '../behaviors/roborock.vacuum/default/initalData.js';
|
|
4
|
+
import { DeviceModel } from '../roborockCommunication/Zmodel/deviceModel.js';
|
|
5
|
+
|
|
6
|
+
export function getSupportedCleanModes(model: string): RvcCleanMode.ModeOption[] {
|
|
7
|
+
switch (model) {
|
|
8
|
+
case DeviceModel.QREVO_EDGE_5V1:
|
|
9
|
+
return getSupportedCleanModesA187();
|
|
10
|
+
default:
|
|
11
|
+
return getDefaultSupportedCleanModes();
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { RvcRunMode } from 'matterbridge/matter/clusters';
|
|
2
|
+
import { getSupportedRunModesA187 } from '../behaviors/roborock.vacuum/QREVO_EDGE_5V1/initalData.js';
|
|
3
|
+
import { getDefaultSupportedRunModes } from '../behaviors/roborock.vacuum/default/initalData.js';
|
|
4
|
+
import { DeviceModel } from '../roborockCommunication/Zmodel/deviceModel.js';
|
|
5
|
+
|
|
6
|
+
export function getRunningMode(model: string | undefined, modeTag: RvcRunMode.ModeTag | undefined): number | null {
|
|
7
|
+
if (!model || !modeTag) return null;
|
|
8
|
+
|
|
9
|
+
const supportedMode = getSupportedRunModes(model);
|
|
10
|
+
const runningMode = supportedMode.find((s) => s.modeTags.some((mt) => mt.value === modeTag));
|
|
11
|
+
return runningMode?.mode ?? null;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function getSupportedRunModes(model: string): RvcRunMode.ModeOption[] {
|
|
15
|
+
switch (model) {
|
|
16
|
+
case DeviceModel.QREVO_EDGE_5V1:
|
|
17
|
+
return getSupportedRunModesA187();
|
|
18
|
+
default:
|
|
19
|
+
return getDefaultSupportedRunModes();
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { getSupportedRunModes } from './getSupportedRunModes.js';
|
|
2
|
+
export { getOperationalStates, getOperationalErrorState } from './getOperationalStates.js';
|
|
3
|
+
export { getSupportedCleanModes } from './getSupportedCleanModes.js';
|
|
4
|
+
export { getSupportedAreas } from './getSupportedAreas.js';
|
|
5
|
+
export { getBatteryStatus, getBatteryState } from './getBatteryStatus.js';
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export type DockingStationStatus = {
|
|
2
|
+
cleanFluidStatus: number;
|
|
3
|
+
waterBoxFilterStatus: number;
|
|
4
|
+
dustBagStatus: number;
|
|
5
|
+
dirtyWaterBoxStatus: number;
|
|
6
|
+
clearWaterBoxStatus: number;
|
|
7
|
+
isUpdownWaterReady: number;
|
|
8
|
+
};
|
|
9
|
+
//{"cleanFluidStatus":0,"waterBoxFilterStatus":0,"dustBagStatus":2,"dirtyWaterBoxStatus":2,"clearWaterBoxStatus":2,"isUpdownWaterReady":0}
|
|
10
|
+
|
|
11
|
+
export enum DockingStationStatusType {
|
|
12
|
+
Unknown = 0,
|
|
13
|
+
Error = 1,
|
|
14
|
+
OK = 2,
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function parseDockingStationStatus(dss: number): DockingStationStatus {
|
|
18
|
+
return {
|
|
19
|
+
cleanFluidStatus: (dss >> 10) & 0b11,
|
|
20
|
+
waterBoxFilterStatus: (dss >> 8) & 0b11,
|
|
21
|
+
dustBagStatus: (dss >> 6) & 0b11,
|
|
22
|
+
dirtyWaterBoxStatus: (dss >> 4) & 0b11,
|
|
23
|
+
clearWaterBoxStatus: (dss >> 2) & 0b11,
|
|
24
|
+
isUpdownWaterReady: dss & 0b11,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function hasDockingStationError(status: DockingStationStatus): boolean {
|
|
29
|
+
return (
|
|
30
|
+
status.cleanFluidStatus === DockingStationStatusType.Error ||
|
|
31
|
+
status.waterBoxFilterStatus === DockingStationStatusType.Error ||
|
|
32
|
+
status.dustBagStatus === DockingStationStatusType.Error ||
|
|
33
|
+
status.dirtyWaterBoxStatus === DockingStationStatusType.Error ||
|
|
34
|
+
status.clearWaterBoxStatus === DockingStationStatusType.Error ||
|
|
35
|
+
status.isUpdownWaterReady === DockingStationStatusType.Error
|
|
36
|
+
);
|
|
37
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/*
|
|
2
|
+
rooms = [
|
|
3
|
+
{ id: 123456, name: 'Study' },
|
|
4
|
+
{ id: 123457, name: 'Bedroom' },
|
|
5
|
+
{ id: 123458, name: 'Kitchen' },
|
|
6
|
+
{ id: 123459, name: 'Living room' }
|
|
7
|
+
]
|
|
8
|
+
roomMap = {
|
|
9
|
+
rooms: [
|
|
10
|
+
{ id: 1, globalId: "123456", displayName: undefined },
|
|
11
|
+
{ id: 2, globalId: "123457", displayName: undefined },
|
|
12
|
+
{ id: 3, globalId: "123458", displayName: undefined },
|
|
13
|
+
{ id: 4, globalId: "123459", displayName: undefined },
|
|
14
|
+
],
|
|
15
|
+
};
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import { Room } from '../roborockCommunication/Zmodel/room.js';
|
|
19
|
+
|
|
20
|
+
export default class RoomMap {
|
|
21
|
+
readonly rooms: {
|
|
22
|
+
id: number;
|
|
23
|
+
globalId: number | undefined;
|
|
24
|
+
displayName: string | undefined;
|
|
25
|
+
}[] = [];
|
|
26
|
+
|
|
27
|
+
constructor(roomData: number[][], rooms: Room[]) {
|
|
28
|
+
this.rooms = roomData.map((entry) => {
|
|
29
|
+
return {
|
|
30
|
+
id: entry[0],
|
|
31
|
+
globalId: Number(entry[1]),
|
|
32
|
+
displayName: rooms.find((r) => Number(r.id) == Number(entry[1]))?.name,
|
|
33
|
+
};
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
getGlobalId(id: number): number | undefined {
|
|
38
|
+
return this.rooms.find((r) => Number(r.id) == Number(id))?.globalId;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
getRoomId(globalId: number): number | undefined {
|
|
42
|
+
return this.rooms.find((r) => Number(r.globalId) == Number(globalId))?.id;
|
|
43
|
+
}
|
|
44
|
+
}
|
package/src/platform.ts
ADDED
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import './extensions/index.js';
|
|
2
|
+
import { Matterbridge, MatterbridgeDynamicPlatform, PlatformConfig } from 'matterbridge';
|
|
3
|
+
import * as axios from 'axios';
|
|
4
|
+
import { AnsiLogger, LogLevel } from 'matterbridge/logger';
|
|
5
|
+
import RoborockService from './roborockService.js';
|
|
6
|
+
import { PLUGIN_NAME } from './settings.js';
|
|
7
|
+
import ClientManager from './clientManager.js';
|
|
8
|
+
import { isSupportedDevice } from './helper.js';
|
|
9
|
+
import { PlatformRunner } from './platformRunner.js';
|
|
10
|
+
import { RoborockVacuumCleaner } from './rvc.js';
|
|
11
|
+
import { configurateBehavior } from './behaviorFactory.js';
|
|
12
|
+
import { NotifyMessageTypes } from './notifyMessageTypes.js';
|
|
13
|
+
import { Device, RoborockAuthenticateApi, RoborockIoTApi } from './roborockCommunication/index.js';
|
|
14
|
+
import { getSupportedAreas } from './initialData/index.js';
|
|
15
|
+
|
|
16
|
+
export class RoborockMatterbridgePlatform extends MatterbridgeDynamicPlatform {
|
|
17
|
+
robot: RoborockVacuumCleaner | undefined;
|
|
18
|
+
rvcInterval: NodeJS.Timeout | undefined;
|
|
19
|
+
roborockService: RoborockService | undefined;
|
|
20
|
+
clientManager: ClientManager;
|
|
21
|
+
platformRunner: PlatformRunner | undefined;
|
|
22
|
+
devices: Map<string, Device>;
|
|
23
|
+
serialNumber: string | undefined;
|
|
24
|
+
|
|
25
|
+
constructor(matterbridge: Matterbridge, log: AnsiLogger, config: PlatformConfig) {
|
|
26
|
+
super(matterbridge, log, config);
|
|
27
|
+
|
|
28
|
+
// Verify that Matterbridge is the correct version
|
|
29
|
+
if (this.verifyMatterbridgeVersion === undefined || typeof this.verifyMatterbridgeVersion !== 'function' || !this.verifyMatterbridgeVersion('3.0.4')) {
|
|
30
|
+
throw new Error(
|
|
31
|
+
`This plugin requires Matterbridge version >= "3.0.4". Please update Matterbridge from ${this.matterbridge.matterbridgeVersion} to the latest version in the frontend.`,
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
this.log.info('Initializing platform:', this.config.name);
|
|
35
|
+
if (config.whiteList === undefined) config.whiteList = [];
|
|
36
|
+
if (config.blackList === undefined) config.blackList = [];
|
|
37
|
+
if (config.enableExperimentalFeature === undefined) config.enableExperimentalFeature = false;
|
|
38
|
+
|
|
39
|
+
this.clientManager = new ClientManager(this.log);
|
|
40
|
+
this.devices = new Map<string, Device>();
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
override async onStart(reason?: string) {
|
|
44
|
+
this.log.notice('onStart called with reason:', reason ?? 'none');
|
|
45
|
+
const self = this;
|
|
46
|
+
|
|
47
|
+
// Wait for the platform to start
|
|
48
|
+
await this.ready;
|
|
49
|
+
await this.clearSelect();
|
|
50
|
+
|
|
51
|
+
// Verify that the config is correct
|
|
52
|
+
if (this.config.username === undefined || this.config.password === undefined) {
|
|
53
|
+
this.log.error('"username" and "password" are required in the config');
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Add request interceptor
|
|
58
|
+
const axiosInstance = axios.default ?? axios;
|
|
59
|
+
axiosInstance.interceptors.request.use((request: any) => {
|
|
60
|
+
self.log.debug('Axios Request:', {
|
|
61
|
+
method: request.method,
|
|
62
|
+
url: request.url,
|
|
63
|
+
data: request.data,
|
|
64
|
+
headers: request.headers,
|
|
65
|
+
});
|
|
66
|
+
return request;
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
axiosInstance.interceptors.response.use((response: any) => {
|
|
70
|
+
self.log.debug('Axios Response:', {
|
|
71
|
+
status: response.status,
|
|
72
|
+
data: response.data,
|
|
73
|
+
headers: response.headers,
|
|
74
|
+
url: response.config.url,
|
|
75
|
+
});
|
|
76
|
+
return response;
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
this.platformRunner = new PlatformRunner(this);
|
|
80
|
+
|
|
81
|
+
this.roborockService = new RoborockService(
|
|
82
|
+
() => new RoborockAuthenticateApi(this.log, axiosInstance),
|
|
83
|
+
(logger, ud) => new RoborockIoTApi(ud, logger),
|
|
84
|
+
(this.config.refreshInterval as number) ?? 60,
|
|
85
|
+
this.clientManager,
|
|
86
|
+
this.log,
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
const username = this.config.username as string;
|
|
90
|
+
const password = this.config.password as string;
|
|
91
|
+
|
|
92
|
+
const userData = await this.roborockService.loginWithPassword(username, password);
|
|
93
|
+
this.log.debug('Initializing - userData:', JSON.stringify(userData));
|
|
94
|
+
const devices = await this.roborockService.listDevices(username);
|
|
95
|
+
this.log.notice('Initializing - devices: ', JSON.stringify(devices));
|
|
96
|
+
|
|
97
|
+
let vacuum: Device | undefined = undefined;
|
|
98
|
+
if ((this.config.whiteList as string[]).length > 0) {
|
|
99
|
+
const firstDUID = (this.config.whiteList as string[])[0];
|
|
100
|
+
vacuum = devices.find((d) => d.duid == firstDUID);
|
|
101
|
+
} else {
|
|
102
|
+
vacuum = devices.find((d) => isSupportedDevice(d.data.model));
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (!vacuum) {
|
|
106
|
+
this.log.error('Initializing: No supported devices found');
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
await this.roborockService.initializeMessageClient(username, vacuum, userData);
|
|
110
|
+
this.devices.set(vacuum.serialNumber, vacuum);
|
|
111
|
+
this.serialNumber = vacuum.serialNumber;
|
|
112
|
+
|
|
113
|
+
await this.onConfigurateDevice();
|
|
114
|
+
this.log.notice('onStart finished');
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
override async onConfigure() {
|
|
118
|
+
await super.onConfigure();
|
|
119
|
+
const self = this;
|
|
120
|
+
this.rvcInterval = setInterval(
|
|
121
|
+
async () => {
|
|
122
|
+
self.platformRunner?.requestHomeData();
|
|
123
|
+
},
|
|
124
|
+
((this.config.refreshInterval as number) ?? 60) * 1000 + 100,
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
async onConfigurateDevice(): Promise<void> {
|
|
129
|
+
this.log.info('onConfigurateDevice start');
|
|
130
|
+
if (this.platformRunner === undefined || this.roborockService === undefined) {
|
|
131
|
+
this.log.error('Initializing: PlatformRunner or RoborockService is undefined');
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
const vacuum = this.devices.get(this.serialNumber as string);
|
|
135
|
+
const username = this.config.username as string;
|
|
136
|
+
if (!vacuum || !username) {
|
|
137
|
+
this.log.error('Initializing: No supported devices found');
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const self = this;
|
|
142
|
+
await this.roborockService.initializeMessageClientForLocal(vacuum);
|
|
143
|
+
const roomMap = await this.platformRunner.getRoomMapFromDevice(vacuum);
|
|
144
|
+
|
|
145
|
+
this.log.debug('Initializing - roomMap: ', JSON.stringify(roomMap));
|
|
146
|
+
|
|
147
|
+
const behaviorHandler = configurateBehavior(vacuum.data.model, vacuum.duid, this.roborockService, this.log);
|
|
148
|
+
|
|
149
|
+
this.roborockService.setSupportedAreas(vacuum.duid, getSupportedAreas(vacuum.rooms, roomMap, this.log));
|
|
150
|
+
this.robot = new RoborockVacuumCleaner(username, vacuum, roomMap, this.log);
|
|
151
|
+
this.robot.configurateHandler(behaviorHandler);
|
|
152
|
+
|
|
153
|
+
// const xxx = this.roborockService.customGet(vacuum.duid, 'get_custom_mode');
|
|
154
|
+
// this.log.error('XXXXXXX: ', JSON.stringify(xxx));
|
|
155
|
+
// const yyyy = this.roborockService.customGetInSecure(vacuum.duid, 'get_custom_mode');
|
|
156
|
+
// this.log.error('XXXXXXX: ', JSON.stringify(yyyy));
|
|
157
|
+
|
|
158
|
+
this.log.info('vacuum:', JSON.stringify(vacuum));
|
|
159
|
+
|
|
160
|
+
this.setSelectDevice(this.robot.serialNumber ?? '', this.robot.deviceName ?? '', undefined, 'hub');
|
|
161
|
+
if (this.validateDevice(this.robot.deviceName ?? '')) {
|
|
162
|
+
await this.registerDevice(this.robot);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
this.roborockService.setDeviceNotify(async function (messageSource: NotifyMessageTypes, homeData: any) {
|
|
166
|
+
await self.platformRunner?.updateRobot(messageSource, homeData);
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
await this.roborockService.activateDeviceNotify(this.robot.device);
|
|
170
|
+
await self.platformRunner?.requestHomeData();
|
|
171
|
+
|
|
172
|
+
this.log.info('onConfigurateDevice finished');
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
override async onShutdown(reason?: string) {
|
|
176
|
+
await super.onShutdown(reason);
|
|
177
|
+
this.log.notice('onShutdown called with reason:', reason ?? 'none');
|
|
178
|
+
this.rvcInterval && clearInterval(this.rvcInterval);
|
|
179
|
+
if (this.roborockService) this.roborockService.stopService();
|
|
180
|
+
if (this.config.unregisterOnShutdown === true) await this.unregisterAllDevices(500);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
override async onChangeLoggerLevel(logLevel: LogLevel): Promise<void> {
|
|
184
|
+
this.log.notice(`Change ${PLUGIN_NAME} log level: ${logLevel} (was ${this.log.logLevel})`);
|
|
185
|
+
this.log.logLevel = logLevel;
|
|
186
|
+
return Promise.resolve();
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
private sleep(ms: number): Promise<void> {
|
|
190
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
191
|
+
}
|
|
192
|
+
}
|