@eohjsc/react-native-smart-city 0.3.92 → 0.3.93

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 (68) hide show
  1. package/package.json +5 -1
  2. package/src/commons/ActionGroup/SliderRangeTemplate.js +2 -2
  3. package/src/commons/AlertAction/index.js +5 -0
  4. package/src/commons/BottomButtonView/index.js +22 -4
  5. package/src/commons/Button/index.js +5 -0
  6. package/src/commons/Device/ConnectedViewHeader.js +0 -1
  7. package/src/commons/Device/Emergency/EmergencyDetail.js +4 -2
  8. package/src/commons/Device/Emergency/__test__/EmergencyDetail.test.js +4 -2
  9. package/src/commons/Device/ProgressBar/index.js +3 -1
  10. package/src/commons/Device/ProgressBar/styles.js +1 -4
  11. package/src/commons/Device/WindSpeed/Anemometer/index.js +1 -1
  12. package/src/commons/Header/HeaderCustom.js +8 -18
  13. package/src/commons/SubUnit/OneTap/ItemOneTap.js +5 -48
  14. package/src/commons/SubUnit/OneTap/__test__/SubUnitAutomate.test.js +6 -15
  15. package/src/commons/SubUnit/OneTap/index.js +3 -3
  16. package/src/commons/ViewButtonBottom/index.js +32 -4
  17. package/src/configs/API.js +3 -0
  18. package/src/configs/AccessibilityLabel.js +1 -0
  19. package/src/configs/BLE.js +1 -0
  20. package/src/context/actionType.ts +2 -1
  21. package/src/context/mockStore.ts +1 -0
  22. package/src/context/reducer.ts +12 -1
  23. package/src/hooks/Explore/useKeyboardAnimated.js +10 -4
  24. package/src/hooks/IoT/useBluetoothConnection.js +14 -26
  25. package/src/hooks/useMqtt.js +95 -0
  26. package/src/iot/Monitor.js +2 -1
  27. package/src/iot/RemoteControl/Bluetooth.js +56 -19
  28. package/src/iot/RemoteControl/__test__/Bluetooth.test.js +140 -0
  29. package/src/iot/mqtt.js +233 -0
  30. package/src/navigations/UnitStack.js +11 -3
  31. package/src/screens/AddLocationMaps/index.js +18 -16
  32. package/src/screens/AddLocationMaps/indexStyle.js +3 -0
  33. package/src/screens/Automate/AddNewAction/NewActionWrapper.js +3 -6
  34. package/src/screens/Automate/AddNewAction/SetupConfigCondition.js +29 -29
  35. package/src/screens/Automate/AddNewAction/__test__/SetupSensor.test.js +45 -39
  36. package/src/screens/Automate/AddNewAutoSmart/AddAutomationTypeSmart.js +25 -0
  37. package/src/screens/{AddNewAutoSmart/index.js → Automate/AddNewAutoSmart/AddTypeSmart.js} +16 -51
  38. package/src/screens/Automate/AddNewAutoSmart/AddUnknownTypeSmart.js +29 -0
  39. package/src/screens/{AddNewAutoSmart → Automate/AddNewAutoSmart}/__test__/AddNewAutoSmart.test.js +11 -11
  40. package/src/screens/{AddNewAutoSmart → Automate/AddNewAutoSmart}/styles/AddNewAutoSmartStyles.js +1 -1
  41. package/src/screens/Automate/Components/InputNameStyles.js +1 -1
  42. package/src/screens/Automate/EditActionsList/__tests__/index.test.js +11 -25
  43. package/src/screens/Automate/EditActionsList/index.js +32 -33
  44. package/src/screens/Automate/MultiUnits.js +1 -1
  45. package/src/screens/Automate/ScriptDetail/__test__/index.test.js +42 -3
  46. package/src/screens/Automate/ScriptDetail/index.js +21 -7
  47. package/src/screens/Automate/__test__/MultiUnits.test.js +1 -1
  48. package/src/screens/Automate/__test__/index.test.js +1 -1
  49. package/src/screens/Automate/index.js +1 -1
  50. package/src/screens/Device/__test__/detail.test.js +1 -1
  51. package/src/screens/Device/__test__/mqttDetail.test.js +599 -0
  52. package/src/screens/Device/components/SensorDisplayItem.js +1 -7
  53. package/src/screens/Device/detail.js +64 -30
  54. package/src/screens/Device/hooks/useDeviceWatchConfigControl.js +13 -3
  55. package/src/screens/SelectUnit/__test__/index.test.js +8 -13
  56. package/src/screens/SmartAccount/__test__/SmartAccount.test.js +8 -4
  57. package/src/screens/SmartAccount/index.js +8 -9
  58. package/src/screens/SmartAccount/style.js +8 -7
  59. package/src/screens/Unit/Detail.js +4 -19
  60. package/src/screens/Unit/Summaries.js +6 -17
  61. package/src/screens/Unit/__test__/Summaries.test.js +2 -2
  62. package/src/utils/FactoryGateway.js +525 -0
  63. package/src/utils/I18n/translations/en.json +5 -1
  64. package/src/utils/I18n/translations/vi.json +5 -2
  65. package/src/utils/Route/index.js +2 -1
  66. package/src/utils/Utils.js +11 -0
  67. package/src/commons/Device/SensorConnectedStatus.js +0 -56
  68. package/src/commons/Device/__test__/SensorConnectedStatus.test.js +0 -29
@@ -0,0 +1,233 @@
1
+ import { updateGlobalValue } from './Monitor';
2
+ import { Buffer } from 'buffer';
3
+
4
+ const int_all = (bytes_str, config_data = null) => {
5
+ let val;
6
+ val = Number.parseInt(bytes_str, 16);
7
+
8
+ if (val > 32767) {
9
+ val = val - 65536;
10
+ }
11
+
12
+ return val;
13
+ };
14
+
15
+ const int_first4 = (bytes_str, config_data = null) => {
16
+ return Number.parseInt(bytes_str.slice(0, 4), 16);
17
+ };
18
+
19
+ const int_last4 = (bytes_str, config_data = null) => {
20
+ return Number.parseInt(bytes_str.slice(4), 16);
21
+ };
22
+
23
+ const uint_32 = (bytes_str, config_data = null) => {
24
+ let bits = 32;
25
+ let value = Number.parseInt(bytes_str, 16);
26
+
27
+ if (value & (1 << (bits - 1))) {
28
+ value -= 1 << bits;
29
+ }
30
+
31
+ return value;
32
+ };
33
+
34
+ const float_convert = (bytes_str) => {
35
+ const struct = require('python-struct');
36
+ let value = null;
37
+ try {
38
+ value = struct.unpack('>f', Buffer.from(bytes_str, 'hex'))[0];
39
+ // eslint-disable-next-line no-empty
40
+ } catch {}
41
+ return value;
42
+ };
43
+
44
+ const float_cdba = (bytes_str, config_data = null) => {
45
+ const bytes_cdba = bytes_str?.slice(4) + bytes_str?.slice(0, 4);
46
+ return float_convert(bytes_cdba);
47
+ };
48
+
49
+ const float_abcd = (bytes_str, config_data = null) => {
50
+ return float_convert(bytes_str);
51
+ };
52
+
53
+ const range_value_converting = (
54
+ raw_value,
55
+ raw_min,
56
+ raw_max,
57
+ value_min,
58
+ value_max
59
+ ) => {
60
+ const rawValue = parseInt(raw_value, 16);
61
+ const meaning_range = value_max - value_min;
62
+ const value_range = raw_max - raw_min;
63
+ if (!value_range) {
64
+ return null;
65
+ }
66
+
67
+ const value = rawValue - raw_min;
68
+ const meaning_value = (value * meaning_range) / value_range;
69
+ return meaning_value + value_min;
70
+ };
71
+
72
+ const convert_ai = (bytes_str, config_data = null) => {
73
+ return range_value_converting(
74
+ bytes_str,
75
+ config_data?.ai_trans_min_raw_in,
76
+ config_data?.ai_trans_max_raw_in,
77
+ config_data?.ai_trans_min_value,
78
+ config_data?.ai_trans_max_value
79
+ );
80
+ };
81
+
82
+ const mappingTransformer = {
83
+ int_all: int_all,
84
+ int_first4: int_first4,
85
+ int_last4: int_last4,
86
+ uint_32: uint_32,
87
+ float_cdba: float_cdba,
88
+ float_abcd: float_abcd,
89
+ convert_ai: convert_ai,
90
+ };
91
+
92
+ const extract_config_value = (register, value_bytes) => {
93
+ const transformer = mappingTransformer[register?.transformer];
94
+ if (!transformer) {
95
+ return null;
96
+ }
97
+
98
+ return transformer(value_bytes, register);
99
+ };
100
+
101
+ const get_modbus_length = (config) => {
102
+ const len = config?.len;
103
+ if ([1, 2].includes(config?.func)) {
104
+ return len * 2;
105
+ }
106
+ return len * 4;
107
+ };
108
+
109
+ const get_config_value = (register, data) => {
110
+ const size = get_modbus_length(register);
111
+ const value_bytes = data?.slice(0, size);
112
+
113
+ const value = extract_config_value(register, value_bytes);
114
+ if (value === undefined || value === null) {
115
+ return { value: null, data: null };
116
+ }
117
+
118
+ return { value, data: data.slice(size) };
119
+ };
120
+
121
+ const get_sensor_size = (sensor) => {
122
+ let size = 0;
123
+ (sensor?.configs || []).forEach((config) => {
124
+ size += get_modbus_length(config);
125
+ });
126
+ return size;
127
+ };
128
+
129
+ const get_sensor_data = (sensor, data) => {
130
+ const size = get_sensor_size(sensor);
131
+ return { sensor_data: data.slice(0, size), data: data.slice(size) };
132
+ };
133
+
134
+ const get_scale_value = (value, config) => {
135
+ value = value && value * (config?.scale || 1);
136
+ value =
137
+ config?.decimal_behind || config?.decimal_behind === 0
138
+ ? value.toFixed(config?.decimal_behind)
139
+ : value;
140
+ return value;
141
+ };
142
+
143
+ const updateGlobalValueMqtt = (config, value) => {
144
+ if (!config?.id) {
145
+ return;
146
+ }
147
+ if (value === undefined || value === null) {
148
+ return;
149
+ }
150
+
151
+ value = get_scale_value(value, config);
152
+ updateGlobalValue(config.id, { value: Number(value) });
153
+ };
154
+
155
+ export const handleModbusData = (chip, configById, data) => {
156
+ const modbus_gateway = chip?.modbus_gateway;
157
+ if (!modbus_gateway) {
158
+ return;
159
+ }
160
+
161
+ (modbus_gateway?.sensors || []).forEach((device) => {
162
+ let { sensor_data, data: new_data } = get_sensor_data(device, data);
163
+ data = new_data;
164
+
165
+ (device?.configs || []).forEach((register) => {
166
+ let { value, data: new_sensor_data } = get_config_value(
167
+ register,
168
+ sensor_data
169
+ );
170
+ sensor_data = new_sensor_data;
171
+
172
+ const configId = register?.config;
173
+ updateGlobalValueMqtt(configById[configId], value);
174
+ });
175
+ });
176
+ };
177
+
178
+ export const handleChipData = (configById, configId, data) => {
179
+ updateGlobalValueMqtt(configById[configId], Number(data?.v));
180
+ };
181
+
182
+ export const handleZigbeeData = (chip, configById, ieee_address, data) => {
183
+ const zigbee_gateway = chip?.zigbee_gateway;
184
+ if (!zigbee_gateway) {
185
+ return;
186
+ }
187
+
188
+ const zigbee_device_by_ieee = {};
189
+
190
+ (zigbee_gateway?.sensors || []).forEach((device) => {
191
+ zigbee_device_by_ieee[device.ieee_address] = device;
192
+ });
193
+
194
+ const device = zigbee_device_by_ieee[ieee_address];
195
+ if (device) {
196
+ (device.configs || []).forEach((config_map) => {
197
+ let value = data?.data[config_map?.key];
198
+ const configId = config_map?.config;
199
+ updateGlobalValueMqtt(configById[configId], value);
200
+ });
201
+ }
202
+ };
203
+
204
+ export const handleMqttMessage = (
205
+ topic,
206
+ payloadData,
207
+ code,
208
+ chip,
209
+ configById
210
+ ) => {
211
+ if (topic === `eoh/chip/${code}/data`) {
212
+ // Modbus
213
+ // topic: eoh/chip/${code}/data
214
+ // payload: {data: '01aa010a', 'ack':'1', 'chip_temperature':5333, 'rssi':-61}
215
+ handleModbusData(chip, configById, payloadData.data);
216
+ }
217
+
218
+ const matchArduinoConfigValue = topic.match(/config\/(\d+)\/value/);
219
+ if (matchArduinoConfigValue) {
220
+ // Arduino
221
+ // topic: eoh/chip/{code}/config/127363/value
222
+ // payload: {v: 2130}
223
+ handleChipData(configById, matchArduinoConfigValue[1], payloadData);
224
+ }
225
+
226
+ const matchZigbeeConfigValue = topic.match(/zigbee\/(.+)\/data/);
227
+ if (matchZigbeeConfigValue) {
228
+ // Zigbee
229
+ // topic: eoh/chip/code-123/zigbee/IEEE-123/data
230
+ // payload: {zigbee_data: 'aaa', 'data': {state: 1, state_0: 0}}
231
+ handleZigbeeData(chip, configById, matchZigbeeConfigValue[1], payloadData);
232
+ }
233
+ };
@@ -33,7 +33,7 @@ import TDSGuide from '../screens/TDSGuide';
33
33
  import WaterQualityGuide from '../screens/WaterQualityGuide';
34
34
  import DeviceInfo from '../screens/DeviceInfo';
35
35
  import AddNewOneTap from '../screens/Automate/OneTap';
36
- import AddNewAutoSmart from '../screens/AddNewAutoSmart';
36
+ import AddUnknownTypeSmart from '../screens/Automate/AddNewAutoSmart/AddUnknownTypeSmart';
37
37
  import PlaybackCamera from '../screens/PlayBackCamera';
38
38
  import AllCamera from '../screens/AllCamera';
39
39
  import ManageAccessScreen from '../screens/ManageAccess';
@@ -65,6 +65,7 @@ import SelectControlDevices from '../screens/Automate/AddNewAction/SelectControl
65
65
  import ChooseAction from '../screens/Automate/AddNewAction/ChooseAction';
66
66
  import ScenarioName from '../screens/Automate/Scenario/ScenarioName';
67
67
  import ValueChangeName from '../screens/Automate/ValueChange/ValueChangeName';
68
+ import AddAutomationTypeSmart from '../screens/Automate/AddNewAutoSmart/AddAutomationTypeSmart';
68
69
 
69
70
  const Stack = createStackNavigator();
70
71
 
@@ -399,8 +400,15 @@ export const UnitStack = memo((props) => {
399
400
  }}
400
401
  />
401
402
  <Stack.Screen
402
- name={Route.AddNewAutoSmart}
403
- component={AddNewAutoSmart}
403
+ name={Route.AddUnknownTypeSmart}
404
+ component={AddUnknownTypeSmart}
405
+ options={{
406
+ headerShown: false,
407
+ }}
408
+ />
409
+ <Stack.Screen
410
+ name={Route.AddAutomationTypeSmart}
411
+ component={AddAutomationTypeSmart}
404
412
  options={{
405
413
  headerShown: false,
406
414
  }}
@@ -7,7 +7,7 @@ import { check, RESULTS } from 'react-native-permissions';
7
7
  import { useTranslations } from '../../hooks/Common/useTranslations';
8
8
 
9
9
  import Text from '../../commons/Text';
10
- import { ViewButtonBottom, FullLoading } from '../../commons';
10
+ import { FullLoading } from '../../commons';
11
11
  import SearchBarLocation from '../../commons/SearchLocation';
12
12
  import RowLocation from '../../commons/SearchLocation/RowLocation';
13
13
  import { axiosGet } from '../../utils/Apis/axios';
@@ -25,8 +25,7 @@ import {
25
25
  OpenSetting,
26
26
  } from '../../utils/Permission/common';
27
27
  import { openPromptEnableLocation } from '../../utils/Setting/Location';
28
-
29
- export const initialRadius = 250;
28
+ import BottomButtonView from '../../commons/BottomButtonView';
30
29
 
31
30
  navigator.geolocation = require('@react-native-community/geolocation');
32
31
 
@@ -44,11 +43,7 @@ const AddLocationMaps = memo(() => {
44
43
  location: searchedLocation,
45
44
  isAddUnit: true,
46
45
  });
47
- }, [searchedLocation, navigate]);
48
-
49
- const onBack = useCallback(() => {
50
- goBack();
51
- }, [goBack]);
46
+ }, [navigate, searchedLocation]);
52
47
 
53
48
  const onTextInput = useCallback(async (newValue) => {
54
49
  setInput(newValue);
@@ -191,7 +186,10 @@ const AddLocationMaps = memo(() => {
191
186
  {t('text_explain_add_geolocation')}
192
187
  </Text>
193
188
  <View style={styles.searchLocation}>
194
- <SearchBarLocation input={input} onTextInput={onTextInput} />
189
+ <SearchBarLocation
190
+ input={input?.description || input}
191
+ onTextInput={onTextInput}
192
+ />
195
193
  <ScrollView
196
194
  style={styles.searchData}
197
195
  scrollIndicatorInsets={{ right: 1 }}
@@ -282,13 +280,17 @@ const AddLocationMaps = memo(() => {
282
280
  </MapView>
283
281
  </View>
284
282
  </View>
285
- <ViewButtonBottom
286
- leftTitle={t('cancel')}
287
- onLeftClick={onBack}
288
- rightTitle={t('done')}
289
- onRightClick={onDone}
290
- rightDisabled={!input}
291
- accessibilityLabelPrefix={'LOCATION_'}
283
+ <BottomButtonView
284
+ rowButton
285
+ style={styles.bottomButton}
286
+ mainTitle={t('done')}
287
+ onPressMain={onDone}
288
+ secondaryTitle={t('cancel')}
289
+ onPressSecondary={goBack}
290
+ typeMain={input?.description ? 'primaryText' : 'disabled'}
291
+ typeSecondary="primaryText"
292
+ accessibilityLabelPrefix="LOCATION_"
293
+ disableBackgroundMainButton
292
294
  />
293
295
  {loading && <FullLoading />}
294
296
  </View>
@@ -57,4 +57,7 @@ export default StyleSheet.create({
57
57
  text: {
58
58
  marginLeft: 12,
59
59
  },
60
+ bottomButton: {
61
+ backgroundColor: Colors.White,
62
+ },
60
63
  });
@@ -1,5 +1,5 @@
1
1
  import React, { useCallback, useMemo } from 'react';
2
- import { KeyboardAvoidingView, Platform, TouchableOpacity } from 'react-native';
2
+ import { TouchableOpacity, View } from 'react-native';
3
3
 
4
4
  import styles from './Styles/SelectActionStyles';
5
5
  import WrapHeaderScrollable from '../../../commons/Sharing/WrapHeaderScrollable';
@@ -32,10 +32,7 @@ const NewActionWrapper = ({ name, children, canNext, onNext, nextTitle }) => {
32
32
  );
33
33
 
34
34
  return (
35
- <KeyboardAvoidingView
36
- behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
37
- style={styles.wrap}
38
- >
35
+ <View style={styles.wrap}>
39
36
  <WrapHeaderScrollable
40
37
  title={name}
41
38
  headerAniStyle={styles.headerAniStyle}
@@ -49,7 +46,7 @@ const NewActionWrapper = ({ name, children, canNext, onNext, nextTitle }) => {
49
46
  onPressMain={onNext}
50
47
  typeMain={canNext ? 'primary' : 'disabled'}
51
48
  />
52
- </KeyboardAvoidingView>
49
+ </View>
53
50
  );
54
51
  };
55
52
 
@@ -7,11 +7,11 @@ import { Colors, Images } from '../../../configs';
7
7
  import { useTranslations } from '../../../hooks/Common/useTranslations';
8
8
  import { ModalCustom } from '../../../commons/Modal';
9
9
  import Text from '../../../commons/Text';
10
- import { HorizontalPicker } from '../../../commons';
11
10
  import { setStatusBarPreview } from '../../../hooks/Common/useStatusBar';
12
11
  import Routes from '../../../utils/Route';
13
12
  import NewActionWrapper from './NewActionWrapper';
14
13
  import _TextInput from '../../../commons/Form/TextInput';
14
+ import { ToastBottomHelper } from '../../../utils/Utils';
15
15
 
16
16
  const SetupConfigCondition = () => {
17
17
  const t = useTranslations();
@@ -22,7 +22,7 @@ const SetupConfigCondition = () => {
22
22
 
23
23
  const [isShowModal, setIsShowModal] = useState(false);
24
24
  const [itemActiveModal, setItemActiveModal] = useState(defaultCondition);
25
- const [value, setValue] = useState(defaultCondition?.value);
25
+ const [value, setValue] = useState(defaultCondition?.value || '');
26
26
 
27
27
  const hasNoValueEvaluation = !item?.evaluate_configuration;
28
28
 
@@ -68,6 +68,27 @@ const SetupConfigCondition = () => {
68
68
  };
69
69
 
70
70
  const onSave = useCallback(() => {
71
+ const conditionValue = parseFloat(itemActiveModal?.value);
72
+ if (isNaN(conditionValue)) {
73
+ ToastBottomHelper.error(t('value_must_be_a_number'));
74
+ return;
75
+ }
76
+
77
+ if (['<', '>', '='].includes(itemActiveModal?.condition)) {
78
+ if (item.range_min !== null && conditionValue < item.range_min) {
79
+ ToastBottomHelper.error(
80
+ t('value_must_be_greater_than_min', { min: item.range_min })
81
+ );
82
+ return;
83
+ }
84
+ if (item.range_max !== null && conditionValue > item.range_max) {
85
+ ToastBottomHelper.error(
86
+ t('value_must_be_less_than_max', { max: item.range_max })
87
+ );
88
+ return;
89
+ }
90
+ }
91
+
71
92
  navigate({
72
93
  name: Routes.ChooseConfig,
73
94
  merge: true,
@@ -78,7 +99,7 @@ const SetupConfigCondition = () => {
78
99
  },
79
100
  },
80
101
  });
81
- }, [item.id, itemActiveModal, navigate]);
102
+ }, [item.id, item.range_max, item.range_min, itemActiveModal, navigate, t]);
82
103
 
83
104
  useEffect(() => {
84
105
  !hasNoValueEvaluation && setValue(1);
@@ -98,12 +119,9 @@ const SetupConfigCondition = () => {
98
119
  );
99
120
  }, [isShowModal]);
100
121
 
101
- const onChangeValue = useCallback((v) => {
102
- const newValue = parseInt(v, 10);
103
- if (!isNaN(newValue)) {
104
- setValue(newValue);
105
- setItemActiveModal((prev) => ({ ...prev, value: newValue }));
106
- }
122
+ const onChangeValue = useCallback((newValue) => {
123
+ setValue(newValue);
124
+ setItemActiveModal((prev) => ({ ...prev, value: newValue }));
107
125
  }, []);
108
126
 
109
127
  return (
@@ -136,7 +154,8 @@ const SetupConfigCondition = () => {
136
154
  <_TextInput
137
155
  textInputStyle={styles.value}
138
156
  wrapStyle={styles.wrapValue}
139
- value={value?.toString()}
157
+ value={value}
158
+ keyboardType={'numeric'}
140
159
  onChange={onChangeValue}
141
160
  />
142
161
  </View>
@@ -144,25 +163,6 @@ const SetupConfigCondition = () => {
144
163
  {item?.unit}
145
164
  </Text>
146
165
  </View>
147
- <HorizontalPicker
148
- minimum={item?.range_min}
149
- maximum={item?.range_max}
150
- segmentSpacing={8}
151
- segmentWidth={8}
152
- step={
153
- item?.decimal_behind
154
- ? 1 / Math.pow(10, item?.decimal_behind)
155
- : 1
156
- }
157
- normalHeight={4}
158
- normalWidth={4}
159
- stepHeight={12}
160
- stepWidth={12}
161
- stepColor={Colors.Gray6}
162
- normalColor={Colors.Gray6}
163
- onChangeValue={onChangeValue}
164
- value={value}
165
- />
166
166
  </>
167
167
  )}
168
168
  </NewActionWrapper>
@@ -1,12 +1,13 @@
1
1
  import React from 'react';
2
- import { Animated, TouchableOpacity, View } from 'react-native';
2
+ import { TouchableOpacity } from 'react-native';
3
3
  import { act, create } from 'react-test-renderer';
4
- import { HorizontalPicker } from '../../../../commons';
5
4
  import { SCProvider } from '../../../../context';
6
5
  import { mockSCStore } from '../../../../context/mockStore';
7
6
  import BottomButtonView from '../../../../commons/BottomButtonView';
8
7
  import SetupConfigCondition from '../SetupConfigCondition';
9
8
  import { useNavigation, useRoute } from '@react-navigation/native';
9
+ import _TextInput from '../../../../commons/Form/TextInput';
10
+ import { ToastBottomHelper } from '../../../../utils/Utils';
10
11
 
11
12
  jest.mock('react', () => {
12
13
  return {
@@ -24,11 +25,13 @@ const wrapComponent = (configuration, onPress) => (
24
25
  describe('Test SetupConfigCondition', () => {
25
26
  const mockNavigate = useNavigation().navigate;
26
27
  const mockGoBack = useNavigation().goBack;
28
+ const spyToastError = jest.spyOn(ToastBottomHelper, 'error');
27
29
 
28
30
  let tree;
29
31
  beforeEach(() => {
30
32
  mockGoBack.mockReset();
31
33
  useRoute.mockClear();
34
+ spyToastError.mockClear();
32
35
  });
33
36
 
34
37
  it('Test render', async () => {
@@ -38,7 +41,7 @@ describe('Test SetupConfigCondition', () => {
38
41
  id: 1,
39
42
  value: 10,
40
43
  range_min: 0,
41
- range_max: 10,
44
+ range_max: 1000,
42
45
  decimal_behind: 0,
43
46
  title: 'is below (<)',
44
47
  sensor_type: 'air_quality',
@@ -63,10 +66,10 @@ describe('Test SetupConfigCondition', () => {
63
66
  await act(async () => {
64
67
  touchableOpacities[4].props.onPress();
65
68
  });
66
- const HorizontalPickers = instance.findAllByType(HorizontalPicker);
67
- expect(HorizontalPickers).toHaveLength(1);
69
+ const inputs = instance.findAllByType(_TextInput);
70
+ expect(inputs).toHaveLength(1);
68
71
  await act(async () => {
69
- HorizontalPickers[0].props.onChangeValue(128);
72
+ inputs[0].props.onChange('128');
70
73
  });
71
74
 
72
75
  const BottomButtonViews = instance.findAllByType(BottomButtonView);
@@ -77,13 +80,13 @@ describe('Test SetupConfigCondition', () => {
77
80
  expect(mockGoBack).toBeCalled();
78
81
  });
79
82
 
80
- it('Test render when have no maximum', async () => {
83
+ const testWithRange = async (rangeMin, rangeMax, value, message) => {
81
84
  useRoute.mockReturnValue({
82
85
  params: {
83
86
  item: {
84
87
  id: 1,
85
- range_min: 0,
86
- range_max: 0,
88
+ range_min: rangeMin,
89
+ range_max: rangeMax,
87
90
  decimal_behind: 0,
88
91
  title: 'is below (<)',
89
92
  sensor_type: 'air_quality',
@@ -94,38 +97,41 @@ describe('Test SetupConfigCondition', () => {
94
97
  tree = await create(wrapComponent());
95
98
  });
96
99
  const instance = tree.root;
97
- const picker = instance.findByType(HorizontalPicker);
98
- const scroll = picker.findByType(Animated.ScrollView);
99
- const views = scroll.findByType(View).findAll((el) => {
100
- return el.type === View && el.props.accessibilityLabel?.includes('x-');
101
- });
102
- expect(views).toHaveLength(10 + 1);
103
- });
104
- it('Test render when have no maximum and value is near maximum', async () => {
105
- useRoute.mockReturnValue({
106
- params: {
107
- item: {
108
- id: 1,
109
- range_min: 0,
110
- range_max: 0,
111
- decimal_behind: 1,
112
- title: 'is below (<)',
113
- sensor_type: 'air_quality',
114
- },
115
- defaultCondition: {
116
- value: 0.9,
117
- },
118
- },
119
- });
100
+ const input = instance.findByType(_TextInput);
101
+
120
102
  await act(async () => {
121
- tree = await create(wrapComponent());
103
+ input.props.onChange(value);
122
104
  });
123
- const instance = tree.root;
124
- const picker = instance.findByType(HorizontalPicker);
125
- const scroll = picker.findByType(Animated.ScrollView);
126
- const views = scroll.findByType(View).findAll((el) => {
127
- return el.type === View && el.props.accessibilityLabel?.includes('x-');
105
+
106
+ const bottomButtonView = instance.findByType(BottomButtonView);
107
+
108
+ await act(async () => {
109
+ bottomButtonView.props.onPressMain();
128
110
  });
129
- expect(views).toHaveLength(20 - 1); // I don't know why
111
+
112
+ if (message === null) {
113
+ expect(mockNavigate).toBeCalled();
114
+ } else {
115
+ expect(spyToastError).toBeCalledWith(message);
116
+ }
117
+ };
118
+
119
+ it('Test render when have maximum', async () => {
120
+ await testWithRange(0, 10, '128', 'Value must be less than 10');
121
+ });
122
+
123
+ it('Test render when have no maximum', async () => {
124
+ await testWithRange(0, null, '128', null);
125
+ });
126
+ it('Test render when have minium', async () => {
127
+ await testWithRange(4, 10, '1', 'Value must be greater than 4');
128
+ });
129
+
130
+ it('Test render when have no minium', async () => {
131
+ await testWithRange(null, 10, '1', null);
132
+ });
133
+
134
+ it('Test render when have input not number', async () => {
135
+ await testWithRange(null, 10, 'abc', 'Value must be a number');
130
136
  });
131
137
  });
@@ -0,0 +1,25 @@
1
+ import React from 'react';
2
+ import { AUTOMATE_TYPE } from '../../../configs/Constants';
3
+ import Routes from '../../../utils/Route';
4
+ import AddTypeSmart from './AddTypeSmart';
5
+
6
+ const smartTypes = [
7
+ {
8
+ type: AUTOMATE_TYPE.VALUE_CHANGE,
9
+ route: Routes.SelectMonitorDevices,
10
+ },
11
+ {
12
+ type: AUTOMATE_TYPE.SCHEDULE,
13
+ route: Routes.SetSchedule,
14
+ },
15
+ {
16
+ type: AUTOMATE_TYPE.EVENT,
17
+ route: Routes.SelectMonitorDevices,
18
+ },
19
+ ];
20
+
21
+ const AddAutomationTypeSmart = ({ route }) => {
22
+ return <AddTypeSmart route={route} smartTypes={smartTypes} />;
23
+ };
24
+
25
+ export default AddAutomationTypeSmart;