Skip to content

Commit

Permalink
Merge branch 'dev' of https://github.com/emsesp/EMS-ESP32 into idf4_n…
Browse files Browse the repository at this point in the history
…o_master
  • Loading branch information
MichaelDvP committed May 8, 2022
2 parents 6203eed + db8c30d commit b16fa6d
Show file tree
Hide file tree
Showing 40 changed files with 521 additions and 433 deletions.
1 change: 1 addition & 0 deletions CHANGELOG_LATEST.md
Expand Up @@ -40,6 +40,7 @@
- API fetch individual attributes from an entity [#462](https://github.com/emsesp/EMS-ESP32/issues/462)
- Option to disable mDNS
- Option for rendering booleans on dashboard [#456](https://github.com/emsesp/EMS-ESP32/issues/456)
- Upload customization settings from a file [#256](https://github.com/emsesp/EMS-ESP32/issues/256)

### Fixed

Expand Down
14 changes: 7 additions & 7 deletions interface/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion interface/package.json
Expand Up @@ -25,7 +25,7 @@
"react": "^17.0.2",
"react-app-rewired": "^2.2.1",
"react-dom": "^17.0.2",
"react-dropzone": "^14.2.0",
"react-dropzone": "^14.2.1",
"react-icons": "^4.3.1",
"react-router-dom": "^6.3.0",
"react-scripts": "5.0.1",
Expand Down
5 changes: 1 addition & 4 deletions interface/src/AppRouting.tsx
Expand Up @@ -47,10 +47,7 @@ const AppRouting: FC = () => {
<RemoveTrailingSlashes />
<Routes>
<Route path="/unauthorized" element={<RootRedirect message="Please sign in to continue" signOut />} />
<Route
path="/firmwareUpdated"
element={<RootRedirect message="Firmware update successful" variant="success" />}
/>
<Route path="/fileUpdated" element={<RootRedirect message="Upload successful" variant="success" />} />
{features.security && (
<Route
path="/"
Expand Down
2 changes: 1 addition & 1 deletion interface/src/api/endpoints.ts
Expand Up @@ -92,7 +92,7 @@ export interface FileUploadConfig {
onUploadProgress?: (progressEvent: ProgressEvent) => void;
}

export const uploadFile = (url: string, file: File, config?: FileUploadConfig): AxiosPromise<void> => {
export const startUploadFile = (url: string, file: File, config?: FileUploadConfig): AxiosPromise<void> => {
const formData = new FormData();
formData.append('file', file);

Expand Down
6 changes: 3 additions & 3 deletions interface/src/api/system.ts
Expand Up @@ -2,7 +2,7 @@ import { AxiosPromise } from 'axios';

import { OTASettings, SystemStatus, LogSettings, LogEntries } from '../types';

import { AXIOS, AXIOS_BIN, FileUploadConfig, uploadFile } from './endpoints';
import { AXIOS, AXIOS_BIN, FileUploadConfig, startUploadFile } from './endpoints';

export function readSystemStatus(timeout?: number): AxiosPromise<SystemStatus> {
return AXIOS.get('/systemStatus', { timeout });
Expand All @@ -24,8 +24,8 @@ export function updateOTASettings(otaSettings: OTASettings): AxiosPromise<OTASet
return AXIOS.post('/otaSettings', otaSettings);
}

export const uploadFirmware = (file: File, config?: FileUploadConfig): AxiosPromise<void> =>
uploadFile('/uploadFirmware', file, config);
export const uploadFile = (file: File, config?: FileUploadConfig): AxiosPromise<void> =>
startUploadFile('/uploadFile', file, config);

export function readLogSettings(): AxiosPromise<LogSettings> {
return AXIOS.get('/logSettings');
Expand Down
3 changes: 2 additions & 1 deletion interface/src/components/upload/SingleUpload.tsx
Expand Up @@ -32,7 +32,8 @@ const SingleUpload: FC<SingleUploadProps> = ({ onDrop, onCancel, uploading, prog
const dropzoneState = useDropzone({
onDrop,
accept: {
'application/octet-stream': ['.bin']
'application/octet-stream': ['.bin'],
'application/json': ['.json']
},
disabled: uploading,
multiple: false
Expand Down
2 changes: 1 addition & 1 deletion interface/src/components/upload/useFileUpload.ts
Expand Up @@ -42,7 +42,7 @@ const useFileUpload = ({ upload }: MediaUploadOptions) => {
cancelToken: cancelToken.token
});
resetUploadingStates();
enqueueSnackbar('Upload successful', { variant: 'success' });
enqueueSnackbar('File uploaded', { variant: 'success' });
} catch (error: unknown) {
if (axios.isCancel(error)) {
enqueueSnackbar('Upload aborted', { variant: 'warning' });
Expand Down
19 changes: 10 additions & 9 deletions interface/src/framework/security/ManageUsersForm.tsx
Expand Up @@ -83,14 +83,13 @@ const ManageUsersForm: FC = () => {
const noAdminConfigured = () => !data.users.find((u) => u.admin);

const removeUser = (toRemove: User) => {
const users = data.users.filter((u) => u.id !== toRemove.id);
const users = data.users.filter((u) => u.username !== toRemove.username);
setData({ ...data, users });
};

const createUser = () => {
setCreating(true);
setUser({
id: '',
username: '',
password: '',
admin: true
Expand All @@ -108,7 +107,7 @@ const ManageUsersForm: FC = () => {

const doneEditingUser = () => {
if (user) {
const users = [...data.users.filter((u) => u.id !== user.id), user];
const users = [...data.users.filter((u) => u.username !== user.username), user];
setData({ ...data, users });
setUser(undefined);
}
Expand All @@ -118,18 +117,20 @@ const ManageUsersForm: FC = () => {
setGeneratingToken(undefined);
};

const generateToken = (id: string) => {
setGeneratingToken(id);
const generateToken = (username: string) => {
setGeneratingToken(username);
};

const onSubmit = async () => {
await saveData();
authenticatedContext.refresh();
};

const user_table = data.users.map((u) => ({ ...u, id: u.username }));

return (
<>
<Table data={{ nodes: data.users }} theme={table_theme}>
<Table data={{ nodes: user_table }} theme={table_theme}>
{(tableList: any) => (
<>
<Header>
Expand All @@ -140,16 +141,16 @@ const ManageUsersForm: FC = () => {
</HeaderRow>
</Header>
<Body>
{tableList.map((u: User, index: number) => (
{tableList.map((u: any) => (
<Row key={u.id} item={u}>
<Cell>{u.id}</Cell>
<Cell>{u.username}</Cell>
<Cell>{u.admin ? <CheckIcon /> : <CloseIcon />}</Cell>
<Cell>
<IconButton
size="small"
disabled={!authenticatedContext.me.admin}
aria-label="Generate Token"
onClick={() => generateToken(u.id)}
onClick={() => generateToken(u.username)}
>
<VpnKeyIcon />
</IconButton>
Expand Down
33 changes: 0 additions & 33 deletions interface/src/framework/system/FirmwareFileUpload.tsx

This file was deleted.

28 changes: 28 additions & 0 deletions interface/src/framework/system/GeneralFileUpload.tsx
@@ -0,0 +1,28 @@
import { AxiosPromise } from 'axios';
import { FC } from 'react';

import { FileUploadConfig } from '../../api/endpoints';
import { MessageBox, SingleUpload, useFileUpload } from '../../components';

interface UploadFileProps {
uploadGeneralFile: (file: File, config?: FileUploadConfig) => AxiosPromise<void>;
}

const GeneralFileUpload: FC<UploadFileProps> = ({ uploadGeneralFile }) => {
const [uploadFile, cancelUpload, uploading, uploadProgress] = useFileUpload({ upload: uploadGeneralFile });

return (
<>
{!uploading && (
<MessageBox
message="Upload a new firmware (.bin) file or an exported settings or customizations (.json) file below. EMS-ESP will restart afterwards to apply the new changes."
level="warning"
my={2}
/>
)}
<SingleUpload onDrop={uploadFile} onCancel={cancelUpload} uploading={uploading} progress={uploadProgress} />
</>
);
};

export default GeneralFileUpload;
Expand Up @@ -8,15 +8,15 @@ const RESTART_TIMEOUT = 2 * 60 * 1000;
const POLL_TIMEOUT = 2000;
const POLL_INTERVAL = 5000;

const FirmwareRestartMonitor: FC = () => {
const RestartMonitor: FC = () => {
const [failed, setFailed] = useState<boolean>(false);
const [timeoutId, setTimeoutId] = useState<NodeJS.Timeout>();

const timeoutAt = useRef(new Date().getTime() + RESTART_TIMEOUT);
const poll = useRef(async () => {
try {
await SystemApi.readSystemStatus(POLL_TIMEOUT);
document.location.href = '/firmwareUpdated';
document.location.href = '/fileUpdated';
} catch (error: unknown) {
if (new Date().getTime() < timeoutAt.current) {
setTimeoutId(setTimeout(poll.current, POLL_INTERVAL));
Expand All @@ -40,4 +40,4 @@ const FirmwareRestartMonitor: FC = () => {
);
};

export default FirmwareRestartMonitor;
export default RestartMonitor;
6 changes: 3 additions & 3 deletions interface/src/framework/system/System.tsx
Expand Up @@ -6,7 +6,7 @@ import { Tab } from '@mui/material';
import { useRouterTab, RouterTabs, useLayoutTitle, RequireAdmin } from '../../components';
import { AuthenticatedContext } from '../../contexts/authentication';
import { FeaturesContext } from '../../contexts/features';
import UploadFirmwareForm from './UploadFirmwareForm';
import UploadFileForm from './UploadFileForm';
import SystemStatusForm from './SystemStatusForm';
import OTASettingsForm from './OTASettingsForm';

Expand All @@ -26,7 +26,7 @@ const System: FC = () => {
<Tab value="log" label="System Log" />

{features.ota && <Tab value="ota" label="OTA Settings" disabled={!me.admin} />}
{features.upload_firmware && <Tab value="upload" label="Upload Firmware" disabled={!me.admin} />}
{features.upload_firmware && <Tab value="upload" label="Upload" disabled={!me.admin} />}
</RouterTabs>
<Routes>
<Route path="status" element={<SystemStatusForm />} />
Expand All @@ -46,7 +46,7 @@ const System: FC = () => {
path="upload"
element={
<RequireAdmin>
<UploadFirmwareForm />
<UploadFileForm />
</RequireAdmin>
}
/>
Expand Down
2 changes: 1 addition & 1 deletion interface/src/framework/system/SystemStatusForm.tsx
Expand Up @@ -159,7 +159,7 @@ const SystemStatusForm: FC = () => {
<Typography variant="body2">
Use&nbsp;
<Link target="_blank" href={uploadURL} color="primary">
{'UPLOAD FIRMWARE'}
{'UPLOAD'}
</Link>
&nbsp;to apply the new firmware
</Typography>
Expand Down
26 changes: 26 additions & 0 deletions interface/src/framework/system/UploadFileForm.tsx
@@ -0,0 +1,26 @@
import { FC, useRef, useState } from 'react';

import * as SystemApi from '../../api/system';
import { SectionContent } from '../../components';
import { FileUploadConfig } from '../../api/endpoints';

import GeneralFileUpload from './GeneralFileUpload';
import RestartMonitor from './RestartMonitor';

const UploadFileForm: FC = () => {
const [restarting, setRestarting] = useState<boolean>();

const uploadFile = useRef(async (file: File, config?: FileUploadConfig) => {
const response = await SystemApi.uploadFile(file, config);
setRestarting(true);
return response;
});

return (
<SectionContent title="Upload File" titleGutter>
{restarting ? <RestartMonitor /> : <GeneralFileUpload uploadGeneralFile={uploadFile.current} />}
</SectionContent>
);
};

export default UploadFileForm;
26 changes: 0 additions & 26 deletions interface/src/framework/system/UploadFirmwareForm.tsx

This file was deleted.

0 comments on commit b16fa6d

Please sign in to comment.