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 EMSESPSettingsController from './EMSESPSettingsController';
|
||||||
import EMSESPStatusController from './EMSESPStatusController';
|
import EMSESPStatusController from './EMSESPStatusController';
|
||||||
|
import EMSESPDevicesController from './EMSESPDevicesController';
|
||||||
|
|
||||||
class EMSESP extends Component<RouteComponentProps> {
|
class EMSESP extends Component<RouteComponentProps> {
|
||||||
|
|
||||||
@@ -21,10 +22,12 @@ class EMSESP extends Component<RouteComponentProps> {
|
|||||||
<MenuAppBar sectionTitle="EMS-ESP">
|
<MenuAppBar sectionTitle="EMS-ESP">
|
||||||
<Tabs value={this.props.match.url} onChange={this.handleTabChange} variant="fullWidth">
|
<Tabs value={this.props.match.url} onChange={this.handleTabChange} variant="fullWidth">
|
||||||
<Tab value={`/${PROJECT_PATH}/status`} label="EMS-ESP Status" />
|
<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" />
|
<Tab value={`/${PROJECT_PATH}/settings`} label="EMS-ESP Settings" />
|
||||||
</Tabs>
|
</Tabs>
|
||||||
<Switch>
|
<Switch>
|
||||||
<AuthenticatedRoute exact path={`/${PROJECT_PATH}/status`} component={EMSESPStatusController} />
|
<AuthenticatedRoute exact path={`/${PROJECT_PATH}/status`} component={EMSESPStatusController} />
|
||||||
|
<AuthenticatedRoute exact path={`/${PROJECT_PATH}/devices`} component={EMSESPDevicesController} />
|
||||||
<AuthenticatedRoute exact path={`/${PROJECT_PATH}/settings`} component={EMSESPSettingsController} />
|
<AuthenticatedRoute exact path={`/${PROJECT_PATH}/settings`} component={EMSESPSettingsController} />
|
||||||
<Redirect to={`/${PROJECT_PATH}/status`} />
|
<Redirect to={`/${PROJECT_PATH}/status`} />
|
||||||
</Switch>
|
</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']}
|
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"]}
|
errorMessages={['TX mode is required', "Must be a number", "Must be 0 or higher", "Max value is 255"]}
|
||||||
name="tx_mode"
|
name="tx_mode"
|
||||||
label="Tx mode (0=off)"
|
label="Tx Mode (0=off)"
|
||||||
fullWidth
|
fullWidth
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
value={data.tx_mode}
|
value={data.tx_mode}
|
||||||
@@ -71,10 +71,10 @@ function EMSESPSettingsControllerForm(props: EMSESPSettingsControllerFormProps)
|
|||||||
value="system_heartbeat"
|
value="system_heartbeat"
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
label="MQTT heartbeat"
|
label="MQTT Heartbeat"
|
||||||
/>
|
/>
|
||||||
<SelectValidator name="mqtt_format"
|
<SelectValidator name="mqtt_format"
|
||||||
label="MQTT format"
|
label="MQTT Format"
|
||||||
value={data.mqtt_format}
|
value={data.mqtt_format}
|
||||||
fullWidth
|
fullWidth
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
@@ -100,7 +100,7 @@ function EMSESPSettingsControllerForm(props: EMSESPSettingsControllerFormProps)
|
|||||||
validators={['required', 'isNumber', 'minNumber:1', 'maxNumber:65535']}
|
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"]}
|
errorMessages={['Keep alive is required', "Must be a number", "Must be greater than 0", "Max value is 65535"]}
|
||||||
name="publish_time"
|
name="publish_time"
|
||||||
label="MQTT Publish time (seconds)"
|
label="MQTT Publish Time (seconds)"
|
||||||
fullWidth
|
fullWidth
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
value={data.publish_time}
|
value={data.publish_time}
|
||||||
@@ -108,18 +108,6 @@ function EMSESPSettingsControllerForm(props: EMSESPSettingsControllerFormProps)
|
|||||||
onChange={handleValueChange('publish_time')}
|
onChange={handleValueChange('publish_time')}
|
||||||
margin="normal"
|
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
|
<BlockFormControlLabel
|
||||||
control={
|
control={
|
||||||
<Checkbox
|
<Checkbox
|
||||||
@@ -140,6 +128,18 @@ function EMSESPSettingsControllerForm(props: EMSESPSettingsControllerFormProps)
|
|||||||
}
|
}
|
||||||
label="Shower Alert"
|
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 &&
|
{data.syslog_level !== -1 &&
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<TextValidator
|
<TextValidator
|
||||||
@@ -154,13 +154,13 @@ function EMSESPSettingsControllerForm(props: EMSESPSettingsControllerFormProps)
|
|||||||
margin="normal"
|
margin="normal"
|
||||||
/>
|
/>
|
||||||
<TextValidator
|
<TextValidator
|
||||||
validators={['required', 'isNumber', 'minNumber:1', 'maxNumber:65535']}
|
validators={['required', 'isNumber', 'minNumber:0', 'maxNumber:65535']}
|
||||||
errorMessages={['Keep alive is required', "Must be a number", "Must be greater than 0", "Max value is 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"
|
name="syslog_mark_interval"
|
||||||
label="Syslog mark interval (seconds)"
|
label="Syslog Mark Interval (seconds, 0=off)"
|
||||||
fullWidth
|
fullWidth
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
value={data.publish_time}
|
value={data.syslog_mark_interval}
|
||||||
type="number"
|
type="number"
|
||||||
onChange={handleValueChange('syslog_mark_interval')}
|
onChange={handleValueChange('syslog_mark_interval')}
|
||||||
margin="normal"
|
margin="normal"
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import {
|
|||||||
List,
|
List,
|
||||||
ListItem,
|
ListItem,
|
||||||
ListItemAvatar,
|
ListItemAvatar,
|
||||||
ListItemText,
|
ListItemText
|
||||||
} from "@material-ui/core";
|
} from "@material-ui/core";
|
||||||
|
|
||||||
import BuildIcon from "@material-ui/icons/Build";
|
import BuildIcon from "@material-ui/icons/Build";
|
||||||
@@ -81,7 +81,7 @@ class EMSESPStatusForm extends Component<EMSESPStatusFormProps> {
|
|||||||
</HighlightAvatar>
|
</HighlightAvatar>
|
||||||
</ListItemAvatar>
|
</ListItemAvatar>
|
||||||
<ListItemText
|
<ListItemText
|
||||||
primary="MQTT Publish failures"
|
primary="MQTT Publish Errors"
|
||||||
secondary={data.mqtt_fails}
|
secondary={data.mqtt_fails}
|
||||||
/>
|
/>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
@@ -125,7 +125,7 @@ class EMSESPStatusForm extends Component<EMSESPStatusFormProps> {
|
|||||||
</TableRow>
|
</TableRow>
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<TableCell component="th" scope="row">
|
<TableCell component="th" scope="row">
|
||||||
(Tx) Send failures
|
(Tx) Send Errors
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell align="center">{data.tx_errors}</TableCell>
|
<TableCell align="center">{data.tx_errors}</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
|
|||||||
@@ -30,3 +30,17 @@ export interface EMSESPStatus {
|
|||||||
uptime: string;
|
uptime: string;
|
||||||
free_mem: number;
|
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 "EMSESPStatusService.h"
|
||||||
#include "emsesp.h"
|
#include "emsesp.h"
|
||||||
#include "mqtt.h"
|
#include "mqtt.h"
|
||||||
|
#include "version.h"
|
||||||
|
|
||||||
namespace emsesp {
|
namespace emsesp {
|
||||||
|
|
||||||
|
|||||||
@@ -5,14 +5,7 @@
|
|||||||
#include <AsyncJson.h>
|
#include <AsyncJson.h>
|
||||||
#include <ESPAsyncWebServer.h>
|
#include <ESPAsyncWebServer.h>
|
||||||
#include <SecurityManager.h>
|
#include <SecurityManager.h>
|
||||||
|
#include <AsyncMqttClient.h>
|
||||||
|
|
||||||
#include <HttpEndpoint.h>
|
|
||||||
#include <MqttPubSub.h>
|
|
||||||
#include <WebSocketTxRx.h>
|
|
||||||
|
|
||||||
#include "EMSESPSettingsService.h"
|
|
||||||
#include "version.h"
|
|
||||||
|
|
||||||
#define MAX_EMSESP_STATUS_SIZE 1024
|
#define MAX_EMSESP_STATUS_SIZE 1024
|
||||||
#define EMSESP_STATUS_SERVICE_PATH "/rest/emsespStatus"
|
#define EMSESP_STATUS_SERVICE_PATH "/rest/emsespStatus"
|
||||||
@@ -24,9 +17,8 @@ class EMSESPStatusService {
|
|||||||
EMSESPStatusService(AsyncWebServer * server, SecurityManager * securityManager, AsyncMqttClient * mqttClient);
|
EMSESPStatusService(AsyncWebServer * server, SecurityManager * securityManager, AsyncMqttClient * mqttClient);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
EMSESPSettingsService * _emsespSettingsService;
|
void emsespStatusService(AsyncWebServerRequest * request);
|
||||||
void emsespStatusService(AsyncWebServerRequest * request);
|
AsyncMqttClient * _mqttClient;
|
||||||
AsyncMqttClient * _mqttClient;
|
|
||||||
|
|
||||||
void init_mqtt();
|
void init_mqtt();
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user