matterbridge-roborock-vacuum-plugin 1.1.0-rc17 → 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 (34) hide show
  1. package/dist/helper.js +2 -7
  2. package/dist/initialData/getSupportedAreas.js +1 -1
  3. package/dist/model/RoomMap.js +1 -1
  4. package/dist/model/roomIndexMap.js +8 -4
  5. package/dist/platform.js +4 -3
  6. package/dist/platformRunner.js +10 -266
  7. package/dist/runtimes/handleCloudMessage.js +110 -0
  8. package/dist/runtimes/handleHomeDataMessage.js +57 -0
  9. package/dist/runtimes/handleLocalMessage.js +169 -0
  10. package/dist/tests/testData/mockData.js +359 -0
  11. package/matterbridge-roborock-vacuum-plugin.config.json +2 -2
  12. package/matterbridge-roborock-vacuum-plugin.schema.json +10 -37
  13. package/package.json +2 -2
  14. package/src/behaviors/roborock.vacuum/default/runtimes.ts +1 -1
  15. package/src/behaviors/roborock.vacuum/smart/runtimes.ts +1 -1
  16. package/src/helper.ts +2 -12
  17. package/src/initialData/getSupportedAreas.ts +2 -10
  18. package/src/model/RoomMap.ts +4 -30
  19. package/src/model/roomIndexMap.ts +10 -6
  20. package/src/platform.ts +6 -3
  21. package/src/platformRunner.ts +12 -349
  22. package/src/roborockCommunication/Zmodel/device.ts +13 -1
  23. package/src/roborockCommunication/Zmodel/messageResult.ts +28 -27
  24. package/src/roborockCommunication/Zmodel/userData.ts +2 -1
  25. package/src/runtimes/handleCloudMessage.ts +134 -0
  26. package/src/runtimes/handleHomeDataMessage.ts +67 -0
  27. package/src/runtimes/handleLocalMessage.ts +209 -0
  28. package/src/share/runtimeHelper.ts +1 -1
  29. package/src/tests/helper.test.ts +59 -10
  30. package/src/tests/roborockService.setSelectedAreas.test.ts +61 -0
  31. package/src/tests/runtimes/handleCloudMessage.test.ts +200 -0
  32. package/src/tests/runtimes/handleHomeDataMessage.test.ts +53 -0
  33. package/src/tests/runtimes/handleLocalMessage.test.ts +222 -0
  34. package/src/tests/testData/mockData.ts +370 -0
@@ -0,0 +1,200 @@
1
+ import { AdditionalPropCode, Protocol } from '../../roborockCommunication/index.js';
2
+ import { handleCloudMessage } from '../../runtimes/handleCloudMessage';
3
+ import { mapInfo, roomData, roomIndexMap, supportedAreas, supportedMaps } from '../testData/mockData.js';
4
+
5
+ // Mocks for dependencies
6
+ const mockUpdateAttribute = jest.fn().mockImplementation((clusterId: number, attributeName: string, value: any, log: any) => {
7
+ log.debug(`Mock updateAttribute called with clusterId: ${clusterId}, attributeName: ${attributeName}, value: ${value}`);
8
+ });
9
+ const mockSetSupportedAreas = jest.fn();
10
+ const mockSetSelectedAreas = jest.fn();
11
+ const mockSetSupportedAreaIndexMap = jest.fn();
12
+ const mockGetCleanModeData = jest.fn();
13
+ const mockUpdateFromMQTTMessage = jest.fn();
14
+ const mockGetRoomMapFromDevice = jest.fn();
15
+ const mockGetSupportedAreas = jest.fn();
16
+
17
+ jest.mock('./src/helper', () => ({
18
+ getRoomMapFromDevice: (...args: any[]) => mockGetRoomMapFromDevice(...args),
19
+ isStatusUpdate: (result: any) => result === 'status',
20
+ }));
21
+ jest.mock('./src/initialData/getSupportedAreas', () => ({
22
+ getSupportedAreas: (...args: any[]) => mockGetSupportedAreas(...args),
23
+ }));
24
+
25
+ const duid = 'test-duid';
26
+ const robot = {
27
+ updateAttribute: mockUpdateAttribute,
28
+ device: {
29
+ data: { model: 'test-model' },
30
+ rooms: [
31
+ { id: 11100845, name: 'Kitchen' },
32
+ { id: 11100849, name: 'Study' },
33
+ { id: 11100842, name: 'Living room' },
34
+ { id: 11100847, name: 'Bedroom' },
35
+ { id: 12469150, name: 'Dining room' },
36
+ { id: 12461114, name: 'Guest bedroom' },
37
+ { id: 12461109, name: 'Master bedroom' },
38
+ { id: 12461111, name: 'Balcony' },
39
+ { id: 11100842, name: 'Living room' },
40
+ ],
41
+ },
42
+ dockStationStatus: {},
43
+ };
44
+ const platform = {
45
+ robots: new Map([[duid, robot]]),
46
+ log: {
47
+ error: jest.fn(),
48
+ debug: jest.fn(),
49
+ notice: jest.fn(),
50
+ /* eslint-disable no-console */
51
+ fatal: jest.fn().mockImplementation((message: string, ...arg: unknown[]) => console.info(message, ...arg)),
52
+ },
53
+ roborockService: {
54
+ getCleanModeData: mockGetCleanModeData,
55
+ setSupportedAreas: mockSetSupportedAreas,
56
+ setSelectedAreas: mockSetSelectedAreas,
57
+ setSupportedAreaIndexMap: mockSetSupportedAreaIndexMap,
58
+ getMapInformation: jest.fn().mockResolvedValue(mapInfo),
59
+ getRoomMappings: jest.fn().mockResolvedValue(roomData),
60
+ },
61
+ enableExperimentalFeature: {
62
+ enableExperimentalFeature: true,
63
+ advancedFeature: {
64
+ enableMultipleMap: true,
65
+ },
66
+ },
67
+ };
68
+ const runner = { updateFromMQTTMessage: mockUpdateFromMQTTMessage };
69
+
70
+ describe('handleCloudMessage', () => {
71
+ beforeEach(() => {
72
+ jest.clearAllMocks();
73
+ });
74
+
75
+ it('handles status_update', async () => {
76
+ const data = { duid: 'test-duid', dps: { 121: 6, 128: 3, 139: 5 } };
77
+ await handleCloudMessage(data as any, platform as any, runner as any, duid);
78
+ await new Promise(process.nextTick);
79
+ expect(mockUpdateAttribute).toHaveBeenCalled();
80
+ });
81
+
82
+ it('handles rpc_response with status', async () => {
83
+ const data = {
84
+ duid: 'test-duid',
85
+ dps: {
86
+ 102: {
87
+ id: 19937,
88
+ result: [
89
+ {
90
+ msg_ver: 2,
91
+ msg_seq: 2609,
92
+ state: 5,
93
+ battery: 99,
94
+ clean_time: 12,
95
+ clean_area: 0,
96
+ error_code: 0,
97
+ map_present: 1,
98
+ in_cleaning: 1,
99
+ in_returning: 0,
100
+ in_fresh_state: 0,
101
+ lab_status: 3,
102
+ water_box_status: 1,
103
+ fan_power: 101,
104
+ dnd_enabled: 0,
105
+ map_status: 3,
106
+ is_locating: 0,
107
+ lock_status: 0,
108
+ water_box_mode: 202,
109
+ distance_off: 60,
110
+ water_box_carriage_status: 0,
111
+ mop_forbidden_enable: 0,
112
+ adbumper_status: [0, 0, 0],
113
+ dock_type: 5,
114
+ dust_collection_status: 0,
115
+ auto_dust_collection: 1,
116
+ debug_mode: 0,
117
+ switch_map_mode: 0,
118
+ dock_error_status: 0,
119
+ charge_status: 1,
120
+ },
121
+ ],
122
+ },
123
+ },
124
+ };
125
+
126
+ await handleCloudMessage(data as any, platform as any, runner as any, duid);
127
+ await new Promise(process.nextTick);
128
+ expect(mockUpdateFromMQTTMessage).toHaveBeenCalled();
129
+ });
130
+
131
+ it('handles additional_props with map_change', async () => {
132
+ mockGetRoomMapFromDevice.mockResolvedValueOnce({});
133
+ mockGetSupportedAreas.mockReturnValueOnce({
134
+ supportedAreas,
135
+ supportedMaps,
136
+ roomIndexMap,
137
+ });
138
+ const data = { dps: { [Protocol.additional_props]: AdditionalPropCode.map_change } };
139
+ await handleCloudMessage(data as any, platform as any, runner as any, duid);
140
+
141
+ await new Promise(process.nextTick);
142
+ expect(mockSetSupportedAreas).toHaveBeenCalled();
143
+ expect(mockSetSelectedAreas).toHaveBeenCalled();
144
+ expect(mockUpdateAttribute).toHaveBeenCalled();
145
+ });
146
+
147
+ it('logs error if robot not found', async () => {
148
+ const fakePlatform = { ...platform, robots: new Map() };
149
+ const data = { dps: { [Protocol.status_update]: 1 } };
150
+ await handleCloudMessage(data as any, fakePlatform as any, runner as any, duid);
151
+ await new Promise(process.nextTick);
152
+ expect(platform.log.error).toHaveBeenCalled();
153
+ });
154
+
155
+ it('handles unknown message type', async () => {
156
+ const data = { dps: { 999: 42 } };
157
+ await handleCloudMessage(data as any, platform as any, runner as any, duid);
158
+ await new Promise(process.nextTick);
159
+ expect(platform.log.notice).toHaveBeenCalled();
160
+ });
161
+
162
+ it('handles map_change', async () => {
163
+ const data = { dps: { 128: 4 } };
164
+ mockGetRoomMapFromDevice.mockReturnValue({
165
+ rooms: [
166
+ { id: 1, globalId: 11100845, displayName: 'Kitchen', alternativeId: '114', mapId: 0 },
167
+ { id: 2, globalId: 11100849, displayName: 'Study', alternativeId: '29', mapId: 0 },
168
+ { id: 3, globalId: 11100842, displayName: 'Living room', alternativeId: '36', mapId: 0 },
169
+ { id: 4, globalId: 11100847, displayName: 'Bedroom', alternativeId: '41', mapId: 0 },
170
+ ],
171
+ mapInfo: [
172
+ {
173
+ id: 0,
174
+ name: 'First Map',
175
+ rooms: [
176
+ { id: 1, globalId: 11100845, iot_name_id: '11100845', tag: 14, displayName: 'Kitchen', mapId: 0 },
177
+ { id: 2, globalId: 11100849, iot_name_id: '11100849', tag: 9, displayName: 'Study', mapId: 0 },
178
+ { id: 3, globalId: 11100842, iot_name_id: '11100842', tag: 6, displayName: 'Living room', mapId: 0 },
179
+ { id: 4, globalId: 11100847, iot_name_id: '11100847', tag: 1, displayName: 'Bedroom', mapId: 0 },
180
+ ],
181
+ },
182
+ {
183
+ id: 1,
184
+ name: 'Second Map',
185
+ rooms: [
186
+ { id: 1, globalId: 12469150, iot_name_id: '12469150', tag: 13, displayName: 'Dining room', mapId: 1 },
187
+ { id: 2, globalId: 12461114, iot_name_id: '12461114', tag: 3, displayName: 'Guest bedroom', mapId: 1 },
188
+ { id: 3, globalId: 12461109, iot_name_id: '12461109', tag: 2, displayName: 'Master bedroom', mapId: 1 },
189
+ { id: 4, globalId: 12461111, iot_name_id: '12461111', tag: 7, displayName: 'Balcony', mapId: 1 },
190
+ { id: 5, globalId: 11100842, iot_name_id: '11100842', tag: 6, displayName: 'Living room', mapId: 1 },
191
+ ],
192
+ },
193
+ ],
194
+ });
195
+
196
+ await handleCloudMessage(data as any, platform as any, runner as any, duid);
197
+ await new Promise(process.nextTick);
198
+ expect(platform.log.notice).toHaveBeenCalled();
199
+ });
200
+ });
@@ -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
+ });