@eohjsc/react-native-smart-city 0.3.92 → 0.3.94
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.
- package/package.json +5 -1
- package/src/commons/ActionGroup/SliderRangeTemplate.js +2 -2
- package/src/commons/AlertAction/index.js +5 -0
- package/src/commons/BottomButtonView/index.js +22 -4
- package/src/commons/Button/index.js +5 -0
- package/src/commons/Device/ConnectedViewHeader.js +0 -1
- package/src/commons/Device/Emergency/EmergencyDetail.js +4 -2
- package/src/commons/Device/Emergency/__test__/EmergencyDetail.test.js +4 -2
- package/src/commons/Device/ProgressBar/index.js +3 -1
- package/src/commons/Device/ProgressBar/styles.js +1 -4
- package/src/commons/Device/WindSpeed/Anemometer/index.js +1 -1
- package/src/commons/Header/HeaderCustom.js +8 -18
- package/src/commons/SubUnit/OneTap/ItemOneTap.js +84 -129
- package/src/commons/SubUnit/OneTap/__test__/SubUnitAutomate.test.js +74 -39
- package/src/commons/SubUnit/OneTap/index.js +24 -4
- package/src/commons/ViewButtonBottom/index.js +32 -4
- package/src/configs/API.js +4 -0
- package/src/configs/AccessibilityLabel.js +1 -0
- package/src/configs/BLE.js +1 -0
- package/src/context/actionType.ts +2 -1
- package/src/context/mockStore.ts +1 -0
- package/src/context/reducer.ts +12 -1
- package/src/hooks/Explore/useKeyboardAnimated.js +10 -4
- package/src/hooks/IoT/useBluetoothConnection.js +14 -26
- package/src/hooks/useMqtt.js +95 -0
- package/src/iot/Monitor.js +2 -1
- package/src/iot/RemoteControl/Bluetooth.js +56 -19
- package/src/iot/RemoteControl/__test__/Bluetooth.test.js +140 -0
- package/src/iot/mqtt.js +233 -0
- package/src/navigations/UnitStack.js +11 -3
- package/src/screens/AddLocationMaps/index.js +18 -16
- package/src/screens/AddLocationMaps/indexStyle.js +3 -0
- package/src/screens/AddNewGateway/ConnectingDevice.js +7 -0
- package/src/screens/AddNewGateway/RenameNewDevices.js +2 -2
- package/src/screens/AddNewGateway/SelectDeviceType.js +67 -9
- package/src/screens/AddNewGateway/__test__/ConnectingZigbeeDevice.test.js +35 -0
- package/src/screens/AddNewGateway/__test__/SelectDeviceType.test.js +183 -29
- package/src/screens/Automate/AddNewAction/NewActionWrapper.js +3 -6
- package/src/screens/Automate/AddNewAction/SetupConfigCondition.js +29 -29
- package/src/screens/Automate/AddNewAction/__test__/SetupSensor.test.js +45 -39
- package/src/screens/Automate/AddNewAutoSmart/AddAutomationTypeSmart.js +25 -0
- package/src/screens/{AddNewAutoSmart/index.js → Automate/AddNewAutoSmart/AddTypeSmart.js} +16 -51
- package/src/screens/Automate/AddNewAutoSmart/AddUnknownTypeSmart.js +29 -0
- package/src/screens/{AddNewAutoSmart → Automate/AddNewAutoSmart}/__test__/AddNewAutoSmart.test.js +11 -11
- package/src/screens/{AddNewAutoSmart → Automate/AddNewAutoSmart}/styles/AddNewAutoSmartStyles.js +1 -1
- package/src/screens/Automate/Components/InputNameStyles.js +1 -1
- package/src/screens/Automate/EditActionsList/__tests__/index.test.js +11 -25
- package/src/screens/Automate/EditActionsList/index.js +32 -33
- package/src/screens/Automate/MultiUnits.js +29 -19
- package/src/screens/Automate/ScriptDetail/__test__/index.test.js +78 -5
- package/src/screens/Automate/ScriptDetail/index.js +38 -10
- package/src/screens/Automate/Styles/MultiUnitsStyles.js +1 -1
- package/src/screens/Automate/__test__/MultiUnits.test.js +64 -7
- package/src/screens/Automate/__test__/index.test.js +45 -11
- package/src/screens/Automate/index.js +53 -38
- package/src/screens/Device/__test__/detail.test.js +1 -1
- package/src/screens/Device/__test__/mqttDetail.test.js +599 -0
- package/src/screens/Device/components/SensorDisplayItem.js +1 -7
- package/src/screens/Device/detail.js +64 -30
- package/src/screens/Device/hooks/__test__/useEvaluateValue.test.js +24 -1
- package/src/screens/Device/hooks/useDeviceWatchConfigControl.js +13 -3
- package/src/screens/SelectUnit/__test__/index.test.js +8 -13
- package/src/screens/Sharing/InfoMemberUnit.js +2 -2
- package/src/screens/Sharing/MemberList.js +28 -7
- package/src/screens/Sharing/__test__/InfoMemberUnit.test.js +32 -18
- package/src/screens/Sharing/__test__/MemberList.test.js +37 -4
- package/src/screens/SmartAccount/__test__/SmartAccount.test.js +8 -4
- package/src/screens/SmartAccount/index.js +8 -9
- package/src/screens/SmartAccount/style.js +8 -7
- package/src/screens/Unit/AddMenu.js +42 -19
- package/src/screens/Unit/Detail.js +4 -19
- package/src/screens/Unit/Summaries.js +6 -17
- package/src/screens/Unit/__test__/AddMenu.test.js +68 -15
- package/src/screens/Unit/__test__/Summaries.test.js +2 -2
- package/src/screens/Unit/components/AutomateScript/index.js +1 -1
- package/src/utils/FactoryGateway.js +525 -0
- package/src/utils/I18n/translations/en.js +1409 -0
- package/src/utils/I18n/translations/vi.js +1411 -0
- package/src/utils/I18n/translations.ts +2 -2
- package/src/utils/Permission/backend.js +7 -0
- package/src/utils/Route/index.js +2 -1
- package/src/utils/Utils.js +11 -0
- package/src/commons/Device/SensorConnectedStatus.js +0 -56
- package/src/commons/Device/__test__/SensorConnectedStatus.test.js +0 -29
- package/src/screens/Sharing/__test__/MemberList2.test.js +0 -74
- package/src/utils/I18n/translations/en.json +0 -1138
- package/src/utils/I18n/translations/vi.json +0 -1136
|
@@ -16,11 +16,13 @@ import Routes from '../../../../utils/Route';
|
|
|
16
16
|
import api from '../../../../utils/Apis/axios';
|
|
17
17
|
import { API } from '../../../../configs';
|
|
18
18
|
import { useNavigation } from '@react-navigation/native';
|
|
19
|
+
import { ToastBottomHelper } from '../../../../utils/Utils';
|
|
20
|
+
import { getTranslate } from '../../../../utils/I18n';
|
|
19
21
|
|
|
20
22
|
const mock = new MockAdapter(api.axiosInstance);
|
|
21
23
|
|
|
22
|
-
const wrapComponent = (data) => (
|
|
23
|
-
<SCProvider initState={mockSCStore(
|
|
24
|
+
const wrapComponent = (data, storeData = {}) => (
|
|
25
|
+
<SCProvider initState={mockSCStore(storeData)}>
|
|
24
26
|
<SubUnitAutomate {...data} />
|
|
25
27
|
</SCProvider>
|
|
26
28
|
);
|
|
@@ -33,34 +35,36 @@ jest.mock('react-redux', () => {
|
|
|
33
35
|
});
|
|
34
36
|
|
|
35
37
|
let tree;
|
|
36
|
-
let data
|
|
37
|
-
isOwner: true,
|
|
38
|
-
listAutomate: [
|
|
39
|
-
{
|
|
40
|
-
text: 'Scenario',
|
|
41
|
-
data: [
|
|
42
|
-
{
|
|
43
|
-
id: 1,
|
|
44
|
-
user: 6,
|
|
45
|
-
type: 'one_tap',
|
|
46
|
-
activate_at: '2021-09-17T05:30:00Z',
|
|
47
|
-
script: {
|
|
48
|
-
name: 'Joshua Ray',
|
|
49
|
-
icon: '',
|
|
50
|
-
icon_kit: '',
|
|
51
|
-
},
|
|
52
|
-
},
|
|
53
|
-
],
|
|
54
|
-
type: AUTOMATE_TABS.SCENARIO,
|
|
55
|
-
},
|
|
56
|
-
],
|
|
57
|
-
};
|
|
38
|
+
let data;
|
|
58
39
|
|
|
59
40
|
describe('test Item', () => {
|
|
60
41
|
const mockedNavigate = useNavigation().navigate;
|
|
61
42
|
beforeEach(() => {
|
|
62
43
|
mockedNavigate.mockClear();
|
|
44
|
+
data = {
|
|
45
|
+
isOwner: true,
|
|
46
|
+
listAutomate: [
|
|
47
|
+
{
|
|
48
|
+
text: 'Scenario',
|
|
49
|
+
data: [
|
|
50
|
+
{
|
|
51
|
+
id: 1,
|
|
52
|
+
user: 6,
|
|
53
|
+
type: 'one_tap',
|
|
54
|
+
activate_at: '2021-09-17T05:30:00Z',
|
|
55
|
+
script: {
|
|
56
|
+
name: 'Joshua Ray',
|
|
57
|
+
icon: '',
|
|
58
|
+
icon_kit: '',
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
],
|
|
62
|
+
type: AUTOMATE_TABS.SCENARIO,
|
|
63
|
+
},
|
|
64
|
+
],
|
|
65
|
+
};
|
|
63
66
|
});
|
|
67
|
+
|
|
64
68
|
it('render SubUnitAutomate isOwner and handleOnAddNew', async () => {
|
|
65
69
|
await act(async () => {
|
|
66
70
|
tree = await create(wrapComponent(data));
|
|
@@ -78,7 +82,7 @@ describe('test Item', () => {
|
|
|
78
82
|
await act(async () => {
|
|
79
83
|
await item[0].props.onPress();
|
|
80
84
|
});
|
|
81
|
-
expect(mockedNavigate).toHaveBeenCalledWith(Routes.
|
|
85
|
+
expect(mockedNavigate).toHaveBeenCalledWith(Routes.ScenarioName, {
|
|
82
86
|
automate: {
|
|
83
87
|
type: 'one_tap',
|
|
84
88
|
unit: undefined,
|
|
@@ -97,13 +101,8 @@ describe('test Item', () => {
|
|
|
97
101
|
await goDetail[0].props.onPress();
|
|
98
102
|
});
|
|
99
103
|
expect(mockedNavigate).toHaveBeenCalledWith(Routes.ScriptDetail, {
|
|
100
|
-
havePermission: true,
|
|
101
104
|
id: 1,
|
|
102
|
-
|
|
103
|
-
type: 'one_tap',
|
|
104
|
-
unit: undefined,
|
|
105
|
-
textCondition: null,
|
|
106
|
-
automate: data.listAutomate[0].data[0],
|
|
105
|
+
preAutomate: data.listAutomate[0].data[0],
|
|
107
106
|
});
|
|
108
107
|
|
|
109
108
|
const handleScriptAction = instance.findAll(
|
|
@@ -189,15 +188,11 @@ describe('test Item', () => {
|
|
|
189
188
|
await goDetail[0].props.onPress();
|
|
190
189
|
});
|
|
191
190
|
expect(mockedNavigate).toHaveBeenCalledWith(Routes.ScriptDetail, {
|
|
192
|
-
havePermission: false,
|
|
193
191
|
id: 1,
|
|
194
|
-
|
|
195
|
-
type: 'value_change',
|
|
196
|
-
unit: undefined,
|
|
197
|
-
textCondition: 'Temperature higher than 29',
|
|
198
|
-
automate: data.listAutomate[0].data[0],
|
|
192
|
+
preAutomate: data.listAutomate[0].data[0],
|
|
199
193
|
});
|
|
200
194
|
});
|
|
195
|
+
|
|
201
196
|
it('render SubUnitAutomate script schedule and handleOnAddNew item automate', async () => {
|
|
202
197
|
data.isOwner = false;
|
|
203
198
|
data.type = 'schedule';
|
|
@@ -237,8 +232,8 @@ describe('test Item', () => {
|
|
|
237
232
|
await act(async () => {
|
|
238
233
|
await item[0].props.onPress();
|
|
239
234
|
});
|
|
240
|
-
expect(mockedNavigate).toHaveBeenCalledWith(Routes.
|
|
241
|
-
automate: {
|
|
235
|
+
expect(mockedNavigate).toHaveBeenCalledWith(Routes.AddAutomationTypeSmart, {
|
|
236
|
+
automate: { unit: undefined },
|
|
242
237
|
closeScreen: undefined,
|
|
243
238
|
});
|
|
244
239
|
mockedNavigate.mockClear();
|
|
@@ -251,6 +246,46 @@ describe('test Item', () => {
|
|
|
251
246
|
expect(goDetail).toHaveLength(1);
|
|
252
247
|
});
|
|
253
248
|
|
|
249
|
+
it('add new automate when reach maximum', async () => {
|
|
250
|
+
data.isOwner = false;
|
|
251
|
+
data.type = 'schedule';
|
|
252
|
+
data.unit = { id: 1 };
|
|
253
|
+
|
|
254
|
+
await act(async () => {
|
|
255
|
+
tree = await create(
|
|
256
|
+
wrapComponent(data, {
|
|
257
|
+
auth: {
|
|
258
|
+
account: {
|
|
259
|
+
user: {
|
|
260
|
+
permissions: {
|
|
261
|
+
max_automations_per_unit: 0,
|
|
262
|
+
},
|
|
263
|
+
},
|
|
264
|
+
},
|
|
265
|
+
},
|
|
266
|
+
})
|
|
267
|
+
);
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
const instance = tree.root;
|
|
271
|
+
const item = instance.findAll(
|
|
272
|
+
(el) =>
|
|
273
|
+
el.props.accessibilityLabel ===
|
|
274
|
+
AccessibilityLabel.SUB_UNIT_ADD_DEVICE &&
|
|
275
|
+
el.type === TouchableWithoutFeedback
|
|
276
|
+
);
|
|
277
|
+
|
|
278
|
+
expect(item).toHaveLength(1);
|
|
279
|
+
const spyToastError = jest.spyOn(ToastBottomHelper, 'error');
|
|
280
|
+
await act(async () => {
|
|
281
|
+
await item[0].props.onPress();
|
|
282
|
+
});
|
|
283
|
+
expect(mockedNavigate).not.toBeCalled();
|
|
284
|
+
expect(spyToastError).toBeCalledWith(
|
|
285
|
+
getTranslate('en', 'reach_max_automations_per_unit')
|
|
286
|
+
);
|
|
287
|
+
});
|
|
288
|
+
|
|
254
289
|
it('render click select option by automation', async () => {
|
|
255
290
|
await act(async () => {
|
|
256
291
|
tree = await create(wrapComponent(data));
|
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
import React, {
|
|
1
|
+
import React, {
|
|
2
|
+
useCallback,
|
|
3
|
+
useEffect,
|
|
4
|
+
useMemo,
|
|
5
|
+
useRef,
|
|
6
|
+
useState,
|
|
7
|
+
} from 'react';
|
|
2
8
|
import { TouchableOpacity, View } from 'react-native';
|
|
3
9
|
|
|
4
10
|
import { Section } from '../..';
|
|
@@ -15,17 +21,32 @@ import {
|
|
|
15
21
|
import Text from '../../Text/index.js';
|
|
16
22
|
|
|
17
23
|
import styles from './OneTapStyles.js';
|
|
24
|
+
import { ToastBottomHelper } from '../../../utils/Utils';
|
|
25
|
+
import { useBackendPermission } from '../../../utils/Permission/backend';
|
|
18
26
|
|
|
19
27
|
const SubUnitAutomate = ({ isOwner, listAutomate, unit, wrapItemStyle }) => {
|
|
20
28
|
const t = useTranslations();
|
|
21
29
|
const { navigate } = useNavigation();
|
|
22
30
|
const [automates, setAutomates] = useState(listAutomate[0]);
|
|
23
31
|
const [indexAutomate, setIndexAutomate] = useState(0);
|
|
32
|
+
const permissions = useBackendPermission();
|
|
24
33
|
const { name: currentScreen } = useRoute();
|
|
34
|
+
|
|
35
|
+
const totalAutomates = useMemo(
|
|
36
|
+
() => listAutomate.reduce((acc, item) => item.data.length + acc, 0),
|
|
37
|
+
[listAutomate]
|
|
38
|
+
);
|
|
39
|
+
|
|
25
40
|
const handleOnAddNew = () => {
|
|
41
|
+
if (unit?.id) {
|
|
42
|
+
if (permissions.max_automations_per_unit <= totalAutomates) {
|
|
43
|
+
ToastBottomHelper.error(t('reach_max_automations_per_unit'));
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
26
47
|
switch (automates.type) {
|
|
27
48
|
case AUTOMATE_TABS.SCENARIO:
|
|
28
|
-
navigate(Routes.
|
|
49
|
+
navigate(Routes.ScenarioName, {
|
|
29
50
|
automate: {
|
|
30
51
|
type: AUTOMATE_TYPE.ONE_TAP,
|
|
31
52
|
unit: unit?.id,
|
|
@@ -34,9 +55,8 @@ const SubUnitAutomate = ({ isOwner, listAutomate, unit, wrapItemStyle }) => {
|
|
|
34
55
|
});
|
|
35
56
|
break;
|
|
36
57
|
case AUTOMATE_TABS.AUTOMATION:
|
|
37
|
-
navigate(Routes.
|
|
58
|
+
navigate(Routes.AddAutomationTypeSmart, {
|
|
38
59
|
automate: {
|
|
39
|
-
type: AUTOMATE_TYPE.VALUE_CHANGE,
|
|
40
60
|
unit: unit?.id,
|
|
41
61
|
},
|
|
42
62
|
closeScreen: currentScreen,
|
|
@@ -1,10 +1,17 @@
|
|
|
1
|
-
import React, { useMemo } from 'react';
|
|
2
|
-
import {
|
|
1
|
+
import React, { useMemo, useEffect, useState } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
StyleSheet,
|
|
4
|
+
TouchableOpacity,
|
|
5
|
+
Animated,
|
|
6
|
+
Platform,
|
|
7
|
+
Easing,
|
|
8
|
+
} from 'react-native';
|
|
3
9
|
|
|
4
10
|
import { Colors } from '../../configs';
|
|
5
11
|
import Text from '../../commons/Text';
|
|
6
12
|
import { AccessibilityLabel } from '../../configs/Constants';
|
|
7
13
|
import withPreventDoubleClick from '../WithPreventDoubleClick';
|
|
14
|
+
import useKeyboardAnimated from '../../hooks/Explore/useKeyboardAnimated';
|
|
8
15
|
|
|
9
16
|
const PreventDoubleTouch = withPreventDoubleClick(TouchableOpacity);
|
|
10
17
|
|
|
@@ -23,15 +30,34 @@ const ViewButtonBottom = ({
|
|
|
23
30
|
styleButtonRight,
|
|
24
31
|
accessibilityLabelPrefix = '',
|
|
25
32
|
isPreventDoubleTouch = false,
|
|
33
|
+
wrapStyle,
|
|
34
|
+
disableKeyBoardAnimated = false,
|
|
26
35
|
}) => {
|
|
27
36
|
const useTwoButton = leftTitle && rightTitle;
|
|
37
|
+
const transY = useKeyboardAnimated();
|
|
38
|
+
const [keyboardAnim] = useState(new Animated.Value(0));
|
|
28
39
|
|
|
29
40
|
const RightButtonView = useMemo(() => {
|
|
30
41
|
return isPreventDoubleTouch ? PreventDoubleTouch : TouchableOpacity;
|
|
31
42
|
}, [isPreventDoubleTouch]);
|
|
32
43
|
|
|
44
|
+
useEffect(() => {
|
|
45
|
+
Animated.timing(keyboardAnim, {
|
|
46
|
+
toValue: transY,
|
|
47
|
+
duration: Platform.OS === 'ios' ? 220 : 0,
|
|
48
|
+
easing: Easing.linear(),
|
|
49
|
+
useNativeDriver: false,
|
|
50
|
+
}).start();
|
|
51
|
+
}, [keyboardAnim, transY]);
|
|
52
|
+
|
|
33
53
|
return (
|
|
34
|
-
<View
|
|
54
|
+
<Animated.View
|
|
55
|
+
style={[
|
|
56
|
+
styles.container,
|
|
57
|
+
wrapStyle,
|
|
58
|
+
!disableKeyBoardAnimated && { bottom: keyboardAnim },
|
|
59
|
+
]}
|
|
60
|
+
>
|
|
35
61
|
{leftTitle && (
|
|
36
62
|
<TouchableOpacity
|
|
37
63
|
style={[
|
|
@@ -76,7 +102,7 @@ const ViewButtonBottom = ({
|
|
|
76
102
|
</Text>
|
|
77
103
|
</RightButtonView>
|
|
78
104
|
)}
|
|
79
|
-
</View>
|
|
105
|
+
</Animated.View>
|
|
80
106
|
);
|
|
81
107
|
};
|
|
82
108
|
|
|
@@ -85,6 +111,8 @@ const styles = StyleSheet.create({
|
|
|
85
111
|
flexDirection: 'row',
|
|
86
112
|
justifyContent: 'center',
|
|
87
113
|
paddingHorizontal: 16,
|
|
114
|
+
position: 'absolute',
|
|
115
|
+
bottom: 0,
|
|
88
116
|
},
|
|
89
117
|
button: {
|
|
90
118
|
paddingVertical: 24,
|
package/src/configs/API.js
CHANGED
|
@@ -35,6 +35,8 @@ const API = {
|
|
|
35
35
|
`/property_manager/units/${id}/star_automate_scripts/`,
|
|
36
36
|
},
|
|
37
37
|
SUB_UNIT: {
|
|
38
|
+
END_DEVICES_STATUS: (stationId) =>
|
|
39
|
+
`/property_manager/iot_dashboard/stations_v2/${stationId}/end_devices_status/`,
|
|
38
40
|
REMOVE_SUB_UNIT: (unitId, id) =>
|
|
39
41
|
`/property_manager/${unitId}/sub_units/${id}/`,
|
|
40
42
|
CREATE_SUB_UNIT: (unitId) => `/property_manager/${unitId}/sub_units/`,
|
|
@@ -49,6 +51,7 @@ const API = {
|
|
|
49
51
|
ACCEPT_NEW_DEVICE: (id) =>
|
|
50
52
|
`/iot/modules/zigbee/chips/${id}/device_permit_to_join/`,
|
|
51
53
|
},
|
|
54
|
+
JSON_CONFIGURATION: '/chip_manager/chips/json_configuration/',
|
|
52
55
|
},
|
|
53
56
|
DEVICE: {
|
|
54
57
|
SENSOR_DETAIL: (id) => `/property_manager/devices/${id}/`,
|
|
@@ -212,6 +215,7 @@ const API = {
|
|
|
212
215
|
},
|
|
213
216
|
GATEWAY: {
|
|
214
217
|
LIST: () => '/chip_manager/developer_mode_chips/',
|
|
218
|
+
COUNT: () => '/chip_manager/developer_mode_chips/count/',
|
|
215
219
|
DETAIL: (id) => `/chip_manager/developer_mode_chips/${id}/`,
|
|
216
220
|
REBOOT: (id) => `/chip_manager/developer_mode_chips/${id}/reboot_chip/`,
|
|
217
221
|
},
|
|
@@ -238,6 +238,7 @@ export default {
|
|
|
238
238
|
BUTTON_EDIT_SCRIPT_ACTION: 'BUTTON_EDIT_SCRIPT_ACTION',
|
|
239
239
|
BUTTON_ADD_SCRIPT_ACTION: 'BUTTON_ADD_SCRIPT_ACTION',
|
|
240
240
|
ICON_CLOSE: 'ICON_CLOSE',
|
|
241
|
+
ICON_MORE: 'ICON_MORE',
|
|
241
242
|
ICON_ARROW_RIGHT: 'ICON_ARROW_RIGHT',
|
|
242
243
|
|
|
243
244
|
// Parking input maunaly spot
|
package/src/configs/BLE.js
CHANGED
|
@@ -36,11 +36,11 @@ export const Action = {
|
|
|
36
36
|
IS_CHECK_CLEAR_CACHE_UNITS: 'IS_CHECK_CLEAR_CACHE_UNITS',
|
|
37
37
|
DELETE_UNIT_SUCCESSFULLY: 'DELETE_UNIT_SUCCESSFULLY',
|
|
38
38
|
RESET_DELETE_UNIT_ACTION: 'RESET_DELETE_UNIT_ACTION',
|
|
39
|
-
// NOTE: DEV MODE
|
|
40
39
|
SET_WIDGET_DRAGGING: 'SET_WIDGET_DRAGGING',
|
|
41
40
|
SET_IS_EDITING_TEMPLATE: 'SET_IS_EDITING_TEMPLATE',
|
|
42
41
|
SET_IS_IN_EDIT_TEMPLATE_SCREEN: 'SET_IS_IN_EDIT_TEMPLATE_SCREEN',
|
|
43
42
|
LOGOUT: 'LOGOUT',
|
|
43
|
+
SET_APP_STATE_CHANGE: 'SET_APP_STATE_CHANGE',
|
|
44
44
|
};
|
|
45
45
|
|
|
46
46
|
export type AuthData = {
|
|
@@ -101,6 +101,7 @@ export type AppType = {
|
|
|
101
101
|
isLockWhenPickColor: boolean;
|
|
102
102
|
isNeedUpdateCache: boolean;
|
|
103
103
|
isDeleteUnitSuccessFully: boolean;
|
|
104
|
+
appState: string;
|
|
104
105
|
};
|
|
105
106
|
|
|
106
107
|
export type IoTType = {
|
package/src/context/mockStore.ts
CHANGED
package/src/context/reducer.ts
CHANGED
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
} from './actionType';
|
|
17
17
|
import _ from 'lodash';
|
|
18
18
|
import { STORAGE_KEY, removeMultiple } from '../utils/Storage.js';
|
|
19
|
+
import { AppState } from 'react-native';
|
|
19
20
|
|
|
20
21
|
export type ContextData = {
|
|
21
22
|
auth: AuthData;
|
|
@@ -71,6 +72,7 @@ export const initialState = {
|
|
|
71
72
|
isEmergencyPopupScreen: false,
|
|
72
73
|
isNeedUpdateCache: false,
|
|
73
74
|
isDeleteUnitSuccessFully: false,
|
|
75
|
+
appState: AppState.currentState,
|
|
74
76
|
},
|
|
75
77
|
iot: {
|
|
76
78
|
bluetooth: {
|
|
@@ -394,7 +396,7 @@ export const reducer = (currentState: ContextData, action: Action) => {
|
|
|
394
396
|
...currentState.iot,
|
|
395
397
|
internet: {
|
|
396
398
|
...currentState.iot.internet,
|
|
397
|
-
statuses: newStatuses,
|
|
399
|
+
statuses: JSON.parse(JSON.stringify(newStatuses)),
|
|
398
400
|
},
|
|
399
401
|
},
|
|
400
402
|
};
|
|
@@ -527,6 +529,15 @@ export const reducer = (currentState: ContextData, action: Action) => {
|
|
|
527
529
|
},
|
|
528
530
|
};
|
|
529
531
|
|
|
532
|
+
case Action.SET_APP_STATE_CHANGE:
|
|
533
|
+
return {
|
|
534
|
+
...currentState,
|
|
535
|
+
app: {
|
|
536
|
+
...currentState.app,
|
|
537
|
+
appState: payload,
|
|
538
|
+
},
|
|
539
|
+
};
|
|
540
|
+
|
|
530
541
|
default:
|
|
531
542
|
return currentState;
|
|
532
543
|
}
|
|
@@ -18,11 +18,17 @@ const useKeyboardAnimated = (tabHeight = 0) => {
|
|
|
18
18
|
);
|
|
19
19
|
|
|
20
20
|
useEffect(() => {
|
|
21
|
-
Keyboard.addListener(
|
|
22
|
-
|
|
21
|
+
const keyboardDidHide = Keyboard.addListener(
|
|
22
|
+
'keyboard' + action + 'Hide',
|
|
23
|
+
_keyboardWillHide
|
|
24
|
+
);
|
|
25
|
+
const keyboardDidShow = Keyboard.addListener(
|
|
26
|
+
'keyboard' + action + 'Show',
|
|
27
|
+
_keyboardWillShow
|
|
28
|
+
);
|
|
23
29
|
return () => {
|
|
24
|
-
|
|
25
|
-
|
|
30
|
+
keyboardDidHide?.remove();
|
|
31
|
+
keyboardDidShow?.remove();
|
|
26
32
|
};
|
|
27
33
|
}, [_keyboardWillHide, _keyboardWillShow, tabHeight]);
|
|
28
34
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { useContext, useCallback,
|
|
2
|
-
import {
|
|
1
|
+
import { useContext, useCallback, useEffect, useMemo } from 'react';
|
|
2
|
+
import { Platform } from 'react-native';
|
|
3
3
|
import { getSystemVersion } from 'react-native-device-info';
|
|
4
4
|
|
|
5
5
|
import { SCContext, useSCContextSelector } from '../../context';
|
|
@@ -20,7 +20,7 @@ const permissions = [
|
|
|
20
20
|
// NOTE: fnCallback is used for Lavida when found device
|
|
21
21
|
const useBluetoothConnection = (fnCallback) => {
|
|
22
22
|
const t = useTranslations();
|
|
23
|
-
const appState =
|
|
23
|
+
const appState = useSCContextSelector((state) => state.app.appState);
|
|
24
24
|
|
|
25
25
|
const { setAction } = useContext(SCContext);
|
|
26
26
|
const isEnabled = useSCContextSelector((state) => state.bluetooth.isEnabled);
|
|
@@ -51,33 +51,21 @@ const useBluetoothConnection = (fnCallback) => {
|
|
|
51
51
|
[onDeviceFound]
|
|
52
52
|
);
|
|
53
53
|
|
|
54
|
-
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
nextAppState === 'active'
|
|
61
|
-
) {
|
|
62
|
-
const results = await Permissions.checkMultiple(permissions);
|
|
63
|
-
const granted = Object.values(results).every((result) =>
|
|
64
|
-
[
|
|
65
|
-
Permissions.RESULTS.GRANTED,
|
|
66
|
-
Permissions.RESULTS.UNAVAILABLE,
|
|
67
|
-
].includes(result)
|
|
68
|
-
);
|
|
69
|
-
setAction(Action.SET_BLUETOOTH_PERM_GRANTED, granted);
|
|
70
|
-
}
|
|
71
|
-
appState.current = nextAppState;
|
|
72
|
-
}
|
|
54
|
+
const requestPermission = useCallback(async () => {
|
|
55
|
+
const results = await Permissions.checkMultiple(permissions);
|
|
56
|
+
const granted = Object.values(results).every((result) =>
|
|
57
|
+
[Permissions.RESULTS.GRANTED, Permissions.RESULTS.UNAVAILABLE].includes(
|
|
58
|
+
result
|
|
59
|
+
)
|
|
73
60
|
);
|
|
74
|
-
|
|
75
|
-
return () => {
|
|
76
|
-
subscription?.remove();
|
|
77
|
-
};
|
|
61
|
+
setAction(Action.SET_BLUETOOTH_PERM_GRANTED, granted);
|
|
78
62
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
79
63
|
}, []);
|
|
80
64
|
|
|
65
|
+
useEffect(() => {
|
|
66
|
+
appState === 'active' && requestPermission();
|
|
67
|
+
}, [appState, requestPermission]);
|
|
68
|
+
|
|
81
69
|
const requestPerm = useCallback(async () => {
|
|
82
70
|
let results = await Permissions.requestMultiple(permissions);
|
|
83
71
|
const blocked = Object.values(results).some(
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import mqtt from 'precompiled-mqtt/dist/mqtt.browser';
|
|
2
|
+
import { useState, useEffect, useMemo, useRef } from 'react';
|
|
3
|
+
|
|
4
|
+
import API from '../configs/API';
|
|
5
|
+
import { axiosGet } from '../utils/Apis/axios';
|
|
6
|
+
import { handleMqttMessage } from '../iot/mqtt';
|
|
7
|
+
|
|
8
|
+
const useChipJsonConfiguration = (dashboardId) => {
|
|
9
|
+
const [chips, setChips] = useState([]);
|
|
10
|
+
// get chip data
|
|
11
|
+
useEffect(() => {
|
|
12
|
+
const getGateways = async () => {
|
|
13
|
+
const { success, data } = await axiosGet(API.CHIP.JSON_CONFIGURATION, {
|
|
14
|
+
params: {
|
|
15
|
+
unit: dashboardId,
|
|
16
|
+
},
|
|
17
|
+
});
|
|
18
|
+
if (success) {
|
|
19
|
+
setChips(data);
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
getGateways();
|
|
23
|
+
}, [dashboardId]);
|
|
24
|
+
|
|
25
|
+
return {
|
|
26
|
+
chips,
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const connectChipMqtt = (chip) => {
|
|
31
|
+
const configById = {};
|
|
32
|
+
const { code, mqtt_server } = chip;
|
|
33
|
+
const { host, websocket_port } = mqtt_server;
|
|
34
|
+
|
|
35
|
+
const client = mqtt.connect(`wss://${host}:${websocket_port}`, {
|
|
36
|
+
username: code,
|
|
37
|
+
password: code,
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
(chip?.sensors).forEach((sensor) => {
|
|
41
|
+
(sensor?.configs).forEach((config) => {
|
|
42
|
+
configById[config.id] = config;
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
client.on('connect', () => {
|
|
47
|
+
client.subscribe(`eoh/chip/${code}/#`);
|
|
48
|
+
});
|
|
49
|
+
client.on('message', (topic, payload, packet) => {
|
|
50
|
+
const payloadObject = JSON.parse(payload.toString());
|
|
51
|
+
handleMqttMessage(topic, payloadObject, code, chip, configById);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
return client;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
export const useConnectChipMqtt = (chips) => {
|
|
58
|
+
const mqttChips = useMemo(
|
|
59
|
+
() => chips.filter((chip) => chip?.mqtt_server?.allow_frontend_connect),
|
|
60
|
+
[chips]
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
const mqttConfigs = useMemo(() => {
|
|
64
|
+
let configById = {};
|
|
65
|
+
|
|
66
|
+
mqttChips.forEach((chip) => {
|
|
67
|
+
chip?.sensors.forEach((sensor) => {
|
|
68
|
+
sensor?.configs.forEach((config) => {
|
|
69
|
+
configById[config.id] = config;
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
return configById;
|
|
75
|
+
}, [mqttChips]);
|
|
76
|
+
|
|
77
|
+
const mqttClientsRef = useRef([]);
|
|
78
|
+
useEffect(() => {
|
|
79
|
+
mqttChips.forEach((chip) => {
|
|
80
|
+
const client = connectChipMqtt(chip);
|
|
81
|
+
mqttClientsRef.current.push(client);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
return () => {
|
|
85
|
+
mqttClientsRef.current.forEach((client) => {
|
|
86
|
+
client && client.end();
|
|
87
|
+
});
|
|
88
|
+
mqttClientsRef.current = [];
|
|
89
|
+
};
|
|
90
|
+
}, [mqttChips]);
|
|
91
|
+
|
|
92
|
+
return { mqttConfigs };
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
export default useChipJsonConfiguration;
|
package/src/iot/Monitor.js
CHANGED
|
@@ -70,7 +70,8 @@ let waitWatchConfigTimerId = null;
|
|
|
70
70
|
let waitWatchConfigIds = [];
|
|
71
71
|
|
|
72
72
|
export const watchMultiConfigs = async (configIds) => {
|
|
73
|
-
|
|
73
|
+
const uniqueConfigIds = [...new Set(configIds)]?.filter(Boolean);
|
|
74
|
+
waitWatchConfigIds = waitWatchConfigIds.concat(uniqueConfigIds);
|
|
74
75
|
if (!waitWatchConfigTimerId) {
|
|
75
76
|
waitWatchConfigTimerId = setTimeout(async () => {
|
|
76
77
|
await realWatchMultiConfigs(waitWatchConfigIds);
|