import { useCallback, useMemo, useState } from 'react';
import { toast } from 'react-toastify';
import CancelIcon from '@mui/icons-material/Cancel';
import PowerSettingsNewIcon from '@mui/icons-material/PowerSettingsNew';
import WarningIcon from '@mui/icons-material/Warning';
import {
Box,
Button,
Checkbox,
Divider,
Grid,
InputAdornment,
MenuItem,
TextField,
Typography
} from '@mui/material';
import { readSystemStatus } from 'api/system';
import { useRequest } from 'alova/client';
import SystemMonitor from 'app/status/SystemMonitor';
import type { ValidateFieldsError } from 'async-validator';
import {
BlockFormControlLabel,
BlockNavigation,
ButtonRow,
FormLoader,
MessageBox,
SectionContent,
ValidatedTextField,
useLayoutTitle
} from 'components';
import { useI18nContext } from 'i18n/i18n-react';
import { numberValue, updateValueDirty, useRest } from 'utils';
import { validate } from 'validators';
import { API, getBoardProfile, readSettings, writeSettings } from '../../api/app';
import { BOARD_PROFILES } from '../main/types';
import type { APIcall, Settings } from '../main/types';
import { createSettingsValidator } from '../main/validators';
export function boardProfileSelectItems() {
return Object.keys(BOARD_PROFILES).map((code) => (
));
}
const ApplicationSettings = () => {
const { data: hardwareData } = useRequest(readSystemStatus);
const {
loadData,
saveData,
updateDataValue,
data,
origData,
dirtyFlags,
setDirtyFlags,
blocker,
errorMessage,
restartNeeded
} = useRest({
read: readSettings,
update: writeSettings
});
const [restarting, setRestarting] = useState();
const { LL } = useI18nContext();
const updateFormValue = updateValueDirty(
origData as unknown as Record,
dirtyFlags,
setDirtyFlags,
updateDataValue as (value: unknown) => void
);
const [fieldErrors, setFieldErrors] = useState();
const { send: sendAPI } = useRequest((data: APIcall) => API(data), {
immediate: false
});
const { loading: processingBoard, send: readBoardProfile } = useRequest(
(boardProfile: string) => getBoardProfile(boardProfile),
{
immediate: false
}
).onSuccess((event) => {
const response = event.data as Settings;
updateDataValue({
...data,
board_profile: response.board_profile,
led_gpio: response.led_gpio,
dallas_gpio: response.dallas_gpio,
rx_gpio: response.rx_gpio,
tx_gpio: response.tx_gpio,
pbutton_gpio: response.pbutton_gpio,
phy_type: response.phy_type,
eth_power: response.eth_power,
eth_phy_addr: response.eth_phy_addr,
eth_clock_mode: response.eth_clock_mode
});
});
// Memoized input props to prevent recreation on every render
const SecondsInputProps = useMemo(
() => ({
endAdornment: {LL.SECONDS()}
}),
[LL]
);
const MinutesInputProps = useMemo(
() => ({
endAdornment: {LL.MINUTES()}
}),
[LL]
);
const HoursInputProps = useMemo(
() => ({
endAdornment: {LL.HOURS()}
}),
[LL]
);
const doRestart = useCallback(async () => {
setRestarting(true);
await sendAPI({ device: 'system', cmd: 'restart', id: 0 }).catch(
(error: Error) => {
toast.error(error.message);
}
);
}, [sendAPI]);
const updateBoardProfile = useCallback(
async (board_profile: string) => {
await readBoardProfile(board_profile).catch((error: Error) => {
toast.error(error.message);
});
},
[readBoardProfile]
);
useLayoutTitle(LL.APPLICATION());
const validateAndSubmit = useCallback(async () => {
try {
setFieldErrors(undefined);
await validate(createSettingsValidator(data), data);
} catch (error) {
setFieldErrors(error as ValidateFieldsError);
} finally {
await saveData();
}
}, [data, saveData]);
const changeBoardProfile = useCallback(
(event: React.ChangeEvent) => {
const boardProfile = event.target.value;
updateFormValue(event);
if (boardProfile === 'CUSTOM') {
updateDataValue({
...data,
board_profile: boardProfile
});
} else {
void updateBoardProfile(boardProfile);
}
},
[data, updateBoardProfile, updateFormValue, updateDataValue]
);
const restart = useCallback(async () => {
await validateAndSubmit();
await doRestart();
}, [validateAndSubmit, doRestart]);
// Memoize board profile select items to prevent recreation
const boardProfileItems = useMemo(() => boardProfileSelectItems(), []);
const content = () => {
if (!data || !hardwareData) {
return ;
}
return (
<>
{LL.SERVICES()}
API
}
label={LL.BYPASS_TOKEN()}
/>
Console
}
label={LL.ENABLE_TELNET()}
/>
Modbus
}
label={
{LL.ENABLE_MODBUS()}
{!hardwareData.psram && (
({LL.IS_REQUIRED('PSRAM')})
)}
}
/>
{data.modbus_enabled && (
)}
Syslog
}
label={LL.ENABLE_SYSLOG()}
/>
{data.syslog_enabled && (
)}
{LL.SENSORS()}
Analog
}
label={LL.ENABLE_ANALOG()}
/>
{data.dallas_gpio !== 0 && (
<>
{LL.TEMPERATURE()}
}
label={LL.ENABLE_PARASITE()}
/>
>
)}
{LL.FORMATTING_OPTIONS()}
}
label={LL.CONVERT_FAHRENHEIT()}
/>
}
label={LL.LOG_HEX()}
/>
{LL.SETTINGS_OF(LL.HARDWARE())}
{boardProfileItems}
{data.board_profile === 'CUSTOM' && (
<>
{data.led_gpio !== 0 && (
)}
{data.phy_type !== 0 && (
)}
>
)}
}
label={LL.READONLY()}
/>
{data.led_gpio !== 0 && (
}
label={LL.HIDE_LED()}
/>
)}
}
label={LL.UNDERCLOCK_CPU()}
/>
{LL.SPECIAL_FUNCTIONS()}
}
label={LL.DEVELOPER_MODE()}
/>
}
label={LL.HEATINGOFF()}
/>
}
label={LL.REMOTE_TIMEOUT_EN()}
/>
{data.remote_timeout_en && (
)}
}
label={LL.ENABLE_SHOWER_TIMER()}
/>
}
label={LL.ENABLE_SHOWER_ALERT()}
disabled={!data.shower_timer}
/>
{data.shower_timer && (
)}
{data.shower_alert && (
<>
>
)}
{restartNeeded && (
}
variant="contained"
color="error"
onClick={restart}
>
{LL.RESTART()}
)}
{!restartNeeded && dirtyFlags && dirtyFlags.length !== 0 && (
}
variant="outlined"
color="secondary"
type="submit"
onClick={loadData}
>
{LL.CANCEL()}
}
variant="contained"
color="info"
type="submit"
onClick={validateAndSubmit}
>
{LL.APPLY_CHANGES(dirtyFlags.length)}
)}
>
);
};
return (
{blocker ? : null}
{restarting ? : content()}
);
};
export default ApplicationSettings;