@eohjsc/react-native-smart-city 0.7.3-rc23 → 0.7.3-rc25
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 +3 -2
- package/src/commons/ActionGroup/index.js +2 -2
- package/src/commons/Widgets/IFrameWithConfig/IFrameWithConfig.js +4 -15
- package/src/commons/Widgets/IFrameWithConfig/__tests__/IFrameWithConfig.test.js +29 -18
- package/src/configs/API.js +1 -1
- package/src/hooks/IoT/__test__/useRemoteControl.test.js +14 -7
- package/src/hooks/IoT/useRemoteControl.js +18 -7
- package/src/iot/RemoteControl/Bluetooth.js +19 -22
- package/src/iot/RemoteControl/Internet.js +11 -3
- package/src/navigations/UnitStack.js +1 -3
- package/src/screens/Device/EditDevice/__test__/EditDevice.test.js +4 -4
- package/src/screens/Device/EditDevice/index.js +2 -2
- package/src/screens/Device/__test__/BluetoothDevice.test.js +300 -0
- package/src/screens/Device/components/BluetoothDevice.js +135 -0
- package/src/screens/Device/components/SensorDisplayItem.js +4 -3
- package/src/screens/Device/detail.js +63 -55
- package/src/screens/SubUnit/EditSubUnit.js +3 -3
- package/src/screens/SubUnit/__test__/AddSubUnit.test.js +4 -2
- package/src/screens/SubUnit/__test__/EditSubUnit.test.js +3 -3
- package/src/utils/bluetooth.js +3 -0
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.7.3-
|
|
4
|
+
"version": "0.7.3-rc25",
|
|
5
5
|
"description": "TODO",
|
|
6
6
|
"main": "index.js",
|
|
7
7
|
"files": [
|
|
@@ -119,6 +119,7 @@
|
|
|
119
119
|
"lodash": "^4.17.19",
|
|
120
120
|
"lottie-react-native": "^6.7.2",
|
|
121
121
|
"metro-react-native-babel-preset": "0.73.9",
|
|
122
|
+
"md5": "^2.3.0",
|
|
122
123
|
"moment": "^2.27.0",
|
|
123
124
|
"moment-timezone": "^0.5.32",
|
|
124
125
|
"node-html-parser": "^2.0.2",
|
|
@@ -131,7 +132,7 @@
|
|
|
131
132
|
"pusher-js-auth": "^4.0.1",
|
|
132
133
|
"python-struct": "^1.1.3",
|
|
133
134
|
"querystring": "^0.2.0",
|
|
134
|
-
"react": "18.
|
|
135
|
+
"react": "^18.3.1",
|
|
135
136
|
"react-content-loader": "^6.0.3",
|
|
136
137
|
"react-dom": "18.2.0",
|
|
137
138
|
"react-hooks-global-state": "^2.1.0",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, {
|
|
1
|
+
import React, { useMemo } from 'react';
|
|
2
2
|
import Text from '../Text';
|
|
3
3
|
import OneBigButtonTemplate from './OneBigButtonTemplate';
|
|
4
4
|
import ThreeButtonTemplate from './ThreeButtonTemplate';
|
|
@@ -74,4 +74,4 @@ const ActionGroup = (params = {}) => {
|
|
|
74
74
|
);
|
|
75
75
|
};
|
|
76
76
|
|
|
77
|
-
export default
|
|
77
|
+
export default ActionGroup;
|
|
@@ -9,16 +9,10 @@ import { useFetchConfigHistory } from '../../UnitSummary/ConfigHistoryChart';
|
|
|
9
9
|
import API from '../../../configs/API';
|
|
10
10
|
import WebView from 'react-native-webview';
|
|
11
11
|
import { TouchableOpacity, View } from 'react-native';
|
|
12
|
-
import { useRemoteControl } from '../../../hooks/IoT';
|
|
13
|
-
import { useSCContextSelector } from '../../../context';
|
|
14
12
|
|
|
15
13
|
const IFrameWithConfig = memo(
|
|
16
|
-
({ item = {}, widgetStyle, isWidgetBox, isSetting, isEditing }) => {
|
|
14
|
+
({ item = {}, widgetStyle, isWidgetBox, isSetting, isEditing, doAction }) => {
|
|
17
15
|
const ref = useRef();
|
|
18
|
-
const sendRemoteCommand = useRemoteControl();
|
|
19
|
-
const userId = useSCContextSelector(
|
|
20
|
-
(state) => state?.auth?.account?.user?.id
|
|
21
|
-
);
|
|
22
16
|
|
|
23
17
|
const { configuration } = item;
|
|
24
18
|
const { url } = configuration || {};
|
|
@@ -73,7 +67,7 @@ const IFrameWithConfig = memo(
|
|
|
73
67
|
}, [item?.id, url]);
|
|
74
68
|
|
|
75
69
|
const triggerAction = useCallback(
|
|
76
|
-
async ({ actionKey, actionIndex, data }) => {
|
|
70
|
+
async ({ actionKey, actionIndex, data, interrupted = false, silent }) => {
|
|
77
71
|
let action = null;
|
|
78
72
|
if (!configuration?.actions) {
|
|
79
73
|
return;
|
|
@@ -90,14 +84,9 @@ const IFrameWithConfig = memo(
|
|
|
90
84
|
if (!action) {
|
|
91
85
|
return;
|
|
92
86
|
}
|
|
93
|
-
await
|
|
94
|
-
action.action_data.end_device,
|
|
95
|
-
action.action_data,
|
|
96
|
-
data,
|
|
97
|
-
userId
|
|
98
|
-
);
|
|
87
|
+
await doAction(action.action_data, data, interrupted, silent);
|
|
99
88
|
},
|
|
100
|
-
[configuration?.actions,
|
|
89
|
+
[configuration?.actions, doAction]
|
|
101
90
|
);
|
|
102
91
|
|
|
103
92
|
const onMessage = useCallback(
|
|
@@ -12,13 +12,6 @@ import { TouchableOpacity, View } from 'react-native';
|
|
|
12
12
|
import { refFunctions } from '../../../../../__mocks__/react-native-webview';
|
|
13
13
|
|
|
14
14
|
const mockDoAction = jest.fn();
|
|
15
|
-
jest.mock('../../../../hooks/IoT', () => {
|
|
16
|
-
return {
|
|
17
|
-
...jest.requireActual('../../../../hooks/IoT'),
|
|
18
|
-
useRemoteControl: () => mockDoAction,
|
|
19
|
-
};
|
|
20
|
-
});
|
|
21
|
-
|
|
22
15
|
jest.mock('../../../../iot/states', () => {
|
|
23
16
|
return {
|
|
24
17
|
...jest.requireActual('../../../../iot/states'),
|
|
@@ -48,7 +41,9 @@ describe('Test IFrame With Config', () => {
|
|
|
48
41
|
});
|
|
49
42
|
|
|
50
43
|
test('test with widget box', async () => {
|
|
51
|
-
const { root } = await render(
|
|
44
|
+
const { root } = await render(
|
|
45
|
+
<IFrameWithConfig doAction={mockDoAction} isWidgetBox item={item} />
|
|
46
|
+
);
|
|
52
47
|
|
|
53
48
|
const iframes = root.findAllByType(WebView);
|
|
54
49
|
expect(iframes).toHaveLength(1);
|
|
@@ -60,7 +55,9 @@ describe('Test IFrame With Config', () => {
|
|
|
60
55
|
|
|
61
56
|
test('test with query string', async () => {
|
|
62
57
|
item.configuration.url = 'http://localhost:3000/?test=1';
|
|
63
|
-
const { root } = await render(
|
|
58
|
+
const { root } = await render(
|
|
59
|
+
<IFrameWithConfig isWidgetBox doAction={mockDoAction} item={item} />
|
|
60
|
+
);
|
|
64
61
|
|
|
65
62
|
const iframes = root.findAllByType(WebView);
|
|
66
63
|
expect(iframes).toHaveLength(1);
|
|
@@ -71,7 +68,9 @@ describe('Test IFrame With Config', () => {
|
|
|
71
68
|
});
|
|
72
69
|
|
|
73
70
|
test('test reload', async () => {
|
|
74
|
-
const { root } = await render(
|
|
71
|
+
const { root } = await render(
|
|
72
|
+
<IFrameWithConfig isWidgetBox doAction={mockDoAction} item={item} />
|
|
73
|
+
);
|
|
75
74
|
|
|
76
75
|
const buttons = root.findAllByType(TouchableOpacity);
|
|
77
76
|
expect(buttons).toHaveLength(1);
|
|
@@ -84,7 +83,9 @@ describe('Test IFrame With Config', () => {
|
|
|
84
83
|
});
|
|
85
84
|
|
|
86
85
|
test('test iframe is ready', async () => {
|
|
87
|
-
const { root } = await render(
|
|
86
|
+
const { root } = await render(
|
|
87
|
+
<IFrameWithConfig isWidgetBox doAction={mockDoAction} item={item} />
|
|
88
|
+
);
|
|
88
89
|
const webview = root.findByType(WebView);
|
|
89
90
|
|
|
90
91
|
await act(async () => {
|
|
@@ -129,7 +130,9 @@ describe('Test IFrame With Config', () => {
|
|
|
129
130
|
});
|
|
130
131
|
|
|
131
132
|
test('test iframe request more height', async () => {
|
|
132
|
-
const { root } = await render(
|
|
133
|
+
const { root } = await render(
|
|
134
|
+
<IFrameWithConfig isWidgetBox doAction={mockDoAction} item={item} />
|
|
135
|
+
);
|
|
133
136
|
const webview = root.findByType(WebView);
|
|
134
137
|
|
|
135
138
|
await act(async () => {
|
|
@@ -151,7 +154,9 @@ describe('Test IFrame With Config', () => {
|
|
|
151
154
|
});
|
|
152
155
|
|
|
153
156
|
test('test iframe receive message from unexpected origin', async () => {
|
|
154
|
-
const { root } = await render(
|
|
157
|
+
const { root } = await render(
|
|
158
|
+
<IFrameWithConfig isWidgetBox doAction={mockDoAction} item={item} />
|
|
159
|
+
);
|
|
155
160
|
const webview = root.findByType(WebView);
|
|
156
161
|
|
|
157
162
|
await act(async () => {
|
|
@@ -187,7 +192,9 @@ describe('Test IFrame With Config', () => {
|
|
|
187
192
|
item.configuration.history_configs = [{ id: 1 }];
|
|
188
193
|
item.configuration.realtime_configs = [];
|
|
189
194
|
|
|
190
|
-
const { root } = await render(
|
|
195
|
+
const { root } = await render(
|
|
196
|
+
<IFrameWithConfig isWidgetBox doAction={mockDoAction} item={item} />
|
|
197
|
+
);
|
|
191
198
|
const webview = root.findByType(WebView);
|
|
192
199
|
|
|
193
200
|
await act(async () => {
|
|
@@ -226,7 +233,9 @@ describe('Test IFrame With Config', () => {
|
|
|
226
233
|
item.configuration.history_configs = [{ id: 1 }];
|
|
227
234
|
item.configuration.realtime_configs = [];
|
|
228
235
|
|
|
229
|
-
const { root } = await render(
|
|
236
|
+
const { root } = await render(
|
|
237
|
+
<IFrameWithConfig isWidgetBox doAction={mockDoAction} item={item} />
|
|
238
|
+
);
|
|
230
239
|
const webview = root.findByType(WebView);
|
|
231
240
|
|
|
232
241
|
await act(async () => {
|
|
@@ -303,7 +312,9 @@ describe('Test IFrame With Config', () => {
|
|
|
303
312
|
]
|
|
304
313
|
) => {
|
|
305
314
|
item.configuration.actions = actions;
|
|
306
|
-
const { root } = await render(
|
|
315
|
+
const { root } = await render(
|
|
316
|
+
<IFrameWithConfig isWidgetBox doAction={mockDoAction} item={item} />
|
|
317
|
+
);
|
|
307
318
|
const webview = root.findByType(WebView);
|
|
308
319
|
|
|
309
320
|
await act(async () => {
|
|
@@ -324,9 +335,9 @@ describe('Test IFrame With Config', () => {
|
|
|
324
335
|
test('test iframe trigger action', async () => {
|
|
325
336
|
await triggerAction({ actionKey: 'xxx' });
|
|
326
337
|
expect(mockDoAction).toHaveBeenCalledWith(
|
|
327
|
-
item.configuration.actions[0].action_data.end_device,
|
|
328
338
|
item.configuration.actions[0].action_data,
|
|
329
339
|
undefined,
|
|
340
|
+
false,
|
|
330
341
|
undefined
|
|
331
342
|
);
|
|
332
343
|
});
|
|
@@ -334,9 +345,9 @@ describe('Test IFrame With Config', () => {
|
|
|
334
345
|
test('test iframe trigger by index', async () => {
|
|
335
346
|
await triggerAction({ actionIndex: 0 });
|
|
336
347
|
expect(mockDoAction).toHaveBeenCalledWith(
|
|
337
|
-
item.configuration.actions[0].action_data.end_device,
|
|
338
348
|
item.configuration.actions[0].action_data,
|
|
339
349
|
undefined,
|
|
350
|
+
false,
|
|
340
351
|
undefined
|
|
341
352
|
);
|
|
342
353
|
});
|
package/src/configs/API.js
CHANGED
|
@@ -54,7 +54,7 @@ const API = {
|
|
|
54
54
|
JSON_CONFIGURATION: '/chip_manager/chips/json_configuration/',
|
|
55
55
|
},
|
|
56
56
|
DEVICE: {
|
|
57
|
-
|
|
57
|
+
DEVICE_DETAIL: (id) => `/property_manager/devices/${id}/`,
|
|
58
58
|
DISPLAY: (id) => `/property_manager/devices/${id}/display/`,
|
|
59
59
|
SIDE_MENU_DETAIL: (id, side_menu_id) =>
|
|
60
60
|
`/property_manager/devices/${id}/display/side_menu/${side_menu_id}/`,
|
|
@@ -81,7 +81,8 @@ describe('Test useRemoteControl', () => {
|
|
|
81
81
|
sensor,
|
|
82
82
|
action,
|
|
83
83
|
data,
|
|
84
|
-
userId
|
|
84
|
+
userId,
|
|
85
|
+
false
|
|
85
86
|
);
|
|
86
87
|
expect(sendCommandOverInternet).not.toBeCalled();
|
|
87
88
|
expect(sendCommandOverHomeAssistant).not.toBeCalled();
|
|
@@ -106,13 +107,15 @@ describe('Test useRemoteControl', () => {
|
|
|
106
107
|
sensor,
|
|
107
108
|
action,
|
|
108
109
|
data,
|
|
109
|
-
userId
|
|
110
|
+
userId,
|
|
111
|
+
false
|
|
110
112
|
);
|
|
111
113
|
expect(sendCommandOverInternet).toBeCalledWith(
|
|
112
114
|
sensor,
|
|
113
115
|
action,
|
|
114
116
|
data,
|
|
115
|
-
'bluetooth'
|
|
117
|
+
'bluetooth',
|
|
118
|
+
false
|
|
116
119
|
);
|
|
117
120
|
expect(sendCommandOverInternet).toBeCalledTimes(1);
|
|
118
121
|
expect(sendCommandOverHomeAssistant).not.toBeCalled();
|
|
@@ -143,7 +146,8 @@ describe('Test useRemoteControl', () => {
|
|
|
143
146
|
sensor,
|
|
144
147
|
action,
|
|
145
148
|
data,
|
|
146
|
-
userId
|
|
149
|
+
userId,
|
|
150
|
+
false
|
|
147
151
|
);
|
|
148
152
|
expect(sendCommandOverInternet).not.toBeCalled();
|
|
149
153
|
expect(sendCommandOverHomeAssistant).not.toBeCalled();
|
|
@@ -181,7 +185,8 @@ describe('Test useRemoteControl', () => {
|
|
|
181
185
|
sensor,
|
|
182
186
|
action,
|
|
183
187
|
data,
|
|
184
|
-
userId
|
|
188
|
+
userId,
|
|
189
|
+
false
|
|
185
190
|
);
|
|
186
191
|
expect(sendCommandOverHomeAssistant).not.toBeCalled();
|
|
187
192
|
});
|
|
@@ -202,7 +207,8 @@ describe('Test useRemoteControl', () => {
|
|
|
202
207
|
sensor,
|
|
203
208
|
action,
|
|
204
209
|
data,
|
|
205
|
-
'internet'
|
|
210
|
+
'internet',
|
|
211
|
+
false
|
|
206
212
|
);
|
|
207
213
|
expect(sendCommandOverInternet).toBeCalledTimes(1);
|
|
208
214
|
expect(sendCommandOverBluetooth).not.toBeCalled();
|
|
@@ -303,7 +309,8 @@ describe('Test useRemoteControl', () => {
|
|
|
303
309
|
sensor,
|
|
304
310
|
action,
|
|
305
311
|
data,
|
|
306
|
-
'bluetooth'
|
|
312
|
+
'bluetooth',
|
|
313
|
+
false
|
|
307
314
|
);
|
|
308
315
|
expect(sendCommandOverBluetooth).toBeCalledTimes(6);
|
|
309
316
|
expect(sendCommandOverInternet).toBeCalledTimes(1);
|
|
@@ -26,13 +26,13 @@ const handleReconnectionDeviceBle = async (device, action, data, userId) => {
|
|
|
26
26
|
return SEND_COMMAND_OVER_BLUETOOTH_FAIL;
|
|
27
27
|
};
|
|
28
28
|
|
|
29
|
-
const useRemoteControl = () => {
|
|
29
|
+
const useRemoteControl = (bluetoothDevice) => {
|
|
30
30
|
const homeAssistantConnections = useSCContextSelector(
|
|
31
31
|
(state) => state.iot.homeassistant.connections
|
|
32
32
|
);
|
|
33
33
|
|
|
34
34
|
const sendRemoteCommand = useCallback(
|
|
35
|
-
async (device, action, data, userId) => {
|
|
35
|
+
async (device, action, data, userId, silent = false) => {
|
|
36
36
|
// No action, raise not authorized
|
|
37
37
|
let result = false;
|
|
38
38
|
if (!action) {
|
|
@@ -41,9 +41,18 @@ const useRemoteControl = () => {
|
|
|
41
41
|
);
|
|
42
42
|
return result;
|
|
43
43
|
}
|
|
44
|
-
|
|
45
44
|
if (action.command_prefer_over_bluetooth) {
|
|
46
|
-
|
|
45
|
+
// custom control for i/o pin over bluetooth
|
|
46
|
+
if (bluetoothDevice?.isConnected && action.arduino_action) {
|
|
47
|
+
return bluetoothDevice.control(action, data, silent);
|
|
48
|
+
}
|
|
49
|
+
let res = await sendCommandOverBluetooth(
|
|
50
|
+
device,
|
|
51
|
+
action,
|
|
52
|
+
data,
|
|
53
|
+
userId,
|
|
54
|
+
silent
|
|
55
|
+
);
|
|
47
56
|
if (res === BLUETOOOH_DEVICE_UNSTABLE) {
|
|
48
57
|
res = await handleReconnectionDeviceBle(device, action, data, userId);
|
|
49
58
|
}
|
|
@@ -57,7 +66,8 @@ const useRemoteControl = () => {
|
|
|
57
66
|
device,
|
|
58
67
|
action,
|
|
59
68
|
data,
|
|
60
|
-
'bluetooth'
|
|
69
|
+
'bluetooth',
|
|
70
|
+
silent
|
|
61
71
|
);
|
|
62
72
|
}
|
|
63
73
|
return result;
|
|
@@ -80,7 +90,8 @@ const useRemoteControl = () => {
|
|
|
80
90
|
device,
|
|
81
91
|
action,
|
|
82
92
|
data,
|
|
83
|
-
action.command_prefer_over_bluetooth ? 'bluetooth' : 'internet'
|
|
93
|
+
action.command_prefer_over_bluetooth ? 'bluetooth' : 'internet',
|
|
94
|
+
silent
|
|
84
95
|
);
|
|
85
96
|
}
|
|
86
97
|
|
|
@@ -95,7 +106,7 @@ const useRemoteControl = () => {
|
|
|
95
106
|
|
|
96
107
|
return await sendCommandOverInternet(device, action, data, 'internet');
|
|
97
108
|
},
|
|
98
|
-
[homeAssistantConnections]
|
|
109
|
+
[bluetoothDevice, homeAssistantConnections]
|
|
99
110
|
);
|
|
100
111
|
|
|
101
112
|
return sendRemoteCommand;
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
/* eslint-disable promise/prefer-await-to-callbacks */
|
|
2
2
|
import base64 from 'react-native-base64';
|
|
3
|
-
import {
|
|
3
|
+
import { ScanMode } from 'react-native-ble-plx';
|
|
4
4
|
import CryptoAesCbc from 'react-native-crypto-aes-cbc';
|
|
5
5
|
|
|
6
6
|
import { base64ToHex, ToastBottomHelper } from '../../utils/Utils';
|
|
7
7
|
import { BLE } from '../../configs';
|
|
8
8
|
import t from '../../hooks/Common/useTranslations';
|
|
9
|
+
import { bleManager } from '../../utils/bluetooth';
|
|
9
10
|
|
|
10
11
|
const bluetoothDevices = {};
|
|
11
12
|
const needToScanDevices = [];
|
|
12
|
-
const bleManager = new BleManager();
|
|
13
13
|
|
|
14
14
|
let isScanning = false;
|
|
15
15
|
|
|
@@ -98,7 +98,8 @@ export const sendCommandOverBluetooth = async (
|
|
|
98
98
|
sensor,
|
|
99
99
|
action,
|
|
100
100
|
data,
|
|
101
|
-
userID
|
|
101
|
+
userID,
|
|
102
|
+
silent
|
|
102
103
|
) => {
|
|
103
104
|
const bluetooth = sensor?.remote_control_options?.bluetooth;
|
|
104
105
|
let device = null;
|
|
@@ -115,7 +116,8 @@ export const sendCommandOverBluetooth = async (
|
|
|
115
116
|
user: userID,
|
|
116
117
|
},
|
|
117
118
|
false,
|
|
118
|
-
bluetooth?.is_use_secret_key
|
|
119
|
+
bluetooth?.is_use_secret_key,
|
|
120
|
+
silent
|
|
119
121
|
);
|
|
120
122
|
return result;
|
|
121
123
|
};
|
|
@@ -124,18 +126,6 @@ export const getDeviceByName = (name) => {
|
|
|
124
126
|
return bluetoothDevices[name];
|
|
125
127
|
};
|
|
126
128
|
|
|
127
|
-
export const isDeviceConnected = (deviceName) => {
|
|
128
|
-
return !!bluetoothDevices[deviceName];
|
|
129
|
-
};
|
|
130
|
-
|
|
131
|
-
export const subcribeCharacteristicNotify = async (device, onListener) => {
|
|
132
|
-
return await device.monitorCharacteristicForService(
|
|
133
|
-
BLE.BLE_REMOTE_SERVICE_UUID,
|
|
134
|
-
BLE.BLE_REMOTE_CHARACTERISTIC_UUID_TX,
|
|
135
|
-
onListener
|
|
136
|
-
);
|
|
137
|
-
};
|
|
138
|
-
|
|
139
129
|
export const readCharacteristic = async (
|
|
140
130
|
device,
|
|
141
131
|
serviceUUID = BLE.BLE_REMOTE_SERVICE_UUID,
|
|
@@ -151,13 +141,16 @@ export const sendDataOverBluetooth = async (
|
|
|
151
141
|
device = null,
|
|
152
142
|
data,
|
|
153
143
|
keepConnect = false,
|
|
154
|
-
isUseSecretKey = false
|
|
144
|
+
isUseSecretKey = false,
|
|
145
|
+
silent
|
|
155
146
|
) => {
|
|
156
147
|
if (!device) {
|
|
157
148
|
return SEND_COMMAND_OVER_BLUETOOTH_FAIL;
|
|
158
149
|
}
|
|
159
150
|
|
|
160
|
-
|
|
151
|
+
if (!silent) {
|
|
152
|
+
ToastBottomHelper.info(t('Sending command via bluetooth'));
|
|
153
|
+
}
|
|
161
154
|
|
|
162
155
|
let connectedDevice = null;
|
|
163
156
|
let fullDataDevice = null;
|
|
@@ -196,9 +189,11 @@ export const sendDataOverBluetooth = async (
|
|
|
196
189
|
hasResponse = true;
|
|
197
190
|
const notify = error ? '' : base64.decode(characteristic.value);
|
|
198
191
|
if (notify === BLE.BLE_RESPONSE_OK) {
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
192
|
+
if (!silent) {
|
|
193
|
+
ToastBottomHelper.success(
|
|
194
|
+
t('control_device_via_bluetooth_successfully')
|
|
195
|
+
);
|
|
196
|
+
}
|
|
202
197
|
if (!keepConnect) {
|
|
203
198
|
await connectedDevice.cancelConnection();
|
|
204
199
|
}
|
|
@@ -234,7 +229,9 @@ export const sendDataOverBluetooth = async (
|
|
|
234
229
|
base64.encode(JSON.stringify(data))
|
|
235
230
|
);
|
|
236
231
|
}
|
|
237
|
-
|
|
232
|
+
if (!silent) {
|
|
233
|
+
ToastBottomHelper.info(t('command_is_sent_to_device_via_bluetooth'));
|
|
234
|
+
}
|
|
238
235
|
} catch (e) {
|
|
239
236
|
await connectedDevice.cancelConnection();
|
|
240
237
|
ToastBottomHelper.error(t('command_is_fail_to_send_via_bluetooth'));
|
|
@@ -4,7 +4,13 @@ import { API } from '../../configs';
|
|
|
4
4
|
import { axiosPost } from '../../utils/Apis/axios';
|
|
5
5
|
import { ToastBottomHelper } from '../../utils/Utils';
|
|
6
6
|
|
|
7
|
-
export const sendCommandOverInternet = async (
|
|
7
|
+
export const sendCommandOverInternet = async (
|
|
8
|
+
sensor,
|
|
9
|
+
action,
|
|
10
|
+
data,
|
|
11
|
+
source,
|
|
12
|
+
silent
|
|
13
|
+
) => {
|
|
8
14
|
if (data !== null) {
|
|
9
15
|
if (Number.isInteger(data)) {
|
|
10
16
|
data = data.toString(16).toUpperCase();
|
|
@@ -13,13 +19,15 @@ export const sendCommandOverInternet = async (sensor, action, data, source) => {
|
|
|
13
19
|
if (typeof data === 'object') {
|
|
14
20
|
data = JSON.stringify(data);
|
|
15
21
|
}
|
|
16
|
-
|
|
22
|
+
if (!silent) {
|
|
23
|
+
ToastBottomHelper.info(t('Sending command via internet'));
|
|
24
|
+
}
|
|
17
25
|
const { success } = await axiosPost(API.DEVICE.TRIGGER_ACTION(sensor.id), {
|
|
18
26
|
key: action.key,
|
|
19
27
|
data,
|
|
20
28
|
source,
|
|
21
29
|
});
|
|
22
|
-
if (success) {
|
|
30
|
+
if (success && !silent) {
|
|
23
31
|
ToastBottomHelper.success(t('Command is sent to device via internet'));
|
|
24
32
|
}
|
|
25
33
|
return success;
|
|
@@ -4,7 +4,6 @@ import { createNativeStackNavigator } from '@react-navigation/native-stack';
|
|
|
4
4
|
import { get } from 'lodash';
|
|
5
5
|
import React, { memo, useContext, useEffect } from 'react';
|
|
6
6
|
import { AppState, View } from 'react-native';
|
|
7
|
-
import { BleManager } from 'react-native-ble-plx';
|
|
8
7
|
|
|
9
8
|
import Text from '../commons/Text';
|
|
10
9
|
import { API, Colors, Device } from '../configs';
|
|
@@ -68,11 +67,10 @@ import { axiosGet, fetchWithCache } from '../utils/Apis/axios';
|
|
|
68
67
|
import { ToastBottomHelper } from '../utils/Utils';
|
|
69
68
|
import { HanetCameraStack } from './HanetCameraStack';
|
|
70
69
|
import { styles } from './UnitStackStyles';
|
|
70
|
+
import { bleManager } from '../utils/bluetooth';
|
|
71
71
|
|
|
72
72
|
const Stack = createNativeStackNavigator();
|
|
73
73
|
|
|
74
|
-
const bleManager = new BleManager();
|
|
75
|
-
|
|
76
74
|
export const UnitStack = memo((props) => {
|
|
77
75
|
const t = useTranslations();
|
|
78
76
|
const { setAction } = useContext(SCContext);
|
|
@@ -63,7 +63,7 @@ describe('Test EditDevice', () => {
|
|
|
63
63
|
await act(async () => {
|
|
64
64
|
textInput.props.onChange('new_name');
|
|
65
65
|
});
|
|
66
|
-
mock.onPatch(API.DEVICE.
|
|
66
|
+
mock.onPatch(API.DEVICE.DEVICE_DETAIL(1)).reply(200, { name: 'new_name' });
|
|
67
67
|
await act(async () => {
|
|
68
68
|
await alertAction.props.rightButtonClick();
|
|
69
69
|
});
|
|
@@ -92,7 +92,7 @@ describe('Test EditDevice', () => {
|
|
|
92
92
|
await act(async () => {
|
|
93
93
|
textInput.props.onChange('new_name');
|
|
94
94
|
});
|
|
95
|
-
mock.onPatch(API.DEVICE.
|
|
95
|
+
mock.onPatch(API.DEVICE.DEVICE_DETAIL(1)).reply(400);
|
|
96
96
|
await act(async () => {
|
|
97
97
|
await alertAction.props.rightButtonClick();
|
|
98
98
|
});
|
|
@@ -120,7 +120,7 @@ describe('Test EditDevice', () => {
|
|
|
120
120
|
await buttonDelete[0].props.onPress();
|
|
121
121
|
});
|
|
122
122
|
expect(alertAction.props.visible).toBeTruthy();
|
|
123
|
-
mock.onDelete(API.DEVICE.
|
|
123
|
+
mock.onDelete(API.DEVICE.DEVICE_DETAIL(1)).reply(204);
|
|
124
124
|
await act(async () => {
|
|
125
125
|
await alertAction.props.rightButtonClick();
|
|
126
126
|
});
|
|
@@ -144,7 +144,7 @@ describe('Test EditDevice', () => {
|
|
|
144
144
|
await buttonDelete[0].props.onPress();
|
|
145
145
|
});
|
|
146
146
|
expect(alertAction.props.visible).toBeTruthy();
|
|
147
|
-
mock.onDelete(API.DEVICE.
|
|
147
|
+
mock.onDelete(API.DEVICE.DEVICE_DETAIL(1)).reply(400);
|
|
148
148
|
await act(async () => {
|
|
149
149
|
await alertAction.props.rightButtonClick();
|
|
150
150
|
});
|
|
@@ -30,7 +30,7 @@ const EditDevice = memo(() => {
|
|
|
30
30
|
useEditDevice();
|
|
31
31
|
const renameSensor = useCallback(async () => {
|
|
32
32
|
const { success, data } = await axiosPatch(
|
|
33
|
-
API.DEVICE.
|
|
33
|
+
API.DEVICE.DEVICE_DETAIL(sensor?.id),
|
|
34
34
|
{
|
|
35
35
|
name: inputName,
|
|
36
36
|
}
|
|
@@ -55,7 +55,7 @@ const EditDevice = memo(() => {
|
|
|
55
55
|
|
|
56
56
|
const deleteSensor = useCallback(async () => {
|
|
57
57
|
hideAlertAction();
|
|
58
|
-
const { success } = await axiosDelete(API.DEVICE.
|
|
58
|
+
const { success } = await axiosDelete(API.DEVICE.DEVICE_DETAIL(sensor?.id));
|
|
59
59
|
|
|
60
60
|
if (success) {
|
|
61
61
|
navigation.pop(2);
|