mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2025-12-08 00:39:50 +03:00
Merge branch 'v2_web' of https://github.com/proddy/EMS-ESP into web
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
# Change the IP address to that of your ESP device to enable local development of the UI.
|
# Change the IP address to that of your ESP device to enable local development of the UI.
|
||||||
# Remember to also enable CORS in platformio.ini before uploading the code to the device.
|
# Remember to also enable CORS in platformio.ini before uploading the code to the device.
|
||||||
REACT_APP_HTTP_ROOT=http://10.10.10.81
|
REACT_APP_HTTP_ROOT=http://10.10.10.140
|
||||||
REACT_APP_WEB_SOCKET_ROOT=ws://10.10.10.81
|
REACT_APP_WEB_SOCKET_ROOT=ws://10.10.10.140
|
||||||
|
|||||||
@@ -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);
|
||||||
184
interface/src/project/EMSESPDevicesForm.tsx
Normal file
184
interface/src/project/EMSESPDevicesForm.tsx
Normal file
@@ -0,0 +1,184 @@
|
|||||||
|
import React, { Component, Fragment } from "react";
|
||||||
|
|
||||||
|
import {
|
||||||
|
Table,
|
||||||
|
TableBody,
|
||||||
|
TableCell,
|
||||||
|
TableHead,
|
||||||
|
TableRow,
|
||||||
|
List,
|
||||||
|
withWidth,
|
||||||
|
WithWidthProps,
|
||||||
|
isWidthDown,
|
||||||
|
Button,
|
||||||
|
DialogTitle, DialogContent, DialogActions, Box, Dialog, Typography
|
||||||
|
} from "@material-ui/core";
|
||||||
|
|
||||||
|
import RefreshIcon from "@material-ui/icons/Refresh";
|
||||||
|
|
||||||
|
import { redirectingAuthorizedFetch, withAuthenticatedContext, AuthenticatedContextProps } from '../authentication';
|
||||||
|
|
||||||
|
import {
|
||||||
|
RestFormProps,
|
||||||
|
FormButton,
|
||||||
|
} from "../components";
|
||||||
|
|
||||||
|
import { EMSESPDevices, Device } from "./types";
|
||||||
|
|
||||||
|
import { ENDPOINT_ROOT } from '../api';
|
||||||
|
export const SCANDEVICES_ENDPOINT = ENDPOINT_ROOT + "scanDevices";
|
||||||
|
|
||||||
|
function compareDevices(a: Device, b: Device) {
|
||||||
|
if (a.type < b.type) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (a.type > b.type) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface EMSESPDevicesFormState {
|
||||||
|
confirmScanDevices: boolean;
|
||||||
|
processing: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
type EMSESPDevicesFormProps = RestFormProps<EMSESPDevices> & AuthenticatedContextProps & WithWidthProps;
|
||||||
|
|
||||||
|
class EMSESPDevicesForm extends Component<EMSESPDevicesFormProps, EMSESPDevicesFormState> {
|
||||||
|
|
||||||
|
state: EMSESPDevicesFormState = {
|
||||||
|
confirmScanDevices: false,
|
||||||
|
processing: false
|
||||||
|
}
|
||||||
|
|
||||||
|
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 >
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderScanDevicesDialog() {
|
||||||
|
return (
|
||||||
|
<Dialog
|
||||||
|
open={this.state.confirmScanDevices}
|
||||||
|
onClose={this.onScanDevicesRejected}
|
||||||
|
>
|
||||||
|
<DialogTitle>Confirm Scan Devices</DialogTitle>
|
||||||
|
<DialogContent dividers>
|
||||||
|
Are you sure you want to scan the EMS bus for all new devices?
|
||||||
|
</DialogContent>
|
||||||
|
<DialogActions>
|
||||||
|
<Button variant="contained" onClick={this.onScanDevicesRejected} color="secondary">
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
<Button startIcon={<RefreshIcon />} variant="contained" onClick={this.onScanDevicesConfirmed} disabled={this.state.processing} color="primary" autoFocus>
|
||||||
|
Start Scan
|
||||||
|
</Button>
|
||||||
|
</DialogActions>
|
||||||
|
</Dialog>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
onScanDevices = () => {
|
||||||
|
this.setState({ confirmScanDevices: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
onScanDevicesRejected = () => {
|
||||||
|
this.setState({ confirmScanDevices: false });
|
||||||
|
}
|
||||||
|
|
||||||
|
onScanDevicesConfirmed = () => {
|
||||||
|
this.setState({ processing: true });
|
||||||
|
redirectingAuthorizedFetch(SCANDEVICES_ENDPOINT, { method: 'POST' })
|
||||||
|
.then(response => {
|
||||||
|
if (response.status === 200) {
|
||||||
|
this.props.enqueueSnackbar("Device scan is starting...", { variant: 'info' });
|
||||||
|
this.setState({ processing: false, confirmScanDevices: false });
|
||||||
|
} else {
|
||||||
|
throw Error("Invalid status code: " + response.status);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
this.props.enqueueSnackbar(error.message || "Problem with scan", { variant: 'error' });
|
||||||
|
this.setState({ processing: false, confirmScanDevices: false });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<Fragment>
|
||||||
|
<List>{this.createListItems()}</List>
|
||||||
|
|
||||||
|
<Box display="flex" flexWrap="wrap">
|
||||||
|
<Box flexGrow={1} padding={1}>
|
||||||
|
<FormButton startIcon={<RefreshIcon />} variant="contained" color="secondary" onClick={this.props.loadData}>
|
||||||
|
Refresh
|
||||||
|
</FormButton>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Box flexWrap="none" padding={1} whiteSpace="nowrap">
|
||||||
|
<FormButton startIcon={<RefreshIcon />} variant="contained" color="primary" onClick={this.onScanDevices}>
|
||||||
|
Scan Devices
|
||||||
|
</FormButton>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
{this.renderScanDevicesDialog()}
|
||||||
|
</Fragment>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export default withAuthenticatedContext(withWidth()(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}
|
||||||
@@ -129,7 +129,7 @@ function EMSESPSettingsControllerForm(props: EMSESPSettingsControllerFormProps)
|
|||||||
label="Shower Alert"
|
label="Shower Alert"
|
||||||
/>
|
/>
|
||||||
<SelectValidator name="syslog_level"
|
<SelectValidator name="syslog_level"
|
||||||
label="Syslog log level"
|
label="Syslog Log Level"
|
||||||
value={data.syslog_level}
|
value={data.syslog_level}
|
||||||
fullWidth
|
fullWidth
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
@@ -155,9 +155,9 @@ function EMSESPSettingsControllerForm(props: EMSESPSettingsControllerFormProps)
|
|||||||
/>
|
/>
|
||||||
<TextValidator
|
<TextValidator
|
||||||
validators={['required', 'isNumber', 'minNumber:0', 'maxNumber:65535']}
|
validators={['required', 'isNumber', 'minNumber:0', 'maxNumber:65535']}
|
||||||
errorMessages={['Mark intervall is required', "Must be a number", "0 means off", "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.syslog_mark_interval}
|
value={data.syslog_mark_interval}
|
||||||
|
|||||||
@@ -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
|
||||||
30
src/EMSESPDevicesService.h
Normal file
30
src/EMSESPDevicesService.h
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
#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 "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
|
||||||
20
src/EMSESPScanDevicesService.cpp
Normal file
20
src/EMSESPScanDevicesService.cpp
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
#include <EMSESPScanDevicesService.h>
|
||||||
|
|
||||||
|
#include "emsesp.h"
|
||||||
|
|
||||||
|
namespace emsesp {
|
||||||
|
|
||||||
|
EMSESPScanDevicesService::EMSESPScanDevicesService(AsyncWebServer * server, SecurityManager * securityManager) {
|
||||||
|
server->on(SCAN_DEVICES_SERVICE_PATH,
|
||||||
|
HTTP_POST,
|
||||||
|
securityManager->wrapRequest(std::bind(&EMSESPScanDevicesService::scan_devices, this, std::placeholders::_1), AuthenticationPredicates::IS_ADMIN));
|
||||||
|
}
|
||||||
|
|
||||||
|
void EMSESPScanDevicesService::scan_devices(AsyncWebServerRequest * request) {
|
||||||
|
request->onDisconnect([]() {
|
||||||
|
EMSESP::send_read_request(EMSdevice::EMS_TYPE_UBADevices, EMSdevice::EMS_DEVICE_ID_BOILER);
|
||||||
|
});
|
||||||
|
request->send(200);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace emsesp
|
||||||
21
src/EMSESPScanDevicesService.h
Normal file
21
src/EMSESPScanDevicesService.h
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
#ifndef EMSESPScanDevicesService_h
|
||||||
|
#define EMSESPScanDevicesService_h
|
||||||
|
|
||||||
|
#include <ESPAsyncWebServer.h>
|
||||||
|
#include <SecurityManager.h>
|
||||||
|
|
||||||
|
#define SCAN_DEVICES_SERVICE_PATH "/rest/scanDevices"
|
||||||
|
|
||||||
|
namespace emsesp {
|
||||||
|
|
||||||
|
class EMSESPScanDevicesService {
|
||||||
|
public:
|
||||||
|
EMSESPScanDevicesService(AsyncWebServer * server, SecurityManager * securityManager);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void scan_devices(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();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -47,20 +47,6 @@ EMSESPShell::EMSESPShell()
|
|||||||
|
|
||||||
void EMSESPShell::started() {
|
void EMSESPShell::started() {
|
||||||
logger().log(LogLevel::INFO, LogFacility::CONSOLE, F("User session opened on console %s"), console_name().c_str());
|
logger().log(LogLevel::INFO, LogFacility::CONSOLE, F("User session opened on console %s"), console_name().c_str());
|
||||||
|
|
||||||
// set console name
|
|
||||||
EMSESP::esp8266React.getWiFiSettingsService()->read([&](WiFiSettings & wifiSettings) { console_hostname_ = wifiSettings.hostname.c_str(); });
|
|
||||||
|
|
||||||
if (console_hostname_.empty()) {
|
|
||||||
console_hostname_.resize(16, '\0');
|
|
||||||
|
|
||||||
|
|
||||||
#if defined(ESP8266)
|
|
||||||
snprintf_P(&console_hostname_[0], console_hostname_.capacity() + 1, PSTR("esp8266"));
|
|
||||||
#else
|
|
||||||
snprintf_P(&console_hostname_[0], console_hostname_.capacity() + 1, PSTR("esp32"));
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void EMSESPShell::stopped() {
|
void EMSESPShell::stopped() {
|
||||||
@@ -76,6 +62,7 @@ void EMSESPShell::stopped() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// show welcome banner
|
// show welcome banner
|
||||||
|
// this is one of the first functions called when the shell is started
|
||||||
void EMSESPShell::display_banner() {
|
void EMSESPShell::display_banner() {
|
||||||
println();
|
println();
|
||||||
printfln(F("┌──────────────────────────────────────────┐"));
|
printfln(F("┌──────────────────────────────────────────┐"));
|
||||||
@@ -86,6 +73,18 @@ void EMSESPShell::display_banner() {
|
|||||||
printfln(F("└──────────────────────────────────────────┘"));
|
printfln(F("└──────────────────────────────────────────┘"));
|
||||||
println();
|
println();
|
||||||
|
|
||||||
|
// set console name
|
||||||
|
EMSESP::esp8266React.getWiFiSettingsService()->read([&](WiFiSettings & wifiSettings) { console_hostname_ = wifiSettings.hostname.c_str(); });
|
||||||
|
|
||||||
|
if (console_hostname_.empty()) {
|
||||||
|
console_hostname_.resize(16, '\0');
|
||||||
|
#if defined(ESP8266)
|
||||||
|
snprintf_P(&console_hostname_[0], console_hostname_.capacity() + 1, PSTR("esp8266"));
|
||||||
|
#else
|
||||||
|
snprintf_P(&console_hostname_[0], console_hostname_.capacity() + 1, PSTR("esp32"));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
// load the list of commands
|
// load the list of commands
|
||||||
add_console_commands();
|
add_console_commands();
|
||||||
|
|
||||||
@@ -197,7 +196,7 @@ void EMSESPShell::add_console_commands() {
|
|||||||
if (arguments.size() == 0) {
|
if (arguments.size() == 0) {
|
||||||
EMSESP::send_read_request(EMSdevice::EMS_TYPE_UBADevices, EMSdevice::EMS_DEVICE_ID_BOILER);
|
EMSESP::send_read_request(EMSdevice::EMS_TYPE_UBADevices, EMSdevice::EMS_DEVICE_ID_BOILER);
|
||||||
} else {
|
} else {
|
||||||
shell.printfln(F("Performing a deep scan by pinging our device library..."));
|
shell.printfln(F("Performing a deep scan..."));
|
||||||
std::vector<uint8_t> Device_Ids;
|
std::vector<uint8_t> Device_Ids;
|
||||||
|
|
||||||
Device_Ids.push_back(0x08); // Boilers - 0x08
|
Device_Ids.push_back(0x08); // Boilers - 0x08
|
||||||
|
|||||||
@@ -239,16 +239,18 @@ void Solar::process_SM100Energy(std::shared_ptr<const Telegram> telegram) {
|
|||||||
void Solar::process_ISM1StatusMessage(std::shared_ptr<const Telegram> telegram) {
|
void Solar::process_ISM1StatusMessage(std::shared_ptr<const Telegram> telegram) {
|
||||||
telegram->read_value(collectorTemp_, 4); // Collector Temperature
|
telegram->read_value(collectorTemp_, 4); // Collector Temperature
|
||||||
telegram->read_value(bottomTemp_, 6); // Temperature Bottom of Solar Boiler
|
telegram->read_value(bottomTemp_, 6); // Temperature Bottom of Solar Boiler
|
||||||
|
|
||||||
uint16_t Wh = 0xFFFF;
|
uint16_t Wh = 0xFFFF;
|
||||||
telegram->read_value(Wh, 2); // Solar Energy produced in last hour only ushort, is not * 10
|
telegram->read_value(Wh, 2); // Solar Energy produced in last hour only ushort, is not * 10
|
||||||
if (Wh != 0xFFFF) {
|
if (Wh != 0xFFFF) {
|
||||||
energyLastHour_ = Wh * 10; // set to *10
|
energyLastHour_ = Wh * 10; // set to *10
|
||||||
}
|
}
|
||||||
|
|
||||||
telegram->read_bitvalue(pump_, 8, 0); // Solar pump on (1) or off (0)
|
telegram->read_bitvalue(pump_, 8, 0); // Solar pump on (1) or off (0)
|
||||||
telegram->read_value(pumpWorkMin_, 10, 3); // force to 3 bytes
|
telegram->read_value(pumpWorkMin_, 10, 3); // force to 3 bytes
|
||||||
|
|
||||||
telegram->read_bitvalue(tankHeated_, 9, 2); // issue #422
|
telegram->read_bitvalue(collectorOnOff_, 9, 0); // collector on/off
|
||||||
telegram->read_bitvalue(collectorOnOff_, 9, 0);
|
telegram->read_bitvalue(tankHeated_, 9, 2); // tank full
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -82,6 +82,10 @@ class EMSdevice {
|
|||||||
version_ = version;
|
version_ = version;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline std::string version() const {
|
||||||
|
return version_;
|
||||||
|
}
|
||||||
|
|
||||||
inline void brand(uint8_t brand) {
|
inline void brand(uint8_t brand) {
|
||||||
brand_ = brand;
|
brand_ = brand;
|
||||||
}
|
}
|
||||||
@@ -94,6 +98,10 @@ class EMSdevice {
|
|||||||
name_ = name;
|
name_ = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline std::string name() const {
|
||||||
|
return name_;
|
||||||
|
}
|
||||||
|
|
||||||
std::string brand_to_string() const;
|
std::string brand_to_string() const;
|
||||||
static uint8_t decode_brand(uint8_t value);
|
static uint8_t decode_brand(uint8_t value);
|
||||||
|
|
||||||
|
|||||||
@@ -38,6 +38,10 @@ EMSESPSettingsService EMSESP::emsespSettingsService = EMSESPSettingsService(&web
|
|||||||
EMSESPStatusService EMSESP::emsespStatusService =
|
EMSESPStatusService EMSESP::emsespStatusService =
|
||||||
EMSESPStatusService(&webServer, EMSESP::esp8266React.getSecurityManager(), EMSESP::esp8266React.getMqttClient());
|
EMSESPStatusService(&webServer, EMSESP::esp8266React.getSecurityManager(), EMSESP::esp8266React.getMqttClient());
|
||||||
|
|
||||||
|
EMSESPDevicesService EMSESP::emsespDevicesService = EMSESPDevicesService(&webServer, EMSESP::esp8266React.getSecurityManager());
|
||||||
|
|
||||||
|
EMSESPScanDevicesService EMSESP::emsespScanDevicesService = EMSESPScanDevicesService(&webServer, EMSESP::esp8266React.getSecurityManager());
|
||||||
|
|
||||||
std::vector<std::unique_ptr<EMSdevice>> EMSESP::emsdevices; // array of all the detected EMS devices
|
std::vector<std::unique_ptr<EMSdevice>> EMSESP::emsdevices; // array of all the detected EMS devices
|
||||||
std::vector<emsesp::EMSESP::Device_record> EMSESP::device_library_; // libary of all our known EMS devices so far
|
std::vector<emsesp::EMSESP::Device_record> EMSESP::device_library_; // libary of all our known EMS devices so far
|
||||||
|
|
||||||
@@ -60,7 +64,7 @@ bool EMSESP::tap_water_active_ = false; /
|
|||||||
uint32_t EMSESP::last_fetch_ = 0;
|
uint32_t EMSESP::last_fetch_ = 0;
|
||||||
|
|
||||||
// for a specific EMS device go and request data values
|
// for a specific EMS device go and request data values
|
||||||
// or if device_id is 0 it will fetch from all known devices
|
// or if device_id is 0 it will fetch from all our known and active devices
|
||||||
void EMSESP::fetch_device_values(const uint8_t device_id) {
|
void EMSESP::fetch_device_values(const uint8_t device_id) {
|
||||||
for (const auto & emsdevice : emsdevices) {
|
for (const auto & emsdevice : emsdevices) {
|
||||||
if (emsdevice) {
|
if (emsdevice) {
|
||||||
@@ -103,13 +107,16 @@ void EMSESP::watch_id(uint16_t watch_id) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// change the tx_mode
|
||||||
|
// resets all counters and bumps the UART
|
||||||
void EMSESP::reset_tx(uint8_t const tx_mode) {
|
void EMSESP::reset_tx(uint8_t const tx_mode) {
|
||||||
txservice_.telegram_read_count(0);
|
txservice_.telegram_read_count(0);
|
||||||
txservice_.telegram_write_count(0);
|
txservice_.telegram_write_count(0);
|
||||||
txservice_.telegram_fail_count(0);
|
txservice_.telegram_fail_count(0);
|
||||||
if (tx_mode) {
|
if (tx_mode) {
|
||||||
EMSuart::stop();
|
EMSuart::stop();
|
||||||
EMSuart::start(tx_mode); // reset the UART
|
EMSuart::start(tx_mode);
|
||||||
|
EMSESP::fetch_device_values();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
17
src/emsesp.h
17
src/emsesp.h
@@ -35,7 +35,9 @@
|
|||||||
|
|
||||||
#include <ESP8266React.h>
|
#include <ESP8266React.h>
|
||||||
#include "EMSESPStatusService.h"
|
#include "EMSESPStatusService.h"
|
||||||
|
#include "EMSESPDevicesService.h"
|
||||||
#include "EMSESPSettingsService.h"
|
#include "EMSESPSettingsService.h"
|
||||||
|
#include "EMSESPScanDevicesService.h"
|
||||||
|
|
||||||
#include "emsdevice.h"
|
#include "emsdevice.h"
|
||||||
#include "emsfactory.h"
|
#include "emsfactory.h"
|
||||||
@@ -133,6 +135,9 @@ class EMSESP {
|
|||||||
|
|
||||||
static bool add_device(const uint8_t device_id, const uint8_t product_id, std::string & version, const uint8_t brand);
|
static bool add_device(const uint8_t device_id, const uint8_t product_id, std::string & version, const uint8_t brand);
|
||||||
|
|
||||||
|
static std::vector<std::unique_ptr<EMSdevice>> emsdevices;
|
||||||
|
|
||||||
|
// services
|
||||||
static Mqtt mqtt_;
|
static Mqtt mqtt_;
|
||||||
static System system_;
|
static System system_;
|
||||||
static Sensors sensors_;
|
static Sensors sensors_;
|
||||||
@@ -141,9 +146,12 @@ class EMSESP {
|
|||||||
static RxService rxservice_;
|
static RxService rxservice_;
|
||||||
static TxService txservice_;
|
static TxService txservice_;
|
||||||
|
|
||||||
static ESP8266React esp8266React;
|
// web controllers
|
||||||
static EMSESPSettingsService emsespSettingsService;
|
static ESP8266React esp8266React;
|
||||||
static EMSESPStatusService emsespStatusService;
|
static EMSESPSettingsService emsespSettingsService;
|
||||||
|
static EMSESPStatusService emsespStatusService;
|
||||||
|
static EMSESPDevicesService emsespDevicesService;
|
||||||
|
static EMSESPScanDevicesService emsespScanDevicesService;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
EMSESP() = delete;
|
EMSESP() = delete;
|
||||||
@@ -165,8 +173,7 @@ class EMSESP {
|
|||||||
uint8_t flags;
|
uint8_t flags;
|
||||||
};
|
};
|
||||||
|
|
||||||
static std::vector<std::unique_ptr<EMSdevice>> emsdevices;
|
static std::vector<Device_record> device_library_;
|
||||||
static std::vector<Device_record> device_library_;
|
|
||||||
|
|
||||||
static uint8_t actual_master_thermostat_;
|
static uint8_t actual_master_thermostat_;
|
||||||
static uint16_t watch_id_;
|
static uint16_t watch_id_;
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
#define EMSESP_APP_VERSION "2.0.0_b1" // based off b32
|
#define EMSESP_APP_VERSION "2.0.0_b2" // based off a32
|
||||||
|
|||||||
Reference in New Issue
Block a user