@eohjsc/react-native-smart-city 0.7.26 → 0.7.30

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 (74) hide show
  1. package/index.js +2 -0
  2. package/package.json +2 -1
  3. package/src/commons/Dashboard/MyDashboardDevice/__test__/index.test.js +68 -0
  4. package/src/commons/Dashboard/MyDashboardDevice/index.js +46 -11
  5. package/src/commons/Dashboard/MyUnit/__test__/MyUnit.test.js +43 -11
  6. package/src/commons/Dashboard/MyUnit/index.js +40 -32
  7. package/src/commons/ModalAlert/index.js +51 -0
  8. package/src/commons/ModalAlert/styles.js +54 -0
  9. package/src/commons/SubUnit/ShortDetail.js +20 -4
  10. package/src/commons/SubUnit/__test__/ShortDetail.test.js +46 -1
  11. package/src/configs/API.js +6 -0
  12. package/src/configs/AccessibilityLabel.js +1 -0
  13. package/src/configs/Constants.js +7 -0
  14. package/src/configs/SCConfig.js +6 -0
  15. package/src/context/SCContext.tsx +12 -1
  16. package/src/context/SCStore.ts +14 -0
  17. package/src/context/actionType.ts +10 -0
  18. package/src/context/mockStore.ts +30 -1
  19. package/src/context/reducer.ts +35 -0
  20. package/src/hooks/IoT/useRemoteControl.js +4 -1
  21. package/src/hooks/IoT/useWatchSharedChips.js +130 -0
  22. package/src/hooks/Review/__test__/useInAppReview.test.js +99 -0
  23. package/src/hooks/Review/useInAppReview.js +70 -0
  24. package/src/hooks/useMqtt.js +78 -27
  25. package/src/iot/Monitor.js +149 -26
  26. package/src/iot/UpdateStates.js +60 -0
  27. package/src/iot/mqtt.js +177 -22
  28. package/src/navigations/UnitStack.js +16 -0
  29. package/src/screens/ActivityLog/FilterPopup.js +4 -79
  30. package/src/screens/ActivityLog/ItemLog.js +1 -0
  31. package/src/screens/ActivityLog/__test__/FilterPopup.test.js +2 -6
  32. package/src/screens/ActivityLog/__test__/index.test.js +51 -29
  33. package/src/screens/ActivityLog/index.js +0 -1
  34. package/src/screens/ActivityLog/styles/filterPopupStyles.js +5 -2
  35. package/src/screens/AddNewGateway/RenameNewDevices.js +5 -0
  36. package/src/screens/AddNewGateway/__test__/RenameNewDevices.test.js +18 -0
  37. package/src/screens/Automate/AddNewAction/ReceiverSelect.js +208 -0
  38. package/src/screens/Automate/AddNewAction/SetupScriptEmail.js +1 -1
  39. package/src/screens/Automate/AddNewAction/SetupScriptNotify.js +18 -28
  40. package/src/screens/Automate/AddNewAction/SetupScriptReceiverEmail.js +22 -129
  41. package/src/screens/Automate/AddNewAction/SetupScriptReceiverNotify.js +59 -0
  42. package/src/screens/Automate/AddNewAction/SetupScriptReceiverSms.js +22 -129
  43. package/src/screens/Automate/AddNewAction/SetupScriptSms.js +1 -1
  44. package/src/screens/Automate/AddNewAction/Styles/{SetupScriptReceiverEmailStyles.js → ReceiverSelectStyles.js} +18 -1
  45. package/src/screens/Automate/AddNewAction/__test__/SetupScriptNotify.test.js +16 -33
  46. package/src/screens/Automate/AddNewAction/__test__/SetupScriptReceiverEmail.test.js +10 -8
  47. package/src/screens/Automate/AddNewAction/__test__/SetupScriptReceiverNotify.test.js +217 -0
  48. package/src/screens/Automate/AddNewAction/__test__/SetupScriptReceiverSms.test.js +10 -8
  49. package/src/screens/Automate/Components/InputName.js +5 -1
  50. package/src/screens/Automate/OneTap/__test__/AddNewOneTap.test.js +18 -0
  51. package/src/screens/Automate/ScriptDetail/index.js +6 -6
  52. package/src/screens/CreatePassword/__test__/index.test.js +133 -0
  53. package/src/screens/CreatePassword/index.js +134 -0
  54. package/src/screens/CreatePassword/styles.js +45 -0
  55. package/src/screens/Device/__test__/DeviceDetail-3rdparty.test.js +447 -0
  56. package/src/screens/Device/__test__/DeviceDetail-arduino.test.js +344 -0
  57. package/src/screens/Device/__test__/{mqttDetail.test.js → DeviceDetail-modbus.test.js} +287 -320
  58. package/src/screens/Device/__test__/DeviceDetail-zigbee.test.js +451 -0
  59. package/src/screens/Device/__test__/DeviceDetail.test.js +502 -0
  60. package/src/screens/Device/__test__/detail.test.js +61 -3
  61. package/src/screens/Device/__test__/sensorDisplayItem.test.js +28 -3
  62. package/src/screens/Device/components/SensorDisplayItem.js +16 -14
  63. package/src/screens/Device/detail.js +14 -6
  64. package/src/screens/Device/hooks/useDeviceWatchConfigControl.js +3 -2
  65. package/src/screens/Device/styles.js +0 -5
  66. package/src/screens/EnterPassword/__test__/EnterPassword.test.js +76 -1
  67. package/src/screens/EnterPassword/index.js +34 -4
  68. package/src/screens/EnterPassword/styles.js +1 -1
  69. package/src/utils/FactoryGateway.js +597 -0
  70. package/src/utils/I18n/translations/en.js +10 -0
  71. package/src/utils/I18n/translations/vi.js +10 -0
  72. package/src/utils/Route/index.js +3 -1
  73. package/src/utils/Validation.js +5 -0
  74. package/src/utils/store.js +5 -0
@@ -1,12 +1,36 @@
1
1
  import mqtt from 'precompiled-mqtt/dist/mqtt.browser';
2
- import { useEffect, useMemo, useRef, useState } from 'react';
2
+ import { useCallback, useContext, useEffect, useMemo, useState } from 'react';
3
3
 
4
4
  import API from '../configs/API';
5
+ import { SCContext } from '../context';
6
+ import { Action } from '../context/actionType';
5
7
  import { handleMqttMessage } from '../iot/mqtt';
6
8
  import { axiosGet } from '../utils/Apis/axios';
9
+ import { getConfigsById } from '../utils/store';
7
10
 
8
- const useChipJsonConfiguration = (dashboardId) => {
11
+ export const mqttClientsPool = new Map();
12
+
13
+ const useChipJsonConfiguration = ({ dashboardId, ready = true }) => {
14
+ const { setAction } = useContext(SCContext);
9
15
  const [chips, setChips] = useState([]);
16
+ const [isFetching, setIsFetching] = useState(true);
17
+
18
+ const handleUpdateConfigsById = useCallback(
19
+ (listChip) => {
20
+ const configsById = listChip.reduce((acc, chip) => {
21
+ chip.sensors.forEach((sensor) => {
22
+ sensor.configs.forEach((config) => {
23
+ acc[config.id] = config;
24
+ });
25
+ });
26
+ return acc;
27
+ }, {});
28
+ setAction(Action.UPDATE_CONFIGS_BY_ID, configsById);
29
+ },
30
+ // eslint-disable-next-line react-hooks/exhaustive-deps
31
+ []
32
+ );
33
+
10
34
  // get chip data
11
35
  useEffect(() => {
12
36
  const getGateways = async () => {
@@ -17,49 +41,54 @@ const useChipJsonConfiguration = (dashboardId) => {
17
41
  });
18
42
  if (success) {
19
43
  setChips(data);
44
+ handleUpdateConfigsById(data);
20
45
  }
46
+ setIsFetching(false);
21
47
  };
22
- getGateways();
23
- }, [dashboardId]);
48
+ ready && getGateways();
49
+ }, [dashboardId, ready, handleUpdateConfigsById]);
24
50
 
25
51
  return {
26
52
  chips,
53
+ isFetching,
27
54
  };
28
55
  };
29
56
 
30
57
  export const connectMqttServer = (mqttServer, code) => {
31
- const { websocket_host, websocket_port } = mqttServer;
32
- const client = mqtt.connect(`wss://${websocket_host}:${websocket_port}`, {
33
- username: code,
34
- password: code,
35
- });
58
+ const { websocket_host, websocket_port, is_wss = true } = mqttServer;
59
+ const client = mqtt.connect(
60
+ `ws${is_wss ? 's' : ''}://${websocket_host}:${websocket_port}`,
61
+ {
62
+ username: code,
63
+ password: code,
64
+ }
65
+ );
36
66
  return client;
37
67
  };
38
68
 
39
69
  const connectChipMqtt = (chip) => {
40
- const configById = {};
41
70
  const { code, mqtt_server } = chip;
71
+ let currentChip = chip;
42
72
 
43
73
  const client = connectMqttServer(mqtt_server, code);
44
74
 
45
- (chip?.sensors).forEach((sensor) => {
46
- (sensor?.configs).forEach((config) => {
47
- configById[config.id] = config;
48
- });
49
- });
50
-
51
75
  client.on('connect', () => {
52
76
  client.subscribe(`eoh/chip/${code}/#`);
53
77
  });
54
78
  client.on('message', (topic, payload, packet) => {
55
79
  try {
80
+ const configsById = getConfigsById();
56
81
  const payloadObject = JSON.parse(payload.toString());
57
- handleMqttMessage(topic, payloadObject, code, chip, configById);
82
+ handleMqttMessage(topic, payloadObject, code, currentChip, configsById);
58
83
  // eslint-disable-next-line no-empty
59
84
  } catch {}
60
85
  });
61
86
 
62
- return client;
87
+ const updateChip = (newChip) => {
88
+ currentChip = newChip;
89
+ };
90
+
91
+ return { client, updateChip };
63
92
  };
64
93
 
65
94
  export const useConnectChipMqtt = (chips) => {
@@ -69,31 +98,53 @@ export const useConnectChipMqtt = (chips) => {
69
98
  );
70
99
 
71
100
  const mqttConfigs = useMemo(() => {
72
- let configById = {};
101
+ let configsById = {};
73
102
 
74
103
  mqttChips.forEach((chip) => {
75
104
  chip?.sensors.forEach((sensor) => {
76
105
  sensor?.configs.forEach((config) => {
77
- configById[config.id] = config;
106
+ configsById[config.id] = config;
78
107
  });
79
108
  });
80
109
  });
81
110
 
82
- return configById;
111
+ return configsById;
83
112
  }, [mqttChips]);
84
113
 
85
- const mqttClientsRef = useRef([]);
86
114
  useEffect(() => {
87
115
  mqttChips.forEach((chip) => {
88
- const client = connectChipMqtt(chip);
89
- mqttClientsRef.current.push(client);
116
+ let pool = mqttClientsPool.get(chip.id);
117
+ if (!pool) {
118
+ const { client, updateChip } = connectChipMqtt(chip);
119
+ pool = { client, updateChip, count: 0, timer: null };
120
+ mqttClientsPool.set(chip.id, pool);
121
+ } else {
122
+ pool.updateChip(chip);
123
+ }
124
+ if (pool.timer) {
125
+ clearTimeout(pool.timer);
126
+ pool.timer = null;
127
+ }
128
+ pool.count += 1;
90
129
  });
91
130
 
92
131
  return () => {
93
- mqttClientsRef.current.forEach((client) => {
94
- client && client.end();
132
+ mqttChips.forEach((chip) => {
133
+ const pool = mqttClientsPool.get(chip.id);
134
+ if (!pool) {
135
+ return;
136
+ }
137
+ pool.count -= 1;
138
+ if (pool.count) {
139
+ return;
140
+ }
141
+
142
+ pool.timer = setTimeout(() => {
143
+ const latest = mqttClientsPool.get(chip.id);
144
+ latest?.client?.end();
145
+ mqttClientsPool.delete(chip.id);
146
+ }, 1000);
95
147
  });
96
- mqttClientsRef.current = [];
97
148
  };
98
149
  }, [mqttChips]);
99
150
 
@@ -1,18 +1,28 @@
1
- import { API } from '../configs';
2
- import { getConfigGlobalState, setConfigGlobalState } from './states';
3
1
  import _ from 'lodash';
4
2
  import Pusher from 'pusher-js/react-native';
5
3
  import PusherBatchAuthorizer from 'pusher-js-auth';
6
- import { axiosPost } from '../utils/Apis/axios';
4
+ import { API } from '../configs';
7
5
  import { SCConfig } from '../configs';
8
- import api from '../utils/Apis/axios';
6
+ import { WATCH_DATA_CHIP_TYPE } from '../configs/Constants';
7
+ import api, { axiosPost } from '../utils/Apis/axios';
8
+ import { getConfigsById, getSharedChipsById } from '../utils/store';
9
+
10
+ import { updateGlobalValues } from './UpdateStates';
11
+ import {
12
+ handleChipData,
13
+ handleModbusData,
14
+ handleZigbeeData,
15
+ handleThirdPartyData,
16
+ handleSelfSensorData,
17
+ } from './mqtt';
9
18
 
10
19
  Pusher.logToConsole = true;
11
- let pusher = null;
20
+ const pusherMap = new Map();
12
21
 
13
- const getPusher = () => {
14
- if (!pusher) {
15
- pusher = new Pusher(SCConfig.pusherAppKey, {
22
+ const getPusher = (watch_type) => {
23
+ watch_type = watch_type || 'config';
24
+ if (!pusherMap.has(watch_type)) {
25
+ const pusher = new Pusher(SCConfig.pusherAppKey, {
16
26
  cluster: 'ap1',
17
27
  authorizer: PusherBatchAuthorizer,
18
28
  authEndpoint: SCConfig.apiRoot + API.IOT.CHIP_MANAGER.PUSHER_AUTH(),
@@ -22,24 +32,29 @@ const getPusher = () => {
22
32
  Accept: 'application/json',
23
33
  Authorization: api.headers.Authorization,
24
34
  },
35
+ params: { watch_type },
25
36
  },
26
37
  });
38
+ pusherMap.set(watch_type, pusher);
27
39
  }
28
- return pusher;
40
+ return pusherMap.get(watch_type);
29
41
  };
30
42
 
31
- const destroyPusher = () => {
32
- pusher.disconnect();
33
- pusher = null;
43
+ const destroyPusher = (watch_type) => {
44
+ const pusher = pusherMap.get(watch_type);
45
+ if (pusher) {
46
+ pusher.disconnect();
47
+ pusherMap.delete(watch_type);
48
+ }
34
49
  };
35
50
 
36
51
  const watchingConfigs = {};
52
+ const CHANNEL_CONFIG = 'private-config_v2';
53
+
54
+ const updateConfigValue = (configId, value) => {
55
+ const configsById = getConfigsById();
37
56
 
38
- export const updateGlobalValue = (configId, value) => {
39
- const configValues = getConfigGlobalState('configValues');
40
- let newConfigValues = { ...configValues };
41
- newConfigValues[configId] = value;
42
- setConfigGlobalState('configValues', newConfigValues);
57
+ handleChipData(configsById, configId, { v: value.value }, value.last_updated);
43
58
  };
44
59
 
45
60
  const watchConfig = (configId) => {
@@ -48,8 +63,10 @@ const watchConfig = (configId) => {
48
63
  return;
49
64
  }
50
65
  watchingConfigs[configId] = 1;
51
- const channel = getPusher().subscribe(`private-config_v2-${configId}`);
52
- channel.bind('new-value', updateGlobalValue.bind(channel, configId));
66
+ const channel = getPusher('config').subscribe(
67
+ `${CHANNEL_CONFIG}-${configId}`
68
+ );
69
+ channel.bind('new-raw', updateConfigValue.bind(channel, configId));
53
70
  };
54
71
 
55
72
  const unwatchConfig = (configId) => {
@@ -59,9 +76,9 @@ const unwatchConfig = (configId) => {
59
76
  watchingConfigs[configId] -= 1;
60
77
  if (!watchingConfigs[configId]) {
61
78
  delete watchingConfigs[configId];
62
- getPusher().unsubscribe(`private-config_v2-${configId}`);
79
+ getPusher('config').unsubscribe(`${CHANNEL_CONFIG}-${configId}`);
63
80
  if (_.isEmpty(watchingConfigs)) {
64
- destroyPusher();
81
+ destroyPusher('config');
65
82
  }
66
83
  }
67
84
  };
@@ -90,15 +107,15 @@ export const realWatchMultiConfigs = async (configIds) => {
90
107
  API.IOT.CHIP_MANAGER.WATCH_CONFIGS(),
91
108
  {
92
109
  configs: configIds,
110
+ is_raw: true,
93
111
  }
94
112
  );
95
113
  if (success) {
96
- const configValues = getConfigGlobalState('configValues');
97
- const newConfigValues = { ...configValues, ...data };
98
- setConfigGlobalState('configValues', newConfigValues);
99
- for (const configId in data) {
114
+ updateGlobalValues(data);
115
+
116
+ Object.keys(data).forEach((configId) => {
100
117
  watchConfig(configId);
101
- }
118
+ });
102
119
  }
103
120
  };
104
121
 
@@ -111,3 +128,109 @@ export const unwatchAllConfigs = async () => {
111
128
  await unwatchMultiConfigs(Object.keys(watchingConfigs));
112
129
  }
113
130
  };
131
+
132
+ // WATCH DATA CHIP
133
+ const watchingDataChips = {};
134
+ const CHANNEL_DATA_CHIP = 'private-data';
135
+
136
+ const updateDataChip = (chipId, value) => {
137
+ const configsById = getConfigsById();
138
+ const chip = getSharedChipsById()[chipId];
139
+
140
+ const { type, data = {} } = value;
141
+
142
+ switch (type) {
143
+ case WATCH_DATA_CHIP_TYPE.MODBUS:
144
+ data.data &&
145
+ handleModbusData(chip, configsById, data.data, data.last_updated);
146
+ break;
147
+ case WATCH_DATA_CHIP_TYPE.ZIGBEE:
148
+ Object.keys(data).forEach((key) => {
149
+ handleZigbeeData(
150
+ chip,
151
+ configsById,
152
+ key,
153
+ data[key],
154
+ data[key].last_updated
155
+ );
156
+ });
157
+ break;
158
+ case WATCH_DATA_CHIP_TYPE.THIRD_PARTY:
159
+ Object.keys(data).forEach((key) => {
160
+ handleThirdPartyData(
161
+ chip,
162
+ configsById,
163
+ key,
164
+ data[key],
165
+ data[key].last_updated
166
+ );
167
+ });
168
+ break;
169
+ case WATCH_DATA_CHIP_TYPE.SELF:
170
+ handleSelfSensorData(chip, configsById, data, data.last_updated);
171
+ break;
172
+ /* istanbul ignore next */
173
+ default:
174
+ break;
175
+ }
176
+ };
177
+
178
+ const watchDataChip = async (chipId) => {
179
+ if (watchingDataChips[chipId]) {
180
+ watchingDataChips[chipId] += 1;
181
+ return;
182
+ }
183
+ watchingDataChips[chipId] = 1;
184
+ const channel = getPusher('data').subscribe(`${CHANNEL_DATA_CHIP}-${chipId}`);
185
+ channel.bind('new-data', updateDataChip.bind(channel, chipId));
186
+ };
187
+
188
+ export const watchMultiDataChips = async (chipIds) => {
189
+ const { success, data } = await axiosPost(
190
+ API.IOT.CHIP_MANAGER.WATCH_DATA_CHIPS(),
191
+ {
192
+ chips: chipIds,
193
+ }
194
+ );
195
+ if (success) {
196
+ Object.keys(data).forEach((chipId) => {
197
+ const dataChip = data[chipId];
198
+ updateDataChip(chipId, {
199
+ type: WATCH_DATA_CHIP_TYPE.MODBUS,
200
+ data: dataChip?.last_modbus_data,
201
+ });
202
+ updateDataChip(chipId, {
203
+ type: WATCH_DATA_CHIP_TYPE.ZIGBEE,
204
+ data: dataChip?.last_zigbee_data,
205
+ });
206
+ updateDataChip(chipId, {
207
+ type: WATCH_DATA_CHIP_TYPE.THIRD_PARTY,
208
+ data: dataChip?.last_third_party_data,
209
+ });
210
+ updateDataChip(chipId, {
211
+ type: WATCH_DATA_CHIP_TYPE.SELF,
212
+ data: dataChip?.last_self_data,
213
+ });
214
+ watchDataChip(chipId);
215
+ });
216
+ }
217
+ };
218
+
219
+ const unwatchDataChip = (chipId) => {
220
+ if (!watchingDataChips[chipId]) {
221
+ return;
222
+ }
223
+ watchingDataChips[chipId] -= 1;
224
+
225
+ if (!watchingDataChips[chipId]) {
226
+ delete watchingDataChips[chipId];
227
+ getPusher('data').unsubscribe(`${CHANNEL_DATA_CHIP}-${chipId}`);
228
+ if (_.isEmpty(watchingDataChips)) {
229
+ destroyPusher('data');
230
+ }
231
+ }
232
+ };
233
+
234
+ export const unwatchMultiDataChips = async (chipIds) => {
235
+ chipIds.map((chipId) => unwatchDataChip(chipId));
236
+ };
@@ -0,0 +1,60 @@
1
+ import moment from 'moment';
2
+
3
+ import { getConfigGlobalState, setConfigGlobalState } from './states';
4
+
5
+ const isNewer = (valueTime, valueMicro, currentTime, currentMicro) => {
6
+ if (valueTime?.isAfter(currentTime)) {
7
+ return true;
8
+ }
9
+ if (valueTime?.isSame(currentTime)) {
10
+ return valueMicro > currentMicro;
11
+ }
12
+ return false;
13
+ };
14
+
15
+ export const getLastUpdated = (time) => {
16
+ let last_updated_micro = 0;
17
+ if (!time) {
18
+ return { last_updated: moment(), last_updated_micro };
19
+ }
20
+ if (typeof time === 'number') {
21
+ const millis = time * 1000;
22
+ last_updated_micro = Math.round((millis - Math.floor(millis)) * 1000);
23
+ time = Math.floor(millis);
24
+ } else {
25
+ const [, fracRaw = ''] = time.split('.');
26
+ const cleanFrac = fracRaw.replace(/\D/g, '');
27
+ const frac = cleanFrac.slice(0, 6).padEnd(6, '0');
28
+ last_updated_micro = parseInt(frac.slice(3), 10);
29
+ }
30
+ return { last_updated: moment(time), last_updated_micro };
31
+ };
32
+
33
+ export const updateGlobalValues = (data) => {
34
+ Object.keys(data).forEach((key) => {
35
+ const value = data[key];
36
+ updateGlobalValue(key, {
37
+ ...value,
38
+ ...getLastUpdated(value.last_updated),
39
+ });
40
+ });
41
+ };
42
+
43
+ export const updateGlobalValue = (configId, value) => {
44
+ const configValues = getConfigGlobalState('configValues');
45
+ const currentValue = configValues[configId];
46
+ if (
47
+ !isNewer(
48
+ value.last_updated,
49
+ value.last_updated_micro,
50
+ currentValue?.last_updated || moment(0),
51
+ currentValue?.last_updated_micro || 0
52
+ )
53
+ ) {
54
+ return;
55
+ }
56
+ setConfigGlobalState('configValues', {
57
+ ...configValues,
58
+ [configId]: value,
59
+ });
60
+ };