This commit is contained in:
MichaelDvP
2024-09-16 13:48:18 +02:00
42 changed files with 407 additions and 619 deletions

View File

@@ -31,7 +31,7 @@
"async-validator": "^4.2.5",
"jwt-decode": "^4.0.0",
"mime-types": "^2.1.35",
"preact": "^10.23.2",
"preact": "^10.24.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-icons": "^5.3.0",
@@ -44,10 +44,10 @@
"@babel/core": "^7.25.2",
"@eslint/js": "^9.10.0",
"@preact/compat": "^18.3.1",
"@preact/preset-vite": "^2.9.0",
"@preact/preset-vite": "^2.9.1",
"@trivago/prettier-plugin-sort-imports": "^4.3.0",
"@types/formidable": "^3",
"@types/node": "^22.5.4",
"@types/node": "^22.5.5",
"@types/react": "^18.3.5",
"@types/react-dom": "^18.3.0",
"@types/react-router-dom": "^5.3.3",
@@ -59,9 +59,9 @@
"rollup-plugin-visualizer": "^5.12.0",
"terser": "^5.32.0",
"typescript-eslint": "8.5.0",
"vite": "^5.4.4",
"vite": "^5.4.5",
"vite-plugin-imagemin": "^0.6.1",
"vite-tsconfig-paths": "^5.0.1"
},
"packageManager": "yarn@4.4.1"
"packageManager": "yarn@4.5.0"
}

View File

@@ -6,7 +6,6 @@ import type {
CoreData,
DeviceData,
DeviceEntity,
Devices,
Entities,
EntityItem,
ModuleItem,
@@ -53,11 +52,9 @@ export const readActivity = () => alovaInstance.Get<Activity>('/rest/activity');
// API
export const API = (apiCall: APIcall) => alovaInstance.Post('/api', apiCall);
// UploadFileForm
export const getSettings = () => alovaInstance.Get('/rest/getSettings');
export const getCustomizations = () => alovaInstance.Get('/rest/getCustomizations');
export const getEntities = () => alovaInstance.Get<Entities>('/rest/getEntities');
export const getSchedule = () => alovaInstance.Get('/rest/getSchedule');
// DownloadUpload
export const exportData = (type: string) =>
alovaInstance.Get('/rest/exportData', { params: { type } });
// SettingsCustomization
export const readDeviceEntities = (id: number) =>
@@ -75,7 +72,6 @@ export const readDeviceEntities = (id: number) =>
}));
}
});
export const readDevices = () => alovaInstance.Get<Devices>('/rest/devices');
export const resetCustomizations = () =>
alovaInstance.Post('/rest/resetCustomizations');
export const writeCustomizationEntities = (data: {

View File

@@ -1,12 +1,8 @@
import type { HardwareStatus, LogSettings, SystemStatus } from 'types';
import type { LogSettings, SystemStatus } from 'types';
import { alovaInstance, alovaInstanceGH } from './endpoints';
// hardwareStatus - also used to ping in Restart monitor for pinging
export const readHardwareStatus = () =>
alovaInstance.Get<HardwareStatus>('/rest/hardwareStatus');
// SystemStatus
// systemStatus - also used to ping in Restart monitor for pinging
export const readSystemStatus = () =>
alovaInstance.Get<SystemStatus>('/rest/systemStatus');

View File

@@ -50,8 +50,8 @@ import { useI18nContext } from 'i18n/i18n-react';
import {
API,
readCoreData,
readDeviceEntities,
readDevices,
resetCustomizations,
writeCustomizationEntities,
writeDeviceName
@@ -60,7 +60,7 @@ import SettingsCustomizationsDialog from './CustomizationsDialog';
import EntityMaskToggle from './EntityMaskToggle';
import OptionIcon from './OptionIcon';
import { DeviceEntityMask } from './types';
import type { APIcall, DeviceEntity, DeviceShort } from './types';
import type { APIcall, Device, DeviceEntity } from './types';
export const APIURL = window.location.origin + '/api/';
@@ -81,8 +81,8 @@ const Customizations = () => {
useLayoutTitle(LL.CUSTOMIZATIONS());
// fetch devices first
const { data: devices, send: fetchDevices } = useRequest(readDevices);
// fetch devices first from coreData
const { data: devices, send: fetchCoreData } = useRequest(readCoreData);
const { send: sendAPI } = useRequest((data: APIcall) => API(data), {
immediate: false
@@ -242,13 +242,13 @@ const Customizations = () => {
useEffect(() => {
if (devices && selectedDevice !== -1) {
void sendDeviceEntities(selectedDevice);
const id = devices.devices.findIndex((d) => d.i === selectedDevice);
if (id === -1) {
const index = devices.devices.findIndex((d) => d.id === selectedDevice);
if (index === -1) {
setSelectedDevice(-1);
setSelectedDeviceTypeNameURL('');
} else {
setSelectedDeviceTypeNameURL(devices.devices[id].url || '');
setSelectedDeviceName(devices.devices[id].s);
setSelectedDeviceTypeNameURL(devices.devices[index].url || '');
setSelectedDeviceName(devices.devices[index].n);
setNumChanges(0);
setRestartNeeded(false);
}
@@ -414,7 +414,7 @@ const Customizations = () => {
})
.finally(async () => {
setRename(false);
await fetchDevices();
await fetchCoreData();
});
};
@@ -449,9 +449,9 @@ const Customizations = () => {
<MenuItem disabled key={-1} value={-1}>
{LL.SELECT_DEVICE()}...
</MenuItem>
{devices.devices.map((device: DeviceShort) => (
<MenuItem key={device.i} value={device.i}>
{device.s}&nbsp;({device.tn})
{devices.devices.map((device: Device) => (
<MenuItem key={device.id} value={device.id}>
{device.n}&nbsp;({device.tn})
</MenuItem>
))}
</TextField>

View File

@@ -71,6 +71,7 @@ export interface Device {
p: number; // productid
v: string; // version
e: number; // entities
url?: string; // lowercase type name used in API URL
}
export interface TemperatureSensor {
@@ -113,20 +114,6 @@ export interface CoreData {
devices: Device[];
}
export interface DeviceShort {
i: number; // id
d?: number; // deviceid
p?: number; // productid
s: string; // shortname
t?: number; // device type id
tn?: string; // device type internal name (translated)
url?: string; // lowercase type name used in API URL
}
export interface Devices {
devices: DeviceShort[];
}
export interface DeviceValue {
id: string; // index, contains mask+name
v: unknown; // value, Number or String

View File

@@ -16,7 +16,7 @@ import {
} from '@mui/material';
import Grid from '@mui/material/Grid2';
import { readHardwareStatus } from 'api/system';
import { readSystemStatus } from 'api/system';
import { useRequest } from 'alova/client';
import RestartMonitor from 'app/status/RestartMonitor';
@@ -49,7 +49,7 @@ export function boardProfileSelectItems() {
}
const ApplicationSettings = () => {
const { data: hardwareData } = useRequest(readHardwareStatus);
const { data: hardwareData } = useRequest(readSystemStatus);
const {
loadData,

View File

@@ -18,13 +18,7 @@ import {
import Grid from '@mui/material/Grid2';
import * as SystemApi from 'api/system';
import {
API,
getCustomizations,
getEntities,
getSchedule,
getSettings
} from 'api/app';
import { API, exportData } from 'api/app';
import {
checkUpgrade,
getDevVersion,
@@ -52,48 +46,26 @@ const DownloadUpload = () => {
const [useDev, setUseDev] = useState<boolean>(false);
const [upgradeAvailable, setUpgradeAvailable] = useState<boolean>(false);
const { send: sendSettings } = useRequest(getSettings(), {
const { send: sendExportData } = useRequest((type: string) => exportData(type), {
immediate: false
}).onSuccess((event) => {
saveFile(event.data, 'settings.json');
});
const { send: sendCustomizations } = useRequest(getCustomizations(), {
immediate: false
}).onSuccess((event) => {
saveFile(event.data, 'customizations.json');
});
const { send: sendEntities } = useRequest(getEntities(), {
immediate: false
}).onSuccess((event) => {
saveFile(event.data, 'custom_entities.json');
});
const { send: sendSchedule } = useRequest(getSchedule(), {
immediate: false
}).onSuccess((event) => {
saveFile(event.data, 'schedule.json');
});
})
.onSuccess((event) => {
saveFile(event.data, event.args[0]);
toast.info(LL.DOWNLOAD_SUCCESSFUL());
})
.onError((error) => {
toast.error(error.message);
});
const { send: sendAPI } = useRequest((data: APIcall) => API(data), {
immediate: false
});
const { send: sendAPIandSave } = useRequest((data: APIcall) => API(data), {
immediate: false
}).onSuccess((event) => {
saveFile(
event.data,
String(event.args[0].device) + '_' + String(event.args[0].cmd) + '.txt'
);
});
const {
data: data,
send: loadData,
error
} = useRequest(SystemApi.readHardwareStatus);
} = useRequest(SystemApi.readSystemStatus);
const { send: sendUploadURL } = useRequest(
(data: { url: string }) => uploadURL(data),
@@ -123,6 +95,8 @@ const DownloadUpload = () => {
// immediate: false,
// initialData: '3.6.5'
});
// called immediately to get the latest version, on page load, then check for upgrade
const { data: latestDevVersion } = useRequest(getDevVersion, {
// uncomment next 2 lines for testing, uses https://github.com/emsesp/EMS-ESP32/releases/download/latest/EMS-ESP-3_7_0-dev_31-ESP32-16MB+.bin
// immediate: false,
@@ -175,40 +149,9 @@ const DownloadUpload = () => {
type: 'text/plain'
})
);
anchor.download = 'emsesp_' + filename;
anchor.download = 'emsesp_' + filename + '.json';
anchor.click();
URL.revokeObjectURL(anchor.href);
toast.info(LL.DOWNLOAD_SUCCESSFUL());
};
const downloadSettings = async () => {
await sendSettings().catch((error: Error) => {
toast.error(error.message);
});
};
const downloadCustomizations = async () => {
await sendCustomizations().catch((error: Error) => {
toast.error(error.message);
});
};
const downloadEntities = async () => {
await sendEntities().catch((error: Error) => {
toast.error(error.message);
});
};
const downloadSchedule = async () => {
await sendSchedule().catch((error: Error) => {
toast.error(error.message);
});
};
const callAPIandSave = async (device: string, cmd: string) => {
await sendAPIandSave({ device, cmd, id: 0 }).catch((error: Error) => {
toast.error(error.message);
});
};
useLayoutTitle(LL.DOWNLOAD_UPLOAD());
@@ -301,7 +244,7 @@ const DownloadUpload = () => {
startIcon={<DownloadIcon />}
variant="outlined"
color="primary"
onClick={downloadSettings}
onClick={() => sendExportData('settings')}
>
{LL.DOWNLOAD(1)}&nbsp;{LL.SETTINGS_OF(LL.APPLICATION())}
</Button>
@@ -311,7 +254,7 @@ const DownloadUpload = () => {
startIcon={<DownloadIcon />}
variant="outlined"
color="primary"
onClick={downloadCustomizations}
onClick={() => sendExportData('customizations')}
>
{LL.DOWNLOAD(1)}&nbsp;{LL.CUSTOMIZATIONS()}
</Button>
@@ -320,7 +263,7 @@ const DownloadUpload = () => {
startIcon={<DownloadIcon />}
variant="outlined"
color="primary"
onClick={downloadEntities}
onClick={() => sendExportData('entities')}
>
{LL.DOWNLOAD(1)}&nbsp;{LL.CUSTOM_ENTITIES(0)}
</Button>
@@ -329,7 +272,7 @@ const DownloadUpload = () => {
startIcon={<DownloadIcon />}
variant="outlined"
color="primary"
onClick={downloadSchedule}
onClick={() => sendExportData('schedule')}
>
{LL.DOWNLOAD(1)}&nbsp;{LL.SCHEDULE(0)}
</Button>

View File

@@ -36,7 +36,7 @@ const HardwareStatus = () => {
data: data,
send: loadData,
error
} = useAutoRequest(SystemApi.readHardwareStatus, { pollingTime: 2000 });
} = useAutoRequest(SystemApi.readSystemStatus, { pollingTime: 2000 });
const content = () => {
if (!data) {

View File

@@ -8,7 +8,7 @@ import {
Typography
} from '@mui/material';
import { readHardwareStatus } from 'api/system';
import { readSystemStatus } from 'api/system';
import { dialogStyle } from 'CustomTheme';
import { useAutoRequest } from 'alova/client';
@@ -22,7 +22,7 @@ const RestartMonitor = () => {
let count = 0;
const { data } = useAutoRequest(readHardwareStatus, {
const { data } = useAutoRequest(readSystemStatus, {
pollingTime: 1000,
force: true,
initialData: { status: 'Getting ready...' },
@@ -38,7 +38,7 @@ const RestartMonitor = () => {
document.location.href = '/';
}
})
.onError((error, _method) => {
.onError((error) => {
setErrorMessage(error.message);
});

View File

@@ -29,8 +29,8 @@ import {
useTheme
} from '@mui/material';
import * as SystemApi from 'api/system';
import { API } from 'api/app';
import { readSystemStatus } from 'api/system';
import { dialogStyle } from 'CustomTheme';
import { useAutoRequest, useRequest } from 'alova/client';
@@ -63,7 +63,7 @@ const SystemStatus = () => {
data: data,
send: loadData,
error
} = useAutoRequest(SystemApi.readSystemStatus, {
} = useAutoRequest(readSystemStatus, {
initialData: [],
pollingTime: 5000,
async middleware(_, next) {
@@ -101,7 +101,7 @@ const SystemStatus = () => {
const busStatus = () => {
if (data) {
switch (data.status) {
switch (data.bus_status) {
case busConnectionStatus.BUS_STATUS_CONNECTED:
return (
'EMS ' +
@@ -120,7 +120,7 @@ const SystemStatus = () => {
};
const busStatusHighlight = () => {
switch (data.status) {
switch (data.bus_status) {
case busConnectionStatus.BUS_STATUS_TX_ERRORS:
return theme.palette.warning.main;
case busConnectionStatus.BUS_STATUS_CONNECTED:

View File

@@ -8,7 +8,7 @@ import Grid from '@mui/material/Grid2';
import { fetchLogES, readLogSettings, updateLogSettings } from 'api/system';
import { useRequest, useSSE } from 'alova/client';
import { useSSE } from 'alova/client';
import {
BlockFormControlLabel,
BlockNavigation,

View File

@@ -16,7 +16,7 @@ const DragNdrop = ({ onFileSelected }) => {
const { LL } = useI18nContext();
const checkFileExtension = (file: File) => {
const validExtensions = ['.json', '.txt', '.csv', '.bin', '.md5'];
const validExtensions = ['.json', '.bin', '.md5'];
const fileName = file.name;
const fileExtension = fileName.substring(fileName.lastIndexOf('.'));
if (validExtensions.includes(fileExtension)) {

View File

@@ -2,8 +2,19 @@ import type { busConnectionStatus } from 'app/main/types';
import type { NetworkConnectionStatus } from './network';
export interface HardwareStatus {
export interface SystemStatus {
emsesp_version: string;
bus_status: busConnectionStatus;
uptime: number;
bus_uptime: number;
num_devices: number;
num_sensors: number;
num_analogs: number;
ntp_status: number;
mqtt_status: boolean;
ap_status: boolean;
network_status: NetworkConnectionStatus;
wifi_rssi: number;
build_flags: string;
esp_platform: string;
max_alloc_heap: number;
@@ -32,22 +43,6 @@ export interface HardwareStatus {
status: string;
}
export interface SystemStatus {
emsesp_version: string;
status: busConnectionStatus;
uptime: number;
bus_uptime: number;
num_devices: number;
num_sensors: number;
num_analogs: number;
free_heap: number;
ntp_status: number;
mqtt_status: boolean;
ap_status: boolean;
network_status: NetworkConnectionStatus;
wifi_rssi: number;
}
export enum LogLevel {
ERROR = 3,
WARNING = 4,

View File

@@ -1054,9 +1054,9 @@ __metadata:
languageName: node
linkType: hard
"@preact/preset-vite@npm:^2.9.0":
version: 2.9.0
resolution: "@preact/preset-vite@npm:2.9.0"
"@preact/preset-vite@npm:^2.9.1":
version: 2.9.1
resolution: "@preact/preset-vite@npm:2.9.1"
dependencies:
"@babel/code-frame": "npm:^7.22.13"
"@babel/plugin-transform-react-jsx": "npm:^7.22.15"
@@ -1068,13 +1068,12 @@ __metadata:
kolorist: "npm:^1.8.0"
magic-string: "npm:0.30.5"
node-html-parser: "npm:^6.1.10"
resolve: "npm:^1.22.8"
source-map: "npm:^0.7.4"
stack-trace: "npm:^1.0.0-pre2"
peerDependencies:
"@babel/core": 7.x
vite: 2.x || 3.x || 4.x || 5.x
checksum: 10c0/658e3dc048d1f1d8ad7cb1fef4a3db0f933be4e00d3d6cdfbd29fe7ec02341b3a26747520a5b261992923b3d3f49800c23a4d77da849e708a5c1ad9a920343b2
checksum: 10c0/6c2f2a7f06b08b2bd817d493101c4654891d6b86f661d48e2fb0b1388289bc4cb40b11a9ab30f9a12f818a28a1b48d60d97b24b8d3376c317a9d8abe06a68b1e
languageName: node
linkType: hard
@@ -1417,7 +1416,7 @@ __metadata:
languageName: node
linkType: hard
"@types/node@npm:*, @types/node@npm:^22.5.4":
"@types/node@npm:*":
version: 22.5.4
resolution: "@types/node@npm:22.5.4"
dependencies:
@@ -1426,6 +1425,15 @@ __metadata:
languageName: node
linkType: hard
"@types/node@npm:^22.5.5":
version: 22.5.5
resolution: "@types/node@npm:22.5.5"
dependencies:
undici-types: "npm:~6.19.2"
checksum: 10c0/ead9495cfc6b1da5e7025856dcce2591e9bae635357410c0d2dd619fce797d2a1d402887580ca4b336cb78168b195224869967de370a23f61663cf1e4836121c
languageName: node
linkType: hard
"@types/parse-json@npm:^4.0.0":
version: 4.0.2
resolution: "@types/parse-json@npm:4.0.2"
@@ -1635,11 +1643,11 @@ __metadata:
"@mui/icons-material": "npm:^6.1.0"
"@mui/material": "npm:^6.1.0"
"@preact/compat": "npm:^18.3.1"
"@preact/preset-vite": "npm:^2.9.0"
"@preact/preset-vite": "npm:^2.9.1"
"@table-library/react-table-library": "npm:4.1.7"
"@trivago/prettier-plugin-sort-imports": "npm:^4.3.0"
"@types/formidable": "npm:^3"
"@types/node": "npm:^22.5.4"
"@types/node": "npm:^22.5.5"
"@types/react": "npm:^18.3.5"
"@types/react-dom": "npm:^18.3.0"
"@types/react-router-dom": "npm:^5.3.3"
@@ -1651,7 +1659,7 @@ __metadata:
formidable: "npm:^3.5.1"
jwt-decode: "npm:^4.0.0"
mime-types: "npm:^2.1.35"
preact: "npm:^10.23.2"
preact: "npm:^10.24.0"
prettier: "npm:^3.3.3"
react: "npm:^18.3.1"
react-dom: "npm:^18.3.1"
@@ -1663,7 +1671,7 @@ __metadata:
typesafe-i18n: "npm:^5.26.2"
typescript: "npm:^5.6.2"
typescript-eslint: "npm:8.5.0"
vite: "npm:^5.4.4"
vite: "npm:^5.4.5"
vite-plugin-imagemin: "npm:^0.6.1"
vite-tsconfig-paths: "npm:^5.0.1"
languageName: unknown
@@ -5568,10 +5576,10 @@ __metadata:
languageName: node
linkType: hard
"preact@npm:^10.23.2":
version: 10.23.2
resolution: "preact@npm:10.23.2"
checksum: 10c0/6e0dc1b38ead7554c99ddec9a32162b456e8f622229413b136042a777445a12d115633cd49d6df83c30b64d721a0ad4d3c71bb468edc759c15799896e96fd9f2
"preact@npm:^10.24.0":
version: 10.24.0
resolution: "preact@npm:10.24.0"
checksum: 10c0/09d490d2326c511e205a96f81db0adf05f1b42dbe2a39be6fc494662c7476575494e96140252f351a0e3b3d15aee5b079bf963865bb01287f69c45c6755ed22e
languageName: node
linkType: hard
@@ -5897,7 +5905,7 @@ __metadata:
languageName: node
linkType: hard
"resolve@npm:^1.10.0, resolve@npm:^1.19.0, resolve@npm:^1.22.8":
"resolve@npm:^1.10.0, resolve@npm:^1.19.0":
version: 1.22.8
resolution: "resolve@npm:1.22.8"
dependencies:
@@ -5910,7 +5918,7 @@ __metadata:
languageName: node
linkType: hard
"resolve@patch:resolve@npm%3A^1.10.0#optional!builtin<compat/resolve>, resolve@patch:resolve@npm%3A^1.19.0#optional!builtin<compat/resolve>, resolve@patch:resolve@npm%3A^1.22.8#optional!builtin<compat/resolve>":
"resolve@patch:resolve@npm%3A^1.10.0#optional!builtin<compat/resolve>, resolve@patch:resolve@npm%3A^1.19.0#optional!builtin<compat/resolve>":
version: 1.22.8
resolution: "resolve@patch:resolve@npm%3A1.22.8#optional!builtin<compat/resolve>::version=1.22.8&hash=c3c19d"
dependencies:
@@ -6960,9 +6968,9 @@ __metadata:
languageName: node
linkType: hard
"vite@npm:^5.4.4":
version: 5.4.4
resolution: "vite@npm:5.4.4"
"vite@npm:^5.4.5":
version: 5.4.5
resolution: "vite@npm:5.4.5"
dependencies:
esbuild: "npm:^0.21.3"
fsevents: "npm:~2.3.3"
@@ -6999,7 +7007,7 @@ __metadata:
optional: true
bin:
vite: bin/vite.js
checksum: 10c0/2752e7dd5584ea7cc057742e8f5cbf2f2bd3a2bceb8794fbd3d52f1e88d362b5ac7f1c70be7a3d01b3d768320c8a8ad0df287fd72f253bf040423c36c67a3e89
checksum: 10c0/89c6459452fc238cdf8e99681b30996af171c9c557af476f96408a18a639fb5a0a6ee2d2257e005b21dc284edceb604595c34920cd4a007ad18f7ebafb654c76
languageName: node
linkType: hard