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.
Files changed (170) hide show
  1. package/.github/workflows/publish.yml +34 -0
  2. package/.tarignore +5 -0
  3. package/LICENSE +202 -0
  4. package/README.md +34 -0
  5. package/README_DEV.md +69 -0
  6. package/bmc-button.svg +22 -0
  7. package/dist/appliances.js +6 -0
  8. package/dist/behaviorFactory.js +18 -0
  9. package/dist/behaviors/BehaviorDeviceGeneric.js +31 -0
  10. package/dist/behaviors/roborock.vacuum/QREVO_EDGE_5V1/a187.js +94 -0
  11. package/dist/behaviors/roborock.vacuum/QREVO_EDGE_5V1/initalData.js +81 -0
  12. package/dist/behaviors/roborock.vacuum/QREVO_EDGE_5V1/runtimes.js +39 -0
  13. package/dist/behaviors/roborock.vacuum/default/default.js +58 -0
  14. package/dist/behaviors/roborock.vacuum/default/initalData.js +66 -0
  15. package/dist/clientManager.js +17 -0
  16. package/dist/extensions/AxiosStaticExtensions.js +22 -0
  17. package/dist/extensions/index.js +1 -0
  18. package/dist/helper.js +16 -0
  19. package/dist/index.js +4 -0
  20. package/dist/initialData/getBatteryStatus.js +24 -0
  21. package/dist/initialData/getOperationalStates.js +22 -0
  22. package/dist/initialData/getSupportedAreas.js +69 -0
  23. package/dist/initialData/getSupportedCleanModes.js +11 -0
  24. package/dist/initialData/getSupportedRunModes.js +18 -0
  25. package/dist/initialData/index.js +5 -0
  26. package/dist/model/CloudMessageModel.js +1 -0
  27. package/dist/model/DockingStationStatus.js +24 -0
  28. package/dist/model/RoomMap.js +18 -0
  29. package/dist/notifyMessageTypes.js +9 -0
  30. package/dist/platform.js +146 -0
  31. package/dist/platformRunner.js +249 -0
  32. package/dist/roborockCommunication/RESTAPI/roborockAuthenticateApi.js +73 -0
  33. package/dist/roborockCommunication/RESTAPI/roborockIoTApi.js +61 -0
  34. package/dist/roborockCommunication/Zenum/dockType.js +4 -0
  35. package/dist/roborockCommunication/Zenum/operationStatusCode.js +44 -0
  36. package/dist/roborockCommunication/Zenum/vacuumAndDockErrorCode.js +68 -0
  37. package/dist/roborockCommunication/Zmodel/apiResponse.js +1 -0
  38. package/dist/roborockCommunication/Zmodel/authenticateResponse.js +1 -0
  39. package/dist/roborockCommunication/Zmodel/baseURL.js +1 -0
  40. package/dist/roborockCommunication/Zmodel/device.js +1 -0
  41. package/dist/roborockCommunication/Zmodel/deviceModel.js +28 -0
  42. package/dist/roborockCommunication/Zmodel/deviceSchema.js +1 -0
  43. package/dist/roborockCommunication/Zmodel/deviceStatus.js +22 -0
  44. package/dist/roborockCommunication/Zmodel/dockInfo.js +6 -0
  45. package/dist/roborockCommunication/Zmodel/home.js +1 -0
  46. package/dist/roborockCommunication/Zmodel/homeInfo.js +1 -0
  47. package/dist/roborockCommunication/Zmodel/messageResult.js +7 -0
  48. package/dist/roborockCommunication/Zmodel/networkInfo.js +1 -0
  49. package/dist/roborockCommunication/Zmodel/product.js +1 -0
  50. package/dist/roborockCommunication/Zmodel/room.js +1 -0
  51. package/dist/roborockCommunication/Zmodel/roomInfo.js +22 -0
  52. package/dist/roborockCommunication/Zmodel/userData.js +1 -0
  53. package/dist/roborockCommunication/Zmodel/vacuumError.js +27 -0
  54. package/dist/roborockCommunication/broadcast/abstractClient.js +41 -0
  55. package/dist/roborockCommunication/broadcast/client/LocalNetworkClient.js +148 -0
  56. package/dist/roborockCommunication/broadcast/client/MQTTClient.js +101 -0
  57. package/dist/roborockCommunication/broadcast/client.js +1 -0
  58. package/dist/roborockCommunication/broadcast/clientRouter.js +79 -0
  59. package/dist/roborockCommunication/broadcast/listener/abstractConnectionListener.js +1 -0
  60. package/dist/roborockCommunication/broadcast/listener/abstractMessageHandler.js +1 -0
  61. package/dist/roborockCommunication/broadcast/listener/abstractMessageListener.js +1 -0
  62. package/dist/roborockCommunication/broadcast/listener/implementation/chainedConnectionListener.js +21 -0
  63. package/dist/roborockCommunication/broadcast/listener/implementation/chainedMessageListener.js +11 -0
  64. package/dist/roborockCommunication/broadcast/listener/implementation/simpleMessageListener.js +27 -0
  65. package/dist/roborockCommunication/broadcast/listener/implementation/syncMessageListener.js +35 -0
  66. package/dist/roborockCommunication/broadcast/listener/index.js +1 -0
  67. package/dist/roborockCommunication/broadcast/messageProcessor.js +79 -0
  68. package/dist/roborockCommunication/broadcast/model/dps.js +1 -0
  69. package/dist/roborockCommunication/broadcast/model/messageContext.js +26 -0
  70. package/dist/roborockCommunication/broadcast/model/protocol.js +19 -0
  71. package/dist/roborockCommunication/broadcast/model/requestMessage.js +33 -0
  72. package/dist/roborockCommunication/broadcast/model/responseMessage.js +14 -0
  73. package/dist/roborockCommunication/helper/chunkBuffer.js +17 -0
  74. package/dist/roborockCommunication/helper/cryptoHelper.js +27 -0
  75. package/dist/roborockCommunication/helper/messageDeserializer.js +74 -0
  76. package/dist/roborockCommunication/helper/messageSerializer.js +70 -0
  77. package/dist/roborockCommunication/helper/nameDecoder.js +66 -0
  78. package/dist/roborockCommunication/helper/sequence.js +16 -0
  79. package/dist/roborockCommunication/index.js +9 -0
  80. package/dist/roborockService.js +300 -0
  81. package/dist/rvc.js +39 -0
  82. package/dist/settings.js +1 -0
  83. package/dist/share/function.js +96 -0
  84. package/dist/share/runtimeHelper.js +29 -0
  85. package/eslint.config.js +76 -0
  86. package/matterbridge-roborock-vacuum-plugin.config.json +11 -0
  87. package/matterbridge-roborock-vacuum-plugin.schema.json +57 -0
  88. package/package.json +43 -0
  89. package/prettier.config.js +49 -0
  90. package/src/appliances.ts +15 -0
  91. package/src/behaviorFactory.ts +24 -0
  92. package/src/behaviors/BehaviorDeviceGeneric.ts +39 -0
  93. package/src/behaviors/roborock.vacuum/QREVO_EDGE_5V1/a187.ts +117 -0
  94. package/src/behaviors/roborock.vacuum/QREVO_EDGE_5V1/initalData.ts +85 -0
  95. package/src/behaviors/roborock.vacuum/QREVO_EDGE_5V1/runtimes.ts +29 -0
  96. package/src/behaviors/roborock.vacuum/default/default.ts +78 -0
  97. package/src/behaviors/roborock.vacuum/default/initalData.ts +69 -0
  98. package/src/clientManager.ts +23 -0
  99. package/src/extensions/AxiosStaticExtensions.ts +35 -0
  100. package/src/extensions/index.ts +1 -0
  101. package/src/helper.ts +22 -0
  102. package/src/index.ts +16 -0
  103. package/src/initialData/getBatteryStatus.ts +26 -0
  104. package/src/initialData/getOperationalStates.ts +24 -0
  105. package/src/initialData/getSupportedAreas.ts +95 -0
  106. package/src/initialData/getSupportedCleanModes.ts +13 -0
  107. package/src/initialData/getSupportedRunModes.ts +21 -0
  108. package/src/initialData/index.ts +5 -0
  109. package/src/model/CloudMessageModel.ts +13 -0
  110. package/src/model/DockingStationStatus.ts +37 -0
  111. package/src/model/RoomMap.ts +44 -0
  112. package/src/notifyMessageTypes.ts +8 -0
  113. package/src/platform.ts +192 -0
  114. package/src/platformRunner.ts +292 -0
  115. package/src/roborockCommunication/RESTAPI/roborockAuthenticateApi.ts +98 -0
  116. package/src/roborockCommunication/RESTAPI/roborockIoTApi.ts +70 -0
  117. package/src/roborockCommunication/Zenum/dockType.ts +3 -0
  118. package/src/roborockCommunication/Zenum/operationStatusCode.ts +43 -0
  119. package/src/roborockCommunication/Zenum/vacuumAndDockErrorCode.ts +68 -0
  120. package/src/roborockCommunication/Zmodel/apiResponse.ts +3 -0
  121. package/src/roborockCommunication/Zmodel/authenticateResponse.ts +5 -0
  122. package/src/roborockCommunication/Zmodel/baseURL.ts +5 -0
  123. package/src/roborockCommunication/Zmodel/device.ts +39 -0
  124. package/src/roborockCommunication/Zmodel/deviceModel.ts +27 -0
  125. package/src/roborockCommunication/Zmodel/deviceSchema.ts +8 -0
  126. package/src/roborockCommunication/Zmodel/deviceStatus.ts +30 -0
  127. package/src/roborockCommunication/Zmodel/dockInfo.ts +9 -0
  128. package/src/roborockCommunication/Zmodel/home.ts +13 -0
  129. package/src/roborockCommunication/Zmodel/homeInfo.ts +5 -0
  130. package/src/roborockCommunication/Zmodel/messageResult.ts +72 -0
  131. package/src/roborockCommunication/Zmodel/networkInfo.ts +7 -0
  132. package/src/roborockCommunication/Zmodel/product.ts +9 -0
  133. package/src/roborockCommunication/Zmodel/room.ts +4 -0
  134. package/src/roborockCommunication/Zmodel/roomInfo.ts +25 -0
  135. package/src/roborockCommunication/Zmodel/userData.ts +23 -0
  136. package/src/roborockCommunication/Zmodel/vacuumError.ts +35 -0
  137. package/src/roborockCommunication/broadcast/abstractClient.ts +61 -0
  138. package/src/roborockCommunication/broadcast/client/LocalNetworkClient.ts +177 -0
  139. package/src/roborockCommunication/broadcast/client/MQTTClient.ts +129 -0
  140. package/src/roborockCommunication/broadcast/client.ts +19 -0
  141. package/src/roborockCommunication/broadcast/clientRouter.ts +100 -0
  142. package/src/roborockCommunication/broadcast/listener/abstractConnectionListener.ts +5 -0
  143. package/src/roborockCommunication/broadcast/listener/abstractMessageHandler.ts +11 -0
  144. package/src/roborockCommunication/broadcast/listener/abstractMessageListener.ts +5 -0
  145. package/src/roborockCommunication/broadcast/listener/implementation/chainedConnectionListener.ts +26 -0
  146. package/src/roborockCommunication/broadcast/listener/implementation/chainedMessageListener.ts +16 -0
  147. package/src/roborockCommunication/broadcast/listener/implementation/simpleMessageListener.ts +37 -0
  148. package/src/roborockCommunication/broadcast/listener/implementation/syncMessageListener.ts +48 -0
  149. package/src/roborockCommunication/broadcast/listener/index.ts +3 -0
  150. package/src/roborockCommunication/broadcast/messageProcessor.ts +110 -0
  151. package/src/roborockCommunication/broadcast/model/dps.ts +17 -0
  152. package/src/roborockCommunication/broadcast/model/messageContext.ts +34 -0
  153. package/src/roborockCommunication/broadcast/model/protocol.ts +19 -0
  154. package/src/roborockCommunication/broadcast/model/requestMessage.ts +44 -0
  155. package/src/roborockCommunication/broadcast/model/responseMessage.ts +19 -0
  156. package/src/roborockCommunication/helper/chunkBuffer.ts +18 -0
  157. package/src/roborockCommunication/helper/cryptoHelper.ts +34 -0
  158. package/src/roborockCommunication/helper/messageDeserializer.ts +99 -0
  159. package/src/roborockCommunication/helper/messageSerializer.ts +82 -0
  160. package/src/roborockCommunication/helper/nameDecoder.ts +78 -0
  161. package/src/roborockCommunication/helper/sequence.ts +18 -0
  162. package/src/roborockCommunication/index.ts +15 -0
  163. package/src/roborockService.ts +379 -0
  164. package/src/rvc.ts +66 -0
  165. package/src/settings.ts +1 -0
  166. package/src/share/function.ts +106 -0
  167. package/src/share/runtimeHelper.ts +35 -0
  168. package/tsconfig.json +37 -0
  169. package/tsconfig.production.json +19 -0
  170. 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,13 @@
1
+ import { CloudMessageResult } from '../roborockCommunication/Zmodel/messageResult.js';
2
+
3
+ export interface CloudMessageModel {
4
+ duid: string;
5
+ dps: {
6
+ [key: string]:
7
+ | number
8
+ | {
9
+ id: number;
10
+ result: CloudMessageResult[];
11
+ };
12
+ };
13
+ }
@@ -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
+ }
@@ -0,0 +1,8 @@
1
+ export enum NotifyMessageTypes {
2
+ LocalMessage = 'LocalMessage',
3
+ CloudMessage = 'CloudMessage',
4
+ BatteryUpdate = 'BatteryUpdate',
5
+ ErrorOccurred = 'ErrorOccurred',
6
+ HomeData = 'HomeData',
7
+ DeviceStatus = 'DeviceStatus',
8
+ }
@@ -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
+ }