Update general device information
This commit is contained in:
@ -120,6 +120,18 @@ export class DeviceApi {
|
||||
).data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current state of a single device
|
||||
*/
|
||||
static async GetSingleState(id: string): Promise<DeviceState> {
|
||||
return (
|
||||
await APIClient.exec({
|
||||
uri: `/device/${encodeURIComponent(id)}/state`,
|
||||
method: "GET",
|
||||
})
|
||||
).data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a device general information
|
||||
*/
|
||||
|
@ -0,0 +1,15 @@
|
||||
import { TableCell, TableRow } from "@mui/material";
|
||||
|
||||
export function DeviceInfoProperty(p: {
|
||||
icon?: React.ReactElement;
|
||||
label: string;
|
||||
value: string;
|
||||
color?: string;
|
||||
}): React.ReactElement {
|
||||
return (
|
||||
<TableRow hover sx={{ "&:last-child td, &:last-child th": { border: 0 } }}>
|
||||
<TableCell>{p.label}</TableCell>
|
||||
<TableCell style={{ color: p.color }}>{p.value}</TableCell>
|
||||
</TableRow>
|
||||
);
|
||||
}
|
@ -1,5 +1,7 @@
|
||||
import DeleteIcon from "@mui/icons-material/Delete";
|
||||
import RefreshIcon from "@mui/icons-material/Refresh";
|
||||
import { IconButton, Tooltip } from "@mui/material";
|
||||
import Grid from "@mui/material/Grid2";
|
||||
import React from "react";
|
||||
import { useNavigate, useParams } from "react-router-dom";
|
||||
import { Device, DeviceApi } from "../../api/DeviceApi";
|
||||
@ -9,9 +11,9 @@ import { useLoadingMessage } from "../../hooks/context_providers/LoadingMessageP
|
||||
import { useSnackbar } from "../../hooks/context_providers/SnackbarProvider";
|
||||
import { AsyncWidget } from "../../widgets/AsyncWidget";
|
||||
import { SolarEnergyRouteContainer } from "../../widgets/SolarEnergyRouteContainer";
|
||||
import { GeneralDeviceInfo } from "./GeneralDeviceInfo";
|
||||
import { DeviceRelays } from "./DeviceRelays";
|
||||
import Grid from "@mui/material/Grid2";
|
||||
import { DeviceStateBlock } from "./DeviceStateBlock";
|
||||
import { GeneralDeviceInfo } from "./GeneralDeviceInfo";
|
||||
|
||||
export function DeviceRoute(): React.ReactElement {
|
||||
const { id } = useParams();
|
||||
@ -70,15 +72,23 @@ function DeviceRouteInner(p: {
|
||||
loadingMessage.hide();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<SolarEnergyRouteContainer
|
||||
label={`Device ${p.device.name}`}
|
||||
actions={
|
||||
<Tooltip title="Delete device">
|
||||
<IconButton onClick={() => deleteDevice(p.device)}>
|
||||
<DeleteIcon />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
<span>
|
||||
<Tooltip title="Refresh information">
|
||||
<IconButton onClick={p.onReload}>
|
||||
<RefreshIcon />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
<Tooltip title="Delete device">
|
||||
<IconButton onClick={() => deleteDevice(p.device)}>
|
||||
<DeleteIcon />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
</span>
|
||||
}
|
||||
>
|
||||
<Grid container spacing={2}>
|
||||
@ -88,6 +98,9 @@ function DeviceRouteInner(p: {
|
||||
<Grid size={{ xs: 12, md: 6 }}>
|
||||
<DeviceRelays {...p} />
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, md: 6 }}>
|
||||
<DeviceStateBlock {...p} />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</SolarEnergyRouteContainer>
|
||||
);
|
||||
|
44
central_frontend/src/routes/DeviceRoute/DeviceStateBlock.tsx
Normal file
44
central_frontend/src/routes/DeviceRoute/DeviceStateBlock.tsx
Normal file
@ -0,0 +1,44 @@
|
||||
import React from "react";
|
||||
import { Device, DeviceApi, DeviceState } from "../../api/DeviceApi";
|
||||
import { AsyncWidget } from "../../widgets/AsyncWidget";
|
||||
import { DeviceRouteCard } from "./DeviceRouteCard";
|
||||
import { Table, TableBody } from "@mui/material";
|
||||
import { DeviceInfoProperty } from "./DeviceInfoProperty";
|
||||
import { timeDiff } from "../../widgets/TimeWidget";
|
||||
|
||||
export function DeviceStateBlock(p: { device: Device }): React.ReactElement {
|
||||
const [state, setState] = React.useState<DeviceState>();
|
||||
|
||||
const load = async () => {
|
||||
setState(await DeviceApi.GetSingleState(p.device.id));
|
||||
};
|
||||
|
||||
return (
|
||||
<DeviceRouteCard title="Device state">
|
||||
<AsyncWidget
|
||||
loadKey={p.device.id}
|
||||
load={load}
|
||||
ready={!!state}
|
||||
errMsg="Failed to load device state!"
|
||||
build={() => <DeviceStateInner state={state!} />}
|
||||
/>
|
||||
</DeviceRouteCard>
|
||||
);
|
||||
}
|
||||
|
||||
function DeviceStateInner(p: { state: DeviceState }): React.ReactElement {
|
||||
return (
|
||||
<Table size="small">
|
||||
<TableBody>
|
||||
<DeviceInfoProperty
|
||||
label="Status"
|
||||
value={p.state.online ? "Online" : "Offline"}
|
||||
/>
|
||||
<DeviceInfoProperty
|
||||
label="Last ping"
|
||||
value={timeDiff(0, p.state.last_ping)}
|
||||
/>
|
||||
</TableBody>
|
||||
</Table>
|
||||
);
|
||||
}
|
@ -1,16 +1,10 @@
|
||||
import EditIcon from "@mui/icons-material/Edit";
|
||||
import {
|
||||
IconButton,
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableRow,
|
||||
Tooltip,
|
||||
} from "@mui/material";
|
||||
import { IconButton, Table, TableBody, Tooltip } from "@mui/material";
|
||||
import React from "react";
|
||||
import { Device } from "../../api/DeviceApi";
|
||||
import { EditDeviceMetadataDialog } from "../../dialogs/EditDeviceMetadataDialog";
|
||||
import { formatDate } from "../../widgets/TimeWidget";
|
||||
import { DeviceInfoProperty } from "./DeviceInfoProperty";
|
||||
import { DeviceRouteCard } from "./DeviceRouteCard";
|
||||
|
||||
export function GeneralDeviceInfo(p: {
|
||||
@ -78,17 +72,3 @@ export function GeneralDeviceInfo(p: {
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function DeviceInfoProperty(p: {
|
||||
icon?: React.ReactElement;
|
||||
label: string;
|
||||
value: string;
|
||||
color?: string;
|
||||
}): React.ReactElement {
|
||||
return (
|
||||
<TableRow hover sx={{ "&:last-child td, &:last-child th": { border: 0 } }}>
|
||||
<TableCell>{p.label}</TableCell>
|
||||
<TableCell style={{ color: p.color }}>{p.value}</TableCell>
|
||||
</TableRow>
|
||||
);
|
||||
}
|
||||
|
Reference in New Issue
Block a user