mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2025-12-06 15:59:52 +03:00
quick link from device entities to custom entities page (the list icon)
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
import CommentsDisabledOutlinedIcon from '@mui/icons-material/CommentsDisabledOutlined';
|
import CommentsDisabledOutlinedIcon from '@mui/icons-material/CommentsDisabledOutlined';
|
||||||
import EditIcon from '@mui/icons-material/Edit';
|
import EditIcon from '@mui/icons-material/Edit';
|
||||||
import EditOffOutlinedIcon from '@mui/icons-material/EditOffOutlined';
|
import EditOffOutlinedIcon from '@mui/icons-material/EditOffOutlined';
|
||||||
|
import FormatListNumberedIcon from '@mui/icons-material/FormatListNumbered';
|
||||||
import DownloadIcon from '@mui/icons-material/GetApp';
|
import DownloadIcon from '@mui/icons-material/GetApp';
|
||||||
import HighlightOffIcon from '@mui/icons-material/HighlightOff';
|
import HighlightOffIcon from '@mui/icons-material/HighlightOff';
|
||||||
import InfoOutlinedIcon from '@mui/icons-material/InfoOutlined';
|
import InfoOutlinedIcon from '@mui/icons-material/InfoOutlined';
|
||||||
@@ -35,6 +36,7 @@ import { useRequest } from 'alova';
|
|||||||
import { useState, useContext, useEffect, useCallback, useLayoutEffect } from 'react';
|
import { useState, useContext, useEffect, useCallback, useLayoutEffect } from 'react';
|
||||||
|
|
||||||
import { IconContext } from 'react-icons';
|
import { IconContext } from 'react-icons';
|
||||||
|
import { useNavigate } from 'react-router-dom';
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
import DashboardDevicesDialog from './DashboardDevicesDialog';
|
import DashboardDevicesDialog from './DashboardDevicesDialog';
|
||||||
import DeviceIcon from './DeviceIcon';
|
import DeviceIcon from './DeviceIcon';
|
||||||
@@ -62,6 +64,8 @@ const DashboardDevices: FC = () => {
|
|||||||
const [showDeviceInfo, setShowDeviceInfo] = useState<boolean>(false);
|
const [showDeviceInfo, setShowDeviceInfo] = useState<boolean>(false);
|
||||||
const [selectedDevice, setSelectedDevice] = useState<number>();
|
const [selectedDevice, setSelectedDevice] = useState<number>();
|
||||||
|
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
const { data: coreData, send: readCoreData } = useRequest(() => EMSESP.readCoreData(), {
|
const { data: coreData, send: readCoreData } = useRequest(() => EMSESP.readCoreData(), {
|
||||||
initialData: {
|
initialData: {
|
||||||
connected: true,
|
connected: true,
|
||||||
@@ -264,13 +268,16 @@ const DashboardDevices: FC = () => {
|
|||||||
}, [escFunction]);
|
}, [escFunction]);
|
||||||
|
|
||||||
const refreshData = () => {
|
const refreshData = () => {
|
||||||
if (deviceValueDialogOpen) {
|
if (!deviceValueDialogOpen) {
|
||||||
return;
|
selectedDevice ? void readDeviceData(selectedDevice) : void readCoreData();
|
||||||
}
|
}
|
||||||
if (selectedDevice) {
|
};
|
||||||
void readDeviceData(selectedDevice);
|
|
||||||
|
const customize = () => {
|
||||||
|
if (selectedDevice == 99) {
|
||||||
|
navigate('/settings/customentities');
|
||||||
} else {
|
} else {
|
||||||
void readCoreData();
|
navigate('/settings/customization', { state: selectedDevice });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -496,10 +503,19 @@ const DashboardDevices: FC = () => {
|
|||||||
|
|
||||||
<Grid container justifyContent="space-between">
|
<Grid container justifyContent="space-between">
|
||||||
<Typography sx={{ ml: 1 }} variant="subtitle2" color="primary">
|
<Typography sx={{ ml: 1 }} variant="subtitle2" color="primary">
|
||||||
{shown_data.length + ' ' + LL.ENTITIES(shown_data.length)}
|
{LL.SHOWING() +
|
||||||
|
' ' +
|
||||||
|
shown_data.length +
|
||||||
|
'/' +
|
||||||
|
coreData.devices[deviceIndex].e +
|
||||||
|
' ' +
|
||||||
|
LL.ENTITIES(shown_data.length)}
|
||||||
<IconButton onClick={() => setShowDeviceInfo(true)}>
|
<IconButton onClick={() => setShowDeviceInfo(true)}>
|
||||||
<InfoOutlinedIcon color="primary" sx={{ fontSize: 18, verticalAlign: 'middle' }} />
|
<InfoOutlinedIcon color="primary" sx={{ fontSize: 18, verticalAlign: 'middle' }} />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
|
<IconButton onClick={customize}>
|
||||||
|
<FormatListNumberedIcon color="primary" sx={{ fontSize: 18, verticalAlign: 'middle' }} />
|
||||||
|
</IconButton>
|
||||||
<IconButton onClick={handleDownloadCsv}>
|
<IconButton onClick={handleDownloadCsv}>
|
||||||
<DownloadIcon color="primary" sx={{ fontSize: 18, verticalAlign: 'middle' }} />
|
<DownloadIcon color="primary" sx={{ fontSize: 18, verticalAlign: 'middle' }} />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ import { Table, Header, HeaderRow, HeaderCell, Body, Row, Cell } from '@table-li
|
|||||||
import { useTheme } from '@table-library/react-table-library/theme';
|
import { useTheme } from '@table-library/react-table-library/theme';
|
||||||
import { useRequest } from 'alova';
|
import { 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, useLocation } from 'react-router-dom';
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
|
|
||||||
import EntityMaskToggle from './EntityMaskToggle';
|
import EntityMaskToggle from './EntityMaskToggle';
|
||||||
@@ -52,19 +52,23 @@ const SettingsCustomization: FC = () => {
|
|||||||
const [restarting, setRestarting] = useState<boolean>(false);
|
const [restarting, setRestarting] = useState<boolean>(false);
|
||||||
const [restartNeeded, setRestartNeeded] = useState<boolean>(false);
|
const [restartNeeded, setRestartNeeded] = useState<boolean>(false);
|
||||||
const [deviceEntities, setDeviceEntities] = useState<DeviceEntity[]>([]);
|
const [deviceEntities, setDeviceEntities] = useState<DeviceEntity[]>([]);
|
||||||
const [selectedDevice, setSelectedDevice] = useState<number>(-1);
|
|
||||||
const [confirmReset, setConfirmReset] = useState<boolean>(false);
|
const [confirmReset, setConfirmReset] = useState<boolean>(false);
|
||||||
const [selectedFilters, setSelectedFilters] = useState<number>(0);
|
const [selectedFilters, setSelectedFilters] = useState<number>(0);
|
||||||
const [search, setSearch] = useState('');
|
const [search, setSearch] = useState('');
|
||||||
const [selectedDeviceEntity, setSelectedDeviceEntity] = useState<DeviceEntity>();
|
const [selectedDeviceEntity, setSelectedDeviceEntity] = useState<DeviceEntity>();
|
||||||
const [dialogOpen, setDialogOpen] = useState<boolean>(false);
|
const [dialogOpen, setDialogOpen] = useState<boolean>(false);
|
||||||
|
|
||||||
|
// fetch devices first
|
||||||
|
const { data: devices } = useRequest(EMSESP.readDevices);
|
||||||
|
|
||||||
|
// const { state } = useLocation();
|
||||||
|
const [selectedDevice, setSelectedDevice] = useState<number>(useLocation().state || -1);
|
||||||
|
const [selectedDeviceName, setSelectedDeviceName] = useState<string>('');
|
||||||
|
|
||||||
const { send: resetCustomizations } = useRequest(EMSESP.resetCustomizations(), {
|
const { send: resetCustomizations } = useRequest(EMSESP.resetCustomizations(), {
|
||||||
immediate: false
|
immediate: false
|
||||||
});
|
});
|
||||||
|
|
||||||
const { data: devices } = useRequest(EMSESP.readDevices);
|
|
||||||
|
|
||||||
const { send: writeCustomizationEntities } = useRequest((data) => EMSESP.writeCustomizationEntities(data), {
|
const { send: writeCustomizationEntities } = useRequest((data) => EMSESP.writeCustomizationEntities(data), {
|
||||||
immediate: false
|
immediate: false
|
||||||
});
|
});
|
||||||
@@ -176,6 +180,22 @@ const SettingsCustomization: FC = () => {
|
|||||||
}
|
}
|
||||||
}, [deviceEntities]);
|
}, [deviceEntities]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (devices && selectedDevice !== -1) {
|
||||||
|
void readDeviceEntities(selectedDevice);
|
||||||
|
const id = devices.devices.findIndex((d) => d.i === selectedDevice);
|
||||||
|
if (id === -1) {
|
||||||
|
setSelectedDevice(-1);
|
||||||
|
setSelectedDeviceName('');
|
||||||
|
} else {
|
||||||
|
setSelectedDeviceName(devices.devices[id].tn || '');
|
||||||
|
setNumChanges(0);
|
||||||
|
setRestartNeeded(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [devices, selectedDevice]);
|
||||||
|
|
||||||
const restart = async () => {
|
const restart = async () => {
|
||||||
await restartCommand().catch((error) => {
|
await restartCommand().catch((error) => {
|
||||||
toast.error(error.message);
|
toast.error(error.message);
|
||||||
@@ -246,16 +266,6 @@ const SettingsCustomization: FC = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const changeSelectedDevice = (event: React.ChangeEvent<HTMLInputElement>) => {
|
|
||||||
if (devices) {
|
|
||||||
const selected_device = parseInt(event.target.value, 10);
|
|
||||||
setSelectedDevice(selected_device);
|
|
||||||
setNumChanges(0);
|
|
||||||
void readDeviceEntities(devices?.devices[selected_device].i);
|
|
||||||
setRestartNeeded(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const resetCustomization = async () => {
|
const resetCustomization = async () => {
|
||||||
try {
|
try {
|
||||||
await resetCustomizations();
|
await resetCustomizations();
|
||||||
@@ -314,30 +324,21 @@ const SettingsCustomization: FC = () => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await writeCustomizationEntities({ id: devices?.devices[selectedDevice].i, entity_ids: masked_entities }).catch(
|
await writeCustomizationEntities({ id: selectedDevice, entity_ids: masked_entities }).catch((error) => {
|
||||||
(error) => {
|
if (error.message === 'Reboot required') {
|
||||||
if (error.message === 'Reboot required') {
|
setRestartNeeded(true);
|
||||||
setRestartNeeded(true);
|
} else {
|
||||||
} else {
|
toast.error(error.message);
|
||||||
toast.error(error.message);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
);
|
});
|
||||||
setOriginalSettings(deviceEntities);
|
setOriginalSettings(deviceEntities);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderDeviceList = () => (
|
const renderDeviceList = () => (
|
||||||
<>
|
<>
|
||||||
<Box mb={2} color="warning.main">
|
<Box mb={1} color="warning.main">
|
||||||
<Typography variant="body2">{LL.CUSTOMIZATIONS_HELP_1()}.</Typography>
|
<Typography variant="body2">{LL.CUSTOMIZATIONS_HELP_1()}.</Typography>
|
||||||
<Typography variant="body2" mt={1}>
|
|
||||||
<OptionIcon type="favorite" isSet={true} />={LL.CUSTOMIZATIONS_HELP_2()}
|
|
||||||
<OptionIcon type="readonly" isSet={true} />={LL.CUSTOMIZATIONS_HELP_3()}
|
|
||||||
<OptionIcon type="api_mqtt_exclude" isSet={true} />={LL.CUSTOMIZATIONS_HELP_4()}
|
|
||||||
<OptionIcon type="web_exclude" isSet={true} />={LL.CUSTOMIZATIONS_HELP_5()}
|
|
||||||
<OptionIcon type="deleted" isSet={true} />={LL.CUSTOMIZATIONS_HELP_6()}
|
|
||||||
</Typography>
|
|
||||||
</Box>
|
</Box>
|
||||||
<TextField
|
<TextField
|
||||||
name="device"
|
name="device"
|
||||||
@@ -346,15 +347,15 @@ const SettingsCustomization: FC = () => {
|
|||||||
fullWidth
|
fullWidth
|
||||||
value={selectedDevice}
|
value={selectedDevice}
|
||||||
disabled={numChanges !== 0}
|
disabled={numChanges !== 0}
|
||||||
onChange={changeSelectedDevice}
|
onChange={(e) => setSelectedDevice(parseInt(e.target.value))}
|
||||||
margin="normal"
|
margin="normal"
|
||||||
select
|
select
|
||||||
>
|
>
|
||||||
<MenuItem disabled key={-1} value={-1}>
|
<MenuItem disabled key={-1} value={-1}>
|
||||||
{LL.SELECT_DEVICE()}...
|
{LL.SELECT_DEVICE()}...
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
{devices.devices.map((device: DeviceShort, index) => (
|
{devices.devices.map((device: DeviceShort) => (
|
||||||
<MenuItem key={index} value={index}>
|
<MenuItem key={device.i} value={device.i}>
|
||||||
{device.s}
|
{device.s}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
))}
|
))}
|
||||||
@@ -363,14 +364,19 @@ const SettingsCustomization: FC = () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const renderDeviceData = () => {
|
const renderDeviceData = () => {
|
||||||
if (deviceEntities.length === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const shown_data = deviceEntities.filter((de) => filter_entity(de));
|
const shown_data = deviceEntities.filter((de) => filter_entity(de));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
<Box color="warning.main">
|
||||||
|
<Typography variant="body2" mt={1}>
|
||||||
|
<OptionIcon type="favorite" isSet={true} />={LL.CUSTOMIZATIONS_HELP_2()}
|
||||||
|
<OptionIcon type="readonly" isSet={true} />={LL.CUSTOMIZATIONS_HELP_3()}
|
||||||
|
<OptionIcon type="api_mqtt_exclude" isSet={true} />={LL.CUSTOMIZATIONS_HELP_4()}
|
||||||
|
<OptionIcon type="web_exclude" isSet={true} />={LL.CUSTOMIZATIONS_HELP_5()}
|
||||||
|
<OptionIcon type="deleted" isSet={true} />={LL.CUSTOMIZATIONS_HELP_6()}
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
<Grid container mb={1} mt={0} spacing={1} direction="row" justifyContent="flex-start" alignItems="center">
|
<Grid container mb={1} mt={0} spacing={1} direction="row" justifyContent="flex-start" alignItems="center">
|
||||||
<Grid item xs={2}>
|
<Grid item xs={2}>
|
||||||
<TextField
|
<TextField
|
||||||
@@ -443,7 +449,7 @@ const SettingsCustomization: FC = () => {
|
|||||||
</Grid>
|
</Grid>
|
||||||
<Grid item>
|
<Grid item>
|
||||||
<Typography variant="subtitle2" color="primary">
|
<Typography variant="subtitle2" color="primary">
|
||||||
{LL.SHOWING()} {shown_data.length}/{deviceEntities.length}
|
{LL.SHOWING()} {shown_data.length}/{deviceEntities.length} {LL.ENTITIES(deviceEntities.length)}
|
||||||
</Typography>
|
</Typography>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
@@ -467,7 +473,7 @@ const SettingsCustomization: FC = () => {
|
|||||||
</Cell>
|
</Cell>
|
||||||
<Cell>
|
<Cell>
|
||||||
{formatName(de, false)} (
|
{formatName(de, false)} (
|
||||||
<Link target="_blank" href={APIURL + devices?.devices[selectedDevice].tn + '/' + de.id}>
|
<Link target="_blank" href={APIURL + selectedDeviceName + '/' + de.id}>
|
||||||
{de.id}
|
{de.id}
|
||||||
</Link>
|
</Link>
|
||||||
)
|
)
|
||||||
@@ -506,7 +512,7 @@ const SettingsCustomization: FC = () => {
|
|||||||
{LL.DEVICE_ENTITIES()}
|
{LL.DEVICE_ENTITIES()}
|
||||||
</Typography>
|
</Typography>
|
||||||
{devices && renderDeviceList()}
|
{devices && renderDeviceList()}
|
||||||
{renderDeviceData()}
|
{deviceEntities && renderDeviceData()}
|
||||||
{restartNeeded && (
|
{restartNeeded && (
|
||||||
<MessageBox my={2} level="warning" message={LL.RESTART_TEXT()}>
|
<MessageBox my={2} level="warning" message={LL.RESTART_TEXT()}>
|
||||||
<Button startIcon={<PowerSettingsNewIcon />} variant="contained" color="error" onClick={restart}>
|
<Button startIcon={<PowerSettingsNewIcon />} variant="contained" color="error" onClick={restart}>
|
||||||
@@ -523,7 +529,7 @@ const SettingsCustomization: FC = () => {
|
|||||||
startIcon={<CancelIcon />}
|
startIcon={<CancelIcon />}
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
color="secondary"
|
color="secondary"
|
||||||
onClick={() => devices && readDeviceEntities(devices.devices[selectedDevice].i)}
|
onClick={() => devices && readDeviceEntities(selectedDevice)}
|
||||||
>
|
>
|
||||||
{LL.CANCEL()}
|
{LL.CANCEL()}
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
Reference in New Issue
Block a user