matterbridge-roborock-vacuum-plugin 1.1.0-rc18 → 1.1.1-rc01

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 (33) hide show
  1. package/dist/helper.js +2 -7
  2. package/dist/model/RoomMap.js +1 -1
  3. package/dist/model/roomIndexMap.js +8 -4
  4. package/dist/platform.js +4 -3
  5. package/dist/platformRunner.js +10 -268
  6. package/dist/runtimes/handleCloudMessage.js +110 -0
  7. package/dist/runtimes/handleHomeDataMessage.js +57 -0
  8. package/dist/runtimes/handleLocalMessage.js +169 -0
  9. package/dist/tests/testData/mockData.js +359 -0
  10. package/matterbridge-roborock-vacuum-plugin.config.json +2 -2
  11. package/matterbridge-roborock-vacuum-plugin.schema.json +10 -37
  12. package/package.json +2 -2
  13. package/src/behaviors/roborock.vacuum/default/runtimes.ts +1 -1
  14. package/src/behaviors/roborock.vacuum/smart/runtimes.ts +1 -1
  15. package/src/helper.ts +2 -12
  16. package/src/initialData/getSupportedAreas.ts +1 -9
  17. package/src/model/RoomMap.ts +4 -30
  18. package/src/model/roomIndexMap.ts +10 -6
  19. package/src/platform.ts +6 -3
  20. package/src/platformRunner.ts +12 -350
  21. package/src/roborockCommunication/Zmodel/device.ts +13 -1
  22. package/src/roborockCommunication/Zmodel/messageResult.ts +28 -27
  23. package/src/roborockCommunication/Zmodel/userData.ts +2 -1
  24. package/src/runtimes/handleCloudMessage.ts +134 -0
  25. package/src/runtimes/handleHomeDataMessage.ts +67 -0
  26. package/src/runtimes/handleLocalMessage.ts +209 -0
  27. package/src/share/runtimeHelper.ts +1 -1
  28. package/src/tests/helper.test.ts +59 -10
  29. package/src/tests/roborockService.setSelectedAreas.test.ts +61 -0
  30. package/src/tests/runtimes/handleCloudMessage.test.ts +200 -0
  31. package/src/tests/runtimes/handleHomeDataMessage.test.ts +53 -0
  32. package/src/tests/runtimes/handleLocalMessage.test.ts +222 -0
  33. package/src/tests/testData/mockData.ts +370 -0
package/dist/helper.js CHANGED
@@ -55,16 +55,13 @@ export async function getRoomMap(duid, platform) {
55
55
  export async function getRoomMapFromDevice(device, platform) {
56
56
  const rooms = device?.rooms ?? [];
57
57
  const enableMultipleMap = (platform.enableExperimentalFeature?.enableExperimentalFeature && platform.enableExperimentalFeature?.advancedFeature.enableMultipleMap) ?? false;
58
- platform.log.notice('-------------------------------------------0--------------------------------------------------------');
59
- platform.log.notice(`getRoomMapFromDevice - device.rooms: ${debugStringify(rooms)}`);
60
58
  if (device && platform.roborockService) {
61
59
  const mapInfo = await platform.roborockService.getMapInformation(device.duid);
62
- platform.log.notice(`getRoomMapFromDevice - mapInfo: ${mapInfo ? debugStringify(mapInfo) : 'undefined'}`);
60
+ platform.log.debug(`getRoomMapFromDevice - mapInfo: ${mapInfo ? debugStringify(mapInfo) : 'undefined'}`);
61
+ platform.log.debug(`getRoomMapFromDevice - rooms: ${debugStringify(rooms)}`);
63
62
  if (mapInfo && mapInfo.allRooms && mapInfo.allRooms.length > 0) {
64
63
  const roomDataMap = mapInfo.allRooms;
65
64
  const roomMap = new RoomMap(roomDataMap, rooms, mapInfo.maps, enableMultipleMap);
66
- platform.log.notice(`getRoomMapFromDevice - roomMap: ${debugStringify(roomMap)}`);
67
- platform.log.notice('-------------------------------------------2--------------------------------------------------------');
68
65
  return roomMap;
69
66
  }
70
67
  const roomData = await platform.roborockService.getRoomMappings(device.duid);
@@ -72,8 +69,6 @@ export async function getRoomMapFromDevice(device, platform) {
72
69
  platform.log.notice(`getRoomMapFromDevice - roomData: ${debugStringify(roomData ?? [])}`);
73
70
  const roomDataMap = roomData.map((r) => ({ id: r[0], iot_name_id: String(r[1]), globalId: r[1], tag: r[2], mapId: 0, displayName: undefined }));
74
71
  const roomMap = new RoomMap(roomDataMap ?? [], rooms, [], enableMultipleMap);
75
- platform.log.notice(`getRoomMapFromDevice - roomMap: ${debugStringify(roomMap)}`);
76
- platform.log.notice('-------------------------------------------1--------------------------------------------------------');
77
72
  return roomMap;
78
73
  }
79
74
  }
@@ -9,7 +9,7 @@ export class RoomMap {
9
9
  return {
10
10
  id,
11
11
  globalId: globalId !== undefined ? Number(globalId) : undefined,
12
- displayName: room?.name,
12
+ displayName: room?.name ?? `Room ${id}`,
13
13
  alternativeId: `${id}${tag}`,
14
14
  mapId,
15
15
  };
@@ -4,12 +4,16 @@ export class RoomIndexMap {
4
4
  constructor(roomMap) {
5
5
  this.indexMap = roomMap;
6
6
  this.roomMap = new Map();
7
- for (const [areaId, { roomId }] of roomMap.entries()) {
8
- this.roomMap.set(roomId, areaId);
7
+ for (const [areaId, { roomId, mapId }] of roomMap.entries()) {
8
+ this.roomMap.set(`${roomId}:${mapId}`, areaId);
9
9
  }
10
10
  }
11
- getAreaId(roomId) {
12
- return this.roomMap.get(roomId);
11
+ getAreaId(roomId, mapId) {
12
+ const areaId = this.roomMap.get(`${roomId}:${mapId}`);
13
+ if (areaId === undefined) {
14
+ return undefined;
15
+ }
16
+ return areaId;
13
17
  }
14
18
  getRoomId(areaId) {
15
19
  return this.indexMap.get(areaId)?.roomId;
package/dist/platform.js CHANGED
@@ -26,8 +26,8 @@ export class RoborockMatterbridgePlatform extends MatterbridgeDynamicPlatform {
26
26
  rrHomeId;
27
27
  constructor(matterbridge, log, config) {
28
28
  super(matterbridge, log, config);
29
- if (this.verifyMatterbridgeVersion === undefined || typeof this.verifyMatterbridgeVersion !== 'function' || !this.verifyMatterbridgeVersion('3.1.8')) {
30
- throw new Error(`This plugin requires Matterbridge version >= "3.1.8". Please update Matterbridge from ${this.matterbridge.matterbridgeVersion} to the latest version in the frontend.`);
29
+ if (this.verifyMatterbridgeVersion === undefined || typeof this.verifyMatterbridgeVersion !== 'function' || !this.verifyMatterbridgeVersion('3.2.0')) {
30
+ throw new Error(`This plugin requires Matterbridge version >= "3.2.0". Please update Matterbridge from ${this.matterbridge.matterbridgeVersion} to the latest version in the frontend.`);
31
31
  }
32
32
  this.log.info('Initializing platform:', this.config.name);
33
33
  if (config.whiteList === undefined)
@@ -52,6 +52,7 @@ export class RoborockMatterbridgePlatform extends MatterbridgeDynamicPlatform {
52
52
  }
53
53
  const axiosInstance = axios.default ?? axios;
54
54
  this.enableExperimentalFeature = this.config.enableExperimental;
55
+ this.enableExperimentalFeature.advancedFeature.enableMultipleMap = false;
55
56
  if (this.enableExperimentalFeature?.enableExperimentalFeature && this.enableExperimentalFeature?.cleanModeSettings?.enableCleanModeMapping) {
56
57
  this.cleanModeSettings = this.enableExperimentalFeature.cleanModeSettings;
57
58
  this.log.notice(`Experimental Feature has been enable`);
@@ -171,7 +172,7 @@ export class RoborockMatterbridgePlatform extends MatterbridgeDynamicPlatform {
171
172
  this.roborockService.setSupportedAreaIndexMap(vacuum.duid, roomIndexMap);
172
173
  let routineAsRoom = [];
173
174
  if (this.enableExperimentalFeature?.enableExperimentalFeature && this.enableExperimentalFeature.advancedFeature?.showRoutinesAsRoom) {
174
- routineAsRoom = getSupportedScenes(vacuum.scenes, this.log);
175
+ routineAsRoom = getSupportedScenes(vacuum.scenes ?? [], this.log);
175
176
  this.roborockService.setSupportedScenes(vacuum.duid, routineAsRoom);
176
177
  }
177
178
  const robot = new RoborockVacuumCleaner(username, vacuum, roomMap, routineAsRoom, this.enableExperimentalFeature, this.log);
@@ -1,15 +1,10 @@
1
- import { RvcRunMode, PowerSource, ServiceArea, RvcOperationalState, RvcCleanMode } from 'matterbridge/matter/clusters';
2
- import { getRoomMap, getRoomMapFromDevice, getVacuumProperty, isStatusUpdate } from './helper.js';
3
- import { getRunningMode } from './initialData/getSupportedRunModes.js';
4
- import { state_to_matter_operational_status, state_to_matter_state } from './share/function.js';
5
- import { getBatteryState, getBatteryStatus, getOperationalErrorState, getSupportedAreas } from './initialData/index.js';
1
+ import { PowerSource, RvcOperationalState } from 'matterbridge/matter/clusters';
2
+ import { getBatteryStatus, getOperationalErrorState } from './initialData/index.js';
6
3
  import { NotifyMessageTypes } from './notifyMessageTypes.js';
7
- import { Protocol } from './roborockCommunication/broadcast/model/protocol.js';
8
- import { hasDockingStationError, parseDockingStationStatus } from './model/DockingStationStatus.js';
9
- import { AdditionalPropCode } from './roborockCommunication/index.js';
10
- import { OperationStatusCode } from './roborockCommunication/Zenum/operationStatusCode.js';
11
- import { getCurrentCleanModeFunc } from './share/runtimeHelper.js';
12
4
  import { debugStringify } from 'matterbridge/logger';
5
+ import { handleLocalMessage } from './runtimes/handleLocalMessage.js';
6
+ import { handleCloudMessage } from './runtimes/handleCloudMessage.js';
7
+ import { updateFromHomeData } from './runtimes/handleHomeDataMessage.js';
13
8
  export class PlatformRunner {
14
9
  platform;
15
10
  constructor(platform) {
@@ -17,7 +12,7 @@ export class PlatformRunner {
17
12
  }
18
13
  async updateRobot(messageSource, homeData) {
19
14
  if (messageSource === NotifyMessageTypes.HomeData) {
20
- this.updateFromHomeData(homeData);
15
+ updateFromHomeData(homeData, this.platform);
21
16
  }
22
17
  else {
23
18
  await this.updateFromMQTTMessage(messageSource, homeData);
@@ -74,275 +69,22 @@ export class PlatformRunner {
74
69
  case NotifyMessageTypes.LocalMessage: {
75
70
  const data = messageData;
76
71
  const robot = platform.robots.get(duid);
77
- if (!robot) {
78
- platform.log.error(`Error2: Robot with DUID ${duid} not found`);
72
+ if (robot && data) {
73
+ await handleLocalMessage(data, platform, duid);
79
74
  return;
80
75
  }
81
- if (data) {
82
- const state = state_to_matter_state(data.state);
83
- if (state) {
84
- robot.updateAttribute(RvcRunMode.Cluster.id, 'currentMode', getRunningMode(state), platform.log);
85
- }
86
- if (data.state === OperationStatusCode.Idle) {
87
- const selectedAreas = platform.roborockService?.getSelectedAreas(duid) ?? [];
88
- robot.updateAttribute(ServiceArea.Cluster.id, 'selectedAreas', selectedAreas, platform.log);
89
- }
90
- if (state === RvcRunMode.ModeTag.Cleaning && !data.cleaning_info) {
91
- robot.updateAttribute(ServiceArea.Cluster.id, 'currentArea', null, platform.log);
92
- robot.updateAttribute(ServiceArea.Cluster.id, 'selectedAreas', [], platform.log);
93
- }
94
- else {
95
- const currentMappedAreas = this.platform.roborockService?.getSupportedAreas(duid);
96
- const roomIndexMap = this.platform.roborockService?.getSupportedAreasIndexMap(duid);
97
- const roomMap = await getRoomMap(duid, this.platform);
98
- const segment_id = data.cleaning_info?.segment_id ?? -1;
99
- const target_segment_id = data.cleaning_info?.target_segment_id ?? -1;
100
- let target_room_id = roomMap?.rooms.find((x) => x.id === segment_id || x.alternativeId === segment_id.toString())?.id ?? -1;
101
- this.platform.log.debug(`Target segment id: ${segment_id}, targetRoom: ${target_room_id}`);
102
- const isMappedArea = currentMappedAreas?.some((x) => x.areaId == segment_id);
103
- if (segment_id !== -1 && isMappedArea) {
104
- this.platform.log.debug(`RoomMap: ${roomMap ? debugStringify(roomMap) : 'undefined'}`);
105
- this.platform.log.debug(`Part1: CurrentRoom: ${segment_id}, room name: ${roomMap?.rooms.find((x) => x.id === segment_id || x.alternativeId === segment_id.toString())?.displayName ?? 'unknown'}`);
106
- const areaId = roomIndexMap?.getAreaId(segment_id) ?? segment_id;
107
- this.platform.log.notice(`AreaId: ${areaId}, segment_id: ${segment_id}`);
108
- robot.updateAttribute(ServiceArea.Cluster.id, 'currentArea', areaId, platform.log);
109
- }
110
- if (segment_id == -1) {
111
- const isTargetMappedArea = currentMappedAreas?.some((x) => x.areaId == target_segment_id);
112
- target_room_id = roomMap?.rooms.find((x) => x.id == target_segment_id || x.alternativeId === target_segment_id.toString())?.id ?? -1;
113
- this.platform.log.debug(`Target segment id: ${target_segment_id}, targetRoom: ${target_room_id}`);
114
- if (target_segment_id !== -1 && isTargetMappedArea) {
115
- this.platform.log.debug(`RoomMap: ${roomMap ? debugStringify(roomMap) : 'undefined'}`);
116
- this.platform.log.debug(`Part2: TargetRoom: ${target_segment_id}, room name: ${roomMap?.rooms.find((x) => x.id === target_segment_id || x.alternativeId === segment_id.toString())?.displayName ?? 'unknown'}`);
117
- const areaId = roomIndexMap?.getAreaId(target_segment_id) ?? target_room_id;
118
- this.platform.log.notice(`AreaId: ${areaId}, target_segment_id: ${target_segment_id}`);
119
- robot.updateAttribute(ServiceArea.Cluster.id, 'currentArea', areaId, platform.log);
120
- }
121
- }
122
- if (target_segment_id == -1 && segment_id == -1) {
123
- robot.updateAttribute(ServiceArea.Cluster.id, 'currentArea', null, platform.log);
124
- }
125
- }
126
- if (data.battery) {
127
- const batteryLevel = data.battery;
128
- robot.updateAttribute(PowerSource.Cluster.id, 'batPercentRemaining', batteryLevel * 2, platform.log);
129
- robot.updateAttribute(PowerSource.Cluster.id, 'batChargeState', getBatteryState(data.state, data.battery), platform.log);
130
- robot.updateAttribute(PowerSource.Cluster.id, 'batChargeLevel', getBatteryStatus(batteryLevel), platform.log);
131
- }
132
- const currentCleanModeSetting = {
133
- suctionPower: data.cleaning_info?.fan_power ?? data.fan_power,
134
- waterFlow: data.cleaning_info?.water_box_status ?? data.water_box_mode,
135
- distance_off: data.distance_off,
136
- mopRoute: data.cleaning_info?.mop_mode ?? data.mop_mode,
137
- segment_id: data.cleaning_info?.segment_id,
138
- target_segment_id: data.cleaning_info?.target_segment_id,
139
- };
140
- this.platform.log.debug(`data: ${debugStringify(data)}`);
141
- this.platform.log.notice(`currentCleanModeSetting: ${debugStringify(currentCleanModeSetting)}`);
142
- if (currentCleanModeSetting.mopRoute && currentCleanModeSetting.suctionPower && currentCleanModeSetting.waterFlow) {
143
- const currentCleanMode = getCurrentCleanModeFunc(deviceData.model, this.platform.enableExperimentalFeature?.advancedFeature?.forceRunAtDefault ?? false)(currentCleanModeSetting);
144
- this.platform.log.debug(`Current clean mode: ${currentCleanMode}`);
145
- if (currentCleanMode) {
146
- robot.updateAttribute(RvcCleanMode.Cluster.id, 'currentMode', currentCleanMode, platform.log);
147
- }
148
- }
149
- this.processAdditionalProps(robot, data, duid);
150
- }
76
+ platform.log.error(`Error2: Robot with DUID ${duid} not found`);
151
77
  break;
152
78
  }
153
79
  case NotifyMessageTypes.CloudMessage: {
154
80
  const data = messageData;
155
81
  if (!data)
156
82
  return;
157
- this.handlerCloudMessage(data, duid);
83
+ await handleCloudMessage(data, platform, this, duid);
158
84
  break;
159
85
  }
160
86
  default:
161
87
  break;
162
88
  }
163
89
  }
164
- handlerCloudMessage(data, duid) {
165
- const platform = this.platform;
166
- const messageTypes = Object.keys(data.dps).map(Number);
167
- const self = this;
168
- const robot = platform.robots.get(duid);
169
- if (robot === undefined) {
170
- platform.log.error(`Error3: Robot with DUID ${duid} not found`);
171
- return;
172
- }
173
- messageTypes.forEach(async (messageType) => {
174
- switch (messageType) {
175
- case Protocol.status_update: {
176
- const status = Number(data.dps[messageType]);
177
- const matterState = state_to_matter_state(status);
178
- if (matterState) {
179
- robot.updateAttribute(RvcRunMode.Cluster.id, 'currentMode', getRunningMode(matterState), platform.log);
180
- }
181
- const operationalStateId = state_to_matter_operational_status(status);
182
- if (operationalStateId) {
183
- const dssHasError = hasDockingStationError(robot.dockStationStatus);
184
- if (!(dssHasError && self.triggerDssError(robot))) {
185
- robot.updateAttribute(RvcOperationalState.Cluster.id, 'operationalState', operationalStateId, platform.log);
186
- }
187
- }
188
- break;
189
- }
190
- case Protocol.rpc_response: {
191
- const response = data.dps[messageType];
192
- if (!isStatusUpdate(response.result)) {
193
- platform.log.debug('Ignore message:', debugStringify(data));
194
- return;
195
- }
196
- let roboStatus;
197
- if (Array.isArray(response.result) && response.result.length > 0) {
198
- roboStatus = response.result[0];
199
- }
200
- if (roboStatus) {
201
- const message = { ...roboStatus };
202
- platform.log.debug('rpc_response:', debugStringify(message));
203
- await self.updateFromMQTTMessage(NotifyMessageTypes.LocalMessage, message, duid, true);
204
- }
205
- break;
206
- }
207
- case Protocol.suction_power:
208
- case Protocol.water_box_mode: {
209
- await platform.roborockService?.getCleanModeData(duid).then((cleanModeData) => {
210
- if (cleanModeData) {
211
- const currentCleanMode = getCurrentCleanModeFunc(robot.device.data.model, platform.enableExperimentalFeature?.advancedFeature?.forceRunAtDefault ?? false)({
212
- suctionPower: cleanModeData.suctionPower,
213
- waterFlow: cleanModeData.waterFlow,
214
- distance_off: cleanModeData.distance_off,
215
- mopRoute: cleanModeData.mopRoute,
216
- });
217
- platform.log.debug(`Clean mode data: ${debugStringify(cleanModeData)}`);
218
- platform.log.debug(`Current clean mode: ${currentCleanMode}`);
219
- if (currentCleanMode) {
220
- robot.updateAttribute(RvcCleanMode.Cluster.id, 'currentMode', currentCleanMode, platform.log);
221
- }
222
- }
223
- });
224
- break;
225
- }
226
- case Protocol.additional_props: {
227
- platform.log.notice(`Received additional properties for robot ${duid}: ${debugStringify(data)}`);
228
- const propCode = data.dps[Protocol.additional_props];
229
- platform.log.debug(`DPS for additional properties: ${propCode}, AdditionalPropCode: ${AdditionalPropCode[propCode]}`);
230
- const enableMultipleMap = (platform.enableExperimentalFeature?.enableExperimentalFeature && platform.enableExperimentalFeature?.advancedFeature?.enableMultipleMap) ?? false;
231
- if (propCode === AdditionalPropCode.map_change) {
232
- platform.log.notice('------------------------ get roomData ----------------------------');
233
- const roomMap = await getRoomMapFromDevice(robot.device, platform);
234
- platform.log.notice('------------------------ Room map updated ------------------------');
235
- const { supportedAreas, supportedMaps, roomIndexMap } = getSupportedAreas(robot.device.rooms, roomMap, enableMultipleMap, platform.log);
236
- platform.log.notice(`Supported areas: ${debugStringify(supportedAreas)}`);
237
- platform.log.notice('------------------------ Supported areas updated ------------------');
238
- platform.roborockService?.setSupportedAreas(duid, supportedAreas);
239
- platform.roborockService?.setSelectedAreas(duid, []);
240
- robot.updateAttribute(ServiceArea.Cluster.id, 'supportedAreas', supportedAreas, platform.log);
241
- robot.updateAttribute(ServiceArea.Cluster.id, 'selectedAreas', [], platform.log);
242
- robot.updateAttribute(ServiceArea.Cluster.id, 'currentArea', null, platform.log);
243
- if (enableMultipleMap) {
244
- platform.roborockService?.setSupportedAreaIndexMap(duid, roomIndexMap);
245
- robot.updateAttribute(ServiceArea.Cluster.id, 'supportedMaps', supportedMaps, platform.log);
246
- }
247
- }
248
- break;
249
- }
250
- case Protocol.back_type: {
251
- break;
252
- }
253
- default: {
254
- platform.log.notice(`Unknown message type ${messageType}, protocol: ${Protocol[messageType]}, message: ${debugStringify(data)}`);
255
- break;
256
- }
257
- }
258
- });
259
- }
260
- async processAdditionalProps(robot, message, duid) {
261
- const dssStatus = this.getDssStatus(message, duid);
262
- if (dssStatus) {
263
- this.triggerDssError(robot);
264
- }
265
- }
266
- getDssStatus(message, duid) {
267
- const platform = this.platform;
268
- const robot = platform.robots.get(duid);
269
- if (robot === undefined) {
270
- platform.log.error(`Error4: Robot with DUID ${duid} not found`);
271
- return undefined;
272
- }
273
- if (platform.enableExperimentalFeature &&
274
- platform.enableExperimentalFeature.enableExperimentalFeature &&
275
- platform.enableExperimentalFeature.advancedFeature.includeDockStationStatus &&
276
- message.dss !== undefined) {
277
- const dss = parseDockingStationStatus(message.dss);
278
- if (dss && robot) {
279
- robot.dockStationStatus = dss;
280
- }
281
- if (dss && hasDockingStationError(dss)) {
282
- return RvcOperationalState.OperationalState.Error;
283
- }
284
- }
285
- return undefined;
286
- }
287
- updateFromHomeData(homeData) {
288
- const platform = this.platform;
289
- if (platform.robots.size === 0)
290
- return;
291
- const devices = homeData.devices.filter((d) => platform.robots.has(d.duid));
292
- for (const device of devices) {
293
- const robot = platform.robots.get(device.duid);
294
- if (robot === undefined) {
295
- platform.log.error(`Error5: Robot with DUID ${device.duid} not found`);
296
- continue;
297
- }
298
- const deviceData = robot.device.data;
299
- if (!device || deviceData === undefined) {
300
- platform.log.error('Device not found in home data');
301
- return;
302
- }
303
- device.schema = homeData.products.find((prd) => prd.id === device.productId || prd.model === device.data.model)?.schema ?? [];
304
- this.platform.log.debug('updateFromHomeData-homeData:', debugStringify(homeData));
305
- this.platform.log.debug('updateFromHomeData-device:', debugStringify(device));
306
- const batteryLevel = getVacuumProperty(device, 'battery');
307
- this.platform.log.debug('updateFromHomeData-schema:' + debugStringify(device.schema));
308
- this.platform.log.debug('updateFromHomeData-battery:' + debugStringify(device.deviceStatus));
309
- if (batteryLevel) {
310
- robot.updateAttribute(PowerSource.Cluster.id, 'batPercentRemaining', batteryLevel ? batteryLevel * 2 : 200, platform.log);
311
- robot.updateAttribute(PowerSource.Cluster.id, 'batChargeLevel', getBatteryStatus(batteryLevel), platform.log);
312
- }
313
- const state = getVacuumProperty(device, 'state');
314
- const matterState = state_to_matter_state(state);
315
- if (!state || !matterState) {
316
- return;
317
- }
318
- this.platform.log.debug(`updateFromHomeData-RvcRunMode code: ${state} name: ${OperationStatusCode[state]}, matterState: ${RvcRunMode.ModeTag[matterState]}`);
319
- if (matterState) {
320
- robot.updateAttribute(RvcRunMode.Cluster.id, 'currentMode', getRunningMode(matterState), platform.log);
321
- }
322
- const operationalStateId = state_to_matter_operational_status(state);
323
- if (operationalStateId) {
324
- const dssHasError = hasDockingStationError(robot.dockStationStatus);
325
- this.platform.log.debug(`dssHasError: ${dssHasError}, dockStationStatus: ${debugStringify(robot.dockStationStatus ?? {})}`);
326
- if (!(dssHasError && this.triggerDssError(robot))) {
327
- this.platform.log.debug(`updateFromHomeData-OperationalState: ${RvcOperationalState.OperationalState[operationalStateId]}`);
328
- robot.updateAttribute(RvcOperationalState.Cluster.id, 'operationalState', operationalStateId, platform.log);
329
- }
330
- }
331
- if (batteryLevel) {
332
- robot.updateAttribute(PowerSource.Cluster.id, 'batChargeState', getBatteryState(state, batteryLevel), platform.log);
333
- }
334
- }
335
- }
336
- triggerDssError(robot) {
337
- const platform = this.platform;
338
- const currentOperationState = robot.getAttribute(RvcOperationalState.Cluster.id, 'operationalState');
339
- if (currentOperationState === RvcOperationalState.OperationalState.Error) {
340
- return true;
341
- }
342
- if (currentOperationState === RvcOperationalState.OperationalState.Docked) {
343
- robot.updateAttribute(RvcOperationalState.Cluster.id, 'operationalState', RvcOperationalState.OperationalState.Error, platform.log);
344
- return true;
345
- }
346
- return false;
347
- }
348
90
  }
@@ -0,0 +1,110 @@
1
+ import { getRunningMode } from '../initialData/getSupportedRunModes.js';
2
+ import { hasDockingStationError } from '../model/DockingStationStatus.js';
3
+ import { AdditionalPropCode, Protocol } from '../roborockCommunication/index.js';
4
+ import { state_to_matter_operational_status, state_to_matter_state } from '../share/function.js';
5
+ import { RvcCleanMode, RvcOperationalState, RvcRunMode, ServiceArea } from 'matterbridge/matter/clusters';
6
+ import { triggerDssError } from './handleLocalMessage.js';
7
+ import { getRoomMapFromDevice, isStatusUpdate } from '../helper.js';
8
+ import { debugStringify } from 'matterbridge/logger';
9
+ import { NotifyMessageTypes } from '../notifyMessageTypes.js';
10
+ import { getCurrentCleanModeFunc } from '../share/runtimeHelper.js';
11
+ import { getSupportedAreas } from '../initialData/getSupportedAreas.js';
12
+ export async function handleCloudMessage(data, platform, runner, duid) {
13
+ const messageTypes = Object.keys(data.dps).map(Number);
14
+ const robot = platform.robots.get(duid);
15
+ if (robot === undefined) {
16
+ platform.log.error(`Error3: Robot with DUID ${duid} not found`);
17
+ return;
18
+ }
19
+ messageTypes.forEach(async (messageType) => {
20
+ switch (messageType) {
21
+ case Protocol.status_update: {
22
+ const status = Number(data.dps[messageType]);
23
+ const matterState = state_to_matter_state(status);
24
+ if (matterState) {
25
+ robot.updateAttribute(RvcRunMode.Cluster.id, 'currentMode', getRunningMode(matterState), platform.log);
26
+ }
27
+ const operationalStateId = state_to_matter_operational_status(status);
28
+ if (operationalStateId) {
29
+ const dssHasError = hasDockingStationError(robot.dockStationStatus);
30
+ if (!(dssHasError && triggerDssError(robot, platform))) {
31
+ robot.updateAttribute(RvcOperationalState.Cluster.id, 'operationalState', operationalStateId, platform.log);
32
+ }
33
+ }
34
+ break;
35
+ }
36
+ case Protocol.rpc_response: {
37
+ const response = data.dps[messageType];
38
+ if (!isStatusUpdate(response.result)) {
39
+ platform.log.debug('Ignore message:', debugStringify(data));
40
+ return;
41
+ }
42
+ let roboStatus;
43
+ if (Array.isArray(response.result) && response.result.length > 0) {
44
+ roboStatus = response.result[0];
45
+ }
46
+ if (roboStatus) {
47
+ const message = { ...roboStatus };
48
+ platform.log.debug('rpc_response:', debugStringify(message));
49
+ await runner.updateFromMQTTMessage(NotifyMessageTypes.LocalMessage, message, duid, true);
50
+ }
51
+ break;
52
+ }
53
+ case Protocol.suction_power:
54
+ case Protocol.water_box_mode: {
55
+ await platform.roborockService?.getCleanModeData(duid).then((cleanModeData) => {
56
+ if (cleanModeData) {
57
+ const currentCleanMode = getCurrentCleanModeFunc(robot.device.data.model, platform.enableExperimentalFeature?.advancedFeature?.forceRunAtDefault ?? false)({
58
+ suctionPower: cleanModeData.suctionPower,
59
+ waterFlow: cleanModeData.waterFlow,
60
+ distance_off: cleanModeData.distance_off,
61
+ mopRoute: cleanModeData.mopRoute,
62
+ });
63
+ platform.log.debug(`Clean mode data: ${debugStringify(cleanModeData)}`);
64
+ platform.log.debug(`Current clean mode: ${currentCleanMode}`);
65
+ if (currentCleanMode) {
66
+ robot.updateAttribute(RvcCleanMode.Cluster.id, 'currentMode', currentCleanMode, platform.log);
67
+ }
68
+ }
69
+ });
70
+ break;
71
+ }
72
+ case Protocol.additional_props: {
73
+ platform.log.notice(`Received additional properties for robot ${duid}: ${debugStringify(data)}`);
74
+ const propCode = data.dps[Protocol.additional_props];
75
+ platform.log.debug(`DPS for additional properties: ${propCode}, AdditionalPropCode: ${AdditionalPropCode[propCode]}`);
76
+ if (propCode === AdditionalPropCode.map_change) {
77
+ await handleMapChange(robot, platform, duid);
78
+ }
79
+ break;
80
+ }
81
+ case Protocol.back_type: {
82
+ break;
83
+ }
84
+ default: {
85
+ platform.log.notice(`Unknown message type ${messageType}, protocol: ${Protocol[messageType]}, message: ${debugStringify(data)}`);
86
+ break;
87
+ }
88
+ }
89
+ });
90
+ }
91
+ export async function handleMapChange(robot, platform, duid) {
92
+ const enableMultipleMap = (platform.enableExperimentalFeature?.enableExperimentalFeature && platform.enableExperimentalFeature?.advancedFeature?.enableMultipleMap) ?? false;
93
+ if (!enableMultipleMap)
94
+ return;
95
+ await getRoomMapFromDevice(robot.device, platform).then((roomMap) => {
96
+ const { supportedAreas, supportedMaps, roomIndexMap } = getSupportedAreas(robot.device.rooms, roomMap, enableMultipleMap, platform.log);
97
+ platform.log.debug(`handleMapChange - supportedAreas: ${debugStringify(supportedAreas)}`);
98
+ platform.log.debug(`handleMapChange - supportedMaps: ${debugStringify(supportedMaps)}`);
99
+ platform.log.debug(`handleMapChange - roomIndexMap: `, roomIndexMap);
100
+ platform.roborockService?.setSupportedAreas(duid, supportedAreas);
101
+ platform.roborockService?.setSelectedAreas(duid, []);
102
+ robot.updateAttribute(ServiceArea.Cluster.id, 'supportedAreas', supportedAreas, platform.log);
103
+ robot.updateAttribute(ServiceArea.Cluster.id, 'selectedAreas', [], platform.log);
104
+ robot.updateAttribute(ServiceArea.Cluster.id, 'currentArea', null, platform.log);
105
+ if (enableMultipleMap) {
106
+ platform.roborockService?.setSupportedAreaIndexMap(duid, roomIndexMap);
107
+ robot.updateAttribute(ServiceArea.Cluster.id, 'supportedMaps', supportedMaps, platform.log);
108
+ }
109
+ });
110
+ }
@@ -0,0 +1,57 @@
1
+ import { PowerSource, RvcOperationalState, RvcRunMode } from 'matterbridge/matter/clusters';
2
+ import { getVacuumProperty } from '../helper.js';
3
+ import { debugStringify } from 'matterbridge/logger';
4
+ import { getBatteryState, getBatteryStatus } from '../initialData/index.js';
5
+ import { state_to_matter_operational_status, state_to_matter_state } from '../share/function.js';
6
+ import { OperationStatusCode } from '../roborockCommunication/Zenum/operationStatusCode.js';
7
+ import { getRunningMode } from '../initialData/getSupportedRunModes.js';
8
+ import { hasDockingStationError } from '../model/DockingStationStatus.js';
9
+ import { triggerDssError } from './handleLocalMessage.js';
10
+ export async function updateFromHomeData(homeData, platform) {
11
+ if (platform.robots.size === 0)
12
+ return;
13
+ const devices = homeData.devices.filter((d) => platform.robots.has(d.duid));
14
+ for (const device of devices) {
15
+ const robot = platform.robots.get(device.duid);
16
+ if (robot === undefined) {
17
+ platform.log.error(`Error5: Robot with DUID ${device.duid} not found`);
18
+ continue;
19
+ }
20
+ const deviceData = robot.device.data;
21
+ if (!device || deviceData === undefined) {
22
+ platform.log.error('Device not found in home data');
23
+ return;
24
+ }
25
+ device.schema = homeData.products.find((prd) => prd.id === device.productId || prd.model === device.data.model)?.schema ?? [];
26
+ platform.log.debug('updateFromHomeData-homeData:', debugStringify(homeData));
27
+ platform.log.debug('updateFromHomeData-device:', debugStringify(device));
28
+ platform.log.debug('updateFromHomeData-schema:' + debugStringify(device.schema));
29
+ platform.log.debug('updateFromHomeData-battery:' + debugStringify(device.deviceStatus));
30
+ const batteryLevel = getVacuumProperty(device, 'battery');
31
+ if (batteryLevel) {
32
+ robot.updateAttribute(PowerSource.Cluster.id, 'batPercentRemaining', batteryLevel ? batteryLevel * 2 : 200, platform.log);
33
+ robot.updateAttribute(PowerSource.Cluster.id, 'batChargeLevel', getBatteryStatus(batteryLevel), platform.log);
34
+ }
35
+ const state = getVacuumProperty(device, 'state');
36
+ const matterState = state_to_matter_state(state);
37
+ if (!state || !matterState) {
38
+ return;
39
+ }
40
+ platform.log.debug(`updateFromHomeData-RvcRunMode code: ${state} name: ${OperationStatusCode[state]}, matterState: ${RvcRunMode.ModeTag[matterState]}`);
41
+ if (matterState) {
42
+ robot.updateAttribute(RvcRunMode.Cluster.id, 'currentMode', getRunningMode(matterState), platform.log);
43
+ }
44
+ if (batteryLevel) {
45
+ robot.updateAttribute(PowerSource.Cluster.id, 'batChargeState', getBatteryState(state, batteryLevel), platform.log);
46
+ }
47
+ const operationalStateId = state_to_matter_operational_status(state);
48
+ if (operationalStateId) {
49
+ const dssHasError = hasDockingStationError(robot.dockStationStatus);
50
+ platform.log.debug(`dssHasError: ${dssHasError}, dockStationStatus: ${debugStringify(robot.dockStationStatus ?? {})}`);
51
+ if (!(dssHasError && triggerDssError(robot, platform))) {
52
+ platform.log.debug(`updateFromHomeData-OperationalState: ${RvcOperationalState.OperationalState[operationalStateId]}`);
53
+ robot.updateAttribute(RvcOperationalState.Cluster.id, 'operationalState', operationalStateId, platform.log);
54
+ }
55
+ }
56
+ }
57
+ }