@eohjsc/react-native-smart-city 0.3.38 → 0.3.39

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 (36) hide show
  1. package/package.json +1 -1
  2. package/src/commons/AlertAction/index.js +5 -1
  3. package/src/commons/Device/ItemDevice.js +21 -34
  4. package/src/commons/Form/TextInput.js +9 -1
  5. package/src/commons/WrapParallaxScrollView/index.js +6 -1
  6. package/src/configs/AccessibilityLabel.js +5 -0
  7. package/src/context/actionType.ts +4 -0
  8. package/src/context/mockStore.ts +3 -0
  9. package/src/context/reducer.ts +17 -0
  10. package/src/hooks/Common/index.js +0 -2
  11. package/src/hooks/Common/useDevicesStatus.js +26 -28
  12. package/src/hooks/IoT/__test__/useRemoteControl.test.js +3 -0
  13. package/src/hooks/IoT/index.js +8 -0
  14. package/src/hooks/IoT/useBluetoothConnection.js +27 -0
  15. package/src/hooks/IoT/useBluetoothDeviceConnected.js +18 -0
  16. package/src/hooks/IoT/useEoHBackendDeviceConnected.js +21 -0
  17. package/src/hooks/{Common → IoT}/useHomeAssistantDeviceConnected.js +0 -0
  18. package/src/hooks/IoT/useRemoteControl.js +8 -19
  19. package/src/iot/RemoteControl/Bluetooth.js +5 -12
  20. package/src/iot/RemoteControl/__test__/Bluetooth.test.js +19 -63
  21. package/src/navigations/UnitStack.js +11 -1
  22. package/src/screens/AddLocationMaps/index.js +1 -0
  23. package/src/screens/AddNewGateway/RenameNewDevices.js +7 -6
  24. package/src/screens/AddNewGateway/ScanGatewayQR.js +2 -6
  25. package/src/screens/AddNewGateway/__test__/ScanGatewayQR.test.js +1 -1
  26. package/src/screens/ConfirmUnitDeletion/index.js +6 -1
  27. package/src/screens/Device/__test__/detail.test.js +1 -0
  28. package/src/screens/Device/detail.js +53 -59
  29. package/src/screens/SubUnit/AddSubUnit.js +5 -1
  30. package/src/screens/Unit/MoreMenu.js +3 -3
  31. package/src/screens/Unit/SelectAddress.js +1 -0
  32. package/src/screens/Unit/components/MyUnitDevice/index.js +1 -1
  33. package/src/screens/Unit/hook/useUnitConnectRemoteDevices.js +11 -8
  34. package/src/screens/UnitSummary/__test__/index.test.js +1 -1
  35. package/src/screens/UnitSummary/components/RunningDevices/__test__/index.test.js +1 -1
  36. package/src/screens/UnitSummary/components/RunningDevices/index.js +3 -17
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@eohjsc/react-native-smart-city",
3
3
  "title": "React Native Smart Home",
4
- "version": "0.3.38",
4
+ "version": "0.3.39",
5
5
  "description": "TODO",
6
6
  "main": "index.js",
7
7
  "files": [
@@ -39,7 +39,11 @@ const AlertAction = ({
39
39
  <Animated.View style={[styles.popoverStyle, animatedStyle]}>
40
40
  <View style={styles.modalWrapper}>
41
41
  <View style={styles.modalHeader}>
42
- <Text semibold style={styles.modalHeaderText}>
42
+ <Text
43
+ semibold
44
+ style={styles.modalHeaderText}
45
+ accessibilityLabel={`${accessibilityLabelPrefix}_TITLE`}
46
+ >
43
47
  {title}
44
48
  </Text>
45
49
  </View>
@@ -11,9 +11,11 @@ import { useNavigation } from '@react-navigation/native';
11
11
  import { useTranslations } from '../../hooks/Common/useTranslations';
12
12
  import ItemQuickAction from '../../commons/Action/ItemQuickAction';
13
13
  import Text from '../../commons/Text';
14
- import { isDeviceConnected as isBluetoothDeviceConnected } from '../../iot/RemoteControl/Bluetooth';
15
- import { useSCContextSelector } from '../../context';
16
- import { useHomeAssistantDeviceConnected } from '../../hooks/Common';
14
+ import {
15
+ useHomeAssistantDeviceConnected,
16
+ useBluetoothDeviceConnected,
17
+ useEoHBackendDeviceConnected,
18
+ } from '../../hooks/IoT';
17
19
 
18
20
  import { Colors } from '../../configs';
19
21
  import {
@@ -27,20 +29,20 @@ const ItemDevice = memo(
27
29
  ({ svgMain, description, title, sensor, unit, station, wrapStyle }) => {
28
30
  const t = useTranslations();
29
31
  const navigation = useNavigation();
30
- const isBluetoothEnabled = useSCContextSelector(
31
- (state) => state.app.isBluetoothEnabled
32
- );
33
- const isNetworkConnected = useSCContextSelector(
34
- (state) => state.app.isNetworkConnected
35
- );
36
- const statuses = useSCContextSelector(
37
- (state) => state.iot.internet.statuses
38
- );
32
+
33
+ const {
34
+ isConnected: isEoHBackendConnected,
35
+ isFetchingStatus: isFetchingStatusFromEoHBackend,
36
+ } = useEoHBackendDeviceConnected(sensor);
37
+
39
38
  const {
40
39
  isConnected: isHomeAssistantConnected,
41
40
  isConnecting: isHomeAssistantConnecting,
42
41
  } = useHomeAssistantDeviceConnected(sensor);
43
42
 
43
+ const { isConnected: isBluetoothConnected } =
44
+ useBluetoothDeviceConnected(sensor);
45
+
44
46
  const goToSensorDisplay = useCallback(() => {
45
47
  navigation.navigate(Routes.DeviceDetail, {
46
48
  unitData: unit,
@@ -50,30 +52,15 @@ const ItemDevice = memo(
50
52
  });
51
53
  }, [navigation, sensor, station, title, unit]);
52
54
 
53
- const isConnectedViaInternet =
54
- isNetworkConnected &&
55
- !!sensor &&
56
- sensor.id in statuses &&
57
- statuses[sensor.id]?.isConnected;
58
-
59
- const isFetchingStatus =
60
- isNetworkConnected && !!sensor && !(sensor.id in statuses);
61
-
62
- const isConnectedViaBLE =
63
- isBluetoothEnabled &&
64
- isBluetoothDeviceConnected(
65
- sensor?.remote_control_options?.bluetooth?.address
66
- );
67
-
68
55
  const borderColor = (() => {
69
56
  if (!!sensor && sensor?.is_managed_by_backend) {
70
57
  if (sensor?.device_type === DEVICE_TYPE.LG_THINQ) {
71
58
  return Colors.Gray4;
72
59
  }
73
- if (isConnectedViaBLE) {
60
+ if (isBluetoothConnected) {
74
61
  return Colors.Gray4;
75
62
  }
76
- if (isFetchingStatus || isConnectedViaInternet) {
63
+ if (isFetchingStatusFromEoHBackend || isEoHBackendConnected) {
77
64
  return Colors.Gray4;
78
65
  }
79
66
  return Colors.Red6;
@@ -92,13 +79,13 @@ const ItemDevice = memo(
92
79
  if (sensor?.device_type === DEVICE_TYPE.LG_THINQ) {
93
80
  return t('connected');
94
81
  }
95
- if (isConnectedViaBLE) {
82
+ if (isBluetoothConnected) {
96
83
  return t('connected');
97
84
  }
98
- if (isFetchingStatus) {
85
+ if (isFetchingStatusFromEoHBackend) {
99
86
  return '';
100
87
  }
101
- if (isConnectedViaInternet) {
88
+ if (isEoHBackendConnected) {
102
89
  return t('connected');
103
90
  }
104
91
  return t('disconnected');
@@ -120,10 +107,10 @@ const ItemDevice = memo(
120
107
  if (sensor?.device_type === DEVICE_TYPE.LG_THINQ) {
121
108
  return true;
122
109
  }
123
- if (isConnectedViaBLE) {
110
+ if (isBluetoothConnected) {
124
111
  return true;
125
112
  }
126
- if (isFetchingStatus) {
113
+ if (isFetchingStatusFromEoHBackend) {
127
114
  return false;
128
115
  }
129
116
  return true;
@@ -29,6 +29,7 @@ const _TextInput = ({
29
29
  accessibilityLabel,
30
30
  selection,
31
31
  onSelectionChange,
32
+ accessibilityLabelError,
32
33
  }) => {
33
34
  const errorStyle = !!errorText && styles.errorWrap;
34
35
  return (
@@ -70,7 +71,14 @@ const _TextInput = ({
70
71
  onSelectionChange={onSelectionChange}
71
72
  />
72
73
  {extraText && extraText}
73
- {!!errorText && <Text style={styles.errorText}>{errorText}</Text>}
74
+ {!!errorText && (
75
+ <Text
76
+ accessibilityLabel={accessibilityLabelError}
77
+ style={styles.errorText}
78
+ >
79
+ {errorText}
80
+ </Text>
81
+ )}
74
82
  </View>
75
83
  );
76
84
  };
@@ -37,7 +37,12 @@ const WrapParallaxScrollView = ({
37
37
  hideRightPlus={hideRightPlus}
38
38
  idButtonMore={idButtonMore}
39
39
  />
40
- <Text numberOfLines={1} semibold style={styles.nameUnit}>
40
+ <Text
41
+ numberOfLines={1}
42
+ semibold
43
+ style={styles.nameUnit}
44
+ accessibilityLabel={AccessibilityLabel.HEADER_UNIT_BUTTON_TEXT}
45
+ >
41
46
  {title}
42
47
  </Text>
43
48
  {contentBackground}
@@ -43,6 +43,8 @@ export default {
43
43
 
44
44
  // ConfirmUnitDeletion
45
45
  CONFIRM_UNIT_DELETION_BUTTON: 'CONFIRM_UNIT_DELETION_BUTTON',
46
+ CONFIRM_UNIT_DELETION_CONFIRM_FIELD: 'CONFIRM_UNIT_DELETION_CONFIRM_FIELD',
47
+ CONFIRM_UNIT_DELETION_CONFIRM_ERROR: 'CONFIRM_UNIT_DELETION_CONFIRM_ERROR',
46
48
 
47
49
  //member list
48
50
  SHARING_MEMBER: 'SHARING_MEMBER',
@@ -252,6 +254,8 @@ export default {
252
254
  EMERGENCY_BUTTON: 'EMERGENCY_BUTTON',
253
255
 
254
256
  // ADD SUB UNIT
257
+ ADD_SUB_UNIT_SCREEN: 'ADD_SUB_UNIT_SCREEN',
258
+ ADD_SUB_UNIT_NAME_FIELD: 'ADD_SUB_UNIT_NAME_FIELD',
255
259
  ADD_SUB_UNIT_BUTTON_CHOOSE_PHOTO: 'ADD_SUB_UNIT_BUTTON_CHOOSE_PHOTO',
256
260
  ADD_SUB_UNIT_BUTTON_CHOOSE_LOCATION: 'ADD_SUB_UNIT_BUTTON_CHOOSE_LOCATION',
257
261
 
@@ -440,6 +444,7 @@ export default {
440
444
 
441
445
  // Header Unit
442
446
  HEADER_UNIT_BUTTON_BACK: 'HEADER_UNIT_BUTTON_BACK',
447
+ HEADER_UNIT_BUTTON_TEXT: 'HEADER_UNIT_BUTTON_TEXT',
443
448
  HEADER_UNIT_BUTTON_ADD: 'HEADER_UNIT_BUTTON_ADD',
444
449
  HEADER_UNIT_BUTTON_MORE: 'HEADER_UNIT_BUTTON_MORE',
445
450
 
@@ -17,6 +17,7 @@ export const Action = {
17
17
  SET_STARRED_SCRIPTS: 'SET_STARRED_SCRIPTS',
18
18
  STAR_SCRIPTS: 'STAR_SCRIPTS',
19
19
  UNSTAR_SCRIPTS: 'UNSTAR_SCRIPTS',
20
+ SET_BLUETOOTH_CONNECTED_DEVICE: 'SET_BLUETOOTH_CONNECTED_DEVICE',
20
21
  SET_HOME_ASSISTANT_CONNECTIONS: 'SET_HOME_ASSISTANT_CONNECTIONS',
21
22
  CHANGE_HOME_ASSISTANT_CONN_STATE: 'CHANGE_HOME_ASSISTANT_CONN_STATE',
22
23
  SET_LG_THINQ_CONNECTED: 'SET_LG_THINQ_CONNECTED',
@@ -95,6 +96,9 @@ export type AppType = {
95
96
  };
96
97
 
97
98
  export type IoTType = {
99
+ bluetooth: {
100
+ connectedDevices: {};
101
+ };
98
102
  homeassistant: {
99
103
  connections: {};
100
104
  };
@@ -41,6 +41,9 @@ export const mockDataStore: ContextData = {
41
41
  isLockWhenPickColor: false,
42
42
  },
43
43
  iot: {
44
+ bluetooth: {
45
+ connectedDevices: {},
46
+ },
44
47
  homeassistant: {
45
48
  connections: {},
46
49
  },
@@ -68,6 +68,9 @@ export const initialState = {
68
68
  isLockWhenPickColor: false,
69
69
  },
70
70
  iot: {
71
+ bluetooth: {
72
+ connectedDevices: {},
73
+ },
71
74
  homeassistant: {
72
75
  connections: {},
73
76
  },
@@ -291,6 +294,20 @@ export const reducer = (currentState: ContextData, action: Action) => {
291
294
  },
292
295
  };
293
296
 
297
+ case Action.SET_BLUETOOTH_CONNECTED_DEVICE:
298
+ return {
299
+ ...currentState,
300
+ iot: {
301
+ ...currentState.iot,
302
+ bluetooth: {
303
+ ...currentState.iot.bluetooth,
304
+ connectedDevices: {
305
+ ...currentState.iot.bluetooth.connectedDevices,
306
+ [payload.name]: payload.device,
307
+ },
308
+ },
309
+ },
310
+ };
294
311
  case Action.SET_HOME_ASSISTANT_CONNECTIONS:
295
312
  return {
296
313
  ...currentState,
@@ -5,7 +5,6 @@ import useKeyboardShow from './useKeyboardShow';
5
5
  import usePopover from './usePopover';
6
6
  import useTitleHeader from './useTitleHeader';
7
7
  import useDevicesStatus from './useDevicesStatus';
8
- import useHomeAssistantDeviceConnected from './useHomeAssistantDeviceConnected';
9
8
  import { useBlockBackAndroid } from './useBlockBackAndroid';
10
9
  import { useIsOwnerOfUnit } from './useIsOwnerOfUnit';
11
10
  import { useStatusBar } from './useStatusBar';
@@ -23,5 +22,4 @@ export {
23
22
  useStatusBar,
24
23
  useGetIdUser,
25
24
  useDevicesStatus,
26
- useHomeAssistantDeviceConnected,
27
25
  };
@@ -1,6 +1,5 @@
1
- import { useCallback, useContext } from 'react';
2
- import { InteractionManager } from 'react-native';
3
- import { useFocusEffect } from '@react-navigation/native';
1
+ import { useCallback, useContext, useEffect } from 'react';
2
+ import { useIsFocused } from '@react-navigation/native';
4
3
  import { SCContext, useSCContextSelector } from '../../context';
5
4
  import { axiosGet } from '../../utils/Apis/axios';
6
5
  import { API } from '../../configs';
@@ -13,8 +12,13 @@ const useDevicesStatus = (unit, devices) => {
13
12
  const isNetworkConnected = useSCContextSelector(
14
13
  (state) => state.app.isNetworkConnected
15
14
  );
15
+ const isFocused = useIsFocused();
16
16
 
17
17
  const getDevicesStatus = useCallback(async (_unit, _devices) => {
18
+ if (timeoutId) {
19
+ clearTimeout(timeoutId);
20
+ timeoutId = 0;
21
+ }
18
22
  const params = new URLSearchParams();
19
23
  _devices.forEach((device) => {
20
24
  params.append('sensors', device.id);
@@ -30,32 +34,26 @@ const useDevicesStatus = (unit, devices) => {
30
34
  // eslint-disable-next-line react-hooks/exhaustive-deps
31
35
  }, []);
32
36
 
33
- useFocusEffect(
34
- useCallback(() => {
35
- const task = InteractionManager.runAfterInteractions(() => {
36
- if (!devices?.length || !isNetworkConnected) {
37
- return;
38
- }
37
+ useEffect(() => {
38
+ if (!isFocused) {
39
+ return;
40
+ }
41
+ if (!devices?.length || !isNetworkConnected) {
42
+ return;
43
+ }
39
44
 
40
- const managedDevices = devices.filter(
41
- (device) => device?.is_managed_by_backend
42
- );
43
- if (!managedDevices.length) {
44
- return;
45
- }
46
- timeoutId = setTimeout(
47
- () => getDevicesStatus(unit, managedDevices),
48
- 200
49
- );
50
- });
51
-
52
- return () => {
53
- clearTimeout(timeoutId);
54
- task.cancel();
55
- };
56
- // eslint-disable-next-line react-hooks/exhaustive-deps
57
- }, [unit, devices, isNetworkConnected])
58
- );
45
+ const managedDevices = devices.filter(
46
+ (device) => device?.is_managed_by_backend
47
+ );
48
+ if (!managedDevices.length) {
49
+ return;
50
+ }
51
+ getDevicesStatus(unit, devices);
52
+ return () => {
53
+ clearTimeout(timeoutId);
54
+ timeoutId = 0;
55
+ };
56
+ }, [getDevicesStatus, unit, devices, isNetworkConnected, isFocused]);
59
57
  };
60
58
 
61
59
  export default useDevicesStatus;
@@ -88,6 +88,8 @@ describe('Test useRemoteControl', () => {
88
88
  });
89
89
 
90
90
  it('test send remote command via bluetooth failed then send via internet', async () => {
91
+ action.command_prefer_over_bluetooth = true;
92
+ action.command_prefer_over_internet = true;
91
93
  const { result: sendRemoteCommand } = renderHook(() => useRemoteControl(), {
92
94
  wrapper,
93
95
  });
@@ -156,6 +158,7 @@ describe('Test useRemoteControl', () => {
156
158
  data,
157
159
  'internet'
158
160
  );
161
+ expect(sendCommandOverInternet).toBeCalledTimes(1);
159
162
  expect(sendCommandOverBluetooth).not.toBeCalled();
160
163
  expect(sendCommandOverHomeAssistant).not.toBeCalled();
161
164
  expect(sendCommandOverLGThinq).not.toBeCalled();
@@ -1,13 +1,21 @@
1
+ import useBluetoothConnection from './useBluetoothConnection';
1
2
  import useHomeAssistantConnection from './useHomeAssistantConnection';
2
3
  import useRemoteControl from './useRemoteControl';
3
4
  import useValueEvaluations from './useValueEvaluation';
4
5
  import useWatchConfigs from './useWatchConfigs';
5
6
  import useUnwatchLGDeviceConfigControl from './useUnwatchLGDeviceConfigControl';
7
+ import useHomeAssistantDeviceConnected from './useHomeAssistantDeviceConnected';
8
+ import useBluetoothDeviceConnected from './useBluetoothDeviceConnected';
9
+ import useEoHBackendDeviceConnected from './useEoHBackendDeviceConnected';
6
10
 
7
11
  export {
12
+ useBluetoothConnection,
8
13
  useHomeAssistantConnection,
9
14
  useRemoteControl,
10
15
  useValueEvaluations,
11
16
  useWatchConfigs,
12
17
  useUnwatchLGDeviceConfigControl,
18
+ useHomeAssistantDeviceConnected,
19
+ useBluetoothDeviceConnected,
20
+ useEoHBackendDeviceConnected,
13
21
  };
@@ -0,0 +1,27 @@
1
+ import { useContext, useCallback } from 'react';
2
+ import { SCContext } from '../../context';
3
+ import { Action } from '../../context/actionType';
4
+ import { scanBluetoothDevices } from '../../iot/RemoteControl/Bluetooth';
5
+
6
+ const useBluetoothConnection = () => {
7
+ const { setAction } = useContext(SCContext);
8
+
9
+ const onDeviceFound = useCallback(async (name, device) => {
10
+ setAction(Action.SET_BLUETOOTH_CONNECTED_DEVICE, { name, device });
11
+ // eslint-disable-next-line react-hooks/exhaustive-deps
12
+ }, []);
13
+
14
+ const bluetoothScanDevices = useCallback(
15
+ (addresses) => {
16
+ scanBluetoothDevices(addresses, onDeviceFound);
17
+ },
18
+ [onDeviceFound]
19
+ );
20
+
21
+ return {
22
+ onDeviceFound,
23
+ bluetoothScanDevices,
24
+ };
25
+ };
26
+
27
+ export default useBluetoothConnection;
@@ -0,0 +1,18 @@
1
+ import { useSCContextSelector } from '../../context';
2
+
3
+ const useBluetoothDeviceConnected = (device) => {
4
+ const connectedDevices = useSCContextSelector(
5
+ (state) => state.iot.bluetooth.connectedDevices
6
+ );
7
+ const isBluetoothEnabled = useSCContextSelector((state) => {
8
+ return state.app.isBluetoothEnabled;
9
+ });
10
+
11
+ const isConnected =
12
+ isBluetoothEnabled &&
13
+ !!connectedDevices[device?.remote_control_options?.bluetooth?.address];
14
+
15
+ return { isConnected };
16
+ };
17
+
18
+ export default useBluetoothDeviceConnected;
@@ -0,0 +1,21 @@
1
+ import { useSCContextSelector } from '../../context';
2
+
3
+ const useEoHBackendDeviceConnected = (device) => {
4
+ const isNetworkConnected = useSCContextSelector(
5
+ (state) => state.app.isNetworkConnected
6
+ );
7
+ const statuses = useSCContextSelector((state) => state.iot.internet.statuses);
8
+
9
+ const isConnected =
10
+ isNetworkConnected &&
11
+ !!device &&
12
+ device.id in statuses &&
13
+ statuses[device.id]?.isConnected;
14
+
15
+ const isFetchingStatus =
16
+ isNetworkConnected && !!device && !(device.id in statuses);
17
+
18
+ return { isConnected, isFetchingStatus };
19
+ };
20
+
21
+ export default useEoHBackendDeviceConnected;
@@ -15,7 +15,7 @@ const useRemoteControl = () => {
15
15
  );
16
16
 
17
17
  const sendRemoteCommand = useCallback(
18
- async (sensor, action, data, userId) => {
18
+ async (device, action, data, userId) => {
19
19
  // No action, raise not authorized
20
20
  let result = false;
21
21
  if (!action) {
@@ -27,39 +27,28 @@ const useRemoteControl = () => {
27
27
 
28
28
  if (action.command_prefer_over_bluetooth) {
29
29
  try {
30
- result = await sendCommandOverBluetooth(sensor, action, data, userId);
30
+ result = await sendCommandOverBluetooth(device, action, data, userId);
31
31
  } catch (err) {
32
- result = false;
33
- if (err === SEND_COMMAND_OVER_BLUETOOTH_FAIL) {
34
- result = await sendCommandOverInternet(
35
- sensor,
36
- action,
37
- data,
38
- 'bluetooth'
39
- );
40
- } else {
32
+ if (err !== SEND_COMMAND_OVER_BLUETOOTH_FAIL) {
41
33
  throw err;
42
34
  }
35
+ result = false;
43
36
  }
44
37
  }
45
38
 
46
- if (
47
- (action.command_prefer_over_internet ||
48
- action.command_prefer_over_bluetooth) &&
49
- !result
50
- ) {
39
+ if (action.command_prefer_over_internet && !result) {
51
40
  result = await sendCommandOverInternet(
52
- sensor,
41
+ device,
53
42
  action,
54
43
  data,
55
- 'internet'
44
+ action.command_prefer_over_bluetooth ? 'bluetooth' : 'internet'
56
45
  );
57
46
  }
58
47
 
59
48
  if (action.command_prefer_over_googlehome && !result) {
60
49
  result = await sendCommandOverHomeAssistant(
61
50
  homeAssistantConnections,
62
- sensor,
51
+ device,
63
52
  action,
64
53
  data
65
54
  );
@@ -22,24 +22,17 @@ export const clearFoundDevices = () => {
22
22
  }
23
23
  };
24
24
 
25
- bleManager.onStateChange((state) => {
26
- if (state === 'PoweredOn') {
27
- ToastBottomHelper.success(t('text_ble_is_powered_on'));
28
- realScanBluetoothDevices();
29
- }
30
- }, true);
31
-
32
- export const scanBluetoothDevices = (names) => {
25
+ export const scanBluetoothDevices = (names, onDeviceFound) => {
33
26
  names.map((name) => {
34
27
  if (bluetoothDevices[name]) {
35
28
  return;
36
29
  }
37
30
  needToScanDevices.push(name);
38
31
  });
39
- realScanBluetoothDevices();
32
+ realScanBluetoothDevices(onDeviceFound);
40
33
  };
41
34
 
42
- const realScanBluetoothDevices = () => {
35
+ export const realScanBluetoothDevices = (onDeviceFound) => {
43
36
  if (!needToScanDevices.length) {
44
37
  return;
45
38
  }
@@ -67,9 +60,9 @@ const realScanBluetoothDevices = () => {
67
60
  const index = needToScanDevices.indexOf(name);
68
61
  needToScanDevices.splice(index, 1);
69
62
 
70
- ToastBottomHelper.success(t(`Found bluetooth ${name} for remote control`));
71
-
72
63
  bluetoothDevices[name] = device;
64
+ onDeviceFound && onDeviceFound(name, device);
65
+
73
66
  if (!needToScanDevices.length) {
74
67
  try {
75
68
  bleManager.stopDeviceScan();
@@ -1,6 +1,4 @@
1
1
  import { BleManager } from 'react-native-ble-plx';
2
- import Toast from 'react-native-toast-message';
3
- import { getTranslate } from '../../../utils/I18n';
4
2
  import '../Bluetooth';
5
3
  import {
6
4
  scanBluetoothDevices,
@@ -12,11 +10,13 @@ import {
12
10
 
13
11
  const bleManager = new BleManager();
14
12
 
13
+ const mockOnDeviceFound = jest.fn();
14
+
15
15
  describe('Test IOT Bluetooth', () => {
16
16
  beforeEach(() => {
17
+ mockOnDeviceFound.mockClear();
17
18
  bleManager.startDeviceScan.mockClear();
18
19
  bleManager.stopDeviceScan.mockClear();
19
- Toast.show.mockClear();
20
20
  clearNeedToScanDevices();
21
21
  clearFoundDevices();
22
22
  });
@@ -26,53 +26,14 @@ describe('Test IOT Bluetooth', () => {
26
26
  clearNeedToScanDevices();
27
27
  });
28
28
 
29
- it('When bluetooth is powered on, user get notification', async () => {
30
- const onStateChangeFn = bleManager.onStateChangeFn;
31
- onStateChangeFn('PoweredOn');
32
- expect(Toast.show).toBeCalledWith({
33
- type: 'success',
34
- position: 'bottom',
35
- text1: getTranslate('en', 'text_ble_is_powered_on'),
36
- visibilityTime: 1000,
37
- });
38
- });
39
-
40
- it('When bluetooth is powered on but Toast not ready, user does not get notification', async () => {
41
- const onStateChangeFn = bleManager.onStateChangeFn;
42
- Toast._ref = null;
43
-
44
- onStateChangeFn('PoweredOn');
45
- expect(Toast.show).toBeCalled();
46
-
47
- Toast._ref = true;
48
- });
49
-
50
- it('When bluetooth is powered off, user does not get notification', async () => {
51
- const onStateChangeFn = bleManager.onStateChangeFn;
52
- onStateChangeFn('PoweredOff');
53
- expect(Toast.show).not.toBeCalled();
54
- });
55
-
56
- it('Scan bluetooth device will init hardware scan', async () => {
57
- scanBluetoothDevices(['123456']);
29
+ test('Scan bluetooth device will init hardware scan', async () => {
30
+ scanBluetoothDevices(['123456'], mockOnDeviceFound);
58
31
  expect(bleManager.startDeviceScan).toBeCalled();
59
32
  });
60
33
 
61
- it('When power on, hardware will continue look for bluetooth', async () => {
62
- scanBluetoothDevices(['123456']);
63
- expect(bleManager.startDeviceScan).toBeCalledTimes(1);
64
- bleManager.onStateChangeFn('PoweredOn');
65
- expect(bleManager.startDeviceScan).toBeCalledTimes(2);
66
- });
67
-
68
- it('When power on, if not looking for bluetooth then hardware will not scan', async () => {
69
- bleManager.onStateChangeFn('PoweredOn');
70
- expect(bleManager.startDeviceScan).not.toBeCalled();
71
- });
72
-
73
- it('When look for bluetooth name, hardware will auto stop after period of time', async () => {
34
+ test('When look for bluetooth name, hardware will auto stop after period of time', async () => {
74
35
  jest.useFakeTimers();
75
- scanBluetoothDevices(['123456']);
36
+ scanBluetoothDevices(['123456'], mockOnDeviceFound);
76
37
  jest.runAllTimers();
77
38
  expect(bleManager.stopDeviceScan).toBeCalled();
78
39
  });
@@ -87,13 +48,8 @@ describe('Test IOT Bluetooth', () => {
87
48
  }
88
49
  );
89
50
 
90
- scanBluetoothDevices([device.localName]);
91
- expect(Toast.show).toBeCalledWith({
92
- type: 'success',
93
- position: 'bottom',
94
- text1: 'Found bluetooth 123456 for remote control',
95
- visibilityTime: 1000,
96
- });
51
+ scanBluetoothDevices([device.localName], mockOnDeviceFound);
52
+ expect(mockOnDeviceFound).toBeCalledWith(device.localName, device);
97
53
  });
98
54
 
99
55
  it('When search bluetooth has error then program does nothing', async () => {
@@ -107,8 +63,8 @@ describe('Test IOT Bluetooth', () => {
107
63
  }
108
64
  );
109
65
 
110
- scanBluetoothDevices([device.name]);
111
- expect(Toast.show).not.toBeCalled();
66
+ scanBluetoothDevices([device.name], mockOnDeviceFound);
67
+ expect(mockOnDeviceFound).not.toBeCalled();
112
68
  });
113
69
 
114
70
  it('When search bluetooth but found another device then program does nothing', async () => {
@@ -122,8 +78,8 @@ describe('Test IOT Bluetooth', () => {
122
78
  }
123
79
  );
124
80
 
125
- scanBluetoothDevices([device.name + 'x']);
126
- expect(Toast.show).not.toBeCalled();
81
+ scanBluetoothDevices([device.name + 'x'], mockOnDeviceFound);
82
+ expect(mockOnDeviceFound).not.toBeCalled();
127
83
  });
128
84
 
129
85
  it('When not found all bluetooth then hardware will continue scan', async () => {
@@ -135,7 +91,7 @@ describe('Test IOT Bluetooth', () => {
135
91
  listener(null, device);
136
92
  }
137
93
  );
138
- scanBluetoothDevices([device.name, device.name + 'x']);
94
+ scanBluetoothDevices([device.name, device.name + 'x'], mockOnDeviceFound);
139
95
  expect(bleManager.stopDeviceScan).not.toBeCalled();
140
96
  });
141
97
 
@@ -148,7 +104,7 @@ describe('Test IOT Bluetooth', () => {
148
104
  listener(null, device);
149
105
  }
150
106
  );
151
- scanBluetoothDevices([device.name]);
107
+ scanBluetoothDevices([device.name], mockOnDeviceFound);
152
108
  expect(bleManager.stopDeviceScan).toBeCalled();
153
109
  });
154
110
 
@@ -161,12 +117,12 @@ describe('Test IOT Bluetooth', () => {
161
117
  listener(null, device);
162
118
  }
163
119
  );
164
- scanBluetoothDevices([device.name]);
120
+ scanBluetoothDevices([device.name], mockOnDeviceFound);
165
121
  expect(bleManager.startDeviceScan).toBeCalled();
166
122
 
167
123
  bleManager.startDeviceScan.mockClear();
168
124
 
169
- scanBluetoothDevices([device.name]);
125
+ scanBluetoothDevices([device.name], mockOnDeviceFound);
170
126
  expect(bleManager.startDeviceScan).not.toBeCalled();
171
127
  });
172
128
 
@@ -203,7 +159,7 @@ describe('Test IOT Bluetooth', () => {
203
159
  listener(null, device);
204
160
  }
205
161
  );
206
- scanBluetoothDevices([device.name]);
162
+ scanBluetoothDevices([device.name], mockOnDeviceFound);
207
163
 
208
164
  await sendCommandBluetoothFail({
209
165
  remote_control_options: {
@@ -230,7 +186,7 @@ describe('Test IOT Bluetooth', () => {
230
186
  listener(null, device);
231
187
  }
232
188
  );
233
- scanBluetoothDevices([device.name]);
189
+ scanBluetoothDevices([device.name], mockOnDeviceFound);
234
190
 
235
191
  await sendCommandOverBluetooth(
236
192
  {
@@ -59,6 +59,9 @@ import { axiosGet } from '../utils/Apis/axios';
59
59
  import { API } from '../configs';
60
60
  import SideMenuDetail from '../screens/SideMenuDetail';
61
61
  import { styles } from './UnitStackStyles';
62
+ import { useBluetoothConnection } from '../hooks/IoT';
63
+ import { realScanBluetoothDevices } from '../iot/RemoteControl/Bluetooth';
64
+ import { ToastBottomHelper } from '../utils/Utils';
62
65
 
63
66
  const Stack = createStackNavigator();
64
67
 
@@ -78,9 +81,16 @@ export const UnitStack = memo((props) => {
78
81
  // eslint-disable-next-line react-hooks/exhaustive-deps
79
82
  }, []);
80
83
 
84
+ const { onDeviceFound } = useBluetoothConnection();
85
+
81
86
  useEffect(() => {
82
87
  const subscription = bleManager.onStateChange((state) => {
83
- setAction(Action.SET_BLUETOOTH_STATE, state === 'PoweredOn');
88
+ const connected = state === 'PoweredOn';
89
+ setAction(Action.SET_BLUETOOTH_STATE, connected);
90
+ if (connected) {
91
+ ToastBottomHelper.success(t('text_ble_is_powered_on'));
92
+ realScanBluetoothDevices(onDeviceFound);
93
+ }
84
94
  }, true);
85
95
 
86
96
  return () => subscription.remove();
@@ -287,6 +287,7 @@ const AddLocationMaps = memo(() => {
287
287
  rightTitle={t('done')}
288
288
  onRightClick={onDone}
289
289
  rightDisabled={!input}
290
+ accessibilityLabelPrefix={'LOCATION_'}
290
291
  />
291
292
  {loading && <FullLoading />}
292
293
  </View>
@@ -106,13 +106,13 @@ const RenameNewDevices = memo(({ route }) => {
106
106
  onChange={updateItemName('sensor', sensor_index)}
107
107
  />
108
108
  </View>
109
- {sensor.end_devices?.length &&
110
- sensor.end_devices.map((end_device, end_device_index) => (
111
- <View style={styles.device} key={`${end_device.id}`}>
109
+ {(sensor?.end_devices || []).map(
110
+ (end_device, end_device_index) => (
111
+ <View style={styles.device} key={`${end_device?.id}`}>
112
112
  <AddDeviceIcon width={43} height={43} />
113
113
  <_TextInput
114
- accessibilityLabel={`${renameLabel}-end_device-${end_device.id}`}
115
- value={end_device.name}
114
+ accessibilityLabel={`${renameLabel}-end_device-${end_device?.id}`}
115
+ value={end_device?.name}
116
116
  textInputStyle={styles.textItem}
117
117
  onChange={updateItemName(
118
118
  'end_device',
@@ -121,7 +121,8 @@ const RenameNewDevices = memo(({ route }) => {
121
121
  )}
122
122
  />
123
123
  </View>
124
- ))}
124
+ )
125
+ )}
125
126
  </>
126
127
  ))}
127
128
  </View>
@@ -22,18 +22,14 @@ const ScanGatewayQR = memo(({ route }) => {
22
22
  setLoading(false);
23
23
  return;
24
24
  }
25
- const devicePrefixName = data?.imei?.split('-')[0];
26
-
27
- // todo Bang change QR format
28
- if (['LITE'].indexOf(devicePrefixName) === -1) {
25
+ if (!data?.prefix || !data?.org_slug) {
29
26
  setIsInvalidQrCode(true);
30
27
  setLoading(false);
31
28
  return;
32
29
  }
33
30
  navigate(Routes.ConnectingWifiGuide, {
34
31
  unitId,
35
- stationId: stationId,
36
- prefix: devicePrefixName,
32
+ stationId,
37
33
  qrData: data,
38
34
  });
39
35
  setLoading(true);
@@ -65,7 +65,7 @@ describe('test scan gateway device QR', () => {
65
65
  });
66
66
 
67
67
  it('on scan correct QR code', async () => {
68
- await scanQRCode({ imei: 'LITE-xxx' }, jest.fn());
68
+ await scanQRCode({ org_slug: 'eoh', prefix: 'lite' }, jest.fn());
69
69
  expect(mockedNavigate).toHaveBeenCalled();
70
70
  expect(mockedNavigate.mock.calls[0][0]).toEqual(Routes.ConnectingWifiGuide);
71
71
  });
@@ -49,11 +49,16 @@ const ConfirmUnitDeletion = ({ route }) => {
49
49
  {t('enter_yes_to_perform')}
50
50
  </Text>
51
51
  <_TextInput
52
- placeholder={t('text_password')}
53
52
  onChange={onChangePassword}
54
53
  errorText={errorText}
55
54
  wrapStyle={styles.noMarginTop}
56
55
  textInputStyle={styles.textInput}
56
+ accessibilityLabel={
57
+ AccessibilityLabel.CONFIRM_UNIT_DELETION_CONFIRM_FIELD
58
+ }
59
+ accessibilityLabelError={
60
+ AccessibilityLabel.CONFIRM_UNIT_DELETION_CONFIRM_ERROR
61
+ }
57
62
  />
58
63
  <View style={styles.buttonWrap}>
59
64
  <Button
@@ -226,6 +226,7 @@ describe('test DeviceDetail', () => {
226
226
  await act(async () => {
227
227
  tree = await create(wrapComponent(store, account, route));
228
228
  });
229
+
229
230
  const instance = tree.root;
230
231
  const sensorDisplayItem = instance.findAll(
231
232
  (el) =>
@@ -7,59 +7,57 @@ import React, {
7
7
  useContext,
8
8
  } from 'react';
9
9
  import { View, TouchableOpacity, Platform } from 'react-native';
10
- import { useTranslations } from '../../hooks/Common/useTranslations';
11
- import moment from 'moment';
12
- import { get } from 'lodash';
10
+ import { useNavigation } from '@react-navigation/native';
13
11
  import { useSelector } from 'react-redux';
14
12
  import { IconFill, IconOutline } from '@ant-design/icons-react-native';
15
13
  import { Icon } from '@ant-design/react-native';
16
- import Routes from '../../utils/Route';
14
+ import moment from 'moment';
15
+ import { get } from 'lodash';
16
+
17
+ import { SCContext } from '../../context';
18
+ import { Action } from '../../context/actionType';
19
+ import { useSCContextSelector } from '../../context';
20
+ import { useTranslations } from '../../hooks/Common/useTranslations';
17
21
  import { useCountUp } from './hooks/useCountUp';
18
- import { getData as getLocalData } from '../../utils/Storage';
19
- import { API, Colors } from '../../configs';
20
- import { axiosGet } from '../../utils/Apis/axios';
21
- import {
22
- isDeviceConnected,
23
- scanBluetoothDevices,
24
- } from '../../iot/RemoteControl/Bluetooth';
25
- import WrapHeaderScrollable from '../../commons/Sharing/WrapHeaderScrollable';
26
- import { getActionComponent } from '../../commons/ActionGroup';
27
- import { AlertSendConfirm } from '../../commons/EmergencyButton/AlertSendConfirm';
28
- import { AlertSent } from '../../commons/EmergencyButton/AlertSent';
29
22
  import {
30
23
  useAlertResolveEmergency,
31
24
  useEmergencyButton,
32
25
  } from './hooks/useEmergencyButton';
33
26
  import { useFavoriteDevice } from './hooks/useFavoriteDevice';
34
- import BottomButtonView from '../../commons/BottomButtonView';
35
- import Text from '../../commons/Text';
36
- import { AlertAction, ButtonPopup, MenuActionMore } from '../../commons';
37
- import { DEVICE_TYPE, AccessibilityLabel } from '../../configs/Constants';
38
- import { SCContext } from '../../context';
39
- import { Action } from '../../context/actionType';
40
-
41
27
  import { usePopover } from '../../hooks/Common';
42
28
  import { useConfigGlobalState } from '../../iot/states';
43
- import { useNavigation } from '@react-navigation/native';
44
- import styles from './styles';
45
- import {
46
- useIsOwnerOfUnit,
47
- useBoolean,
48
- useHomeAssistantDeviceConnected,
49
- } from '../../hooks/Common';
29
+ import { useIsOwnerOfUnit, useBoolean } from '../../hooks/Common';
50
30
  import {
31
+ useBluetoothConnection,
51
32
  useHomeAssistantConnection,
52
33
  useValueEvaluations,
34
+ useBluetoothDeviceConnected,
35
+ useHomeAssistantDeviceConnected,
36
+ useEoHBackendDeviceConnected,
53
37
  } from '../../hooks/IoT';
54
- import { SensorDisplayItem } from './components/SensorDisplayItem';
55
- import { useSCContextSelector } from '../../context';
56
- import { EmergencyCountdown } from './components/EmergencyCountdown';
57
- import { SensorConnectStatusViewHeader } from './components/SensorConnectStatusViewHeader';
58
38
  import { useDisconnectedDevice } from './hooks/useDisconnectedDevice';
59
39
  import { useEvaluateValue } from './hooks/useEvaluateValue';
60
40
  import { useDeviceWatchConfigControl } from './hooks/useDeviceWatchConfigControl';
41
+
42
+ import WrapHeaderScrollable from '../../commons/Sharing/WrapHeaderScrollable';
43
+ import { getActionComponent } from '../../commons/ActionGroup';
44
+ import { AlertSendConfirm } from '../../commons/EmergencyButton/AlertSendConfirm';
45
+ import { AlertSent } from '../../commons/EmergencyButton/AlertSent';
46
+ import BottomButtonView from '../../commons/BottomButtonView';
47
+ import Text from '../../commons/Text';
48
+ import { AlertAction, ButtonPopup, MenuActionMore } from '../../commons';
49
+ import { SensorDisplayItem } from './components/SensorDisplayItem';
50
+ import { EmergencyCountdown } from './components/EmergencyCountdown';
51
+ import { SensorConnectStatusViewHeader } from './components/SensorConnectStatusViewHeader';
61
52
  import { Card } from '../../commons/CardShadow';
62
53
  import PreventAccess from '../../commons/PreventAccess';
54
+
55
+ import styles from './styles';
56
+ import Routes from '../../utils/Route';
57
+ import { getData as getLocalData } from '../../utils/Storage';
58
+ import { API, Colors } from '../../configs';
59
+ import { DEVICE_TYPE, AccessibilityLabel } from '../../configs/Constants';
60
+ import { axiosGet } from '../../utils/Apis/axios';
63
61
  import { notImplemented } from '../../utils/Utils';
64
62
 
65
63
  const DeviceDetail = ({ route }) => {
@@ -102,20 +100,17 @@ const DeviceDetail = ({ route }) => {
102
100
  const isNetworkConnected = useSCContextSelector(
103
101
  (state) => state.app.isNetworkConnected
104
102
  );
105
- const isBluetoothEnabled = useSCContextSelector((state) => {
106
- return state.app.isBluetoothEnabled;
107
- });
103
+
104
+ const { isConnected: isEoHBackendConnected } =
105
+ useEoHBackendDeviceConnected(sensor);
106
+
108
107
  const {
109
108
  isConnected: isHomeAssistantConnected,
110
109
  isConnecting: isHomeAssistantConnecting,
111
110
  } = useHomeAssistantDeviceConnected(sensor);
112
111
 
113
- const isDeviceConnectedViaBle = useMemo(
114
- () =>
115
- isBluetoothEnabled &&
116
- isDeviceConnected(sensor?.remote_control_options?.bluetooth?.address),
117
- [sensor, isBluetoothEnabled]
118
- );
112
+ const { isConnected: isBluetoothConnected } =
113
+ useBluetoothDeviceConnected(sensor);
119
114
 
120
115
  const isDeviceHasBle = useMemo(() => {
121
116
  const action = display.items.filter((item) => item.type === 'action');
@@ -183,6 +178,8 @@ const DeviceDetail = ({ route }) => {
183
178
 
184
179
  const { connectHomeAssistant } = useHomeAssistantConnection();
185
180
 
181
+ const { bluetoothScanDevices } = useBluetoothConnection();
182
+
186
183
  useEffect(() => {
187
184
  if (
188
185
  unit.remote_control_options &&
@@ -196,6 +193,14 @@ const DeviceDetail = ({ route }) => {
196
193
  // eslint-disable-next-line react-hooks/exhaustive-deps
197
194
  }, [unit, isNetworkConnected]);
198
195
 
196
+ useEffect(() => {
197
+ if (controlOptions?.bluetooth) {
198
+ const bluetooth = controlOptions.bluetooth;
199
+ bluetoothScanDevices([bluetooth.address]);
200
+ }
201
+ // eslint-disable-next-line react-hooks/exhaustive-deps
202
+ }, [controlOptions, unit]);
203
+
199
204
  const fetchSensorDetail = useCallback(async () => {
200
205
  const { success, data, resp_status } = await axiosGet(
201
206
  API.DEVICE.SENSOR_DETAIL(sensorId || sensor?.id)
@@ -424,16 +429,9 @@ const DeviceDetail = ({ route }) => {
424
429
 
425
430
  const { countUpStr } = useCountUp(lastEvent.reportedAt);
426
431
 
427
- useEffect(() => {
428
- if (controlOptions?.bluetooth) {
429
- const bluetooth = controlOptions.bluetooth;
430
- scanBluetoothDevices([bluetooth.address]);
431
- }
432
- }, [controlOptions, unit]);
433
-
434
432
  useEffect(() => {
435
433
  fetchDataDeviceDetail();
436
- }, [sensor, fetchDataDeviceDetail]);
434
+ }, [sensor, isNetworkConnected, fetchDataDeviceDetail]);
437
435
 
438
436
  const onRefresh = useCallback(() => {
439
437
  fetchSensorDetail();
@@ -542,6 +540,7 @@ const DeviceDetail = ({ route }) => {
542
540
  });
543
541
  };
544
542
  if (
543
+ isNetworkConnected &&
545
544
  sensor?.is_managed_by_backend &&
546
545
  sensor?.device_type !== DEVICE_TYPE.LG_THINQ
547
546
  ) {
@@ -553,7 +552,7 @@ const DeviceDetail = ({ route }) => {
553
552
  setLoading((preState) => ({ ...preState, isConnected: false }));
554
553
  }
555
554
  // eslint-disable-next-line react-hooks/exhaustive-deps
556
- }, [sensor, display]);
555
+ }, [sensor, display, isNetworkConnected]);
557
556
 
558
557
  const isShowEmergencyResolve =
559
558
  display.items.filter(
@@ -573,6 +572,7 @@ const DeviceDetail = ({ route }) => {
573
572
  params: { unitId: unit?.id, group },
574
573
  });
575
574
  }, [navigation, unit?.group, unit?.id]);
575
+
576
576
  const isHaveColorSliderTemplate = useMemo(() => {
577
577
  let isFlag = false;
578
578
  display?.items.some((item) => {
@@ -592,14 +592,8 @@ const DeviceDetail = ({ route }) => {
592
592
  return (
593
593
  <SensorConnectStatusViewHeader
594
594
  sensor={sensor}
595
- connectedViaNetwork={
596
- isNetworkConnected && displayValuesData.isConnected
597
- }
598
- connectedViaBle={
599
- (!isNetworkConnected ||
600
- (isNetworkConnected && !displayValuesData.isConnected)) &&
601
- isDeviceConnectedViaBle
602
- }
595
+ connectedViaNetwork={isEoHBackendConnected}
596
+ connectedViaBle={isBluetoothConnected}
603
597
  connectedViaHomeAssistant={isHomeAssistantConnected}
604
598
  isHomeAssistantConnecting={isHomeAssistantConnecting}
605
599
  lastUpdated={displayValuesData.lastUpdated}
@@ -164,7 +164,10 @@ const AddSubUnit = ({ route }) => {
164
164
  }, [navigate]);
165
165
 
166
166
  return (
167
- <SafeAreaView style={styles.wrap}>
167
+ <SafeAreaView
168
+ style={styles.wrap}
169
+ accessibilityLabel={AccessibilityLabel.ADD_SUB_UNIT_SCREEN}
170
+ >
168
171
  <TouchableWithoutFeedback onPress={Keyboard.dismiss} accessible={false}>
169
172
  <View style={styles.commonWrap}>
170
173
  <View style={styles.commonWrap}>
@@ -186,6 +189,7 @@ const AddSubUnit = ({ route }) => {
186
189
  selectionColor={Colors.Primary}
187
190
  value={roomName}
188
191
  maxLength={50}
192
+ accessibilityLabel={AccessibilityLabel.ADD_SUB_UNIT_NAME_FIELD}
189
193
  />
190
194
 
191
195
  {isAddUnit && (
@@ -28,19 +28,19 @@ const MoreMenu = memo(
28
28
 
29
29
  const listMenuItem = useMemo(() => {
30
30
  const RouteManageUnit = {
31
- id: 1,
31
+ id: 'mange-unit',
32
32
  route: Routes.ManageUnit,
33
33
  text: t('manage_unit'),
34
34
  data: { unitId: unit.id, unit },
35
35
  };
36
36
  const RouteUnitMemberList = {
37
- id: 2,
37
+ id: 'unit-member',
38
38
  route: Routes.UnitMemberList,
39
39
  text: t('members'),
40
40
  data: { unitId: unit.id, unit },
41
41
  };
42
42
  const ListSmartAccount = {
43
- id: 3,
43
+ id: 'smart-account',
44
44
  route: Routes.ListSmartAccount,
45
45
  text: t('smart_account'),
46
46
  data: { unitId: unit.id, unit },
@@ -257,6 +257,7 @@ const SelectAddress = memo(({ route }) => {
257
257
  onPressSecondary={goBack}
258
258
  typeMain="primaryText"
259
259
  typeSecondary="primaryText"
260
+ accessibilityLabelPrefix="LOCATION"
260
261
  />
261
262
  {loading && <FullLoading />}
262
263
  </View>
@@ -1,7 +1,7 @@
1
1
  import React, { useState, useCallback } from 'react';
2
2
  import { StyleSheet, View, TouchableOpacity } from 'react-native';
3
3
  import { useNavigation } from '@react-navigation/native';
4
- import { useHomeAssistantDeviceConnected } from '../../../../hooks/Common';
4
+ import { useHomeAssistantDeviceConnected } from '../../../../hooks/IoT';
5
5
 
6
6
  import ItemQuickAction from '../../../../commons/Action/ItemQuickAction';
7
7
  import Text from '../../../../commons/Text';
@@ -1,8 +1,8 @@
1
1
  import { useEffect, useCallback, useContext } from 'react';
2
2
  import { SCContext } from '../../../context';
3
3
  import { Action } from '../../../context/actionType';
4
- import { scanBluetoothDevices } from '../../../iot/RemoteControl/Bluetooth';
5
4
  import { lgThinqConnect } from '../../../iot/RemoteControl/LG';
5
+ import { useBluetoothConnection } from '../../../hooks/IoT';
6
6
  import { useHomeAssistantConnection } from '../../../hooks/IoT';
7
7
  import { useSCContextSelector } from '../../../context';
8
8
 
@@ -10,6 +10,9 @@ export const useUnitConnectRemoteDevices = (unit) => {
10
10
  const isNetworkConnected = useSCContextSelector(
11
11
  (state) => state.app.isNetworkConnected
12
12
  );
13
+ const isBluetoothEnabled = useSCContextSelector((state) => {
14
+ return state.app.isBluetoothEnabled;
15
+ });
13
16
  const lqthinqUnitConnected = useSCContextSelector(
14
17
  (state) => state.iot.lgthinq.unitConnected
15
18
  );
@@ -17,6 +20,8 @@ export const useUnitConnectRemoteDevices = (unit) => {
17
20
 
18
21
  const { connectHomeAssistant } = useHomeAssistantConnection();
19
22
 
23
+ const { bluetoothScanDevices } = useBluetoothConnection();
24
+
20
25
  let lgThinqConnected = false;
21
26
 
22
27
  const onLgThinqConnected = useCallback(() => {
@@ -37,16 +42,14 @@ export const useUnitConnectRemoteDevices = (unit) => {
37
42
  );
38
43
 
39
44
  useEffect(() => {
40
- if (unit?.remote_control_options?.bluetooth) {
41
- scanBluetoothDevices(unit.remote_control_options.bluetooth);
45
+ if (unit?.remote_control_options?.bluetooth && isBluetoothEnabled) {
46
+ bluetoothScanDevices(unit.remote_control_options.bluetooth);
42
47
  }
43
- }, [unit]);
48
+ // eslint-disable-next-line react-hooks/exhaustive-deps
49
+ }, [unit, isBluetoothEnabled]);
44
50
 
45
51
  useEffect(() => {
46
- if (
47
- unit?.remote_control_options?.googlehome?.length &&
48
- isNetworkConnected
49
- ) {
52
+ if (unit?.remote_control_options?.googlehome && isNetworkConnected) {
50
53
  (async () => {
51
54
  await connectHomeAssistant(unit.remote_control_options.googlehome);
52
55
  })();
@@ -30,7 +30,7 @@ jest.mock('@react-navigation/native', () => {
30
30
  useNavigation: () => ({
31
31
  navigate: mockedNavigate,
32
32
  }),
33
- useFocusEffect: jest.fn(),
33
+ useIsFocused: () => true,
34
34
  };
35
35
  });
36
36
 
@@ -19,7 +19,7 @@ jest.mock('@react-navigation/native', () => {
19
19
  useNavigation: () => ({
20
20
  navigate: mockedNavigate,
21
21
  }),
22
- useFocusEffect: jest.fn(),
22
+ useIsFocused: () => true,
23
23
  };
24
24
  });
25
25
 
@@ -1,27 +1,13 @@
1
- import React, { memo, useEffect } from 'react';
1
+ import React, { memo } from 'react';
2
2
  import { StyleSheet, View } from 'react-native';
3
3
  import ItemDevice from '../../../../commons/Device/ItemDevice';
4
- import { scanBluetoothDevices } from '../../../../iot/RemoteControl/Bluetooth';
5
- import { useHomeAssistantConnection } from '../../../../hooks/IoT';
6
4
  import { useDevicesStatus } from '../../../../hooks/Common';
5
+ import { useUnitConnectRemoteDevices } from '../../../Unit/hook/useUnitConnectRemoteDevices';
7
6
 
8
7
  const RunningDevices = memo(({ unit, summaryDetail }) => {
9
8
  const { devices } = summaryDetail;
10
9
 
11
- const { connectHomeAssistant } = useHomeAssistantConnection();
12
-
13
- useEffect(() => {
14
- if (unit.remote_control_options) {
15
- if (unit.remote_control_options.bluetooth) {
16
- scanBluetoothDevices(unit.remote_control_options.bluetooth);
17
- }
18
- if (unit.remote_control_options.googlehome) {
19
- (async () =>
20
- await connectHomeAssistant(unit.remote_control_options.googlehome))();
21
- }
22
- }
23
- // eslint-disable-next-line react-hooks/exhaustive-deps
24
- }, [unit]);
10
+ useUnitConnectRemoteDevices(unit);
25
11
 
26
12
  useDevicesStatus(unit, devices);
27
13