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) => ( {BOARD_PROFILES[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 && ( OFF ERR NOTICE INFO DEBUG ALL )} {LL.SENSORS()} Analog } label={LL.ENABLE_ANALOG()} /> {data.dallas_gpio !== 0 && ( <> {LL.TEMPERATURE()} } label={LL.ENABLE_PARASITE()} /> )} {LL.FORMATTING_OPTIONS()} Česky (CZ) Deutsch (DE) English (EN) Français (FR) Italiano (IT) Nederlands (NL) Norsk (NO) Polski (PL) Slovenčina (SK) Svenska (SV) Türk (TR) {LL.ONOFF()} {LL.ONOFF_CAP()} true/false 1/0 {LL.ONOFF()} {LL.ONOFF_CAP()} "true"/"false" true/false "1"/"0" 1/0 {LL.VALUE(5)} {LL.INDEX()} } label={LL.CONVERT_FAHRENHEIT()} /> } label={LL.LOG_HEX()} /> {LL.SETTINGS_OF(LL.HARDWARE())} {boardProfileItems} {LL.CUSTOM()}… {data.board_profile === 'CUSTOM' && ( <> {data.led_gpio !== 0 && ( LED RGB-LED )} {LL.DISABLED(1)} LAN8720 TLK110 {data.phy_type !== 0 && ( GPIO0_IN GPIO0_OUT GPIO16_OUT GPIO17_OUT )} )} EMS EMS+ HT3 {LL.HARDWARE()} Terminal (0x0A) Service Key (0x0B) Modem (0x0D) Converter (0x0E) Time Module (0x0F) Gateway 1 (0x48) Gateway 2 (0x49) Gateway 3 (0x4A) Gateway 4 (0x4B) Gateway 5 (0x4C) Gateway 7 (0x4D) } 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 && ( )} {!restartNeeded && dirtyFlags && dirtyFlags.length !== 0 && ( )} ); }; return ( {blocker ? : null} {restarting ? : content()} ); }; export default ApplicationSettings;