diff --git a/.gitignore b/.gitignore index 2eb46fa12..8eef96223 100644 --- a/.gitignore +++ b/.gitignore @@ -47,9 +47,6 @@ interface/src/i18n/i18n-types.ts interface/src/i18n/i18n-util.ts interface/src/i18n/i18n-util.sync.ts interface/src/i18n/i18n-util.async.ts -# mock-api uploads -*/uploads/* -!uploads/README.md # scripts test.sh diff --git a/interface/package.json b/interface/package.json index 252358b2d..3b82bb022 100644 --- a/interface/package.json +++ b/interface/package.json @@ -21,7 +21,7 @@ "lint": "eslint . --fix" }, "dependencies": { - "@alova/adapter-xhr": "2.0.4", + "@alova/adapter-xhr": "2.0.5", "@emotion/react": "^11.13.0", "@emotion/styled": "^11.13.0", "@mui/icons-material": "^5.16.7", diff --git a/interface/src/api/system.ts b/interface/src/api/system.ts index ff9007b31..ce380e8b8 100644 --- a/interface/src/api/system.ts +++ b/interface/src/api/system.ts @@ -45,3 +45,6 @@ export const uploadFile = (file: File) => { timeout: 60000 // override timeout for uploading firmware - 1 minute }); }; + +export const uploadURL = (data: { url: string }) => + alovaInstance.Post('/rest/uploadURL', data); diff --git a/interface/src/app/main/Help.tsx b/interface/src/app/main/Help.tsx index dbc939e3e..fa4411e21 100644 --- a/interface/src/app/main/Help.tsx +++ b/interface/src/app/main/Help.tsx @@ -113,7 +113,7 @@ const Help = () => { color="primary" onClick={() => callAPI('system', 'allvalues')} > - {LL.ALLVALUES(0)} + {LL.ALLVALUES(0)} diff --git a/interface/src/app/main/Sensors.tsx b/interface/src/app/main/Sensors.tsx index b93b61cb3..7833e3066 100644 --- a/interface/src/app/main/Sensors.tsx +++ b/interface/src/app/main/Sensors.tsx @@ -59,15 +59,18 @@ const Sensors = () => { const [analogDialogOpen, setAnalogDialogOpen] = useState(false); const [creating, setCreating] = useState(false); - const { data: sensorData } = useAutoRequest(() => readSensorData(), { - initialData: { - ts: [], - as: [], - analog_enabled: false, - platform: 'ESP32' - }, - pollingTime: 2000 - }); + const { data: sensorData, send: fetchSensorData } = useAutoRequest( + () => readSensorData(), + { + initialData: { + ts: [], + as: [], + analog_enabled: false, + platform: 'ESP32' + }, + pollingTime: 2000 + } + ); const { send: sendTemperatureSensor } = useRequest( (data: WriteTemperatureSensor) => writeTemperatureSensor(data), @@ -256,6 +259,7 @@ const Sensors = () => { const onTemperatureDialogClose = () => { setTemperatureDialogOpen(false); + void fetchSensorData(); }; const onTemperatureDialogSave = async (ts: TemperatureSensor) => { @@ -269,6 +273,7 @@ const Sensors = () => { .finally(() => { setTemperatureDialogOpen(false); setSelectedTemperatureSensor(undefined); + void fetchSensorData(); }); }; @@ -283,6 +288,7 @@ const Sensors = () => { const onAnalogDialogClose = () => { setAnalogDialogOpen(false); + void fetchSensorData(); }; const addAnalogSensor = () => { @@ -322,6 +328,7 @@ const Sensors = () => { .finally(() => { setAnalogDialogOpen(false); setSelectedAnalogSensor(undefined); + void fetchSensorData(); }); }; diff --git a/interface/src/app/settings/DownloadUpload.tsx b/interface/src/app/settings/DownloadUpload.tsx index c63138d2e..cae8044cd 100644 --- a/interface/src/app/settings/DownloadUpload.tsx +++ b/interface/src/app/settings/DownloadUpload.tsx @@ -3,6 +3,7 @@ import { toast } from 'react-toastify'; import DownloadIcon from '@mui/icons-material/GetApp'; import PowerSettingsNewIcon from '@mui/icons-material/PowerSettingsNew'; +import WarningIcon from '@mui/icons-material/Warning'; import { Box, Button, Divider, Link, Typography } from '@mui/material'; import * as SystemApi from 'api/system'; @@ -15,6 +16,7 @@ import { } from 'api/app'; import { getDevVersion, getStableVersion } from 'api/system'; +import { dialogStyle } from 'CustomTheme'; import { useRequest } from 'alova/client'; import type { APIcall } from 'app/main/types'; import RestartMonitor from 'app/status/RestartMonitor'; @@ -72,6 +74,13 @@ const DownloadUpload = () => { error } = useRequest(SystemApi.readHardwareStatus); + const { send: sendUploadURL } = useRequest( + (data: { url: string }) => SystemApi.uploadURL(data), + { + immediate: false + } + ); + const { send: restartCommand } = useRequest(SystemApi.restart(), { immediate: false }); @@ -87,14 +96,19 @@ const DownloadUpload = () => { }; // called immediately to get the latest version, on page load - // set immediate to false to avoid calling the API on page load and GH blocking while testing! const { data: latestVersion } = useRequest(getStableVersion, { immediate: true - // immediate: false + // uncomment for testing + // https://github.com/emsesp/EMS-ESP32/releases/download/v3.6.5/EMS-ESP-3_6_5-ESP32-16MB+.bin + // immediate: false, + // initialData: '3.6.5' }); const { data: latestDevVersion } = useRequest(getDevVersion, { immediate: true - // immediate: false + // uncomment for testing + // https://github.com/emsesp/EMS-ESP32/releases/download/latest/EMS-ESP-3_7_0-dev_31-ESP32-16MB+.bin + // immediate: false, + // initialData: '3.7.0-dev.31' }); const STABLE_URL = 'https://github.com/emsesp/EMS-ESP32/releases/download/'; @@ -115,6 +129,13 @@ const DownloadUpload = () => { ); }; + const installFirmwareURL = async (url: string) => { + await sendUploadURL({ url: url }).catch((error: Error) => { + toast.error(error.message); + }); + setRestarting(true); + }; + const saveFile = (json: unknown, filename: string) => { const anchor = document.createElement('a'); anchor.href = URL.createObjectURL( @@ -275,6 +296,20 @@ const DownloadUpload = () => { {LL.DOWNLOAD(1)} ) + )} {latestDevVersion && ( @@ -295,6 +330,18 @@ const DownloadUpload = () => { {LL.DOWNLOAD(1)} ) + )} @@ -326,7 +373,13 @@ const DownloadUpload = () => { }; return ( - {restarting ? : content()} + + {restarting ? ( + + ) : ( + content() + )} + ); }; diff --git a/interface/src/app/status/RestartMonitor.tsx b/interface/src/app/status/RestartMonitor.tsx index ba358af4a..685cac3e8 100644 --- a/interface/src/app/status/RestartMonitor.tsx +++ b/interface/src/app/status/RestartMonitor.tsx @@ -1,4 +1,4 @@ -import { useEffect, useRef, useState } from 'react'; +import { type FC, useEffect, useRef, useState } from 'react'; import * as SystemApi from 'api/system'; @@ -6,10 +6,14 @@ import { useRequest } from 'alova/client'; import { FormLoader } from 'components'; import { useI18nContext } from 'i18n/i18n-react'; -const RESTART_TIMEOUT = 2 * 60 * 1000; -const POLL_INTERVAL = 1000; +const RESTART_TIMEOUT = 2 * 60 * 1000; // 2 minutes +const POLL_INTERVAL = 1000; // every 1 second -const RestartMonitor = () => { +export interface RestartMonitorProps { + message?: string; +} + +const RestartMonitor: FC = ({ message }) => { const [failed, setFailed] = useState(false); const [timeoutId, setTimeoutId] = useState(); const { LL } = useI18nContext(); @@ -38,7 +42,7 @@ const RestartMonitor = () => { return ( ); diff --git a/interface/src/app/status/Status.tsx b/interface/src/app/status/Status.tsx index e591b8d8b..cca48dbee 100644 --- a/interface/src/app/status/Status.tsx +++ b/interface/src/app/status/Status.tsx @@ -306,7 +306,7 @@ const SystemStatus = () => { ); const content = () => { - if (!data) { + if (!data || !LL) { return ; } @@ -321,7 +321,7 @@ const SystemStatus = () => { {me.admin && (