alova - implementing UpdateState

This commit is contained in:
proddy
2023-06-14 23:30:52 +02:00
parent c44903e1b0
commit adc4760b5f
17 changed files with 202 additions and 237 deletions

View File

@@ -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.1", "react-router-dom": "^6.13.0",
"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",

View File

@@ -5,11 +5,10 @@ import type { APSettings, APStatus } from 'types';
export const readAPStatus = () => alovaInstance.Get<APStatus>('/apStatus'); export const readAPStatus = () => alovaInstance.Get<APStatus>('/apStatus');
// TODO change AXIOS to Alova // TODO change APSettings AXIOS to Alova
export function readAPSettings(): AxiosPromise<APSettings> { export function readAPSettings(): AxiosPromise<APSettings> {
return AXIOS.get('/apSettings'); return AXIOS.get('/apSettings');
} }
export function updateAPSettings(apSettings: APSettings): AxiosPromise<APSettings> { export function updateAPSettings(apSettings: APSettings): AxiosPromise<APSettings> {
return AXIOS.post('/apSettings', apSettings); return AXIOS.post('/apSettings', apSettings);
} }

View File

@@ -6,9 +6,6 @@ import { unpack } from '../api/unpack';
import type { AxiosPromise, CancelToken, AxiosProgressEvent } from 'axios'; import type { AxiosPromise, CancelToken, AxiosProgressEvent } from 'axios';
export const WS_BASE_URL = '/ws/';
export const ES_BASE_URL = '/es/';
export const REST_BASE_URL = '/rest/'; export const REST_BASE_URL = '/rest/';
export const API_BASE_URL = '/api/'; export const API_BASE_URL = '/api/';
@@ -16,13 +13,14 @@ export const ACCESS_TOKEN = 'access_token';
const location = window.location; const location = window.location;
const webProtocol = location.protocol === 'https:' ? 'wss:' : 'ws:'; const webProtocol = location.protocol === 'https:' ? 'wss:' : 'ws:';
export const WS_BASE_URL = '/ws/';
export const ES_BASE_URL = '/es/';
export const WEB_SOCKET_ROOT = webProtocol + '//' + location.host + WS_BASE_URL; export const WEB_SOCKET_ROOT = webProtocol + '//' + location.host + WS_BASE_URL;
export const EVENT_SOURCE_ROOT = location.protocol + '//' + location.host + ES_BASE_URL; export const EVENT_SOURCE_ROOT = location.protocol + '//' + location.host + ES_BASE_URL;
export const alovaInstance = createAlova({ export const alovaInstance = createAlova({
baseURL: '/rest/',
statesHook: ReactHook, statesHook: ReactHook,
timeout: 50000, timeout: 5000,
requestAdapter: xhrRequestAdapter(), requestAdapter: xhrRequestAdapter(),
beforeRequest(method) { beforeRequest(method) {
if (localStorage.getItem(ACCESS_TOKEN)) { if (localStorage.getItem(ACCESS_TOKEN)) {

View File

@@ -28,7 +28,7 @@ export const apStatusHighlight = ({ status }: APStatus, theme: Theme) => {
}; };
const APStatusForm: FC = () => { const APStatusForm: FC = () => {
const { data: data, send: loadData, error } = useRequest(APApi.readAPStatus()); const { data: data, send: loadData, error } = useRequest(APApi.readAPStatus);
const { LL } = useI18nContext(); const { LL } = useI18nContext();

View File

@@ -1,5 +1,6 @@
import DownloadIcon from '@mui/icons-material/GetApp'; import DownloadIcon from '@mui/icons-material/GetApp';
import { Typography, Button, Box } from '@mui/material'; import { Typography, Button, Box } from '@mui/material';
import { useRequest } from 'alova';
import { toast } from 'react-toastify'; import { toast } from 'react-toastify';
import type { FileUploadConfig } from 'api/endpoints'; import type { FileUploadConfig } from 'api/endpoints';
import type { AxiosPromise } from 'axios'; import type { AxiosPromise } from 'axios';
@@ -9,7 +10,6 @@ import { SingleUpload, useFileUpload } from 'components';
import { useI18nContext } from 'i18n/i18n-react'; import { useI18nContext } from 'i18n/i18n-react';
import * as EMSESP from 'project/api'; import * as EMSESP from 'project/api';
import { extractErrorMessage } from 'utils';
interface UploadFileProps { interface UploadFileProps {
uploadGeneralFile: (file: File, config?: FileUploadConfig) => AxiosPromise<void>; uploadGeneralFile: (file: File, config?: FileUploadConfig) => AxiosPromise<void>;
@@ -18,6 +18,19 @@ interface UploadFileProps {
const GeneralFileUpload: FC<UploadFileProps> = ({ uploadGeneralFile }) => { const GeneralFileUpload: FC<UploadFileProps> = ({ uploadGeneralFile }) => {
const [uploadFile, cancelUpload, uploading, uploadProgress, md5] = useFileUpload({ upload: uploadGeneralFile }); const [uploadFile, cancelUpload, uploading, uploadProgress, md5] = useFileUpload({ upload: uploadGeneralFile });
const { send: getSettings, onSuccess: onSuccessGetSettings } = useRequest(EMSESP.getSettings(), {
immediate: false
});
const { send: getCustomizations, onSuccess: onSuccessgetCustomizations } = useRequest(EMSESP.getCustomizations(), {
immediate: false
});
const { send: getEntities, onSuccess: onSuccessGetEntities } = useRequest(EMSESP.getEntities(), {
immediate: false
});
const { send: getSchedule, onSuccess: onSuccessGetSchedule } = useRequest(EMSESP.getSchedule(), {
immediate: false
});
const { LL } = useI18nContext(); const { LL } = useI18nContext();
const saveFile = (json: any, endpoint: string) => { const saveFile = (json: any, endpoint: string) => {
@@ -35,56 +48,41 @@ const GeneralFileUpload: FC<UploadFileProps> = ({ uploadGeneralFile }) => {
toast.info(LL.DOWNLOAD_SUCCESSFUL()); toast.info(LL.DOWNLOAD_SUCCESSFUL());
}; };
onSuccessGetSettings((event) => {
saveFile(event.data, 'settings');
});
onSuccessgetCustomizations((event) => {
saveFile(event.data, 'customizations');
});
onSuccessGetEntities((event) => {
saveFile(event.data, 'entities');
});
onSuccessGetSchedule((event) => {
saveFile(event.data, 'schedule');
});
const downloadSettings = async () => { const downloadSettings = async () => {
try { await getSettings().catch((error) => {
const response = await EMSESP.getSettings(); toast.error(error.message);
if (response.status !== 200) { });
toast.error(LL.PROBLEM_LOADING());
} else {
saveFile(response.data, 'settings');
}
} catch (error) {
toast.error(extractErrorMessage(error, LL.PROBLEM_LOADING()));
}
}; };
const downloadCustomizations = async () => { const downloadCustomizations = async () => {
try { await getCustomizations().catch((error) => {
const response = await EMSESP.getCustomizations(); toast.error(error.message);
if (response.status !== 200) { });
toast.error(LL.PROBLEM_LOADING());
} else {
saveFile(response.data, 'customizations');
}
} catch (error) {
toast.error(extractErrorMessage(error, LL.PROBLEM_LOADING()));
}
}; };
const downloadEntities = async () => { const downloadEntities = async () => {
try { await getEntities().catch((error) => {
const response = await EMSESP.getEntities(); toast.error(error.message);
if (response.status !== 200) { });
toast.error(LL.PROBLEM_LOADING());
} else {
saveFile(response.data, 'entities');
}
} catch (error) {
toast.error(extractErrorMessage(error, LL.PROBLEM_LOADING()));
}
}; };
const downloadSchedule = async () => { const downloadSchedule = async () => {
try { await getSchedule().catch((error) => {
const response = await EMSESP.getSchedule(); toast.error(error.message);
if (response.status !== 200) { });
toast.error(LL.PROBLEM_LOADING());
} else {
saveFile(response.data, 'schedule');
}
} catch (error) {
toast.error(extractErrorMessage(error, LL.PROBLEM_LOADING()));
}
}; };
return ( return (

View File

@@ -170,15 +170,6 @@ const DashboardStatus: FC = () => {
toast.error(err.message); toast.error(err.message);
}); });
setConfirmScan(false); setConfirmScan(false);
// try {
// await EMSESP.scanDevices();
// toast.info(LL.SCANNING() + '...');
// } catch (error) {
// toast.error(extractErrorMessage(error, LL.PROBLEM_UPDATING()));
// } finally {
// setConfirmScan(false);
// }
}; };
const renderScanDialog = () => ( const renderScanDialog = () => (

View File

@@ -4,6 +4,7 @@ import DownloadIcon from '@mui/icons-material/GetApp';
import GitHubIcon from '@mui/icons-material/GitHub'; import GitHubIcon from '@mui/icons-material/GitHub';
import MenuBookIcon from '@mui/icons-material/MenuBookTwoTone'; import MenuBookIcon from '@mui/icons-material/MenuBookTwoTone';
import { Typography, Button, Box, List, ListItem, ListItemText, Link, ListItemAvatar } from '@mui/material'; import { Typography, Button, Box, List, ListItem, ListItemText, Link, ListItemAvatar } from '@mui/material';
import { useRequest } from 'alova';
import { toast } from 'react-toastify'; import { toast } from 'react-toastify';
import * as EMSESP from './api'; import * as EMSESP from './api';
import type { FC } from 'react'; import type { FC } from 'react';
@@ -11,16 +12,19 @@ import type { FC } from 'react';
import { SectionContent } from 'components'; import { SectionContent } from 'components';
import { useI18nContext } from 'i18n/i18n-react'; import { useI18nContext } from 'i18n/i18n-react';
import { extractErrorMessage } from 'utils';
const HelpInformation: FC = () => { const HelpInformation: FC = () => {
const { LL } = useI18nContext(); const { LL } = useI18nContext();
const saveFile = (json: any, endpoint: string) => { const { send: API, onSuccess: onSuccessAPI } = useRequest((data) => EMSESP.API(data), {
immediate: false
});
onSuccessAPI((event) => {
const a = document.createElement('a'); const a = document.createElement('a');
const filename = 'emsesp_' + endpoint + '.txt'; const filename = 'emsesp_info.txt';
a.href = URL.createObjectURL( a.href = URL.createObjectURL(
new Blob([JSON.stringify(json, null, 2)], { new Blob([JSON.stringify(event.data, null, 2)], {
type: 'text/plain' type: 'text/plain'
}) })
); );
@@ -29,23 +33,12 @@ const HelpInformation: FC = () => {
a.click(); a.click();
document.body.removeChild(a); document.body.removeChild(a);
toast.info(LL.DOWNLOAD_SUCCESSFUL()); toast.info(LL.DOWNLOAD_SUCCESSFUL());
};
const callAPI = async (endpoint: string) => {
try {
const response = await EMSESP.API({
device: 'system',
entity: endpoint,
id: 0
}); });
if (response.status !== 200) {
toast.error(LL.PROBLEM_LOADING()); const callAPI = async () => {
} else { await API({ device: 'system', entity: 'info', id: 0 }).catch((error) => {
saveFile(response.data, endpoint); toast.error(error.message);
} });
} catch (error) {
toast.error(extractErrorMessage(error, LL.PROBLEM_LOADING()));
}
}; };
return ( return (
@@ -96,7 +89,7 @@ const HelpInformation: FC = () => {
size="small" size="small"
variant="outlined" variant="outlined"
color="primary" color="primary"
onClick={() => callAPI('info')} onClick={() => callAPI()}
> >
{LL.SUPPORT_INFO()} {LL.SUPPORT_INFO()}
</Button> </Button>

View File

@@ -61,7 +61,7 @@ const SettingsCustomization: FC = () => {
immediate: false immediate: false
}); });
const { data: devices } = useRequest(EMSESP.readDevices()); const { data: devices } = useRequest(EMSESP.readDevices);
const { send: writeCustomEntities } = useRequest((data) => EMSESP.writeCustomEntities(data), { immediate: false }); const { send: writeCustomEntities } = useRequest((data) => EMSESP.writeCustomEntities(data), { immediate: false });
@@ -236,8 +236,9 @@ const SettingsCustomization: FC = () => {
}; };
const maskDisabled = (set: boolean) => { const maskDisabled = (set: boolean) => {
// TODO fix update!
updateDeviceEntities( updateDeviceEntities(
deviceEntities?.map(function (de) { deviceEntities.map(function (de) {
if ((de.m & selectedFilters || !selectedFilters) && de.id.toLowerCase().includes(search.toLowerCase())) { if ((de.m & selectedFilters || !selectedFilters) && de.id.toLowerCase().includes(search.toLowerCase())) {
return { return {
...de, ...de,
@@ -329,7 +330,6 @@ const SettingsCustomization: FC = () => {
} }
} }
); );
// does this work or use onSuccess hook?
setOriginalSettings(deviceEntities); setOriginalSettings(deviceEntities);
} }
}; };

View File

@@ -124,7 +124,7 @@ const SettingsCustomizationDialog = ({ open, onClose, onSave, selectedItem }: Se
{LL.CANCEL()} {LL.CANCEL()}
</Button> </Button>
<Button startIcon={<DoneIcon />} variant="outlined" onClick={save} color="primary"> <Button startIcon={<DoneIcon />} variant="outlined" onClick={save} color="primary">
{LL.UPDATE(0)} {LL.UPDATE()}
</Button> </Button>
</DialogActions> </DialogActions>
</Dialog> </Dialog>

View File

@@ -6,6 +6,7 @@ import WarningIcon from '@mui/icons-material/Warning';
import { Box, Typography, Divider, Stack, Button } from '@mui/material'; import { Box, Typography, Divider, Stack, Button } from '@mui/material';
import { Table, Header, HeaderRow, HeaderCell, Body, Row, Cell } from '@table-library/react-table-library/table'; import { Table, Header, HeaderRow, HeaderCell, Body, Row, Cell } from '@table-library/react-table-library/table';
import { useTheme } from '@table-library/react-table-library/theme'; import { useTheme } from '@table-library/react-table-library/theme';
import { updateState, useRequest } from 'alova';
import { useState, useEffect, useCallback } from 'react'; import { useState, useEffect, useCallback } from 'react';
import { unstable_useBlocker as useBlocker } from 'react-router-dom'; import { unstable_useBlocker as useBlocker } from 'react-router-dom';
import { toast } from 'react-toastify'; import { toast } from 'react-toastify';
@@ -19,23 +20,30 @@ import type { FC } from 'react';
import { ButtonRow, FormLoader, SectionContent, BlockNavigation } from 'components'; import { ButtonRow, FormLoader, SectionContent, BlockNavigation } from 'components';
import { useI18nContext } from 'i18n/i18n-react'; import { useI18nContext } from 'i18n/i18n-react';
import { extractErrorMessage } from 'utils';
const SettingsScheduler: FC = () => { const SettingsScheduler: FC = () => {
const { LL, locale } = useI18nContext(); const { LL, locale } = useI18nContext();
const [numChanges, setNumChanges] = useState<number>(0); const [numChanges, setNumChanges] = useState<number>(0);
const blocker = useBlocker(numChanges !== 0); const blocker = useBlocker(numChanges !== 0);
const [schedule, setSchedule] = useState<ScheduleItem[]>([]);
const [selectedScheduleItem, setSelectedScheduleItem] = useState<ScheduleItem>(); const [selectedScheduleItem, setSelectedScheduleItem] = useState<ScheduleItem>();
const [dow, setDow] = useState<string[]>([]); const [dow, setDow] = useState<string[]>([]);
const [errorMessage, setErrorMessage] = useState<string>();
const [creating, setCreating] = useState<boolean>(false); const [creating, setCreating] = useState<boolean>(false);
const [dialogOpen, setDialogOpen] = useState<boolean>(false); const [dialogOpen, setDialogOpen] = useState<boolean>(false);
const {
data: schedule,
send: fetchSchedule,
error
} = useRequest(EMSESP.readSchedule, {
initialData: []
});
const { send: writeSchedule } = useRequest((data) => EMSESP.writeSchedule(data), { immediate: false });
function hasScheduleChanged(si: ScheduleItem) { function hasScheduleChanged(si: ScheduleItem) {
return ( return (
si.id !== si.o_id || si.id !== si.o_id ||
(si?.name || '') !== (si?.o_name || '') || (si.name || '') !== (si.o_name || '') ||
si.active !== si.o_active || si.active !== si.o_active ||
si.deleted !== si.o_deleted || si.deleted !== si.o_deleted ||
si.flags !== si.o_flags || si.flags !== si.o_flags ||
@@ -46,10 +54,13 @@ const SettingsScheduler: FC = () => {
} }
useEffect(() => { useEffect(() => {
if (schedule) { const formatter = new Intl.DateTimeFormat(locale, { weekday: 'short', timeZone: 'UTC' });
setNumChanges(schedule ? schedule.filter((si) => hasScheduleChanged(si)).length : 0); const days = [1, 2, 3, 4, 5, 6, 7].map((day) => {
} const dd = day < 10 ? `0${day}` : day;
}, [schedule]); return new Date(`2017-01-${dd}T00:00:00+00:00`);
});
setDow(days.map((date) => formatter.format(date)));
}, [locale]);
const schedule_theme = useTheme({ const schedule_theme = useTheme({
Table: ` Table: `
@@ -96,42 +107,9 @@ const SettingsScheduler: FC = () => {
` `
}); });
const fetchSchedule = useCallback(async () => {
try {
const response = await EMSESP.readSchedule();
setSchedule(
response.data.schedule.map((si) => ({
...si,
o_id: si.id,
o_active: si.active,
o_deleted: si.deleted,
o_flags: si.flags,
o_time: si.time,
o_cmd: si.cmd,
o_value: si.value,
o_name: si.name
}))
);
} catch (error) {
setErrorMessage(extractErrorMessage(error, LL.PROBLEM_LOADING()));
}
}, [LL]);
useEffect(() => {
const formatter = new Intl.DateTimeFormat(locale, { weekday: 'short', timeZone: 'UTC' });
const days = [1, 2, 3, 4, 5, 6, 7].map((day) => {
const dd = day < 10 ? `0${day}` : day;
return new Date(`2017-01-${dd}T00:00:00+00:00`);
});
setDow(days.map((date) => formatter.format(date)));
void fetchSchedule();
}, [locale, fetchSchedule]);
const saveSchedule = async () => { const saveSchedule = async () => {
if (schedule) { await writeSchedule(
try { schedule
const response = await EMSESP.writeSchedule({
schedule: schedule
.filter((si) => !si.deleted) .filter((si) => !si.deleted)
.map((condensed_si) => ({ .map((condensed_si) => ({
id: condensed_si.id, id: condensed_si.id,
@@ -142,17 +120,17 @@ const SettingsScheduler: FC = () => {
value: condensed_si.value, value: condensed_si.value,
name: condensed_si.name name: condensed_si.name
})) }))
}); )
if (response.status === 200) { .then(() => {
toast.success(LL.SCHEDULE_UPDATED()); toast.success(LL.SCHEDULE_UPDATED());
} else { })
toast.error(LL.PROBLEM_UPDATING()); .catch((err) => {
} toast.error(err.message);
})
.finally(async () => {
await fetchSchedule(); await fetchSchedule();
} catch (error) { setNumChanges(0);
toast.error(extractErrorMessage(error, LL.PROBLEM_UPDATING())); });
}
}
}; };
const editScheduleItem = useCallback((si: ScheduleItem) => { const editScheduleItem = useCallback((si: ScheduleItem) => {
@@ -167,11 +145,14 @@ const SettingsScheduler: FC = () => {
const onDialogSave = (updatedItem: ScheduleItem) => { const onDialogSave = (updatedItem: ScheduleItem) => {
setDialogOpen(false); setDialogOpen(false);
if (schedule && creating) {
setSchedule([...schedule.filter((si) => creating || si.o_id !== updatedItem.o_id), updatedItem]); updateState('schedule', (data) => {
} else { const new_data = creating
setSchedule(schedule?.map((si) => (si.id === updatedItem.id ? { ...si, ...updatedItem } : si))); ? [...data.filter((si) => creating || si.o_id !== updatedItem.o_id), updatedItem]
} : data.map((si) => (si.id === updatedItem.id ? { ...si, ...updatedItem } : si));
setNumChanges(new_data.filter((si) => hasScheduleChanged(si)).length);
return new_data;
});
}; };
const addScheduleItem = () => { const addScheduleItem = () => {
@@ -191,7 +172,7 @@ const SettingsScheduler: FC = () => {
const renderSchedule = () => { const renderSchedule = () => {
if (!schedule) { if (!schedule) {
return <FormLoader errorMessage={errorMessage} />; return <FormLoader onRetry={fetchSchedule} errorMessage={error?.message} />;
} }
const dayBox = (si: ScheduleItem, flag: number) => ( const dayBox = (si: ScheduleItem, flag: number) => (

View File

@@ -215,7 +215,7 @@ const SettingsSchedulerDialog = ({
/> />
<TextField <TextField
name="value" name="value"
label={LL.VALUE(0)} label={LL.VALUE(1)}
multiline multiline
margin="normal" margin="normal"
fullWidth fullWidth

View File

@@ -8,89 +8,88 @@ import type {
WriteTemperatureSensor, WriteTemperatureSensor,
WriteAnalogSensor, WriteAnalogSensor,
SensorData, SensorData,
Schedule,
Entities, Entities,
DeviceData DeviceData,
ScheduleItem
} from './types'; } from './types';
import type { AxiosPromise } from 'axios'; import type { AxiosPromise } from 'axios';
import { AXIOS, AXIOS_API, alovaInstance } from 'api/endpoints'; import { AXIOS, alovaInstance } from 'api/endpoints';
// DashboardDevices // DashboardDevices
export const readCoreData = () => alovaInstance.Get<CoreData>(`/coreData`); export const readCoreData = () => alovaInstance.Get<CoreData>(`/rest/coreData`);
export const readDeviceData = (id: number) => export const readDeviceData = (id: number) =>
alovaInstance.Get<DeviceData>('/deviceData', { alovaInstance.Get<DeviceData>('/rest/deviceData', {
params: { id }, params: { id },
responseType: 'arraybuffer' // uses msgpack responseType: 'arraybuffer' // uses msgpack
}); });
export const writeDeviceValue = (data: any) => alovaInstance.Post('/writeDeviceValue', data); export const writeDeviceValue = (data: any) => alovaInstance.Post('/rest/writeDeviceValue', data);
// SettingsApplication // SettingsApplication
export const readSettings = () => alovaInstance.Get<Settings>('/settings'); export const readSettings = () => alovaInstance.Get<Settings>('/rest/settings');
export const writeSettings = (data: any) => alovaInstance.Post('/settings', data); export const writeSettings = (data: any) => alovaInstance.Post('/rest/settings', data);
export const getBoardProfile = (boardProfile: string) => export const getBoardProfile = (boardProfile: string) =>
alovaInstance.Get('/boardProfile', { alovaInstance.Get('/rest/boardProfile', {
params: { boardProfile } params: { boardProfile }
}); });
export const restart = () => alovaInstance.Post('/restart'); export const restart = () => alovaInstance.Post('/rest/restart');
// SettingsCustomization // SettingsCustomization
export const readDeviceEntities = (id: number) => export const readDeviceEntities = (id: number) =>
alovaInstance.Get<DeviceEntity[]>('/deviceEntities', { alovaInstance.Get<DeviceEntity[]>('/rest/deviceEntities', {
params: { id }, params: { id },
responseType: 'arraybuffer', responseType: 'arraybuffer',
transformData(rawData: any) { transformData(data: any) {
return rawData.map((de: DeviceEntity) => ({ ...de, o_m: de.m, o_cn: de.cn, o_mi: de.mi, o_ma: de.ma })); return data.map((de: DeviceEntity) => ({ ...de, o_m: de.m, o_cn: de.cn, o_mi: de.mi, o_ma: de.ma }));
} }
}); });
export const readDevices = () => alovaInstance.Get<Devices>('/devices'); export const readDevices = () => alovaInstance.Get<Devices>('/rest/devices');
export const resetCustomizations = () => alovaInstance.Post('/resetCustomizations'); export const resetCustomizations = () => alovaInstance.Post('/rest/resetCustomizations');
export const writeCustomEntities = (data: any) => alovaInstance.Post('/customEntities', data); export const writeCustomEntities = (data: any) => alovaInstance.Post('/rest/customEntities', data);
// DashboardSensors // DashboardSensors
export const readSensorData = () => alovaInstance.Get<SensorData>('/sensorData'); export const readSensorData = () => alovaInstance.Get<SensorData>('/rest/sensorData');
export const writeTemperatureSensor = (ts: WriteTemperatureSensor) => alovaInstance.Post('/writeTemperatureSensor', ts); export const writeTemperatureSensor = (ts: WriteTemperatureSensor) =>
export const writeAnalogSensor = (as: WriteAnalogSensor) => alovaInstance.Post('/writeAnalogSensor', as); alovaInstance.Post('/rest/writeTemperatureSensor', ts);
export const writeAnalogSensor = (as: WriteAnalogSensor) => alovaInstance.Post('/rest/writeAnalogSensor', as);
// TODO think about naming, get... and not get etc...
// DashboardStatus // DashboardStatus
export const readStatus = () => alovaInstance.Get<Status>('/status'); export const readStatus = () => alovaInstance.Get<Status>('/rest/status');
export const scanDevices = () => alovaInstance.Post('/scanDevices'); export const scanDevices = () => alovaInstance.Post('/rest/scanDevices');
// ALOVA goes here.... // HelpInformation
export const API = (apiCall: APIcall) => alovaInstance.Post('/api', apiCall);
export function API(apiCall: APIcall): AxiosPromise<void> { // GeneralFileUpload
return AXIOS_API.post('/', apiCall); // command export const getSettings = () => alovaInstance.Get('/rest/getSettings');
} export const getCustomizations = () => alovaInstance.Get('/rest/getCustomizations');
export const getEntities = () => alovaInstance.Get<Entities>('/rest/getEntities');
export const getSchedule = () => alovaInstance.Get('/rest/getSchedule');
export function getSettings(): AxiosPromise<void> { // SettingsScheduler
return AXIOS.get('/getSettings'); export const readSchedule = () =>
} alovaInstance.Get<ScheduleItem[]>('/rest/schedule', {
name: 'schedule',
export function getCustomizations(): AxiosPromise<void> { transformData(data: any) {
return AXIOS.get('/getCustomizations'); return data.map((si: ScheduleItem) => ({
} ...si,
o_id: si.id,
export function getSchedule(): AxiosPromise<Schedule> { o_active: si.active,
return AXIOS.get('/getSchedule'); o_deleted: si.deleted,
} o_flags: si.flags,
o_time: si.time,
export function readSchedule(): AxiosPromise<Schedule> { o_cmd: si.cmd,
return AXIOS.get('/schedule'); o_value: si.value,
} o_name: si.name
}));
export function writeSchedule(schedule: Schedule): AxiosPromise<void> { }
return AXIOS.post('/schedule', schedule); });
} export const writeSchedule = (data: any) => alovaInstance.Post('/rest/schedule', data);
export function getEntities(): AxiosPromise<Entities> {
return AXIOS.get('/getEntities');
}
// SettingsCustomization
// TODO move last Entities* to Alova
export function readEntities(): AxiosPromise<Entities> { export function readEntities(): AxiosPromise<Entities> {
return AXIOS.get('/entities'); return AXIOS.get('/entities');
} }
export function writeEntities(entities: Entities): AxiosPromise<void> { export function writeEntities(entities: Entities): AxiosPromise<void> {
return AXIOS.post('/entities', entities); return AXIOS.post('/entities', entities);
} }

View File

@@ -292,7 +292,7 @@ export interface ScheduleItem {
time: string; time: string;
cmd: string; cmd: string;
value: string; value: string;
name?: string; // optional name: string; // optional
o_id?: number; o_id?: number;
o_active?: boolean; o_active?: boolean;
o_deleted?: boolean; o_deleted?: boolean;
@@ -303,10 +303,6 @@ export interface ScheduleItem {
o_name?: string; o_name?: string;
} }
export interface Schedule {
schedule: ScheduleItem[];
}
export enum ScheduleFlag { export enum ScheduleFlag {
SCHEDULE_SUN = 1, SCHEDULE_SUN = 1,
SCHEDULE_MON = 2, SCHEDULE_MON = 2,

View File

@@ -90,6 +90,8 @@ export const schedulerItemValidation = () =>
new Schema({ new Schema({
name: [ name: [
{ {
// TODO name must be unique - add check
required: true,
type: 'string', type: 'string',
pattern: /^[a-zA-Z0-9_\\.]{0,15}$/, pattern: /^[a-zA-Z0-9_\\.]{0,15}$/,
message: "Must be <15 characters: alpha numeric, '_' or '.'" message: "Must be <15 characters: alpha numeric, '_' or '.'"

View File

@@ -3,15 +3,11 @@ import { useState } from 'react';
import { unstable_useBlocker as useBlocker } from 'react-router-dom'; import { unstable_useBlocker as useBlocker } from 'react-router-dom';
import { toast } from 'react-toastify'; import { toast } from 'react-toastify';
import type { AlovaXHRRequestConfig, AlovaXHRResponse, AlovaXHRResponseHeaders } from '@alova/adapter-xhr';
import { useI18nContext } from 'i18n/i18n-react'; import { useI18nContext } from 'i18n/i18n-react';
export interface RestRequestOptions2<D> { export interface RestRequestOptions2<D> {
read: () => Method<any, any, any, any, any, AlovaXHRResponse<any>, AlovaXHRResponseHeaders>; read: () => Method<any, any, any, any, any, any, any>;
update: ( update: (value: D) => Method<any, any, any, any, any, any, any>;
value: D
) => Method<any, unknown, unknown, unknown, AlovaXHRRequestConfig, AlovaXHRResponse<any>, AlovaXHRResponseHeaders>;
} }
// TODO rename back to useRest // TODO rename back to useRest

View File

@@ -1567,7 +1567,7 @@ __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.1 react-router-dom: ^6.13.0
react-toastify: ^9.1.3 react-toastify: ^9.1.3
rollup-plugin-visualizer: ^5.9.2 rollup-plugin-visualizer: ^5.9.2
sockette: ^2.0.6 sockette: ^2.0.6
@@ -4807,27 +4807,27 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"react-router-dom@npm:^6.12.1": "react-router-dom@npm:^6.13.0":
version: 6.12.1 version: 6.13.0
resolution: "react-router-dom@npm:6.12.1" resolution: "react-router-dom@npm:6.13.0"
dependencies: dependencies:
"@remix-run/router": 1.6.3 "@remix-run/router": 1.6.3
react-router: 6.12.1 react-router: 6.13.0
peerDependencies: peerDependencies:
react: ">=16.8" react: ">=16.8"
react-dom: ">=16.8" react-dom: ">=16.8"
checksum: 4de0d4159a9dd2de0477d7608e9055262ebdd5dc41fc918b44b38170cc8ed407fa7dbb73bdb85e9469614502ad4772523b8a7f32c2609e62973feb41b70d871b checksum: 760c57063e305b0623f7b39bca3c7f5c169af557867eac7d1b19419111f7b6a9ed95d2083305c40ce98fd3be27e82c61f5674c90dd066a67653cb15901d00991
languageName: node languageName: node
linkType: hard linkType: hard
"react-router@npm:6.12.1": "react-router@npm:6.13.0":
version: 6.12.1 version: 6.13.0
resolution: "react-router@npm:6.12.1" resolution: "react-router@npm:6.13.0"
dependencies: dependencies:
"@remix-run/router": 1.6.3 "@remix-run/router": 1.6.3
peerDependencies: peerDependencies:
react: ">=16.8" react: ">=16.8"
checksum: 33a39eca122f3519f1aa91d8e6585fab669b3b06b621aa624f7db1ffda6b84676facd57028652f2fba7325a208abef906a5ba6c91d55d5a5d29993583d82dd61 checksum: c58b4b943d3a76d328c6ef299567a85d84a69b417f5f5d07596b0b9bd40632a828591d6f470b0f233ff9095f489f9fd5c8666fb03513e9470927447169a91d8a
languageName: node languageName: node
linkType: hard linkType: hard

View File

@@ -1798,8 +1798,9 @@ let emsesp_entities = {
}; };
// SCHEDULE // SCHEDULE
let emsesp_schedule = { let emsesp_schedule =
schedule: [ // schedule: [
[
{ {
id: 1, id: 1,
active: true, active: true,
@@ -1836,11 +1837,9 @@ let emsesp_schedule = {
value: '', value: '',
name: 'auto_restart' name: 'auto_restart'
} }
] ];
};
// CUSTOMIZATIONS // CUSTOMIZATIONS
const emsesp_deviceentities_1 = [{}]; const emsesp_deviceentities_1 = [{}];
const emsesp_deviceentities_3 = [{}]; const emsesp_deviceentities_3 = [{}];
const emsesp_deviceentities_5 = [{}]; const emsesp_deviceentities_5 = [{}];
@@ -2327,7 +2326,7 @@ rest_server.post(EMSESP_CUSTOM_ENTITIES_ENDPOINT, (req, res) => {
rest_server.post(EMSESP_WRITE_SCHEDULE_ENDPOINT, (req, res) => { rest_server.post(EMSESP_WRITE_SCHEDULE_ENDPOINT, (req, res) => {
console.log('write schedule'); console.log('write schedule');
console.log(req.body.schedule); console.log(req.body);
emsesp_schedule = req.body; emsesp_schedule = req.body;
res.sendStatus(200); res.sendStatus(200);
}); });
@@ -2625,6 +2624,7 @@ rest_server.post(API_ENDPOINT_ROOT, (req, res) => {
if (req.body.device === 'system') { if (req.body.device === 'system') {
if (req.body.entity === 'info') { if (req.body.entity === 'info') {
console.log('sending system info: ' + JSON.stringify(emsesp_info)); console.log('sending system info: ' + JSON.stringify(emsesp_info));
res.json(emsesp_info);
} else if (req.body.entity === 'settings') { } else if (req.body.entity === 'settings') {
console.log('sending system settings: ' + JSON.stringify(settings)); console.log('sending system settings: ' + JSON.stringify(settings));
res.json(settings); res.json(settings);
@@ -2652,25 +2652,37 @@ rest_server.get(SYSTEM_INFO_ENDPOINT, (req, res) => {
const GET_SETTINGS_ENDPOINT = REST_ENDPOINT_ROOT + 'getSettings'; const GET_SETTINGS_ENDPOINT = REST_ENDPOINT_ROOT + 'getSettings';
rest_server.get(GET_SETTINGS_ENDPOINT, (req, res) => { rest_server.get(GET_SETTINGS_ENDPOINT, (req, res) => {
console.log('System Settings:'); console.log('getSettings:');
res.json(settings); res.json(settings);
}); });
const GET_CUSTOMIZATIONS_ENDPOINT = REST_ENDPOINT_ROOT + 'getCustomizations'; const GET_CUSTOMIZATIONS_ENDPOINT = REST_ENDPOINT_ROOT + 'getCustomizations';
rest_server.get(GET_CUSTOMIZATIONS_ENDPOINT, (req, res) => { rest_server.get(GET_CUSTOMIZATIONS_ENDPOINT, (req, res) => {
console.log('Customization'); console.log('getCustomization');
// not implemented yet // not implemented yet
res.sendStatus(200); res.sendStatus(200);
}); });
const GET_SCHEDULE_ENDPOINT = REST_ENDPOINT_ROOT + 'schedule'; const GET_ENTITIES_ENDPOINT = REST_ENDPOINT_ROOT + 'getEntities';
rest_server.get(GET_ENTITIES_ENDPOINT, (req, res) => {
console.log('getEntities');
res.json(emsesp_entities);
});
const GET_SCHEDULE_ENDPOINT = REST_ENDPOINT_ROOT + 'getSchedule';
rest_server.get(GET_SCHEDULE_ENDPOINT, (req, res) => { rest_server.get(GET_SCHEDULE_ENDPOINT, (req, res) => {
console.log('getSchedule');
res.json(emsesp_schedule);
});
const SCHEDULE_ENDPOINT = REST_ENDPOINT_ROOT + 'schedule';
rest_server.get(SCHEDULE_ENDPOINT, (req, res) => {
console.log('Sending Schedule data'); console.log('Sending Schedule data');
res.json(emsesp_schedule); res.json(emsesp_schedule);
}); });
const GET_ENTITIES_ENDPOINT = REST_ENDPOINT_ROOT + 'entities'; const ENTITIES_ENDPOINT = REST_ENDPOINT_ROOT + 'entities';
rest_server.get(GET_ENTITIES_ENDPOINT, (req, res) => { rest_server.get(ENTITIES_ENDPOINT, (req, res) => {
console.log('Sending Entities data'); console.log('Sending Entities data');
res.json(emsesp_entities); res.json(emsesp_entities);
}); });