mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2025-12-07 08:19:52 +03:00
417 lines
12 KiB
TypeScript
417 lines
12 KiB
TypeScript
import { useState } from 'react';
|
|
import { toast } from 'react-toastify';
|
|
|
|
import DownloadIcon from '@mui/icons-material/GetApp';
|
|
import { Box, Button, Divider, Link, Typography } from '@mui/material';
|
|
|
|
import * as SystemApi from 'api/system';
|
|
import {
|
|
API,
|
|
getCustomizations,
|
|
getEntities,
|
|
getSchedule,
|
|
getSettings
|
|
} from 'api/app';
|
|
import { getDevVersion, getStableVersion } from 'api/system';
|
|
|
|
// import { useRequest } from 'alova/client' // TODO replace when Alova 3 is released
|
|
import { useRequest } from 'alova';
|
|
import type { APIcall } from 'app/main/types';
|
|
import {
|
|
FormLoader,
|
|
SectionContent,
|
|
SingleUpload,
|
|
useLayoutTitle
|
|
} from 'components';
|
|
import { useI18nContext } from 'i18n/i18n-react';
|
|
|
|
import RestartMonitor from '../status/RestartMonitor';
|
|
|
|
const UploadDownload = () => {
|
|
const { LL } = useI18nContext();
|
|
const [restarting, setRestarting] = useState<boolean>();
|
|
const [md5, setMd5] = useState<string>();
|
|
|
|
const { send: sendSettings, onSuccess: onSuccessGetSettings } = useRequest(
|
|
getSettings(),
|
|
{
|
|
immediate: false
|
|
}
|
|
);
|
|
const { send: sendCustomizations, onSuccess: onSuccessGetCustomizations } =
|
|
useRequest(getCustomizations(), {
|
|
immediate: false
|
|
});
|
|
const { send: sendEntities, onSuccess: onSuccessGetEntities } = useRequest(
|
|
getEntities(),
|
|
{
|
|
immediate: false
|
|
}
|
|
);
|
|
const { send: sendSchedule, onSuccess: onSuccessGetSchedule } = useRequest(
|
|
getSchedule(),
|
|
{
|
|
immediate: false
|
|
}
|
|
);
|
|
const { send: getAPI, onSuccess: onGetAPI } = useRequest(
|
|
(data: APIcall) => API(data),
|
|
{
|
|
immediate: false
|
|
}
|
|
);
|
|
|
|
// Alova 3 code...
|
|
// const {
|
|
// loading: isUploading,
|
|
// uploading: progress,
|
|
// send: sendUpload,
|
|
// abort: cancelUpload
|
|
// } = useRequest(SystemApi.uploadFile, {
|
|
// immediate: false
|
|
// }).onSuccess(({ data }) => {
|
|
// if (data) {
|
|
// setMd5(data.md5);
|
|
// toast.success(LL.UPLOAD() + ' MD5 ' + LL.SUCCESSFUL());
|
|
// } else {
|
|
// setRestarting(true);
|
|
// }
|
|
// });
|
|
const {
|
|
loading: isUploading,
|
|
uploading: progress,
|
|
send: sendUpload,
|
|
onSuccess: onSuccessUpload,
|
|
abort: cancelUpload
|
|
} = useRequest(SystemApi.uploadFile, {
|
|
immediate: false
|
|
});
|
|
|
|
onSuccessUpload(({ data }) => {
|
|
if (data) {
|
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
setMd5(data.md5);
|
|
toast.success(LL.UPLOAD() + ' MD5 ' + LL.SUCCESSFUL());
|
|
} else {
|
|
setRestarting(true);
|
|
}
|
|
});
|
|
onSuccessGetSettings((event) => {
|
|
saveFile(event.data, 'settings.json');
|
|
});
|
|
onSuccessGetCustomizations((event) => {
|
|
saveFile(event.data, 'customizations.json');
|
|
});
|
|
onSuccessGetEntities((event) => {
|
|
saveFile(event.data, 'entities.json');
|
|
});
|
|
onSuccessGetSchedule((event) => {
|
|
saveFile(event.data, 'schedule.json');
|
|
});
|
|
onGetAPI((event) => {
|
|
saveFile(
|
|
event.data,
|
|
event.sendArgs[0].device + '_' + event.sendArgs[0].entity + '.txt'
|
|
);
|
|
});
|
|
|
|
// TODO Alova 3 code...
|
|
/*
|
|
|
|
const { send: sendSettings } = useRequest(getSettings(), {
|
|
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, 'entities.json');
|
|
});
|
|
|
|
const { send: sendSchedule } = useRequest(getSchedule(), {
|
|
immediate: false
|
|
}).onSuccess((event) => {
|
|
saveFile(event.data, 'schedule.json');
|
|
});
|
|
|
|
const { send: getAPI } = useRequest((data: APIcall) => API(data), {
|
|
immediate: false
|
|
}).onSuccess((event) => {
|
|
saveFile(
|
|
event.data,
|
|
String(event.args[0].device) + '_' + String(event.args[0].entity) + '.txt'
|
|
);
|
|
});
|
|
|
|
*/
|
|
|
|
const {
|
|
data: data,
|
|
send: loadData,
|
|
error
|
|
} = useRequest(SystemApi.readHardwareStatus);
|
|
|
|
// called immediately to get the latest version, on page load
|
|
const { data: latestVersion } = useRequest(getStableVersion);
|
|
const { data: latestDevVersion } = useRequest(getDevVersion);
|
|
|
|
const STABLE_URL = 'https://github.com/emsesp/EMS-ESP32/releases/download/';
|
|
const STABLE_RELNOTES_URL =
|
|
'https://github.com/emsesp/EMS-ESP32/blob/main/CHANGELOG.md';
|
|
|
|
const DEV_URL = 'https://github.com/emsesp/EMS-ESP32/releases/download/latest/';
|
|
const DEV_RELNOTES_URL =
|
|
'https://github.com/emsesp/EMS-ESP32/blob/dev/CHANGELOG_LATEST.md';
|
|
|
|
const getBinURL = (v: string) =>
|
|
'EMS-ESP-' +
|
|
v.replaceAll('.', '_') +
|
|
'-' +
|
|
getPlatform().replaceAll('-', '_') +
|
|
'.bin';
|
|
|
|
const getPlatform = () => {
|
|
if (data.flash_chip_size === 16384 && data.esp_platform !== 'ESP32-S3') {
|
|
return data.esp_platform + '-16M';
|
|
}
|
|
return data.esp_platform;
|
|
};
|
|
|
|
const startUpload = async (files: File[]) => {
|
|
await sendUpload(files[0]).catch((error: Error) => {
|
|
if (error.message === 'The user aborted a request') {
|
|
toast.warning(LL.UPLOAD() + ' ' + LL.ABORTED());
|
|
} else if (error.message === 'Network Error') {
|
|
toast.warning('Invalid file extension or incompatible bin file');
|
|
} else {
|
|
toast.error(error.message);
|
|
}
|
|
});
|
|
};
|
|
|
|
const saveFile = (json: unknown, endpoint: string) => {
|
|
const anchor = document.createElement('a');
|
|
anchor.href = URL.createObjectURL(
|
|
new Blob([JSON.stringify(json, null, 2)], {
|
|
type: 'text/plain'
|
|
})
|
|
);
|
|
anchor.download = 'emsesp_' + endpoint;
|
|
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 callAPI = async (device: string, entity: string) => {
|
|
await getAPI({ device, entity, id: 0 }).catch((error: Error) => {
|
|
toast.error(error.message);
|
|
});
|
|
};
|
|
|
|
useLayoutTitle(LL.UPLOAD_DOWNLOAD());
|
|
|
|
const content = () => {
|
|
if (!data) {
|
|
return <FormLoader onRetry={loadData} errorMessage={error?.message} />;
|
|
}
|
|
|
|
return (
|
|
<>
|
|
<Typography sx={{ pb: 2 }} variant="h6" color="primary">
|
|
{LL.EMS_ESP_VER()}
|
|
</Typography>
|
|
<Box p={2} border="2px solid grey" borderRadius={2}>
|
|
{LL.VERSION_ON() + ' '}
|
|
<b>{data.emsesp_version}</b> ({getPlatform()})
|
|
<Divider />
|
|
{latestVersion && (
|
|
<Box mt={2}>
|
|
{LL.THE_LATEST()} {LL.OFFICIAL()} {LL.RELEASE_IS()}
|
|
<b>{latestVersion}</b>
|
|
(
|
|
<Link target="_blank" href={STABLE_RELNOTES_URL} color="primary">
|
|
{LL.RELEASE_NOTES()}
|
|
</Link>
|
|
) (
|
|
<Link
|
|
target="_blank"
|
|
href={
|
|
STABLE_URL + 'v' + latestVersion + '/' + getBinURL(latestVersion)
|
|
}
|
|
color="primary"
|
|
>
|
|
{LL.DOWNLOAD(1)}
|
|
</Link>
|
|
)
|
|
</Box>
|
|
)}
|
|
{latestDevVersion && (
|
|
<Box mt={2}>
|
|
{LL.THE_LATEST()} {LL.DEVELOPMENT()} {LL.RELEASE_IS()}
|
|
|
|
<b>{latestDevVersion}</b>
|
|
(
|
|
<Link target="_blank" href={DEV_RELNOTES_URL} color="primary">
|
|
{LL.RELEASE_NOTES()}
|
|
</Link>
|
|
) (
|
|
<Link
|
|
target="_blank"
|
|
href={DEV_URL + getBinURL(latestDevVersion)}
|
|
color="primary"
|
|
>
|
|
{LL.DOWNLOAD(1)}
|
|
</Link>
|
|
)
|
|
</Box>
|
|
)}
|
|
</Box>
|
|
|
|
<Typography sx={{ pt: 2, pb: 2 }} variant="h6" color="primary">
|
|
{LL.UPLOAD()}
|
|
</Typography>
|
|
<Box mb={2} color="warning.main">
|
|
<Typography variant="body2">
|
|
{LL.UPLOAD_TEXT()}
|
|
<br />
|
|
<br />
|
|
{LL.RESTART_TEXT(1)}.
|
|
</Typography>
|
|
</Box>
|
|
{md5 && (
|
|
<Box mb={2}>
|
|
<Typography variant="body2">{'MD5: ' + md5}</Typography>
|
|
</Box>
|
|
)}
|
|
<SingleUpload
|
|
onDrop={startUpload}
|
|
onCancel={cancelUpload}
|
|
isUploading={isUploading}
|
|
progress={progress}
|
|
/>
|
|
{!isUploading && (
|
|
<>
|
|
<Typography sx={{ pt: 4, pb: 2 }} variant="h6" color="primary">
|
|
{LL.DOWNLOAD(0)}
|
|
</Typography>
|
|
<Box color="warning.main">
|
|
<Typography mb={1} variant="body2">
|
|
{LL.HELP_INFORMATION_4()}
|
|
</Typography>
|
|
<Button
|
|
sx={{ ml: 2 }}
|
|
startIcon={<DownloadIcon />}
|
|
variant="outlined"
|
|
color="primary"
|
|
onClick={() => callAPI('system', 'info')}
|
|
>
|
|
{LL.SUPPORT_INFORMATION(0)}
|
|
</Button>
|
|
<Button
|
|
sx={{ ml: 2 }}
|
|
startIcon={<DownloadIcon />}
|
|
variant="outlined"
|
|
color="primary"
|
|
onClick={() => callAPI('system', 'allvalues')}
|
|
>
|
|
All Values
|
|
</Button>
|
|
</Box>
|
|
|
|
<Box color="warning.main">
|
|
<Typography mt={2} mb={1} variant="body2">
|
|
{LL.DOWNLOAD_SETTINGS_TEXT()}
|
|
</Typography>
|
|
<Button
|
|
sx={{ ml: 2 }}
|
|
startIcon={<DownloadIcon />}
|
|
variant="outlined"
|
|
color="primary"
|
|
onClick={downloadSettings}
|
|
>
|
|
{LL.SETTINGS_OF('')}
|
|
</Button>
|
|
</Box>
|
|
|
|
<Box color="warning.main">
|
|
<Typography mt={2} mb={1} variant="body2">
|
|
{LL.DOWNLOAD_CUSTOMIZATION_TEXT()}
|
|
</Typography>
|
|
<Button
|
|
sx={{ ml: 2 }}
|
|
startIcon={<DownloadIcon />}
|
|
variant="outlined"
|
|
color="primary"
|
|
onClick={downloadCustomizations}
|
|
>
|
|
{LL.CUSTOMIZATIONS()}
|
|
</Button>
|
|
<Button
|
|
sx={{ ml: 2 }}
|
|
startIcon={<DownloadIcon />}
|
|
variant="outlined"
|
|
color="primary"
|
|
onClick={downloadEntities}
|
|
>
|
|
{LL.CUSTOM_ENTITIES(0)}
|
|
</Button>
|
|
<Box color="warning.main">
|
|
<Typography mt={2} mb={1} variant="body2">
|
|
{LL.DOWNLOAD_SCHEDULE_TEXT()}
|
|
</Typography>
|
|
</Box>
|
|
<Button
|
|
sx={{ ml: 2 }}
|
|
startIcon={<DownloadIcon />}
|
|
variant="outlined"
|
|
color="primary"
|
|
onClick={downloadSchedule}
|
|
>
|
|
{LL.SCHEDULE(0)}
|
|
</Button>
|
|
</Box>
|
|
</>
|
|
)}
|
|
</>
|
|
);
|
|
};
|
|
|
|
return (
|
|
<SectionContent>{restarting ? <RestartMonitor /> : content()}</SectionContent>
|
|
);
|
|
};
|
|
|
|
export default UploadDownload;
|