import { FC, useState, useEffect, useCallback } from 'react'; import { Button, Table, TableBody, TableHead, TableRow, Typography, Box, MenuItem, Dialog, DialogActions, DialogContent, DialogTitle, ToggleButton, ToggleButtonGroup } from '@mui/material'; import TableCell, { tableCellClasses } from '@mui/material/TableCell'; import { styled } from '@mui/material/styles'; import { useSnackbar } from 'notistack'; import SaveIcon from '@mui/icons-material/Save'; import CancelIcon from '@mui/icons-material/Cancel'; import EditOffOutlinedIcon from '@mui/icons-material/EditOffOutlined'; import FavoriteBorderOutlinedIcon from '@mui/icons-material/FavoriteBorderOutlined'; import VisibilityOffOutlinedIcon from '@mui/icons-material/VisibilityOffOutlined'; import CommentsDisabledOutlinedIcon from '@mui/icons-material/CommentsDisabledOutlined'; import SettingsBackupRestoreIcon from '@mui/icons-material/SettingsBackupRestore'; import { ButtonRow, FormLoader, ValidatedTextField, SectionContent } from '../components'; import * as EMSESP from './api'; import { extractErrorMessage } from '../utils'; import { DeviceShort, Devices, DeviceEntity } from './types'; const StyledTableCell = styled(TableCell)(({ theme }) => ({ [`&.${tableCellClasses.head}`]: { backgroundColor: '#607d8b' // color: theme.palette.common.white, // fontSize: 12 }, [`&.${tableCellClasses.body}`]: { // fontSize: 12 } })); const SettingsCustomization: FC = () => { const { enqueueSnackbar } = useSnackbar(); const [deviceEntities, setDeviceEntities] = useState(); const [devices, setDevices] = useState(); const [errorMessage, setErrorMessage] = useState(); const [selectedDevice, setSelectedDevice] = useState(0); const [confirmReset, setConfirmReset] = useState(false); // eslint-disable-next-line const [masks, setMasks] = useState(() => ['']); const fetchDevices = useCallback(async () => { try { setDevices((await EMSESP.readDevices()).data); } catch (error: any) { setErrorMessage(extractErrorMessage(error, 'Failed to fetch device list')); } }, []); const fetchDeviceEntities = async (unique_id: number) => { try { setDeviceEntities((await EMSESP.readDeviceEntities({ id: unique_id })).data); } catch (error: any) { setErrorMessage(extractErrorMessage(error, 'Problem fetching device entities')); } }; useEffect(() => { fetchDevices(); }, [fetchDevices]); function formatValue(value: any) { if (typeof value === 'number') { return new Intl.NumberFormat().format(value); } else if (value === undefined) { return ''; } else if (typeof value === 'boolean') { return value ? 'true' : 'false'; } return value; } const renderDeviceList = () => { if (!devices) { return ; } function compareDevices(a: DeviceShort, b: DeviceShort) { if (a.s < b.s) { return -1; } if (a.s > b.s) { return 1; } return 0; } const changeSelectedDevice = (event: React.ChangeEvent) => { const selected_device = parseInt(event.target.value, 10); setSelectedDevice(selected_device); fetchDeviceEntities(selected_device); }; return ( <> Select a device and customize each of its entities using the options:  mark it as favorite to be listed at the top of the Dashboard  make it read-only, only if it has write operation available  excluded it from MQTT and API outputs  hide it from the Dashboard Select a device... {devices.devices.sort(compareDevices).map((device: DeviceShort, index) => ( {device.s} ))} ); }; const saveCustomization = async () => { if (deviceEntities && selectedDevice) { const masked_entities = deviceEntities // .filter((de) => de.m) .map((new_de) => new_de.m.toString(16).padStart(2, '0') + new_de.s); try { const response = await EMSESP.writeMaskedEntities({ id: selectedDevice, entity_ids: masked_entities }); if (response.status === 200) { enqueueSnackbar('Customization saved', { variant: 'success' }); } else { enqueueSnackbar('Customization save failed', { variant: 'error' }); } } catch (error: any) { enqueueSnackbar(extractErrorMessage(error, 'Problem sending entity list'), { variant: 'error' }); } } }; const renderDeviceData = () => { if (devices?.devices.length === 0 || !deviceEntities) { return; } const setMask = (de: DeviceEntity, newMask: string[]) => { var new_mask = 0; for (let entry of newMask) { new_mask |= Number(entry); } de.m = new_mask; setMasks(newMask); }; const getMask = (de: DeviceEntity) => { var new_masks = []; if ((de.m & 1) === 1 || de.n === '') { new_masks.push('1'); } if ((de.m & 2) === 2) { new_masks.push('2'); } if ((de.m & 4) === 4 && de.w) { new_masks.push('4'); } if ((de.m & 8) === 8) { new_masks.push('8'); } return new_masks; }; return ( OPTIONS ENTITY NAME (CODE) VALUE {deviceEntities.map((de) => ( { setMask(de, mask); }} > {de.n} ({de.s}) {formatValue(de.v)} ))}
); }; const resetCustomization = async () => { try { await EMSESP.resetCustomizations(); enqueueSnackbar('All customizations have been removed. Restarting...', { variant: 'info' }); } catch (error: any) { enqueueSnackbar(extractErrorMessage(error, 'Problem resetting customizations'), { variant: 'error' }); } finally { setConfirmReset(false); } }; const renderResetDialog = () => ( setConfirmReset(false)}> Reset Are you sure you want remove all customizations? EMS-ESP will then restart. ); const content = () => { return ( <> Device Entities {renderDeviceList()} {renderDeviceData()} {renderResetDialog()} ); }; return ( {content()} ); }; export default SettingsCustomization;