@eohjsc/react-native-smart-city 0.3.20 → 0.3.23

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 (94) hide show
  1. package/index.js +2 -0
  2. package/package.json +2 -1
  3. package/src/Images/Common/buttonLeftCurtain.png +0 -0
  4. package/src/Images/Common/buttonLeftCurtain@2x.png +0 -0
  5. package/src/Images/Common/buttonLeftCurtain@3x.png +0 -0
  6. package/src/commons/Action/ItemQuickAction.js +1 -12
  7. package/src/commons/Action/__test__/ItemQuickAction.test.js +1 -1
  8. package/src/commons/ActionGroup/ColorPickerTemplate.js +2 -9
  9. package/src/commons/ActionGroup/CurtainButtonTemplate.js +5 -4
  10. package/src/commons/ActionGroup/CurtainButtonTemplateStyle.js +5 -0
  11. package/src/commons/ActionGroup/NumberUpDownActionTemplate.js +6 -16
  12. package/src/commons/ActionGroup/OnOffSmartLock/OnOffSmartLock.js +7 -4
  13. package/src/commons/ActionGroup/OnOffTemplate/index.js +6 -12
  14. package/src/commons/ActionGroup/OptionsDropdownActionTemplate.js +5 -2
  15. package/src/commons/ActionGroup/SliderRangeTemplate.js +4 -9
  16. package/src/commons/ActionGroup/StatesGridActionTemplate.js +7 -10
  17. package/src/commons/ActionGroup/TimerActionTemplate.js +11 -4
  18. package/src/commons/ActionGroup/TwoButtonTemplate/index.js +12 -15
  19. package/src/commons/ActionGroup/__test__/NumberUpDownTemplate.test.js +37 -2
  20. package/src/commons/ActionGroup/__test__/OnOffButtonTemplate.test.js +7 -0
  21. package/src/commons/ActionGroup/__test__/OnOffSmartLock.test.js +15 -2
  22. package/src/commons/ActionGroup/__test__/OnOffTemplate.test.js +8 -1
  23. package/src/commons/ActionGroup/__test__/OptionsDropdownTemplate.test.js +8 -1
  24. package/src/commons/ActionGroup/__test__/StatesGridActionTemplate.test.js +7 -0
  25. package/src/commons/ActionGroup/__test__/TimerActionTemplate.test.js +8 -1
  26. package/src/commons/ActionGroup/__test__/TimerActionTemplateWithutConfigValue.test.js +7 -0
  27. package/src/commons/ActionGroup/__test__/TwoButtonTemplate.test.js +7 -0
  28. package/src/commons/ActionGroup/__test__/index.test.js +8 -1
  29. package/src/commons/Device/ItemDevice.js +79 -35
  30. package/src/commons/MediaPlayerDetail/index.js +5 -0
  31. package/src/commons/RowItem/index.js +6 -2
  32. package/src/commons/RowItem/styles.js +1 -0
  33. package/src/commons/SubUnit/Favorites/index.js +24 -6
  34. package/src/commons/SubUnit/ShortDetail.js +19 -5
  35. package/src/commons/SubUnit/__test__/Favorites.test.js +1 -0
  36. package/src/commons/SubUnit/__test__/ShortDetail.test.js +1 -0
  37. package/src/configs/API.js +8 -3
  38. package/src/configs/Constants.js +8 -2
  39. package/src/configs/SCConfig.js +4 -0
  40. package/src/context/actionType.ts +6 -5
  41. package/src/context/mockStore.ts +10 -3
  42. package/src/context/reducer.ts +20 -15
  43. package/src/hoc/index.js +3 -0
  44. package/src/hoc/withRemoteControl.js +10 -0
  45. package/src/hooks/Common/index.js +2 -2
  46. package/src/hooks/Common/useDevicesStatus.js +57 -0
  47. package/src/hooks/Common/useGGHomeDeviceConnected.js +3 -3
  48. package/src/hooks/IoT/__test__/useGGHomeConnection.test.js +1 -2
  49. package/src/hooks/IoT/index.js +9 -1
  50. package/src/hooks/IoT/useGGHomeConnection.js +0 -1
  51. package/src/hooks/IoT/useUnwatchLGDeviceConfigControl.js +29 -0
  52. package/src/hooks/IoT/useValueEvaluation.js +17 -4
  53. package/src/hooks/IoT/useWatchConfigs.js +34 -0
  54. package/src/iot/Monitor.js +13 -20
  55. package/src/iot/RemoteControl/GoogleHome.js +12 -13
  56. package/src/iot/RemoteControl/LG.js +1 -0
  57. package/src/iot/RemoteControl/__test__/GoogleHome.test.js +7 -2
  58. package/src/navigations/UnitStack.js +16 -3
  59. package/src/screens/AddNewAction/SelectAction.js +1 -1
  60. package/src/screens/AddNewAction/SetupSensor.js +4 -0
  61. package/src/screens/AddNewGateway/PlugAndPlay/ConnectWifiWarning.js +44 -78
  62. package/src/screens/AddNewGateway/PlugAndPlay/GatewayWifiList.js +15 -35
  63. package/src/screens/AddNewGateway/PlugAndPlay/__test__/GatewayWifiList.test.js +2 -0
  64. package/src/screens/AllCamera/__test__/index.test.js +1 -1
  65. package/src/screens/Device/__test__/detail.test.js +1 -54
  66. package/src/screens/Device/components/SensorConnectStatusViewHeader.js +18 -8
  67. package/src/screens/Device/detail.js +36 -23
  68. package/src/screens/Device/hooks/__test__/useEvaluateValue.test.js +102 -0
  69. package/src/screens/Device/hooks/useDeviceWatchConfigControl.js +20 -0
  70. package/src/screens/Device/utils/index.js +45 -0
  71. package/src/screens/Device/utils/index.test.js +111 -0
  72. package/src/screens/EmergencyContacts/EmergencyContactsAddNew.js +35 -22
  73. package/src/screens/EmergencyContacts/EmergencyContactsSelectContacts.js +2 -1
  74. package/src/screens/EmergencyContacts/__test__/EmergencyContactAddNew.test.js +36 -2
  75. package/src/screens/MoveToAnotherSubUnit/__test__/index.test.js +0 -2
  76. package/src/screens/Notification/__test__/NotificationItem.test.js +84 -19
  77. package/src/screens/Notification/components/NotificationItem.js +64 -31
  78. package/src/screens/ScriptDetail/hooks/useStarredScript.js +2 -2
  79. package/src/screens/SubUnit/AddSubUnit.js +2 -1
  80. package/src/screens/Unit/AddMenu.js +4 -0
  81. package/src/screens/Unit/{SelectFavoritesDevices.js → SelectAddToFavorites.js} +81 -26
  82. package/src/screens/Unit/{SelectFavoritesDevicesStyles.js → SelectAddToFavoritesStyles.js} +0 -0
  83. package/src/screens/Unit/__test__/CheckSendEmail.test.js +12 -0
  84. package/src/screens/Unit/__test__/Detail.test.js +2 -3
  85. package/src/screens/Unit/__test__/SelectAddToFavorites.test.js +267 -0
  86. package/src/screens/Unit/components/AutomateScript/index.js +65 -0
  87. package/src/screens/Unit/components/AutomateScript/styles.js +48 -0
  88. package/src/screens/Unit/components/MyUnitDevice/index.js +3 -2
  89. package/src/screens/UnitSummary/components/3PPowerConsumption/index.js +4 -2
  90. package/src/utils/I18n/translations/en.json +7 -2
  91. package/src/utils/I18n/translations/vi.json +6 -1
  92. package/src/utils/Route/index.js +1 -1
  93. package/src/hooks/Common/useSensorsStatus.js +0 -62
  94. package/src/screens/Unit/__test__/SelectFavoritesDevices.test.js +0 -110
@@ -28,14 +28,9 @@ import { Action } from '../../../context/actionType';
28
28
  import { TESTID } from '../../../configs/Constants';
29
29
  import Connecting from '../../../commons/Connecting';
30
30
 
31
- const isIos = Platform.OS === 'ios';
32
31
  const isAndroid = Platform.OS === 'android';
33
-
34
- let socket = null;
35
- if (isIos) {
36
- socket = dgram.createSocket({ type: 'udp4' });
37
- socket.bind(54321);
38
- }
32
+ let socket,
33
+ intervalSend = null;
39
34
 
40
35
  const ConnectWifiWarning = memo(({ route }) => {
41
36
  const {
@@ -83,77 +78,44 @@ const ConnectWifiWarning = memo(({ route }) => {
83
78
  }, [getPermissionWifiAndroid]);
84
79
 
85
80
  const handleSend = async () => {
86
- let intervalSend = null;
87
- if (isIos) {
88
- await setIsPercentConnect(1);
89
- await socket.on('message', (msg, rinfo) => {
90
- clearInterval(intervalSend);
91
- const data = JSON.parse(msg.toString());
92
- if (Object.prototype.hasOwnProperty.call(data, 'wifi')) {
93
- navigate(Routes.GatewayWifiList, {
94
- list_wifi: data.wifi,
95
- unit_id: unit_id,
96
- chip_id: chip_id,
97
- scan_sensor_data: { ...body },
98
- wifi_ssid: wifi_ssid,
99
- wifi_pass: wifi_pass,
100
- unit_name: unit_name,
101
- devicePrefixName: devicePrefixName,
102
- socket: socket,
103
- });
104
- }
105
- });
106
- intervalSend = setInterval(() => {
107
- socket.send(
108
- JSON.stringify({ type: 'scan', data: { wifi: '' } }),
109
- undefined,
110
- undefined,
111
- 54321,
112
- '192.168.27.1',
113
- undefined
114
- );
115
- }, 1000);
116
- socket.on('error', () => {
117
- ToastBottomHelper.error(t('server_error'));
118
- setAction(Action.IS_CONNECT_WIFI_GATEWAY, false);
119
- goBack();
120
- });
121
- } else {
122
- const dgSocket = dgram.createSocket({ type: 'udp4' });
123
- await setIsPercentConnect(1);
124
- await dgSocket.bind(54321);
125
- await dgSocket.on('message', (msg, rinfo) => {
126
- const data = JSON.parse(msg.toString());
127
- if (Object.prototype.hasOwnProperty.call(data, 'wifi')) {
128
- navigate(Routes.GatewayWifiList, {
129
- list_wifi: data.wifi,
130
- unit_id: unit_id,
131
- chip_id: chip_id,
132
- scan_sensor_data: { ...body },
133
- wifi_ssid: wifi_ssid,
134
- wifi_pass: wifi_pass,
135
- unit_name: unit_name,
136
- devicePrefixName: devicePrefixName,
137
- socket: dgSocket,
138
- });
139
- }
140
- });
141
- await dgSocket.once('listening', async () => {
142
- await dgSocket.send(
143
- JSON.stringify({ type: 'scan', data: { wifi: '' } }),
144
- undefined,
145
- undefined,
146
- 54321,
147
- '192.168.27.1',
148
- undefined
149
- );
150
- });
151
- dgSocket.on('error', () => {
152
- ToastBottomHelper.error(t('server_error'));
153
- setAction(Action.IS_CONNECT_WIFI_GATEWAY, false);
154
- goBack();
155
- });
156
- }
81
+ socket = dgram.createSocket({ type: 'udp4' });
82
+ socket.bind(54321);
83
+ setIsPercentConnect(1);
84
+
85
+ socket.on('message', (msg, rinfo) => {
86
+ clearInterval(intervalSend);
87
+ const data = JSON.parse(msg.toString());
88
+ if (Object.prototype.hasOwnProperty.call(data, 'wifi')) {
89
+ navigate(Routes.GatewayWifiList, {
90
+ list_wifi: data.wifi,
91
+ unit_id: unit_id,
92
+ chip_id: chip_id,
93
+ scan_sensor_data: { ...body },
94
+ wifi_ssid: wifi_ssid,
95
+ wifi_pass: wifi_pass,
96
+ unit_name: unit_name,
97
+ devicePrefixName: devicePrefixName,
98
+ socket: socket,
99
+ });
100
+ }
101
+ });
102
+
103
+ socket.on('error', () => {
104
+ ToastBottomHelper.error(t('server_error'));
105
+ setAction(Action.IS_CONNECT_WIFI_GATEWAY, false);
106
+ goBack();
107
+ });
108
+
109
+ intervalSend = setInterval(() => {
110
+ socket.send(
111
+ JSON.stringify({ type: 'scan', data: { wifi: '' } }),
112
+ undefined,
113
+ undefined,
114
+ 54321,
115
+ '192.168.27.1',
116
+ undefined
117
+ );
118
+ }, 5000); // workaround, todo Bang continue research
157
119
  };
158
120
 
159
121
  const handleConnectWifiGateway = async () => {
@@ -171,6 +133,10 @@ const ConnectWifiWarning = memo(({ route }) => {
171
133
  );
172
134
  };
173
135
 
136
+ useEffect(() => {
137
+ return () => intervalSend && clearInterval(intervalSend);
138
+ }, []);
139
+
174
140
  return (
175
141
  <View style={styles.screen}>
176
142
  {!isLoading ? (
@@ -11,7 +11,6 @@ import {
11
11
  TouchableOpacity,
12
12
  ScrollView,
13
13
  ActivityIndicator,
14
- Platform,
15
14
  } from 'react-native';
16
15
  import { HeaderCustom } from '../../../commons/Header';
17
16
  import { Colors } from '../../../configs';
@@ -128,11 +127,8 @@ const GatewayWifiList = memo(({ route }) => {
128
127
 
129
128
  const sendConnect = useCallback(
130
129
  (i) => {
131
- setTimeout(async () => {
130
+ const timeoutSendConnect = setTimeout(async () => {
132
131
  i++;
133
- await socket.on('message', (msg, rinfo) => {
134
- handleSocketOnMsg(msg);
135
- });
136
132
  socket.send(
137
133
  JSON.stringify({
138
134
  type: 'connect',
@@ -146,48 +142,32 @@ const GatewayWifiList = memo(({ route }) => {
146
142
  );
147
143
  if (i < 3) {
148
144
  sendConnect(i);
145
+ clearTimeout(timeoutSendConnect);
149
146
  }
150
147
  }, 1000);
151
148
  },
152
- [handleSocketOnMsg, password, selectedWifi, socket]
149
+ [password, selectedWifi, socket]
153
150
  );
154
151
 
155
152
  const connectWifi = useCallback(async () => {
156
153
  if (!isConnectWifiGateway) {
157
- if (Platform.OS === 'ios') {
158
- sendConnect(0);
159
- setAction(Action.IS_CONNECT_WIFI_GATEWAY, true);
160
- setIsSendWifi(true);
161
- } else {
162
- socket.on('message', (msg, rinfo) => {
163
- handleSocketOnMsg(msg);
164
- });
165
- socket.send(
166
- JSON.stringify({
167
- type: 'connect',
168
- data: { wifi: { ssid: selectedWifi, pass: password } },
169
- }),
170
- undefined,
171
- undefined,
172
- 54321,
173
- '192.168.27.1',
174
- undefined
175
- );
176
- socket.on('error', () => {
177
- ToastBottomHelper.error(t('server_error'));
178
- setAction(Action.IS_CONNECT_WIFI_GATEWAY, false);
179
- goBack();
180
- });
181
- setAction(Action.IS_CONNECT_WIFI_GATEWAY, true);
182
- setIsSendWifi(true);
183
- }
154
+ socket.on('message', (msg, rinfo) => {
155
+ handleSocketOnMsg(msg);
156
+ });
157
+ socket.on('error', () => {
158
+ ToastBottomHelper.error(t('server_error'));
159
+ setAction(Action.IS_CONNECT_WIFI_GATEWAY, false);
160
+ goBack();
161
+ });
162
+
163
+ sendConnect(0);
164
+ setAction(Action.IS_CONNECT_WIFI_GATEWAY, true);
165
+ setIsSendWifi(true);
184
166
  }
185
167
  }, [
186
168
  goBack,
187
169
  handleSocketOnMsg,
188
170
  isConnectWifiGateway,
189
- password,
190
- selectedWifi,
191
171
  sendConnect,
192
172
  setAction,
193
173
  socket,
@@ -97,6 +97,8 @@ describe('Test GatewayWifiList', () => {
97
97
  await ButtonPopup.props.onPressMain();
98
98
  });
99
99
  expect(socket.on).toBeCalled();
100
+ expect(socket.on.mock.calls[0][0]).toEqual('message');
101
+ jest.runOnlyPendingTimers();
100
102
  expect(socket.send).toBeCalled();
101
103
 
102
104
  const mockWifiThen = jest.fn();
@@ -90,7 +90,7 @@ describe('Test AllCamera screen', () => {
90
90
  });
91
91
  const instance = tree.root;
92
92
  const TouchableOpacities = instance.findAllByType(TouchableOpacity);
93
- expect(TouchableOpacities).toHaveLength(11);
93
+ expect(TouchableOpacities).toHaveLength(9);
94
94
  const ModalFullVideos = instance.findAllByType(ModalFullVideo);
95
95
  expect(ModalFullVideos).toHaveLength(1);
96
96
  act(() => {
@@ -12,7 +12,6 @@ import { TESTID } from '../../../configs/Constants';
12
12
  import Text from '../../../commons/Text';
13
13
  import { IconFill } from '@ant-design/icons-react-native';
14
14
  import CurrentRainSensor from '../../../commons/Device/RainningSensor/CurrentRainSensor';
15
- import { ConnectedViewHeader } from '../../../commons/Device';
16
15
  import { getTranslate } from '../../../utils/I18n';
17
16
  import { SCProvider } from '../../../context';
18
17
  import { mockSCStore } from '../../../context/mockStore';
@@ -30,6 +29,7 @@ jest.mock('@react-navigation/native', () => {
30
29
  useNavigation: () => ({
31
30
  navigate: mockedNavigate,
32
31
  }),
32
+ useFocusEffect: jest.fn(),
33
33
  };
34
34
  });
35
35
 
@@ -609,59 +609,6 @@ describe('test DeviceDetail', () => {
609
609
  expect(sensorDisplayItem).toHaveLength(0);
610
610
  });
611
611
 
612
- test('render CurrentRainSensor but is other device', async () => {
613
- route.params.sensorData.is_other_device = true;
614
- route.params.isGGHomeConnected = true;
615
-
616
- const responseDisplay = {
617
- status: 200,
618
- data: {
619
- items: [
620
- {
621
- id: 170,
622
- order: 1,
623
- template: 'value',
624
- type: 'value',
625
- configuration: {
626
- type: 'circle',
627
- configs: [
628
- {
629
- id: 428,
630
- color: 'red',
631
- standard: 'Temperature',
632
- measure: '°C',
633
- },
634
- ],
635
- },
636
- },
637
- ],
638
- },
639
- };
640
-
641
- const responseDisplayValueV2 = {
642
- status: 200,
643
- data: {
644
- configs: [{ id: 428 }],
645
- is_connected: true,
646
- last_updated: '2021-01-24T12:00:00.000Z',
647
- },
648
- };
649
-
650
- mockAxios(responseDisplay, responseDisplayValueV2);
651
-
652
- await act(async () => {
653
- tree = await create(wrapComponent(store, account, route));
654
- });
655
-
656
- const instance = tree.root;
657
- const sensorDisplayItem = instance.findAll(
658
- (el) => el.props.testID === TESTID.SENSOR_DISPLAY_ITEM
659
- );
660
- expect(sensorDisplayItem).toHaveLength(1);
661
- const connectedViewHeader = instance.findByType(ConnectedViewHeader);
662
- expect(connectedViewHeader.props.type).toEqual('GoogleHome');
663
- });
664
-
665
612
  test('HeaderDevice button more onClick', async () => {
666
613
  await act(async () => {
667
614
  tree = await create(wrapComponent(store, account, route));
@@ -14,18 +14,25 @@ export const SensorConnectStatusViewHeader = ({
14
14
  showWindDirection,
15
15
  children,
16
16
  }) => {
17
- const isConnecting =
18
- !!sensor &&
19
- sensor?.is_other_device &&
20
- sensor?.device_type !== DEVICE_TYPE.LG_THINQ
21
- ? isGGHomeConnecting
22
- : false;
17
+ const isConnecting = (() => {
18
+ if (!!sensor && sensor?.is_managed_by_backend) {
19
+ return false;
20
+ } else {
21
+ if (sensor?.device_type === DEVICE_TYPE.LG_THINQ) {
22
+ return false;
23
+ } else if (sensor?.device_type === DEVICE_TYPE.GOOGLE_HOME) {
24
+ return isGGHomeConnecting;
25
+ } else {
26
+ return false;
27
+ }
28
+ }
29
+ })();
23
30
 
24
31
  if (isConnecting) {
25
32
  return <></>;
26
33
  }
27
34
 
28
- if (!!sensor && !sensor?.is_other_device) {
35
+ if (!!sensor && sensor?.is_managed_by_backend) {
29
36
  if (connectedViaNetwork) {
30
37
  return (
31
38
  <>
@@ -55,6 +62,7 @@ export const SensorConnectStatusViewHeader = ({
55
62
  );
56
63
  }
57
64
  } else {
65
+ // not managed by backend
58
66
  if (sensor?.device_type === DEVICE_TYPE.LG_THINQ) {
59
67
  return (
60
68
  <>
@@ -62,7 +70,7 @@ export const SensorConnectStatusViewHeader = ({
62
70
  {children}
63
71
  </>
64
72
  );
65
- } else {
73
+ } else if (sensor?.device_type === DEVICE_TYPE.GOOGLE_HOME) {
66
74
  if (connectedViaGGHome) {
67
75
  return (
68
76
  <>
@@ -83,6 +91,8 @@ export const SensorConnectStatusViewHeader = ({
83
91
  />
84
92
  );
85
93
  }
94
+ } else {
95
+ return <DisconnectedView sensor={sensor} />;
86
96
  }
87
97
  }
88
98
  };
@@ -33,7 +33,7 @@ import { useFavoriteDevice } from './hooks/useFavoriteDevice';
33
33
  import BottomButtonView from '../../commons/BottomButtonView';
34
34
  import Text from '../../commons/Text';
35
35
  import { AlertAction, ButtonPopup, MenuActionMore } from '../../commons';
36
- import { TESTID } from '../../configs/Constants';
36
+ import { DEVICE_TYPE, TESTID } from '../../configs/Constants';
37
37
 
38
38
  import { usePopover } from '../../hooks/Common';
39
39
  import { useConfigGlobalState } from '../../iot/states';
@@ -51,6 +51,7 @@ import { EmergencyCountdown } from './components/EmergencyCountdown';
51
51
  import { SensorConnectStatusViewHeader } from './components/SensorConnectStatusViewHeader';
52
52
  import { useDisconnectedDevice } from './hooks/useDisconnectedDevice';
53
53
  import { useEvaluateValue } from './hooks/useEvaluateValue';
54
+ import { useDeviceWatchConfigControl } from './hooks/useDeviceWatchConfigControl';
54
55
  import { Card } from '../../commons/CardShadow';
55
56
  import PreventAccess from '../../commons/PreventAccess';
56
57
  import { notImplemented } from '../../utils/Utils';
@@ -122,6 +123,8 @@ const DeviceDetail = ({ route }) => {
122
123
 
123
124
  useDisconnectedDevice(sensorName, isDeviceHasBle, serverDown);
124
125
 
126
+ useDeviceWatchConfigControl(sensor, display);
127
+
125
128
  const isShowSetupEmergencyContact = useMemo(
126
129
  () =>
127
130
  display.items.filter(
@@ -148,7 +151,7 @@ const DeviceDetail = ({ route }) => {
148
151
  );
149
152
 
150
153
  const canManageSubUnit = useMemo(() => {
151
- return currentUserId === unit?.user_id;
154
+ return Number(currentUserId) === Number(unit?.user_id);
152
155
  }, [currentUserId, unit]);
153
156
 
154
157
  const fetchUnitDetail = useCallback(async () => {
@@ -448,20 +451,24 @@ const DeviceDetail = ({ route }) => {
448
451
  if (!item.configuration) {
449
452
  return;
450
453
  }
454
+
451
455
  const data = item.configuration.configs.map((config) => {
452
- const configValue = configValues[config.id];
456
+ const configValue = configValues[config.id]?.value;
453
457
  const displayValue = displayValues.find((k) => k.id === config.id);
454
- if (!configValue && !displayValue) {
458
+ if (
459
+ (configValue === null || configValue === undefined) &&
460
+ !displayValue
461
+ ) {
455
462
  return;
456
463
  }
457
- const value = configValue
458
- ? {
459
- id: config.id,
460
- value: configValue,
461
- evaluate: evaluateValue(config.id, configValue),
462
- }
463
- : displayValue;
464
-
464
+ const value =
465
+ configValue !== null && configValue !== undefined
466
+ ? {
467
+ id: config.id,
468
+ value: configValue,
469
+ evaluate: evaluateValue(config.id, configValue),
470
+ }
471
+ : displayValue;
465
472
  return { ...config, ...value };
466
473
  });
467
474
  return data.filter((value) => value);
@@ -513,7 +520,10 @@ const DeviceDetail = ({ route }) => {
513
520
  }
514
521
  setLoading((preState) => ({ ...preState, isConnected: false }));
515
522
  };
516
- if (sensor?.is_managed_by_backend && !sensor?.is_other_device) {
523
+ if (
524
+ sensor?.is_managed_by_backend &&
525
+ sensor?.device_type !== DEVICE_TYPE.LG_THINQ
526
+ ) {
517
527
  const updateInterval = setInterval(() => fetchValues(), 5000);
518
528
  fetchValues();
519
529
  return () => clearInterval(updateInterval);
@@ -721,16 +731,19 @@ const DeviceDetail = ({ route }) => {
721
731
  isNetworkConnected !== null &&
722
732
  renderSensorConnected()}
723
733
  </View>
724
- {isShowSetupEmergencyContact && canManageSubUnit && (
725
- <BottomButtonView
726
- style={styles.bottomButtonEmergencyContact}
727
- mainIcon={<Icon name="plus" size={16} color={Colors.Primary} />}
728
- mainTitle={t('setup_my_emergency_contact')}
729
- onPressMain={onSetupContacts}
730
- typeMain="primaryBorder"
731
- semiboldMain={false}
732
- />
733
- )}
734
+ {isNetworkConnected &&
735
+ isConnected &&
736
+ isShowSetupEmergencyContact &&
737
+ canManageSubUnit && (
738
+ <BottomButtonView
739
+ style={styles.bottomButtonEmergencyContact}
740
+ mainIcon={<Icon name="plus" size={16} color={Colors.Primary} />}
741
+ mainTitle={t('setup_my_emergency_contact')}
742
+ onPressMain={onSetupContacts}
743
+ typeMain="primaryBorder"
744
+ semiboldMain={false}
745
+ />
746
+ )}
734
747
  <AlertSendConfirm
735
748
  showAlertConfirm={showAlertConfirm && !lockShowing}
736
749
  countDown={countDown}
@@ -0,0 +1,102 @@
1
+ import React from 'react';
2
+ import { renderHook } from '@testing-library/react-hooks';
3
+ import { useEvaluateValue } from '../useEvaluateValue';
4
+ import { SCProvider } from '../../../../context';
5
+ import { mockSCStore } from '../../../../context/mockStore';
6
+
7
+ const wrapper =
8
+ (valueEvaluations) =>
9
+ ({ children }) =>
10
+ (
11
+ <SCProvider initState={mockSCStore({ valueEvaluations })}>
12
+ {children}
13
+ </SCProvider>
14
+ );
15
+
16
+ const mockUseContext = jest.fn().mockImplementation(() => ({
17
+ stateData: mockSCStore({}),
18
+ setAction: jest.fn(),
19
+ }));
20
+
21
+ React.useContext = mockUseContext;
22
+
23
+ describe('Test useEvaluateValue', () => {
24
+ let valueEvaluations = {};
25
+
26
+ beforeEach(() => {
27
+ valueEvaluations = {
28
+ [1]: {
29
+ template: 'range',
30
+ configuration: {
31
+ ranges: [
32
+ { start: 0.5, end: 1.5, evaluate: { text: 'On' } },
33
+ { start: -0.5, end: 0.49, evaluate: { text: 'Off' } },
34
+ ],
35
+ },
36
+ },
37
+ [2]: {
38
+ template: 'boolean',
39
+ configuration: {
40
+ on: {
41
+ value: 1,
42
+ evaluate: {
43
+ text: 'On',
44
+ },
45
+ },
46
+ off: {
47
+ value: 0,
48
+ evaluate: {
49
+ text: 'Off',
50
+ },
51
+ },
52
+ },
53
+ },
54
+ };
55
+ });
56
+
57
+ it('test value null or undefined', async () => {
58
+ const { result: evaluateValue } = renderHook(() => useEvaluateValue(), {
59
+ wrapper: wrapper(valueEvaluations),
60
+ });
61
+
62
+ expect(evaluateValue.current(1, null).text).toBe('--');
63
+ expect(evaluateValue.current(1, undefined).text).toBe('--');
64
+ });
65
+
66
+ it('test not found ValueEvaluation', async () => {
67
+ const { result: evaluateValue } = renderHook(() => useEvaluateValue(), {
68
+ wrapper: wrapper(valueEvaluations),
69
+ });
70
+
71
+ expect(evaluateValue.current(3, 1)).toBe(null);
72
+ });
73
+
74
+ it('test template not handle', async () => {
75
+ valueEvaluations[1].template = 'other_template';
76
+ const { result: evaluateValue } = renderHook(() => useEvaluateValue(), {
77
+ wrapper: wrapper(valueEvaluations),
78
+ });
79
+
80
+ expect(evaluateValue.current(1, 1)).toBe(null);
81
+ });
82
+
83
+ it('test evaluate range', async () => {
84
+ const { result: evaluateValue } = renderHook(() => useEvaluateValue(), {
85
+ wrapper: wrapper(valueEvaluations),
86
+ });
87
+
88
+ expect(evaluateValue.current(1, 1).text).toBe('On');
89
+ expect(evaluateValue.current(1, 0).text).toBe('Off');
90
+ expect(evaluateValue.current(1, 100).text).toBe(100);
91
+ });
92
+
93
+ it('test evaluate boolean', async () => {
94
+ const { result: evaluateValue } = renderHook(() => useEvaluateValue(), {
95
+ wrapper: wrapper(valueEvaluations),
96
+ });
97
+
98
+ expect(evaluateValue.current(2, 0).text).toBe('Off');
99
+ expect(evaluateValue.current(2, 1).text).toBe('On');
100
+ expect(evaluateValue.current(2, 2).text).toBe(2);
101
+ });
102
+ });
@@ -0,0 +1,20 @@
1
+ import { useMemo } from 'react';
2
+ import { DEVICE_TYPE } from '../../../configs/Constants';
3
+ import { getConfigControlFromDeviceDisplay } from '../utils';
4
+ import { useWatchConfigs } from '../../../hooks/IoT';
5
+
6
+ export const useDeviceWatchConfigControl = (device, display) => {
7
+ const configsNeedWatching = useMemo(() => {
8
+ if (
9
+ !device?.is_managed_by_backend ||
10
+ [DEVICE_TYPE.GOOGLE_HOME, DEVICE_TYPE.LG_THINQ].includes(
11
+ device?.device_type
12
+ )
13
+ ) {
14
+ return [];
15
+ }
16
+ return getConfigControlFromDeviceDisplay(display);
17
+ }, [device, display]);
18
+
19
+ useWatchConfigs(configsNeedWatching);
20
+ };
@@ -0,0 +1,45 @@
1
+ export const getConfigControlFromDeviceDisplay = (deviceDisplay) => {
2
+ const configIds = [];
3
+ deviceDisplay?.items.map((item) => {
4
+ if (item.type !== 'action') {
5
+ return;
6
+ }
7
+ const actionGroup = item.configuration;
8
+ const { configuration } = actionGroup;
9
+
10
+ switch (actionGroup.template) {
11
+ case 'on_off_button_action_template':
12
+ case 'OnOffButtonActionTemplate':
13
+ case 'OnOffSimpleActionTemplate':
14
+ case 'OnOffSmartLockActionTemplate':
15
+ case 'NumberUpDownActionTemplate':
16
+ case 'OptionsDropdownActionTemplate':
17
+ case 'color_picker_template':
18
+ case 'slider_range_template':
19
+ if (configuration.config) {
20
+ configIds.push(configuration.config);
21
+ }
22
+ break;
23
+ case 'StatesGridActionTemplate':
24
+ configuration.options.forEach((option) => {
25
+ configIds.push(option.config);
26
+ });
27
+ break;
28
+ case 'TimerActionTemplate':
29
+ if (configuration.config_hour) {
30
+ configIds.push(configuration.config_hour);
31
+ }
32
+ if (configuration.config_minute) {
33
+ configIds.push(configuration.config_minute);
34
+ }
35
+ break;
36
+ case 'two_button_action_template':
37
+ configIds.push(configuration.button1.config);
38
+ configIds.push(configuration.button2.config);
39
+ break;
40
+ default:
41
+ break;
42
+ }
43
+ });
44
+ return configIds;
45
+ };