@eohjsc/react-native-smart-city 0.7.27 → 0.7.31
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/index.js +2 -0
- package/package.json +2 -1
- package/src/commons/Dashboard/MyDashboardDevice/__test__/index.test.js +68 -0
- package/src/commons/Dashboard/MyDashboardDevice/index.js +46 -11
- package/src/commons/Dashboard/MyUnit/__test__/MyUnit.test.js +43 -11
- package/src/commons/Dashboard/MyUnit/index.js +40 -32
- package/src/commons/ModalAlert/index.js +51 -0
- package/src/commons/ModalAlert/styles.js +54 -0
- package/src/commons/SubUnit/ShortDetail.js +20 -4
- package/src/commons/SubUnit/__test__/ShortDetail.test.js +46 -1
- package/src/configs/API.js +8 -0
- package/src/configs/AccessibilityLabel.js +3 -0
- package/src/configs/Constants.js +7 -0
- package/src/configs/SCConfig.js +6 -0
- package/src/context/SCContext.tsx +12 -1
- package/src/context/SCStore.ts +14 -0
- package/src/context/actionType.ts +10 -0
- package/src/context/mockStore.ts +30 -1
- package/src/context/reducer.ts +35 -0
- package/src/hooks/IoT/useRemoteControl.js +4 -1
- package/src/hooks/IoT/useWatchSharedChips.js +130 -0
- package/src/hooks/Review/__test__/useInAppReview.test.js +99 -0
- package/src/hooks/Review/useInAppReview.js +70 -0
- package/src/hooks/useMqtt.js +78 -27
- package/src/iot/Monitor.js +149 -26
- package/src/iot/UpdateStates.js +60 -0
- package/src/iot/mqtt.js +177 -22
- package/src/navigations/UnitStack.js +16 -0
- package/src/screens/ActivityLog/ItemLog.js +1 -0
- package/src/screens/AddNewGateway/RenameNewDevices.js +5 -0
- package/src/screens/AddNewGateway/__test__/RenameNewDevices.test.js +18 -0
- package/src/screens/Automate/AddNewAction/ReceiverSelect.js +210 -0
- package/src/screens/Automate/AddNewAction/SetupScriptEmail.js +1 -1
- package/src/screens/Automate/AddNewAction/SetupScriptNotify.js +18 -28
- package/src/screens/Automate/AddNewAction/SetupScriptReceiverEmail.js +22 -129
- package/src/screens/Automate/AddNewAction/SetupScriptReceiverNotify.js +59 -0
- package/src/screens/Automate/AddNewAction/SetupScriptReceiverSms.js +22 -129
- package/src/screens/Automate/AddNewAction/SetupScriptSms.js +1 -1
- package/src/screens/Automate/AddNewAction/Styles/{SetupScriptReceiverEmailStyles.js → ReceiverSelectStyles.js} +18 -1
- package/src/screens/Automate/AddNewAction/__test__/SetupScriptNotify.test.js +16 -33
- package/src/screens/Automate/AddNewAction/__test__/SetupScriptReceiverEmail.test.js +10 -8
- package/src/screens/Automate/AddNewAction/__test__/SetupScriptReceiverNotify.test.js +217 -0
- package/src/screens/Automate/AddNewAction/__test__/SetupScriptReceiverSms.test.js +10 -8
- package/src/screens/Automate/Components/InputName.js +5 -1
- package/src/screens/Automate/EditActionsList/UpdateReceiverEmailScript.js +4 -3
- package/src/screens/Automate/EditActionsList/UpdateReceiverSmsScript.js +5 -4
- package/src/screens/Automate/OneTap/__test__/AddNewOneTap.test.js +18 -0
- package/src/screens/Automate/ScriptDetail/Styles/indexStyles.js +1 -1
- package/src/screens/Automate/ScriptDetail/__test__/index.test.js +116 -2
- package/src/screens/Automate/ScriptDetail/index.js +47 -9
- package/src/screens/CreatePassword/__test__/index.test.js +133 -0
- package/src/screens/CreatePassword/index.js +134 -0
- package/src/screens/CreatePassword/styles.js +45 -0
- package/src/screens/Device/__test__/DeviceDetail-3rdparty.test.js +447 -0
- package/src/screens/Device/__test__/DeviceDetail-arduino.test.js +344 -0
- package/src/screens/Device/__test__/{mqttDetail.test.js → DeviceDetail-modbus.test.js} +287 -320
- package/src/screens/Device/__test__/DeviceDetail-zigbee.test.js +451 -0
- package/src/screens/Device/__test__/DeviceDetail.test.js +502 -0
- package/src/screens/Device/__test__/detail.test.js +61 -3
- package/src/screens/Device/__test__/sensorDisplayItem.test.js +28 -3
- package/src/screens/Device/detail.js +14 -6
- package/src/screens/Device/hooks/useDeviceWatchConfigControl.js +3 -2
- package/src/screens/EnterPassword/__test__/EnterPassword.test.js +76 -1
- package/src/screens/EnterPassword/index.js +34 -4
- package/src/screens/EnterPassword/styles.js +1 -1
- package/src/utils/FactoryGateway.js +597 -0
- package/src/utils/I18n/translations/en.js +11 -0
- package/src/utils/I18n/translations/vi.js +11 -0
- package/src/utils/Route/index.js +3 -1
- package/src/utils/Validation.js +5 -0
- package/src/utils/store.js +5 -0
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { useIsFocused } from '@react-navigation/native';
|
|
2
|
+
import { useCallback, useEffect } from 'react';
|
|
3
|
+
import InAppReview from 'react-native-in-app-review';
|
|
4
|
+
|
|
5
|
+
import { getObject, storeObject } from '../../utils/Storage';
|
|
6
|
+
|
|
7
|
+
const KEY_CACHE = 'in_app_review';
|
|
8
|
+
|
|
9
|
+
const TRIGGER_LIMIT = 10;
|
|
10
|
+
const MAX_PROMPTS = 3;
|
|
11
|
+
const INTERVAL_RETRY = 3 * 24 * 3600 * 1000;
|
|
12
|
+
|
|
13
|
+
const useInAppReview = (ask, onFailed) => {
|
|
14
|
+
const isFocused = useIsFocused();
|
|
15
|
+
|
|
16
|
+
const allowInAppReview = useCallback(async () => {
|
|
17
|
+
const review = await getObject(KEY_CACHE, {});
|
|
18
|
+
await storeObject(KEY_CACHE, { ...review, allow: true });
|
|
19
|
+
}, []);
|
|
20
|
+
|
|
21
|
+
const incrementReviewTrigger = useCallback(async () => {
|
|
22
|
+
const review = await getObject(KEY_CACHE, {});
|
|
23
|
+
const trigger = (review.trigger ?? 0) + 1;
|
|
24
|
+
await storeObject(KEY_CACHE, {
|
|
25
|
+
...review,
|
|
26
|
+
allow: trigger >= TRIGGER_LIMIT,
|
|
27
|
+
trigger,
|
|
28
|
+
});
|
|
29
|
+
}, []);
|
|
30
|
+
|
|
31
|
+
const askReview = useCallback(async () => {
|
|
32
|
+
const now = Date.now();
|
|
33
|
+
const review = await getObject(KEY_CACHE, {});
|
|
34
|
+
const { allow = false, count = 0, last = 0 } = review;
|
|
35
|
+
if (!allow || count >= MAX_PROMPTS || now - last < INTERVAL_RETRY) {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
await storeObject(KEY_CACHE, {
|
|
40
|
+
...review,
|
|
41
|
+
count: count + 1,
|
|
42
|
+
last: now,
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
if (!InAppReview.isAvailable()) {
|
|
46
|
+
onFailed && onFailed();
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
try {
|
|
51
|
+
await InAppReview.RequestInAppReview();
|
|
52
|
+
return true;
|
|
53
|
+
} catch {
|
|
54
|
+
onFailed && onFailed();
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
}, [onFailed]);
|
|
58
|
+
|
|
59
|
+
useEffect(() => {
|
|
60
|
+
ask && isFocused && askReview();
|
|
61
|
+
}, [ask, isFocused, askReview]);
|
|
62
|
+
|
|
63
|
+
return {
|
|
64
|
+
allowInAppReview,
|
|
65
|
+
askReview,
|
|
66
|
+
incrementReviewTrigger,
|
|
67
|
+
};
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
export default useInAppReview;
|
package/src/hooks/useMqtt.js
CHANGED
|
@@ -1,12 +1,36 @@
|
|
|
1
1
|
import mqtt from 'precompiled-mqtt/dist/mqtt.browser';
|
|
2
|
-
import { useEffect, useMemo,
|
|
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
|
|
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(
|
|
33
|
-
|
|
34
|
-
|
|
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,
|
|
82
|
+
handleMqttMessage(topic, payloadObject, code, currentChip, configsById);
|
|
58
83
|
// eslint-disable-next-line no-empty
|
|
59
84
|
} catch {}
|
|
60
85
|
});
|
|
61
86
|
|
|
62
|
-
|
|
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
|
|
101
|
+
let configsById = {};
|
|
73
102
|
|
|
74
103
|
mqttChips.forEach((chip) => {
|
|
75
104
|
chip?.sensors.forEach((sensor) => {
|
|
76
105
|
sensor?.configs.forEach((config) => {
|
|
77
|
-
|
|
106
|
+
configsById[config.id] = config;
|
|
78
107
|
});
|
|
79
108
|
});
|
|
80
109
|
});
|
|
81
110
|
|
|
82
|
-
return
|
|
111
|
+
return configsById;
|
|
83
112
|
}, [mqttChips]);
|
|
84
113
|
|
|
85
|
-
const mqttClientsRef = useRef([]);
|
|
86
114
|
useEffect(() => {
|
|
87
115
|
mqttChips.forEach((chip) => {
|
|
88
|
-
|
|
89
|
-
|
|
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
|
-
|
|
94
|
-
|
|
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
|
|
package/src/iot/Monitor.js
CHANGED
|
@@ -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 {
|
|
4
|
+
import { API } from '../configs';
|
|
7
5
|
import { SCConfig } from '../configs';
|
|
8
|
-
import
|
|
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
|
-
|
|
20
|
+
const pusherMap = new Map();
|
|
12
21
|
|
|
13
|
-
const getPusher = () => {
|
|
14
|
-
|
|
15
|
-
|
|
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
|
|
40
|
+
return pusherMap.get(watch_type);
|
|
29
41
|
};
|
|
30
42
|
|
|
31
|
-
const destroyPusher = () => {
|
|
32
|
-
pusher.
|
|
33
|
-
pusher
|
|
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
|
-
|
|
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(
|
|
52
|
-
|
|
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(
|
|
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
|
-
|
|
97
|
-
|
|
98
|
-
|
|
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
|
+
};
|