@eohjsc/react-native-smart-city 0.7.3-rc24 → 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 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-rc24",
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.2.0",
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, { memo, useMemo } from '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 memo(ActionGroup);
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 sendRemoteCommand(
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, sendRemoteCommand, userId]
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(<IFrameWithConfig isWidgetBox item={item} />);
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(<IFrameWithConfig isWidgetBox item={item} />);
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(<IFrameWithConfig isWidgetBox item={item} />);
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(<IFrameWithConfig isWidgetBox item={item} />);
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(<IFrameWithConfig isWidgetBox item={item} />);
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(<IFrameWithConfig isWidgetBox item={item} />);
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(<IFrameWithConfig isWidgetBox item={item} />);
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(<IFrameWithConfig isWidgetBox item={item} />);
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(<IFrameWithConfig isWidgetBox item={item} />);
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
  });
@@ -54,7 +54,7 @@ const API = {
54
54
  JSON_CONFIGURATION: '/chip_manager/chips/json_configuration/',
55
55
  },
56
56
  DEVICE: {
57
- SENSOR_DETAIL: (id) => `/property_manager/devices/${id}/`,
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
- let res = await sendCommandOverBluetooth(device, action, data, userId);
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 { BleManager, ScanMode } from 'react-native-ble-plx';
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
- ToastBottomHelper.error(t('Sending command via bluetooth'));
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
- ToastBottomHelper.success(
200
- t('control_device_via_bluetooth_successfully')
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
- ToastBottomHelper.error(t('command_is_sent_to_device_via_bluetooth'));
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 (sensor, action, data, source) => {
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
- ToastBottomHelper.success(t('Sending command via internet'));
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.SENSOR_DETAIL(1)).reply(200, { name: 'new_name' });
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.SENSOR_DETAIL(1)).reply(400);
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.SENSOR_DETAIL(1)).reply(204);
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.SENSOR_DETAIL(1)).reply(400);
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.SENSOR_DETAIL(sensor?.id),
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.SENSOR_DETAIL(sensor?.id));
58
+ const { success } = await axiosDelete(API.DEVICE.DEVICE_DETAIL(sensor?.id));
59
59
 
60
60
  if (success) {
61
61
  navigation.pop(2);