alova update #6

This commit is contained in:
proddy
2023-06-11 20:31:23 +02:00
parent 5c6d704a48
commit 8e081ce04f
30 changed files with 191 additions and 160 deletions

View File

@@ -26,8 +26,8 @@
"@mui/material": "^5.13.4", "@mui/material": "^5.13.4",
"@table-library/react-table-library": "4.1.4", "@table-library/react-table-library": "4.1.4",
"@types/lodash-es": "^4.17.7", "@types/lodash-es": "^4.17.7",
"@types/node": "^20.2.5", "@types/node": "^20.3.0",
"@types/react": "^18.2.9", "@types/react": "^18.2.11",
"@types/react-dom": "^18.2.4", "@types/react-dom": "^18.2.4",
"@types/react-router-dom": "^5.3.3", "@types/react-router-dom": "^5.3.3",
"alova": "^2.6.0", "alova": "^2.6.0",
@@ -40,7 +40,7 @@
"react-dom": "latest", "react-dom": "latest",
"react-dropzone": "^14.2.3", "react-dropzone": "^14.2.3",
"react-icons": "^4.9.0", "react-icons": "^4.9.0",
"react-router-dom": "^6.12.0", "react-router-dom": "^6.12.1",
"react-toastify": "^9.1.3", "react-toastify": "^9.1.3",
"sockette": "^2.0.6", "sockette": "^2.0.6",
"typesafe-i18n": "^5.24.3", "typesafe-i18n": "^5.24.3",
@@ -64,7 +64,7 @@
"nodemon": "^2.0.22", "nodemon": "^2.0.22",
"npm-run-all": "^4.1.5", "npm-run-all": "^4.1.5",
"prettier": "^2.8.8", "prettier": "^2.8.8",
"rollup-plugin-visualizer": "^5.9.0", "rollup-plugin-visualizer": "^5.9.2",
"terser": "^5.17.7", "terser": "^5.17.7",
"vite": "^4.3.9", "vite": "^4.3.9",
"vite-plugin-svgr": "^3.2.0", "vite-plugin-svgr": "^3.2.0",

View File

@@ -1,11 +1,9 @@
import { AXIOS } from './endpoints'; import { AXIOS, alovaInstance } from './endpoints';
import type { AxiosPromise } from 'axios'; import type { AxiosPromise } from 'axios';
import type { APSettings, APStatus } from 'types'; import type { APSettings, APStatus } from 'types';
export function readAPStatus(): AxiosPromise<APStatus> { export const readAPStatus = () => alovaInstance.Get<APStatus>('/apStatus');
return AXIOS.get('/apStatus');
}
export function readAPSettings(): AxiosPromise<APSettings> { export function readAPSettings(): AxiosPromise<APSettings> {
return AXIOS.get('/apSettings'); return AXIOS.get('/apSettings');

View File

@@ -1,6 +1,5 @@
import { xhrRequestAdapter } from '@alova/adapter-xhr'; import { xhrRequestAdapter } from '@alova/adapter-xhr';
import { createAlova } from 'alova'; import { createAlova } from 'alova';
// import GlobalFetch from 'alova/GlobalFetch';
import ReactHook from 'alova/react'; import ReactHook from 'alova/react';
import axios from 'axios'; import axios from 'axios';
import { unpack } from '../api/unpack'; import { unpack } from '../api/unpack';
@@ -23,21 +22,21 @@ export const EVENT_SOURCE_ROOT = location.protocol + '//' + location.host + ES_B
export const alovaInstance = createAlova({ export const alovaInstance = createAlova({
baseURL: '/rest/', baseURL: '/rest/',
statesHook: ReactHook, statesHook: ReactHook,
timeout: 50000,
requestAdapter: xhrRequestAdapter(), requestAdapter: xhrRequestAdapter(),
// requestAdapter: GlobalFetch(),
beforeRequest(method) { beforeRequest(method) {
// TODO check if bearer works
if (localStorage.getItem(ACCESS_TOKEN)) { if (localStorage.getItem(ACCESS_TOKEN)) {
method.config.headers.token = 'Bearer ' + localStorage.getItem(ACCESS_TOKEN); method.config.headers.Authorization = 'Bearer ' + localStorage.getItem(ACCESS_TOKEN);
} }
}, },
responded: { responded: {
onSuccess: async (response) => { onSuccess: async (response) => {
if (response.status === 400) { if (response.status == 202) {
throw new Error('Reboot required');
} else if (response.status === 400) {
throw new Error('Invalid command'); throw new Error('Invalid command');
} } else if (response.status >= 400) {
if (response.status >= 400) {
throw new Error(response.statusText); throw new Error(response.statusText);
} }
const data = await response.data; const data = await response.data;

View File

@@ -3,6 +3,7 @@ import DeviceHubIcon from '@mui/icons-material/DeviceHub';
import RefreshIcon from '@mui/icons-material/Refresh'; import RefreshIcon from '@mui/icons-material/Refresh';
import SettingsInputAntennaIcon from '@mui/icons-material/SettingsInputAntenna'; import SettingsInputAntennaIcon from '@mui/icons-material/SettingsInputAntenna';
import { Avatar, Button, Divider, List, ListItem, ListItemAvatar, ListItemText, useTheme } from '@mui/material'; import { Avatar, Button, Divider, List, ListItem, ListItemAvatar, ListItemText, useTheme } from '@mui/material';
import { useRequest } from 'alova';
import type { Theme } from '@mui/material'; import type { Theme } from '@mui/material';
import type { FC } from 'react'; import type { FC } from 'react';
@@ -12,7 +13,6 @@ import { ButtonRow, FormLoader, SectionContent } from 'components';
import { useI18nContext } from 'i18n/i18n-react'; import { useI18nContext } from 'i18n/i18n-react';
import { APNetworkStatus } from 'types'; import { APNetworkStatus } from 'types';
import { useRest } from 'utils';
export const apStatusHighlight = ({ status }: APStatus, theme: Theme) => { export const apStatusHighlight = ({ status }: APStatus, theme: Theme) => {
switch (status) { switch (status) {
@@ -28,7 +28,8 @@ export const apStatusHighlight = ({ status }: APStatus, theme: Theme) => {
}; };
const APStatusForm: FC = () => { const APStatusForm: FC = () => {
const { loadData, data, errorMessage } = useRest<APStatus>({ read: APApi.readAPStatus }); // TODO missing update!
const { data: data, send: loadData, error } = useRequest(APApi.readAPStatus());
const { LL } = useI18nContext(); const { LL } = useI18nContext();
@@ -49,7 +50,7 @@ const APStatusForm: FC = () => {
const content = () => { const content = () => {
if (!data) { if (!data) {
return <FormLoader onRetry={loadData} errorMessage={errorMessage} />; return <FormLoader onRetry={loadData} errorMessage={error?.message} />;
} }
return ( return (

View File

@@ -39,6 +39,8 @@ export const mqttQueueHighlight = ({ mqtt_queued }: MqttStatus, theme: Theme) =>
}; };
const MqttStatusForm: FC = () => { const MqttStatusForm: FC = () => {
// TODO missing update!
const { loadData, data, errorMessage } = useRest<MqttStatus>({ read: MqttApi.readMqttStatus }); const { loadData, data, errorMessage } = useRest<MqttStatus>({ read: MqttApi.readMqttStatus });
const { LL } = useI18nContext(); const { LL } = useI18nContext();

View File

@@ -59,6 +59,8 @@ const IPs = (status: NetworkStatus) => {
}; };
const NetworkStatusForm: FC = () => { const NetworkStatusForm: FC = () => {
// TODO missing update!
const { loadData, data, errorMessage } = useRest<NetworkStatus>({ read: NetworkApi.readNetworkStatus }); const { loadData, data, errorMessage } = useRest<NetworkStatus>({ read: NetworkApi.readNetworkStatus });
const { LL } = useI18nContext(); const { LL } = useI18nContext();

View File

@@ -52,6 +52,8 @@ export const ntpStatusHighlight = ({ status }: NTPStatus, theme: Theme) => {
}; };
const NTPStatusForm: FC = () => { const NTPStatusForm: FC = () => {
// TODO missing update!
const { loadData, data, errorMessage } = useRest<NTPStatus>({ read: NTPApi.readNTPStatus }); const { loadData, data, errorMessage } = useRest<NTPStatus>({ read: NTPApi.readNTPStatus });
const [localTime, setLocalTime] = useState<string>(''); const [localTime, setLocalTime] = useState<string>('');
const [settingTime, setSettingTime] = useState<boolean>(false); const [settingTime, setSettingTime] = useState<boolean>(false);

View File

@@ -49,6 +49,8 @@ const levelLabel = (level: LogLevel) => {
const SystemLog: FC = () => { const SystemLog: FC = () => {
const { LL } = useI18nContext(); const { LL } = useI18nContext();
// TODO missing update!
const { loadData, data, setData, origData, dirtyFlags, blocker, setDirtyFlags, setOrigData } = useRest<LogSettings>({ const { loadData, data, setData, origData, dirtyFlags, blocker, setDirtyFlags, setOrigData } = useRest<LogSettings>({
read: SystemApi.readLogSettings read: SystemApi.readLogSettings
}); });

View File

@@ -53,6 +53,8 @@ const SystemStatusForm: FC = () => {
const { LL } = useI18nContext(); const { LL } = useI18nContext();
const [restarting, setRestarting] = useState<boolean>(); const [restarting, setRestarting] = useState<boolean>();
// TODO missing update!
const { loadData, data, errorMessage } = useRest<SystemStatus>({ read: SystemApi.readSystemStatus }); const { loadData, data, errorMessage } = useRest<SystemStatus>({ read: SystemApi.readSystemStatus });
const { me } = useContext(AuthenticatedContext); const { me } = useContext(AuthenticatedContext);

View File

@@ -51,7 +51,6 @@ const de: Translation = {
REMOVE: 'Entfernen', REMOVE: 'Entfernen',
PROBLEM_UPDATING: 'Problem beim Aktualisieren', PROBLEM_UPDATING: 'Problem beim Aktualisieren',
PROBLEM_LOADING: 'Problem beim Laden', PROBLEM_LOADING: 'Problem beim Laden',
HTTP_ERROR: 'Error {0}', // TODO translate
ANALOG_SENSOR: 'Analogsensor', ANALOG_SENSOR: 'Analogsensor',
ANALOG_SENSORS: 'Analogsensoren', ANALOG_SENSORS: 'Analogsensoren',
SETTINGS: 'Einstellungen', SETTINGS: 'Einstellungen',

View File

@@ -51,7 +51,6 @@ const en: Translation = {
REMOVE: 'Remove', REMOVE: 'Remove',
PROBLEM_UPDATING: 'Problem updating', PROBLEM_UPDATING: 'Problem updating',
PROBLEM_LOADING: 'Problem loading', PROBLEM_LOADING: 'Problem loading',
HTTP_ERROR: 'Error {0}',
ANALOG_SENSOR: 'Analog Sensor', ANALOG_SENSOR: 'Analog Sensor',
ANALOG_SENSORS: 'Analog Sensors', ANALOG_SENSORS: 'Analog Sensors',
SETTINGS: 'Settings', SETTINGS: 'Settings',

View File

@@ -51,7 +51,6 @@ const fr: Translation = {
REMOVE: 'Enlever', REMOVE: 'Enlever',
PROBLEM_UPDATING: 'Problème lors de la mise à jour', PROBLEM_UPDATING: 'Problème lors de la mise à jour',
PROBLEM_LOADING: 'Problème lors du chargement', PROBLEM_LOADING: 'Problème lors du chargement',
HTTP_ERROR: 'Error {0}', // TODO translate
ANALOG_SENSOR: 'Capteur analogique', ANALOG_SENSOR: 'Capteur analogique',
ANALOG_SENSORS: 'Capteurs analogiques', ANALOG_SENSORS: 'Capteurs analogiques',
SETTINGS: 'Paramètres', SETTINGS: 'Paramètres',

View File

@@ -51,7 +51,6 @@ const nl: Translation = {
REMOVE: 'Verwijderen', REMOVE: 'Verwijderen',
PROBLEM_UPDATING: 'Probleem met updaten', PROBLEM_UPDATING: 'Probleem met updaten',
PROBLEM_LOADING: 'Probleem met laden', PROBLEM_LOADING: 'Probleem met laden',
HTTP_ERROR: 'Error {0}', // TODO translate
ANALOG_SENSOR: 'Analoge sensor', ANALOG_SENSOR: 'Analoge sensor',
ANALOG_SENSORS: 'Analoge Sensoren', ANALOG_SENSORS: 'Analoge Sensoren',
SETTINGS: 'Instellingen', SETTINGS: 'Instellingen',

View File

@@ -51,7 +51,6 @@ const no: Translation = {
REMOVE: 'Fjern', REMOVE: 'Fjern',
PROBLEM_UPDATING: 'Problem med oppdatering', PROBLEM_UPDATING: 'Problem med oppdatering',
PROBLEM_LOADING: 'Problem med opplasting', PROBLEM_LOADING: 'Problem med opplasting',
HTTP_ERROR: 'Error {0}', // TODO translate
ANALOG_SENSOR: 'Analog Sensor', ANALOG_SENSOR: 'Analog Sensor',
ANALOG_SENSORS: 'Analoge Sensorer', ANALOG_SENSORS: 'Analoge Sensorer',
SETTINGS: 'Innstillinger', SETTINGS: 'Innstillinger',

View File

@@ -51,7 +51,6 @@ const pl: BaseTranslation = {
REMOVE: 'Usuń', REMOVE: 'Usuń',
PROBLEM_UPDATING: 'Problem z uaktualnieniem!', PROBLEM_UPDATING: 'Problem z uaktualnieniem!',
PROBLEM_LOADING: 'Problem z załadowaniem!', PROBLEM_LOADING: 'Problem z załadowaniem!',
HTTP_ERROR: 'Error {0}', // TODO translate
ANALOG_SENSOR: '{{u|u||ustawienia u|ustawień u}}rządzeni{{a podłączonego do EMS-ESP|e||a podłączonego do EMS-ESP|a podłączonego do EMS-ESP}}', ANALOG_SENSOR: '{{u|u||ustawienia u|ustawień u}}rządzeni{{a podłączonego do EMS-ESP|e||a podłączonego do EMS-ESP|a podłączonego do EMS-ESP}}',
ANALOG_SENSORS: 'Urządzenia podłączone do EMS-ESP', ANALOG_SENSORS: 'Urządzenia podłączone do EMS-ESP',
SETTINGS: 'ustawienia', SETTINGS: 'ustawienia',

View File

@@ -51,7 +51,6 @@ const sv: Translation = {
REMOVE: 'Ta bort', REMOVE: 'Ta bort',
PROBLEM_UPDATING: 'Problem vid uppdatering', PROBLEM_UPDATING: 'Problem vid uppdatering',
PROBLEM_LOADING: 'Problem vid hämtning', PROBLEM_LOADING: 'Problem vid hämtning',
HTTP_ERROR: 'Error {0}', // TODO translate
ANALOG_SENSOR: 'Analog Sensor', ANALOG_SENSOR: 'Analog Sensor',
ANALOG_SENSORS: 'Analoga Sensorer', ANALOG_SENSORS: 'Analoga Sensorer',
SETTINGS: 'Inställningar', SETTINGS: 'Inställningar',

View File

@@ -51,7 +51,6 @@ const tr: Translation = {
REMOVE: 'Kaldır', REMOVE: 'Kaldır',
PROBLEM_UPDATING: 'Güncelleme Sorunu', PROBLEM_UPDATING: 'Güncelleme Sorunu',
PROBLEM_LOADING: 'Yükleme Sorunu', PROBLEM_LOADING: 'Yükleme Sorunu',
HTTP_ERROR: 'Error {0}', // TODO translate
ANALOG_SENSOR: 'Analog Sensör', ANALOG_SENSOR: 'Analog Sensör',
ANALOG_SENSORS: 'Analog Sensörler', ANALOG_SENSORS: 'Analog Sensörler',
SETTINGS: 'Ayarlar', SETTINGS: 'Ayarlar',

View File

@@ -69,7 +69,7 @@ const DashboardDevices: FC = () => {
immediate: true immediate: true
}); });
const { data: deviceData, send: readDeviceData } = useRequest((id) => EMSESP.readDeviceData(id), { const { data: deviceData, send: readDeviceData } = useRequest((data) => EMSESP.readDeviceData(data), {
initialData: { initialData: {
data: [] data: []
}, },
@@ -77,12 +77,9 @@ const DashboardDevices: FC = () => {
immediate: false immediate: false
}); });
const { loading: submitting, send: writeDeviceValue } = useRequest( const { loading: submitting, send: writeDeviceValue } = useRequest((data) => EMSESP.writeDeviceValue(data), {
(id: number, deviceValue: DeviceValue) => EMSESP.writeDeviceValue(id, deviceValue), immediate: false
{ });
immediate: false
}
);
useLayoutEffect(() => { useLayoutEffect(() => {
function updateSize() { function updateSize() {
@@ -350,7 +347,7 @@ const DashboardDevices: FC = () => {
toast.success(LL.WRITE_CMD_SENT()); toast.success(LL.WRITE_CMD_SENT());
}) })
.catch((error) => { .catch((error) => {
toast.error(LL.HTTP_ERROR(error)); toast.error(error.message);
}) })
.finally(async () => { .finally(async () => {
setDeviceValueDialogOpen(false); setDeviceValueDialogOpen(false);

View File

@@ -64,6 +64,8 @@ const showQuality = (stat: Stat) => {
}; };
const DashboardStatus: FC = () => { const DashboardStatus: FC = () => {
// TODO missing update!
const { loadData, data, errorMessage } = useRest<Status>({ read: EMSESP.readStatus }); const { loadData, data, errorMessage } = useRest<Status>({ read: EMSESP.readStatus });
const { LL } = useI18nContext(); const { LL } = useI18nContext();

View File

@@ -23,7 +23,7 @@ import {
import RestartMonitor from 'framework/system/RestartMonitor'; import RestartMonitor from 'framework/system/RestartMonitor';
import { useI18nContext } from 'i18n/i18n-react'; import { useI18nContext } from 'i18n/i18n-react';
import { numberValue, extractErrorMessage, updateValueDirty, useRest } from 'utils'; import { numberValue, extractErrorMessage, updateValueDirty, useRest2 } from 'utils';
import { validate } from 'validators'; import { validate } from 'validators';
export function boardProfileSelectItems() { export function boardProfileSelectItems() {
@@ -47,7 +47,7 @@ const SettingsApplication: FC = () => {
blocker, blocker,
errorMessage, errorMessage,
restartNeeded restartNeeded
} = useRest<Settings>({ } = useRest2<Settings>({
read: EMSESP.readSettings, read: EMSESP.readSettings,
update: EMSESP.writeSettings update: EMSESP.writeSettings
}); });
@@ -60,6 +60,12 @@ const SettingsApplication: FC = () => {
const [fieldErrors, setFieldErrors] = useState<ValidateFieldsError>(); const [fieldErrors, setFieldErrors] = useState<ValidateFieldsError>();
const [processingBoard, setProcessingBoard] = useState<boolean>(false); const [processingBoard, setProcessingBoard] = useState<boolean>(false);
// TODO remove - just for testing loaddata
// useEffect(() => {
// void loadData();
// }, []);
// TODO replace with Alova!
const updateBoardProfile = async (boardProfile: string) => { const updateBoardProfile = async (boardProfile: string) => {
setProcessingBoard(true); setProcessingBoard(true);
try { try {

View File

@@ -14,8 +14,7 @@ import type {
WriteAnalogSensor, WriteAnalogSensor,
SensorData, SensorData,
Schedule, Schedule,
Entities, Entities
DeviceValue
} from './types'; } from './types';
import type { AxiosPromise } from 'axios'; import type { AxiosPromise } from 'axios';
import { AXIOS, AXIOS_API, AXIOS_BIN, alovaInstance } from 'api/endpoints'; import { AXIOS, AXIOS_API, AXIOS_BIN, alovaInstance } from 'api/endpoints';
@@ -28,8 +27,11 @@ export const readDeviceData = (id: number) =>
responseType: 'arraybuffer' // uses msgpack responseType: 'arraybuffer' // uses msgpack
}); });
export const writeDeviceValue = (id: number, devicevalue: DeviceValue) => export const writeDeviceValue = (data: any) => alovaInstance.Post('/writeDeviceValue', data);
alovaInstance.Post('/writeDeviceValue', { id, devicevalue });
// SettingsApplication
export const readSettings = () => alovaInstance.Get<Settings>('/settings');
export const writeSettings = (data: any) => alovaInstance.Post('/settings', data);
// //
// TODO change below to use alova // TODO change below to use alova
@@ -39,14 +41,6 @@ export function restart(): AxiosPromise<void> {
return AXIOS.post('/restart'); return AXIOS.post('/restart');
} }
export function readSettings(): AxiosPromise<Settings> {
return AXIOS.get('/settings');
}
export function writeSettings(settings: Settings): AxiosPromise<Settings> {
return AXIOS.post('/settings', settings); // call command
}
// TODO change to GET // TODO change to GET
export function getBoardProfile(boardProfile: BoardProfileName): AxiosPromise<BoardProfile> { export function getBoardProfile(boardProfile: BoardProfileName): AxiosPromise<BoardProfile> {
return AXIOS.post('/boardProfile', boardProfile); return AXIOS.post('/boardProfile', boardProfile);

View File

@@ -280,13 +280,6 @@ export interface APIcall {
entity: string; entity: string;
id: any; id: any;
} }
// TODO can be removed?
export interface WriteDeviceValue {
id: number;
devicevalue: DeviceValue;
}
export interface WriteAnalogSensor { export interface WriteAnalogSensor {
id: number; id: number;
gpio: number; gpio: number;

View File

@@ -1,5 +1,3 @@
type UpdateEntity<S> = (state: (prevState: Readonly<S>) => S) => void;
export const numberValue = (value: number) => (isNaN(value) ? '' : value.toString()); export const numberValue = (value: number) => (isNaN(value) ? '' : value.toString());
export const extractEventValue = (event: React.ChangeEvent<HTMLInputElement>) => { export const extractEventValue = (event: React.ChangeEvent<HTMLInputElement>) => {
@@ -13,6 +11,8 @@ export const extractEventValue = (event: React.ChangeEvent<HTMLInputElement>) =>
} }
}; };
type UpdateEntity<S> = (state: (prevState: Readonly<S>) => S) => void;
export const updateValue = export const updateValue =
<S>(updateEntity: UpdateEntity<S>) => <S>(updateEntity: UpdateEntity<S>) =>
(event: React.ChangeEvent<HTMLInputElement>) => { (event: React.ChangeEvent<HTMLInputElement>) => {
@@ -23,10 +23,13 @@ export const updateValue =
}; };
export const updateValueDirty = export const updateValueDirty =
<S>(origData: any, dirtyFlags: any, setDirtyFlags: any, updateEntity: UpdateEntity<S>) => <S>(origData: any, dirtyFlags: any, setDirtyFlags: any, updateEntity: any) =>
// <S>(origData: any, dirtyFlags: any, setDirtyFlags: any, updateEntity: UpdateEntity<S>) =>
(event: React.ChangeEvent<HTMLInputElement>) => { (event: React.ChangeEvent<HTMLInputElement>) => {
const updated_value = extractEventValue(event); const updated_value = extractEventValue(event);
const name = event.target.name; const name = event.target.name;
// TODO not sure how this is even working!!
updateEntity((prevState) => ({ updateEntity((prevState) => ({
...prevState, ...prevState,
[name]: updated_value [name]: updated_value
@@ -34,6 +37,11 @@ export const updateValueDirty =
const arr: string[] = dirtyFlags; const arr: string[] = dirtyFlags;
// TODO remove comments
// console.log('updating ' + name + ' to ' + updated_value); // TODO remove
// console.log('dirtyFlags:', dirtyFlags); // TODO remove
// console.log('binding.ts origData:', origData); // TODO remove
if (origData[name] !== updated_value) { if (origData[name] !== updated_value) {
if (!arr.includes(name)) { if (!arr.includes(name)) {
arr.push(name); arr.push(name);

View File

@@ -4,4 +4,6 @@ export * from './route';
export * from './submit'; export * from './submit';
export * from './time'; export * from './time';
export * from './useRest'; export * from './useRest';
// TODO remove
export * from './useRest2';
export * from './props'; export * from './props';

View File

@@ -0,0 +1,92 @@
import { useRequest, type Method } from 'alova';
import { useState } from 'react';
import { unstable_useBlocker as useBlocker } from 'react-router-dom';
import { toast } from 'react-toastify';
import type { AlovaXHRRequestConfig, AlovaXHRResponse, AlovaXHRResponseHeaders } from '@alova/adapter-xhr';
import { useI18nContext } from 'i18n/i18n-react';
export interface RestRequestOptions<D> {
read: () => Method<any, any, any, any, any, AlovaXHRResponse<any>, AlovaXHRResponseHeaders>;
update: (
value: D
) => Method<any, unknown, unknown, unknown, AlovaXHRRequestConfig, AlovaXHRResponse<any>, AlovaXHRResponseHeaders>;
}
export const useRest2 = <D>({ read, update }: RestRequestOptions<D>) => {
const { LL } = useI18nContext();
const [errorMessage, setErrorMessage] = useState<string>();
const [restartNeeded, setRestartNeeded] = useState<boolean>(false);
const [origData, setOrigData] = useState<D>();
const [dirtyFlags, setDirtyFlags] = useState<string[]>([]);
const blocker = useBlocker(dirtyFlags.length !== 0);
const { data: data, send: readData, update: updateData, onComplete: onReadComplete } = useRequest(read());
const {
loading: saving,
send: writeData,
onSuccess: onWriteSuccess
} = useRequest((newData: D) => update(newData), { immediate: false });
const setData = (new_data: D) => {
console.log('SET DATA'); // TODO remove console
console.log('new_data:', new_data); // TODO remove console
updateData({
data: new_data
});
};
onWriteSuccess(() => {
toast.success(LL.UPDATED_OF(LL.SETTINGS()));
setDirtyFlags([]);
});
onReadComplete((event) => {
setOrigData(event.data); // make a copy
});
const loadData = async () => {
setDirtyFlags([]);
setErrorMessage(undefined);
await readData().catch((error) => {
toast.error(error.message);
setErrorMessage(error.message);
});
};
const saveData = async () => {
if (!data) {
return;
}
console.log('SAVE DATA'); // TODO remove console
setRestartNeeded(false);
setErrorMessage(undefined);
await writeData(data).catch((error) => {
if (error.message === 'Reboot required') {
setRestartNeeded(true);
} else {
toast.error(error.message);
setErrorMessage(error.message);
}
});
};
return {
loadData,
saveData,
saving,
setData,
data,
origData,
dirtyFlags,
setDirtyFlags,
setOrigData,
blocker,
errorMessage,
restartNeeded
} as const;
};

View File

@@ -1288,10 +1288,10 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@types/node@npm:^20.2.5": "@types/node@npm:^20.3.0":
version: 20.2.5 version: 20.3.0
resolution: "@types/node@npm:20.2.5" resolution: "@types/node@npm:20.3.0"
checksum: 1c3db8a4ceb5e5d12e7cb140e37c14a16ce013084c6d65579b91cefbe0ecaca57d85093d968172b11c3d1d95bcbc5d972b08aa3dc3935206fb39bc6c10751102 checksum: f717d92c29c4877db394b604771b3734216f013312f93252f72c2018aabe8083be905fbcf0644c859938c8183b6e0245faaeaab94c9e78268b87a449bc6ef4aa
languageName: node languageName: node
linkType: hard linkType: hard
@@ -1368,14 +1368,14 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@types/react@npm:^18.2.9": "@types/react@npm:^18.2.11":
version: 18.2.9 version: 18.2.11
resolution: "@types/react@npm:18.2.9" resolution: "@types/react@npm:18.2.11"
dependencies: dependencies:
"@types/prop-types": "*" "@types/prop-types": "*"
"@types/scheduler": "*" "@types/scheduler": "*"
csstype: ^3.0.2 csstype: ^3.0.2
checksum: 5935a8cb8efc2804dbb0f85989970f654fb4117274b6d2836baa58d5e9dbfc09fd9d60e1f23a8d0fca70ad75e9f0e074790947899be40525cbe62f7ac58adcc6 checksum: 9360d7be13195050eb16598796056123ee9d30470e7073a914300fed9282585d0dd0638bb5ff65843e308c3ac213d25b3388e8186f3134490c758f18f11f3bd8
languageName: node languageName: node
linkType: hard linkType: hard
@@ -1536,8 +1536,8 @@ __metadata:
"@mui/material": ^5.13.4 "@mui/material": ^5.13.4
"@table-library/react-table-library": 4.1.4 "@table-library/react-table-library": 4.1.4
"@types/lodash-es": ^4.17.7 "@types/lodash-es": ^4.17.7
"@types/node": ^20.2.5 "@types/node": ^20.3.0
"@types/react": ^18.2.9 "@types/react": ^18.2.11
"@types/react-dom": ^18.2.4 "@types/react-dom": ^18.2.4
"@types/react-router-dom": ^5.3.3 "@types/react-router-dom": ^5.3.3
"@typescript-eslint/eslint-plugin": ^5.59.9 "@typescript-eslint/eslint-plugin": ^5.59.9
@@ -1567,9 +1567,9 @@ __metadata:
react-dom: latest react-dom: latest
react-dropzone: ^14.2.3 react-dropzone: ^14.2.3
react-icons: ^4.9.0 react-icons: ^4.9.0
react-router-dom: ^6.12.0 react-router-dom: ^6.12.1
react-toastify: ^9.1.3 react-toastify: ^9.1.3
rollup-plugin-visualizer: ^5.9.0 rollup-plugin-visualizer: ^5.9.2
sockette: ^2.0.6 sockette: ^2.0.6
terser: ^5.17.7 terser: ^5.17.7
typesafe-i18n: ^5.24.3 typesafe-i18n: ^5.24.3
@@ -4807,27 +4807,27 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"react-router-dom@npm:^6.12.0": "react-router-dom@npm:^6.12.1":
version: 6.12.0 version: 6.12.1
resolution: "react-router-dom@npm:6.12.0" resolution: "react-router-dom@npm:6.12.1"
dependencies: dependencies:
"@remix-run/router": 1.6.3 "@remix-run/router": 1.6.3
react-router: 6.12.0 react-router: 6.12.1
peerDependencies: peerDependencies:
react: ">=16.8" react: ">=16.8"
react-dom: ">=16.8" react-dom: ">=16.8"
checksum: 910b23ae3555e7baff97038c478b8fa65d5b54856c74e789c529850c002850a961329b692a8f1f4643100f46075918d2b55cbf3c7f87f85588387148010600f5 checksum: 4de0d4159a9dd2de0477d7608e9055262ebdd5dc41fc918b44b38170cc8ed407fa7dbb73bdb85e9469614502ad4772523b8a7f32c2609e62973feb41b70d871b
languageName: node languageName: node
linkType: hard linkType: hard
"react-router@npm:6.12.0": "react-router@npm:6.12.1":
version: 6.12.0 version: 6.12.1
resolution: "react-router@npm:6.12.0" resolution: "react-router@npm:6.12.1"
dependencies: dependencies:
"@remix-run/router": 1.6.3 "@remix-run/router": 1.6.3
peerDependencies: peerDependencies:
react: ">=16.8" react: ">=16.8"
checksum: b7cee04f5edbf48f1aa0303107514a3a9dc2b6f94a7a753fbf4eb8dde1d3eb67940204ff0b8b907c5b09f69c716dd958a6187346b1c35ccf19162d74e77d99af checksum: 33a39eca122f3519f1aa91d8e6585fab669b3b06b621aa624f7db1ffda6b84676facd57028652f2fba7325a208abef906a5ba6c91d55d5a5d29993583d82dd61
languageName: node languageName: node
linkType: hard linkType: hard
@@ -5030,9 +5030,9 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"rollup-plugin-visualizer@npm:^5.9.0": "rollup-plugin-visualizer@npm:^5.9.2":
version: 5.9.0 version: 5.9.2
resolution: "rollup-plugin-visualizer@npm:5.9.0" resolution: "rollup-plugin-visualizer@npm:5.9.2"
dependencies: dependencies:
open: ^8.4.0 open: ^8.4.0
picomatch: ^2.3.1 picomatch: ^2.3.1
@@ -5045,7 +5045,7 @@ __metadata:
optional: true optional: true
bin: bin:
rollup-plugin-visualizer: dist/bin/cli.js rollup-plugin-visualizer: dist/bin/cli.js
checksum: dc706e09c78124b2e05b58779c757e488c76e679b92795f8538f8efffa61845339d75a7f767d5a9eeb5d7e4fd76a835dd93ebf17315bf8a0a0e5fc85a4f59ab5 checksum: e6280bed797084e9f9ee06726e46706e32854fc7647c5c5bcec68a9be8ca8e6fbf7f8a0b2f76255e9df72e84135a7d57eb2662b6cfd68496a1ef60fa33597eaf
languageName: node languageName: node
linkType: hard linkType: hard

View File

@@ -2128,8 +2128,8 @@ rest_server.get(EMSESP_SETTINGS_ENDPOINT, (req, res) => {
rest_server.post(EMSESP_SETTINGS_ENDPOINT, (req, res) => { rest_server.post(EMSESP_SETTINGS_ENDPOINT, (req, res) => {
settings = req.body; settings = req.body;
console.log('Write settings: ' + JSON.stringify(settings)); console.log('Write settings: ' + JSON.stringify(settings));
res.status(202).json(settings); // restart needed // res.status(202).json(settings); // restart needed
// res.status(200).json(settings); // no restart needed res.status(200).json(settings); // no restart needed
}); });
rest_server.get(EMSESP_CORE_DATA_ENDPOINT, (req, res) => { rest_server.get(EMSESP_CORE_DATA_ENDPOINT, (req, res) => {
console.log('send back core data...'); console.log('send back core data...');
@@ -2337,6 +2337,7 @@ rest_server.post(EMSESP_WRITE_ENTITIES_ENDPOINT, (req, res) => {
}); });
rest_server.post(EMSESP_WRITE_VALUE_ENDPOINT, async (req, res) => { rest_server.post(EMSESP_WRITE_VALUE_ENDPOINT, async (req, res) => {
console.log(req.body);
const devicevalue = req.body.devicevalue; const devicevalue = req.body.devicevalue;
const id = req.body.id; const id = req.body.id;
console.log('Write device value for id : ' + id); console.log('Write device value for id : ' + id);

View File

@@ -1 +1 @@
#define EMSESP_APP_VERSION "3.6.0-dev.12" #define EMSESP_APP_VERSION "3.6.0-dev.13"

View File

@@ -23,20 +23,17 @@ namespace emsesp {
using namespace std::placeholders; // for `_1` etc using namespace std::placeholders; // for `_1` etc
WebDataService::WebDataService(AsyncWebServer * server, SecurityManager * securityManager) WebDataService::WebDataService(AsyncWebServer * server, SecurityManager * securityManager)
: _device_data_handler(DEVICE_DATA_SERVICE_PATH, : _write_value_handler(WRITE_DEVICE_VALUE_SERVICE_PATH,
securityManager->wrapCallback(std::bind(&WebDataService::device_data, this, _1, _2), AuthenticationPredicates::IS_AUTHENTICATED))
, _write_value_handler(WRITE_DEVICE_VALUE_SERVICE_PATH,
securityManager->wrapCallback(std::bind(&WebDataService::write_device_value, this, _1, _2), AuthenticationPredicates::IS_ADMIN)) securityManager->wrapCallback(std::bind(&WebDataService::write_device_value, this, _1, _2), AuthenticationPredicates::IS_ADMIN))
, _write_temperature_handler(WRITE_TEMPERATURE_SENSOR_SERVICE_PATH, , _write_temperature_handler(WRITE_TEMPERATURE_SENSOR_SERVICE_PATH,
securityManager->wrapCallback(std::bind(&WebDataService::write_temperature_sensor, this, _1, _2), securityManager->wrapCallback(std::bind(&WebDataService::write_temperature_sensor, this, _1, _2),
AuthenticationPredicates::IS_ADMIN)) AuthenticationPredicates::IS_ADMIN))
, _write_analog_handler(WRITE_ANALOG_SENSOR_SERVICE_PATH, , _write_analog_handler(WRITE_ANALOG_SENSOR_SERVICE_PATH,
securityManager->wrapCallback(std::bind(&WebDataService::write_analog_sensor, this, _1, _2), AuthenticationPredicates::IS_ADMIN)) { securityManager->wrapCallback(std::bind(&WebDataService::write_analog_sensor, this, _1, _2), AuthenticationPredicates::IS_ADMIN)) {
// TODO Get for /deviceData // GET's
server->on(DEVICE_DATA_SERVICE_PATH, server->on(DEVICE_DATA_SERVICE_PATH,
HTTP_GET, HTTP_GET,
securityManager->wrapRequest(std::bind(&WebDataService::device_data2, this, _1), AuthenticationPredicates::IS_AUTHENTICATED)); securityManager->wrapRequest(std::bind(&WebDataService::device_data, this, _1), AuthenticationPredicates::IS_AUTHENTICATED));
server->on(CORE_DATA_SERVICE_PATH, server->on(CORE_DATA_SERVICE_PATH,
HTTP_GET, HTTP_GET,
@@ -46,14 +43,11 @@ WebDataService::WebDataService(AsyncWebServer * server, SecurityManager * securi
HTTP_GET, HTTP_GET,
securityManager->wrapRequest(std::bind(&WebDataService::sensor_data, this, _1), AuthenticationPredicates::IS_AUTHENTICATED)); securityManager->wrapRequest(std::bind(&WebDataService::sensor_data, this, _1), AuthenticationPredicates::IS_AUTHENTICATED));
// POST's
server->on(SCAN_DEVICES_SERVICE_PATH, server->on(SCAN_DEVICES_SERVICE_PATH,
HTTP_POST, HTTP_POST,
securityManager->wrapRequest(std::bind(&WebDataService::scan_devices, this, _1), AuthenticationPredicates::IS_ADMIN)); securityManager->wrapRequest(std::bind(&WebDataService::scan_devices, this, _1), AuthenticationPredicates::IS_ADMIN));
_device_data_handler.setMethod(HTTP_POST);
_device_data_handler.setMaxContentLength(256);
server->addHandler(&_device_data_handler);
_write_value_handler.setMethod(HTTP_POST); _write_value_handler.setMethod(HTTP_POST);
_write_value_handler.setMaxContentLength(256); _write_value_handler.setMaxContentLength(256);
server->addHandler(&_write_value_handler); server->addHandler(&_write_value_handler);
@@ -175,11 +169,10 @@ void WebDataService::sensor_data(AsyncWebServerRequest * request) {
// The unique_id is the unique record ID from the Web table to identify which device to load // The unique_id is the unique record ID from the Web table to identify which device to load
// Compresses the JSON using MsgPack https://msgpack.org/index.html // Compresses the JSON using MsgPack https://msgpack.org/index.html
void WebDataService::device_data2(AsyncWebServerRequest * request) { void WebDataService::device_data(AsyncWebServerRequest * request) {
uint8_t id; uint8_t id;
if (request->hasParam(F_(id))) { if (request->hasParam(F_(id))) {
// TODO get id id = Helpers::atoint(request->getParam(F_(id))->value().c_str()); // get id from url
id = Helpers::atoint(request->getParam(F_(id))->value().c_str());
size_t buffer = EMSESP_JSON_SIZE_XXXXLARGE; size_t buffer = EMSESP_JSON_SIZE_XXXXLARGE;
auto * response = new MsgpackAsyncJsonResponse(false, buffer); auto * response = new MsgpackAsyncJsonResponse(false, buffer);
@@ -230,56 +223,6 @@ void WebDataService::device_data2(AsyncWebServerRequest * request) {
request->send(response); request->send(response);
} }
// The unique_id is the unique record ID from the Web table to identify which device to load
// Compresses the JSON using MsgPack https://msgpack.org/index.html
void WebDataService::device_data(AsyncWebServerRequest * request, JsonVariant & json) {
if (json.is<JsonObject>()) {
size_t buffer = EMSESP_JSON_SIZE_XXXXLARGE;
auto * response = new MsgpackAsyncJsonResponse(false, buffer);
while (!response->getSize()) {
delete response;
buffer -= 1024;
response = new MsgpackAsyncJsonResponse(false, buffer);
}
for (const auto & emsdevice : EMSESP::emsdevices) {
if (emsdevice->unique_id() == json["id"]) {
// wait max 2.5 sec for updated data (post_send_delay is 2 sec)
for (uint16_t i = 0; i < (emsesp::TxService::POST_SEND_DELAY + 500) && EMSESP::wait_validate(); i++) {
delay(1);
}
EMSESP::wait_validate(0); // reset in case of timeout
#ifndef EMSESP_STANDALONE
JsonObject output = response->getRoot();
emsdevice->generate_values_web(output);
#endif
#if defined(EMSESP_DEBUG)
size_t length = response->setLength();
EMSESP::logger().debug("Dashboard buffer used: %d", length);
#else
response->setLength();
#endif
request->send(response);
return;
}
}
#ifndef EMSESP_STANDALONE
if (json["id"] == 99) {
JsonObject output = response->getRoot();
EMSESP::webEntityService.generate_value_web(output);
response->setLength();
request->send(response);
return;
}
#endif
}
// invalid but send ok
AsyncWebServerResponse * response = request->beginResponse(200);
request->send(response);
}
// takes a command and its data value from a specific EMS Device, from the Web
// assumes the service has been checked for admin authentication // assumes the service has been checked for admin authentication
void WebDataService::write_device_value(AsyncWebServerRequest * request, JsonVariant & json) { void WebDataService::write_device_value(AsyncWebServerRequest * request, JsonVariant & json) {
if (json.is<JsonObject>()) { if (json.is<JsonObject>()) {

View File

@@ -21,7 +21,6 @@
// GET // GET
#define CORE_DATA_SERVICE_PATH "/rest/coreData" #define CORE_DATA_SERVICE_PATH "/rest/coreData"
#define SCAN_DEVICES_SERVICE_PATH "/rest/scanDevices"
#define DEVICE_DATA_SERVICE_PATH "/rest/deviceData" #define DEVICE_DATA_SERVICE_PATH "/rest/deviceData"
#define SENSOR_DATA_SERVICE_PATH "/rest/sensorData" #define SENSOR_DATA_SERVICE_PATH "/rest/sensorData"
@@ -29,6 +28,7 @@
#define WRITE_DEVICE_VALUE_SERVICE_PATH "/rest/writeDeviceValue" #define WRITE_DEVICE_VALUE_SERVICE_PATH "/rest/writeDeviceValue"
#define WRITE_TEMPERATURE_SENSOR_SERVICE_PATH "/rest/writeTemperatureSensor" #define WRITE_TEMPERATURE_SENSOR_SERVICE_PATH "/rest/writeTemperatureSensor"
#define WRITE_ANALOG_SENSOR_SERVICE_PATH "/rest/writeAnalogSensor" #define WRITE_ANALOG_SENSOR_SERVICE_PATH "/rest/writeAnalogSensor"
#define SCAN_DEVICES_SERVICE_PATH "/rest/scanDevices"
namespace emsesp { namespace emsesp {
@@ -44,22 +44,15 @@ class WebDataService {
// GET // GET
void core_data(AsyncWebServerRequest * request); void core_data(AsyncWebServerRequest * request);
void sensor_data(AsyncWebServerRequest * request); void sensor_data(AsyncWebServerRequest * request);
void device_data(AsyncWebServerRequest * request);
// TODO make it a Get
void device_data2(AsyncWebServerRequest * request);
// POST // POST
// TODO probably can be removed
void device_data(AsyncWebServerRequest * request, JsonVariant & json);
void write_device_value(AsyncWebServerRequest * request, JsonVariant & json); void write_device_value(AsyncWebServerRequest * request, JsonVariant & json);
void write_temperature_sensor(AsyncWebServerRequest * request, JsonVariant & json); void write_temperature_sensor(AsyncWebServerRequest * request, JsonVariant & json);
void write_analog_sensor(AsyncWebServerRequest * request, JsonVariant & json); void write_analog_sensor(AsyncWebServerRequest * request, JsonVariant & json);
void scan_devices(AsyncWebServerRequest * request); void scan_devices(AsyncWebServerRequest * request); // command
AsyncCallbackJsonWebHandler _device_data_handler, _write_value_handler, _write_temperature_handler, _write_analog_handler; AsyncCallbackJsonWebHandler _write_value_handler, _write_temperature_handler, _write_analog_handler;
}; };
} // namespace emsesp } // namespace emsesp