@eohjsc/react-native-smart-city 0.7.22 → 0.7.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 (61) hide show
  1. package/package.json +1 -1
  2. package/src/commons/ActionGroup/TerminalBoxTemplate.js +3 -0
  3. package/src/commons/ActionTemplate/OnOffButtonAction.js +38 -4
  4. package/src/commons/ActionTemplate/OnOffSimpleAction.js +55 -15
  5. package/src/commons/ActionTemplate/OnOffSmartLockAction.js +46 -8
  6. package/src/commons/ActionTemplate/SwitchButtonAction.js +35 -4
  7. package/src/commons/ActionTemplate/ThreeButtonAction.js +13 -3
  8. package/src/commons/ActionTemplate/__test__/OnOffButtonAction.test.js +46 -7
  9. package/src/commons/ActionTemplate/__test__/OnOffSimpleAction.test.js +66 -6
  10. package/src/commons/ActionTemplate/__test__/OnOffSmartLockAction.test.js +53 -13
  11. package/src/commons/ActionTemplate/__test__/SwitchButtonAction.test.js +46 -7
  12. package/src/commons/ActionTemplate/__test__/index.test.js +6 -2
  13. package/src/commons/ActionTemplate/index.js +65 -10
  14. package/src/commons/MediaPlayerDetail/MediaPlayerFull.js +26 -32
  15. package/src/commons/OneTapTemplate/StatesGridActionTemplate.js +8 -6
  16. package/src/commons/SubUnit/OneTap/__test__/SubUnitAutomate.test.js +6 -0
  17. package/src/commons/SubUnit/OneTap/index.js +5 -0
  18. package/src/commons/UnitSummary/ConfigHistoryChart/index.js +9 -11
  19. package/src/commons/Widgets/IFrameWithConfig/IFrameWithConfig.js +3 -1
  20. package/src/commons/Widgets/IFrameWithConfig/__tests__/IFrameWithConfig.test.js +1 -1
  21. package/src/configs/API.js +6 -0
  22. package/src/configs/AccessibilityLabel.js +2 -0
  23. package/src/screens/ActivityLog/__test__/index.test.js +10 -0
  24. package/src/screens/ActivityLog/hooks/index.js +1 -1
  25. package/src/screens/Automate/AddNewAction/ChooseAction.js +15 -51
  26. package/src/screens/Automate/AddNewAction/SelectControlDevices.js +13 -3
  27. package/src/screens/Automate/AddNewAction/SetupConfigCondition.js +74 -54
  28. package/src/screens/Automate/AddNewAction/__test__/ChooseAction.test.js +114 -4
  29. package/src/screens/Automate/AddNewAction/__test__/SetupConfigCondition.test.js +37 -8
  30. package/src/screens/Automate/AddNewAutoSmart/AddTypeSmart.js +5 -0
  31. package/src/screens/Automate/AddNewAutoSmart/__test__/AddNewAutoSmart.test.js +18 -2
  32. package/src/screens/Automate/Components/InputName.js +7 -6
  33. package/src/screens/Automate/Constants.js +12 -0
  34. package/src/screens/Automate/EditActionsList/UpdateActionScript.js +24 -55
  35. package/src/screens/Automate/EditActionsList/__tests__/UpdateActionScript.test.js +298 -41
  36. package/src/screens/Automate/EditActionsList/__tests__/index.test.js +2 -2
  37. package/src/screens/Automate/EditActionsList/index.js +26 -14
  38. package/src/screens/Automate/MultiUnits.js +9 -1
  39. package/src/screens/Automate/OneTap/__test__/AddNewOneTap.test.js +3 -3
  40. package/src/screens/Automate/ScriptDetail/Components/AddActionScript.js +4 -10
  41. package/src/screens/Automate/ScriptDetail/Components/DeleteScript.js +2 -4
  42. package/src/screens/Automate/ScriptDetail/__test__/index.test.js +78 -0
  43. package/src/screens/Automate/ScriptDetail/index.js +16 -10
  44. package/src/screens/Automate/ScriptDetail/utils.js +39 -15
  45. package/src/screens/Automate/SetSchedule/AddEditConditionSchedule.js +27 -160
  46. package/src/screens/Automate/SetSchedule/EditSchedule.js +269 -0
  47. package/src/screens/Automate/SetSchedule/__test__/AddEditConditionSchedule.test.js +327 -22
  48. package/src/screens/Automate/SetSchedule/__test__/index.test.js +35 -22
  49. package/src/screens/Automate/SetSchedule/components/RepeatOptionsPopup.js +2 -8
  50. package/src/screens/Automate/SetSchedule/index.js +15 -129
  51. package/src/screens/Automate/SetSchedule/styles/indexStyles.js +9 -0
  52. package/src/screens/Automate/__test__/MultiUnits.test.js +6 -1
  53. package/src/screens/Automate/hooks/useAction.js +222 -0
  54. package/src/screens/Device/__test__/detail.test.js +48 -1
  55. package/src/screens/Device/detail.js +46 -3
  56. package/src/screens/PlayBackCamera/__test__/index.test.js +48 -13
  57. package/src/screens/PlayBackCamera/index.js +1 -1
  58. package/src/utils/Apis/axios.js +6 -0
  59. package/src/utils/I18n/translations/en.js +8 -0
  60. package/src/utils/I18n/translations/vi.js +8 -0
  61. package/src/screens/Automate/constants.js +0 -0
@@ -1,141 +1,27 @@
1
1
  import { useNavigation } from '@react-navigation/native';
2
- import moment from 'moment';
3
- import React, { memo, useCallback, useState } from 'react';
4
- import { ScrollView } from 'react-native';
5
- import Calendar from '../../../commons/Calendar';
6
- import WheelDateTimePicker from '../../../commons/WheelDateTimePicker';
7
- import { useBoolean } from '../../../hooks/Common';
8
- import { useTranslations } from '../../../hooks/Common/useTranslations';
2
+ import React, { useCallback } from 'react';
9
3
  import Routes from '../../../utils/Route';
10
- import NewActionWrapper from '../AddNewAction/NewActionWrapper';
11
- import RepeatOptionsPopup, {
12
- REPEAT_OPTIONS,
13
- } from './components/RepeatOptionsPopup';
14
- import RowItem from './components/RowItem';
15
- import SelectWeekday from './components/SelectWeekday';
16
- import styles from './styles/indexStyles';
4
+ import EditSchedule from './EditSchedule';
17
5
 
18
6
  const SetSchedule = ({ route }) => {
19
- const t = useTranslations();
20
7
  const { automate = {}, closeScreen } = route?.params || {};
21
8
  const { navigate } = useNavigation();
22
- const [repeat, setRepeat] = useState(automate.repeat || REPEAT_OPTIONS.ONCE);
23
9
 
24
- const getDateString = (date) => {
25
- const today = moment();
26
- if (date.isSame(today, 'day')) {
27
- return date.format(`[${t('today')}], D MMMM YYYY `);
28
- }
29
- return date.format('ddd, D MMMM YYYY');
30
- };
31
-
32
- const [time, setTime] = useState(
33
- automate.time_repeat
34
- ? moment(automate.time_repeat, 'HH:mm:ss')
35
- : moment().second(0)
36
- );
37
- const [date, setDate] = useState(
38
- automate.date_repeat ? moment(automate.date_repeat, 'YYYY-MM-DD') : moment()
39
- );
40
- const [weekday, setWeekday] = useState(automate.weekday_repeat || []);
41
-
42
- const [showRepeatOptions, setShowRepeatOptions, setHideRepeatOptions] =
43
- useBoolean();
44
- const [showTimePicker, setShowTimePicker, setHideTimePicker] = useBoolean();
45
- const [showCalendar, setShowCalendar, setHideCalendar] = useBoolean();
46
-
47
- const handleOnSave = useCallback(() => {
48
- navigate(Routes.ValueChangeName, {
49
- closeScreen: closeScreen,
50
- automate: {
51
- ...automate,
52
- repeat: repeat,
53
- time_repeat: time.format('HH:mm:ss'),
54
- date_repeat: date.format('YYYY-MM-DD'),
55
- weekday_repeat: weekday,
56
- },
57
- unitId: automate.unit,
58
- });
59
- }, [navigate, closeScreen, automate, repeat, time, date, weekday]);
60
-
61
- const onSetRepeatOption = useCallback(
62
- (value) => {
63
- setRepeat(value);
64
- setHideRepeatOptions();
10
+ const handleOnSave = useCallback(
11
+ (data) => {
12
+ navigate(Routes.ValueChangeName, {
13
+ closeScreen: closeScreen,
14
+ automate: {
15
+ ...automate,
16
+ conditions: [data],
17
+ },
18
+ unitId: automate.unit,
19
+ });
65
20
  },
66
- [setRepeat, setHideRepeatOptions]
21
+ [navigate, closeScreen, automate]
67
22
  );
68
23
 
69
- const onTimePicked = useCallback(
70
- (timeData) => {
71
- setTime(moment(timeData));
72
- },
73
- [setTime]
74
- );
75
-
76
- const onDatePicked = useCallback(
77
- (datePicked) => {
78
- setDate(datePicked);
79
- },
80
- [setDate]
81
- );
82
-
83
- return (
84
- <NewActionWrapper
85
- name={t('set_schedule')}
86
- onNext={handleOnSave}
87
- canNext
88
- nextTitle={t('save')}
89
- >
90
- <ScrollView
91
- contentContainerStyle={styles.scollView}
92
- scrollIndicatorInsets={{ right: 1 }}
93
- >
94
- <RowItem
95
- title={t('set_time')}
96
- value={time.format('HH:mm')}
97
- icon="clock-circle"
98
- onPress={setShowTimePicker}
99
- />
100
- {repeat === REPEAT_OPTIONS.ONCE && (
101
- <RowItem
102
- title={t('select_date')}
103
- value={getDateString(date)}
104
- icon="calendar"
105
- onPress={setShowCalendar}
106
- />
107
- )}
108
- {repeat === REPEAT_OPTIONS.EVERYWEEK && (
109
- <SelectWeekday weekday={weekday} setWeekday={setWeekday} />
110
- )}
111
- <RowItem
112
- title={t('repeat')}
113
- value={t(`${repeat}`)}
114
- arrow
115
- onPress={setShowRepeatOptions}
116
- />
117
- </ScrollView>
118
- <RepeatOptionsPopup
119
- isVisible={showRepeatOptions}
120
- onHide={setHideRepeatOptions}
121
- onSetRepeat={onSetRepeatOption}
122
- />
123
- <WheelDateTimePicker
124
- mode="time"
125
- isVisible={showTimePicker}
126
- defaultValue={time.valueOf()}
127
- onCancel={setHideTimePicker}
128
- onPicked={onTimePicked}
129
- />
130
- <Calendar
131
- isVisible={showCalendar}
132
- defaultDate={date}
133
- minDate={moment()}
134
- onCancel={setHideCalendar}
135
- onConfirm={onDatePicked}
136
- />
137
- </NewActionWrapper>
138
- );
24
+ return <EditSchedule onSave={handleOnSave} />;
139
25
  };
140
26
 
141
- export default memo(SetSchedule);
27
+ export default SetSchedule;
@@ -20,4 +20,13 @@ export default StyleSheet.create({
20
20
  borderTopWidth: 1,
21
21
  backgroundColor: Colors.White,
22
22
  },
23
+ inputWrap: {
24
+ marginTop: 0,
25
+ },
26
+ inputNumber: {
27
+ borderWidth: 1,
28
+ borderColor: Colors.Gray4,
29
+ borderStyle: 'solid',
30
+ borderRadius: 10,
31
+ },
23
32
  });
@@ -190,11 +190,16 @@ describe('Test MultiUnits', () => {
190
190
  await act(async () => {
191
191
  ItemAddNews[0].props.onAddNew();
192
192
  });
193
- expect(global.mockedNavigate).toBeCalledWith(Routes.UnitStack, {
193
+ expect(global.mockedNavigate).toHaveBeenCalledWith(Routes.UnitStack, {
194
194
  screen: Routes.ScenarioName,
195
195
  params: {
196
196
  automate: {
197
197
  type: AUTOMATE_TYPE.ONE_TAP,
198
+ conditions: [
199
+ {
200
+ type: AUTOMATE_TYPE.ONE_TAP,
201
+ },
202
+ ],
198
203
  unit: undefined,
199
204
  },
200
205
  closeScreen: undefined,
@@ -0,0 +1,222 @@
1
+ import { useCallback, useEffect, useMemo, useState } from 'react';
2
+
3
+ import { API } from '../../../configs';
4
+ import {
5
+ axiosDelete,
6
+ axiosGet,
7
+ axiosPost,
8
+ axiosPut,
9
+ } from '../../../utils/Apis/axios';
10
+
11
+ const useAction = (device, scriptItems, activatedScript = {}) => {
12
+ const {
13
+ action_script,
14
+ toggle_script,
15
+ order = scriptItems.length + 1,
16
+ } = activatedScript;
17
+ const [saveActions, setSaveActions] = useState([]);
18
+ const [displayActions, setDisplayActions] = useState([]);
19
+
20
+ const listAction = useMemo(() => {
21
+ return saveActions
22
+ .filter((item) => !item.is_toggle)
23
+ .map((item) => ({
24
+ action: item.action,
25
+ data: item.data,
26
+ order: order + item.index,
27
+ }));
28
+ }, [saveActions, order]);
29
+
30
+ const listToggle = useMemo(() => {
31
+ return saveActions
32
+ .filter((item) => item.is_toggle)
33
+ .map((item) => ({
34
+ action_on: item.action_on,
35
+ action_off: item.action_off,
36
+ config: item.config,
37
+ action_on_data: item.action_on_data,
38
+ action_off_data: item.action_off_data,
39
+ is_on_values: item.is_on_values,
40
+ order: order + item.index,
41
+ }));
42
+ }, [saveActions, order]);
43
+
44
+ const fetchDisplayActions = useCallback(async () => {
45
+ const { success, data } = await axiosGet(
46
+ API.DEVICE.DISPLAY_ACTIONS(device.id)
47
+ );
48
+
49
+ success && setDisplayActions(data);
50
+ }, [device.id]);
51
+
52
+ const onSelectAction = useCallback((action) => {
53
+ setSaveActions((prevActions) => {
54
+ const index = prevActions.findIndex(
55
+ (item) => item.index === action.index
56
+ );
57
+ if (index !== -1) {
58
+ const newActions = [...prevActions];
59
+ newActions[index] = action;
60
+ return newActions;
61
+ } else {
62
+ return [...prevActions, action];
63
+ }
64
+ });
65
+ }, []);
66
+
67
+ const onAddAction = useCallback(
68
+ async (automateId, unitId, startOrder, numberAction) => {
69
+ if (!listAction.length) {
70
+ return false;
71
+ }
72
+ const { success } = await axiosPost(
73
+ API.AUTOMATE.ADD_SCRIPT_ACTION(automateId),
74
+ {
75
+ list_action: listAction,
76
+ ...(startOrder && { start_order: startOrder }),
77
+ ...(numberAction && { number_action: numberAction }),
78
+ unit: unitId,
79
+ }
80
+ );
81
+ return success;
82
+ },
83
+ [listAction]
84
+ );
85
+
86
+ const onAddToggle = useCallback(
87
+ async (automateId, unitId, startOrder, numberAction) => {
88
+ if (!listToggle.length) {
89
+ return false;
90
+ }
91
+ const { success } = await axiosPost(
92
+ API.AUTOMATE.ADD_SCRIPT_TOGGLE(automateId),
93
+ {
94
+ list_action: listToggle,
95
+ ...(startOrder && { start_order: startOrder }),
96
+ ...(numberAction && { number_action: numberAction }),
97
+ unit: unitId,
98
+ }
99
+ );
100
+ return success;
101
+ },
102
+ [listToggle]
103
+ );
104
+
105
+ const onAddSubmit = useCallback(
106
+ async (automateId, unitId) => {
107
+ const actionSuccess = await onAddAction(automateId, unitId);
108
+ const toggleSuccess = await onAddToggle(automateId, unitId);
109
+ return actionSuccess || toggleSuccess;
110
+ },
111
+ [onAddAction, onAddToggle]
112
+ );
113
+
114
+ const onDeleteScript = useCallback(async (automateId, scriptId) => {
115
+ const { success } = await axiosDelete(
116
+ API.AUTOMATE.DELETE_SCRIPT_ITEM(automateId, scriptId)
117
+ );
118
+ return success;
119
+ }, []);
120
+
121
+ const onUpdateAction = useCallback(
122
+ async (automateId, scriptId, unitId) => {
123
+ if (!action_script) {
124
+ const success = await onDeleteScript(automateId, scriptId);
125
+ return (
126
+ success &&
127
+ (await onAddAction(
128
+ automateId,
129
+ unitId,
130
+ listAction[0].order,
131
+ saveActions.length
132
+ ))
133
+ );
134
+ }
135
+ const { success } = await axiosPut(
136
+ API.AUTOMATE.UPDATE_SCRIPT_ACTION(automateId),
137
+ {
138
+ id: scriptId,
139
+ list_action: listAction,
140
+ number_action: saveActions.length,
141
+ unit: unitId,
142
+ }
143
+ );
144
+ return success;
145
+ },
146
+ [action_script, listAction, saveActions.length, onAddAction, onDeleteScript]
147
+ );
148
+
149
+ const onUpdateToggle = useCallback(
150
+ async (automateId, scriptId, unitId) => {
151
+ if (!toggle_script) {
152
+ const success = await onDeleteScript(automateId, scriptId);
153
+ return (
154
+ success &&
155
+ (await onAddToggle(
156
+ automateId,
157
+ unitId,
158
+ listToggle[0].order,
159
+ saveActions.length
160
+ ))
161
+ );
162
+ }
163
+ const { success } = await axiosPut(
164
+ API.AUTOMATE.UPDATE_SCRIPT_TOGGLE(automateId),
165
+ {
166
+ id: scriptId,
167
+ list_action: listToggle,
168
+ number_action: saveActions.length,
169
+ unit: unitId,
170
+ }
171
+ );
172
+ return success;
173
+ },
174
+ [toggle_script, listToggle, saveActions.length, onAddToggle, onDeleteScript]
175
+ );
176
+
177
+ const onUpdateSubmit = useCallback(
178
+ async (automateId, scriptId, unitId) => {
179
+ if (saveActions[0].is_toggle) {
180
+ const toggleSuccess = await onUpdateToggle(
181
+ automateId,
182
+ scriptId,
183
+ unitId
184
+ );
185
+ const actionSuccess = await onAddAction(automateId, unitId);
186
+ return toggleSuccess || actionSuccess;
187
+ } else {
188
+ const actionSuccess = await onUpdateAction(
189
+ automateId,
190
+ scriptId,
191
+ unitId
192
+ );
193
+ const toggleSuccess = await onAddToggle(automateId, unitId);
194
+ return actionSuccess || toggleSuccess;
195
+ }
196
+ },
197
+ [saveActions, onAddAction, onAddToggle, onUpdateAction, onUpdateToggle]
198
+ );
199
+
200
+ useEffect(() => {
201
+ fetchDisplayActions();
202
+ }, [fetchDisplayActions]);
203
+
204
+ return {
205
+ displayActions,
206
+ saveActions,
207
+
208
+ setDisplayActions,
209
+ setSaveActions,
210
+ onSelectAction,
211
+
212
+ onAddAction,
213
+ onAddToggle,
214
+ onAddSubmit,
215
+
216
+ onUpdateAction,
217
+ onUpdateToggle,
218
+ onUpdateSubmit,
219
+ };
220
+ };
221
+
222
+ export default useAction;
@@ -8,6 +8,7 @@ import CurrentRainSensor from '../../../commons/Device/RainningSensor/CurrentRai
8
8
  import { AlertSendConfirm } from '../../../commons/EmergencyButton/AlertSendConfirm';
9
9
  import { AlertSent } from '../../../commons/EmergencyButton/AlertSent';
10
10
  import Text from '../../../commons/Text';
11
+ import { SensorConnectStatusViewHeader } from '../components/SensorConnectStatusViewHeader';
11
12
  import { API } from '../../../configs';
12
13
  import { AccessibilityLabel } from '../../../configs/Constants';
13
14
  import { SCProvider } from '../../../context';
@@ -369,10 +370,19 @@ describe('test DeviceDetail', () => {
369
370
  const instance = tree.root;
370
371
  const scrollView = instance.findByType(ScrollView);
371
372
  const refreshControl = scrollView.props.refreshControl;
373
+ expect(mock.history.post).toHaveLength(2);
372
374
  await act(async () => {
373
375
  refreshControl.props.onRefresh();
374
376
  });
375
377
  expect(scrollView).toBeDefined();
378
+ expect(mock.history.post).toHaveLength(4);
379
+ const urls = mock.history.post.map((item) => item.url);
380
+ expect(
381
+ urls.filter((url) => url === API.UNIT.END_DEVICES_STATUS(1))
382
+ ).toHaveLength(2);
383
+ expect(
384
+ urls.filter((url) => url === API.HOME_ASSISTANT.CHECK_SEND_EMAIL())
385
+ ).toHaveLength(2);
376
386
  });
377
387
 
378
388
  it('Should render SensorDisplayItem', async () => {
@@ -760,7 +770,7 @@ describe('test DeviceDetail', () => {
760
770
 
761
771
  it('Test fetchUnitDetail', async () => {
762
772
  const unitId = 1;
763
- mock.onGet(API.UNIT.UNIT_DETAIL(unitId)).reply(200);
773
+ mock.onGet(API.UNIT.UNIT_DETAIL(unitId)).reply(200, { id: unitId });
764
774
  await act(async () => {
765
775
  await create(
766
776
  wrapComponent(store, account, {
@@ -773,6 +783,43 @@ describe('test DeviceDetail', () => {
773
783
  expect(urls).toContain(API.UNIT.UNIT_DETAIL(unitId));
774
784
  });
775
785
 
786
+ it('Test fetch end device status', async () => {
787
+ const unitId = 1;
788
+ mock.onGet(API.UNIT.UNIT_DETAIL(unitId)).reply(200, { id: unitId });
789
+ mock.onPost(API.UNIT.END_DEVICES_STATUS(unitId)).reply(200, [
790
+ {
791
+ id: 1,
792
+ is_connected: true,
793
+ },
794
+ ]);
795
+ await act(async () => {
796
+ tree = await create(
797
+ wrapComponent(store, account, {
798
+ ...route,
799
+ params: { ...route.params, unitData: null, unitId: unitId },
800
+ })
801
+ );
802
+ });
803
+ const instance = tree.root;
804
+ const sensorConnectStatus = instance.findByType(
805
+ SensorConnectStatusViewHeader
806
+ );
807
+ expect(sensorConnectStatus.props.connectedViaNetwork).toBeTruthy();
808
+ expect(mock.history.post).toHaveLength(1);
809
+ expect(mock.history.post[0].url).toEqual(
810
+ API.UNIT.END_DEVICES_STATUS(unitId)
811
+ );
812
+ });
813
+
814
+ it('Test not fetch end device status if not managed by backend', async () => {
815
+ route.params.sensorData.is_managed_by_backend = false;
816
+ await act(async () => {
817
+ tree = await create(wrapComponent(store, account, route));
818
+ });
819
+ const urls = mock.history.post.map((x) => x.url);
820
+ expect(urls).not.toContain(API.UNIT.END_DEVICES_STATUS(1));
821
+ });
822
+
776
823
  it('should render device detail, useWatchConfig value', async () => {
777
824
  mock.onGet(API.CHIP.JSON_CONFIGURATION).reply(200, []);
778
825
  const responseDisplay = {
@@ -54,7 +54,7 @@ import Routes from '../../utils/Route';
54
54
  import { getData as getLocalData } from '../../utils/Storage';
55
55
  import { API, Colors, SCConfig } from '../../configs';
56
56
  import { DEVICE_TYPE, AccessibilityLabel } from '../../configs/Constants';
57
- import { axiosGet } from '../../utils/Apis/axios';
57
+ import { axiosGet, axiosPost } from '../../utils/Apis/axios';
58
58
  import { notImplemented } from '../../utils/Utils';
59
59
  import { useReceiveNotifications } from '../../hooks';
60
60
  import useChipJsonConfiguration, {
@@ -482,6 +482,31 @@ const DeviceDetail = ({ route }) => {
482
482
  fetchDataDeviceDetail();
483
483
  }, [device, isNetworkConnected, fetchDataDeviceDetail]);
484
484
 
485
+ const fetchDeviceStatus = useCallback(async () => {
486
+ if (
487
+ !isNetworkConnected ||
488
+ !device.is_managed_by_backend ||
489
+ configIdsTemp.current.length
490
+ ) {
491
+ return;
492
+ }
493
+
494
+ const { success, data } = await axiosPost(
495
+ API.UNIT.END_DEVICES_STATUS(unit.id),
496
+ {
497
+ end_device_ids: [device.id],
498
+ }
499
+ );
500
+
501
+ success && setAction(Action.SET_DEVICES_STATUS, data);
502
+ }, [
503
+ isNetworkConnected,
504
+ unit.id,
505
+ device.id,
506
+ device.is_managed_by_backend,
507
+ setAction,
508
+ ]);
509
+
485
510
  const onRefresh = useCallback(async () => {
486
511
  await fetchSensorDetail();
487
512
  await fetchDataDeviceDetail();
@@ -495,9 +520,11 @@ const DeviceDetail = ({ route }) => {
495
520
  })();
496
521
  }
497
522
  await checkScanDevicesBLE();
523
+ await fetchDeviceStatus();
498
524
  }, [
499
525
  fetchSensorDetail,
500
526
  fetchDataDeviceDetail,
527
+ fetchDeviceStatus,
501
528
  unit?.remote_control_options,
502
529
  isNetworkConnected,
503
530
  checkScanDevicesBLE,
@@ -516,11 +543,20 @@ const DeviceDetail = ({ route }) => {
516
543
  return;
517
544
  }
518
545
 
519
- const { configs, options, config, from_config, to_config } =
520
- configuration;
546
+ const {
547
+ configs,
548
+ options,
549
+ config,
550
+ from_config,
551
+ to_config,
552
+ history_configs,
553
+ realtime_configs,
554
+ } = configuration;
521
555
 
522
556
  configs?.forEach((object) => configIdsSet.add(object.id));
523
557
  options?.forEach((option) => configIdsSet.add(option.config));
558
+ history_configs?.forEach((object) => configIdsSet.add(object.id));
559
+ realtime_configs?.forEach((object) => configIdsSet.add(object.id));
524
560
 
525
561
  if (config) {
526
562
  const configId = config.id || config;
@@ -622,6 +658,13 @@ const DeviceDetail = ({ route }) => {
622
658
  }, [display.items, isNetworkConnected, device, mqttConfigs])
623
659
  );
624
660
 
661
+ useFocusEffect(
662
+ useCallback(() => {
663
+ fetchDeviceStatus();
664
+ // eslint-disable-next-line react-hooks/exhaustive-deps
665
+ }, [])
666
+ );
667
+
625
668
  useWatchConfigs(chipFiltered.length ? [] : configIdsTemp.current);
626
669
 
627
670
  const isShowEmergencyResolve =
@@ -20,19 +20,22 @@ const wrapComponent = () => (
20
20
 
21
21
  describe('Test PlayBackCamera', () => {
22
22
  let tree;
23
- jest.useFakeTimers();
24
- global.mockedUseRoute.mockReturnValue({
25
- params: {
26
- item: {
27
- configuration: {
28
- time_zone: 0,
29
- playback: '',
30
- name: 'name',
31
- uri: '',
23
+
24
+ beforeEach(() => {
25
+ jest.useFakeTimers();
26
+ global.mockedUseRoute.mockReturnValue({
27
+ params: {
28
+ item: {
29
+ configuration: {
30
+ time_zone: 0,
31
+ playback: 'rtsp://example.com/playback',
32
+ name: 'name',
33
+ uri: 'rtsp://example.com/stream',
34
+ },
32
35
  },
36
+ thumbnail: {},
33
37
  },
34
- thumbnail: {},
35
- },
38
+ });
36
39
  });
37
40
 
38
41
  it('Test render', async () => {
@@ -129,7 +132,9 @@ describe('Test PlayBackCamera', () => {
129
132
  },
130
133
  });
131
134
  });
132
- jest.runAllTimers();
135
+ await act(async () => {
136
+ jest.runAllTimers();
137
+ });
133
138
  //NOTE: isFirstTime = false
134
139
  await act(async () => {
135
140
  timerScroll[0].props.onScroll({
@@ -147,7 +152,37 @@ describe('Test PlayBackCamera', () => {
147
152
  });
148
153
  const media = instance.findByType(MediaPlayerFull);
149
154
  expect(media.props.uri).toEqual(
150
- `=${moment().format('YYYYMMDDT003730\\Z')}`
155
+ `rtsp://example.com/playback=${moment().format('YYYYMMDDT003730\\Z')}`
156
+ );
157
+ });
158
+
159
+ it('Test render when playback is null', async () => {
160
+ global.mockedUseRoute.mockReturnValue({
161
+ params: {
162
+ item: {
163
+ configuration: {
164
+ time_zone: 0,
165
+ playback: null,
166
+ name: 'name',
167
+ uri: 'rtsp://example.com/stream',
168
+ },
169
+ },
170
+ thumbnail: {},
171
+ },
172
+ });
173
+ await act(async () => {
174
+ tree = await create(wrapComponent());
175
+ });
176
+ const instance = tree.root;
177
+ const buttonAddDate = instance.findAll(
178
+ (el) =>
179
+ el.props.accessibilityLabel === AccessibilityLabel.ON_PRESS_ADD_DATE &&
180
+ el.type === TouchableOpacity
151
181
  );
182
+ await act(async () => {
183
+ await buttonAddDate[0].props.onPress();
184
+ });
185
+ const media = instance.findByType(MediaPlayerFull);
186
+ expect(media.props.uri).toEqual('rtsp://example.com/stream');
152
187
  });
153
188
  });
@@ -121,7 +121,7 @@ const PlayBackCamera = () => {
121
121
  'YYYY-MM-DD HH:mm:SS'
122
122
  ).valueOf();
123
123
 
124
- if (selectedDayTime > currentTime) {
124
+ if (selectedDayTime > currentTime || !playback) {
125
125
  setUriCamera(uri);
126
126
  } else {
127
127
  if (playback.includes('chID')) {
@@ -75,6 +75,12 @@ const parseErrorResponse = async (error) => {
75
75
  if (message instanceof Array) {
76
76
  message = message[0];
77
77
  }
78
+ if (message instanceof Object) {
79
+ return await parseErrorResponse({
80
+ ...error,
81
+ data: message,
82
+ });
83
+ }
78
84
  if (message?.message) {
79
85
  message = message.message;
80
86
  }