matterbridge-roborock-vacuum-plugin 1.0.8-rc09 → 1.1.0-rc02

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "matterbridge-roborock-vacuum-plugin",
3
- "version": "1.0.8-rc09",
3
+ "version": "1.1.0-rc02",
4
4
  "description": "Matterbridge Roborock Vacuum Plugin",
5
5
  "author": "https://github.com/RinDevJunior",
6
6
  "license": "MIT",
@@ -5,6 +5,7 @@ export interface ExperimentalFeatureSetting {
5
5
  includeDockStationStatus: boolean;
6
6
  forceRunAtDefault: boolean;
7
7
  useVacationModeToSendVacuumToDock: boolean;
8
+ enableServerMode: boolean;
8
9
  };
9
10
  cleanModeSettings: CleanModeSettings;
10
11
  }
package/src/platform.ts CHANGED
@@ -17,24 +17,24 @@ import NodePersist from 'node-persist';
17
17
  import Path from 'node:path';
18
18
 
19
19
  export class RoborockMatterbridgePlatform extends MatterbridgeDynamicPlatform {
20
- robot: RoborockVacuumCleaner | undefined;
20
+ robots: Map<string, RoborockVacuumCleaner> = new Map<string, RoborockVacuumCleaner>();
21
21
  rvcInterval: NodeJS.Timeout | undefined;
22
22
  roborockService: RoborockService | undefined;
23
23
  clientManager: ClientManager;
24
24
  platformRunner: PlatformRunner | undefined;
25
- devices: Map<string, Device>;
26
- serialNumber: string | undefined;
25
+ devices: Map<string, Device> = new Map<string, Device>();
27
26
  cleanModeSettings: CleanModeSettings | undefined;
28
27
  enableExperimentalFeature: ExperimentalFeatureSetting | undefined;
29
28
  persist: NodePersist.LocalStorage;
29
+ rrHomeId: number | undefined;
30
30
 
31
31
  constructor(matterbridge: Matterbridge, log: AnsiLogger, config: PlatformConfig) {
32
32
  super(matterbridge, log, config);
33
33
 
34
34
  // Verify that Matterbridge is the correct version
35
- if (this.verifyMatterbridgeVersion === undefined || typeof this.verifyMatterbridgeVersion !== 'function' || !this.verifyMatterbridgeVersion('3.0.7')) {
35
+ if (this.verifyMatterbridgeVersion === undefined || typeof this.verifyMatterbridgeVersion !== 'function' || !this.verifyMatterbridgeVersion('3.1.3')) {
36
36
  throw new Error(
37
- `This plugin requires Matterbridge version >= "3.0.7". Please update Matterbridge from ${this.matterbridge.matterbridgeVersion} to the latest version in the frontend.`,
37
+ `This plugin requires Matterbridge version >= "3.1.3". Please update Matterbridge from ${this.matterbridge.matterbridgeVersion} to the latest version in the frontend.`,
38
38
  );
39
39
  }
40
40
  this.log.info('Initializing platform:', this.config.name);
@@ -92,22 +92,33 @@ export class RoborockMatterbridgePlatform extends MatterbridgeDynamicPlatform {
92
92
  const devices = await this.roborockService.listDevices(username);
93
93
  this.log.notice('Initializing - devices: ', debugStringify(devices));
94
94
 
95
- let vacuum: Device | undefined = undefined;
95
+ let vacuums: Device[] = [];
96
96
  if ((this.config.whiteList as string[]).length > 0) {
97
- const firstDUID = (this.config.whiteList as string[])[0];
98
- const duid = firstDUID.split('-')[1];
99
- vacuum = devices.find((d) => d.duid == duid);
97
+ const whiteList = (this.config.whiteList ?? []) as string[];
98
+ for (const item of whiteList) {
99
+ const duid = item.split('-')[1];
100
+ const vacuum = devices.find((d) => d.duid == duid);
101
+ if (vacuum) {
102
+ vacuums.push(vacuum);
103
+ }
104
+ }
100
105
  } else {
101
- vacuum = devices.find((d) => isSupportedDevice(d.data.model));
106
+ vacuums = devices.filter((d) => isSupportedDevice(d.data.model));
102
107
  }
103
108
 
104
- if (!vacuum) {
109
+ if (vacuums.length === 0) {
105
110
  this.log.error('Initializing: No device found');
106
111
  return;
107
112
  }
108
- await this.roborockService.initializeMessageClient(username, vacuum, userData);
109
- this.devices.set(vacuum.serialNumber, vacuum);
110
- this.serialNumber = vacuum.serialNumber;
113
+
114
+ if (!this.enableExperimentalFeature || !this.enableExperimentalFeature.advancedFeature.enableServerMode) {
115
+ vacuums = [vacuums[0]]; // If server mode is not enabled, only use the first vacuum
116
+ }
117
+
118
+ for (const vacuum of vacuums) {
119
+ await this.roborockService.initializeMessageClient(username, vacuum, userData);
120
+ this.devices.set(vacuum.serialNumber, vacuum);
121
+ }
111
122
 
112
123
  await this.onConfigurateDevice();
113
124
  this.log.notice('onStart finished');
@@ -131,15 +142,42 @@ export class RoborockMatterbridgePlatform extends MatterbridgeDynamicPlatform {
131
142
  this.log.error('Initializing: PlatformRunner or RoborockService is undefined');
132
143
  return;
133
144
  }
134
- const vacuum = this.devices.get(this.serialNumber as string);
145
+
135
146
  const username = this.config.username as string;
136
- if (!vacuum || !username) {
147
+ if (this.devices.size === 0 || !username) {
137
148
  this.log.error('Initializing: No supported devices found');
138
149
  return;
139
150
  }
140
151
 
141
152
  // eslint-disable-next-line @typescript-eslint/no-this-alias
142
153
  const self = this;
154
+
155
+ for (const vacuum of this.devices.values()) {
156
+ await this.configurateDevice(vacuum);
157
+ this.rrHomeId = vacuum.rrHomeId;
158
+ }
159
+
160
+ this.roborockService.setDeviceNotify(async function (messageSource: NotifyMessageTypes, homeData: unknown) {
161
+ await self.platformRunner?.updateRobot(messageSource, homeData);
162
+ });
163
+
164
+ for (const robot of this.robots.values()) {
165
+ await this.roborockService.activateDeviceNotify(robot.device);
166
+ }
167
+
168
+ await this.platformRunner?.requestHomeData();
169
+
170
+ this.log.info('onConfigurateDevice finished');
171
+ }
172
+
173
+ private async configurateDevice(vacuum: Device) {
174
+ const username = this.config.username as string;
175
+
176
+ if (this.platformRunner === undefined || this.roborockService === undefined) {
177
+ this.log.error('Initializing: PlatformRunner or RoborockService is undefined');
178
+ return;
179
+ }
180
+
143
181
  await this.roborockService.initializeMessageClientForLocal(vacuum);
144
182
  const roomMap = await this.platformRunner.getRoomMapFromDevice(vacuum);
145
183
 
@@ -162,24 +200,17 @@ export class RoborockMatterbridgePlatform extends MatterbridgeDynamicPlatform {
162
200
  this.roborockService.setSupportedScenes(vacuum.duid, routineAsRoom);
163
201
  }
164
202
 
165
- this.robot = new RoborockVacuumCleaner(username, vacuum, roomMap, routineAsRoom, this.enableExperimentalFeature, this.log);
166
- this.robot.configurateHandler(behaviorHandler);
203
+ const robot = new RoborockVacuumCleaner(username, vacuum, roomMap, routineAsRoom, this.enableExperimentalFeature, this.log);
204
+ robot.configurateHandler(behaviorHandler);
167
205
 
168
206
  this.log.info('vacuum:', debugStringify(vacuum));
169
207
 
170
- this.setSelectDevice(this.robot.serialNumber ?? '', this.robot.deviceName ?? '', undefined, 'hub');
171
- if (this.validateDevice(this.robot.deviceName ?? '')) {
172
- await this.registerDevice(this.robot);
208
+ this.setSelectDevice(robot.serialNumber ?? '', robot.deviceName ?? '', undefined, 'hub');
209
+ if (this.validateDevice(robot.deviceName ?? '')) {
210
+ await this.registerDevice(robot);
173
211
  }
174
212
 
175
- this.roborockService.setDeviceNotify(async function (messageSource: NotifyMessageTypes, homeData: unknown) {
176
- await self.platformRunner?.updateRobot(messageSource, homeData);
177
- });
178
-
179
- await this.roborockService.activateDeviceNotify(this.robot.device);
180
- await self.platformRunner?.requestHomeData();
181
-
182
- this.log.info('onConfigurateDevice finished');
213
+ this.robots.set(robot.serialNumber ?? '', robot);
183
214
  }
184
215
 
185
216
  override async onShutdown(reason?: string) {
@@ -34,10 +34,10 @@ export class PlatformRunner {
34
34
 
35
35
  public async requestHomeData(): Promise<void> {
36
36
  const platform = this.platform;
37
- if (!platform.robot || !platform.robot.rrHomeId) return;
37
+ if (platform.robots.size === 0 || !platform.rrHomeId) return;
38
38
  if (platform.roborockService === undefined) return;
39
39
 
40
- const homeData = await platform.roborockService.getHomeDataForUpdating(platform.robot.rrHomeId);
40
+ const homeData = await platform.roborockService.getHomeDataForUpdating(platform.rrHomeId);
41
41
  await this.updateRobot(NotifyMessageTypes.HomeData, homeData);
42
42
  }
43
43
 
@@ -52,21 +52,39 @@ export class PlatformRunner {
52
52
  return new RoomMap([], rooms);
53
53
  }
54
54
 
55
- async getRoomMap(): Promise<RoomMap | undefined> {
55
+ async getRoomMap(duid: string): Promise<RoomMap | undefined> {
56
56
  const platform = this.platform;
57
- const rooms = platform.robot?.device.rooms ?? [];
58
- if (platform.robot?.device === undefined || platform.roborockService === undefined) return undefined;
59
- if (platform.robot.roomInfo === undefined) {
60
- const roomData = await platform.roborockService.getRoomMappings(platform.robot.device.duid);
61
- platform.robot.roomInfo = new RoomMap(roomData ?? [], rooms);
62
- return platform.robot.roomInfo;
57
+
58
+ const robot = platform.robots.get(duid);
59
+ if (robot === undefined) {
60
+ platform.log.error(`Error6: Robot with DUID ${duid} not found`);
61
+ return undefined;
63
62
  }
64
- return platform.robot.roomInfo;
63
+
64
+ if (platform.roborockService === undefined) return undefined;
65
+
66
+ const rooms = robot.device.rooms ?? [];
67
+ // if (platform.robot?.device === undefined || platform.roborockService === undefined) return undefined;
68
+ if (robot.roomInfo === undefined) {
69
+ const roomData = await platform.roborockService.getRoomMappings(robot.device.duid);
70
+ robot.roomInfo = new RoomMap(roomData ?? [], rooms);
71
+ return robot.roomInfo;
72
+ }
73
+
74
+ return robot.roomInfo;
65
75
  }
66
76
 
67
- private async updateFromMQTTMessage(messageSource: NotifyMessageTypes, messageData: unknown, tracked = false): Promise<void> {
77
+ private async updateFromMQTTMessage(messageSource: NotifyMessageTypes, messageData: unknown, duid = '', tracked = false): Promise<void> {
68
78
  const platform = this.platform;
69
- const deviceData = platform.robot?.device.data;
79
+ duid = duid || (messageData as DeviceStatusNotify)?.duid || '';
80
+
81
+ const robot = platform.robots.get(duid);
82
+ if (robot === undefined) {
83
+ platform.log.error(`Error1: Robot with DUID ${duid} not found`);
84
+ return;
85
+ }
86
+
87
+ const deviceData = robot.device.data;
70
88
  if (deviceData === undefined) {
71
89
  platform.log.error('Device data is undefined');
72
90
  return;
@@ -76,18 +94,16 @@ export class PlatformRunner {
76
94
  platform.log.debug(`${messageSource} updateFromMQTTMessage: ${debugStringify(messageData as DeviceStatusNotify)}`);
77
95
  }
78
96
 
79
- const duid = (messageData as DeviceStatusNotify).duid;
80
- if (platform.robot === undefined) return;
81
- if (!platform.robot.serialNumber) {
97
+ if (!robot.serialNumber) {
82
98
  platform.log.error('Robot serial number is undefined');
83
99
  return;
84
100
  }
85
101
 
86
102
  // duid is set as device serial number
87
- if (platform.robot.serialNumber !== duid) {
88
- platform.log.notice(`DUID mismatch: ${duid}, device serial number: ${platform.robot.serialNumber}`);
89
- return;
90
- }
103
+ // if (platform.robot.serialNumber !== duid) {
104
+ // platform.log.notice(`DUID mismatch: ${duid}, device serial number: ${platform.robot.serialNumber}`);
105
+ // return;
106
+ // }
91
107
 
92
108
  switch (messageSource) {
93
109
  case NotifyMessageTypes.ErrorOccurred: {
@@ -95,7 +111,7 @@ export class PlatformRunner {
95
111
  const operationalStateId = getOperationalErrorState(message.errorCode);
96
112
  if (operationalStateId) {
97
113
  platform.log.error(`Error occurred: ${message.errorCode}`);
98
- platform.robot.updateAttribute(RvcOperationalState.Cluster.id, 'operationalState', operationalStateId, platform.log);
114
+ robot.updateAttribute(RvcOperationalState.Cluster.id, 'operationalState', operationalStateId, platform.log);
99
115
 
100
116
  // const errorState = getErrorFromErrorCode(message.errorCode);
101
117
  // if (operationalStateId === RvcOperationalState.OperationalState.Error && errorState) {
@@ -108,18 +124,24 @@ export class PlatformRunner {
108
124
  const message = messageData as BatteryMessage;
109
125
  const batteryLevel = message.percentage;
110
126
  if (batteryLevel) {
111
- platform.robot.updateAttribute(PowerSource.Cluster.id, 'batPercentRemaining', batteryLevel * 2, platform.log);
112
- platform.robot.updateAttribute(PowerSource.Cluster.id, 'batChargeLevel', getBatteryStatus(batteryLevel), platform.log);
127
+ robot.updateAttribute(PowerSource.Cluster.id, 'batPercentRemaining', batteryLevel * 2, platform.log);
128
+ robot.updateAttribute(PowerSource.Cluster.id, 'batChargeLevel', getBatteryStatus(batteryLevel), platform.log);
113
129
  }
114
130
  break;
115
131
  }
116
132
 
117
133
  case NotifyMessageTypes.LocalMessage: {
118
134
  const data = messageData as CloudMessageResult;
135
+ const robot = platform.robots.get(duid);
136
+ if (!robot) {
137
+ platform.log.error(`Error2: Robot with DUID ${duid} not found`);
138
+ return;
139
+ }
140
+
119
141
  if (data) {
120
142
  const state = state_to_matter_state(data.state);
121
143
  if (state) {
122
- platform.robot.updateAttribute(RvcRunMode.Cluster.id, 'currentMode', getRunningMode(state), platform.log);
144
+ robot.updateAttribute(RvcRunMode.Cluster.id, 'currentMode', getRunningMode(state), platform.log);
123
145
  }
124
146
 
125
147
  const currentRoom = data.cleaning_info?.segment_id ?? -1;
@@ -127,17 +149,17 @@ export class PlatformRunner {
127
149
  const isMappedArea = currentMappedAreas?.some((x) => x.areaId == currentRoom);
128
150
 
129
151
  if (currentRoom !== -1 && isMappedArea) {
130
- const roomMap = await this.getRoomMap();
152
+ const roomMap = await this.getRoomMap(duid);
131
153
  this.platform.log.debug(`RoomMap: ${roomMap ? debugStringify(roomMap) : 'undefined'}`);
132
154
  this.platform.log.debug('CurrentRoom:', currentRoom);
133
- platform.robot.updateAttribute(ServiceArea.Cluster.id, 'currentArea', currentRoom, platform.log);
155
+ robot.updateAttribute(ServiceArea.Cluster.id, 'currentArea', currentRoom, platform.log);
134
156
  }
135
157
 
136
158
  if (data.battery) {
137
159
  const batteryLevel = data.battery as number;
138
- platform.robot.updateAttribute(PowerSource.Cluster.id, 'batPercentRemaining', batteryLevel * 2, platform.log);
139
- platform.robot.updateAttribute(PowerSource.Cluster.id, 'batChargeState', getBatteryState(data.state, data.battery), platform.log);
140
- platform.robot.updateAttribute(PowerSource.Cluster.id, 'batChargeLevel', getBatteryStatus(batteryLevel), platform.log);
160
+ robot.updateAttribute(PowerSource.Cluster.id, 'batPercentRemaining', batteryLevel * 2, platform.log);
161
+ robot.updateAttribute(PowerSource.Cluster.id, 'batChargeState', getBatteryState(data.state, data.battery), platform.log);
162
+ robot.updateAttribute(PowerSource.Cluster.id, 'batChargeLevel', getBatteryStatus(batteryLevel), platform.log);
141
163
  }
142
164
 
143
165
  const currentCleanModeSetting = {
@@ -158,11 +180,11 @@ export class PlatformRunner {
158
180
  this.platform.log.debug(`Current clean mode: ${currentCleanMode}`);
159
181
 
160
182
  if (currentCleanMode) {
161
- platform.robot.updateAttribute(RvcCleanMode.Cluster.id, 'currentMode', currentCleanMode, platform.log);
183
+ robot.updateAttribute(RvcCleanMode.Cluster.id, 'currentMode', currentCleanMode, platform.log);
162
184
  }
163
185
  }
164
186
 
165
- this.processAdditionalProps(platform.robot, data);
187
+ this.processAdditionalProps(robot, data, duid);
166
188
  }
167
189
  break;
168
190
  }
@@ -185,6 +207,12 @@ export class PlatformRunner {
185
207
  // eslint-disable-next-line @typescript-eslint/no-this-alias
186
208
  const self = this;
187
209
 
210
+ const robot = platform.robots.get(duid);
211
+ if (robot === undefined) {
212
+ platform.log.error(`Error3: Robot with DUID ${duid} not found`);
213
+ return;
214
+ }
215
+
188
216
  // Known: 122, 121, 102,
189
217
  // Unknown: 128, 139
190
218
  messageTypes.forEach(async (messageType) => {
@@ -193,14 +221,14 @@ export class PlatformRunner {
193
221
  const status = Number(data.dps[messageType]);
194
222
  const matterState = state_to_matter_state(status);
195
223
  if (matterState) {
196
- platform.robot?.updateAttribute(RvcRunMode.Cluster.id, 'currentMode', getRunningMode(matterState), platform.log);
224
+ robot.updateAttribute(RvcRunMode.Cluster.id, 'currentMode', getRunningMode(matterState), platform.log);
197
225
  }
198
226
 
199
227
  const operationalStateId = state_to_matter_operational_status(status);
200
228
  if (operationalStateId) {
201
- const dssHasError = hasDockingStationError(platform.robot?.dockStationStatus);
202
- if (!(dssHasError && self.triggerDssError())) {
203
- platform.robot?.updateAttribute(RvcOperationalState.Cluster.id, 'operationalState', operationalStateId, platform.log);
229
+ const dssHasError = hasDockingStationError(robot.dockStationStatus);
230
+ if (!(dssHasError && self.triggerDssError(robot))) {
231
+ robot.updateAttribute(RvcOperationalState.Cluster.id, 'operationalState', operationalStateId, platform.log);
204
232
  }
205
233
  }
206
234
  break;
@@ -219,18 +247,18 @@ export class PlatformRunner {
219
247
  }
220
248
 
221
249
  if (roboStatus) {
222
- const message = { duid: duid, ...roboStatus } as DeviceStatusNotify;
250
+ const message = { ...roboStatus } as DeviceStatusNotify;
223
251
  platform.log.debug('rpc_response:', debugStringify(message));
224
- await self.updateFromMQTTMessage(NotifyMessageTypes.LocalMessage, message, true);
252
+ await self.updateFromMQTTMessage(NotifyMessageTypes.LocalMessage, message, duid, true);
225
253
  }
226
254
  break;
227
255
  }
228
256
  case Protocol.suction_power:
229
257
  case Protocol.water_box_mode: {
230
258
  await platform.roborockService?.getCleanModeData(duid).then((cleanModeData) => {
231
- if (cleanModeData && platform.robot) {
259
+ if (cleanModeData) {
232
260
  const currentCleanMode = getCurrentCleanModeFunc(
233
- platform.robot.device.data.model,
261
+ robot.device.data.model,
234
262
  platform.enableExperimentalFeature?.advancedFeature?.forceRunAtDefault ?? false,
235
263
  )({
236
264
  suctionPower: cleanModeData.suctionPower,
@@ -242,7 +270,7 @@ export class PlatformRunner {
242
270
  platform.log.debug(`Clean mode data: ${debugStringify(cleanModeData)}`);
243
271
  platform.log.debug(`Current clean mode: ${currentCleanMode}`);
244
272
  if (currentCleanMode) {
245
- platform.robot?.updateAttribute(RvcCleanMode.Cluster.id, 'currentMode', currentCleanMode, platform.log);
273
+ robot.updateAttribute(RvcCleanMode.Cluster.id, 'currentMode', currentCleanMode, platform.log);
246
274
  }
247
275
  }
248
276
  });
@@ -261,17 +289,22 @@ export class PlatformRunner {
261
289
  });
262
290
  }
263
291
 
264
- private async processAdditionalProps(robot: RoborockVacuumCleaner, message: CloudMessageResult): Promise<void> {
292
+ private async processAdditionalProps(robot: RoborockVacuumCleaner, message: CloudMessageResult, duid: string): Promise<void> {
265
293
  // dss -> DockingStationStatus
266
- const dssStatus = this.getDssStatus(message);
294
+ const dssStatus = this.getDssStatus(message, duid);
267
295
  if (dssStatus) {
268
- this.triggerDssError();
296
+ this.triggerDssError(robot);
269
297
  }
270
298
  }
271
299
 
272
- private getDssStatus(message: CloudMessageResult): RvcOperationalState.OperationalState | undefined {
300
+ private getDssStatus(message: CloudMessageResult, duid: string): RvcOperationalState.OperationalState | undefined {
273
301
  const platform = this.platform;
274
- const robot = platform.robot;
302
+ const robot = platform.robots.get(duid);
303
+ if (robot === undefined) {
304
+ platform.log.error(`Error4: Robot with DUID ${duid} not found`);
305
+ return undefined;
306
+ }
307
+
275
308
  if (
276
309
  platform.enableExperimentalFeature &&
277
310
  platform.enableExperimentalFeature.enableExperimentalFeature &&
@@ -304,60 +337,74 @@ export class PlatformRunner {
304
337
 
305
338
  private updateFromHomeData(homeData: Home): void {
306
339
  const platform = this.platform;
307
- if (platform.robot === undefined) return;
308
- const device = homeData.devices.find((d: Device) => d.duid === platform.robot?.serialNumber);
309
- const deviceData = platform.robot?.device.data;
310
- if (!device || deviceData === undefined) {
311
- platform.log.error('Device not found in home data');
312
- return;
313
- }
314
340
 
315
- device.schema = homeData.products.find((prd) => prd.id == device.productId || prd.model == device.data.model)?.schema ?? [];
316
- this.platform.log.debug('updateFromHomeData-homeData:', debugStringify(homeData));
317
- this.platform.log.debug('updateFromHomeData-device:', debugStringify(device));
341
+ if (platform.robots.size === 0) return;
342
+ const devices = homeData.devices.filter((d: Device) => platform.robots.has(d.duid));
318
343
 
319
- const batteryLevel = getVacuumProperty(device, 'battery');
320
- if (batteryLevel) {
321
- platform.robot.updateAttribute(PowerSource.Cluster.id, 'batPercentRemaining', batteryLevel ? batteryLevel * 2 : 200, platform.log);
322
- platform.robot.updateAttribute(PowerSource.Cluster.id, 'batChargeLevel', getBatteryStatus(batteryLevel), platform.log);
323
- }
344
+ for (const device of devices) {
345
+ const robot = platform.robots.get(device.duid);
346
+ if (robot === undefined) {
347
+ platform.log.error(`Error5: Robot with DUID ${device.duid} not found`);
348
+ continue;
349
+ }
324
350
 
325
- const state = getVacuumProperty(device, 'state');
326
- const matterState = state_to_matter_state(state);
327
- if (!state || !matterState) {
328
- return;
329
- }
330
- this.platform.log.debug(`updateFromHomeData-RvcRunMode code: ${state} name: ${OperationStatusCode[state]}, matterState: ${RvcRunMode.ModeTag[matterState]}`);
351
+ const deviceData = robot.device.data;
352
+ if (!device || deviceData === undefined) {
353
+ platform.log.error('Device not found in home data');
354
+ return;
355
+ }
331
356
 
332
- if (matterState) {
333
- platform.robot.updateAttribute(RvcRunMode.Cluster.id, 'currentMode', getRunningMode(matterState), platform.log);
334
- }
357
+ device.schema = homeData.products.find((prd) => prd.id === device.productId || prd.model === device.data.model)?.schema ?? [];
358
+ this.platform.log.debug('updateFromHomeData-homeData:', debugStringify(homeData));
359
+ this.platform.log.debug('updateFromHomeData-device:', debugStringify(device));
335
360
 
336
- const operationalStateId = state_to_matter_operational_status(state);
361
+ const batteryLevel = getVacuumProperty(device, 'battery');
337
362
 
338
- if (operationalStateId) {
339
- const dssHasError = hasDockingStationError(platform.robot?.dockStationStatus);
340
- this.platform.log.debug(`dssHasError: ${dssHasError}, dockStationStatus: ${debugStringify(platform.robot?.dockStationStatus ?? {})}`);
341
- if (!(dssHasError && this.triggerDssError())) {
342
- this.platform.log.debug(`updateFromHomeData-OperationalState: ${RvcOperationalState.OperationalState[operationalStateId]}`);
343
- platform.robot.updateAttribute(RvcOperationalState.Cluster.id, 'operationalState', operationalStateId, platform.log);
363
+ this.platform.log.debug('updateFromHomeData-schema:' + debugStringify(device.schema));
364
+ this.platform.log.debug('updateFromHomeData-battery:' + debugStringify(device.deviceStatus));
365
+
366
+ if (batteryLevel) {
367
+ robot.updateAttribute(PowerSource.Cluster.id, 'batPercentRemaining', batteryLevel ? batteryLevel * 2 : 200, platform.log);
368
+ robot.updateAttribute(PowerSource.Cluster.id, 'batChargeLevel', getBatteryStatus(batteryLevel), platform.log);
344
369
  }
345
- }
346
370
 
347
- if (batteryLevel) {
348
- platform.robot.updateAttribute(PowerSource.Cluster.id, 'batChargeState', getBatteryState(state, batteryLevel), platform.log);
371
+ const state = getVacuumProperty(device, 'state');
372
+ const matterState = state_to_matter_state(state);
373
+ if (!state || !matterState) {
374
+ return;
375
+ }
376
+ this.platform.log.debug(`updateFromHomeData-RvcRunMode code: ${state} name: ${OperationStatusCode[state]}, matterState: ${RvcRunMode.ModeTag[matterState]}`);
377
+
378
+ if (matterState) {
379
+ robot.updateAttribute(RvcRunMode.Cluster.id, 'currentMode', getRunningMode(matterState), platform.log);
380
+ }
381
+
382
+ const operationalStateId = state_to_matter_operational_status(state);
383
+
384
+ if (operationalStateId) {
385
+ const dssHasError = hasDockingStationError(robot.dockStationStatus);
386
+ this.platform.log.debug(`dssHasError: ${dssHasError}, dockStationStatus: ${debugStringify(robot.dockStationStatus ?? {})}`);
387
+ if (!(dssHasError && this.triggerDssError(robot))) {
388
+ this.platform.log.debug(`updateFromHomeData-OperationalState: ${RvcOperationalState.OperationalState[operationalStateId]}`);
389
+ robot.updateAttribute(RvcOperationalState.Cluster.id, 'operationalState', operationalStateId, platform.log);
390
+ }
391
+ }
392
+
393
+ if (batteryLevel) {
394
+ robot.updateAttribute(PowerSource.Cluster.id, 'batChargeState', getBatteryState(state, batteryLevel), platform.log);
395
+ }
349
396
  }
350
397
  }
351
398
 
352
- private triggerDssError(): boolean {
399
+ private triggerDssError(robot: RoborockVacuumCleaner): boolean {
353
400
  const platform = this.platform;
354
- const currentOperationState = platform?.robot?.getAttribute(RvcOperationalState.Cluster.id, 'operationalState') as RvcOperationalState.OperationalState;
401
+ const currentOperationState = robot.getAttribute(RvcOperationalState.Cluster.id, 'operationalState') as RvcOperationalState.OperationalState;
355
402
  if (currentOperationState === RvcOperationalState.OperationalState.Error) {
356
403
  return true;
357
404
  }
358
405
 
359
406
  if (currentOperationState === RvcOperationalState.OperationalState.Docked) {
360
- platform.robot?.updateAttribute(RvcOperationalState.Cluster.id, 'operationalState', RvcOperationalState.OperationalState.Error, platform.log);
407
+ robot.updateAttribute(RvcOperationalState.Cluster.id, 'operationalState', RvcOperationalState.OperationalState.Error, platform.log);
361
408
  return true;
362
409
  }
363
410
 
@@ -2,7 +2,6 @@ import { ResponseMessage } from '../../model/responseMessage.js';
2
2
  import { AbstractMessageListener } from '../index.js';
3
3
  import { Protocol } from '../../model/protocol.js';
4
4
  import { AbstractMessageHandler } from '../abstractMessageHandler.js';
5
- // import { DeviceStatus } from '../../../Zmodel/deviceStatus.js';
6
5
 
7
6
  export class SimpleMessageListener implements AbstractMessageListener {
8
7
  private handler: AbstractMessageHandler | undefined;
@@ -227,7 +227,7 @@ export default class RoborockService {
227
227
  if (this.messageProcessor) {
228
228
  await this.messageProcessor.getDeviceStatus(device.duid).then((response: DeviceStatus) => {
229
229
  if (self.deviceNotify) {
230
- const message: DeviceStatusNotify = { duid: device.duid, ...response.errorStatus, ...response.message } as DeviceStatusNotify;
230
+ const message = { duid: device.duid, ...response.errorStatus, ...response.message } as DeviceStatusNotify;
231
231
  self.logger.debug('Device status update', debugStringify(message));
232
232
  self.deviceNotify(NotifyMessageTypes.LocalMessage, message);
233
233
  }
package/src/rvc.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { RoboticVacuumCleaner } from 'matterbridge';
1
+ import { RoboticVacuumCleaner } from 'matterbridge/devices';
2
2
  import RoomMap from './model/RoomMap.js';
3
3
  import { Device } from './roborockCommunication/index.js';
4
4
  import { getOperationalStates, getSupportedAreas, getSupportedCleanModes, getSupportedRunModes } from './initialData/index.js';
@@ -11,7 +11,6 @@ import { DockingStationStatus } from './model/DockingStationStatus.js';
11
11
  export class RoborockVacuumCleaner extends RoboticVacuumCleaner {
12
12
  username: string | undefined;
13
13
  device: Device;
14
- rrHomeId: number;
15
14
  roomInfo: RoomMap | undefined;
16
15
  dockStationStatus: DockingStationStatus | undefined;
17
16
 
@@ -34,9 +33,11 @@ export class RoborockVacuumCleaner extends RoboticVacuumCleaner {
34
33
  log.debug(`Supported Clean Modes: ${JSON.stringify(cleanModes)}`);
35
34
  log.debug(`Supported Run Modes: ${JSON.stringify(supportedRunModes)}`);
36
35
 
36
+ const bridgeMode = enableExperimentalFeature?.advancedFeature.enableServerMode ? 'server' : undefined;
37
37
  super(
38
38
  deviceName, // name
39
39
  device.duid, // serial
40
+ bridgeMode, // mode
40
41
  supportedRunModes[0].mode, // currentRunMode
41
42
  supportedRunModes, // supportedRunModes
42
43
  cleanModes[0].mode, // currentCleanMode
@@ -52,7 +53,6 @@ export class RoborockVacuumCleaner extends RoboticVacuumCleaner {
52
53
 
53
54
  this.username = username;
54
55
  this.device = device;
55
- this.rrHomeId = device.rrHomeId;
56
56
  }
57
57
 
58
58
  public configurateHandler(behaviorHandler: BehaviorFactoryResult): void {