matterbridge-roborock-vacuum-plugin 1.1.0-rc18 → 1.1.1-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.
Files changed (59) 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/roborockCommunication/broadcast/abstractClient.js +3 -3
  7. package/dist/roborockCommunication/broadcast/client/LocalNetworkClient.js +1 -0
  8. package/dist/roborockCommunication/broadcast/client/LocalNetworkUDPClient.js +1 -1
  9. package/dist/roborockCommunication/broadcast/clientRouter.js +5 -2
  10. package/dist/roborockCommunication/broadcast/model/contentMessage.js +1 -0
  11. package/dist/roborockCommunication/broadcast/model/headerMessage.js +1 -0
  12. package/dist/roborockCommunication/broadcast/model/messageContext.js +18 -7
  13. package/dist/roborockCommunication/broadcast/model/requestMessage.js +5 -0
  14. package/dist/roborockCommunication/helper/messageDeserializer.js +31 -18
  15. package/dist/roborockCommunication/helper/messageSerializer.js +17 -11
  16. package/dist/roborockService.js +6 -1
  17. package/dist/runtimes/handleCloudMessage.js +110 -0
  18. package/dist/runtimes/handleHomeDataMessage.js +57 -0
  19. package/dist/runtimes/handleLocalMessage.js +169 -0
  20. package/dist/rvc.js +2 -1
  21. package/dist/tests/testData/mockData.js +359 -0
  22. package/matterbridge-roborock-vacuum-plugin.config.json +2 -2
  23. package/matterbridge-roborock-vacuum-plugin.schema.json +10 -37
  24. package/package.json +2 -3
  25. package/src/behaviors/roborock.vacuum/default/runtimes.ts +1 -1
  26. package/src/behaviors/roborock.vacuum/smart/runtimes.ts +1 -1
  27. package/src/helper.ts +2 -12
  28. package/src/initialData/getSupportedAreas.ts +1 -9
  29. package/src/model/RoomMap.ts +4 -30
  30. package/src/model/roomIndexMap.ts +10 -6
  31. package/src/platform.ts +6 -3
  32. package/src/platformRunner.ts +12 -350
  33. package/src/roborockCommunication/Zmodel/device.ts +13 -1
  34. package/src/roborockCommunication/Zmodel/messageResult.ts +28 -27
  35. package/src/roborockCommunication/Zmodel/userData.ts +2 -1
  36. package/src/roborockCommunication/broadcast/abstractClient.ts +3 -3
  37. package/src/roborockCommunication/broadcast/client/LocalNetworkClient.ts +4 -2
  38. package/src/roborockCommunication/broadcast/client/LocalNetworkUDPClient.ts +3 -3
  39. package/src/roborockCommunication/broadcast/clientRouter.ts +6 -2
  40. package/src/roborockCommunication/broadcast/model/contentMessage.ts +5 -0
  41. package/src/roborockCommunication/broadcast/model/headerMessage.ts +7 -0
  42. package/src/roborockCommunication/broadcast/model/messageContext.ts +24 -11
  43. package/src/roborockCommunication/broadcast/model/requestMessage.ts +12 -5
  44. package/src/roborockCommunication/helper/messageDeserializer.ts +42 -31
  45. package/src/roborockCommunication/helper/messageSerializer.ts +19 -14
  46. package/src/roborockService.ts +8 -1
  47. package/src/runtimes/handleCloudMessage.ts +134 -0
  48. package/src/runtimes/handleHomeDataMessage.ts +67 -0
  49. package/src/runtimes/handleLocalMessage.ts +209 -0
  50. package/src/rvc.ts +2 -2
  51. package/src/share/runtimeHelper.ts +1 -1
  52. package/src/tests/helper.test.ts +59 -10
  53. package/src/tests/roborockCommunication/broadcast/client/LocalNetworkClient.test.ts +0 -19
  54. package/src/tests/roborockCommunication/broadcast/client/MQTTClient.test.ts +0 -32
  55. package/src/tests/roborockService.setSelectedAreas.test.ts +61 -0
  56. package/src/tests/runtimes/handleCloudMessage.test.ts +200 -0
  57. package/src/tests/runtimes/handleHomeDataMessage.test.ts +53 -0
  58. package/src/tests/runtimes/handleLocalMessage.test.ts +222 -0
  59. package/src/tests/testData/mockData.ts +370 -0
@@ -0,0 +1,53 @@
1
+ import { updateFromHomeData } from '../../runtimes/handleHomeDataMessage';
2
+ import { homeData } from '../testData/mockData';
3
+ import { PowerSource, RvcRunMode } from 'matterbridge/matter/clusters';
4
+
5
+ // Mocks
6
+ const mockUpdateAttribute = jest.fn();
7
+ const duid = 'test-duid';
8
+ const robot = {
9
+ updateAttribute: mockUpdateAttribute,
10
+ device: { data: { model: 'test-model' } as { model: string } | undefined },
11
+ dockStationStatus: {},
12
+ };
13
+ const platform = {
14
+ robots: new Map([[duid, robot]]),
15
+ log: {
16
+ error: jest.fn(),
17
+ debug: jest.fn(),
18
+ notice: jest.fn(),
19
+ /* eslint-disable no-console */
20
+ fatal: jest.fn().mockImplementation((message: string, ...arg: unknown[]) => console.info(message, ...arg)),
21
+ },
22
+ roborockService: {},
23
+ enableExperimentalFeature: {},
24
+ };
25
+
26
+ describe('updateFromHomeData', () => {
27
+ beforeEach(() => {
28
+ jest.clearAllMocks();
29
+ });
30
+
31
+ it('should update robot attributes when valid data is provided', async () => {
32
+ await updateFromHomeData(homeData, platform as any);
33
+
34
+ expect(mockUpdateAttribute).toHaveBeenCalledWith(PowerSource.Cluster.id, 'batPercentRemaining', 200, expect.anything());
35
+ expect(mockUpdateAttribute).toHaveBeenCalledWith(PowerSource.Cluster.id, 'batChargeLevel', 0, expect.anything());
36
+ expect(mockUpdateAttribute).toHaveBeenCalledWith(RvcRunMode.Cluster.id, 'currentMode', 1, expect.anything());
37
+ expect(mockUpdateAttribute).toHaveBeenCalledWith(PowerSource.Cluster.id, 'batChargeState', PowerSource.BatChargeState.IsAtFullCharge, expect.anything());
38
+ expect(platform.log.error).not.toHaveBeenCalled();
39
+ });
40
+
41
+ it('should log error if robot is not found', async () => {
42
+ platform.robots.clear();
43
+ await updateFromHomeData(homeData, platform as any);
44
+ expect(platform.log.error).not.toHaveBeenCalledWith(expect.stringContaining('Robot with DUID'));
45
+ });
46
+
47
+ it('should log error if device data is undefined', async () => {
48
+ platform.robots.clear();
49
+ platform.robots.set('test-duid', { ...robot, device: { data: undefined } });
50
+ await updateFromHomeData(homeData, platform as any);
51
+ expect(platform.log.error).toHaveBeenCalledWith('Device not found in home data');
52
+ });
53
+ });
@@ -0,0 +1,222 @@
1
+ import { handleLocalMessage } from '../../runtimes/handleLocalMessage';
2
+ import { OperationStatusCode } from '../../roborockCommunication/Zenum/operationStatusCode';
3
+ import { ServiceArea, PowerSource } from 'matterbridge/matter/clusters';
4
+ import { cloudMessageResult1, cloudMessageResult2, cloudMessageResult3, mapInfo } from '../testData/mockData';
5
+ import { RoomIndexMap } from '../../model/roomIndexMap';
6
+
7
+ // Mocks
8
+ const mockUpdateAttribute = jest.fn();
9
+ const mockGetSupportedAreas = jest.fn();
10
+ const mockGetSupportedAreasIndexMap = jest.fn();
11
+ const mockGetRoomMap = jest.fn();
12
+
13
+ jest.mock('./src/helper', () => ({
14
+ getRoomMap: mockGetRoomMap,
15
+ }));
16
+
17
+ const mockLog = {
18
+ error: jest.fn(),
19
+ debug: jest.fn(),
20
+ notice: jest.fn(),
21
+ info: jest.fn(),
22
+ warn: jest.fn(),
23
+ /* eslint-disable no-console */
24
+ fatal: jest.fn().mockImplementation((message: string, ...arg: unknown[]) => console.info(message, ...arg)),
25
+ };
26
+
27
+ const getMockRobot = () => ({
28
+ device: { data: { model: 'test-model' } },
29
+ updateAttribute: mockUpdateAttribute,
30
+ getAttribute: jest.fn(() => undefined),
31
+ });
32
+
33
+ describe('handleLocalMessage -- FF ON', () => {
34
+ const getMockPlatform = (robotExists = true) => ({
35
+ robots: new Map(robotExists ? [['duid1', getMockRobot()]] : []),
36
+ log: mockLog,
37
+ roborockService: {
38
+ getSelectedAreas: jest.fn(() => ['area1']),
39
+ getSupportedAreas: mockGetSupportedAreas,
40
+ getSupportedAreasIndexMap: mockGetSupportedAreasIndexMap,
41
+ getMapInformation: jest.fn().mockReturnValue(mapInfo),
42
+ },
43
+ enableExperimentalFeature: {
44
+ enableExperimentalFeature: true,
45
+ advancedFeature: {
46
+ enableMultipleMap: true,
47
+ },
48
+ },
49
+ });
50
+
51
+ beforeEach(() => {
52
+ jest.clearAllMocks();
53
+ });
54
+
55
+ it('logs error if robot not found', async () => {
56
+ const platform = getMockPlatform(false);
57
+ await handleLocalMessage({ state: 0 } as any, platform as any, 'duid1');
58
+ expect(mockLog.error).toHaveBeenCalledWith('Robot with DUID duid1 not found.');
59
+ });
60
+
61
+ it('updates selectedAreas on Idle', async () => {
62
+ const platform = getMockPlatform();
63
+ await handleLocalMessage({ state: OperationStatusCode.Idle } as any, platform as any, 'duid1');
64
+ expect(mockUpdateAttribute).toHaveBeenCalledWith(ServiceArea.Cluster.id, 'selectedAreas', ['area1'], mockLog);
65
+ });
66
+
67
+ it('updates currentArea and selectedAreas to null/[] when cleaning_info is missing', async () => {
68
+ const platform = getMockPlatform();
69
+ await handleLocalMessage({ state: 5 } as any, platform as any, 'duid1');
70
+ await new Promise(process.nextTick);
71
+ expect(mockUpdateAttribute).toHaveBeenCalledWith(ServiceArea.Cluster.id, 'currentArea', null, mockLog);
72
+ expect(mockUpdateAttribute).toHaveBeenCalledWith(ServiceArea.Cluster.id, 'selectedAreas', [], mockLog);
73
+ });
74
+
75
+ it('updates battery attributes if battery present', async () => {
76
+ const platform = getMockPlatform();
77
+ await handleLocalMessage({ state: 0, battery: 50 } as any, platform as any, 'duid1');
78
+ expect(mockUpdateAttribute).toHaveBeenCalledWith(PowerSource.Cluster.id, 'batPercentRemaining', 100, mockLog);
79
+ });
80
+
81
+ it('updates currentArea based on segment_id', async () => {
82
+ const platform = getMockPlatform();
83
+
84
+ // currentMappedAreas
85
+ mockGetSupportedAreas.mockReturnValue([
86
+ { areaId: 100, mapId: 0, areaInfo: { locationInfo: { locationName: 'Kitchen', floorNumber: 0, areaType: null }, landmarkInfo: null } },
87
+ { areaId: 101, mapId: 0, areaInfo: { locationInfo: { locationName: 'Study', floorNumber: 0, areaType: null }, landmarkInfo: null } },
88
+ { areaId: 102, mapId: 0, areaInfo: { locationInfo: { locationName: 'Living room', floorNumber: 0, areaType: null }, landmarkInfo: null } },
89
+ { areaId: 103, mapId: 0, areaInfo: { locationInfo: { locationName: 'Bedroom', floorNumber: 0, areaType: null }, landmarkInfo: null } },
90
+ { areaId: 104, mapId: 1, areaInfo: { locationInfo: { locationName: 'Dining room', floorNumber: 1, areaType: null }, landmarkInfo: null } },
91
+ { areaId: 105, mapId: 1, areaInfo: { locationInfo: { locationName: 'Guest bedroom', floorNumber: 1, areaType: null }, landmarkInfo: null } },
92
+ { areaId: 106, mapId: 1, areaInfo: { locationInfo: { locationName: 'Master bedroom', floorNumber: 1, areaType: null }, landmarkInfo: null } },
93
+ { areaId: 107, mapId: 1, areaInfo: { locationInfo: { locationName: 'Balcony', floorNumber: 1, areaType: null }, landmarkInfo: null } },
94
+ { areaId: 108, mapId: 1, areaInfo: { locationInfo: { locationName: 'Living room', floorNumber: 1, areaType: null }, landmarkInfo: null } },
95
+ ]);
96
+
97
+ const roomIndexMap = new RoomIndexMap(
98
+ new Map([
99
+ [100, { roomId: 1, mapId: 0 }],
100
+ [101, { roomId: 2, mapId: 0 }],
101
+ [102, { roomId: 3, mapId: 0 }],
102
+ [103, { roomId: 4, mapId: 0 }],
103
+ [104, { roomId: 1, mapId: 1 }],
104
+ [105, { roomId: 2, mapId: 1 }],
105
+ [106, { roomId: 3, mapId: 1 }],
106
+ [107, { roomId: 4, mapId: 1 }],
107
+ [108, { roomId: 5, mapId: 1 }],
108
+ ]),
109
+ );
110
+
111
+ // roomIndexMap
112
+ mockGetSupportedAreasIndexMap.mockReturnValue(roomIndexMap);
113
+
114
+ await handleLocalMessage(cloudMessageResult3, platform as any, 'duid1');
115
+ expect(mockUpdateAttribute).toHaveBeenCalledWith(ServiceArea.Cluster.id, 'currentArea', null, mockLog);
116
+ expect(mockLog.debug).toHaveBeenCalledWith(expect.stringContaining('No cleaning_info'));
117
+
118
+ await handleLocalMessage(cloudMessageResult1 as any, platform as any, 'duid1');
119
+ expect(mockUpdateAttribute).toHaveBeenCalledWith(ServiceArea.Cluster.id, 'currentArea', 102, mockLog);
120
+
121
+ await handleLocalMessage(cloudMessageResult2 as any, platform as any, 'duid1');
122
+ expect(mockUpdateAttribute).toHaveBeenCalledWith(ServiceArea.Cluster.id, 'currentArea', 103, mockLog);
123
+ });
124
+ });
125
+
126
+ describe('handleLocalMessage -- FF OFF', () => {
127
+ const getMockPlatform = (robotExists = true) => ({
128
+ robots: new Map(robotExists ? [['duid1', getMockRobot()]] : []),
129
+ log: mockLog,
130
+ roborockService: {
131
+ getSelectedAreas: jest.fn(() => ['area1']),
132
+ getSupportedAreas: mockGetSupportedAreas,
133
+ getSupportedAreasIndexMap: mockGetSupportedAreasIndexMap,
134
+ getMapInformation: jest.fn().mockReturnValue(mapInfo),
135
+ },
136
+ enableExperimentalFeature: {
137
+ enableExperimentalFeature: false,
138
+ advancedFeature: {
139
+ enableMultipleMap: false,
140
+ },
141
+ },
142
+ });
143
+
144
+ beforeEach(() => {
145
+ jest.clearAllMocks();
146
+ });
147
+
148
+ it('logs error if robot not found', async () => {
149
+ const platform = getMockPlatform(false);
150
+ await handleLocalMessage({ state: 0 } as any, platform as any, 'duid1');
151
+ expect(mockLog.error).toHaveBeenCalledWith('Robot with DUID duid1 not found.');
152
+ });
153
+
154
+ it('updates selectedAreas on Idle', async () => {
155
+ const platform = getMockPlatform();
156
+ await handleLocalMessage({ state: OperationStatusCode.Idle } as any, platform as any, 'duid1');
157
+ expect(mockUpdateAttribute).toHaveBeenCalledWith(ServiceArea.Cluster.id, 'selectedAreas', ['area1'], mockLog);
158
+ });
159
+
160
+ it('updates currentArea and selectedAreas to null/[] when cleaning_info is missing', async () => {
161
+ const platform = getMockPlatform();
162
+ await handleLocalMessage({ state: 5 } as any, platform as any, 'duid1');
163
+ await new Promise(process.nextTick);
164
+ expect(mockUpdateAttribute).toHaveBeenCalledWith(ServiceArea.Cluster.id, 'currentArea', null, mockLog);
165
+ expect(mockUpdateAttribute).toHaveBeenCalledWith(ServiceArea.Cluster.id, 'selectedAreas', [], mockLog);
166
+ });
167
+
168
+ it('updates battery attributes if battery present', async () => {
169
+ const platform = getMockPlatform();
170
+ await handleLocalMessage({ state: 0, battery: 50 } as any, platform as any, 'duid1');
171
+ expect(mockUpdateAttribute).toHaveBeenCalledWith(PowerSource.Cluster.id, 'batPercentRemaining', 100, mockLog);
172
+ });
173
+
174
+ it('updates currentArea based on segment_id', async () => {
175
+ const platform = getMockPlatform();
176
+ // currentMappedAreas
177
+ mockGetSupportedAreas.mockReturnValue([
178
+ { areaId: 1, mapId: 0, areaInfo: { locationInfo: { locationName: 'Kitchen', floorNumber: 0, areaType: null }, landmarkInfo: null } },
179
+ { areaId: 2, mapId: 0, areaInfo: { locationInfo: { locationName: 'Study', floorNumber: 0, areaType: null }, landmarkInfo: null } },
180
+ { areaId: 3, mapId: 0, areaInfo: { locationInfo: { locationName: 'Living room', floorNumber: 0, areaType: null }, landmarkInfo: null } },
181
+ { areaId: 4, mapId: 0, areaInfo: { locationInfo: { locationName: 'Bedroom', floorNumber: 0, areaType: null }, landmarkInfo: null } },
182
+ ]);
183
+
184
+ // roomIndexMap
185
+ mockGetSupportedAreasIndexMap.mockReturnValue({
186
+ indexMap: new Map([
187
+ [1, { roomId: 1, mapId: 0 }],
188
+ [2, { roomId: 2, mapId: 0 }],
189
+ [3, { roomId: 3, mapId: 0 }],
190
+ [4, { roomId: 4, mapId: 0 }],
191
+ ]),
192
+ roomMap: new Map([
193
+ [1, 1],
194
+ [2, 2],
195
+ [3, 3],
196
+ [4, 4],
197
+ ]),
198
+
199
+ getAreaId(roomId: number): { roomId: number; mapId: number } | undefined {
200
+ const index = this.roomMap.get(roomId);
201
+ if (index === undefined) {
202
+ return undefined;
203
+ }
204
+ return { roomId: index, mapId: this.indexMap.get(index).mapId };
205
+ },
206
+
207
+ getRoomId(areaId: number): number | undefined {
208
+ return this.indexMap.get(areaId)?.roomId;
209
+ },
210
+ });
211
+
212
+ await handleLocalMessage(cloudMessageResult3 as any, platform as any, 'duid1');
213
+ expect(mockUpdateAttribute).toHaveBeenCalledWith(ServiceArea.Cluster.id, 'currentArea', null, mockLog);
214
+ expect(mockLog.debug).toHaveBeenCalledWith(expect.stringContaining('No cleaning_info'));
215
+
216
+ await handleLocalMessage(cloudMessageResult1 as any, platform as any, 'duid1');
217
+ expect(mockUpdateAttribute).toHaveBeenCalledWith(ServiceArea.Cluster.id, 'currentArea', 3, mockLog);
218
+
219
+ await handleLocalMessage(cloudMessageResult2 as any, platform as any, 'duid1');
220
+ expect(mockUpdateAttribute).toHaveBeenCalledWith(ServiceArea.Cluster.id, 'currentArea', 4, mockLog);
221
+ });
222
+ });
@@ -0,0 +1,370 @@
1
+ import { ServiceArea } from 'matterbridge/matter/clusters';
2
+ import { Home, MapInfo } from '../../roborockCommunication/index.js';
3
+ import { CloudMessageResult } from '../../roborockCommunication/Zmodel/messageResult.js';
4
+
5
+ export const supportedAreas: ServiceArea.Area[] = [
6
+ { areaId: 100, mapId: 0, areaInfo: { locationInfo: { locationName: 'Kitchen', floorNumber: 0, areaType: null }, landmarkInfo: null } },
7
+ { areaId: 101, mapId: 0, areaInfo: { locationInfo: { locationName: 'Study', floorNumber: 0, areaType: null }, landmarkInfo: null } },
8
+ { areaId: 102, mapId: 0, areaInfo: { locationInfo: { locationName: 'Living room', floorNumber: 0, areaType: null }, landmarkInfo: null } },
9
+ { areaId: 103, mapId: 0, areaInfo: { locationInfo: { locationName: 'Bedroom', floorNumber: 0, areaType: null }, landmarkInfo: null } },
10
+ { areaId: 104, mapId: 1, areaInfo: { locationInfo: { locationName: 'Living room', floorNumber: 1, areaType: null }, landmarkInfo: null } },
11
+ { areaId: 105, mapId: 1, areaInfo: { locationInfo: { locationName: 'Guest bedroom', floorNumber: 1, areaType: null }, landmarkInfo: null } },
12
+ { areaId: 106, mapId: 1, areaInfo: { locationInfo: { locationName: 'Master bedroom', floorNumber: 1, areaType: null }, landmarkInfo: null } },
13
+ { areaId: 107, mapId: 1, areaInfo: { locationInfo: { locationName: 'Balcony', floorNumber: 1, areaType: null }, landmarkInfo: null } },
14
+ ];
15
+
16
+ export const supportedMaps: ServiceArea.Map[] = [
17
+ { mapId: 0, name: 'First Map' },
18
+ { mapId: 1, name: 'Second Map' },
19
+ ];
20
+
21
+ export const roomIndexMap = {
22
+ indexMap: new Map([
23
+ [100, { roomId: 1, mapId: 0 }],
24
+ [101, { roomId: 2, mapId: 0 }],
25
+ [102, { roomId: 3, mapId: 0 }],
26
+ [103, { roomId: 4, mapId: 0 }],
27
+ [104, { roomId: 1, mapId: 1 }],
28
+ [105, { roomId: 2, mapId: 1 }],
29
+ [106, { roomId: 3, mapId: 1 }],
30
+ [107, { roomId: 4, mapId: 1 }],
31
+ ]),
32
+ roomMap: new Map([
33
+ [1, 104],
34
+ [2, 105],
35
+ [3, 106],
36
+ [4, 107],
37
+ ]),
38
+ };
39
+
40
+ export const mapInfo = new MapInfo({
41
+ max_multi_map: 1,
42
+ max_bak_map: 1,
43
+ multi_map_count: 1,
44
+ map_info: [
45
+ {
46
+ mapFlag: 0,
47
+ add_time: 1753511673,
48
+ length: 9,
49
+ name: 'First Map',
50
+ bak_maps: [{ mapFlag: 4, add_time: 1753578164 }],
51
+ rooms: [
52
+ { id: 1, tag: 14, iot_name_id: '11100845', iot_name: 'Kitchen' },
53
+ { id: 2, tag: 9, iot_name_id: '11100849', iot_name: 'Study' },
54
+ {
55
+ id: 3,
56
+ tag: 6,
57
+ iot_name_id: '11100842',
58
+ iot_name: 'Living room',
59
+ },
60
+ { id: 4, tag: 1, iot_name_id: '11100847', iot_name: 'Bedroom' },
61
+ ],
62
+ },
63
+ {
64
+ mapFlag: 1,
65
+ add_time: 1753579596,
66
+ length: 10,
67
+ name: 'Second Map',
68
+ bak_maps: [{ mapFlag: 5, add_time: 1753578579 }],
69
+ rooms: [
70
+ {
71
+ id: 1,
72
+ tag: 6,
73
+ iot_name_id: '11100842',
74
+ iot_name: 'Living room',
75
+ },
76
+ {
77
+ id: 2,
78
+ tag: 3,
79
+ iot_name_id: '12461114',
80
+ iot_name: 'Guest bedroom',
81
+ },
82
+ {
83
+ id: 3,
84
+ tag: 2,
85
+ iot_name_id: '12461109',
86
+ iot_name: 'Master bedroom',
87
+ },
88
+ { id: 4, tag: 7, iot_name_id: '12461111', iot_name: 'Balcony' },
89
+ ],
90
+ },
91
+ ],
92
+ });
93
+
94
+ export const roomData = [
95
+ [1, '11100845', 14],
96
+ [2, '11100849', 9],
97
+ [3, '11100842', 6],
98
+ [4, '11100847', 1],
99
+ ];
100
+
101
+ export const cloudMessageResult1: CloudMessageResult = {
102
+ msg_ver: 2,
103
+ msg_seq: 424,
104
+ state: 5,
105
+ battery: 100,
106
+ clean_time: 2823,
107
+ clean_area: 36002500,
108
+ error_code: 0,
109
+ map_present: 1,
110
+ in_cleaning: 0,
111
+ in_returning: 0,
112
+ in_fresh_state: 1,
113
+ lab_status: 3,
114
+ water_box_status: 1,
115
+ back_type: -1,
116
+ wash_phase: 0,
117
+ wash_ready: 1,
118
+ wash_status: 0,
119
+ fan_power: 110,
120
+ dnd_enabled: 0,
121
+ map_status: 3,
122
+ is_locating: 0,
123
+ lock_status: 0,
124
+ water_box_mode: 209,
125
+ distance_off: 155,
126
+ water_box_carriage_status: 1,
127
+ mop_forbidden_enable: 1,
128
+ camera_status: 1,
129
+ is_exploring: 0,
130
+ adbumper_status: [0, 0, 0],
131
+ water_shortage_status: 0,
132
+ dock_type: 14,
133
+ dust_collection_status: 0,
134
+ auto_dust_collection: 1,
135
+ avoid_count: 209,
136
+ mop_mode: 306,
137
+ debug_mode: 0,
138
+ in_warmup: 0,
139
+ collision_avoid_status: 1,
140
+ switch_map_mode: 1,
141
+ dock_error_status: 0,
142
+ charge_status: 1,
143
+ unsave_map_reason: 0,
144
+ unsave_map_flag: -1,
145
+ dry_status: 0,
146
+ rdt: 0,
147
+ clean_percent: 0,
148
+ extra_time: 860,
149
+ rss: 2,
150
+ dss: 168,
151
+ common_status: 2,
152
+ last_clean_t: 1754063701,
153
+ replenish_mode: 0,
154
+ repeat: 1,
155
+ kct: 0,
156
+ subdivision_sets: 0,
157
+ cleaning_info: { target_segment_id: -1, segment_id: 3, fan_power: 102, water_box_status: 202, mop_mode: 306 },
158
+ exit_dock: 0,
159
+ seq_type: 0,
160
+ };
161
+
162
+ export const cloudMessageResult2: CloudMessageResult = {
163
+ msg_ver: 2,
164
+ msg_seq: 424,
165
+ state: 5,
166
+ battery: 100,
167
+ clean_time: 2823,
168
+ clean_area: 36002500,
169
+ error_code: 0,
170
+ map_present: 1,
171
+ in_cleaning: 0,
172
+ in_returning: 0,
173
+ in_fresh_state: 1,
174
+ lab_status: 3,
175
+ water_box_status: 1,
176
+ back_type: -1,
177
+ wash_phase: 0,
178
+ wash_ready: 1,
179
+ wash_status: 0,
180
+ fan_power: 110,
181
+ dnd_enabled: 0,
182
+ map_status: 3,
183
+ is_locating: 0,
184
+ lock_status: 0,
185
+ water_box_mode: 209,
186
+ distance_off: 155,
187
+ water_box_carriage_status: 1,
188
+ mop_forbidden_enable: 1,
189
+ camera_status: 1,
190
+ is_exploring: 0,
191
+ adbumper_status: [0, 0, 0],
192
+ water_shortage_status: 0,
193
+ dock_type: 14,
194
+ dust_collection_status: 0,
195
+ auto_dust_collection: 1,
196
+ avoid_count: 209,
197
+ mop_mode: 306,
198
+ debug_mode: 0,
199
+ in_warmup: 0,
200
+ collision_avoid_status: 1,
201
+ switch_map_mode: 1,
202
+ dock_error_status: 0,
203
+ charge_status: 1,
204
+ unsave_map_reason: 0,
205
+ unsave_map_flag: -1,
206
+ dry_status: 0,
207
+ rdt: 0,
208
+ clean_percent: 0,
209
+ extra_time: 860,
210
+ rss: 2,
211
+ dss: 168,
212
+ common_status: 2,
213
+ last_clean_t: 1754063701,
214
+ replenish_mode: 0,
215
+ repeat: 1,
216
+ kct: 0,
217
+ subdivision_sets: 0,
218
+ cleaning_info: { target_segment_id: 4, segment_id: -1, fan_power: 102, water_box_status: 202, mop_mode: 306 },
219
+ exit_dock: 0,
220
+ seq_type: 0,
221
+ };
222
+
223
+ export const cloudMessageResult3: CloudMessageResult = {
224
+ msg_ver: 2,
225
+ msg_seq: 1579,
226
+ state: 5,
227
+ battery: 94,
228
+ clean_time: 567,
229
+ clean_area: 36002500,
230
+ error_code: 0,
231
+ map_present: 1,
232
+ in_cleaning: 0,
233
+ in_returning: 0,
234
+ in_fresh_state: 1,
235
+ lab_status: 3,
236
+ water_box_status: 1,
237
+ fan_power: 104,
238
+ dnd_enabled: 0,
239
+ map_status: 3,
240
+ is_locating: 0,
241
+ lock_status: 0,
242
+ water_box_mode: 202,
243
+ distance_off: 60,
244
+ water_box_carriage_status: 0,
245
+ mop_forbidden_enable: 0,
246
+ adbumper_status: [0, 0, 0],
247
+ dock_type: 5,
248
+ dust_collection_status: 0,
249
+ auto_dust_collection: 1,
250
+ debug_mode: 0,
251
+ switch_map_mode: 0,
252
+ dock_error_status: 0,
253
+ charge_status: 1,
254
+ };
255
+
256
+ export const homeData: Home = {
257
+ id: 3645093,
258
+ name: 'My Home',
259
+ products: [
260
+ {
261
+ id: '2CjvhDFL7Q9NdJQmhE86zn',
262
+ name: 'Roborock Qrevo Edge Series',
263
+ model: 'test-model',
264
+ category: 'robot.vacuum.cleaner',
265
+ schema: [
266
+ { id: 101, name: 'rpc_request', code: 'rpc_request', mode: 'rw', type: 'RAW', property: null },
267
+ { id: 102, name: 'rpc_response', code: 'rpc_response', mode: 'rw', type: 'RAW', property: null },
268
+ { id: 120, name: '错误代码', code: 'error_code', mode: 'ro', type: 'ENUM', property: '{"range": [""]}' },
269
+ { id: 121, name: '设备状态', code: 'state', mode: 'ro', type: 'ENUM', property: '{"range": [""]}' },
270
+ { id: 122, name: '设备电量', code: 'battery', mode: 'ro', type: 'ENUM', property: '{"range": [""]}' },
271
+ { id: 123, name: '清扫模式', code: 'fan_power', mode: 'rw', type: 'ENUM', property: '{"range": [""]}' },
272
+ { id: 124, name: '拖地模式', code: 'water_box_mode', mode: 'rw', type: 'ENUM', property: '{"range": [""]}' },
273
+ { id: 125, name: '主刷寿命', code: 'main_brush_life', mode: 'rw', type: 'VALUE', property: '{"max": 100, "min": 0, "step": 1, "unit": "null", "scale": 1}' },
274
+ { id: 126, name: '边刷寿命', code: 'side_brush_life', mode: 'rw', type: 'VALUE', property: '{"max": 100, "min": 0, "step": 1, "unit": "null", "scale": 1}' },
275
+ { id: 127, name: '滤网寿命', code: 'filter_life', mode: 'rw', type: 'VALUE', property: '{"max": 100, "min": 0, "step": 1, "unit": "null", "scale": 1}' },
276
+ { id: 128, name: '额外状态', code: 'additional_props', mode: 'ro', type: 'RAW', property: null },
277
+ { id: 130, name: '完成事件', code: 'task_complete', mode: 'ro', type: 'RAW', property: null },
278
+ { id: 131, name: '电量不足任务取消', code: 'task_cancel_low_power', mode: 'ro', type: 'RAW', property: null },
279
+ { id: 132, name: '运动中任务取消', code: 'task_cancel_in_motion', mode: 'ro', type: 'RAW', property: null },
280
+ { id: 133, name: '充电状态', code: 'charge_status', mode: 'ro', type: 'RAW', property: null },
281
+ { id: 134, name: '烘干状态', code: 'drying_status', mode: 'ro', type: 'RAW', property: null },
282
+ { id: 135, name: '离线原因细分', code: 'offline_status', mode: 'ro', type: 'RAW', property: null },
283
+ { id: 139, name: '回基站目的', code: 'back_type', mode: 'ro', type: 'RAW', property: null },
284
+ ],
285
+ },
286
+ ],
287
+ devices: [
288
+ {
289
+ duid: 'test-duid',
290
+ name: 'Roborock Qrevo Edge 5V1',
291
+ activeTime: 1749443275,
292
+ createTime: 1746940587,
293
+ localKey: 'v0OKpWXwBmiCk4ku',
294
+ productId: '2CjvhDFL7Q9NdJQmhE86zn',
295
+ online: true,
296
+ fv: '02.28.34',
297
+ pv: '1.0',
298
+ sn: 'RCIEBS50900224',
299
+ featureSet: '2247397454282751',
300
+ newFeatureSet: '00040040282834C9C2FA8F5C7EDEFFFE',
301
+ deviceStatus: { 120: 0, 121: 8, 122: 100, 123: 110, 124: 209, 125: 93, 126: 69, 127: 86, 128: 0, 133: 1, 134: 0, 135: 0, 139: 0 },
302
+ silentOtaSwitch: true,
303
+ rrHomeId: 3645093,
304
+ rooms: [
305
+ { id: 11100849, name: 'Study' },
306
+ { id: 11100847, name: 'Bedroom' },
307
+ { id: 11100845, name: 'Kitchen' },
308
+ { id: 11100842, name: 'Living room' },
309
+ ],
310
+ serialNumber: 'RCIEBS50900224',
311
+ data: {
312
+ id: 'test-duid',
313
+ firmwareVersion: '02.28.34',
314
+ serialNumber: 'RCIEBS50900224',
315
+ model: 'test-model',
316
+ category: 'robot.vacuum.cleaner',
317
+ batteryLevel: 100,
318
+ },
319
+ store: {
320
+ userData: {
321
+ uid: 3635748,
322
+ tokentype: '',
323
+ token: 'rr65af7107da5840:txP8ZF7dj8v7xUMkoFMzZA==:01981b12f83a7723a1cbef8c8e89a7e1',
324
+ rruid: 'rr65af7107da5840',
325
+ region: 'us',
326
+ countrycode: '84',
327
+ country: 'VN',
328
+ nickname: 'Ryan',
329
+ rriot: {
330
+ u: '6BtaRwE14spvanEazqX0kQ',
331
+ s: 'OsErWk',
332
+ h: '195Xn4u3fe',
333
+ k: 'ofKw7nJc',
334
+ r: { r: 'US', a: 'https://api-us.roborock.com', m: 'ssl://mqtt-us-2.roborock.com:8883', l: 'https://wood-us.roborock.com' },
335
+ },
336
+ },
337
+ localKey: 'v0OKpWXwBmiCk4ku',
338
+ pv: '1.0',
339
+ model: 'test-model',
340
+ },
341
+ schema: [
342
+ { id: 101, name: 'rpc_request', code: 'rpc_request', mode: 'rw', type: 'RAW', property: null },
343
+ { id: 102, name: 'rpc_response', code: 'rpc_response', mode: 'rw', type: 'RAW', property: null },
344
+ { id: 120, name: '错误代码', code: 'error_code', mode: 'ro', type: 'ENUM', property: '{"range": [""]}' },
345
+ { id: 121, name: '设备状态', code: 'state', mode: 'ro', type: 'ENUM', property: '{"range": [""]}' },
346
+ { id: 122, name: '设备电量', code: 'battery', mode: 'ro', type: 'ENUM', property: '{"range": [""]}' },
347
+ { id: 123, name: '清扫模式', code: 'fan_power', mode: 'rw', type: 'ENUM', property: '{"range": [""]}' },
348
+ { id: 124, name: '拖地模式', code: 'water_box_mode', mode: 'rw', type: 'ENUM', property: '{"range": [""]}' },
349
+ { id: 125, name: '主刷寿命', code: 'main_brush_life', mode: 'rw', type: 'VALUE', property: '{"max": 100, "min": 0, "step": 1, "unit": "null", "scale": 1}' },
350
+ { id: 126, name: '边刷寿命', code: 'side_brush_life', mode: 'rw', type: 'VALUE', property: '{"max": 100, "min": 0, "step": 1, "unit": "null", "scale": 1}' },
351
+ { id: 127, name: '滤网寿命', code: 'filter_life', mode: 'rw', type: 'VALUE', property: '{"max": 100, "min": 0, "step": 1, "unit": "null", "scale": 1}' },
352
+ { id: 128, name: '额外状态', code: 'additional_props', mode: 'ro', type: 'RAW', property: null },
353
+ { id: 130, name: '完成事件', code: 'task_complete', mode: 'ro', type: 'RAW', property: null },
354
+ { id: 131, name: '电量不足任务取消', code: 'task_cancel_low_power', mode: 'ro', type: 'RAW', property: null },
355
+ { id: 132, name: '运动中任务取消', code: 'task_cancel_in_motion', mode: 'ro', type: 'RAW', property: null },
356
+ { id: 133, name: '充电状态', code: 'charge_status', mode: 'ro', type: 'RAW', property: null },
357
+ { id: 134, name: '烘干状态', code: 'drying_status', mode: 'ro', type: 'RAW', property: null },
358
+ { id: 135, name: '离线原因细分', code: 'offline_status', mode: 'ro', type: 'RAW', property: null },
359
+ { id: 139, name: '回基站目的', code: 'back_type', mode: 'ro', type: 'RAW', property: null },
360
+ ],
361
+ },
362
+ ],
363
+ receivedDevices: [],
364
+ rooms: [
365
+ { id: 11100849, name: 'Study' },
366
+ { id: 11100847, name: 'Bedroom' },
367
+ { id: 11100845, name: 'Kitchen' },
368
+ { id: 11100842, name: 'Living room' },
369
+ ],
370
+ };