Display basic device information

This commit is contained in:
Pierre HUBERT 2024-07-18 20:06:46 +02:00
parent 7be81fe0e9
commit 1ce9ca3321
5 changed files with 135 additions and 3 deletions

View File

@ -139,6 +139,10 @@ pub async fn secure_server(energy_actor: EnergyActorAddr) -> anyhow::Result<()>
"/web_api/devices/list_validated", "/web_api/devices/list_validated",
web::get().to(devices_controller::list_validated), web::get().to(devices_controller::list_validated),
) )
.route(
"/web_api/device/{id}",
web::get().to(devices_controller::get_single),
)
.route( .route(
"/web_api/device/{id}/validate", "/web_api/device/{id}/validate",
web::post().to(devices_controller::validate_device), web::post().to(devices_controller::validate_device),

View File

@ -33,6 +33,18 @@ pub struct DeviceInPath {
id: DeviceId, id: DeviceId,
} }
/// Get a single device information
pub async fn get_single(actor: WebEnergyActor, id: web::Path<DeviceInPath>) -> HttpResult {
let Some(dev) = actor
.send(energy_actor::GetSingleDevice(id.id.clone()))
.await?
else {
return Ok(HttpResponse::NotFound().json("Requested device was not found!"));
};
Ok(HttpResponse::Ok().json(dev))
}
/// Validate a device /// Validate a device
pub async fn validate_device(actor: WebEnergyActor, id: web::Path<DeviceInPath>) -> HttpResult { pub async fn validate_device(actor: WebEnergyActor, id: web::Path<DeviceInPath>) -> HttpResult {
actor actor

View File

@ -12,6 +12,7 @@ import { HomeRoute } from "./routes/HomeRoute";
import { BaseAuthenticatedPage } from "./widgets/BaseAuthenticatedPage"; import { BaseAuthenticatedPage } from "./widgets/BaseAuthenticatedPage";
import { PendingDevicesRoute } from "./routes/PendingDevicesRoute"; import { PendingDevicesRoute } from "./routes/PendingDevicesRoute";
import { DevicesRoute } from "./routes/DevicesRoute"; import { DevicesRoute } from "./routes/DevicesRoute";
import { DeviceRoute } from "./routes/DeviceRoute";
export function App() { export function App() {
if (!AuthApi.SignedIn && !ServerApi.Config.auth_disabled) if (!AuthApi.SignedIn && !ServerApi.Config.auth_disabled)
@ -21,8 +22,9 @@ export function App() {
createRoutesFromElements( createRoutesFromElements(
<Route path="*" element={<BaseAuthenticatedPage />}> <Route path="*" element={<BaseAuthenticatedPage />}>
<Route path="" element={<HomeRoute />} /> <Route path="" element={<HomeRoute />} />
<Route path="devices" element={<DevicesRoute />} />
<Route path="pending_devices" element={<PendingDevicesRoute />} /> <Route path="pending_devices" element={<PendingDevicesRoute />} />
<Route path="devices" element={<DevicesRoute />} />
<Route path="dev/:id" element={<DeviceRoute />} />
<Route path="*" element={<NotFoundRoute />} /> <Route path="*" element={<NotFoundRoute />} />
</Route> </Route>
) )

View File

@ -37,8 +37,8 @@ export interface Device {
relays: DeviceRelay[]; relays: DeviceRelay[];
} }
export function DeviceURL(d: Device, edit: boolean = false): string { export function DeviceURL(d: Device): string {
return `/dev/${d.id}${edit ? "/edit" : ""}`; return `/dev/${encodeURIComponent(d.id)}`;
} }
export class DeviceApi { export class DeviceApi {
@ -76,6 +76,18 @@ export class DeviceApi {
}); });
} }
/**
* Get the information about a single device
*/
static async GetSingle(id: string): Promise<Device> {
return (
await APIClient.exec({
uri: `/device/${encodeURIComponent(id)}`,
method: "GET",
})
).data;
}
/** /**
* Delete a device * Delete a device
*/ */

View File

@ -0,0 +1,102 @@
import { useParams } from "react-router-dom";
import { Device, DeviceApi } from "../api/DeviceApi";
import React from "react";
import { AsyncWidget } from "../widgets/AsyncWidget";
import { SolarEnergyRouteContainer } from "../widgets/SolarEnergyRouteContainer";
import {
Card,
Paper,
Table,
TableBody,
TableCell,
TableContainer,
TableRow,
Typography,
} from "@mui/material";
export function DeviceRoute(): React.ReactElement {
const { id } = useParams();
const [device, setDevice] = React.useState<Device | undefined>();
const loadKey = React.useRef(1);
const load = async () => {
setDevice(await DeviceApi.GetSingle(id!));
};
const reload = () => {
loadKey.current += 1;
setDevice(undefined);
};
return (
<AsyncWidget
loadKey={loadKey.current}
errMsg="Failed to load device information"
load={load}
ready={!!device}
build={() => <DeviceRouteInner device={device!} onReload={reload} />}
/>
);
}
function DeviceRouteInner(p: {
device: Device;
onReload: () => void;
}): React.ReactElement {
return (
<SolarEnergyRouteContainer label={`Device ${p.device.name}`}>
<GeneralDeviceInfo {...p} />
</SolarEnergyRouteContainer>
);
}
function GeneralDeviceInfo(p: { device: Device }): React.ReactElement {
return (
<TableContainer component={Paper}>
<Typography variant="h6" style={{ padding: "6px" }}>
General device information
</Typography>
<Table size="small">
<TableBody>
<DeviceInfoProperty label="ID" value={p.device.id} />
<DeviceInfoProperty
label="Reference"
value={p.device.info.reference}
/>
<DeviceInfoProperty label="Version" value={p.device.info.version} />
<DeviceInfoProperty label="Name" value={p.device.name} />
<DeviceInfoProperty
label="Description"
value={p.device.description}
/>
<DeviceInfoProperty
label="Enabled"
value={p.device.enabled ? "YES" : "NO"}
/>
<DeviceInfoProperty
label="Maximum number of relays"
value={p.device.info.max_relays.toString()}
/>
<DeviceInfoProperty
label="Number of configured relays"
value={p.device.relays.length.toString()}
/>
</TableBody>
</Table>
</TableContainer>
);
}
function DeviceInfoProperty(p: {
icon?: React.ReactElement;
label: string;
value: string;
}): React.ReactElement {
return (
<TableRow hover sx={{ "&:last-child td, &:last-child th": { border: 0 } }}>
<TableCell>{p.label}</TableCell>
<TableCell>{p.value}</TableCell>
</TableRow>
);
}