From e501ac31f57e8b7d7f86b2b05430bab45e98d80f Mon Sep 17 00:00:00 2001 From: proddy Date: Sat, 5 Oct 2024 16:25:53 +0200 Subject: [PATCH] translations and finish writeDevice --- interface/package.json | 4 +- interface/src/app/main/CustomEntities.tsx | 21 ++-- interface/src/app/main/Customizations.tsx | 2 +- interface/src/app/main/Dashboard.tsx | 119 +++++++----------- interface/src/app/main/Devices.tsx | 17 +-- interface/src/app/main/Help.tsx | 4 +- interface/src/app/main/Modules.tsx | 2 +- interface/src/app/main/Scheduler.tsx | 2 +- interface/src/app/main/Sensors.tsx | 2 +- interface/src/app/main/types.ts | 12 +- interface/src/app/settings/DownloadUpload.tsx | 6 +- interface/src/app/status/APStatus.tsx | 2 +- interface/src/app/status/Activity.tsx | 2 +- interface/src/app/status/HardwareStatus.tsx | 2 +- interface/src/app/status/MqttStatus.tsx | 2 +- interface/src/app/status/NTPStatus.tsx | 2 +- interface/src/app/status/NetworkStatus.tsx | 2 +- interface/src/app/status/RestartMonitor.tsx | 2 +- interface/src/app/status/Status.tsx | 2 +- interface/src/i18n/de/index.ts | 5 +- interface/src/i18n/en/index.ts | 5 +- interface/src/i18n/fr/index.ts | 5 +- interface/src/i18n/it/index.ts | 5 +- interface/src/i18n/nl/index.ts | 5 +- interface/src/i18n/no/index.ts | 5 +- interface/src/i18n/pl/index.ts | 5 +- interface/src/i18n/sk/index.ts | 5 +- interface/src/i18n/sv/index.ts | 5 +- interface/src/i18n/tr/index.ts | 5 +- interface/src/utils/index.ts | 2 + interface/src/utils/useInterval.ts | 22 ++++ interface/yarn.lock | 73 ++++++----- mock-api/rest_server.ts | 63 ++++++---- 33 files changed, 230 insertions(+), 187 deletions(-) create mode 100644 interface/src/utils/useInterval.ts diff --git a/interface/package.json b/interface/package.json index f6a92f559..01082ee23 100644 --- a/interface/package.json +++ b/interface/package.json @@ -42,7 +42,7 @@ }, "devDependencies": { "@babel/core": "^7.25.7", - "@eslint/js": "^9.11.1", + "@eslint/js": "^9.12.0", "@preact/compat": "^18.3.1", "@preact/preset-vite": "^2.9.1", "@trivago/prettier-plugin-sort-imports": "^4.3.0", @@ -52,7 +52,7 @@ "@types/react-dom": "^18.3.0", "@types/react-router-dom": "^5.3.3", "concurrently": "^9.0.1", - "eslint": "^9.11.1", + "eslint": "^9.12.0", "eslint-config-prettier": "^9.1.0", "formidable": "^3.5.1", "prettier": "^3.3.3", diff --git a/interface/src/app/main/CustomEntities.tsx b/interface/src/app/main/CustomEntities.tsx index 09a2d51c9..68fac89d2 100644 --- a/interface/src/app/main/CustomEntities.tsx +++ b/interface/src/app/main/CustomEntities.tsx @@ -1,4 +1,4 @@ -import { useCallback, useEffect, useState } from 'react'; +import { useCallback, useState } from 'react'; import { useBlocker } from 'react-router-dom'; import { toast } from 'react-toastify'; @@ -27,6 +27,7 @@ import { useLayoutTitle } from 'components'; import { useI18nContext } from 'i18n/i18n-react'; +import { useInterval } from 'utils'; import { readCustomEntities, writeCustomEntities } from '../../api/app'; import SettingsCustomEntitiesDialog from './CustomEntitiesDialog'; @@ -52,17 +53,11 @@ const CustomEntities = () => { initialData: [] }); - useEffect(() => { - const timer = setInterval(async () => { - if (dialogOpen || numChanges > 0) { - return; - } - await fetchEntities(); - }, 2000); - return () => { - clearInterval(timer); - }; - }, []); + useInterval(() => { + if (!dialogOpen && !numChanges) { + void fetchEntities(); + } + }, 3000); const { send: writeEntities } = useRequest( (data: Entities) => writeCustomEntities(data), @@ -295,7 +290,7 @@ const CustomEntities = () => { {blocker ? : null} - {LL.ENTITIES_HELP_1()} + {LL.ENTITIES_HELP_1()} {renderEntity()} diff --git a/interface/src/app/main/Customizations.tsx b/interface/src/app/main/Customizations.tsx index 653c28e42..72babe728 100644 --- a/interface/src/app/main/Customizations.tsx +++ b/interface/src/app/main/Customizations.tsx @@ -427,7 +427,7 @@ const Customizations = () => { const renderDeviceList = () => ( <> - {LL.CUSTOMIZATIONS_HELP_1()}. + {LL.CUSTOMIZATIONS_HELP_1()}. {rename ? ( diff --git a/interface/src/app/main/Dashboard.tsx b/interface/src/app/main/Dashboard.tsx index cab09a708..190a2ef93 100644 --- a/interface/src/app/main/Dashboard.tsx +++ b/interface/src/app/main/Dashboard.tsx @@ -22,23 +22,29 @@ import { useRequest } from 'alova/client'; import { FormLoader, SectionContent, useLayoutTitle } from 'components'; import { AuthenticatedContext } from 'contexts/authentication'; import { useI18nContext } from 'i18n/i18n-react'; +import { useInterval } from 'utils'; import { readDashboard, writeDeviceValue } from '../../api/app'; import DeviceIcon from './DeviceIcon'; import DashboardDevicesDialog from './DevicesDialog'; import { formatValue } from './deviceValue'; -import { type DashboardItem, type DeviceValue } from './types'; +import { type DashboardItem, DeviceEntityMask, type DeviceValue } from './types'; import { deviceValueItemValidation } from './validators'; const Dashboard = () => { const { LL } = useI18nContext(); const { me } = useContext(AuthenticatedContext); - useLayoutTitle('Dashboard'); // TODO translate + useLayoutTitle(LL.DASHBOARD()); const [firstLoad, setFirstLoad] = useState(true); const [showAll, setShowAll] = useState(true); + const [deviceValueDialogOpen, setDeviceValueDialogOpen] = useState(false); + + const [selectedDashboardItem, setSelectedDashboardItem] = + useState(); + const { data, send: fetchDashboard, @@ -55,17 +61,11 @@ const Dashboard = () => { } ); - const [deviceValueDialogOpen, setDeviceValueDialogOpen] = useState(false); - const [selectedDeviceValue, setSelectedDeviceValue] = useState(); - - const deviceValueDialogClose = () => { - setDeviceValueDialogOpen(false); - void sendDeviceData(selectedDevice); - }; - - // TODO get this working next const deviceValueDialogSave = async (devicevalue: DeviceValue) => { - const id = Number(device_select.state.id); + if (!selectedDashboardItem) { + return; + } + const id = selectedDashboardItem.parentNode.id; // this is the parent ID await sendDeviceValue({ id, c: devicevalue.c ?? '', v: devicevalue.v }) .then(() => { toast.success(LL.WRITE_CMD_SENT()); @@ -73,10 +73,9 @@ const Dashboard = () => { .catch((error: Error) => { toast.error(error.message); }) - .finally(async () => { + .finally(() => { setDeviceValueDialogOpen(false); - await sendDeviceData(id); - setSelectedDeviceValue(undefined); + setSelectedDashboardItem(undefined); }); }; @@ -105,7 +104,7 @@ const Dashboard = () => { const tree = useTree( { nodes: data }, { - onChange: null + onChange: null // not used but needed }, { treeIcon: { @@ -118,25 +117,19 @@ const Dashboard = () => { } ); - useEffect(() => { - const timer = setInterval(() => { - if (deviceValueDialogOpen) { - return; - } - fetchDashboard(); - }, 2000); - return () => { - clearInterval(timer); - }; - }, []); + useInterval(() => { + if (!deviceValueDialogOpen) { + void fetchDashboard(); + } + }, 3000); // auto expand on first load useEffect(() => { - if (firstLoad && data.length && !tree.state.ids.length) { + if (firstLoad && Array.isArray(data) && data.length && !tree.state.ids.length) { tree.fns.onToggleAll({}); setFirstLoad(false); } - }, [data]); + }, [loading]); const showName = (di: DashboardItem) => { if (di.id < 100) { @@ -145,7 +138,7 @@ const Dashboard = () => { return ( <> - +   {di.n}  ({di.nodes?.length}) @@ -153,20 +146,14 @@ const Dashboard = () => { ); } } - return {di.n}; + return {di.dv ? di.dv.id.slice(2) : ''}; }; - const showDeviceValue = (di: DashboardItem) => { - // convert di to dv - // TODO should we not just use dv? - const dv: DeviceValue = { - id: ' ' + di.n, - v: di.v, - u: di.u, - c: di.c, - l: di.l - }; - setSelectedDeviceValue(dv); + const hasMask = (id: string, mask: number) => + (parseInt(id.slice(0, 2), 16) & mask) === mask; + + const editDashboardValue = (di: DashboardItem) => { + setSelectedDashboardItem(di); setDeviceValueDialogOpen(true); }; @@ -185,21 +172,10 @@ const Dashboard = () => { return ; } - // if (data.length === 0) { - // return ( - // - // {/* TODO translate */} - // No entities found. - // - // ); - // } - return ( <> - - {/* TODO translate */} - The dashboard shows all EMS entities that are marked as favorite, and the - sensors. + + {LL.DASHBOARD_1()} { }} > {!loading && data.length === 0 ? ( - - {/* TODO translate */} - No entities found. + + {LL.NO_DATA()} ) : ( { )} - {formatValue(LL, di.v, di.u)} + {di.dv && formatValue(LL, di.dv.v, di.dv.u)} - {me.admin && di.c && ( - showDeviceValue(di)} - > - - - )} + {me.admin && + di.dv?.c && + !hasMask(di.dv.id, DeviceEntityMask.DV_READONLY) && ( + editDashboardValue(di)} + > + + + )} ))} @@ -286,14 +263,14 @@ const Dashboard = () => { return ( {renderContent()} - {selectedDeviceValue && ( + {selectedDashboardItem && selectedDashboardItem.dv && ( setDeviceValueDialogOpen(false)} onSave={deviceValueDialogSave} - selectedItem={selectedDeviceValue} + selectedItem={selectedDashboardItem.dv} writeable={true} - validator={deviceValueItemValidation(selectedDeviceValue)} + validator={deviceValueItemValidation(selectedDashboardItem.dv)} progress={submitting} /> )} diff --git a/interface/src/app/main/Devices.tsx b/interface/src/app/main/Devices.tsx index c57b56a6f..a075279df 100644 --- a/interface/src/app/main/Devices.tsx +++ b/interface/src/app/main/Devices.tsx @@ -60,6 +60,7 @@ import { useRequest } from 'alova/client'; import { MessageBox, SectionContent, useLayoutTitle } from 'components'; import { AuthenticatedContext } from 'contexts/authentication'; import { useI18nContext } from 'i18n/i18n-react'; +import { useInterval } from 'utils'; import { readCoreData, readDeviceData, writeDeviceValue } from '../../api/app'; import DeviceIcon from './DeviceIcon'; @@ -77,7 +78,7 @@ const Devices = () => { const [selectedDeviceValue, setSelectedDeviceValue] = useState(); const [onlyFav, setOnlyFav] = useState(false); const [deviceValueDialogOpen, setDeviceValueDialogOpen] = useState(false); - const [showDeviceInfo, setShowDeviceInfo] = useState(false); + const [showDeviceInfo, setShowDeviceInfo] = useState(false); const [selectedDevice, setSelectedDevice] = useState(); const navigate = useNavigate(); @@ -418,17 +419,11 @@ const Devices = () => { downloadBlob(new Blob([csvData], { type: 'text/csv;charset:utf-8' })); }; - useEffect(() => { - const timer = setInterval(() => { - if (deviceValueDialogOpen) { - return; - } + useInterval(() => { + if (!deviceValueDialogOpen) { selectedDevice ? void sendDeviceData(selectedDevice) : void sendCoreData(); - }, 2000); - return () => { - clearInterval(timer); - }; - }, []); + } + }, 3000); const deviceValueDialogSave = async (devicevalue: DeviceValue) => { const id = Number(device_select.state.id); diff --git a/interface/src/app/main/Help.tsx b/interface/src/app/main/Help.tsx index da45a0f1b..159a1ace6 100644 --- a/interface/src/app/main/Help.tsx +++ b/interface/src/app/main/Help.tsx @@ -24,7 +24,7 @@ import { useRequest } from 'alova/client'; import { SectionContent, useLayoutTitle } from 'components'; import { AuthenticatedContext } from 'contexts/authentication'; import { useI18nContext } from 'i18n/i18n-react'; -import { saveFile } from 'utils/file'; +import { saveFile } from 'utils'; import { API, callAction } from '../../api/app'; import type { APIcall } from './types'; @@ -147,7 +147,7 @@ const Help = () => { )} - + {LL.HELP_INFORMATION_4()}
{ {blocker ? : null} - {LL.SCHEDULER_HELP_1()} + {LL.SCHEDULER_HELP_1()} {renderSchedule()} diff --git a/interface/src/app/main/Sensors.tsx b/interface/src/app/main/Sensors.tsx index bfaf10d19..121c72d95 100644 --- a/interface/src/app/main/Sensors.tsx +++ b/interface/src/app/main/Sensors.tsx @@ -68,7 +68,7 @@ const Sensors = () => { analog_enabled: false, platform: 'ESP32' }, - pollingTime: 2000 + pollingTime: 3000 } ); diff --git a/interface/src/app/main/types.ts b/interface/src/app/main/types.ts index 8b88b6392..c44325825 100644 --- a/interface/src/app/main/types.ts +++ b/interface/src/app/main/types.ts @@ -116,14 +116,10 @@ export interface CoreData { export interface DashboardItem { id: number; // unique index - n: string; // name - v?: unknown; // value, optional - u: number; // uom - t: number; // type from DeviceType - c?: string; // command, optional - l?: string[]; // list, optional - h?: string; // help text, optional - nodes?: DashboardItem[]; // nodes, optional + t?: number; // type from DeviceType + n?: string; // name + dv?: DeviceValue; + nodes?: DashboardItem[]; // children nodes, optional } export interface DashboardData { diff --git a/interface/src/app/settings/DownloadUpload.tsx b/interface/src/app/settings/DownloadUpload.tsx index c3cc12e1a..07753a40a 100644 --- a/interface/src/app/settings/DownloadUpload.tsx +++ b/interface/src/app/settings/DownloadUpload.tsx @@ -32,7 +32,7 @@ import { useLayoutTitle } from 'components'; import { useI18nContext } from 'i18n/i18n-react'; -import { saveFile } from 'utils/file'; +import { saveFile } from 'utils'; const DownloadUpload = () => { const { LL } = useI18nContext(); @@ -221,7 +221,7 @@ const DownloadUpload = () => { {LL.DOWNLOAD(0)} - + {LL.DOWNLOAD_SETTINGS_TEXT()} @@ -269,7 +269,7 @@ const DownloadUpload = () => { - {LL.UPLOAD_TEXT()} + {LL.UPLOAD_TEXT()} diff --git a/interface/src/app/status/APStatus.tsx b/interface/src/app/status/APStatus.tsx index ee6bc22ab..2f4903976 100644 --- a/interface/src/app/status/APStatus.tsx +++ b/interface/src/app/status/APStatus.tsx @@ -38,7 +38,7 @@ const APStatus = () => { data, send: loadData, error - } = useAutoRequest(APApi.readAPStatus, { pollingTime: 5000 }); + } = useAutoRequest(APApi.readAPStatus, { pollingTime: 3000 }); const { LL } = useI18nContext(); useLayoutTitle(LL.STATUS_OF(LL.ACCESS_POINT(0))); diff --git a/interface/src/app/status/Activity.tsx b/interface/src/app/status/Activity.tsx index b91073fc6..6e1a78c50 100644 --- a/interface/src/app/status/Activity.tsx +++ b/interface/src/app/status/Activity.tsx @@ -21,7 +21,7 @@ const SystemActivity = () => { data, send: loadData, error - } = useAutoRequest(readActivity, { pollingTime: 2000 }); + } = useAutoRequest(readActivity, { pollingTime: 3000 }); const { LL } = useI18nContext(); diff --git a/interface/src/app/status/HardwareStatus.tsx b/interface/src/app/status/HardwareStatus.tsx index 50b21dc3e..93f8f3d1a 100644 --- a/interface/src/app/status/HardwareStatus.tsx +++ b/interface/src/app/status/HardwareStatus.tsx @@ -36,7 +36,7 @@ const HardwareStatus = () => { data, send: loadData, error - } = useAutoRequest(SystemApi.readSystemStatus, { pollingTime: 2000 }); + } = useAutoRequest(SystemApi.readSystemStatus, { pollingTime: 3000 }); const content = () => { if (!data) { diff --git a/interface/src/app/status/MqttStatus.tsx b/interface/src/app/status/MqttStatus.tsx index a1096f724..8f7207c1b 100644 --- a/interface/src/app/status/MqttStatus.tsx +++ b/interface/src/app/status/MqttStatus.tsx @@ -58,7 +58,7 @@ const MqttStatus = () => { data, send: loadData, error - } = useAutoRequest(MqttApi.readMqttStatus, { pollingTime: 5000 }); + } = useAutoRequest(MqttApi.readMqttStatus, { pollingTime: 3000 }); const { LL } = useI18nContext(); useLayoutTitle(LL.STATUS_OF('MQTT')); diff --git a/interface/src/app/status/NTPStatus.tsx b/interface/src/app/status/NTPStatus.tsx index 757d65225..a65ee8620 100644 --- a/interface/src/app/status/NTPStatus.tsx +++ b/interface/src/app/status/NTPStatus.tsx @@ -40,7 +40,7 @@ const NTPStatus = () => { data, send: loadData, error - } = useAutoRequest(NTPApi.readNTPStatus, { pollingTime: 5000 }); + } = useAutoRequest(NTPApi.readNTPStatus, { pollingTime: 3000 }); const [localTime, setLocalTime] = useState(''); const [settingTime, setSettingTime] = useState(false); diff --git a/interface/src/app/status/NetworkStatus.tsx b/interface/src/app/status/NetworkStatus.tsx index 92d9dff18..55112b295 100644 --- a/interface/src/app/status/NetworkStatus.tsx +++ b/interface/src/app/status/NetworkStatus.tsx @@ -85,7 +85,7 @@ const NetworkStatus = () => { data, send: loadData, error - } = useAutoRequest(NetworkApi.readNetworkStatus, { pollingTime: 5000 }); + } = useAutoRequest(NetworkApi.readNetworkStatus, { pollingTime: 3000 }); const { LL } = useI18nContext(); useLayoutTitle(LL.STATUS_OF(LL.NETWORK(1))); diff --git a/interface/src/app/status/RestartMonitor.tsx b/interface/src/app/status/RestartMonitor.tsx index d95487d59..d3a7cede8 100644 --- a/interface/src/app/status/RestartMonitor.tsx +++ b/interface/src/app/status/RestartMonitor.tsx @@ -28,7 +28,7 @@ const RestartMonitor = () => { initialData: { status: 'Getting ready...' }, async middleware(_, next) { if (count++ >= 1) { - // skip first request (1 seconds) to allow AsyncWS to send its response + // skip first request (1 second) to allow AsyncWS to send its response await next(); } } diff --git a/interface/src/app/status/Status.tsx b/interface/src/app/status/Status.tsx index 74a6f6747..f3a53dbdd 100644 --- a/interface/src/app/status/Status.tsx +++ b/interface/src/app/status/Status.tsx @@ -65,7 +65,7 @@ const SystemStatus = () => { error } = useAutoRequest(readSystemStatus, { initialData: [], - pollingTime: 5000, + pollingTime: 3000, async middleware(_, next) { if (!restarting) { await next(); diff --git a/interface/src/i18n/de/index.ts b/interface/src/i18n/de/index.ts index 03ad2f1fd..3faa417ac 100644 --- a/interface/src/i18n/de/index.ts +++ b/interface/src/i18n/de/index.ts @@ -340,7 +340,10 @@ const de: Translation = { PLEASE_WAIT: 'Bitte warten', RESTARTING_PRE: 'Initialisierung', RESTARTING_POST: 'Vorbereitung', - AUTO_SCROLL: 'Automatisches Scrollen' + AUTO_SCROLL: 'Automatisches Scrollen', + DASHBOARD: 'Dashboard', // TODO translate + NO_DATA: 'No data available', // TODO translate + DASHBOARD_1: 'Customize your dashboard by marking EMS entities as Favorites using the Customizations module.' // TODO translate }; export default de; diff --git a/interface/src/i18n/en/index.ts b/interface/src/i18n/en/index.ts index b9f48abd2..dc4097adc 100644 --- a/interface/src/i18n/en/index.ts +++ b/interface/src/i18n/en/index.ts @@ -340,7 +340,10 @@ const en: Translation = { PLEASE_WAIT: 'Please wait', RESTARTING_PRE: 'Initializing', RESTARTING_POST: 'Preparing', - AUTO_SCROLL: 'Auto Scroll' + AUTO_SCROLL: 'Auto Scroll', + DASHBOARD: 'Dashboard', + NO_DATA: 'No data available', + DASHBOARD_1: 'Customize your dashboard by marking EMS entities as Favorites using the Customizations module.', }; export default en; diff --git a/interface/src/i18n/fr/index.ts b/interface/src/i18n/fr/index.ts index d9ceff889..65bc641f0 100644 --- a/interface/src/i18n/fr/index.ts +++ b/interface/src/i18n/fr/index.ts @@ -340,7 +340,10 @@ const fr: Translation = { PLEASE_WAIT: 'Please wait', // TODO translate RESTARTING_PRE: 'Initializing', // TODO translate RESTARTING_POST: 'Preparing', // TODO translate - AUTO_SCROLL: 'Auto Scroll' // TODO translate + AUTO_SCROLL: 'Auto Scroll', // TODO translate + DASHBOARD: 'Dashboard', // TODO translate + NO_DATA: 'No data available', // TODO translate + DASHBOARD_1: 'Customize your dashboard by marking EMS entities as Favorites using the Customizations module.' // TODO translate }; export default fr; diff --git a/interface/src/i18n/it/index.ts b/interface/src/i18n/it/index.ts index 4dbc4b5a1..ce9f1c48b 100644 --- a/interface/src/i18n/it/index.ts +++ b/interface/src/i18n/it/index.ts @@ -340,7 +340,10 @@ const it: Translation = { PLEASE_WAIT: 'Please wait', // TODO translate RESTARTING_PRE: 'Initializing', // TODO translate RESTARTING_POST: 'Preparing', // TODO translate - AUTO_SCROLL: 'Auto Scroll' // TODO translate + AUTO_SCROLL: 'Auto Scroll', // TODO translate + DASHBOARD: 'Dashboard', // TODO translate + NO_DATA: 'No data available', // TODO translate + DASHBOARD_1: 'Customize your dashboard by marking EMS entities as Favorites using the Customizations module.' // TODO translate }; export default it; diff --git a/interface/src/i18n/nl/index.ts b/interface/src/i18n/nl/index.ts index 688b83c43..bbc44ad59 100644 --- a/interface/src/i18n/nl/index.ts +++ b/interface/src/i18n/nl/index.ts @@ -340,7 +340,10 @@ const nl: Translation = { PLEASE_WAIT: 'Please wait', // TODO translate RESTARTING_PRE: 'Initializing', // TODO translate RESTARTING_POST: 'Preparing', // TODO translate - AUTO_SCROLL: 'Auto Scroll' // TODO translate + AUTO_SCROLL: 'Auto Scroll', // TODO translate + DASHBOARD: 'Dashboard', // TODO translate + NO_DATA: 'No data available', // TODO translate + DASHBOARD_1: 'Customize your dashboard by marking EMS entities as Favorites using the Customizations module.' // TODO translate }; export default nl; diff --git a/interface/src/i18n/no/index.ts b/interface/src/i18n/no/index.ts index a019130d5..830bbb2b2 100644 --- a/interface/src/i18n/no/index.ts +++ b/interface/src/i18n/no/index.ts @@ -340,7 +340,10 @@ const no: Translation = { PLEASE_WAIT: 'Please wait', // TODO translate RESTARTING_PRE: 'Initializing', // TODO translate RESTARTING_POST: 'Preparing', // TODO translate - AUTO_SCROLL: 'Auto Scroll' // TODO translate + AUTO_SCROLL: 'Auto Scroll', // TODO translate + DASHBOARD: 'Dashboard', // TODO translate + NO_DATA: 'No data available', // TODO translate + DASHBOARD_1: 'Customize your dashboard by marking EMS entities as Favorites using the Customizations module.' // TODO translate }; export default no; diff --git a/interface/src/i18n/pl/index.ts b/interface/src/i18n/pl/index.ts index 09d12cff7..adeed62b1 100644 --- a/interface/src/i18n/pl/index.ts +++ b/interface/src/i18n/pl/index.ts @@ -340,7 +340,10 @@ const pl: BaseTranslation = { PLEASE_WAIT: 'Please wait', // TODO translate RESTARTING_PRE: 'Initializing', // TODO translate RESTARTING_POST: 'Preparing', // TODO translate - AUTO_SCROLL: 'Auto Scroll' // TODO translate + AUTO_SCROLL: 'Auto Scroll', // TODO translate + DASHBOARD: 'Dashboard', // TODO translate + NO_DATA: 'No data available', // TODO translate + DASHBOARD_1: 'Customize your dashboard by marking EMS entities as Favorites using the Customizations module.' // TODO translate }; export default pl; diff --git a/interface/src/i18n/sk/index.ts b/interface/src/i18n/sk/index.ts index e4e68da93..c29a95fe7 100644 --- a/interface/src/i18n/sk/index.ts +++ b/interface/src/i18n/sk/index.ts @@ -340,7 +340,10 @@ const sk: Translation = { PLEASE_WAIT: 'Čakajte prosím', RESTARTING_PRE: 'Prebieha inicializácia', RESTARTING_POST: 'Príprava', - AUTO_SCROLL: 'Auto Scroll' // TODO translate + AUTO_SCROLL: 'Auto Scroll', // TODO translate + DASHBOARD: 'Dashboard', // TODO translate + NO_DATA: 'No data available', // TODO translate + DASHBOARD_1: 'Customize your dashboard by marking EMS entities as Favorites using the Customizations module.' // TODO translate }; export default sk; diff --git a/interface/src/i18n/sv/index.ts b/interface/src/i18n/sv/index.ts index 0a7be5378..2b0900510 100644 --- a/interface/src/i18n/sv/index.ts +++ b/interface/src/i18n/sv/index.ts @@ -340,7 +340,10 @@ const sv: Translation = { PLEASE_WAIT: 'Please wait', // TODO translate RESTARTING_PRE: 'Initializing', // TODO translate RESTARTING_POST: 'Preparing', // TODO translate - AUTO_SCROLL: 'Auto Scroll' // TODO translate + AUTO_SCROLL: 'Auto Scroll', // TODO translate + DASHBOARD: 'Dashboard', // TODO translate + NO_DATA: 'No data available', // TODO translate + DASHBOARD_1: 'Customize your dashboard by marking EMS entities as Favorites using the Customizations module.' // TODO translate }; export default sv; diff --git a/interface/src/i18n/tr/index.ts b/interface/src/i18n/tr/index.ts index a94ef0031..02add6145 100644 --- a/interface/src/i18n/tr/index.ts +++ b/interface/src/i18n/tr/index.ts @@ -340,7 +340,10 @@ const tr: Translation = { PLEASE_WAIT: 'Please wait', // TODO translate RESTARTING_PRE: 'Initializing', // TODO translate RESTARTING_POST: 'Preparing', // TODO translate - AUTO_SCROLL: 'Auto Scroll' // TODO translate + AUTO_SCROLL: 'Auto Scroll', // TODO translate + DASHBOARD: 'Dashboard', // TODO translate + NO_DATA: 'No data available', // TODO translate + DASHBOARD_1: 'Customize your dashboard by marking EMS entities as Favorites using the Customizations module.' // TODO translate }; export default tr; diff --git a/interface/src/utils/index.ts b/interface/src/utils/index.ts index 7ee4414df..e704f66e5 100644 --- a/interface/src/utils/index.ts +++ b/interface/src/utils/index.ts @@ -3,4 +3,6 @@ export * from './route'; export * from './submit'; export * from './time'; export * from './useRest'; +export * from './useInterval'; export * from './props'; +export * from './file'; diff --git a/interface/src/utils/useInterval.ts b/interface/src/utils/useInterval.ts new file mode 100644 index 000000000..61ed2c9e6 --- /dev/null +++ b/interface/src/utils/useInterval.ts @@ -0,0 +1,22 @@ +import { useEffect, useRef } from 'react'; + +// adapted from https://www.joshwcomeau.com/snippets/react-hooks/use-interval/ +export const useInterval = (callback: () => void, delay: number) => { + const intervalRef = useRef(null); + const savedCallback = useRef<() => void>(callback); + useEffect(() => { + savedCallback.current = callback; + }, [callback]); + useEffect(() => { + const tick = () => savedCallback.current(); + if (typeof delay === 'number') { + intervalRef.current = window.setInterval(tick, delay); + return () => { + if (intervalRef.current !== null) { + window.clearInterval(intervalRef.current); + } + }; + } + }, [delay]); + return intervalRef; +}; diff --git a/interface/yarn.lock b/interface/yarn.lock index 643fa47d6..fc0860f80 100644 --- a/interface/yarn.lock +++ b/interface/yarn.lock @@ -918,10 +918,10 @@ __metadata: languageName: node linkType: hard -"@eslint/js@npm:9.11.1, @eslint/js@npm:^9.11.1": - version: 9.11.1 - resolution: "@eslint/js@npm:9.11.1" - checksum: 10c0/22916ef7b09c6f60c62635d897c66e1e3e38d90b5a5cf5e62769033472ecbcfb6ec7c886090a4b32fe65d6ce371da54384e46c26a899e38184dfc152c6152f7b +"@eslint/js@npm:9.12.0, @eslint/js@npm:^9.12.0": + version: 9.12.0 + resolution: "@eslint/js@npm:9.12.0" + checksum: 10c0/325650a59a1ce3d97c69441501ebaf415607248bacbe8c8ca35adc7cb73b524f592f266a75772f496b06f3239e3ee1996722a242148085f0ee5fb3dd7065897c languageName: node linkType: hard @@ -941,6 +941,23 @@ __metadata: languageName: node linkType: hard +"@humanfs/core@npm:^0.19.0": + version: 0.19.0 + resolution: "@humanfs/core@npm:0.19.0" + checksum: 10c0/f87952d5caba6ae427a620eff783c5d0b6cef0cfc256dec359cdaa636c5f161edb8d8dad576742b3de7f0b2f222b34aad6870248e4b7d2177f013426cbcda232 + languageName: node + linkType: hard + +"@humanfs/node@npm:^0.16.5": + version: 0.16.5 + resolution: "@humanfs/node@npm:0.16.5" + dependencies: + "@humanfs/core": "npm:^0.19.0" + "@humanwhocodes/retry": "npm:^0.3.0" + checksum: 10c0/41c365ab09e7c9eaeed373d09243195aef616d6745608a36fc3e44506148c28843872f85e69e2bf5f1e992e194286155a1c1cecfcece6a2f43875e37cd243935 + languageName: node + linkType: hard + "@humanwhocodes/module-importer@npm:^1.0.1": version: 1.0.1 resolution: "@humanwhocodes/module-importer@npm:1.0.1" @@ -955,6 +972,13 @@ __metadata: languageName: node linkType: hard +"@humanwhocodes/retry@npm:^0.3.1": + version: 0.3.1 + resolution: "@humanwhocodes/retry@npm:0.3.1" + checksum: 10c0/f0da1282dfb45e8120480b9e2e275e2ac9bbe1cf016d046fdad8e27cc1285c45bb9e711681237944445157b430093412b4446c1ab3fc4bb037861b5904101d3b + languageName: node + linkType: hard + "@isaacs/cliui@npm:^8.0.2": version: 8.0.2 resolution: "@isaacs/cliui@npm:8.0.2" @@ -1196,7 +1220,7 @@ __metadata: languageName: node linkType: hard -"@nodelib/fs.walk@npm:^1.2.3, @nodelib/fs.walk@npm:^1.2.8": +"@nodelib/fs.walk@npm:^1.2.3": version: 1.2.8 resolution: "@nodelib/fs.walk@npm:1.2.8" dependencies: @@ -1844,7 +1868,7 @@ __metadata: "@babel/core": "npm:^7.25.7" "@emotion/react": "npm:^11.13.3" "@emotion/styled": "npm:^11.13.0" - "@eslint/js": "npm:^9.11.1" + "@eslint/js": "npm:^9.12.0" "@mui/icons-material": "npm:^6.1.2" "@mui/material": "npm:^6.1.2" "@preact/compat": "npm:^18.3.1" @@ -1859,7 +1883,7 @@ __metadata: alova: "npm:3.0.17" async-validator: "npm:^4.2.5" concurrently: "npm:^9.0.1" - eslint: "npm:^9.11.1" + eslint: "npm:^9.12.0" eslint-config-prettier: "npm:^9.1.0" formidable: "npm:^3.5.1" jwt-decode: "npm:^4.0.0" @@ -3322,7 +3346,7 @@ __metadata: languageName: node linkType: hard -"eslint-scope@npm:^8.0.2": +"eslint-scope@npm:^8.1.0": version: 8.1.0 resolution: "eslint-scope@npm:8.1.0" dependencies: @@ -3339,27 +3363,27 @@ __metadata: languageName: node linkType: hard -"eslint-visitor-keys@npm:^4.0.0, eslint-visitor-keys@npm:^4.1.0": +"eslint-visitor-keys@npm:^4.1.0": version: 4.1.0 resolution: "eslint-visitor-keys@npm:4.1.0" checksum: 10c0/5483ef114c93a136aa234140d7aa3bd259488dae866d35cb0d0b52e6a158f614760a57256ac8d549acc590a87042cb40f6951815caa821e55dc4fd6ef4c722eb languageName: node linkType: hard -"eslint@npm:^9.11.1": - version: 9.11.1 - resolution: "eslint@npm:9.11.1" +"eslint@npm:^9.12.0": + version: 9.12.0 + resolution: "eslint@npm:9.12.0" dependencies: "@eslint-community/eslint-utils": "npm:^4.2.0" "@eslint-community/regexpp": "npm:^4.11.0" "@eslint/config-array": "npm:^0.18.0" "@eslint/core": "npm:^0.6.0" "@eslint/eslintrc": "npm:^3.1.0" - "@eslint/js": "npm:9.11.1" + "@eslint/js": "npm:9.12.0" "@eslint/plugin-kit": "npm:^0.2.0" + "@humanfs/node": "npm:^0.16.5" "@humanwhocodes/module-importer": "npm:^1.0.1" - "@humanwhocodes/retry": "npm:^0.3.0" - "@nodelib/fs.walk": "npm:^1.2.8" + "@humanwhocodes/retry": "npm:^0.3.1" "@types/estree": "npm:^1.0.6" "@types/json-schema": "npm:^7.0.15" ajv: "npm:^6.12.4" @@ -3367,9 +3391,9 @@ __metadata: cross-spawn: "npm:^7.0.2" debug: "npm:^4.3.2" escape-string-regexp: "npm:^4.0.0" - eslint-scope: "npm:^8.0.2" - eslint-visitor-keys: "npm:^4.0.0" - espree: "npm:^10.1.0" + eslint-scope: "npm:^8.1.0" + eslint-visitor-keys: "npm:^4.1.0" + espree: "npm:^10.2.0" esquery: "npm:^1.5.0" esutils: "npm:^2.0.2" fast-deep-equal: "npm:^3.1.3" @@ -3379,13 +3403,11 @@ __metadata: ignore: "npm:^5.2.0" imurmurhash: "npm:^0.1.4" is-glob: "npm:^4.0.0" - is-path-inside: "npm:^3.0.3" json-stable-stringify-without-jsonify: "npm:^1.0.1" lodash.merge: "npm:^4.6.2" minimatch: "npm:^3.1.2" natural-compare: "npm:^1.4.0" optionator: "npm:^0.9.3" - strip-ansi: "npm:^6.0.1" text-table: "npm:^0.2.0" peerDependencies: jiti: "*" @@ -3394,11 +3416,11 @@ __metadata: optional: true bin: eslint: bin/eslint.js - checksum: 10c0/fc9afc31155fef8c27fc4fd00669aeafa4b89ce5abfbf6f60e05482c03d7ff1d5e7546e416aa47bf0f28c9a56597a94663fd0264c2c42a1890f53cac49189f24 + checksum: 10c0/67cf6ea3ea28dcda7dd54aac33e2d4028eb36991d13defb0d2339c3eaa877d5dddd12cd4416ddc701a68bcde9e0bb9e65524c2e4e9914992c724f5b51e949dda languageName: node linkType: hard -"espree@npm:^10.0.1, espree@npm:^10.1.0": +"espree@npm:^10.0.1, espree@npm:^10.2.0": version: 10.2.0 resolution: "espree@npm:10.2.0" dependencies: @@ -4552,13 +4574,6 @@ __metadata: languageName: node linkType: hard -"is-path-inside@npm:^3.0.3": - version: 3.0.3 - resolution: "is-path-inside@npm:3.0.3" - checksum: 10c0/cf7d4ac35fb96bab6a1d2c3598fe5ebb29aafb52c0aaa482b5a3ed9d8ba3edc11631e3ec2637660c44b3ce0e61a08d54946e8af30dec0b60a7c27296c68ffd05 - languageName: node - linkType: hard - "is-plain-obj@npm:^1.0.0, is-plain-obj@npm:^1.1.0": version: 1.1.0 resolution: "is-plain-obj@npm:1.1.0" diff --git a/mock-api/rest_server.ts b/mock-api/rest_server.ts index ccd725973..52b720f84 100644 --- a/mock-api/rest_server.ts +++ b/mock-api/rest_server.ts @@ -4294,31 +4294,19 @@ function getDashboardEntityData(id: number) { else if (id == 10) device_data = emsesp_devicedata_10; else if (id == 99) device_data = emsesp_devicedata_99; - // filter device_data - // and only favorite items (bit 8 set), only for non-Custom Entities - // and replace id by striping off the 2-char mask + // filter device_data on + // - only add favorite (mask has bit 8 set) except for Custom Entities (type 99) let new_data = (device_data as any).data - .map(({ id, m, x, s, ...rest }) => ({ - ...rest, - id2: id - })) - .filter((item) => id === 99 || parseInt(item.id2.slice(0, 2), 16) & 0x08) + .filter((item) => id === 99 || parseInt(item.id.slice(0, 2), 16) & 0x08) .map((item, index) => ({ - id: id * 100 + index, // unique id - n: item.id2.slice(2), // name - v: item.v, // value - u: item.u, // uom - c: item.c, // command - l: item.l, // list - h: item.h // help + id: id * 100 + index, // mandatory unique id for table + dv: item // devicevalue })); - // TODO only and command if not marked as READONLY - return new_data; } -// Router starts here... +// Router routing starts here... router // EMS-ESP Settings .get(EMSESP_SETTINGS_ENDPOINT, () => settings) @@ -4360,8 +4348,10 @@ router []; let dashboard_object: { id?: number; n?: string; t?: number; nodes?: any[] } = {}; + let fake = false; - // let fake = true; // fakes no data + + fake = true; // for testing if (!fake) { // pick EMS devices from coredata @@ -4397,9 +4387,11 @@ router let sensor_data: any[] = []; sensor_data = emsesp_sensordata.ts.map((item, index) => ({ id: 980 + index, - n: item.n ? item.n : item.id, // name may not be set - v: item.t ? item.t : undefined, // can have no value - u: item.u + dv: { + id: ' ' + item.n, + v: item.t, // value is called t in ts (temperature) + u: item.u + } })); dashboard_object = { id: 98, @@ -4417,9 +4409,11 @@ router sensor_data = emsesp_sensordata.as.filter((item) => item.t !== 0); sensor_data = sensor_data.map((item, index) => ({ id: 970 + index, - n: item.n, - v: item.v, - u: item.u + dv: { + id: ' ' + item.n, + v: item.v, + u: item.u + } })); dashboard_object = { @@ -4432,9 +4426,24 @@ router if ((dashboard_object.nodes ?? []).length > 0) { dashboard_data.push(dashboard_object); } + } else { + // for testing + // single object + // const element = emsesp_coredata.devices[3]; // pick the 4th device + // const id = element.id; + // dashboard_object = { + // id: id, + // n: element.n, + // t: element.t, + // nodes: getDashboardEntityData(id) + // }; + // if ((dashboard_object.nodes ?? []).length > 0) { + // dashboard_data.push(dashboard_object); + // } } // console.log('dashboard_data: ', dashboard_data); + // return dashboard_data; // if not using msgpack return new Response(encoder.encode(dashboard_data), { headers }); // msgpack it }) @@ -4570,7 +4579,9 @@ router } // await delay(1000); // wait to show spinner - console.log('device value saved', content); + console.log( + 'Device Value updated. command:' + command + ' value:' + value + ' id:' + id + ); return status(200); })