@eohjsc/react-native-smart-city 0.3.81 → 0.3.82
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 +2 -2
- package/src/commons/ActionGroup/__test__/OnOffButtonTemplate.test.js +54 -12
- package/src/commons/Device/ProgressBar/__test__/ProgressBar.test.js +49 -0
- package/src/commons/Device/ProgressBar/index.js +45 -0
- package/src/commons/Device/ProgressBar/styles.js +33 -0
- package/src/commons/Popover/__test__/index.test.js +31 -0
- package/src/configs/Colors.js +1 -0
- package/src/configs/IOPinConstants.js +92 -165
- package/src/screens/AddNewDevice/__test__/AddNewDevice.test.js +17 -8
- package/src/screens/AddNewDevice/index.js +5 -4
- package/src/screens/AllGateway/DetailConfigActionInternal/__test__/index.test.js +1 -1
- package/src/screens/AllGateway/DetailConfigActionInternal/index.js +21 -23
- package/src/screens/AllGateway/DeviceInternalDetail/__test__/index.test.js +8 -6
- package/src/screens/AllGateway/DeviceInternalDetail/index.js +2 -0
- package/src/screens/AllGateway/hooks/useGateway.js +30 -2
- package/src/screens/Device/__test__/DetailHistoryChart.test.js +34 -21
- package/src/screens/Device/__test__/EmergencyCountdown.test.js +34 -0
- package/src/screens/Device/components/SensorDisplayItem.js +3 -0
- package/src/screens/EmergencySetting/__test__/index.test.js +22 -0
- package/src/screens/SyncLGDevice/AddLGDevice.js +5 -4
- package/src/screens/Unit/Detail.js +8 -7
- package/src/screens/Unit/hook/useFavorites.js +1 -1
- package/src/utils/Converter/__test__/timer.test.js +7 -0
- package/src/utils/Functions/preloadImages.js +1 -1
- package/src/utils/I18n/translations/en.json +1 -0
- package/src/utils/I18n/translations/vi.json +1 -0
- package/src/utils/Utils.js +18 -11
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.
|
|
4
|
+
"version": "0.3.82",
|
|
5
5
|
"description": "TODO",
|
|
6
6
|
"main": "index.js",
|
|
7
7
|
"files": [
|
|
@@ -162,7 +162,7 @@
|
|
|
162
162
|
"react-native-credit-card-input": "^0.4.1",
|
|
163
163
|
"react-native-dash": "^0.0.11",
|
|
164
164
|
"react-native-deep-linking": "^2.2.0",
|
|
165
|
-
"react-native-device-info": "^
|
|
165
|
+
"react-native-device-info": "^10.3.0",
|
|
166
166
|
"react-native-draggable-flatlist": "^2.6.2",
|
|
167
167
|
"react-native-fast-image": "^8.3.2",
|
|
168
168
|
"react-native-geocoder": "^0.5.0",
|
|
@@ -1,8 +1,16 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
+
import { TouchableOpacity } from 'react-native';
|
|
2
3
|
import { act, create } from 'react-test-renderer';
|
|
4
|
+
|
|
3
5
|
import OnOffTemplate from '../OnOffTemplate';
|
|
4
6
|
import { mockSCStore } from '../../../context/mockStore';
|
|
5
7
|
import { SCProvider } from '../../../context';
|
|
8
|
+
import Text from '../../Text';
|
|
9
|
+
import { useConfigGlobalState } from '../../../iot/states';
|
|
10
|
+
|
|
11
|
+
jest.mock('../../../iot/states', () => ({
|
|
12
|
+
useConfigGlobalState: jest.fn(),
|
|
13
|
+
}));
|
|
6
14
|
|
|
7
15
|
const wrapComponent = (actionGroup, mockDoAction) => (
|
|
8
16
|
<SCProvider initState={mockSCStore({})}>
|
|
@@ -49,45 +57,79 @@ describe('Test OneBigButtonTemplate', () => {
|
|
|
49
57
|
action_off_data: action_off_data,
|
|
50
58
|
icon_off: 'caret-up',
|
|
51
59
|
text_off: 'OFF',
|
|
60
|
+
config: 1,
|
|
52
61
|
},
|
|
62
|
+
template: 'on_off_button_action_template',
|
|
63
|
+
id: 1,
|
|
53
64
|
};
|
|
54
65
|
let wrapper;
|
|
55
66
|
|
|
56
|
-
|
|
67
|
+
beforeEach(() => {
|
|
68
|
+
useConfigGlobalState.mockClear();
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
const assertRender = async (textValue) => {
|
|
57
72
|
const mockDoAction = jest.fn();
|
|
58
73
|
|
|
59
74
|
await act(async () => {
|
|
60
75
|
wrapper = create(wrapComponent(actionGroup, mockDoAction));
|
|
61
76
|
});
|
|
62
77
|
|
|
63
|
-
const
|
|
64
|
-
|
|
65
|
-
expect(
|
|
66
|
-
expect(renderJson[1]?.type).toEqual('Modal');
|
|
78
|
+
const instance = wrapper.root;
|
|
79
|
+
const texts = instance.findAllByType(Text);
|
|
80
|
+
expect(texts[0].props.children).toEqual(textValue);
|
|
67
81
|
};
|
|
68
82
|
|
|
69
83
|
it('render state on', async () => {
|
|
70
|
-
|
|
84
|
+
useConfigGlobalState.mockImplementation(() => [
|
|
85
|
+
{
|
|
86
|
+
1: {
|
|
87
|
+
value: 1,
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
jest.fn(),
|
|
91
|
+
]);
|
|
92
|
+
await assertRender('ON');
|
|
71
93
|
});
|
|
72
94
|
|
|
73
95
|
it('render state off', async () => {
|
|
74
|
-
|
|
96
|
+
useConfigGlobalState.mockImplementation(() => [
|
|
97
|
+
{
|
|
98
|
+
1: {
|
|
99
|
+
value: 0,
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
jest.fn(),
|
|
103
|
+
]);
|
|
104
|
+
await assertRender('OFF');
|
|
75
105
|
});
|
|
76
106
|
|
|
77
|
-
const assertActionCall = async (state, action_data) => {
|
|
107
|
+
const assertActionCall = async (state, action_data, data2) => {
|
|
108
|
+
useConfigGlobalState.mockImplementation(() => [
|
|
109
|
+
{
|
|
110
|
+
1: {
|
|
111
|
+
value: state,
|
|
112
|
+
},
|
|
113
|
+
},
|
|
114
|
+
jest.fn(),
|
|
115
|
+
]);
|
|
78
116
|
const mockDoAction = jest.fn();
|
|
79
117
|
await act(async () => {
|
|
80
118
|
wrapper = create(wrapComponent(actionGroup, mockDoAction));
|
|
81
119
|
});
|
|
82
|
-
|
|
83
|
-
|
|
120
|
+
const instance = wrapper.root;
|
|
121
|
+
const button = instance.findByType(TouchableOpacity);
|
|
122
|
+
await act(async () => {
|
|
123
|
+
await button.props.onPress();
|
|
124
|
+
});
|
|
125
|
+
expect(mockDoAction).toHaveBeenCalledWith(action_data, data2);
|
|
84
126
|
};
|
|
85
127
|
|
|
86
128
|
it('action state on', async () => {
|
|
87
|
-
await assertActionCall(true,
|
|
129
|
+
await assertActionCall(true, action_on_data, { state: 0, value: 0 });
|
|
88
130
|
});
|
|
89
131
|
|
|
90
132
|
it('action state off', async () => {
|
|
91
|
-
await assertActionCall(false,
|
|
133
|
+
await assertActionCall(false, action_off_data, { state: 1, value: 1 });
|
|
92
134
|
});
|
|
93
135
|
});
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/* eslint-disable promise/prefer-await-to-callbacks */
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { useSelector } from 'react-redux';
|
|
4
|
+
import renderer, { act } from 'react-test-renderer';
|
|
5
|
+
|
|
6
|
+
import ProgressBar from '..';
|
|
7
|
+
import Text from '../../../Text';
|
|
8
|
+
import { SCProvider } from '../../../../context';
|
|
9
|
+
import { mockSCStore } from '../../../../context/mockStore';
|
|
10
|
+
|
|
11
|
+
jest.mock('react-redux', () => ({
|
|
12
|
+
...jest.requireActual('react-redux'),
|
|
13
|
+
useSelector: jest.fn(),
|
|
14
|
+
}));
|
|
15
|
+
|
|
16
|
+
const wrapComponent = (item, data) => (
|
|
17
|
+
<SCProvider initState={mockSCStore({})}>
|
|
18
|
+
<ProgressBar item={item} data={data} />
|
|
19
|
+
</SCProvider>
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
describe('Test ProgressBar', () => {
|
|
23
|
+
let tree;
|
|
24
|
+
beforeEach(() => {
|
|
25
|
+
const localState = {
|
|
26
|
+
language: 'en',
|
|
27
|
+
};
|
|
28
|
+
useSelector.mockImplementation((cb) => {
|
|
29
|
+
return cb(localState);
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('render ProgressBar', async () => {
|
|
34
|
+
const item = {
|
|
35
|
+
label: 'Value bar',
|
|
36
|
+
configuration: { max_value: 100 },
|
|
37
|
+
value: 8,
|
|
38
|
+
};
|
|
39
|
+
const data = [{ value: 10 }];
|
|
40
|
+
await act(async () => {
|
|
41
|
+
tree = renderer.create(wrapComponent(item, data));
|
|
42
|
+
});
|
|
43
|
+
const instance = tree.root;
|
|
44
|
+
const texts = instance.findAllByType(Text);
|
|
45
|
+
expect(texts[0].props.children).toBe('Value bar');
|
|
46
|
+
expect(texts[1].props.children).toEqual(['Max value', ': ', 100]);
|
|
47
|
+
expect(texts[2].props.children).toEqual([10, '%']);
|
|
48
|
+
});
|
|
49
|
+
});
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import React, { memo, useMemo } from 'react';
|
|
2
|
+
import { View } from 'react-native';
|
|
3
|
+
import * as Progress from 'react-native-progress';
|
|
4
|
+
|
|
5
|
+
import { Colors } from '../../../configs';
|
|
6
|
+
import { useTranslations } from '../../../hooks/Common/useTranslations';
|
|
7
|
+
import Text from '../../Text';
|
|
8
|
+
import styles from './styles';
|
|
9
|
+
|
|
10
|
+
const ProgressBar = memo(({ data = [], item }) => {
|
|
11
|
+
const t = useTranslations();
|
|
12
|
+
|
|
13
|
+
const maxValue = useMemo(() => {
|
|
14
|
+
return Number(item?.configuration?.max_value) || 60;
|
|
15
|
+
}, [item?.configuration?.max_value]);
|
|
16
|
+
const value = data.length ? data[0].value : 0;
|
|
17
|
+
const percent = value / maxValue; // a number between 0 and 1
|
|
18
|
+
|
|
19
|
+
return (
|
|
20
|
+
<View style={styles.container}>
|
|
21
|
+
<Text size={16} style={styles.textLabel}>
|
|
22
|
+
{item?.label || 'Label'}
|
|
23
|
+
</Text>
|
|
24
|
+
|
|
25
|
+
<Text size={16} style={styles.textMaxValue}>
|
|
26
|
+
{t('max_value')}: {maxValue}
|
|
27
|
+
</Text>
|
|
28
|
+
|
|
29
|
+
<View style={styles.wrapProgressBar}>
|
|
30
|
+
<Progress.Bar
|
|
31
|
+
progress={percent}
|
|
32
|
+
height={40}
|
|
33
|
+
width={200}
|
|
34
|
+
color={Colors.Blue16}
|
|
35
|
+
unfilledColor={Colors.Blue15}
|
|
36
|
+
borderWidth={0}
|
|
37
|
+
borderRadius={10}
|
|
38
|
+
/>
|
|
39
|
+
<Text style={styles.textValue}>{value}%</Text>
|
|
40
|
+
</View>
|
|
41
|
+
</View>
|
|
42
|
+
);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
export default ProgressBar;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { StyleSheet } from 'react-native';
|
|
2
|
+
|
|
3
|
+
import { Colors } from '../../../configs';
|
|
4
|
+
|
|
5
|
+
export default StyleSheet.create({
|
|
6
|
+
container: {
|
|
7
|
+
flex: 1,
|
|
8
|
+
flexDirection: 'column', //column direction
|
|
9
|
+
marginRight: 60,
|
|
10
|
+
marginLeft: 60,
|
|
11
|
+
borderWidth: 1,
|
|
12
|
+
padding: 12,
|
|
13
|
+
borderRadius: 8,
|
|
14
|
+
borderColor: Colors.Gray4,
|
|
15
|
+
},
|
|
16
|
+
textLabel: {
|
|
17
|
+
paddingHorizontal: 16,
|
|
18
|
+
paddingTop: 8,
|
|
19
|
+
},
|
|
20
|
+
textMaxValue: {
|
|
21
|
+
textAlign: 'right',
|
|
22
|
+
},
|
|
23
|
+
wrapProgressBar: {
|
|
24
|
+
flex: 1,
|
|
25
|
+
flexDirection: 'row',
|
|
26
|
+
justifyContent: 'center',
|
|
27
|
+
alignItems: 'center',
|
|
28
|
+
padding: 8,
|
|
29
|
+
},
|
|
30
|
+
textValue: {
|
|
31
|
+
marginLeft: 8,
|
|
32
|
+
},
|
|
33
|
+
});
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { create, act } from 'react-test-renderer';
|
|
3
|
+
import { SCProvider } from '../../../context';
|
|
4
|
+
import { mockSCStore } from '../../../context/mockStore';
|
|
5
|
+
import PopoverComponent from '../index';
|
|
6
|
+
import Popover from 'react-native-popover-view';
|
|
7
|
+
|
|
8
|
+
const wrapComponent = (data) => (
|
|
9
|
+
<SCProvider initState={mockSCStore({})}>
|
|
10
|
+
<PopoverComponent {...data} />
|
|
11
|
+
</SCProvider>
|
|
12
|
+
);
|
|
13
|
+
const mockOnCloseComplete = jest.fn();
|
|
14
|
+
describe('Test PopoverComponent', () => {
|
|
15
|
+
let tree;
|
|
16
|
+
|
|
17
|
+
it('Test Popover', async () => {
|
|
18
|
+
await act(async () => {
|
|
19
|
+
tree = await create(
|
|
20
|
+
wrapComponent({ isVisible: true, onCloseComplete: mockOnCloseComplete })
|
|
21
|
+
);
|
|
22
|
+
});
|
|
23
|
+
const instance = tree.root;
|
|
24
|
+
const popover = instance.findByType(Popover);
|
|
25
|
+
await act(async () => {
|
|
26
|
+
popover.props.onCloseComplete();
|
|
27
|
+
});
|
|
28
|
+
expect(popover.props.isVisible).toEqual(true);
|
|
29
|
+
expect(mockOnCloseComplete).toBeCalled();
|
|
30
|
+
});
|
|
31
|
+
});
|
package/src/configs/Colors.js
CHANGED
|
@@ -1,26 +1,29 @@
|
|
|
1
1
|
export const INTERNAL_DEVICE = {
|
|
2
|
+
ESP8266: 'esp8266',
|
|
2
3
|
ESP32: 'esp32',
|
|
3
4
|
STM32: 'stm32',
|
|
5
|
+
STM32_WIFI: 'stm32_wifi',
|
|
4
6
|
RASPBERRY_PI: 'raspberry_pi',
|
|
5
7
|
};
|
|
6
8
|
|
|
7
9
|
export const TYPE_BOARD = [
|
|
10
|
+
{ value: INTERNAL_DEVICE.ESP8266, text: 'ESP8266' },
|
|
8
11
|
{ value: INTERNAL_DEVICE.ESP32, text: 'ESP32' },
|
|
9
|
-
{ value: INTERNAL_DEVICE.STM32, text: 'STM32' },
|
|
12
|
+
{ value: INTERNAL_DEVICE.STM32, text: 'STM32 - Gsm' },
|
|
13
|
+
{ value: INTERNAL_DEVICE.STM32_WIFI, text: 'STM32 - Wifi' },
|
|
10
14
|
{ value: INTERNAL_DEVICE.RASPBERRY_PI, text: 'Raspberry Pi' },
|
|
11
15
|
];
|
|
12
16
|
|
|
13
17
|
// DEFAULT
|
|
14
18
|
|
|
15
|
-
export const TYPE_PIN_MODE = {
|
|
16
|
-
OUTPUT: 'output',
|
|
17
|
-
INPUT: 'input',
|
|
18
|
-
};
|
|
19
19
|
export const ARDUINO_PIN_CONFIG_TYPE = [
|
|
20
20
|
{ key: 'boolean', value: 'Digital' },
|
|
21
21
|
{ key: 'integer', value: 'Analog' },
|
|
22
22
|
];
|
|
23
23
|
|
|
24
|
+
export const VIRTUAL_TYPE = 'virtual';
|
|
25
|
+
export const VIRTUAL_CONFIG_TYPE = [{ key: VIRTUAL_TYPE, value: 'Virtual' }];
|
|
26
|
+
|
|
24
27
|
export const READ_DIGITAL_PIN_MODE = [
|
|
25
28
|
{ key: 'input', value: 'Input' },
|
|
26
29
|
{ key: 'pull_up', value: 'Pullup' },
|
|
@@ -36,10 +39,46 @@ export const WRITE_DIGITAL_PIN_MODE = [
|
|
|
36
39
|
|
|
37
40
|
export const WRITE_ANALOG_PIN_MODE = [{ key: 'pwm', value: 'PWM' }];
|
|
38
41
|
|
|
42
|
+
// VIRTUAL_PIN
|
|
43
|
+
export const VIRTUAL_PIN = new Array(256).fill().map((e, i) => {
|
|
44
|
+
return { key: i.toString(), value: `V${i}` };
|
|
45
|
+
});
|
|
46
|
+
|
|
39
47
|
export const CONFIG_IS_WRITE_IS_READ_PIN_MODE = [
|
|
40
48
|
{ key: 'output', value: 'Input' },
|
|
41
49
|
];
|
|
42
50
|
|
|
51
|
+
// ESP8266
|
|
52
|
+
|
|
53
|
+
export const ESP8266_DIGITAL = [
|
|
54
|
+
{ key: '0', value: 'GPIO 0 - D3' },
|
|
55
|
+
{ key: '1', value: 'GPIO 1 - D10 - UART 0 TX' },
|
|
56
|
+
{ key: '2', value: 'GPIO 2 - D4 - UART 0 TX2' },
|
|
57
|
+
{ key: '3', value: 'GPIO 3 - D9 - UART 0 RX' },
|
|
58
|
+
{ key: '4', value: 'GPIO 4 - D2' },
|
|
59
|
+
{ key: '5', value: 'GPIO 5 - D1' },
|
|
60
|
+
{ key: '12', value: 'GPIO 12 - D6' },
|
|
61
|
+
{ key: '13', value: 'GPIO 13 - D7 - UART 1 RX' },
|
|
62
|
+
{ key: '14', value: 'GPIO 14 - D5' },
|
|
63
|
+
{ key: '15', value: 'GPIO 15 - D8 - UART 1 TX' },
|
|
64
|
+
{ key: '16', value: 'GPIO 16 - D0' },
|
|
65
|
+
];
|
|
66
|
+
|
|
67
|
+
export const ESP8266_ANALOG_READ = [{ key: '17', value: 'GPIO 17 - A0' }];
|
|
68
|
+
|
|
69
|
+
export const ESP8266_ANALOG_WRITE = [
|
|
70
|
+
{ key: '0', value: 'GPIO 0 - D3' },
|
|
71
|
+
{ key: '1', value: 'GPIO 1 - D10 - UART 0 TX' },
|
|
72
|
+
{ key: '2', value: 'GPIO 2 - D4 - UART 0 TX2' },
|
|
73
|
+
{ key: '3', value: 'GPIO 3 - D9 - UART 0 RX' },
|
|
74
|
+
{ key: '4', value: 'GPIO 4 - D2' },
|
|
75
|
+
{ key: '5', value: 'GPIO 5 - D1' },
|
|
76
|
+
{ key: '12', value: 'GPIO 12 - D6' },
|
|
77
|
+
{ key: '13', value: 'GPIO 13 - D7 - UART 1 RX' },
|
|
78
|
+
{ key: '14', value: 'GPIO 14 - D5' },
|
|
79
|
+
{ key: '15', value: 'GPIO 15 - D8 - UART 1 TX' },
|
|
80
|
+
];
|
|
81
|
+
|
|
43
82
|
// ESP32
|
|
44
83
|
|
|
45
84
|
export const ESP32_DIGITAL = [
|
|
@@ -146,172 +185,60 @@ export const RASPBERRY_PI_ANALOG_WRITE = [
|
|
|
146
185
|
{ key: '19', value: 'GPIO 19 - PWM 1' },
|
|
147
186
|
];
|
|
148
187
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
{ key:
|
|
164
|
-
{ key:
|
|
165
|
-
{ key:
|
|
166
|
-
{ key: '16', value: 'PC12' },
|
|
167
|
-
{ key: '17', value: 'PC11' },
|
|
168
|
-
{ key: '18', value: 'PC10' },
|
|
169
|
-
{ key: '19', value: 'PA15' },
|
|
170
|
-
{ key: '20', value: 'PA12' },
|
|
171
|
-
{ key: '21', value: 'PA11' },
|
|
172
|
-
{ key: '22', value: 'PA10' },
|
|
173
|
-
{ key: '23', value: 'PA9' },
|
|
174
|
-
{ key: '24', value: 'PA8' },
|
|
175
|
-
{ key: '25', value: 'PC9' },
|
|
176
|
-
{ key: '26', value: 'PC8' },
|
|
177
|
-
{ key: '27', value: 'PC7' },
|
|
178
|
-
{ key: '28', value: 'PC6' },
|
|
179
|
-
{ key: '29', value: 'PD15' },
|
|
180
|
-
{ key: '30', value: 'PD14' },
|
|
181
|
-
{ key: '31', value: 'PD13' },
|
|
182
|
-
{ key: '32', value: 'PD12' },
|
|
183
|
-
{ key: '33', value: 'PD11' },
|
|
184
|
-
{ key: '34', value: 'PD10' },
|
|
185
|
-
{ key: '35', value: 'PD9' },
|
|
186
|
-
{ key: '36', value: 'PD8' },
|
|
187
|
-
{ key: '37', value: 'PB15' },
|
|
188
|
-
{ key: '38', value: 'PE2' },
|
|
189
|
-
{ key: '39', value: 'PE3' },
|
|
190
|
-
{ key: '40', value: 'PE4' },
|
|
191
|
-
{ key: '41', value: 'PE5' },
|
|
192
|
-
{ key: '42', value: 'PE6' },
|
|
193
|
-
{ key: '43', value: 'PC13' },
|
|
194
|
-
{ key: '44', value: 'PC0' },
|
|
195
|
-
{ key: '45', value: 'PC1' },
|
|
196
|
-
{ key: '46', value: 'PC2' },
|
|
197
|
-
{ key: '47', value: 'PC3' },
|
|
198
|
-
{ key: '48', value: 'PA0' },
|
|
199
|
-
{ key: '49', value: 'PA1' },
|
|
200
|
-
{ key: '50', value: 'PA2' },
|
|
201
|
-
{ key: '51', value: 'PA3' },
|
|
202
|
-
{ key: '52', value: 'PA4' },
|
|
203
|
-
{ key: '53', value: 'PA5' },
|
|
204
|
-
{ key: '54', value: 'PC4' },
|
|
205
|
-
{ key: '55', value: 'PC5' },
|
|
206
|
-
{ key: '56', value: 'PB0' },
|
|
207
|
-
{ key: '57', value: 'PB1' },
|
|
208
|
-
{ key: '58', value: 'PA6' },
|
|
209
|
-
{ key: '59', value: 'PA7' },
|
|
210
|
-
{ key: '60', value: 'PE7' },
|
|
211
|
-
{ key: '61', value: 'PE8' },
|
|
212
|
-
{ key: '62', value: 'PE9' },
|
|
213
|
-
{ key: '63', value: 'PE10' },
|
|
214
|
-
{ key: '64', value: 'PE11' },
|
|
215
|
-
{ key: '65', value: 'PE12' },
|
|
216
|
-
{ key: '66', value: 'PE13' },
|
|
217
|
-
{ key: '67', value: 'PE14' },
|
|
218
|
-
{ key: '68', value: 'PE15' },
|
|
219
|
-
{ key: '69', value: 'PB10' },
|
|
220
|
-
{ key: '70', value: 'PB11' },
|
|
221
|
-
{ key: '71', value: 'PB12' },
|
|
222
|
-
{ key: '72', value: 'PB13' },
|
|
223
|
-
{ key: '73', value: 'PB14' },
|
|
224
|
-
{ key: '74', value: 'PB4' },
|
|
225
|
-
];
|
|
226
|
-
export const STM32_ANALOG_READ = [
|
|
227
|
-
{ key: '44', value: 'PCO-A0' },
|
|
228
|
-
{ key: '45', value: 'PC1-A1' },
|
|
229
|
-
{ key: '46', value: 'PC2-A2' },
|
|
230
|
-
{ key: '47', value: 'PC3-A3' },
|
|
231
|
-
{ key: '48', value: 'PA0-A4' },
|
|
232
|
-
{ key: '49', value: 'PA1-A5' },
|
|
233
|
-
{ key: '50', value: 'PA2-A6' },
|
|
234
|
-
{ key: '51', value: 'PA3-A7' },
|
|
235
|
-
{ key: '52', value: 'PA4-A8' },
|
|
236
|
-
{ key: '53', value: 'PA5-A9' },
|
|
237
|
-
{ key: '54', value: 'PC4-A10' },
|
|
238
|
-
{ key: '55', value: 'PC5-A11' },
|
|
239
|
-
{ key: '56', value: 'PB0-A12' },
|
|
240
|
-
{ key: '57', value: 'PB1-A13' },
|
|
241
|
-
{ key: '58', value: 'PA6-A14' },
|
|
242
|
-
{ key: '59', value: 'PA7-A15' },
|
|
188
|
+
// STM32
|
|
189
|
+
|
|
190
|
+
export const APN = {
|
|
191
|
+
VIETTEL: 'viettel',
|
|
192
|
+
MOBIPHONE: 'mobiphone',
|
|
193
|
+
VINAPHONE: 'vinaphone',
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
export const MODEM = {
|
|
197
|
+
SIM800: 'sim800',
|
|
198
|
+
SIM900: 'sim900',
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
export const STM32_APN = [
|
|
202
|
+
{ key: APN.VIETTEL, value: 'APN_VIETTEL' },
|
|
203
|
+
{ key: APN.MOBIPHONE, value: 'APN_MOBIPHONE' },
|
|
204
|
+
{ key: APN.VINAPHONE, value: 'APN_VINAPHONE' },
|
|
243
205
|
];
|
|
244
|
-
|
|
245
|
-
export const
|
|
246
|
-
{ key:
|
|
247
|
-
{ key:
|
|
248
|
-
{ key: '4', value: 'PB7' },
|
|
249
|
-
{ key: '5', value: 'PB6' },
|
|
250
|
-
{ key: '6', value: 'PB5' },
|
|
251
|
-
{ key: '7', value: 'PB3' },
|
|
252
|
-
{ key: '19', value: 'PA15' },
|
|
253
|
-
{ key: '21', value: 'PA11' },
|
|
254
|
-
{ key: '22', value: 'PA10' },
|
|
255
|
-
{ key: '23', value: 'PA9' },
|
|
256
|
-
{ key: '24', value: 'PA8' },
|
|
257
|
-
{ key: '25', value: 'PC8' },
|
|
258
|
-
{ key: '26', value: 'PC8' },
|
|
259
|
-
{ key: '27', value: 'PC7' },
|
|
260
|
-
{ key: '28', value: 'PC6' },
|
|
261
|
-
{ key: '29', value: 'PD15' },
|
|
262
|
-
{ key: '30', value: 'PD14' },
|
|
263
|
-
{ key: '31', value: 'PD13' },
|
|
264
|
-
{ key: '32', value: 'PD12' },
|
|
265
|
-
{ key: '37', value: 'PB15' },
|
|
266
|
-
{ key: '41', value: 'PE5' },
|
|
267
|
-
{ key: '42', value: 'PE6' },
|
|
268
|
-
{ key: '48', value: 'PA0' },
|
|
269
|
-
{ key: '49', value: 'PA1' },
|
|
270
|
-
{ key: '50', value: 'PA2' },
|
|
271
|
-
{ key: '51', value: 'PA3' },
|
|
272
|
-
{ key: '53', value: 'PA5' },
|
|
273
|
-
{ key: '58', value: 'PA6' },
|
|
274
|
-
{ key: '59', value: 'PA7' },
|
|
275
|
-
{ key: '61', value: 'PE9' },
|
|
276
|
-
{ key: '62', value: 'PE9' },
|
|
277
|
-
{ key: '63', value: 'PE10' },
|
|
278
|
-
{ key: '64', value: 'PE11' },
|
|
279
|
-
{ key: '65', value: 'PE12' },
|
|
280
|
-
{ key: '66', value: 'PE13' },
|
|
281
|
-
{ key: '67', value: 'PE14' },
|
|
282
|
-
{ key: '72', value: 'PB13' },
|
|
283
|
-
{ key: '73', value: 'PB14' },
|
|
284
|
-
{ key: '74', value: 'PB4' },
|
|
206
|
+
|
|
207
|
+
export const STM32_MODEM = [
|
|
208
|
+
{ key: MODEM.SIM800, value: 'TINY_GSM_MODEM_SIM800' },
|
|
209
|
+
{ key: MODEM.SIM900, value: 'TINY_GSM_MODEM_SIM900' },
|
|
285
210
|
];
|
|
286
211
|
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
212
|
+
// VIRTUAL_PIN
|
|
213
|
+
const STM32_ALPHA = [
|
|
214
|
+
'A',
|
|
215
|
+
'B',
|
|
216
|
+
'C',
|
|
217
|
+
'D',
|
|
218
|
+
'E',
|
|
219
|
+
'F',
|
|
220
|
+
'G',
|
|
221
|
+
'H',
|
|
222
|
+
'I',
|
|
223
|
+
'J',
|
|
224
|
+
'K',
|
|
225
|
+
'Z',
|
|
299
226
|
];
|
|
300
227
|
|
|
301
|
-
|
|
228
|
+
let stm32Index = 0;
|
|
302
229
|
|
|
303
|
-
export const
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
{ key:
|
|
314
|
-
|
|
230
|
+
export const STM32_PIN = new Array(192).fill().map((e, i) => {
|
|
231
|
+
const nextPart = (stm32Index + 1) * 15 + stm32Index + 1;
|
|
232
|
+
|
|
233
|
+
if (i === nextPart) {
|
|
234
|
+
stm32Index += 1;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
const index = i - stm32Index * 15 - stm32Index;
|
|
238
|
+
const prefix = STM32_ALPHA[stm32Index];
|
|
239
|
+
|
|
240
|
+
return { key: i.toString(), value: `P${prefix}${index}` };
|
|
241
|
+
});
|
|
315
242
|
|
|
316
243
|
export const LANGUAGE = {
|
|
317
244
|
DEFAULT: 'vi',
|
|
@@ -49,7 +49,7 @@ describe('Test AddNewDevice', () => {
|
|
|
49
49
|
unit_id: 1,
|
|
50
50
|
},
|
|
51
51
|
};
|
|
52
|
-
mock.
|
|
52
|
+
mock.reset();
|
|
53
53
|
});
|
|
54
54
|
|
|
55
55
|
const getText = (instance, id) => {
|
|
@@ -96,6 +96,21 @@ describe('Test AddNewDevice', () => {
|
|
|
96
96
|
]);
|
|
97
97
|
});
|
|
98
98
|
|
|
99
|
+
it('fetchDetails no stations', async () => {
|
|
100
|
+
mock.onGet(API.UNIT.UNIT_DETAIL(1)).reply(200, {
|
|
101
|
+
id: 1,
|
|
102
|
+
name: 'Unit name',
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
await act(async () => {
|
|
106
|
+
tree = await create(wrapComponent(route));
|
|
107
|
+
});
|
|
108
|
+
const instance = tree.root;
|
|
109
|
+
|
|
110
|
+
const groupCheckBox = instance.findByType(GroupCheckBox);
|
|
111
|
+
expect(groupCheckBox.props.data).toEqual([]);
|
|
112
|
+
});
|
|
113
|
+
|
|
99
114
|
it('fetchDetails fail', async () => {
|
|
100
115
|
mock.onGet(API.UNIT.UNIT_DETAIL(1)).reply(500);
|
|
101
116
|
await act(async () => {
|
|
@@ -103,13 +118,7 @@ describe('Test AddNewDevice', () => {
|
|
|
103
118
|
});
|
|
104
119
|
const instance = tree.root;
|
|
105
120
|
const groupCheckBox = instance.findByType(GroupCheckBox);
|
|
106
|
-
expect(groupCheckBox.props.data).toEqual([
|
|
107
|
-
{
|
|
108
|
-
id: 2,
|
|
109
|
-
name: 'Station name',
|
|
110
|
-
title: 'Station name',
|
|
111
|
-
},
|
|
112
|
-
]);
|
|
121
|
+
expect(groupCheckBox.props.data).toEqual([]);
|
|
113
122
|
});
|
|
114
123
|
|
|
115
124
|
it('ViewButtonBottom', async () => {
|
|
@@ -43,10 +43,11 @@ const AddNewDevice = memo(({ route }) => {
|
|
|
43
43
|
});
|
|
44
44
|
}, [navigate, stationId, unit.id, unit.name]);
|
|
45
45
|
|
|
46
|
-
const stations =
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
46
|
+
const stations =
|
|
47
|
+
unit?.stations?.map((item) => ({
|
|
48
|
+
...item,
|
|
49
|
+
title: item.name,
|
|
50
|
+
})) || [];
|
|
50
51
|
|
|
51
52
|
const handleOnSelect = (itemSelect) => {
|
|
52
53
|
if (itemSelect.id !== stationId) {
|
|
@@ -5,7 +5,7 @@ import { useRoute } from '@react-navigation/native';
|
|
|
5
5
|
import styles from './styles';
|
|
6
6
|
import DetailConfigAction from '../components/DetailConfigAction';
|
|
7
7
|
import { PIN_MAPPING, PIN_MODE_MAPPING } from '../../../utils/Utils';
|
|
8
|
-
import { INTERNAL_DEVICE } from '../../../configs/IOPinConstants';
|
|
8
|
+
import { INTERNAL_DEVICE, VIRTUAL_PIN } from '../../../configs/IOPinConstants';
|
|
9
9
|
|
|
10
10
|
const DetailConfigActionInternal = () => {
|
|
11
11
|
const { params = {} } = useRoute();
|
|
@@ -17,35 +17,33 @@ const DetailConfigActionInternal = () => {
|
|
|
17
17
|
isAction,
|
|
18
18
|
isInternal,
|
|
19
19
|
writeConfigPins,
|
|
20
|
+
virtualConfigPins,
|
|
20
21
|
board,
|
|
21
22
|
} = params;
|
|
22
23
|
|
|
23
24
|
const readOrWrite = isConfigRead ? 'read' : 'write';
|
|
24
25
|
|
|
25
|
-
const
|
|
26
|
+
const formatPin = useMemo(() => {
|
|
26
27
|
if (!Object.values(INTERNAL_DEVICE)?.includes(board)) {
|
|
27
28
|
return [];
|
|
28
29
|
}
|
|
29
30
|
const writePin = PIN_MAPPING[board].write;
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
return writeConfigPinsUsed?.find((el) => el.key === itemActionConfig?.pin)
|
|
47
|
-
?.value;
|
|
48
|
-
}, [board, itemActionConfig?.pin, writeConfigPins]);
|
|
31
|
+
const allWritePins = writePin.boolean.concat(writePin.integer);
|
|
32
|
+
const allWriteVirtualPins = VIRTUAL_PIN;
|
|
33
|
+
|
|
34
|
+
const formatWritePIN = writeConfigPins?.map((item) => ({
|
|
35
|
+
key: item.id,
|
|
36
|
+
value: allWritePins.find((el) => el?.key === item?.pin_number)?.value,
|
|
37
|
+
}));
|
|
38
|
+
const formatVirtualPIN = virtualConfigPins?.map((item) => ({
|
|
39
|
+
key: item.id,
|
|
40
|
+
value: allWriteVirtualPins.find((el) => el?.key === item?.pin_number)
|
|
41
|
+
?.value,
|
|
42
|
+
}));
|
|
43
|
+
return formatWritePIN
|
|
44
|
+
?.concat(formatVirtualPIN)
|
|
45
|
+
?.find((el) => el?.key === itemActionConfig?.pin)?.value;
|
|
46
|
+
}, [board, virtualConfigPins, writeConfigPins, itemActionConfig]);
|
|
49
47
|
|
|
50
48
|
const listPINs = useCallback(() => {
|
|
51
49
|
return PIN_MAPPING[board][readOrWrite][itemActionConfig?.value_type];
|
|
@@ -121,7 +119,7 @@ const DetailConfigActionInternal = () => {
|
|
|
121
119
|
{
|
|
122
120
|
id: 2,
|
|
123
121
|
title: 'pin_number',
|
|
124
|
-
data: (isInternal ?
|
|
122
|
+
data: (isInternal ? formatPin : itemActionConfig?.pin) || '0',
|
|
125
123
|
},
|
|
126
124
|
{
|
|
127
125
|
id: 3,
|
|
@@ -133,8 +131,8 @@ const DetailConfigActionInternal = () => {
|
|
|
133
131
|
return [];
|
|
134
132
|
}, [
|
|
135
133
|
formatPIN,
|
|
134
|
+
formatPin,
|
|
136
135
|
formatPinMode,
|
|
137
|
-
formatWritePIN,
|
|
138
136
|
isAction,
|
|
139
137
|
isConfigRead,
|
|
140
138
|
isConfigWrite,
|
|
@@ -91,14 +91,14 @@ describe('Test DeviceInternalDetail', () => {
|
|
|
91
91
|
id: 1,
|
|
92
92
|
config: { name: 'config1', is_write: false, is_read: true },
|
|
93
93
|
value_type: 'boolean',
|
|
94
|
-
pin_number: 0,
|
|
94
|
+
pin_number: '0',
|
|
95
95
|
pin_mode: 'input',
|
|
96
96
|
},
|
|
97
97
|
{
|
|
98
98
|
id: 2,
|
|
99
99
|
config: { name: 'config2', is_write: false, is_read: true },
|
|
100
100
|
value_type: 'integer',
|
|
101
|
-
pin_number: 0,
|
|
101
|
+
pin_number: '0',
|
|
102
102
|
pin_mode: 'input',
|
|
103
103
|
},
|
|
104
104
|
];
|
|
@@ -107,14 +107,14 @@ describe('Test DeviceInternalDetail', () => {
|
|
|
107
107
|
id: 1,
|
|
108
108
|
config: { name: 'config1', is_write: true, is_read: false },
|
|
109
109
|
value_type: 'boolean',
|
|
110
|
-
pin_number: 0,
|
|
110
|
+
pin_number: '0',
|
|
111
111
|
pin_mode: 'output',
|
|
112
112
|
},
|
|
113
113
|
{
|
|
114
114
|
id: 2,
|
|
115
115
|
config: { name: 'config2', is_write: true, is_read: true },
|
|
116
116
|
value_type: 'integer',
|
|
117
|
-
pin_number: 0,
|
|
117
|
+
pin_number: '0',
|
|
118
118
|
pin_mode: 'output',
|
|
119
119
|
},
|
|
120
120
|
];
|
|
@@ -289,6 +289,7 @@ describe('Test DeviceInternalDetail', () => {
|
|
|
289
289
|
itemActionConfig: 1,
|
|
290
290
|
isInternal: true,
|
|
291
291
|
board: undefined,
|
|
292
|
+
virtualConfigPins: [],
|
|
292
293
|
writeConfigPins: [
|
|
293
294
|
{
|
|
294
295
|
config: {
|
|
@@ -298,7 +299,7 @@ describe('Test DeviceInternalDetail', () => {
|
|
|
298
299
|
},
|
|
299
300
|
id: 1,
|
|
300
301
|
pin_mode: 'output',
|
|
301
|
-
pin_number: 0,
|
|
302
|
+
pin_number: '0',
|
|
302
303
|
value_type: 'boolean',
|
|
303
304
|
},
|
|
304
305
|
{
|
|
@@ -309,7 +310,7 @@ describe('Test DeviceInternalDetail', () => {
|
|
|
309
310
|
},
|
|
310
311
|
id: 2,
|
|
311
312
|
pin_mode: 'output',
|
|
312
|
-
pin_number: 0,
|
|
313
|
+
pin_number: '0',
|
|
313
314
|
value_type: 'integer',
|
|
314
315
|
},
|
|
315
316
|
],
|
|
@@ -339,6 +340,7 @@ describe('Test DeviceInternalDetail', () => {
|
|
|
339
340
|
board: undefined,
|
|
340
341
|
isInternal: true,
|
|
341
342
|
writeConfigPins: [],
|
|
343
|
+
virtualConfigPins: [],
|
|
342
344
|
});
|
|
343
345
|
});
|
|
344
346
|
|
|
@@ -147,11 +147,13 @@ const DeviceInternalDetail = () => {
|
|
|
147
147
|
isInternal,
|
|
148
148
|
board,
|
|
149
149
|
writeConfigPins: detailDeviceInternal?.config_write,
|
|
150
|
+
virtualConfigPins: detailDeviceInternal?.config_virtual,
|
|
150
151
|
});
|
|
151
152
|
},
|
|
152
153
|
[
|
|
153
154
|
board,
|
|
154
155
|
detailDeviceInternal?.config_write,
|
|
156
|
+
detailDeviceInternal?.config_virtual,
|
|
155
157
|
device,
|
|
156
158
|
isAction,
|
|
157
159
|
isConfigRead,
|
|
@@ -5,6 +5,7 @@ import { ToastBottomHelper } from '../../../utils/Utils';
|
|
|
5
5
|
import { axiosGet, axiosDelete, axiosPost } from '../../../utils/Apis/axios';
|
|
6
6
|
import API from '../../../configs/API';
|
|
7
7
|
import t from '../../../hooks/Common/useTranslations';
|
|
8
|
+
import { VIRTUAL_TYPE } from '../../../configs/IOPinConstants';
|
|
8
9
|
|
|
9
10
|
export const useGateway = () => {
|
|
10
11
|
const navigation = useNavigation();
|
|
@@ -64,12 +65,39 @@ export const useGateway = () => {
|
|
|
64
65
|
API.DEV_MODE.ARDUINO.CONFIG_PINS(gatewayId, deviceId)
|
|
65
66
|
);
|
|
66
67
|
if (success) {
|
|
67
|
-
const config_read = data
|
|
68
|
-
|
|
68
|
+
const config_read = data
|
|
69
|
+
?.filter(
|
|
70
|
+
(item) =>
|
|
71
|
+
item?.config?.is_read && item?.value_type !== VIRTUAL_TYPE
|
|
72
|
+
)
|
|
73
|
+
?.map((item) => {
|
|
74
|
+
return {
|
|
75
|
+
...item,
|
|
76
|
+
pin_number: item?.pin_number.toString(),
|
|
77
|
+
};
|
|
78
|
+
});
|
|
79
|
+
const config_write = data
|
|
80
|
+
?.filter((item) => item?.config?.is_write)
|
|
81
|
+
?.map((item) => {
|
|
82
|
+
return {
|
|
83
|
+
...item,
|
|
84
|
+
pin_number: item?.pin_number.toString(),
|
|
85
|
+
};
|
|
86
|
+
});
|
|
87
|
+
const config_virtual = data
|
|
88
|
+
?.filter((item) => item?.value_type === VIRTUAL_TYPE)
|
|
89
|
+
?.map((item) => {
|
|
90
|
+
return {
|
|
91
|
+
...item,
|
|
92
|
+
pin_number: item?.pin_number.toString(),
|
|
93
|
+
};
|
|
94
|
+
});
|
|
95
|
+
|
|
69
96
|
setDetailDeviceInternal((prev) => ({
|
|
70
97
|
...prev,
|
|
71
98
|
config_read,
|
|
72
99
|
config_write,
|
|
100
|
+
config_virtual,
|
|
73
101
|
}));
|
|
74
102
|
}
|
|
75
103
|
} else {
|
|
@@ -18,32 +18,45 @@ const wrapComponent = (item, sensor) => (
|
|
|
18
18
|
);
|
|
19
19
|
describe('Test DetailHistoryChart', () => {
|
|
20
20
|
let tree;
|
|
21
|
+
let item = {
|
|
22
|
+
id: 10452,
|
|
23
|
+
order: 0,
|
|
24
|
+
template: 'history',
|
|
25
|
+
type: 'history',
|
|
26
|
+
configuration: {
|
|
27
|
+
type: 'horizontal_bar_chart',
|
|
28
|
+
date_format: 'DD.MM',
|
|
29
|
+
configs: [
|
|
30
|
+
{
|
|
31
|
+
id: 9848,
|
|
32
|
+
title: 'horizontal',
|
|
33
|
+
color: 'blue',
|
|
34
|
+
},
|
|
35
|
+
],
|
|
36
|
+
},
|
|
37
|
+
is_configuration_ready: true,
|
|
38
|
+
};
|
|
39
|
+
let sensor = {
|
|
40
|
+
name: 'Sensor name',
|
|
41
|
+
is_managed_by_backend: false,
|
|
42
|
+
};
|
|
21
43
|
it('create DetailHistoryChart', async () => {
|
|
22
44
|
mock
|
|
23
45
|
.onGet(API.VALUE_CONSUME.DISPLAY_HISTORY())
|
|
24
46
|
.reply(200, [{ data: [1, 2, 3] }]);
|
|
25
|
-
const item = {
|
|
26
|
-
id: 10452,
|
|
27
|
-
order: 0,
|
|
28
|
-
template: 'history',
|
|
29
|
-
type: 'history',
|
|
30
|
-
configuration: {
|
|
31
|
-
type: 'horizontal_bar_chart',
|
|
32
|
-
date_format: 'DD.MM',
|
|
33
|
-
configs: [
|
|
34
|
-
{
|
|
35
|
-
id: 9848,
|
|
36
|
-
title: 'horizontal',
|
|
37
|
-
color: 'blue',
|
|
38
|
-
},
|
|
39
|
-
],
|
|
40
|
-
},
|
|
41
|
-
is_configuration_ready: true,
|
|
42
|
-
};
|
|
43
47
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
48
|
+
await act(async () => {
|
|
49
|
+
tree = renderer.create(wrapComponent(item, sensor));
|
|
50
|
+
});
|
|
51
|
+
const instance = tree.root;
|
|
52
|
+
const historyChart = instance.findAllByType(HistoryChart);
|
|
53
|
+
expect(historyChart).toHaveLength(1);
|
|
54
|
+
});
|
|
55
|
+
it('create DetailHistoryChart type line_chart', async () => {
|
|
56
|
+
mock.onGet(API.DEVICE.DISPLAY_HISTORY()).reply(200, [{ data: [1, 2, 3] }]);
|
|
57
|
+
item = {
|
|
58
|
+
...item,
|
|
59
|
+
configuration: { ...item?.configuration, type: 'line_chart' },
|
|
47
60
|
};
|
|
48
61
|
await act(async () => {
|
|
49
62
|
tree = renderer.create(wrapComponent(item, sensor));
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import renderer, { act } from 'react-test-renderer';
|
|
3
|
+
|
|
4
|
+
import { EmergencyCountdown } from '../components/EmergencyCountdown';
|
|
5
|
+
import { SCProvider } from '../../../context';
|
|
6
|
+
import { mockSCStore } from '../../../context/mockStore';
|
|
7
|
+
import Text from '../../../commons/Text';
|
|
8
|
+
|
|
9
|
+
const wrapComponent = (countUpStr, typeEmergency) => (
|
|
10
|
+
<SCProvider initState={mockSCStore({})}>
|
|
11
|
+
<EmergencyCountdown countUpStr={countUpStr} typeEmergency={typeEmergency} />
|
|
12
|
+
</SCProvider>
|
|
13
|
+
);
|
|
14
|
+
describe('Test EmergencyCountdown', () => {
|
|
15
|
+
let tree;
|
|
16
|
+
it(' EmergencyCountdown resolved_emergency', async () => {
|
|
17
|
+
await act(async () => {
|
|
18
|
+
tree = renderer.create(wrapComponent('stopCount', 'resolved_emergency'));
|
|
19
|
+
});
|
|
20
|
+
const instance = tree.root;
|
|
21
|
+
const texts = instance.findAllByType(Text);
|
|
22
|
+
expect(texts[1].props.children).toEqual('');
|
|
23
|
+
});
|
|
24
|
+
it(' EmergencyCountdown not resolved_emergency', async () => {
|
|
25
|
+
await act(async () => {
|
|
26
|
+
tree = renderer.create(
|
|
27
|
+
wrapComponent('stopCount', 'not_resolve_emergency')
|
|
28
|
+
);
|
|
29
|
+
});
|
|
30
|
+
const instance = tree.root;
|
|
31
|
+
const texts = instance.findAllByType(Text);
|
|
32
|
+
expect(texts[1].props.children).toEqual('stopCount');
|
|
33
|
+
});
|
|
34
|
+
});
|
|
@@ -21,6 +21,7 @@ import { useSCContextSelector } from '../../../context';
|
|
|
21
21
|
import { useRemoteControl } from '../../../hooks/IoT';
|
|
22
22
|
import { useConfigGlobalState } from '../../../iot/states';
|
|
23
23
|
import { useEvaluateValue } from '../hooks/useEvaluateValue';
|
|
24
|
+
import ProgressBar from '../../../commons/Device/ProgressBar';
|
|
24
25
|
|
|
25
26
|
export const SensorDisplayItem = ({
|
|
26
27
|
item = {},
|
|
@@ -127,6 +128,8 @@ export const SensorDisplayItem = ({
|
|
|
127
128
|
);
|
|
128
129
|
case 'gauge':
|
|
129
130
|
return <Anemometer data={getData(item)} item={item} />;
|
|
131
|
+
case 'progress_bar':
|
|
132
|
+
return <ProgressBar data={getData(item)} item={item} />;
|
|
130
133
|
case 'compass':
|
|
131
134
|
return <Compass data={getData(item)} />;
|
|
132
135
|
case 'alert_status':
|
|
@@ -24,4 +24,26 @@ describe('test EmergencySetting', () => {
|
|
|
24
24
|
|
|
25
25
|
expect(dropDownItem.length).toEqual(3);
|
|
26
26
|
});
|
|
27
|
+
it('test render EmergencySetting onOpen', async () => {
|
|
28
|
+
await act(async () => {
|
|
29
|
+
tree = await create(wrapComponent());
|
|
30
|
+
});
|
|
31
|
+
const instance = tree.root;
|
|
32
|
+
const dropDownItem = instance.findAllByType(DropDownItem);
|
|
33
|
+
expect(dropDownItem[0].props.isOpen).toEqual(false);
|
|
34
|
+
await act(async () => {
|
|
35
|
+
dropDownItem[0].props.onOpen();
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
it('test render EmergencySetting onSelectItem', async () => {
|
|
39
|
+
await act(async () => {
|
|
40
|
+
tree = await create(wrapComponent());
|
|
41
|
+
});
|
|
42
|
+
const instance = tree.root;
|
|
43
|
+
const dropDownItem = instance.findAllByType(DropDownItem);
|
|
44
|
+
expect(dropDownItem[0].props.isOpen).toEqual(false);
|
|
45
|
+
await act(async () => {
|
|
46
|
+
dropDownItem[0].props.onSelectItem();
|
|
47
|
+
});
|
|
48
|
+
});
|
|
27
49
|
});
|
|
@@ -71,10 +71,11 @@ const AddLGDevice = memo(({ route }) => {
|
|
|
71
71
|
navigate(Routes.Dashboard);
|
|
72
72
|
}, [backend_url, code, navigate, stationId, t]);
|
|
73
73
|
|
|
74
|
-
const stations =
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
74
|
+
const stations =
|
|
75
|
+
unit?.stations?.map((item) => ({
|
|
76
|
+
...item,
|
|
77
|
+
title: item.name,
|
|
78
|
+
})) || [];
|
|
78
79
|
|
|
79
80
|
return (
|
|
80
81
|
<View style={styles.wrap}>
|
|
@@ -143,16 +143,17 @@ const SubUnitList = ({
|
|
|
143
143
|
}, [stationId, listStation]);
|
|
144
144
|
|
|
145
145
|
useEffect(() => {
|
|
146
|
-
if (unit
|
|
147
|
-
let listMenu =
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
146
|
+
if (unit?.stations) {
|
|
147
|
+
let listMenu =
|
|
148
|
+
unit.stations?.map((item, index) => ({
|
|
149
|
+
text: item.name,
|
|
150
|
+
station: item,
|
|
151
|
+
id: item.id,
|
|
152
|
+
})) || [];
|
|
152
153
|
setListMenuItem(listMenu);
|
|
153
154
|
setListStation(listMenu.concat([{ text: '' }]));
|
|
154
155
|
}
|
|
155
|
-
}, [unit
|
|
156
|
+
}, [unit?.stations]);
|
|
156
157
|
|
|
157
158
|
useEffect(() => {
|
|
158
159
|
if (!unit?.stations || !unit.stations[indexStation]) {
|
|
@@ -11,7 +11,7 @@ export const useFavorites = (stations, automatesData) => {
|
|
|
11
11
|
|
|
12
12
|
const favoriteDevices = useMemo(() => {
|
|
13
13
|
return []
|
|
14
|
-
.concat(...stations
|
|
14
|
+
.concat(...stations?.map((station) => station.sensors || []))
|
|
15
15
|
.filter((device) => favoriteDeviceIds.includes(device.id));
|
|
16
16
|
}, [stations, favoriteDeviceIds]);
|
|
17
17
|
|
|
@@ -2,6 +2,13 @@ import { getDateData, getTitleFromTime, timeDifference } from '../time';
|
|
|
2
2
|
|
|
3
3
|
describe('Test timer', () => {
|
|
4
4
|
let result;
|
|
5
|
+
it('Test timeDifference when elapsed < msPerMinute and second<0', async () => {
|
|
6
|
+
result = await timeDifference(
|
|
7
|
+
new Date('2022-08-01T03:24:01'),
|
|
8
|
+
new Date('2022-08-01T03:24:03')
|
|
9
|
+
);
|
|
10
|
+
expect(result).toBe('0 seconds ago');
|
|
11
|
+
});
|
|
5
12
|
it('Test timeDifference when elapsed < msPerMinute and symbol=false', async () => {
|
|
6
13
|
result = await timeDifference(
|
|
7
14
|
new Date('2022-08-01T03:24:01'),
|
|
@@ -25,7 +25,7 @@ export const preloadImagesFromUnits = async (units, storeKey) => {
|
|
|
25
25
|
// eslint-disable-next-line promise/prefer-await-to-then
|
|
26
26
|
).then((results) => {
|
|
27
27
|
const icons = results
|
|
28
|
-
.map((item) => item?.stations
|
|
28
|
+
.map((item) => item?.stations?.map((i) => i?.background) || [])
|
|
29
29
|
.flat()
|
|
30
30
|
.filter(Boolean);
|
|
31
31
|
FastImage.preload(
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
"add_user_note": "Please note that your personal data including records of your control of the connected devices and services will be shared with other members in the same unit.",
|
|
12
12
|
"add_user_invitation_sent": "Invitation sent",
|
|
13
13
|
"cancel": "Cancel",
|
|
14
|
+
"max_value": "Max value",
|
|
14
15
|
"text_password": "Password",
|
|
15
16
|
"text_sign_up": "Sign up",
|
|
16
17
|
"text_sign_up_capitalize": "Sign up",
|
|
@@ -717,6 +717,7 @@
|
|
|
717
717
|
"command_homeassistant_ready": "Home Assistant kết nối thành công",
|
|
718
718
|
"command_homeassistant_lost": "Home Assistant bị mất kết nối. Đang kết nối lại...",
|
|
719
719
|
"confirm": "Xác nhận",
|
|
720
|
+
"max_value": "Giá trị lớn nhất",
|
|
720
721
|
"pick_a_date": "Chọn ngày",
|
|
721
722
|
"car_validate_warning": "Vui lòng nhập đúng định dạng biển số xe \nVD: %{example}",
|
|
722
723
|
"are_you_sure_this_resolved": "Bạn có chắc tình huống khẩn cấp này đã được xử lý xong? Các thành viên thuộc địa điểm này cũng sẽ nhận được thông báo.",
|
package/src/utils/Utils.js
CHANGED
|
@@ -9,18 +9,15 @@ import {
|
|
|
9
9
|
RASPBERRY_PI_DIGITAL,
|
|
10
10
|
RASPBERRY_PI_ANALOG_WRITE,
|
|
11
11
|
RASPBERRY_PI_ANALOG_READ,
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
ESP8266_DIGITAL,
|
|
13
|
+
ESP8266_ANALOG_READ,
|
|
14
|
+
ESP8266_ANALOG_WRITE,
|
|
15
|
+
STM32_PIN,
|
|
16
16
|
READ_DIGITAL_PIN_MODE,
|
|
17
17
|
READ_ANALOG_PIN_MODE,
|
|
18
18
|
WRITE_DIGITAL_PIN_MODE,
|
|
19
19
|
WRITE_ANALOG_PIN_MODE,
|
|
20
20
|
LANGUAGE,
|
|
21
|
-
ESP8266_DIGITAL,
|
|
22
|
-
ESP8266_ANALOG_READ,
|
|
23
|
-
ESP8266_ANALOG_WRITE,
|
|
24
21
|
} from '../configs/IOPinConstants';
|
|
25
22
|
|
|
26
23
|
export const setAxiosDefaultAuthToken = (token) => {
|
|
@@ -197,12 +194,22 @@ export const PIN_MAPPING = {
|
|
|
197
194
|
},
|
|
198
195
|
stm32: {
|
|
199
196
|
read: {
|
|
200
|
-
boolean:
|
|
201
|
-
integer:
|
|
197
|
+
boolean: STM32_PIN,
|
|
198
|
+
integer: STM32_PIN,
|
|
199
|
+
},
|
|
200
|
+
write: {
|
|
201
|
+
boolean: STM32_PIN,
|
|
202
|
+
integer: STM32_PIN,
|
|
203
|
+
},
|
|
204
|
+
},
|
|
205
|
+
stm32_wifi: {
|
|
206
|
+
read: {
|
|
207
|
+
boolean: STM32_PIN,
|
|
208
|
+
integer: STM32_PIN,
|
|
202
209
|
},
|
|
203
210
|
write: {
|
|
204
|
-
boolean:
|
|
205
|
-
integer:
|
|
211
|
+
boolean: STM32_PIN,
|
|
212
|
+
integer: STM32_PIN,
|
|
206
213
|
},
|
|
207
214
|
},
|
|
208
215
|
raspberry_pi: {
|