@eohjsc/react-native-smart-city 0.3.48 → 0.3.49

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 (164) hide show
  1. package/package.json +4 -4
  2. package/src/Images/DevMode/file_copy.svg +3 -0
  3. package/src/Images/DevMode/inforCode.png +0 -0
  4. package/src/Images/DevMode/inforCode@2x.png +0 -0
  5. package/src/Images/DevMode/inforCode@3x.png +0 -0
  6. package/src/commons/Action/__test__/ItemQuickAction.test.js +0 -7
  7. package/src/commons/ActionGroup/__test__/ColorPickerTemplate.test.js +1 -8
  8. package/src/commons/ActionGroup/__test__/OnOffTemplate.test.js +1 -1
  9. package/src/commons/ActionGroup/__test__/SliderRangeTemplate.test.js +1 -8
  10. package/src/commons/ActionTemplate/__test__/index.test.js +0 -7
  11. package/src/commons/Dashboard/MyPinnedSharedUnit/index.js +3 -3
  12. package/src/commons/Dashboard/MyUnit/__test__/MyUnit.test.js +1 -1
  13. package/src/commons/Dashboard/MyUnit/index.js +30 -4
  14. package/src/commons/DevMode/Styles/ItemStyles.js +1 -1
  15. package/src/commons/Device/PMSensor/PMSensorIndicatior.js +2 -1
  16. package/src/commons/FourButtonFilterHistory/__test__/FourButtonFilterHistory.test.js +0 -7
  17. package/src/commons/Header/HeaderCustom.js +13 -6
  18. package/src/commons/MediaPlayer/__test__/index.test.js +0 -7
  19. package/src/commons/MediaPlayerDetail/__test__/MediaPlayerFull.test.js +0 -1
  20. package/src/commons/MenuActionMore/index.js +4 -1
  21. package/src/commons/NavBar/index.js +1 -1
  22. package/src/commons/OneTapTemplate/OptionsDropdownActionTemplate.js +1 -1
  23. package/src/commons/OneTapTemplate/__test__/NumberUpDownActionTemplate.test.js +0 -7
  24. package/src/commons/OneTapTemplate/__test__/OptionsDropdownActionTemplate.test.js +0 -7
  25. package/src/commons/OneTapTemplate/__test__/StatesGridActionTemplate.test.js +0 -7
  26. package/src/commons/RowItem/index.js +6 -1
  27. package/src/commons/SelectUnit/index.js +4 -1
  28. package/src/commons/StatusBox/styles.js +8 -3
  29. package/src/commons/SubUnit/__test__/Favorites.test.js +0 -7
  30. package/src/commons/SubUnit/__test__/ShortDetail.test.js +0 -7
  31. package/src/commons/Tabbar/__test__/index.test.js +0 -7
  32. package/src/commons/Unit/__test__/HeaderUnit.test.js +0 -5
  33. package/src/commons/UnitSummary/ConfigHistoryChart/__test__/ConfigHistoryChart.test.js +0 -7
  34. package/src/configs/API.js +28 -0
  35. package/src/configs/AccessibilityLabel.js +13 -0
  36. package/src/configs/Colors.js +4 -0
  37. package/src/configs/Constants.js +6 -0
  38. package/src/configs/Images.js +1 -0
  39. package/src/context/actionType.ts +8 -1
  40. package/src/context/mockStore.ts +8 -2
  41. package/src/context/reducer.ts +27 -4
  42. package/src/hooks/IoT/__test__/useWatchConfigs.test.js +46 -0
  43. package/src/hooks/IoT/useBluetoothConnection.js +78 -4
  44. package/src/hooks/IoT/useBluetoothDeviceConnected.js +1 -1
  45. package/src/iot/RemoteControl/Bluetooth.js +0 -16
  46. package/src/iot/RemoteControl/__test__/Bluetooth.test.js +0 -25
  47. package/src/navigations/Main.js +39 -0
  48. package/src/navigations/UnitStack.js +18 -5
  49. package/src/screens/ActivityLog/__test__/FilterPopup.test.js +0 -7
  50. package/src/screens/ActivityLog/__test__/ItemLog.test.js +0 -7
  51. package/src/screens/ActivityLog/__test__/index.test.js +0 -7
  52. package/src/screens/AddLocationMaps/__test__/index.test.js +0 -7
  53. package/src/screens/AddNewAction/SelectAction.js +22 -18
  54. package/src/screens/AddNewAction/__test__/SelectAction.test.js +35 -16
  55. package/src/screens/AddNewAction/__test__/SelectSensorDevices.test.js +0 -7
  56. package/src/screens/AddNewDevice/__test__/AddNewDevice.test.js +0 -4
  57. package/src/screens/AddNewGateway/ConnectingZigbeeDevice.js +17 -4
  58. package/src/screens/AddNewGateway/__test__/AddNewGateway.test.js +0 -4
  59. package/src/screens/AddNewGateway/__test__/ConnectingModbusDevice.test.js +0 -4
  60. package/src/screens/AddNewGateway/__test__/ConnectingWifiDevice.test.js +0 -4
  61. package/src/screens/AddNewGateway/__test__/ConnectingZigbeeDevice.test.js +4 -7
  62. package/src/screens/AddNewGateway/__test__/RenameNewDevices.test.js +0 -7
  63. package/src/screens/AddNewGateway/__test__/SelectDeviceType.test.js +0 -4
  64. package/src/screens/AddNewGateway/__test__/SelectModbusGateway.test.js +0 -4
  65. package/src/screens/AddNewGateway/__test__/SelectZigbeeGateway.test.js +0 -4
  66. package/src/screens/AddNewOneTap/__test__/AddNewOneTap.test.js +0 -7
  67. package/src/screens/ConfirmUnitDeletion/__test__/ConfirmUnitDeletion.test.js +0 -1
  68. package/src/screens/Device/EditDevice/__test__/EditDevice.test.js +0 -7
  69. package/src/screens/Device/__test__/detail.test.js +1 -1
  70. package/src/screens/Device/detail.js +11 -3
  71. package/src/screens/Device/hooks/useDisconnectedDevice.js +26 -7
  72. package/src/screens/EmergencyContacts/__test__/hooks.test.js +79 -0
  73. package/src/screens/Gateway/DetailConfigActionModbus/__test__/index.test.js +138 -0
  74. package/src/screens/Gateway/DetailConfigActionModbus/index.js +180 -0
  75. package/src/screens/Gateway/DetailConfigActionModbus/styles.js +9 -0
  76. package/src/screens/Gateway/DetailConfigActionZigbee/__test__/index.test.js +73 -0
  77. package/src/screens/Gateway/DetailConfigActionZigbee/index.js +62 -0
  78. package/src/screens/Gateway/DetailConfigActionZigbee/styles.js +9 -0
  79. package/src/screens/Gateway/DeviceGatewayInfo/__test__/index.test.js +73 -0
  80. package/src/screens/Gateway/DeviceGatewayInfo/index.js +96 -0
  81. package/src/screens/Gateway/DeviceGatewayInfo/styles.js +9 -0
  82. package/src/screens/Gateway/DeviceModbusDetail/__test__/index.test.js +393 -0
  83. package/src/screens/Gateway/DeviceModbusDetail/index.js +176 -0
  84. package/src/screens/Gateway/DeviceModbusDetail/styles.js +12 -0
  85. package/src/screens/Gateway/DeviceZigbeeDetail/__test__/index.test.js +265 -0
  86. package/src/screens/Gateway/DeviceZigbeeDetail/index.js +148 -0
  87. package/src/screens/Gateway/DeviceZigbeeDetail/styles.js +12 -0
  88. package/src/screens/Gateway/GatewayConnectionMethods/__test__/index.test.js +37 -0
  89. package/src/screens/Gateway/GatewayConnectionMethods/index.js +73 -0
  90. package/src/screens/Gateway/GatewayConnectionMethods/styles.js +45 -0
  91. package/src/screens/Gateway/GatewayDetail/__test__/index.test.js +298 -0
  92. package/src/screens/Gateway/GatewayDetail/index.js +148 -0
  93. package/src/screens/Gateway/GatewayDetail/styles.js +12 -0
  94. package/src/screens/Gateway/GatewayInfo/__test__/index.test.js +137 -0
  95. package/src/screens/Gateway/GatewayInfo/index.js +115 -0
  96. package/src/screens/Gateway/GatewayInfo/styles.js +9 -0
  97. package/src/screens/Gateway/__test__/index.test.js +58 -0
  98. package/src/screens/Gateway/components/Detail/__test__/index.test.js +46 -0
  99. package/src/screens/Gateway/components/Detail/index.js +62 -0
  100. package/src/screens/Gateway/components/Detail/styles.js +27 -0
  101. package/src/screens/Gateway/components/DetailActionModbus/__test__/index.test.js +49 -0
  102. package/src/screens/Gateway/components/DetailActionModbus/index.js +52 -0
  103. package/src/screens/Gateway/components/DetailActionModbus/styles.js +32 -0
  104. package/src/screens/Gateway/components/DetailConfigAction/__test__/index.test.js +59 -0
  105. package/src/screens/Gateway/components/DetailConfigAction/index.js +69 -0
  106. package/src/screens/Gateway/components/DetailConfigAction/styles.js +21 -0
  107. package/src/screens/Gateway/components/GatewayItem/__test__/index.test.js +1 -1
  108. package/src/screens/Gateway/components/GatewayItem/styles.js +4 -33
  109. package/src/screens/Gateway/components/Information/__test__/index.test.js +70 -0
  110. package/src/screens/Gateway/components/Information/index.js +116 -0
  111. package/src/screens/Gateway/components/Information/styles.js +59 -0
  112. package/src/screens/Gateway/components/RowItem/__test__/index.test.js +67 -0
  113. package/src/screens/Gateway/components/RowItem/index.js +65 -0
  114. package/src/screens/Gateway/components/RowItem/styles.js +25 -0
  115. package/src/screens/Gateway/components/TabPaneCT/__test__/index.test.js +98 -0
  116. package/src/screens/Gateway/components/TabPaneCT/index.js +134 -0
  117. package/src/screens/Gateway/components/TabPaneCT/styles.js +58 -0
  118. package/src/screens/Gateway/hooks/__test__/index.test.js +93 -0
  119. package/src/screens/Gateway/hooks/useGateway.js +110 -16
  120. package/src/screens/Gateway/index.js +19 -3
  121. package/src/screens/Gateway/styles.js +6 -8
  122. package/src/screens/Gateway/utils/index.js +16 -0
  123. package/src/screens/GuestInfo/__test__/index.test.js +0 -7
  124. package/src/screens/HanetCamera/__test__/CaptureFaceID.test.js +0 -7
  125. package/src/screens/HanetCamera/__test__/Detail.test.js +0 -7
  126. package/src/screens/HanetCamera/__test__/ManageAccess.test.js +0 -7
  127. package/src/screens/HanetCamera/__test__/MemberInfo.test.js +0 -7
  128. package/src/screens/ManageAccess/__test__/ManageAccess.test.js +0 -6
  129. package/src/screens/ManageAccess/hooks/__test__/useManageAccess.test.js +0 -7
  130. package/src/screens/MoveToAnotherSubUnit/__test__/index.test.js +0 -7
  131. package/src/screens/Notification/__test__/Notification.test.js +0 -7
  132. package/src/screens/PlayBackCamera/__test__/index.test.js +0 -1
  133. package/src/screens/ScriptDetail/__test__/index.test.js +0 -7
  134. package/src/screens/SelectUnit/__test__/index.test.js +0 -1
  135. package/src/screens/SetSchedule/__test__/index.test.js +0 -7
  136. package/src/screens/Sharing/Components/__test__/DeviceItem.test.js +0 -7
  137. package/src/screens/Sharing/Components/__test__/ItemChangeRole.test.js +0 -7
  138. package/src/screens/Sharing/Components/__test__/TitleCheckBox.test.js +0 -7
  139. package/src/screens/Sharing/__test__/InfoMemberUnit.test.js +0 -6
  140. package/src/screens/Sharing/hooks/__test__/index.test.js +80 -0
  141. package/src/screens/SmartAccount/ListDevice/__test__/DeviceItem.test.js +0 -7
  142. package/src/screens/SmartIr/__test__/ButtonsBottom.test.js +0 -6
  143. package/src/screens/SmartIr/__test__/GroupButtonByType.test.js +1 -6
  144. package/src/screens/SmartIr/__test__/SelectBrand.test.js +1 -6
  145. package/src/screens/SmartIr/__test__/SelectDeviceType.test.js +1 -6
  146. package/src/screens/SubUnit/AddSubUnit.js +1 -0
  147. package/src/screens/SubUnit/ManageSubUnit.js +4 -1
  148. package/src/screens/SubUnit/hooks/__test__/useEmergencyContacts.test.js +34 -0
  149. package/src/screens/SubUnit/hooks/__test__/useManageSubUnit.test.js +0 -7
  150. package/src/screens/SyncLGDevice/__test__/AddLGDevice.test.js +0 -7
  151. package/src/screens/Unit/__test__/AddMenu.test.js +0 -7
  152. package/src/screens/Unit/__test__/CheckSendEmail.test.js +1 -1
  153. package/src/screens/Unit/__test__/ChooseLocation.test.js +0 -7
  154. package/src/screens/Unit/__test__/Detail.test.js +1 -8
  155. package/src/screens/Unit/__test__/SelectAddToFavorites.test.js +0 -7
  156. package/src/screens/Unit/__test__/SelectAddress.test.js +0 -7
  157. package/src/screens/Unit/__test__/SmartAccount.test.js +0 -7
  158. package/src/screens/Unit/__test__/SmartAccountItem.test.js +0 -7
  159. package/src/screens/Unit/__test__/Summaries.test.js +0 -7
  160. package/src/screens/Unit/hook/useUnitConnectRemoteDevices.js +4 -3
  161. package/src/screens/UnitSummary/components/RunningDevices/__test__/index.test.js +2 -2
  162. package/src/utils/I18n/translations/en.json +28 -0
  163. package/src/utils/I18n/translations/vi.json +28 -0
  164. package/src/utils/Route/index.js +8 -0
@@ -84,6 +84,12 @@ export const DEVICE_TYPE = {
84
84
  WIFI_DEVICE: 'WIFI_DEVICE',
85
85
  MODBUS: 'MODBUS',
86
86
  };
87
+ export const PERMISSION_TYPE = {
88
+ ACTION: 'ACTION',
89
+ CONFIG: 'CONFIG',
90
+ CONFIG_WRITE: 'CONFIG_WRITE',
91
+ CONFIG_READ: 'CONFIG_READ',
92
+ };
87
93
 
88
94
  const marginItem = 12;
89
95
  const marginHorizontal = 16;
@@ -24,4 +24,5 @@ export default {
24
24
  removeCircle: require('../Images/DevMode/remove_circle.png'),
25
25
  dienquang: require('../Images/SmartAccount/DienQuang.png'),
26
26
  lg: require('../Images/SmartAccount/LG.png'),
27
+ inforCode: require('../Images/DevMode/inforCode.png'),
27
28
  };
@@ -18,6 +18,8 @@ export const Action = {
18
18
  STAR_SCRIPTS: 'STAR_SCRIPTS',
19
19
  UNSTAR_SCRIPTS: 'UNSTAR_SCRIPTS',
20
20
  SET_BLUETOOTH_CONNECTED_DEVICE: 'SET_BLUETOOTH_CONNECTED_DEVICE',
21
+ SET_BLUETOOTH_PERM_REQUESTED: 'SET_BLUETOOTH_PERM_REQUESTED',
22
+ SET_BLUETOOTH_PERM_GRANTED: 'SET_BLUETOOTH_PERM_GRANTED',
21
23
  SET_HOME_ASSISTANT_CONNECTIONS: 'SET_HOME_ASSISTANT_CONNECTIONS',
22
24
  CHANGE_HOME_ASSISTANT_CONN_STATE: 'CHANGE_HOME_ASSISTANT_CONN_STATE',
23
25
  SET_LG_THINQ_CONNECTED: 'SET_LG_THINQ_CONNECTED',
@@ -87,7 +89,6 @@ export type AppType = {
87
89
  isFirstOpenCamera: boolean;
88
90
  isLavidaSource: boolean;
89
91
  isConnectWifiGateway: boolean;
90
- isBluetoothEnabled: boolean;
91
92
  isNetworkConnected: boolean;
92
93
  camera_opened: any[];
93
94
  notificationData: any;
@@ -115,3 +116,9 @@ export type DevModeType = {
115
116
  isEditingTemplate: boolean;
116
117
  isInEditTemplateScreen: boolean;
117
118
  };
119
+
120
+ export type BluetoothType = {
121
+ isEnabled: boolean;
122
+ permissionsGranted: boolean;
123
+ permissionsRequested: boolean;
124
+ };
@@ -35,7 +35,6 @@ export const mockDataStore: ContextData = {
35
35
  isFirstOpenCamera: true,
36
36
  isLavidaSource: false,
37
37
  isConnectWifiGateway: false,
38
- isBluetoothEnabled: true,
39
38
  isNetworkConnected: true,
40
39
  camera_opened: [],
41
40
  isLockWhenPickColor: false,
@@ -61,6 +60,11 @@ export const mockDataStore: ContextData = {
61
60
  isEditingTemplate: false,
62
61
  isInEditTemplateScreen: false,
63
62
  },
63
+ bluetooth: {
64
+ isEnabled: true,
65
+ permissionsGranted: false,
66
+ permissionsRequested: false,
67
+ },
64
68
  };
65
69
 
66
70
  export const mockSCStore = (data: ContextData): ContextData => {
@@ -86,7 +90,6 @@ export const mockSCStore = (data: ContextData): ContextData => {
86
90
  app: {
87
91
  isFirstOpenCamera: true,
88
92
  isConnectWifiGateway: false,
89
- isBluetoothEnabled: true,
90
93
  isNetworkConnected: true,
91
94
  isLockWhenPickColor: false,
92
95
  },
@@ -122,5 +125,8 @@ export const mockSCStore = (data: ContextData): ContextData => {
122
125
  isEditingTemplate: false,
123
126
  isInEditTemplateScreen: false,
124
127
  },
128
+ bluetooth: {
129
+ ...mockDataStore.bluetooth,
130
+ },
125
131
  };
126
132
  };
@@ -12,6 +12,7 @@ import {
12
12
  AppType,
13
13
  IoTType,
14
14
  DevModeType,
15
+ BluetoothType,
15
16
  } from './actionType';
16
17
  import { uniq, reduce } from 'lodash';
17
18
 
@@ -28,6 +29,7 @@ export type ContextData = {
28
29
  valueEvaluations: {};
29
30
  fetchedValueEvaluationUnits: Array<number>;
30
31
  devMode: DevModeType;
32
+ bluetooth: BluetoothType;
31
33
  percent: number;
32
34
  };
33
35
 
@@ -60,7 +62,6 @@ export const initialState = {
60
62
  isFirstOpenCamera: true,
61
63
  isLavidaSource: false,
62
64
  isConnectWifiGateway: false,
63
- isBluetoothEnabled: false,
64
65
  isNetworkConnected: false,
65
66
  camera_opened: [],
66
67
  notificationData: null,
@@ -88,6 +89,11 @@ export const initialState = {
88
89
  isEditingTemplate: false,
89
90
  isInEditTemplateScreen: false,
90
91
  },
92
+ bluetooth: {
93
+ isEnabled: false,
94
+ permissionsGranted: false,
95
+ permissionsRequested: false,
96
+ },
91
97
  };
92
98
 
93
99
  export const reducer = (currentState: ContextData, action: Action) => {
@@ -187,9 +193,9 @@ export const reducer = (currentState: ContextData, action: Action) => {
187
193
  case Action.SET_BLUETOOTH_STATE: {
188
194
  return {
189
195
  ...currentState,
190
- app: {
191
- ...currentState.app,
192
- isBluetoothEnabled: payload,
196
+ bluetooth: {
197
+ ...currentState.bluetooth,
198
+ isEnabled: payload,
193
199
  },
194
200
  };
195
201
  }
@@ -308,6 +314,23 @@ export const reducer = (currentState: ContextData, action: Action) => {
308
314
  },
309
315
  },
310
316
  };
317
+ case Action.SET_BLUETOOTH_PERM_REQUESTED:
318
+ return {
319
+ ...currentState,
320
+ bluetooth: {
321
+ ...currentState.bluetooth,
322
+ permissionsRequested: true,
323
+ },
324
+ };
325
+ case Action.SET_BLUETOOTH_PERM_GRANTED:
326
+ return {
327
+ ...currentState,
328
+ bluetooth: {
329
+ ...currentState.bluetooth,
330
+ permissionsGranted: payload,
331
+ },
332
+ };
333
+
311
334
  case Action.SET_HOME_ASSISTANT_CONNECTIONS:
312
335
  return {
313
336
  ...currentState,
@@ -0,0 +1,46 @@
1
+ import React from 'react';
2
+ import { renderHook } from '@testing-library/react-hooks';
3
+ import { SCProvider } from '../../../context';
4
+ import { mockSCStore } from '../../../context/mockStore';
5
+ import { useWatchConfigs } from '../index';
6
+ import { watchMultiConfigs, unwatchMultiConfigs } from '../../../iot/Monitor';
7
+
8
+ const mockedSetAction = jest.fn();
9
+
10
+ const wrapper = ({ children }) => <SCProvider>{children}</SCProvider>;
11
+
12
+ jest.mock('react', () => {
13
+ return {
14
+ ...jest.requireActual('react'),
15
+ useCallback: (func) => func(),
16
+ useContext: () => ({
17
+ stateData: mockSCStore({}),
18
+ setAction: mockedSetAction,
19
+ }),
20
+ };
21
+ });
22
+
23
+ jest.mock('@react-navigation/native', () => {
24
+ return {
25
+ ...jest.requireActual('@react-navigation/native'),
26
+ useFocusEffect: (func) => func(),
27
+ };
28
+ });
29
+
30
+ jest.mock('../../../iot/Monitor');
31
+
32
+ describe('Test useWatchConfigs', () => {
33
+ beforeEach(() => {
34
+ jest.useFakeTimers();
35
+ watchMultiConfigs.mockClear();
36
+ unwatchMultiConfigs.mockClear();
37
+ });
38
+
39
+ it('test unitId null', async () => {
40
+ renderHook(() => useWatchConfigs([1]), {
41
+ wrapper,
42
+ });
43
+ expect(watchMultiConfigs).toBeCalledWith([1]);
44
+ expect(unwatchMultiConfigs).toBeCalledWith([1]);
45
+ });
46
+ });
@@ -1,10 +1,32 @@
1
- import { useContext, useCallback } from 'react';
2
- import { SCContext } from '../../context';
1
+ import { useContext, useCallback, useRef, useEffect } from 'react';
2
+ import { AppState, Platform } from 'react-native';
3
+ import { SCContext, useSCContextSelector } from '../../context';
3
4
  import { Action } from '../../context/actionType';
4
5
  import { scanBluetoothDevices } from '../../iot/RemoteControl/Bluetooth';
6
+ import Permissions from 'react-native-permissions';
7
+ import { OpenSetting } from '../../utils/Permission/common';
8
+ import { useTranslations } from '../Common/useTranslations';
9
+
10
+ const permissions = [
11
+ 'android.permission.BLUETOOTH_CONNECT',
12
+ 'android.permission.BLUETOOTH_SCAN',
13
+ 'android.permission.BLUETOOTH_ADVERTISE',
14
+ ];
5
15
 
6
16
  const useBluetoothConnection = () => {
17
+ const t = useTranslations();
18
+ const appState = useRef(AppState.currentState);
19
+
7
20
  const { setAction } = useContext(SCContext);
21
+ const isEnabled = useSCContextSelector((state) => state.bluetooth.isEnabled);
22
+ const permissionsRequested = useSCContextSelector(
23
+ (state) => state.bluetooth.permissionsRequested
24
+ );
25
+ const permissionsGranted =
26
+ Platform.OS === 'ios'
27
+ ? true
28
+ : // eslint-disable-next-line react-hooks/rules-of-hooks
29
+ useSCContextSelector((state) => state.bluetooth.permissionsGranted);
8
30
 
9
31
  const onDeviceFound = useCallback(async (name, device) => {
10
32
  setAction(Action.SET_BLUETOOTH_CONNECTED_DEVICE, { name, device });
@@ -13,12 +35,64 @@ const useBluetoothConnection = () => {
13
35
 
14
36
  const bluetoothScanDevices = useCallback(
15
37
  (addresses) => {
16
- scanBluetoothDevices(addresses, onDeviceFound);
38
+ permissionsGranted && scanBluetoothDevices(addresses, onDeviceFound);
17
39
  },
18
- [onDeviceFound]
40
+ [permissionsGranted, onDeviceFound]
19
41
  );
20
42
 
43
+ useEffect(() => {
44
+ const subscription = AppState.addEventListener(
45
+ 'change',
46
+ async (nextAppState) => {
47
+ if (
48
+ appState.current.match(/inactive|background/) &&
49
+ nextAppState === 'active'
50
+ ) {
51
+ const results = await Permissions.checkMultiple(permissions);
52
+ const granted = Object.values(results).every((result) =>
53
+ [
54
+ Permissions.RESULTS.GRANTED,
55
+ Permissions.RESULTS.UNAVAILABLE,
56
+ ].includes(result)
57
+ );
58
+ setAction(Action.SET_BLUETOOTH_PERM_GRANTED, granted);
59
+ }
60
+ appState.current = nextAppState;
61
+ }
62
+ );
63
+
64
+ return () => {
65
+ subscription?.remove();
66
+ };
67
+ // eslint-disable-next-line react-hooks/exhaustive-deps
68
+ }, []);
69
+
70
+ const requestPerm = useCallback(async () => {
71
+ let results = await Permissions.requestMultiple(permissions);
72
+ const blocked = Object.values(results).some(
73
+ (result) => result === Permissions.RESULTS.BLOCKED
74
+ );
75
+ if (blocked) {
76
+ OpenSetting(
77
+ t('bluetooth_rationale_title'),
78
+ t('bluetooth_rationale_message')
79
+ );
80
+ }
81
+ const granted = Object.values(results).every((result) =>
82
+ [Permissions.RESULTS.GRANTED, Permissions.RESULTS.UNAVAILABLE].includes(
83
+ result
84
+ )
85
+ );
86
+ setAction(Action.SET_BLUETOOTH_PERM_GRANTED, granted);
87
+ setAction(Action.SET_BLUETOOTH_PERM_REQUESTED);
88
+ // eslint-disable-next-line react-hooks/exhaustive-deps
89
+ }, []);
90
+
21
91
  return {
92
+ isEnabled,
93
+ permissionsRequested,
94
+ permissionsGranted,
95
+ requestPerm,
22
96
  onDeviceFound,
23
97
  bluetoothScanDevices,
24
98
  };
@@ -5,7 +5,7 @@ const useBluetoothDeviceConnected = (device) => {
5
5
  (state) => state.iot.bluetooth.connectedDevices
6
6
  );
7
7
  const isBluetoothEnabled = useSCContextSelector((state) => {
8
- return state.app.isBluetoothEnabled;
8
+ return state.bluetooth.isEnabled;
9
9
  });
10
10
 
11
11
  const isConnected =
@@ -4,23 +4,11 @@ import t from '../../hooks/Common/useTranslations';
4
4
  import base64 from 'react-native-base64';
5
5
  import { BleManager } from 'react-native-ble-plx';
6
6
  import { ToastBottomHelper } from '../../utils/Utils';
7
- import { PermissionsAndroid } from 'react-native';
8
7
 
9
8
  const bluetoothDevices = {};
10
9
  const needToScanDevices = [];
11
10
  const bleManager = new BleManager();
12
11
 
13
- export const requestBluetoothPermissions = async () => {
14
- const results = await PermissionsAndroid.requestMultiple([
15
- 'android.permission.BLUETOOTH_CONNECT',
16
- 'android.permission.BLUETOOTH_SCAN',
17
- 'android.permission.BLUETOOTH_ADVERTISE',
18
- ]);
19
- return Object.values(results).every(
20
- (result) => result === PermissionsAndroid.RESULTS.GRANTED
21
- );
22
- };
23
-
24
12
  export const SEND_COMMAND_OVER_BLUETOOTH_FAIL =
25
13
  'SEND_COMMAND_OVER_BLUETOOTH_FAIL';
26
14
 
@@ -45,10 +33,6 @@ export const scanBluetoothDevices = async (names, onDeviceFound) => {
45
33
  };
46
34
 
47
35
  export const realScanBluetoothDevices = async (onDeviceFound) => {
48
- const permissionsGranted = await requestBluetoothPermissions();
49
- if (!permissionsGranted) {
50
- return;
51
- }
52
36
  if (!needToScanDevices.length) {
53
37
  return;
54
38
  }
@@ -12,20 +12,6 @@ const bleManager = new BleManager();
12
12
 
13
13
  const mockOnDeviceFound = jest.fn();
14
14
 
15
- const mockRequestMultiplePerms = jest.fn();
16
-
17
- jest.mock(
18
- 'react-native/Libraries/PermissionsAndroid/PermissionsAndroid',
19
- () => {
20
- return {
21
- ...jest.requireActual(
22
- 'react-native/Libraries/PermissionsAndroid/PermissionsAndroid'
23
- ),
24
- requestMultiple: mockRequestMultiplePerms,
25
- };
26
- }
27
- );
28
-
29
15
  describe('Test IOT Bluetooth', () => {
30
16
  beforeEach(() => {
31
17
  mockOnDeviceFound.mockClear();
@@ -33,9 +19,6 @@ describe('Test IOT Bluetooth', () => {
33
19
  bleManager.stopDeviceScan.mockClear();
34
20
  clearNeedToScanDevices();
35
21
  clearFoundDevices();
36
- mockRequestMultiplePerms.mockImplementation(async (perms) =>
37
- perms.map(() => 'granted')
38
- );
39
22
  });
40
23
 
41
24
  afterEach(() => {
@@ -48,14 +31,6 @@ describe('Test IOT Bluetooth', () => {
48
31
  expect(bleManager.startDeviceScan).toBeCalled();
49
32
  });
50
33
 
51
- test('Scan bluetooth device not scan without permissions', async () => {
52
- mockRequestMultiplePerms.mockImplementation(async (perms) =>
53
- perms.map(() => 'denied')
54
- );
55
- await scanBluetoothDevices(['123456'], mockOnDeviceFound);
56
- expect(bleManager.startDeviceScan).not.toBeCalled();
57
- });
58
-
59
34
  test('When look for bluetooth name, hardware will auto stop after period of time', async () => {
60
35
  jest.useFakeTimers();
61
36
  await scanBluetoothDevices(['123456'], mockOnDeviceFound);
@@ -27,6 +27,15 @@ import TemplateDetail from '../screens/Template/detail';
27
27
  import Information from '../screens/Template/Information';
28
28
  import GatewayList from '../screens/Template/GatewayList';
29
29
  import EditTemplate from '../screens/Template/EditTemplate';
30
+ import GatewayDetail from '../screens/Gateway/GatewayDetail';
31
+ import GatewayInfo from '../screens/Gateway/GatewayInfo';
32
+ import GatewayConnectionMethods from '../screens/Gateway/GatewayConnectionMethods';
33
+ import DeviceZigbeeDetail from '../screens/Gateway/DeviceZigbeeDetail';
34
+ import DeviceGatewayInfo from '../screens/Gateway/DeviceGatewayInfo';
35
+ import DetailConfigActionZigbee from '../screens/Gateway/DetailConfigActionZigbee';
36
+ import DeviceModbusDetail from '../screens/Gateway/DeviceModbusDetail';
37
+ import DetailConfigActionModbus from '../screens/Gateway/DetailConfigActionModbus';
38
+ import DetailChildConfigActionModbus from '../screens/Gateway/DetailConfigActionModbus';
30
39
 
31
40
  const Tab = createBottomTabNavigator();
32
41
  const Drawer = createDrawerNavigator();
@@ -132,6 +141,36 @@ const DevModeStack = () => {
132
141
  >
133
142
  <Stack.Screen component={DrawerScreen} name={Routes.DrawerScreen} />
134
143
  <Stack.Screen component={TemplateDetail} name={Routes.TemplateDetail} />
144
+ <Stack.Screen component={GatewayDetail} name={Routes.GatewayDetail} />
145
+ <Stack.Screen component={GatewayInfo} name={Routes.GatewayInfo} />
146
+ <Stack.Screen
147
+ component={DeviceGatewayInfo}
148
+ name={Routes.DeviceGatewayInfo}
149
+ />
150
+ <Stack.Screen
151
+ component={GatewayConnectionMethods}
152
+ name={Routes.GatewayConnectionMethods}
153
+ />
154
+ <Stack.Screen
155
+ component={DeviceZigbeeDetail}
156
+ name={Routes.DeviceZigbeeDetail}
157
+ />
158
+ <Stack.Screen
159
+ component={DeviceModbusDetail}
160
+ name={Routes.DeviceModbusDetail}
161
+ />
162
+ <Stack.Screen
163
+ component={DetailConfigActionZigbee}
164
+ name={Routes.DetailConfigActionZigbee}
165
+ />
166
+ <Stack.Screen
167
+ component={DetailConfigActionModbus}
168
+ name={Routes.DetailConfigActionModbus}
169
+ />
170
+ <Stack.Screen
171
+ component={DetailChildConfigActionModbus}
172
+ name={Routes.DetailChildConfigActionModbus}
173
+ />
135
174
  <Stack.Screen component={Information} name={Routes.Information} />
136
175
  <Stack.Screen component={GatewayList} name={Routes.GatewayList} />
137
176
  <Stack.Screen component={EditTemplate} name={Routes.EditTemplate} />
@@ -82,22 +82,35 @@ export const UnitStack = memo((props) => {
82
82
  // eslint-disable-next-line react-hooks/exhaustive-deps
83
83
  }, []);
84
84
 
85
- const { onDeviceFound } = useBluetoothConnection();
85
+ const {
86
+ isEnabled: isBluetoothEnabled,
87
+ permissionsRequested: bluetoothPermRequested,
88
+ permissionsGranted: bluetoothPermGranted,
89
+ requestPerm: requestBluetoothPerm,
90
+ onDeviceFound,
91
+ } = useBluetoothConnection();
92
+
93
+ useEffect(() => {
94
+ !bluetoothPermRequested && requestBluetoothPerm();
95
+ }, [bluetoothPermRequested, requestBluetoothPerm]);
86
96
 
87
97
  useEffect(() => {
88
98
  const subscription = bleManager.onStateChange((state) => {
89
99
  const connected = state === 'PoweredOn';
90
100
  setAction(Action.SET_BLUETOOTH_STATE, connected);
91
- if (connected) {
92
- ToastBottomHelper.success(t('text_ble_is_powered_on'));
93
- realScanBluetoothDevices(onDeviceFound);
94
- }
101
+ connected && ToastBottomHelper.success(t('text_ble_is_powered_on'));
95
102
  }, true);
96
103
 
97
104
  return () => subscription.remove();
98
105
  // eslint-disable-next-line react-hooks/exhaustive-deps
99
106
  }, []);
100
107
 
108
+ useEffect(() => {
109
+ isBluetoothEnabled &&
110
+ bluetoothPermGranted &&
111
+ realScanBluetoothDevices(onDeviceFound);
112
+ }, [isBluetoothEnabled, bluetoothPermGranted, onDeviceFound]);
113
+
101
114
  useEffect(() => {
102
115
  const fetchFavoriteDevices = async () => {
103
116
  const id = unitId || unitData?.id;
@@ -17,13 +17,6 @@ const wrapComponent = (props) => (
17
17
  </SCProvider>
18
18
  );
19
19
 
20
- jest.mock('react', () => {
21
- return {
22
- ...jest.requireActual('react'),
23
- memo: (x) => x,
24
- };
25
- });
26
-
27
20
  it('test FilterPopup', async () => {
28
21
  Date.now = jest.fn(() => new Date('2021-09-09T10:00:00.000Z'));
29
22
  const mockOnHide = jest.fn();
@@ -12,13 +12,6 @@ const wrapComponent = (props) => (
12
12
  </SCProvider>
13
13
  );
14
14
 
15
- jest.mock('react', () => {
16
- return {
17
- ...jest.requireActual('react'),
18
- memo: (x) => x,
19
- };
20
- });
21
-
22
15
  it('test ItemLog smart assistant', async () => {
23
16
  let tree;
24
17
  let props = {
@@ -25,13 +25,6 @@ jest.mock('react-redux', () => {
25
25
  };
26
26
  });
27
27
 
28
- jest.mock('react', () => {
29
- return {
30
- ...jest.requireActual('react'),
31
- memo: (x) => x,
32
- };
33
- });
34
-
35
28
  const mock = new MockAdapter(api.axiosInstance);
36
29
 
37
30
  const wrapComponent = (route) => (
@@ -37,13 +37,6 @@ jest.mock('@react-navigation/native', () => {
37
37
  };
38
38
  });
39
39
 
40
- jest.mock('react', () => {
41
- return {
42
- ...jest.requireActual('react'),
43
- memo: (x) => x,
44
- };
45
- });
46
-
47
40
  jest.mock('react-native-maps', () => {
48
41
  const { forwardRef } = require('react');
49
42
  const { View } = require('react-native');
@@ -252,6 +252,7 @@ const SelectAction = memo(({ route }) => {
252
252
  case 'three_button_action_template':
253
253
  case 'OnOffSimpleActionTemplate':
254
254
  case 'curtain_action_template':
255
+ case 'OnOffSmartLockActionTemplate':
255
256
  index = newActions.findIndex((item) => {
256
257
  return (
257
258
  item.template === 'on_off_button_action_template' ||
@@ -264,13 +265,15 @@ const SelectAction = memo(({ route }) => {
264
265
  });
265
266
  break;
266
267
 
267
- case 'OptionsDropdownActionTemplate':
268
- case 'NumberUpDownActionTemplate':
269
268
  case 'StatesGridActionTemplate':
270
269
  index = newActions.findIndex(
271
270
  (item) => item.template === action.template
272
271
  );
273
272
  break;
273
+ case 'OptionsDropdownActionTemplate':
274
+ case 'NumberUpDownActionTemplate':
275
+ index = newActions.findIndex((item) => item.action === action.action);
276
+ break;
274
277
  }
275
278
 
276
279
  if (index < 0) {
@@ -438,13 +441,13 @@ const SelectAction = memo(({ route }) => {
438
441
  });
439
442
 
440
443
  const RenderActionItem = ({ device, data, onSelectAction }) => {
441
- if (!data) {
444
+ if (!data.length) {
442
445
  return null;
443
446
  }
444
447
  const actionTemplate = [];
445
- let optionsDropdownActionTemplate = {};
446
- let numberUpDownActionTemplate = {};
447
- let statesGridActionTemplate = {};
448
+ const optionsDropdownActionTemplates = [];
449
+ const numberUpDownActionTemplates = [];
450
+ const statesGridActionTemplates = [];
448
451
 
449
452
  data.forEach((item) => {
450
453
  switch (item.template) {
@@ -457,13 +460,13 @@ const RenderActionItem = ({ device, data, onSelectAction }) => {
457
460
  actionTemplate.push(item);
458
461
  break;
459
462
  case 'OptionsDropdownActionTemplate':
460
- optionsDropdownActionTemplate = { ...item };
463
+ optionsDropdownActionTemplates.push(item);
461
464
  break;
462
465
  case 'NumberUpDownActionTemplate':
463
- numberUpDownActionTemplate = { ...item };
466
+ numberUpDownActionTemplates.push(item);
464
467
  break;
465
468
  case 'StatesGridActionTemplate':
466
- statesGridActionTemplate = { ...item };
469
+ statesGridActionTemplates.push(item);
467
470
  break;
468
471
  }
469
472
  });
@@ -481,27 +484,28 @@ const RenderActionItem = ({ device, data, onSelectAction }) => {
481
484
  onSelectAction={handleOnSelectAction}
482
485
  />
483
486
  )}
484
- {Object.keys(numberUpDownActionTemplate).length > 0 && (
487
+ {numberUpDownActionTemplates.map((item) => (
485
488
  <NumberUpDownActionTemplate
486
489
  device={device}
487
- data={numberUpDownActionTemplate}
490
+ data={item}
488
491
  onSelectAction={handleOnSelectAction}
489
492
  />
490
- )}
491
- {Object.keys(optionsDropdownActionTemplate).length > 0 && (
493
+ ))}
494
+
495
+ {optionsDropdownActionTemplates.map((item) => (
492
496
  <OptionsDropdownActionTemplate
493
497
  device={device}
494
- data={optionsDropdownActionTemplate}
498
+ data={item}
495
499
  onSelectAction={handleOnSelectAction}
496
500
  />
497
- )}
498
- {Object.keys(statesGridActionTemplate).length > 0 && (
501
+ ))}
502
+ {statesGridActionTemplates.map((item) => (
499
503
  <StatesGridActionTemplate
500
504
  device={device}
501
- data={statesGridActionTemplate}
505
+ data={item}
502
506
  onSelectAction={handleOnSelectAction}
503
507
  />
504
- )}
508
+ ))}
505
509
  </>
506
510
  );
507
511
  };