@eohjsc/react-native-smart-city 0.7.27 → 0.7.31

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 (71) hide show
  1. package/index.js +2 -0
  2. package/package.json +2 -1
  3. package/src/commons/Dashboard/MyDashboardDevice/__test__/index.test.js +68 -0
  4. package/src/commons/Dashboard/MyDashboardDevice/index.js +46 -11
  5. package/src/commons/Dashboard/MyUnit/__test__/MyUnit.test.js +43 -11
  6. package/src/commons/Dashboard/MyUnit/index.js +40 -32
  7. package/src/commons/ModalAlert/index.js +51 -0
  8. package/src/commons/ModalAlert/styles.js +54 -0
  9. package/src/commons/SubUnit/ShortDetail.js +20 -4
  10. package/src/commons/SubUnit/__test__/ShortDetail.test.js +46 -1
  11. package/src/configs/API.js +8 -0
  12. package/src/configs/AccessibilityLabel.js +3 -0
  13. package/src/configs/Constants.js +7 -0
  14. package/src/configs/SCConfig.js +6 -0
  15. package/src/context/SCContext.tsx +12 -1
  16. package/src/context/SCStore.ts +14 -0
  17. package/src/context/actionType.ts +10 -0
  18. package/src/context/mockStore.ts +30 -1
  19. package/src/context/reducer.ts +35 -0
  20. package/src/hooks/IoT/useRemoteControl.js +4 -1
  21. package/src/hooks/IoT/useWatchSharedChips.js +130 -0
  22. package/src/hooks/Review/__test__/useInAppReview.test.js +99 -0
  23. package/src/hooks/Review/useInAppReview.js +70 -0
  24. package/src/hooks/useMqtt.js +78 -27
  25. package/src/iot/Monitor.js +149 -26
  26. package/src/iot/UpdateStates.js +60 -0
  27. package/src/iot/mqtt.js +177 -22
  28. package/src/navigations/UnitStack.js +16 -0
  29. package/src/screens/ActivityLog/ItemLog.js +1 -0
  30. package/src/screens/AddNewGateway/RenameNewDevices.js +5 -0
  31. package/src/screens/AddNewGateway/__test__/RenameNewDevices.test.js +18 -0
  32. package/src/screens/Automate/AddNewAction/ReceiverSelect.js +210 -0
  33. package/src/screens/Automate/AddNewAction/SetupScriptEmail.js +1 -1
  34. package/src/screens/Automate/AddNewAction/SetupScriptNotify.js +18 -28
  35. package/src/screens/Automate/AddNewAction/SetupScriptReceiverEmail.js +22 -129
  36. package/src/screens/Automate/AddNewAction/SetupScriptReceiverNotify.js +59 -0
  37. package/src/screens/Automate/AddNewAction/SetupScriptReceiverSms.js +22 -129
  38. package/src/screens/Automate/AddNewAction/SetupScriptSms.js +1 -1
  39. package/src/screens/Automate/AddNewAction/Styles/{SetupScriptReceiverEmailStyles.js → ReceiverSelectStyles.js} +18 -1
  40. package/src/screens/Automate/AddNewAction/__test__/SetupScriptNotify.test.js +16 -33
  41. package/src/screens/Automate/AddNewAction/__test__/SetupScriptReceiverEmail.test.js +10 -8
  42. package/src/screens/Automate/AddNewAction/__test__/SetupScriptReceiverNotify.test.js +217 -0
  43. package/src/screens/Automate/AddNewAction/__test__/SetupScriptReceiverSms.test.js +10 -8
  44. package/src/screens/Automate/Components/InputName.js +5 -1
  45. package/src/screens/Automate/EditActionsList/UpdateReceiverEmailScript.js +4 -3
  46. package/src/screens/Automate/EditActionsList/UpdateReceiverSmsScript.js +5 -4
  47. package/src/screens/Automate/OneTap/__test__/AddNewOneTap.test.js +18 -0
  48. package/src/screens/Automate/ScriptDetail/Styles/indexStyles.js +1 -1
  49. package/src/screens/Automate/ScriptDetail/__test__/index.test.js +116 -2
  50. package/src/screens/Automate/ScriptDetail/index.js +47 -9
  51. package/src/screens/CreatePassword/__test__/index.test.js +133 -0
  52. package/src/screens/CreatePassword/index.js +134 -0
  53. package/src/screens/CreatePassword/styles.js +45 -0
  54. package/src/screens/Device/__test__/DeviceDetail-3rdparty.test.js +447 -0
  55. package/src/screens/Device/__test__/DeviceDetail-arduino.test.js +344 -0
  56. package/src/screens/Device/__test__/{mqttDetail.test.js → DeviceDetail-modbus.test.js} +287 -320
  57. package/src/screens/Device/__test__/DeviceDetail-zigbee.test.js +451 -0
  58. package/src/screens/Device/__test__/DeviceDetail.test.js +502 -0
  59. package/src/screens/Device/__test__/detail.test.js +61 -3
  60. package/src/screens/Device/__test__/sensorDisplayItem.test.js +28 -3
  61. package/src/screens/Device/detail.js +14 -6
  62. package/src/screens/Device/hooks/useDeviceWatchConfigControl.js +3 -2
  63. package/src/screens/EnterPassword/__test__/EnterPassword.test.js +76 -1
  64. package/src/screens/EnterPassword/index.js +34 -4
  65. package/src/screens/EnterPassword/styles.js +1 -1
  66. package/src/utils/FactoryGateway.js +597 -0
  67. package/src/utils/I18n/translations/en.js +11 -0
  68. package/src/utils/I18n/translations/vi.js +11 -0
  69. package/src/utils/Route/index.js +3 -1
  70. package/src/utils/Validation.js +5 -0
  71. package/src/utils/store.js +5 -0
@@ -0,0 +1,451 @@
1
+ import { NavigationContext } from '@react-navigation/native';
2
+ import AsyncStorage from '@react-native-async-storage/async-storage';
3
+ import MockAdapter from 'axios-mock-adapter';
4
+ import moment from 'moment';
5
+ import mqtt from 'precompiled-mqtt/dist/mqtt.browser';
6
+ import React from 'react';
7
+ import { Alert } from 'react-native';
8
+ import { act, create } from 'react-test-renderer';
9
+
10
+ import QualityIndicatorItem from '../../../commons/Device/WaterQualitySensor/QualityIndicatorsItem';
11
+ import { API } from '../../../configs';
12
+ import { SCProvider } from '../../../context';
13
+ import { mockSCStore } from '../../../context/mockStore';
14
+ import { mqttClientsPool } from '../../../hooks/useMqtt';
15
+ import { unwatchMultiDataChips, watchMultiConfigs } from '../../../iot/Monitor';
16
+ import { setConfigGlobalState } from '../../../iot/states';
17
+ import api from '../../../utils/Apis/axios';
18
+ import {
19
+ configDataFactory,
20
+ gatewayDataFactory,
21
+ } from '../../../utils/FactoryGateway';
22
+ import DeviceDetail from '../detail';
23
+
24
+ const mock = new MockAdapter(api.axiosInstance);
25
+
26
+ jest.mock('../../../iot/Monitor', () => {
27
+ return {
28
+ ...jest.requireActual('../../../iot/Monitor'),
29
+ watchMultiConfigs: jest.fn(),
30
+ unwatchMultiDataChips: jest.fn(),
31
+ };
32
+ });
33
+
34
+ let store = mockSCStore({});
35
+
36
+ const mockAxios = (
37
+ responseDisplay,
38
+ responseDisplayValueV2,
39
+ responseRemoteControl = {}
40
+ ) => {
41
+ mock.onGet(API.DEVICE.DISPLAY(1)).reply(200, responseDisplay);
42
+ mock
43
+ .onGet(API.DEVICE.REMOTE_CONTROL_OPTIONS(1))
44
+ .reply(200, responseRemoteControl);
45
+ mock
46
+ .onGet(API.DEVICE.DISPLAY_VALUES_V2(1))
47
+ .reply(200, responseDisplayValueV2);
48
+ };
49
+
50
+ const navContextValue = {
51
+ isFocused: () => false,
52
+ addListener: jest.fn(() => jest.fn()),
53
+ };
54
+ const wrapComponent = (state, account, route) => (
55
+ <SCProvider initState={state}>
56
+ <NavigationContext.Provider
57
+ value={{
58
+ ...navContextValue,
59
+ isFocused: () => true,
60
+ }}
61
+ >
62
+ <DeviceDetail account={account} route={route} />
63
+ </NavigationContext.Provider>
64
+ </SCProvider>
65
+ );
66
+
67
+ const mockAlertShow = jest.fn();
68
+ Alert.alert = mockAlertShow;
69
+
70
+ describe('test DeviceDetail', () => {
71
+ let tree;
72
+ let route;
73
+ let account;
74
+ let gatewayData;
75
+
76
+ beforeEach(() => {
77
+ route = {
78
+ params: {
79
+ unitData: {
80
+ id: 1,
81
+ name: 'Unit name',
82
+ address: '298 Dien Bien Phu',
83
+ remote_control_options: {
84
+ googlehome: [
85
+ {
86
+ config_maps: [],
87
+ auth: {},
88
+ chip_id: 1,
89
+ },
90
+ ],
91
+ },
92
+ },
93
+ station: {
94
+ id: 2,
95
+ name: 'Station name',
96
+ },
97
+ sensorData: {
98
+ id: 1,
99
+ is_managed_by_backend: true,
100
+ station: { id: 2, name: 'Station name' },
101
+ name: 'Sensor name',
102
+ chip_id: 7689,
103
+ },
104
+ title: 'Button',
105
+ },
106
+ };
107
+ account = {
108
+ token: 'abc',
109
+ };
110
+ jest.useFakeTimers();
111
+ mock.reset();
112
+ watchMultiConfigs.mockClear();
113
+ unwatchMultiDataChips.mockClear();
114
+ AsyncStorage.clear();
115
+ mqttClientsPool.clear();
116
+ act(() => {
117
+ setConfigGlobalState('configValues', {});
118
+ });
119
+ gatewayData = JSON.parse(JSON.stringify(gatewayDataFactory));
120
+ mock.onGet(API.CHIP.SHARED_CONFIGURATION).reply(200, []);
121
+ });
122
+
123
+ const _receiveDataOnDeviceDetail = async (widgetConfigs, topic, message) => {
124
+ mock.onGet(API.CHIP.JSON_CONFIGURATION).reply(200, [gatewayData]);
125
+
126
+ const responseDisplay = {
127
+ items: [
128
+ {
129
+ id: 1,
130
+ order: 0,
131
+ template: 'value',
132
+ type: 'value',
133
+ configuration: {
134
+ template: 'simple_list',
135
+ configs: widgetConfigs,
136
+ },
137
+ },
138
+ ],
139
+ };
140
+
141
+ const responseRemoteControl = {
142
+ bluetooth: {
143
+ address: 'JUvfa06PMDU8Cqlo',
144
+ password: 'MYcNoskxspWTPsnh',
145
+ },
146
+ internet: {},
147
+ };
148
+
149
+ const responseDisplayValueV2 = {
150
+ configs: [],
151
+ is_connected: true,
152
+ last_updated: '2021-01-24T12:00:00.000Z',
153
+ };
154
+
155
+ mockAxios(responseDisplay, responseDisplayValueV2, responseRemoteControl);
156
+
157
+ await act(async () => {
158
+ tree = await create(
159
+ wrapComponent(store, account, {
160
+ ...route,
161
+ params: { ...route.params },
162
+ })
163
+ );
164
+ });
165
+ const instance = tree.root;
166
+ const client = mqtt.connect();
167
+ await act(async () => {
168
+ await client.trigger('message', topic, JSON.stringify(message), 'packet');
169
+ });
170
+
171
+ const valueBox = instance.findAllByType(QualityIndicatorItem);
172
+ return valueBox;
173
+ };
174
+
175
+ // ZIGBEE
176
+ const testZigbee = async (expect1, expect2, extra = {}) => {
177
+ const valueBoxs = await _receiveDataOnDeviceDetail(
178
+ [{ id: 111 }, { id: 222 }],
179
+ 'zigbee/0x00158d00022fd3c6/data',
180
+ {
181
+ type: 'zigbee_data',
182
+ data: { temperature_measured: 36 },
183
+ ...extra,
184
+ }
185
+ );
186
+ expect(valueBoxs[0].props.value).toEqual(expect1);
187
+ expect(valueBoxs[1].props.value).toEqual(expect2);
188
+ };
189
+
190
+ it('should render device detail, zigbee call updateGlobalValue', async () => {
191
+ await testZigbee(36, '--');
192
+ });
193
+
194
+ it('should render device detail, zigbee call updateGlobalValue with past last_updated', async () => {
195
+ act(() => {
196
+ setConfigGlobalState('configValues', {
197
+ 111: { value: 0, last_updated: moment(1672531200000) },
198
+ });
199
+ });
200
+ await testZigbee(0, '--', { time: 1672531199 });
201
+ });
202
+
203
+ it('should render device detail, zigbee call updateGlobalValue with future last_updated', async () => {
204
+ act(() => {
205
+ setConfigGlobalState('configValues', {
206
+ 111: { value: 0, last_updated: moment(1672531200000) },
207
+ });
208
+ });
209
+ await testZigbee(36, '--', { time: 1672531201 });
210
+ });
211
+
212
+ it('should render device detail, zigbee with edge past last_updated', async () => {
213
+ act(() => {
214
+ setConfigGlobalState('configValues', {
215
+ 111: {
216
+ value: 0,
217
+ last_updated: moment(1672531200123),
218
+ last_updated_micro: 456,
219
+ },
220
+ });
221
+ });
222
+ await testZigbee(0, '--', { time: 1672531200.123455 });
223
+ });
224
+
225
+ it('should render device detail, zigbee with edge future last_updated', async () => {
226
+ act(() => {
227
+ setConfigGlobalState('configValues', {
228
+ 111: {
229
+ value: 0,
230
+ last_updated: moment(1672531200123),
231
+ last_updated_micro: 456,
232
+ },
233
+ });
234
+ });
235
+ await testZigbee(36, '--', { time: 1672531200.123457 });
236
+ });
237
+
238
+ it('should render device detail, zigbee call updateGlobalValue, filter zero', async () => {
239
+ gatewayData.sensors[2].configs[0].active_filter = 'zero';
240
+ const valueBoxs = await _receiveDataOnDeviceDetail(
241
+ [{ id: 111 }, { id: 222 }],
242
+ 'zigbee/0x00158d00022fd3c6/data',
243
+ {
244
+ data: { temperature_measured: 0 },
245
+ }
246
+ );
247
+ expect(valueBoxs[0].props.value).toEqual('--');
248
+ expect(valueBoxs[1].props.value).toEqual('--');
249
+ });
250
+
251
+ it('should render device detail, zigbee call updateGlobalValue, filter in range', async () => {
252
+ gatewayData.sensors[2].configs[0].active_filter = 'range';
253
+ gatewayData.sensors[2].configs[0].high_level = 37;
254
+ await testZigbee(36, '--');
255
+ });
256
+
257
+ it('should render device detail, zigbee call updateGlobalValue, filter out range', async () => {
258
+ gatewayData.sensors[2].configs[0].active_filter = 'range';
259
+ gatewayData.sensors[2].configs[0].high_level = 35;
260
+ await testZigbee('--', '--');
261
+ });
262
+
263
+ it('should render device detail, zigbee call updateGlobalValue, with offset', async () => {
264
+ gatewayData.sensors[2].configs[0].ai_trans_offset_value = '1.000';
265
+ await testZigbee(37, '--');
266
+ });
267
+
268
+ it('should render device detail, zigbee call updateGlobalValue, with average sample first time', async () => {
269
+ const cacheKey = `last_values_${gatewayData.sensors[2].configs[0].id}`;
270
+ gatewayData.sensors[2].configs[0].decimal_behind = 2;
271
+ gatewayData.sensors[2].configs[0].ai_trans_average_sample = 2;
272
+ await testZigbee(36, '--');
273
+ expect(AsyncStorage.setItem).toHaveBeenLastCalledWith(cacheKey, '[36]');
274
+ });
275
+
276
+ it('should render device detail, zigbee call updateGlobalValue, with average sample', async () => {
277
+ const cacheKey = `last_values_${gatewayData.sensors[2].configs[0].id}`;
278
+ await AsyncStorage.setItem(cacheKey, '[0.2, 0.3, 0.4, 0.5]');
279
+ gatewayData.sensors[2].configs[0].decimal_behind = 2;
280
+ gatewayData.sensors[2].configs[0].ai_trans_average_sample = 2;
281
+ await testZigbee(18.1, '--'); // (36 + 0.2) / 2 = 18.1
282
+ expect(AsyncStorage.setItem).toHaveBeenLastCalledWith(
283
+ cacheKey,
284
+ '[18.1,0.2]'
285
+ );
286
+ });
287
+
288
+ it('should render device detail, zigbee call updateGlobalValue, with offset and average sample', async () => {
289
+ const cacheKey = `last_values_${gatewayData.sensors[2].configs[0].id}`;
290
+ await AsyncStorage.setItem(cacheKey, '[0.2, 0.3, 0.4, 0.5]');
291
+ gatewayData.sensors[2].configs[0].decimal_behind = 2;
292
+ gatewayData.sensors[2].configs[0].ai_trans_average_sample = 2;
293
+ gatewayData.sensors[2].configs[0].ai_trans_offset_value = '1.000';
294
+ await testZigbee(18.6, '--'); // (36 + 1 + 0.2) / 2 = 5.6
295
+ expect(AsyncStorage.setItem).toHaveBeenLastCalledWith(
296
+ cacheKey,
297
+ '[18.6,0.2]'
298
+ );
299
+ });
300
+
301
+ it('should render device detail, zigbee not call updateGlobalValue, configs is empty', async () => {
302
+ gatewayData.sensors = [{ id: configDataFactory.sensor, configs: [] }];
303
+ await testZigbee('--', '--');
304
+ });
305
+
306
+ it('should render device detail, zigbee not call updateGlobalValue, sensors is empty', async () => {
307
+ gatewayData.sensors = [];
308
+ await testZigbee('--', '--');
309
+ });
310
+
311
+ it('should render device detail, zigbee not call updateGlobalValue, zigbee_gateway is empty', async () => {
312
+ gatewayData.zigbee_gateway = null;
313
+ await testZigbee('--', '--');
314
+ });
315
+
316
+ const _watchDataChipsOnDeviceDetail = async (
317
+ expect1,
318
+ expect2,
319
+ last_updated = '2025-09-09T12:00:00Z'
320
+ ) => {
321
+ mock.onGet(API.CHIP.JSON_CONFIGURATION).reply(200, []);
322
+ mock.onPost(API.IOT.CHIP_MANAGER.WATCH_DATA_CHIPS()).reply(200, {
323
+ 7689: {
324
+ last_zigbee_data: {
325
+ '0x00158d00022fd3c6': {
326
+ type: 'zigbee_data',
327
+ data: {
328
+ temperature_measured: 36,
329
+ },
330
+ last_updated,
331
+ },
332
+ },
333
+ },
334
+ });
335
+ mock.onGet(API.CHIP.SHARED_CONFIGURATION).reply(200, [gatewayData]);
336
+
337
+ const responseDisplay = {
338
+ items: [
339
+ {
340
+ id: 1,
341
+ order: 0,
342
+ template: 'value',
343
+ type: 'value',
344
+ configuration: {
345
+ template: 'simple_list',
346
+ configs: [{ id: 111 }, { id: 222 }],
347
+ },
348
+ },
349
+ ],
350
+ };
351
+
352
+ const responseRemoteControl = {
353
+ bluetooth: {
354
+ address: 'JUvfa06PMDU8Cqlo',
355
+ password: 'MYcNoskxspWTPsnh',
356
+ },
357
+ internet: {},
358
+ };
359
+
360
+ const responseDisplayValueV2 = {
361
+ configs: [],
362
+ is_connected: true,
363
+ last_updated: '2021-01-24T12:00:00.000Z',
364
+ };
365
+
366
+ mockAxios(responseDisplay, responseDisplayValueV2, responseRemoteControl);
367
+
368
+ await act(async () => {
369
+ tree = await create(
370
+ wrapComponent(store, account, {
371
+ ...route,
372
+ params: { ...route.params },
373
+ })
374
+ );
375
+ });
376
+
377
+ const instance = tree.root;
378
+ const valueBoxs = instance.findAllByType(QualityIndicatorItem);
379
+ expect(valueBoxs[0].props.value).toEqual(expect1);
380
+ expect(valueBoxs[1].props.value).toEqual(expect2);
381
+
382
+ expect(
383
+ mock.history.post.filter(
384
+ (req) => req.url === API.IOT.CHIP_MANAGER.WATCH_DATA_CHIPS()
385
+ )
386
+ ).toHaveLength(1);
387
+
388
+ await act(async () => {
389
+ jest.runOnlyPendingTimers();
390
+ });
391
+ expect(
392
+ mock.history.post.filter(
393
+ (req) => req.url === API.IOT.CHIP_MANAGER.WATCH_DATA_CHIPS()
394
+ )
395
+ ).toHaveLength(2);
396
+ expect(unwatchMultiDataChips).not.toHaveBeenCalled();
397
+
398
+ await act(async () => {
399
+ await tree.unmount();
400
+ });
401
+ expect(unwatchMultiDataChips).toHaveBeenCalledWith([gatewayData.id]);
402
+ };
403
+
404
+ it('should render device detail, watch data chips call updateGlobalValue', async () => {
405
+ await _watchDataChipsOnDeviceDetail(36, '--');
406
+ });
407
+
408
+ it('should render device detail, watch data chips call updateGlobalValue with past last_updated', async () => {
409
+ act(() => {
410
+ setConfigGlobalState('configValues', {
411
+ 111: { value: 0, last_updated: moment('2025-09-09T12:00:01Z') },
412
+ });
413
+ });
414
+ await _watchDataChipsOnDeviceDetail(0, '--');
415
+ });
416
+
417
+ it('should render device detail, watch data chips call updateGlobalValue with future last_updated', async () => {
418
+ act(() => {
419
+ setConfigGlobalState('configValues', {
420
+ 111: { value: 0, last_updated: moment('2025-09-09T11:59:59Z') },
421
+ });
422
+ });
423
+ await _watchDataChipsOnDeviceDetail(36, '--');
424
+ });
425
+
426
+ it('should render device detail, watch data chips with edge past last_updated', async () => {
427
+ act(() => {
428
+ setConfigGlobalState('configValues', {
429
+ 111: {
430
+ value: 0,
431
+ last_updated: moment('2025-09-09T12:00:00.12346Z'),
432
+ last_updated_micro: 460,
433
+ },
434
+ });
435
+ });
436
+ await _watchDataChipsOnDeviceDetail(0, '--', '2025-09-09T12:00:00.12345Z');
437
+ });
438
+
439
+ it('should render device detail, watch data chips with edge future last_updated', async () => {
440
+ act(() => {
441
+ setConfigGlobalState('configValues', {
442
+ 111: {
443
+ value: 0,
444
+ last_updated: moment('2025-09-09T12:00:00.12344Z'),
445
+ last_updated_micro: 440,
446
+ },
447
+ });
448
+ });
449
+ await _watchDataChipsOnDeviceDetail(36, '--', '2025-09-09T12:00:00.12345Z');
450
+ });
451
+ });