mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2025-12-07 00:09:51 +03:00
show EMS Devices in Web UI - (v2) New Web UI #421
This commit is contained in:
@@ -9,6 +9,7 @@ import { AuthenticatedRoute } from '../authentication';
|
||||
|
||||
import EMSESPSettingsController from './EMSESPSettingsController';
|
||||
import EMSESPStatusController from './EMSESPStatusController';
|
||||
import EMSESPDevicesController from './EMSESPDevicesController';
|
||||
|
||||
class EMSESP extends Component<RouteComponentProps> {
|
||||
|
||||
@@ -21,10 +22,12 @@ class EMSESP extends Component<RouteComponentProps> {
|
||||
<MenuAppBar sectionTitle="EMS-ESP">
|
||||
<Tabs value={this.props.match.url} onChange={this.handleTabChange} variant="fullWidth">
|
||||
<Tab value={`/${PROJECT_PATH}/status`} label="EMS-ESP Status" />
|
||||
<Tab value={`/${PROJECT_PATH}/devices`} label="EMS Devices" />
|
||||
<Tab value={`/${PROJECT_PATH}/settings`} label="EMS-ESP Settings" />
|
||||
</Tabs>
|
||||
<Switch>
|
||||
<AuthenticatedRoute exact path={`/${PROJECT_PATH}/status`} component={EMSESPStatusController} />
|
||||
<AuthenticatedRoute exact path={`/${PROJECT_PATH}/devices`} component={EMSESPDevicesController} />
|
||||
<AuthenticatedRoute exact path={`/${PROJECT_PATH}/settings`} component={EMSESPSettingsController} />
|
||||
<Redirect to={`/${PROJECT_PATH}/status`} />
|
||||
</Switch>
|
||||
|
||||
30
interface/src/project/EMSESPDevicesController.tsx
Normal file
30
interface/src/project/EMSESPDevicesController.tsx
Normal file
@@ -0,0 +1,30 @@
|
||||
import React, { Component } from 'react';
|
||||
|
||||
import { restController, RestControllerProps, RestFormLoader, SectionContent } from '../components';
|
||||
import { ENDPOINT_ROOT } from '../api';
|
||||
import EMSESPDevicesForm from './EMSESPDevicesForm';
|
||||
import { EMSESPDevices } from './types';
|
||||
|
||||
export const EMSESP_DEVICES_ENDPOINT = ENDPOINT_ROOT + "emsespDevices";
|
||||
|
||||
type EMSESPDevicesControllerProps = RestControllerProps<EMSESPDevices>;
|
||||
|
||||
class EMSESPDevicesController extends Component<EMSESPDevicesControllerProps> {
|
||||
|
||||
componentDidMount() {
|
||||
this.props.loadData();
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<SectionContent title="EMS Devices">
|
||||
<RestFormLoader
|
||||
{...this.props}
|
||||
render={formProps => <EMSESPDevicesForm {...formProps} />}
|
||||
/>
|
||||
</SectionContent>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default restController(EMSESP_DEVICES_ENDPOINT, EMSESPDevicesController);
|
||||
121
interface/src/project/EMSESPDevicesForm.tsx
Normal file
121
interface/src/project/EMSESPDevicesForm.tsx
Normal file
@@ -0,0 +1,121 @@
|
||||
import React, { Component, Fragment } from "react";
|
||||
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableHead,
|
||||
TableRow,
|
||||
List,
|
||||
withWidth,
|
||||
WithWidthProps,
|
||||
isWidthDown
|
||||
} from "@material-ui/core";
|
||||
|
||||
import { Box, Typography } from '@material-ui/core';
|
||||
|
||||
import RefreshIcon from "@material-ui/icons/Refresh";
|
||||
|
||||
import { withAuthenticatedContext, AuthenticatedContextProps } from '../authentication';
|
||||
|
||||
import {
|
||||
RestFormProps,
|
||||
FormActions,
|
||||
FormButton,
|
||||
} from "../components";
|
||||
|
||||
import { EMSESPDevices, Device } from "./types";
|
||||
|
||||
function compareDevices(a: Device, b: Device) {
|
||||
if (a.type < b.type) {
|
||||
return -1;
|
||||
}
|
||||
if (a.type > b.type) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
type EMSESPDevicesFormProps = RestFormProps<EMSESPDevices> & AuthenticatedContextProps & WithWidthProps;
|
||||
|
||||
class EMSESPDevicesForm extends Component<EMSESPDevicesFormProps> {
|
||||
|
||||
noData = () => {
|
||||
return (this.props.data.devices.length === 0);
|
||||
};
|
||||
|
||||
createListItems() {
|
||||
const { width, data } = this.props;
|
||||
return (
|
||||
<Fragment>
|
||||
<Table size="small" padding={isWidthDown('xs', width!) ? "none" : "default"}>
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell>Type</TableCell>
|
||||
<TableCell align="center">Brand</TableCell>
|
||||
<TableCell align="center">Name</TableCell>
|
||||
<TableCell align="center">Device ID</TableCell>
|
||||
<TableCell align="center">Product ID</TableCell>
|
||||
<TableCell align="center">Version</TableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{data.devices.sort(compareDevices).map(device => (
|
||||
<TableRow key={device.type}>
|
||||
<TableCell component="th" scope="row">
|
||||
{device.type}
|
||||
</TableCell>
|
||||
<TableCell align="center">
|
||||
{device.brand}
|
||||
</TableCell>
|
||||
<TableCell align="center">
|
||||
{device.name}
|
||||
</TableCell>
|
||||
<TableCell align="center">
|
||||
0x{('00' + device.deviceid.toString(16).toUpperCase()).slice(-2)}
|
||||
</TableCell>
|
||||
<TableCell align="center">
|
||||
{device.productid}
|
||||
</TableCell>
|
||||
<TableCell align="center">
|
||||
{device.version}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
{this.noData() &&
|
||||
(
|
||||
<Box bgcolor="error.main" color="error.contrastText" p={2} mt={2} mb={2}>
|
||||
<Typography variant="body1">
|
||||
No EMS devices found.
|
||||
</Typography>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
</Fragment >
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Fragment>
|
||||
<List>{this.createListItems()}</List>
|
||||
<FormActions>
|
||||
<FormButton
|
||||
startIcon={<RefreshIcon />}
|
||||
variant="contained"
|
||||
color="secondary"
|
||||
onClick={this.props.loadData}
|
||||
>
|
||||
Refresh
|
||||
</FormButton>
|
||||
</FormActions>
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withAuthenticatedContext(withWidth()(EMSESPDevicesForm));
|
||||
|
||||
// export default withTheme(EMSESPDevicesForm);
|
||||
@@ -55,7 +55,7 @@ function EMSESPSettingsControllerForm(props: EMSESPSettingsControllerFormProps)
|
||||
validators={['required', 'isNumber', 'minNumber:0', 'maxNumber:255']}
|
||||
errorMessages={['TX mode is required', "Must be a number", "Must be 0 or higher", "Max value is 255"]}
|
||||
name="tx_mode"
|
||||
label="Tx mode (0=off)"
|
||||
label="Tx Mode (0=off)"
|
||||
fullWidth
|
||||
variant="outlined"
|
||||
value={data.tx_mode}
|
||||
@@ -71,10 +71,10 @@ function EMSESPSettingsControllerForm(props: EMSESPSettingsControllerFormProps)
|
||||
value="system_heartbeat"
|
||||
/>
|
||||
}
|
||||
label="MQTT heartbeat"
|
||||
label="MQTT Heartbeat"
|
||||
/>
|
||||
<SelectValidator name="mqtt_format"
|
||||
label="MQTT format"
|
||||
label="MQTT Format"
|
||||
value={data.mqtt_format}
|
||||
fullWidth
|
||||
variant="outlined"
|
||||
@@ -100,7 +100,7 @@ function EMSESPSettingsControllerForm(props: EMSESPSettingsControllerFormProps)
|
||||
validators={['required', 'isNumber', 'minNumber:1', 'maxNumber:65535']}
|
||||
errorMessages={['Keep alive is required', "Must be a number", "Must be greater than 0", "Max value is 65535"]}
|
||||
name="publish_time"
|
||||
label="MQTT Publish time (seconds)"
|
||||
label="MQTT Publish Time (seconds)"
|
||||
fullWidth
|
||||
variant="outlined"
|
||||
value={data.publish_time}
|
||||
@@ -108,18 +108,6 @@ function EMSESPSettingsControllerForm(props: EMSESPSettingsControllerFormProps)
|
||||
onChange={handleValueChange('publish_time')}
|
||||
margin="normal"
|
||||
/>
|
||||
<SelectValidator name="syslog_level"
|
||||
label="Syslog log level"
|
||||
value={data.syslog_level}
|
||||
fullWidth
|
||||
variant="outlined"
|
||||
onChange={handleValueChange('syslog_level')}
|
||||
margin="normal">
|
||||
<MenuItem value={-1}>OFF</MenuItem>
|
||||
<MenuItem value={3}>ERR</MenuItem>
|
||||
<MenuItem value={6}>INFO</MenuItem>
|
||||
<MenuItem value={7}>DEBUG</MenuItem>
|
||||
</SelectValidator>
|
||||
<BlockFormControlLabel
|
||||
control={
|
||||
<Checkbox
|
||||
@@ -140,6 +128,18 @@ function EMSESPSettingsControllerForm(props: EMSESPSettingsControllerFormProps)
|
||||
}
|
||||
label="Shower Alert"
|
||||
/>
|
||||
<SelectValidator name="syslog_level"
|
||||
label="Syslog Log Level"
|
||||
value={data.syslog_level}
|
||||
fullWidth
|
||||
variant="outlined"
|
||||
onChange={handleValueChange('syslog_level')}
|
||||
margin="normal">
|
||||
<MenuItem value={-1}>OFF</MenuItem>
|
||||
<MenuItem value={3}>ERR</MenuItem>
|
||||
<MenuItem value={6}>INFO</MenuItem>
|
||||
<MenuItem value={7}>DEBUG</MenuItem>
|
||||
</SelectValidator>
|
||||
{data.syslog_level !== -1 &&
|
||||
<Fragment>
|
||||
<TextValidator
|
||||
@@ -154,13 +154,13 @@ function EMSESPSettingsControllerForm(props: EMSESPSettingsControllerFormProps)
|
||||
margin="normal"
|
||||
/>
|
||||
<TextValidator
|
||||
validators={['required', 'isNumber', 'minNumber:1', 'maxNumber:65535']}
|
||||
errorMessages={['Keep alive is required', "Must be a number", "Must be greater than 0", "Max value is 65535"]}
|
||||
validators={['required', 'isNumber', 'minNumber:0', 'maxNumber:65535']}
|
||||
errorMessages={['Keep alive is required', "Must be a number", "Must be 0 or higher (0=off)", "Max value is 65535"]}
|
||||
name="syslog_mark_interval"
|
||||
label="Syslog mark interval (seconds)"
|
||||
label="Syslog Mark Interval (seconds, 0=off)"
|
||||
fullWidth
|
||||
variant="outlined"
|
||||
value={data.publish_time}
|
||||
value={data.syslog_mark_interval}
|
||||
type="number"
|
||||
onChange={handleValueChange('syslog_mark_interval')}
|
||||
margin="normal"
|
||||
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
List,
|
||||
ListItem,
|
||||
ListItemAvatar,
|
||||
ListItemText,
|
||||
ListItemText
|
||||
} from "@material-ui/core";
|
||||
|
||||
import BuildIcon from "@material-ui/icons/Build";
|
||||
@@ -81,7 +81,7 @@ class EMSESPStatusForm extends Component<EMSESPStatusFormProps> {
|
||||
</HighlightAvatar>
|
||||
</ListItemAvatar>
|
||||
<ListItemText
|
||||
primary="MQTT Publish failures"
|
||||
primary="MQTT Publish Errors"
|
||||
secondary={data.mqtt_fails}
|
||||
/>
|
||||
</ListItem>
|
||||
@@ -125,7 +125,7 @@ class EMSESPStatusForm extends Component<EMSESPStatusFormProps> {
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell component="th" scope="row">
|
||||
(Tx) Send failures
|
||||
(Tx) Send Errors
|
||||
</TableCell>
|
||||
<TableCell align="center">{data.tx_errors}</TableCell>
|
||||
</TableRow>
|
||||
|
||||
@@ -30,3 +30,17 @@ export interface EMSESPStatus {
|
||||
uptime: string;
|
||||
free_mem: number;
|
||||
}
|
||||
|
||||
export interface Device {
|
||||
type: string;
|
||||
brand: string;
|
||||
name: string;
|
||||
deviceid: number;
|
||||
productid: number;
|
||||
version: string;
|
||||
}
|
||||
|
||||
export interface EMSESPDevices {
|
||||
devices: Device[];
|
||||
}
|
||||
|
||||
|
||||
36
src/EMSESPDevicesService.cpp
Normal file
36
src/EMSESPDevicesService.cpp
Normal file
@@ -0,0 +1,36 @@
|
||||
#include "EMSESPDevicesService.h"
|
||||
#include "emsesp.h"
|
||||
#include "mqtt.h"
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
EMSESPDevicesService::EMSESPDevicesService(AsyncWebServer * server, SecurityManager * securityManager) {
|
||||
server->on(EMSESP_DEVICES_SERVICE_PATH,
|
||||
HTTP_GET,
|
||||
securityManager->wrapRequest(std::bind(&EMSESPDevicesService::emsespDevicesService, this, std::placeholders::_1),
|
||||
AuthenticationPredicates::IS_AUTHENTICATED));
|
||||
}
|
||||
|
||||
void EMSESPDevicesService::emsespDevicesService(AsyncWebServerRequest * request) {
|
||||
AsyncJsonResponse * response = new AsyncJsonResponse(false, MAX_EMSESP_STATUS_SIZE);
|
||||
JsonObject root = response->getRoot();
|
||||
|
||||
JsonArray devices = root.createNestedArray("devices");
|
||||
|
||||
for (const auto & emsdevice : EMSESP::emsdevices) {
|
||||
if (emsdevice) {
|
||||
JsonObject deviceRoot = devices.createNestedObject();
|
||||
deviceRoot["type"] = emsdevice->device_type_name();
|
||||
deviceRoot["brand"] = emsdevice->brand_to_string();
|
||||
deviceRoot["name"] = emsdevice->name();
|
||||
deviceRoot["deviceid"] = emsdevice->device_id();
|
||||
deviceRoot["productid"] = emsdevice->product_id();
|
||||
deviceRoot["version"] = emsdevice->version();
|
||||
}
|
||||
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace emsesp
|
||||
31
src/EMSESPDevicesService.h
Normal file
31
src/EMSESPDevicesService.h
Normal file
@@ -0,0 +1,31 @@
|
||||
#ifndef EMSESPDevicesService_h
|
||||
#define EMSESPDevicesService_h
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <AsyncJson.h>
|
||||
#include <ESPAsyncWebServer.h>
|
||||
#include <SecurityManager.h>
|
||||
|
||||
#include <HttpEndpoint.h>
|
||||
#include <MqttPubSub.h>
|
||||
#include <WebSocketTxRx.h>
|
||||
|
||||
#include "EMSESPSettingsService.h"
|
||||
#include "version.h"
|
||||
|
||||
#define MAX_EMSESP_STATUS_SIZE 1024
|
||||
#define EMSESP_DEVICES_SERVICE_PATH "/rest/emsespDevices"
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
class EMSESPDevicesService {
|
||||
public:
|
||||
EMSESPDevicesService(AsyncWebServer * server, SecurityManager * securityManager);
|
||||
|
||||
private:
|
||||
void emsespDevicesService(AsyncWebServerRequest * request);
|
||||
};
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
#endif
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "EMSESPStatusService.h"
|
||||
#include "emsesp.h"
|
||||
#include "mqtt.h"
|
||||
#include "version.h"
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
|
||||
@@ -5,14 +5,7 @@
|
||||
#include <AsyncJson.h>
|
||||
#include <ESPAsyncWebServer.h>
|
||||
#include <SecurityManager.h>
|
||||
|
||||
|
||||
#include <HttpEndpoint.h>
|
||||
#include <MqttPubSub.h>
|
||||
#include <WebSocketTxRx.h>
|
||||
|
||||
#include "EMSESPSettingsService.h"
|
||||
#include "version.h"
|
||||
#include <AsyncMqttClient.h>
|
||||
|
||||
#define MAX_EMSESP_STATUS_SIZE 1024
|
||||
#define EMSESP_STATUS_SERVICE_PATH "/rest/emsespStatus"
|
||||
@@ -24,7 +17,6 @@ class EMSESPStatusService {
|
||||
EMSESPStatusService(AsyncWebServer * server, SecurityManager * securityManager, AsyncMqttClient * mqttClient);
|
||||
|
||||
private:
|
||||
EMSESPSettingsService * _emsespSettingsService;
|
||||
void emsespStatusService(AsyncWebServerRequest * request);
|
||||
AsyncMqttClient * _mqttClient;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user