@eohjsc/react-native-smart-city 0.3.12 → 0.3.15

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 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.12",
4
+ "version": "0.3.15",
5
5
  "description": "TODO",
6
6
  "main": "index.js",
7
7
  "files": [
@@ -7,7 +7,7 @@ import { useRemoteControl } from '../../hooks/IoT';
7
7
  import { useConfigGlobalState } from '../../iot/states';
8
8
  import IconComponent from '../IconComponent';
9
9
 
10
- const ItemQuickAction = memo(({ sensor, wrapperStyle, setStatus, unit }) => {
10
+ const ItemQuickAction = memo(({ sensor, wrapperStyle, setStatus }) => {
11
11
  const [isSendingCommand, setIsSendingCommand] = useState(false);
12
12
  const [action, setAction] = useState(sensor.action);
13
13
  // eslint-disable-next-line no-unused-vars
@@ -47,6 +47,8 @@ const NumberUpDownActionTemplate = ({ actionGroup, doAction, sensor }) => {
47
47
  const configValue = configValues[config];
48
48
  if (configValue !== null && configValue !== undefined) {
49
49
  setValue(configValue);
50
+ } else {
51
+ setValue(valueDefault);
50
52
  }
51
53
  // eslint-disable-next-line react-hooks/exhaustive-deps
52
54
  }, [JSON.stringify(configValues)]);
@@ -6,6 +6,8 @@ import { useConfigGlobalState } from '../../../iot/states';
6
6
  import OnOffButtonTemplate from './OnOffButtonTemplate';
7
7
  import OnOffSimpleTemplate from './OnOffSimpleTemplate';
8
8
 
9
+ let temp;
10
+
9
11
  const getComponent = (template) => {
10
12
  switch (template) {
11
13
  case 'on_off_button_action_template': // todo refactor later with backend
@@ -20,11 +22,28 @@ const getComponent = (template) => {
20
22
  const OnOffTemplate = memo(({ actionGroup, doAction, sensor }) => {
21
23
  const { configuration } = actionGroup;
22
24
  const { action_data, action_on_data, action_off_data } = configuration;
23
- const [isOn, setIsOn] = useState(null);
24
25
 
25
26
  // eslint-disable-next-line no-unused-vars
26
27
  const [configValues, _] = useConfigGlobalState('configValues');
27
28
 
29
+ const getIsOnValue = useCallback(() => {
30
+ const { is_on_value, config } = configuration;
31
+ const configValue = configValues[config];
32
+ if (is_on_value && is_on_value.length > 0) {
33
+ return is_on_value.includes(configValue);
34
+ }
35
+ return !!configValue;
36
+ }, [configValues, configuration]);
37
+
38
+ const [isOn, setIsOn] = useState(false);
39
+ const [tempIsOn, setTempIsOn] = useState(getIsOnValue());
40
+
41
+ const updateStatusFromPusher = () => {
42
+ setTimeout(() => {
43
+ setTempIsOn(temp);
44
+ }, 2500);
45
+ };
46
+
28
47
  const actionName = useCallback(
29
48
  (text) => {
30
49
  const actionNameType = `${
@@ -34,7 +53,9 @@ const OnOffTemplate = memo(({ actionGroup, doAction, sensor }) => {
34
53
  },
35
54
  [actionGroup?.title, sensor?.name]
36
55
  );
56
+
37
57
  const triggerAction = useCallback(async () => {
58
+ setTempIsOn((prev) => !prev);
38
59
  switch (sensor?.device_type) {
39
60
  case DEVICE_TYPE.ZIGBEE:
40
61
  if (action_on_data && action_off_data) {
@@ -53,6 +74,13 @@ const OnOffTemplate = memo(({ actionGroup, doAction, sensor }) => {
53
74
  }
54
75
  }
55
76
  break;
77
+ case DEVICE_TYPE.LG_THINQ:
78
+ if (action_data) {
79
+ await doAction(action_data, JSON.stringify({ value: !isOn }));
80
+ }
81
+
82
+ updateStatusFromPusher();
83
+ break;
56
84
  default:
57
85
  if (action_data) {
58
86
  if (isOn) {
@@ -88,19 +116,12 @@ const OnOffTemplate = memo(({ actionGroup, doAction, sensor }) => {
88
116
  ]);
89
117
 
90
118
  useEffect(() => {
91
- const { is_on_value, config } = configuration;
92
- const configValue = configValues[config];
93
- if (is_on_value && is_on_value.length > 0) {
94
- setIsOn(is_on_value.includes(configValue));
95
- return;
96
- }
97
- setIsOn(configValue);
98
- }, [
99
- configuration.config,
100
- configValues,
101
- configuration.is_on_value,
102
- configuration,
103
- ]);
119
+ setIsOn(getIsOnValue());
120
+ }, [getIsOnValue]);
121
+
122
+ useEffect(() => {
123
+ temp = isOn;
124
+ }, [isOn]);
104
125
 
105
126
  useEffect(() => {
106
127
  if (sensor?.device_type === DEVICE_TYPE.LG_THINQ) {
@@ -118,7 +139,7 @@ const OnOffTemplate = memo(({ actionGroup, doAction, sensor }) => {
118
139
  return (
119
140
  <>
120
141
  <Component
121
- isOn={isOn}
142
+ isOn={tempIsOn}
122
143
  triggerAction={triggerAction}
123
144
  actionGroup={actionGroup}
124
145
  disabled={!action_data && !action_on_data && !action_off_data}
@@ -90,7 +90,7 @@ const TimerActionTemplate = ({ actionGroup = {}, doAction, sensor = {} }) => {
90
90
 
91
91
  const doActionTime = useCallback(
92
92
  (hour, minute) => {
93
- doAction(configuration.action_data, [hour, minute]);
93
+ doAction(configuration.action_data, JSON.stringify({ hour, minute }));
94
94
  if (sensor.is_managed_by_backend) {
95
95
  hour !== undefined &&
96
96
  minute !== undefined &&
@@ -113,7 +113,7 @@ const TimerActionTemplate = ({ actionGroup = {}, doAction, sensor = {} }) => {
113
113
 
114
114
  const doActionHour = useCallback(
115
115
  (hour) => {
116
- doAction(configuration.action_data, hour);
116
+ doAction(configuration.action_data, JSON.stringify({ hour }));
117
117
  if (
118
118
  sensor.is_managed_by_backend &&
119
119
  sensor.device_type !== 'GOOGLE_HOME'
@@ -326,7 +326,7 @@ describe('Test NumberUpDownActionTemplate', () => {
326
326
  });
327
327
  const instance = wrapper.root;
328
328
  const text = instance.findAllByType(ActivityIndicator);
329
- expect(text).toHaveLength(1);
329
+ expect(text).toHaveLength(0);
330
330
  });
331
331
  test('action up sensor device type is GOOGLE_HOME', async () => {
332
332
  const mockDoAction = jest.fn();
@@ -1,5 +1,5 @@
1
1
  /* eslint-disable promise/prefer-await-to-callbacks */
2
- import { TESTID } from '../../../configs/Constants';
2
+ import { DEVICE_TYPE, TESTID } from '../../../configs/Constants';
3
3
  import { watchMultiConfigs } from '../../../iot/Monitor';
4
4
  import React from 'react';
5
5
  import { View } from 'react-native';
@@ -149,6 +149,7 @@ describe('Test OnOffTemplate', () => {
149
149
  await act(async () => {
150
150
  tree = await create(wrapComponent(actionGroup, mockDoAction, sensor));
151
151
  });
152
+
152
153
  const instance = tree.root;
153
154
  const template = instance.findByType(OnOffSimpleTemplate);
154
155
  expect(template.props.isOn).toEqual(true);
@@ -230,6 +231,39 @@ describe('Test OnOffTemplate', () => {
230
231
  expect(mockDoAction).toHaveBeenCalledWith(action_data, false);
231
232
  });
232
233
 
234
+ test('render with template OnOffSimpleActionTemplate with just action_data lg_thinq', async () => {
235
+ actionGroup = {
236
+ template: 'OnOffSimpleActionTemplate',
237
+ configuration: {
238
+ config: 5,
239
+ action_data: action_data,
240
+ icon: 'up',
241
+ is_on_value: [2],
242
+ },
243
+ title: 'Turn on / off',
244
+ };
245
+ const mockDoAction = jest.fn();
246
+ await act(async () => {
247
+ tree = await create(
248
+ wrapComponent(actionGroup, mockDoAction, {
249
+ device_type: DEVICE_TYPE.LG_THINQ,
250
+ })
251
+ );
252
+ });
253
+ const instance = tree.root;
254
+ const template = instance.findAllByType(OnOffSimpleTemplate);
255
+ expect(template).toHaveLength(1);
256
+ expect(template[0].props.disabled).toBeFalsy();
257
+
258
+ await act(async () => {
259
+ await template[0].props.triggerAction();
260
+ });
261
+ expect(mockDoAction).toHaveBeenCalledWith(
262
+ action_data,
263
+ JSON.stringify({ value: false })
264
+ );
265
+ });
266
+
233
267
  test('render with template OnOffSimpleActionTemplate disabled', async () => {
234
268
  actionGroup = {
235
269
  template: 'OnOffSimpleActionTemplate',
@@ -89,6 +89,9 @@ describe('Test TimerActionTemplate without config value', () => {
89
89
  await dateTimePicker.props.onConfirm();
90
90
  });
91
91
  expect(dateTimePicker.props.isVisible).toBeFalsy();
92
- expect(mockDoAction).toHaveBeenCalledWith(action_data, [11, 0]);
92
+ expect(mockDoAction).toHaveBeenCalledWith(
93
+ action_data,
94
+ JSON.stringify({ hour: 11, minute: 0 })
95
+ );
93
96
  });
94
97
  });
@@ -363,7 +363,7 @@ describe('Test ActionGroup', () => {
363
363
  });
364
364
  const instance = wrapper.root;
365
365
  const activity = instance.findAllByType(ActivityIndicator);
366
- expect(activity.length).toEqual(1);
366
+ expect(activity.length).toEqual(0);
367
367
  });
368
368
 
369
369
  test('render ActionGroup StatesGridActionTemplate', async () => {
@@ -82,7 +82,7 @@ const ItemDevice = memo(
82
82
  const textConnected = isConnected
83
83
  ? t('connected')
84
84
  : isConnecting
85
- ? ''
85
+ ? t('connecting')
86
86
  : t('disconnected');
87
87
 
88
88
  return (
@@ -98,7 +98,7 @@ const ItemDevice = memo(
98
98
  <TouchableOpacity onPress={goToSensorDisplay}>
99
99
  <IconComponent icon={sensor.icon} iconKit={sensor.icon_kit} />
100
100
  </TouchableOpacity>
101
- <ItemQuickAction sensor={sensor} unit={unit} />
101
+ {!isConnecting && <ItemQuickAction sensor={sensor} unit={unit} />}
102
102
  </View>
103
103
  <TouchableOpacity onPress={goToSensorDisplay}>
104
104
  <Text
@@ -19,16 +19,20 @@ const NumberUpDownActionTemplate = memo(({ data, onSelectAction }) => {
19
19
  const { configuration, template, title } = data;
20
20
  const { config, text_format, min_value, max_value, action } = configuration;
21
21
  const [configValues] = useConfigGlobalState('configValues');
22
- const [value, setValue] = useState((config && configValues[config]) || 28);
22
+ const [value, setValue] = useState();
23
23
  const [actionName, setActionName] = useState(null);
24
+ const valueDefault = 28;
24
25
 
25
26
  useEffect(() => {
26
27
  if (!config) {
28
+ setValue(valueDefault);
27
29
  return;
28
30
  }
29
31
  const configValue = configValues[config];
30
32
  if (configValue !== null && configValue !== undefined) {
31
33
  setValue(configValue);
34
+ } else {
35
+ setValue(valueDefault);
32
36
  }
33
37
  }, [configValues, config]);
34
38
 
@@ -190,7 +190,6 @@ describe('Test useRemoteControl', () => {
190
190
  act(() => {
191
191
  sendRemoteCommand.current(sensor, action, data, userId, actionName);
192
192
  });
193
- expect(sendCommandOverLGThinq).toBeCalledWith(sensor, action, data);
194
193
  expect(sendCommandOverBluetooth).not.toBeCalled();
195
194
  expect(sendCommandOverGoogleHome).not.toBeCalled();
196
195
  expect(sendCommandOverInternet).not.toBeCalled();
@@ -2,7 +2,6 @@ import { useCallback } from 'react';
2
2
  import { useSCContextSelector } from '../../context';
3
3
  import { sendCommandOverGoogleHome } from '../../iot/RemoteControl/GoogleHome';
4
4
  import { sendCommandOverInternet } from '../../iot/RemoteControl/Internet';
5
- import { sendCommandOverLGThinq } from '../../iot/RemoteControl/LG';
6
5
  import {
7
6
  sendCommandOverBluetooth,
8
7
  SEND_COMMAND_OVER_BLUETOOTH_FAIL,
@@ -64,10 +63,6 @@ const useRemoteControl = () => {
64
63
  data
65
64
  );
66
65
  }
67
-
68
- if (action.command_prefer_over_lg) {
69
- result = await sendCommandOverLGThinq(sensor, action, data);
70
- }
71
66
  return result;
72
67
  },
73
68
  [ggHomeConnections]
@@ -94,17 +94,17 @@ export async function updateStateByLgThinq(device_id, data) {
94
94
  setConfigGlobalState('configValues', { ...configValues });
95
95
  }
96
96
 
97
- export async function fetchDeviceStatusLG(sensor) {
97
+ export async function fetchDeviceStatusLG(endDevice) {
98
98
  // still need some delay
99
99
  setTimeout(async () => {
100
100
  const { success, data: dataStatus } = await axiosGet(
101
- API.IOT.LG.DEVICE_STATUS(sensor.id),
101
+ API.IOT.LG.DEVICE_STATUS(endDevice.id),
102
102
  {},
103
103
  true
104
104
  );
105
105
 
106
106
  if (success) {
107
- updateStateByLgThinq(sensor.lg_device_id, dataStatus);
107
+ updateStateByLgThinq(endDevice.lg_device_id, dataStatus);
108
108
  }
109
109
  }, 1000);
110
110
  }
@@ -128,11 +128,11 @@ export const lgThinqConnect = async (options) => {
128
128
  const option = options[i];
129
129
  for (let j = 0; j < option.lg_devices.length; j++) {
130
130
  const lgDevice = option.lg_devices[j];
131
- const sensor = {
132
- id: lgDevice.sensor_id,
131
+ const endDevice = {
132
+ id: lgDevice.end_device_id,
133
133
  lg_device_id: lgDevice.device_id,
134
134
  };
135
- await fetchDeviceStatusLG(sensor);
135
+ await fetchDeviceStatusLG(endDevice);
136
136
  }
137
137
  }
138
138
  };
@@ -107,7 +107,7 @@ describe('Remote Control LG Thinq', () => {
107
107
  lg_devices: [
108
108
  {
109
109
  id: 1,
110
- sensor_id: 2,
110
+ end_device_id: 2,
111
111
  device_id: 'DEVICE_ID',
112
112
  configs: [
113
113
  {
@@ -188,7 +188,7 @@ describe('Remote Control LG Thinq', () => {
188
188
  lg_devices: [
189
189
  {
190
190
  id: 1,
191
- sensor_id: 2,
191
+ end_device_id: 2,
192
192
  device_id: 'DEVICE_ID',
193
193
  configs: [
194
194
  {
@@ -49,6 +49,7 @@ export const SensorDisplayItem = ({
49
49
  },
50
50
  [sensor, userId, sendRemoteCommand]
51
51
  );
52
+
52
53
  if (item.configuration.type === 'compass') {
53
54
  setShowWindDirection(true);
54
55
  }
@@ -1,5 +1,7 @@
1
1
  import React from 'react';
2
2
  import { create, act } from 'react-test-renderer';
3
+ import Toast from 'react-native-toast-message';
4
+
3
5
  import MockAdapter from 'axios-mock-adapter';
4
6
  import { SCProvider } from '../../../context';
5
7
  import { mockSCStore } from '../../../context/mockStore';
@@ -117,6 +119,30 @@ describe('Test ScriptDetail', () => {
117
119
  expect(alertAction.props.visible).toBeFalsy();
118
120
  });
119
121
 
122
+ test('test rename script failed', async () => {
123
+ await act(async () => {
124
+ tree = await create(wrapComponent(route));
125
+ });
126
+ const instance = tree.root;
127
+ const menu = instance.findByType(MenuActionMore);
128
+ const alertAction = instance.findByType(AlertAction);
129
+ const rename = menu.props.listMenuItem[0];
130
+
131
+ await act(async () => {
132
+ await menu.props.onItemClick(rename);
133
+ await menu.props.hideComplete();
134
+ });
135
+ expect(menu.props.isVisible).toBeFalsy();
136
+ expect(alertAction.props.visible).toBeTruthy();
137
+
138
+ mock.onPatch(API.AUTOMATE.SCRIPT(1)).reply(500, { name: 'new_name' });
139
+ await act(async () => {
140
+ await alertAction.props.rightButtonClick();
141
+ });
142
+ expect(alertAction.props.visible).toBeFalsy();
143
+ expect(Toast.show).toHaveBeenCalled();
144
+ });
145
+
120
146
  test('test delete script', async () => {
121
147
  await act(async () => {
122
148
  tree = await create(wrapComponent(route));
@@ -99,9 +99,14 @@ const ScriptDetail = ({ route }) => {
99
99
  name: inputName,
100
100
  }
101
101
  );
102
- success && setScriptName(script.name);
102
+ if (success) {
103
+ setScriptName(script.name);
104
+ ToastBottomHelper.success(t('rename_successfully'));
105
+ } else {
106
+ ToastBottomHelper.error(t('rename_failed'));
107
+ }
103
108
  hideAlertAction();
104
- }, [id, inputName, hideAlertAction]);
109
+ }, [id, inputName, hideAlertAction, t]);
105
110
 
106
111
  const deleteScript = useCallback(async () => {
107
112
  hideAlertAction();
@@ -113,12 +118,16 @@ const ScriptDetail = ({ route }) => {
113
118
  } else {
114
119
  goBack();
115
120
  }
121
+ ToastBottomHelper.success(t('removed_successfully'));
122
+ } else {
123
+ ToastBottomHelper.error(t('remove_failed'));
116
124
  }
117
125
  }, [
118
126
  hideAlertAction,
119
127
  id,
120
128
  isCreateScriptSuccess,
121
129
  isCreateNewAction,
130
+ t,
122
131
  dispatch,
123
132
  isAutomateTab,
124
133
  goBack,
@@ -19,7 +19,9 @@ const RunningDevices = memo(({ unit, summaryDetail }) => {
19
19
  await connectGoogleHome(unit.remote_control_options.googlehome))();
20
20
  }
21
21
  }
22
- }, [connectGoogleHome, unit]);
22
+ // eslint-disable-next-line react-hooks/exhaustive-deps
23
+ }, [unit]);
24
+
23
25
  return (
24
26
  <View style={styles.container}>
25
27
  {!!devices &&