This commit is contained in:
proddy
2021-05-14 12:45:57 +02:00
parent 15df0c0552
commit fec5ff3132
108 changed files with 3508 additions and 2455 deletions

View File

@@ -1,23 +1,24 @@
import React from 'react';
import MenuItem from '@material-ui/core/MenuItem';
type BoardProfiles = {
[name: string]: string
[name: string]: string;
};
export const BOARD_PROFILES: BoardProfiles = {
"S32": "BBQKees Gateway S32",
"E32": "BBQKees Gateway E32",
"NODEMCU": "NodeMCU 32S",
"MH-ET": "MH-ET Live D1 Mini",
"LOLIN": "Lolin D32",
"OLIMEX": "Olimex ESP32-EVB",
"TLK110": "Generic Ethernet (TLK110)",
"LAN8720": "Generic Ethernet (LAN8720)"
}
S32: 'BBQKees Gateway S32',
E32: 'BBQKees Gateway E32',
NODEMCU: 'NodeMCU 32S',
'MH-ET': 'MH-ET Live D1 Mini',
LOLIN: 'Lolin D32',
OLIMEX: 'Olimex ESP32-EVB',
TLK110: 'Generic Ethernet (TLK110)',
LAN8720: 'Generic Ethernet (LAN8720)'
};
export function boardProfileSelectItems() {
return Object.keys(BOARD_PROFILES).map(code => (
<MenuItem key={code} value={code}>{BOARD_PROFILES[code]}</MenuItem>
));
return Object.keys(BOARD_PROFILES).map((code) => (
<MenuItem key={code} value={code}>
{BOARD_PROFILES[code]}
</MenuItem>
));
}

View File

@@ -1,5 +1,5 @@
import React, { Component } from 'react';
import { Redirect, Switch, RouteComponentProps } from 'react-router-dom'
import { Component } from 'react';
import { Redirect, Switch, RouteComponentProps } from 'react-router-dom';
import { Tabs, Tab } from '@material-ui/core';
@@ -12,30 +12,43 @@ import EMSESPDevicesController from './EMSESPDevicesController';
import EMSESPHelp from './EMSESPHelp';
class EMSESP extends Component<RouteComponentProps> {
handleTabChange = (event: React.ChangeEvent<{}>, path: string) => {
handleTabChange = (path: string) => {
this.props.history.push(path);
};
render() {
return (
<MenuAppBar sectionTitle="Dashboard">
<Tabs value={this.props.match.url} onChange={this.handleTabChange} variant="fullWidth">
<Tabs
value={this.props.match.url}
onChange={(e, path) => this.handleTabChange(path)}
variant="fullWidth"
>
<Tab value={`/${PROJECT_PATH}/devices`} label="Devices & Sensors" />
<Tab value={`/${PROJECT_PATH}/status`} label="EMS Status" />
<Tab value={`/${PROJECT_PATH}/help`} label="EMS-ESP Help" />
</Tabs>
<Switch>
<AuthenticatedRoute exact path={`/${PROJECT_PATH}/devices`} component={EMSESPDevicesController} />
<AuthenticatedRoute exact path={`/${PROJECT_PATH}/status`} component={EMSESPStatusController} />
<AuthenticatedRoute exact path={`/${PROJECT_PATH}/help`} component={EMSESPHelp} />
<AuthenticatedRoute
exact
path={`/${PROJECT_PATH}/devices`}
component={EMSESPDevicesController}
/>
<AuthenticatedRoute
exact
path={`/${PROJECT_PATH}/status`}
component={EMSESPStatusController}
/>
<AuthenticatedRoute
exact
path={`/${PROJECT_PATH}/help`}
component={EMSESPHelp}
/>
<Redirect to={`/${PROJECT_PATH}/devices`} />
</Switch>
</MenuAppBar>
)
);
}
}
export default EMSESP;

View File

@@ -1,30 +1,34 @@
import React, { Component } from 'react';
import { restController, RestControllerProps, RestFormLoader, SectionContent } from '../components';
import {
restController,
RestControllerProps,
RestFormLoader,
SectionContent
} from '../components';
import { ENDPOINT_ROOT } from '../api';
import EMSESPDevicesForm from './EMSESPDevicesForm';
import { EMSESPDevices } from './EMSESPtypes';
export const EMSESP_DEVICES_ENDPOINT = ENDPOINT_ROOT + "allDevices";
export const EMSESP_DEVICES_ENDPOINT = ENDPOINT_ROOT + 'allDevices';
type EMSESPDevicesControllerProps = RestControllerProps<EMSESPDevices>;
class EMSESPDevicesController extends Component<EMSESPDevicesControllerProps> {
componentDidMount() {
this.props.loadData();
}
componentDidMount() {
this.props.loadData();
}
render() {
return (
<SectionContent title="Devices & Sensors">
<RestFormLoader
{...this.props}
render={formProps => <EMSESPDevicesForm {...formProps} />}
/>
</SectionContent>
)
}
render() {
return (
<SectionContent title="Devices & Sensors">
<RestFormLoader
{...this.props}
render={(formProps) => <EMSESPDevicesForm {...formProps} />}
/>
</SectionContent>
);
}
}
export default restController(EMSESP_DEVICES_ENDPOINT, EMSESPDevicesController);

View File

@@ -1,38 +1,62 @@
import React, { Component, Fragment } from "react";
import { withStyles, Theme, createStyles } from "@material-ui/core/styles";
import React, { Component, Fragment } from 'react';
import { withStyles, Theme, createStyles } from '@material-ui/core/styles';
import {
Table, TableBody, TableCell, TableHead, TableRow, TableContainer, withWidth, WithWidthProps, isWidthDown,
Button, Tooltip, DialogTitle, DialogContent, DialogActions, Box, Dialog, Typography
} from "@material-ui/core";
Table,
TableBody,
TableCell,
TableHead,
TableRow,
TableContainer,
withWidth,
WithWidthProps,
isWidthDown,
Button,
Tooltip,
DialogTitle,
DialogContent,
DialogActions,
Box,
Dialog,
Typography
} from '@material-ui/core';
import RefreshIcon from "@material-ui/icons/Refresh";
import ListIcon from "@material-ui/icons/List";
import RefreshIcon from '@material-ui/icons/Refresh';
import ListIcon from '@material-ui/icons/List';
import IconButton from '@material-ui/core/IconButton';
import EditIcon from '@material-ui/icons/Edit';
import { redirectingAuthorizedFetch, withAuthenticatedContext, AuthenticatedContextProps } from "../authentication";
import { RestFormProps, FormButton, extractEventValue } from "../components";
import {
redirectingAuthorizedFetch,
withAuthenticatedContext,
AuthenticatedContextProps
} from '../authentication';
import { RestFormProps, FormButton, extractEventValue } from '../components';
import { EMSESPDevices, EMSESPDeviceData, Device, DeviceValue } from "./EMSESPtypes";
import {
EMSESPDevices,
EMSESPDeviceData,
Device,
DeviceValue
} from './EMSESPtypes';
import ValueForm from './ValueForm';
import { ENDPOINT_ROOT } from "../api";
import { ENDPOINT_ROOT } from '../api';
export const SCANDEVICES_ENDPOINT = ENDPOINT_ROOT + "scanDevices";
export const DEVICE_DATA_ENDPOINT = ENDPOINT_ROOT + "deviceData";
export const WRITE_VALUE_ENDPOINT = ENDPOINT_ROOT + "writeValue";
export const SCANDEVICES_ENDPOINT = ENDPOINT_ROOT + 'scanDevices';
export const DEVICE_DATA_ENDPOINT = ENDPOINT_ROOT + 'deviceData';
export const WRITE_VALUE_ENDPOINT = ENDPOINT_ROOT + 'writeValue';
const StyledTableCell = withStyles((theme: Theme) =>
createStyles({
head: {
backgroundColor: theme.palette.common.black,
color: theme.palette.common.white,
color: theme.palette.common.white
},
body: {
fontSize: 14,
},
fontSize: 14
}
})
)(TableCell);
@@ -42,8 +66,8 @@ const CustomTooltip = withStyles((theme: Theme) => ({
color: 'white',
boxShadow: theme.shadows[1],
fontSize: 11,
border: '1px solid #dadde9',
},
border: '1px solid #dadde9'
}
}))(Tooltip);
function compareDevices(a: Device, b: Device) {
@@ -64,63 +88,81 @@ interface EMSESPDevicesFormState {
devicevalue?: DeviceValue;
}
type EMSESPDevicesFormProps = RestFormProps<EMSESPDevices> & AuthenticatedContextProps & WithWidthProps;
type EMSESPDevicesFormProps = RestFormProps<EMSESPDevices> &
AuthenticatedContextProps &
WithWidthProps;
function formatTemp(t: string) {
if (t == null) {
return "n/a";
return 'n/a';
}
return t + " °C";
return t + ' °C';
}
function formatUnit(u: string) {
if (u == null) {
return u;
}
return " " + u;
return ' ' + u;
}
class EMSESPDevicesForm extends Component<EMSESPDevicesFormProps, EMSESPDevicesFormState> {
class EMSESPDevicesForm extends Component<
EMSESPDevicesFormProps,
EMSESPDevicesFormState
> {
state: EMSESPDevicesFormState = {
confirmScanDevices: false,
processing: false
};
handleValueChange = (name: keyof DeviceValue) => (event: React.ChangeEvent<HTMLInputElement>) => {
this.setState({ devicevalue: { ...this.state.devicevalue!, [name]: extractEventValue(event) } });
handleValueChange = (name: keyof DeviceValue) => (
event: React.ChangeEvent<HTMLInputElement>
) => {
this.setState({
devicevalue: {
...this.state.devicevalue!,
[name]: extractEventValue(event)
}
});
};
cancelEditingValue = () => {
this.setState({
devicevalue: undefined
});
}
};
doneEditingValue = () => {
const { devicevalue } = this.state;
redirectingAuthorizedFetch(WRITE_VALUE_ENDPOINT, {
method: "POST",
method: 'POST',
body: JSON.stringify({ devicevalue: devicevalue }),
headers: {
"Content-Type": "application/json",
},
'Content-Type': 'application/json'
}
})
.then((response) => {
if (response.status === 200) {
this.props.enqueueSnackbar("Write command sent to device", { variant: "success" });
this.props.enqueueSnackbar('Write command sent to device', {
variant: 'success'
});
} else if (response.status === 204) {
this.props.enqueueSnackbar("Write command failed", { variant: "error" });
this.props.enqueueSnackbar('Write command failed', {
variant: 'error'
});
} else if (response.status === 403) {
this.props.enqueueSnackbar("Write access denied", { variant: "error" });
this.props.enqueueSnackbar('Write access denied', {
variant: 'error'
});
} else {
throw Error("Unexpected response code: " + response.status);
throw Error('Unexpected response code: ' + response.status);
}
})
.catch((error) => {
this.props.enqueueSnackbar(
error.message || "Problem writing value", { variant: "error" }
);
this.props.enqueueSnackbar(error.message || 'Problem writing value', {
variant: 'error'
});
});
if (devicevalue) {
@@ -128,20 +170,19 @@ class EMSESPDevicesForm extends Component<EMSESPDevicesFormProps, EMSESPDevicesF
devicevalue: undefined
});
}
};
sendCommand = (i: any) => {
sendCommand = (i: number) => {
this.setState({
devicevalue: {
id: this.state.selectedDevice!,
data: this.state.deviceData?.data[i]!,
uom: this.state.deviceData?.data[i + 1]!,
name: this.state.deviceData?.data[i + 2]!,
cmd: this.state.deviceData?.data[i + 3]!,
cmd: this.state.deviceData?.data[i + 3]!
}
});
}
};
noDevices = () => {
return this.props.data.devices.length === 0;
@@ -166,22 +207,41 @@ class EMSESPDevicesForm extends Component<EMSESPDevicesFormProps, EMSESPDevicesF
{!this.noDevices() && (
<Table
size="small"
padding={isWidthDown("xs", width!) ? "none" : "default"}
padding={isWidthDown('xs', width!) ? 'none' : 'default'}
>
<TableBody>
{data.devices.sort(compareDevices).map((device) => (
<TableRow hover key={device.id} onClick={() => this.handleRowClick(device)}>
<TableRow
hover
key={device.id}
onClick={() => this.handleRowClick(device)}
>
<TableCell>
<CustomTooltip
title={"DeviceID:0x" + ("00" + device.deviceid.toString(16).toUpperCase()).slice(-2) + " ProductID:" + device.productid + " Version:" + device.version}
title={
'DeviceID:0x' +
(
'00' + device.deviceid.toString(16).toUpperCase()
).slice(-2) +
' ProductID:' +
device.productid +
' Version:' +
device.version
}
placement="right-end"
>
<Button startIcon={<ListIcon />} size="small" variant="outlined">
<Button
startIcon={<ListIcon />}
size="small"
variant="outlined"
>
{device.type}
</Button>
</CustomTooltip>
</TableCell>
<TableCell align="right">{device.brand + " " + device.name} </TableCell>
<TableCell align="right">
{device.brand + ' ' + device.name}{' '}
</TableCell>
</TableRow>
))}
</TableBody>
@@ -191,10 +251,13 @@ class EMSESPDevicesForm extends Component<EMSESPDevicesFormProps, EMSESPDevicesF
<Box
bgcolor="error.main"
color="error.contrastText"
p={2} mt={2} mb={2}
p={2}
mt={2}
mb={2}
>
<Typography variant="body1">
No EMS devices found. Check the connections and for possible Tx errors.
No EMS devices found. Check the connections and for possible Tx
errors.
</Typography>
</Box>
)}
@@ -255,14 +318,25 @@ class EMSESPDevicesForm extends Component<EMSESPDevicesFormProps, EMSESPDevicesF
>
<DialogTitle>Confirm Scan Devices</DialogTitle>
<DialogContent dividers>
Are you sure you want to initiate a scan on the EMS bus for all new devices?
Are you sure you want to initiate a scan on the EMS bus for all new
devices?
</DialogContent>
<DialogActions>
<Button variant="contained" onClick={this.onScanDevicesRejected} color="secondary">
<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>
startIcon={<RefreshIcon />}
variant="contained"
onClick={this.onScanDevicesConfirmed}
disabled={this.state.processing}
color="primary"
autoFocus
>
Start Scan
</Button>
</DialogActions>
@@ -283,17 +357,17 @@ class EMSESPDevicesForm extends Component<EMSESPDevicesFormProps, EMSESPDevicesF
redirectingAuthorizedFetch(SCANDEVICES_ENDPOINT)
.then((response) => {
if (response.status === 200) {
this.props.enqueueSnackbar("Device scan is starting...", {
variant: "info",
this.props.enqueueSnackbar('Device scan is starting...', {
variant: 'info'
});
this.setState({ processing: false, confirmScanDevices: false });
} else {
throw Error("Invalid status code: " + response.status);
throw Error('Invalid status code: ' + response.status);
}
})
.catch((error) => {
this.props.enqueueSnackbar(error.message || "Problem with scan", {
variant: "error",
this.props.enqueueSnackbar(error.message || 'Problem with scan', {
variant: 'error'
});
this.setState({ processing: false, confirmScanDevices: false });
});
@@ -302,25 +376,25 @@ class EMSESPDevicesForm extends Component<EMSESPDevicesFormProps, EMSESPDevicesF
handleRowClick = (device: any) => {
this.setState({ selectedDevice: device.id, deviceData: undefined });
redirectingAuthorizedFetch(DEVICE_DATA_ENDPOINT, {
method: "POST",
method: 'POST',
body: JSON.stringify({ id: device.id }),
headers: {
"Content-Type": "application/json",
},
'Content-Type': 'application/json'
}
})
.then((response) => {
if (response.status === 200) {
return response.json();
}
throw Error("Unexpected response code: " + response.status);
throw Error('Unexpected response code: ' + response.status);
})
.then((json) => {
this.setState({ deviceData: json });
})
.catch((error) => {
this.props.enqueueSnackbar(
error.message || "Problem getting device data",
{ variant: "error" }
error.message || 'Problem getting device data',
{ variant: 'error' }
);
this.setState({ deviceData: undefined });
});
@@ -351,10 +425,9 @@ class EMSESPDevicesForm extends Component<EMSESPDevicesFormProps, EMSESPDevicesF
<TableContainer>
<Table
size="small"
padding={isWidthDown("xs", width!) ? "none" : "default"}
padding={isWidthDown('xs', width!) ? 'none' : 'default'}
>
<TableHead>
</TableHead>
<TableHead></TableHead>
<TableBody>
{deviceData.data.map((item, i) => {
if (i % 4) {
@@ -362,19 +435,30 @@ class EMSESPDevicesForm extends Component<EMSESPDevicesFormProps, EMSESPDevicesF
} else {
return (
<TableRow hover key={i}>
<TableCell padding="checkbox" style={{ width: 18 }} >
<TableCell padding="checkbox" style={{ width: 18 }}>
{deviceData.data[i + 3] && me.admin && (
<CustomTooltip title="change value" placement="left-end"
<CustomTooltip
title="change value"
placement="left-end"
>
<IconButton edge="start" size="small" aria-label="Edit"
onClick={() => this.sendCommand(i)}>
<IconButton
edge="start"
size="small"
aria-label="Edit"
onClick={() => this.sendCommand(i)}
>
<EditIcon color="primary" fontSize="small" />
</IconButton>
</CustomTooltip>
)}
</TableCell>
<TableCell padding="none" component="th" scope="row">{deviceData.data[i + 2]}</TableCell>
<TableCell padding="none" align="right">{deviceData.data[i]}{formatUnit(deviceData.data[i + 1])}</TableCell>
<TableCell padding="none" component="th" scope="row">
{deviceData.data[i + 2]}
</TableCell>
<TableCell padding="none" align="right">
{deviceData.data[i]}
{formatUnit(deviceData.data[i + 1])}
</TableCell>
</TableRow>
);
}
@@ -390,7 +474,7 @@ class EMSESPDevicesForm extends Component<EMSESPDevicesFormProps, EMSESPDevicesF
</Typography>
</Box>
)}
</Fragment >
</Fragment>
);
}
@@ -405,26 +489,34 @@ class EMSESPDevicesForm extends Component<EMSESPDevicesFormProps, EMSESPDevicesF
<br></br>
<Box display="flex" flexWrap="wrap">
<Box flexGrow={1} padding={1}>
<FormButton startIcon={<RefreshIcon />} variant="contained" color="secondary" onClick={this.props.loadData} >
<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" onClick={this.onScanDevices} >
<FormButton
startIcon={<RefreshIcon />}
variant="contained"
onClick={this.onScanDevices}
>
Scan Devices
</FormButton>
</Box>
</Box>
{this.renderScanDevicesDialog()}
{
devicevalue &&
{devicevalue && (
<ValueForm
devicevalue={devicevalue}
onDoneEditing={this.doneEditingValue}
onCancelEditing={this.cancelEditingValue}
handleValueChange={this.handleValueChange}
/>
}
)}
</Fragment>
);
}

View File

@@ -1,85 +1,110 @@
import React, { Component } from 'react';
import { Typography, Box, List, ListItem, ListItemText, Link, ListItemAvatar } from '@material-ui/core';
import {
Typography,
Box,
List,
ListItem,
ListItemText,
Link,
ListItemAvatar
} from '@material-ui/core';
import { SectionContent } from '../components';
import CommentIcon from "@material-ui/icons/CommentTwoTone";
import MenuBookIcon from "@material-ui/icons/MenuBookTwoTone";
import GitHubIcon from "@material-ui/icons/GitHub";
import StarIcon from "@material-ui/icons/Star";
import ImportExportIcon from "@material-ui/icons/ImportExport";
import BugReportIcon from "@material-ui/icons/BugReportTwoTone";
import CommentIcon from '@material-ui/icons/CommentTwoTone';
import MenuBookIcon from '@material-ui/icons/MenuBookTwoTone';
import GitHubIcon from '@material-ui/icons/GitHub';
import StarIcon from '@material-ui/icons/Star';
import ImportExportIcon from '@material-ui/icons/ImportExport';
import BugReportIcon from '@material-ui/icons/BugReportTwoTone';
export const WebAPISystemSettings = window.location.origin + "/api/system/settings";
export const WebAPISystemInfo = window.location.origin + "/api/system/info";
export const WebAPISystemSettings =
window.location.origin + '/api/system/settings';
export const WebAPISystemInfo = window.location.origin + '/api/system/info';
class EMSESPHelp extends Component {
render() {
return (
<SectionContent title="EMS-ESP Help" titleGutter>
<List>
<ListItem>
<ListItemAvatar>
<MenuBookIcon />
</ListItemAvatar>
<ListItemText>
For the latest news and updates go to the{' '}
<Link href="https://emsesp.github.io/docs" color="primary">
{'official documentation'}&nbsp;website
</Link>
</ListItemText>
</ListItem>
render() {
return (
<SectionContent title='EMS-ESP Help' titleGutter>
<ListItem>
<ListItemAvatar>
<CommentIcon />
</ListItemAvatar>
<ListItemText>
For live community chat join our{' '}
<Link href="https://discord.gg/3J3GgnzpyT" color="primary">
{'Discord'}&nbsp;server
</Link>
</ListItemText>
</ListItem>
<List>
<ListItem>
<ListItemAvatar>
<GitHubIcon />
</ListItemAvatar>
<ListItemText>
To report an issue or feature request go to{' '}
<Link
href="https://github.com/emsesp/EMS-ESP32/issues/new/choose"
color="primary"
>
{'click here'}
</Link>
</ListItemText>
</ListItem>
<ListItem>
<ListItemAvatar>
<MenuBookIcon />
</ListItemAvatar>
<ListItemText>
For the latest news and updates go to the <Link href="https://emsesp.github.io/docs" color="primary">{'official documentation'}&nbsp;website</Link>
</ListItemText>
</ListItem>
<ListItem>
<ListItemAvatar>
<ImportExportIcon />
</ListItemAvatar>
<ListItemText>
To export your system settings{' '}
<Link target="_blank" href={WebAPISystemSettings} color="primary">
{'click here'}
</Link>
</ListItemText>
</ListItem>
<ListItem>
<ListItemAvatar>
<CommentIcon />
</ListItemAvatar>
<ListItemText>
For live community chat join our <Link href="https://discord.gg/3J3GgnzpyT" color="primary">{'Discord'}&nbsp;server</Link>
</ListItemText>
</ListItem>
<ListItem>
<ListItemAvatar>
<GitHubIcon />
</ListItemAvatar>
<ListItemText>
To report an issue or feature request go to <Link href="https://github.com/emsesp/EMS-ESP32/issues/new/choose" color="primary">{'click here'}</Link>
</ListItemText>
</ListItem>
<ListItem>
<ListItemAvatar>
<ImportExportIcon />
</ListItemAvatar>
<ListItemText>
To export your system settings <Link target="_blank" href={WebAPISystemSettings} color="primary">{'click here'}</Link>
</ListItemText>
</ListItem>
<ListItem>
<ListItemAvatar>
<BugReportIcon />
</ListItemAvatar>
<ListItemText>
To export the current status of EMS-ESP <Link target="_blank" href={WebAPISystemInfo} color="primary">{'click here'}</Link>
</ListItemText>
</ListItem>
</List>
<Box bgcolor="info.main" border={1} p={3} mt={1} mb={0}>
<Typography variant="h6">
EMS-ESP is free and open-source.
<br></br>Please consider supporting this project by giving it a <StarIcon style={{ color: '#fdff3a' }} /> on our <Link href="https://github.com/emsesp/EMS-ESP32" color="primary">{'GitHub page'}</Link>.
</Typography>
</Box>
<br></br>
</SectionContent>
)
}
<ListItem>
<ListItemAvatar>
<BugReportIcon />
</ListItemAvatar>
<ListItemText>
To export the current status of EMS-ESP{' '}
<Link target="_blank" href={WebAPISystemInfo} color="primary">
{'click here'}
</Link>
</ListItemText>
</ListItem>
</List>
<Box bgcolor="info.main" border={1} p={3} mt={1} mb={0}>
<Typography variant="h6">
EMS-ESP is free and open-source.
<br></br>Please consider supporting this project by giving it a{' '}
<StarIcon style={{ color: '#fdff3a' }} /> on our{' '}
<Link href="https://github.com/emsesp/EMS-ESP32" color="primary">
{'GitHub page'}
</Link>
.
</Typography>
</Box>
<br></br>
</SectionContent>
);
}
}
export default EMSESPHelp;

View File

@@ -1,5 +1,5 @@
import React, { Component } from 'react';
import { Redirect, Switch, RouteComponentProps } from 'react-router-dom'
import { Component } from 'react';
import { Redirect, Switch, RouteComponentProps } from 'react-router-dom';
import { Tabs, Tab } from '@material-ui/core';
@@ -10,26 +10,31 @@ import { AuthenticatedRoute } from '../authentication';
import EMSESPSettingsController from './EMSESPSettingsController';
class EMSESP extends Component<RouteComponentProps> {
handleTabChange = (event: React.ChangeEvent<{}>, path: string) => {
handleTabChange = (path: string) => {
this.props.history.push(path);
};
render() {
return (
<MenuAppBar sectionTitle="Settings">
<Tabs value={this.props.match.url} onChange={this.handleTabChange} variant="fullWidth">
<Tabs
value={this.props.match.url}
onChange={(e, path) => this.handleTabChange(path)}
variant="fullWidth"
>
<Tab value={`/${PROJECT_PATH}/settings`} label="EMS-ESP Settings" />
</Tabs>
<Switch>
<AuthenticatedRoute exact path={`/${PROJECT_PATH}/settings`} component={EMSESPSettingsController} />
<AuthenticatedRoute
exact
path={`/${PROJECT_PATH}/settings`}
component={EMSESPSettingsController}
/>
<Redirect to={`/${PROJECT_PATH}/settings`} />
</Switch>
</MenuAppBar>
)
);
}
}
export default EMSESP;

View File

@@ -1,38 +1,41 @@
import React, { Component } from 'react';
// import { Container } from '@material-ui/core';
import { Component } from 'react';
import { ENDPOINT_ROOT } from '../api';
import EMSESPSettingsForm from './EMSESPSettingsForm';
import { restController, RestControllerProps, RestFormLoader, SectionContent } from '../components';
import {
restController,
RestControllerProps,
RestFormLoader,
SectionContent
} from '../components';
import { EMSESPSettings } from './EMSESPtypes';
export const EMSESP_SETTINGS_ENDPOINT = ENDPOINT_ROOT + "emsespSettings";
export const EMSESP_SETTINGS_ENDPOINT = ENDPOINT_ROOT + 'emsespSettings';
type EMSESPSettingsControllerProps = RestControllerProps<EMSESPSettings>;
class EMSESPSettingsController extends Component<EMSESPSettingsControllerProps> {
componentDidMount() {
this.props.loadData();
}
componentDidMount() {
this.props.loadData();
}
render() {
return (
// <Container maxWidth="md" disableGutters>
<SectionContent title='' titleGutter>
<RestFormLoader
{...this.props}
render={formProps => (
<EMSESPSettingsForm {...formProps} />
)}
/>
</SectionContent>
// </Container>
)
}
render() {
return (
// <Container maxWidth="md" disableGutters>
<SectionContent title="" titleGutter>
<RestFormLoader
{...this.props}
render={(formProps) => <EMSESPSettingsForm {...formProps} />}
/>
</SectionContent>
// </Container>
);
}
}
export default restController(EMSESP_SETTINGS_ENDPOINT, EMSESPSettingsController);
export default restController(
EMSESP_SETTINGS_ENDPOINT,
EMSESPSettingsController
);

View File

@@ -1,9 +1,9 @@
import React from "react";
import React from 'react';
import {
ValidatorForm,
TextValidator,
SelectValidator,
} from "react-material-ui-form-validator";
SelectValidator
} from 'react-material-ui-form-validator';
import {
Checkbox,
@@ -11,34 +11,34 @@ import {
Box,
Link,
withWidth,
WithWidthProps,
} from "@material-ui/core";
import SaveIcon from "@material-ui/icons/Save";
import MenuItem from "@material-ui/core/MenuItem";
WithWidthProps
} from '@material-ui/core';
import SaveIcon from '@material-ui/icons/Save';
import MenuItem from '@material-ui/core/MenuItem';
import Grid from "@material-ui/core/Grid";
import Grid from '@material-ui/core/Grid';
import {
redirectingAuthorizedFetch,
withAuthenticatedContext,
AuthenticatedContextProps,
} from "../authentication";
AuthenticatedContextProps
} from '../authentication';
import {
RestFormProps,
FormActions,
FormButton,
BlockFormControlLabel,
} from "../components";
BlockFormControlLabel
} from '../components';
import { isIP, optional } from "../validators";
import { isIP, optional } from '../validators';
import { EMSESPSettings } from "./EMSESPtypes";
import { EMSESPSettings } from './EMSESPtypes';
import { boardProfileSelectItems } from "./EMSESPBoardProfiles";
import { boardProfileSelectItems } from './EMSESPBoardProfiles';
import { ENDPOINT_ROOT } from "../api";
export const BOARD_PROFILE_ENDPOINT = ENDPOINT_ROOT + "boardProfile";
import { ENDPOINT_ROOT } from '../api';
export const BOARD_PROFILE_ENDPOINT = ENDPOINT_ROOT + 'boardProfile';
type EMSESPSettingsFormProps = RestFormProps<EMSESPSettings> &
AuthenticatedContextProps &
@@ -50,38 +50,38 @@ interface EMSESPSettingsFormState {
class EMSESPSettingsForm extends React.Component<EMSESPSettingsFormProps> {
state: EMSESPSettingsFormState = {
processing: false,
processing: false
};
componentDidMount() {
ValidatorForm.addValidationRule("isOptionalIP", optional(isIP));
ValidatorForm.addValidationRule('isOptionalIP', optional(isIP));
}
changeBoardProfile = (event: React.ChangeEvent<HTMLSelectElement>) => {
const { data, setData } = this.props;
setData({
...data,
board_profile: event.target.value,
board_profile: event.target.value
});
if (event.target.value === "CUSTOM") return;
if (event.target.value === 'CUSTOM') return;
this.setState({ processing: true });
redirectingAuthorizedFetch(BOARD_PROFILE_ENDPOINT, {
method: "POST",
method: 'POST',
body: JSON.stringify({ code: event.target.value }),
headers: {
"Content-Type": "application/json",
},
'Content-Type': 'application/json'
}
})
.then((response) => {
if (response.status === 200) {
return response.json();
}
throw Error("Unexpected response code: " + response.status);
throw Error('Unexpected response code: ' + response.status);
})
.then((json) => {
this.props.enqueueSnackbar("Profile loaded", { variant: "success" });
this.props.enqueueSnackbar('Profile loaded', { variant: 'success' });
setData({
...data,
led_gpio: json.led_gpio,
@@ -89,14 +89,14 @@ class EMSESPSettingsForm extends React.Component<EMSESPSettingsFormProps> {
rx_gpio: json.rx_gpio,
tx_gpio: json.tx_gpio,
pbutton_gpio: json.pbutton_gpio,
board_profile: event.target.value,
board_profile: event.target.value
});
this.setState({ processing: false });
})
.catch((error) => {
this.props.enqueueSnackbar(
error.message || "Problem fetching board profile",
{ variant: "warning" }
error.message || 'Problem fetching board profile',
{ variant: 'warning' }
);
this.setState({ processing: false });
});
@@ -108,13 +108,13 @@ class EMSESPSettingsForm extends React.Component<EMSESPSettingsFormProps> {
<ValidatorForm onSubmit={saveData}>
<Box bgcolor="info.main" p={2} mt={2} mb={2}>
<Typography variant="body1">
Adjust any of the EMS-ESP settings here. For help refer to the{" "}
Adjust any of the EMS-ESP settings here. For help refer to the{' '}
<Link
target="_blank"
href="https://emsesp.github.io/docs/#/Configure-firmware32?id=ems-esp-settings"
color="primary"
>
{"online documentation"}
{'online documentation'}
</Link>
.
</Typography>
@@ -139,7 +139,7 @@ class EMSESPSettingsForm extends React.Component<EMSESPSettingsFormProps> {
value={data.tx_mode}
fullWidth
variant="outlined"
onChange={handleValueChange("tx_mode")}
onChange={handleValueChange('tx_mode')}
margin="normal"
>
<MenuItem value={0}>Off</MenuItem>
@@ -156,7 +156,7 @@ class EMSESPSettingsForm extends React.Component<EMSESPSettingsFormProps> {
value={data.ems_bus_id}
fullWidth
variant="outlined"
onChange={handleValueChange("ems_bus_id")}
onChange={handleValueChange('ems_bus_id')}
margin="normal"
>
<MenuItem value={0x0b}>Service Key (0x0B)</MenuItem>
@@ -169,16 +169,16 @@ class EMSESPSettingsForm extends React.Component<EMSESPSettingsFormProps> {
<Grid item xs={6}>
<TextValidator
validators={[
"required",
"isNumber",
"minNumber:0",
"maxNumber:120",
'required',
'isNumber',
'minNumber:0',
'maxNumber:120'
]}
errorMessages={[
"Tx delay is required",
"Must be a number",
"Must be 0 or higher",
"Max value is 120",
'Tx delay is required',
'Must be a number',
'Must be 0 or higher',
'Max value is 120'
]}
name="tx_delay"
label="Tx start delay (seconds)"
@@ -186,7 +186,7 @@ class EMSESPSettingsForm extends React.Component<EMSESPSettingsFormProps> {
variant="outlined"
value={data.tx_delay}
type="number"
onChange={handleValueChange("tx_delay")}
onChange={handleValueChange('tx_delay')}
margin="normal"
/>
</Grid>
@@ -216,12 +216,12 @@ class EMSESPSettingsForm extends React.Component<EMSESPSettingsFormProps> {
margin="normal"
>
{boardProfileSelectItems()}
<MenuItem key={"CUSTOM"} value={"CUSTOM"}>
<MenuItem key={'CUSTOM'} value={'CUSTOM'}>
Custom...
</MenuItem>
</SelectValidator>
{data.board_profile === "CUSTOM" && (
{data.board_profile === 'CUSTOM' && (
<Grid
container
spacing={1}
@@ -232,18 +232,18 @@ class EMSESPSettingsForm extends React.Component<EMSESPSettingsFormProps> {
<Grid item xs={4}>
<TextValidator
validators={[
"required",
"isNumber",
"minNumber:0",
"maxNumber:40",
"matchRegexp:^((?!6|7|8|9|10|11|12|14|15|20|24|28|29|30|31)[0-9]*)$",
'required',
'isNumber',
'minNumber:0',
'maxNumber:40',
'matchRegexp:^((?!6|7|8|9|10|11|12|14|15|20|24|28|29|30|31)[0-9]*)$'
]}
errorMessages={[
"GPIO is required",
"Must be a number",
"Must be 0 or higher",
"Max value is 40",
"Not a valid GPIO",
'GPIO is required',
'Must be a number',
'Must be 0 or higher',
'Max value is 40',
'Not a valid GPIO'
]}
name="rx_gpio"
label="Rx GPIO"
@@ -251,25 +251,25 @@ class EMSESPSettingsForm extends React.Component<EMSESPSettingsFormProps> {
variant="outlined"
value={data.rx_gpio}
type="number"
onChange={handleValueChange("rx_gpio")}
onChange={handleValueChange('rx_gpio')}
margin="normal"
/>
</Grid>
<Grid item xs={4}>
<TextValidator
validators={[
"required",
"isNumber",
"minNumber:0",
"maxNumber:40",
"matchRegexp:^((?!6|7|8|9|10|11|12|14|15|20|24|28|29|30|31)[0-9]*)$",
'required',
'isNumber',
'minNumber:0',
'maxNumber:40',
'matchRegexp:^((?!6|7|8|9|10|11|12|14|15|20|24|28|29|30|31)[0-9]*)$'
]}
errorMessages={[
"GPIO is required",
"Must be a number",
"Must be 0 or higher",
"Max value is 40",
"Not a valid GPIO",
'GPIO is required',
'Must be a number',
'Must be 0 or higher',
'Max value is 40',
'Not a valid GPIO'
]}
name="tx_gpio"
label="Tx GPIO"
@@ -277,25 +277,25 @@ class EMSESPSettingsForm extends React.Component<EMSESPSettingsFormProps> {
variant="outlined"
value={data.tx_gpio}
type="number"
onChange={handleValueChange("tx_gpio")}
onChange={handleValueChange('tx_gpio')}
margin="normal"
/>
</Grid>
<Grid item xs={4}>
<TextValidator
validators={[
"required",
"isNumber",
"minNumber:0",
"maxNumber:40",
"matchRegexp:^((?!6|7|8|9|10|11|12|14|15|20|24|28|29|30|31)[0-9]*)$",
'required',
'isNumber',
'minNumber:0',
'maxNumber:40',
'matchRegexp:^((?!6|7|8|9|10|11|12|14|15|20|24|28|29|30|31)[0-9]*)$'
]}
errorMessages={[
"GPIO is required",
"Must be a number",
"Must be 0 or higher",
"Max value is 40",
"Not a valid GPIO",
'GPIO is required',
'Must be a number',
'Must be 0 or higher',
'Max value is 40',
'Not a valid GPIO'
]}
name="pbutton_gpio"
label="Button GPIO"
@@ -303,25 +303,25 @@ class EMSESPSettingsForm extends React.Component<EMSESPSettingsFormProps> {
variant="outlined"
value={data.pbutton_gpio}
type="number"
onChange={handleValueChange("pbutton_gpio")}
onChange={handleValueChange('pbutton_gpio')}
margin="normal"
/>
</Grid>
<Grid item xs={4}>
<TextValidator
validators={[
"required",
"isNumber",
"minNumber:0",
"maxNumber:40",
"matchRegexp:^((?!6|7|8|9|10|11|12|14|15|20|24|28|29|30|31)[0-9]*)$",
'required',
'isNumber',
'minNumber:0',
'maxNumber:40',
'matchRegexp:^((?!6|7|8|9|10|11|12|14|15|20|24|28|29|30|31)[0-9]*)$'
]}
errorMessages={[
"GPIO is required",
"Must be a number",
"Must be 0 or higher",
"Max value is 40",
"Not a valid GPIO",
'GPIO is required',
'Must be a number',
'Must be 0 or higher',
'Max value is 40',
'Not a valid GPIO'
]}
name="dallas_gpio"
label="Dallas GPIO (0=none)"
@@ -329,25 +329,25 @@ class EMSESPSettingsForm extends React.Component<EMSESPSettingsFormProps> {
variant="outlined"
value={data.dallas_gpio}
type="number"
onChange={handleValueChange("dallas_gpio")}
onChange={handleValueChange('dallas_gpio')}
margin="normal"
/>
</Grid>
<Grid item xs={4}>
<TextValidator
validators={[
"required",
"isNumber",
"minNumber:0",
"maxNumber:40",
"matchRegexp:^((?!6|7|8|9|10|11|12|14|15|20|24|28|29|30|31)[0-9]*)$",
'required',
'isNumber',
'minNumber:0',
'maxNumber:40',
'matchRegexp:^((?!6|7|8|9|10|11|12|14|15|20|24|28|29|30|31)[0-9]*)$'
]}
errorMessages={[
"GPIO is required",
"Must be a number",
"Must be 0 or higher",
"Max value is 40",
"Not a valid GPIO",
'GPIO is required',
'Must be a number',
'Must be 0 or higher',
'Max value is 40',
'Not a valid GPIO'
]}
name="led_gpio"
label="LED GPIO (0=none)"
@@ -355,7 +355,7 @@ class EMSESPSettingsForm extends React.Component<EMSESPSettingsFormProps> {
variant="outlined"
value={data.led_gpio}
type="number"
onChange={handleValueChange("led_gpio")}
onChange={handleValueChange('led_gpio')}
margin="normal"
/>
</Grid>
@@ -372,7 +372,7 @@ class EMSESPSettingsForm extends React.Component<EMSESPSettingsFormProps> {
control={
<Checkbox
checked={data.hide_led}
onChange={handleValueChange("hide_led")}
onChange={handleValueChange('hide_led')}
value="hide_led"
/>
}
@@ -385,7 +385,7 @@ class EMSESPSettingsForm extends React.Component<EMSESPSettingsFormProps> {
control={
<Checkbox
checked={data.dallas_parasite}
onChange={handleValueChange("dallas_parasite")}
onChange={handleValueChange('dallas_parasite')}
value="dallas_parasite"
/>
}
@@ -397,7 +397,7 @@ class EMSESPSettingsForm extends React.Component<EMSESPSettingsFormProps> {
control={
<Checkbox
checked={data.notoken_api}
onChange={handleValueChange("notoken_api")}
onChange={handleValueChange('notoken_api')}
value="notoken_api"
/>
}
@@ -407,7 +407,7 @@ class EMSESPSettingsForm extends React.Component<EMSESPSettingsFormProps> {
control={
<Checkbox
checked={data.analog_enabled}
onChange={handleValueChange("analog_enabled")}
onChange={handleValueChange('analog_enabled')}
value="analog_enabled"
/>
}
@@ -424,7 +424,7 @@ class EMSESPSettingsForm extends React.Component<EMSESPSettingsFormProps> {
control={
<Checkbox
checked={data.shower_timer}
onChange={handleValueChange("shower_timer")}
onChange={handleValueChange('shower_timer')}
value="shower_timer"
/>
}
@@ -434,7 +434,7 @@ class EMSESPSettingsForm extends React.Component<EMSESPSettingsFormProps> {
control={
<Checkbox
checked={data.shower_alert}
onChange={handleValueChange("shower_alert")}
onChange={handleValueChange('shower_alert')}
value="shower_alert"
/>
}
@@ -451,7 +451,7 @@ class EMSESPSettingsForm extends React.Component<EMSESPSettingsFormProps> {
control={
<Checkbox
checked={data.syslog_enabled}
onChange={handleValueChange("syslog_enabled")}
onChange={handleValueChange('syslog_enabled')}
value="syslog_enabled"
/>
}
@@ -468,30 +468,30 @@ class EMSESPSettingsForm extends React.Component<EMSESPSettingsFormProps> {
>
<Grid item xs={5}>
<TextValidator
validators={["isOptionalIP"]}
errorMessages={["Not a valid IP address"]}
validators={['isOptionalIP']}
errorMessages={['Not a valid IP address']}
name="syslog_host"
label="IP"
fullWidth
variant="outlined"
value={data.syslog_host}
onChange={handleValueChange("syslog_host")}
onChange={handleValueChange('syslog_host')}
margin="normal"
/>
</Grid>
<Grid item xs={6}>
<TextValidator
validators={[
"required",
"isNumber",
"minNumber:0",
"maxNumber:65535",
'required',
'isNumber',
'minNumber:0',
'maxNumber:65535'
]}
errorMessages={[
"Port is required",
"Must be a number",
"Must be greater than 0 ",
"Max value is 65535",
'Port is required',
'Must be a number',
'Must be greater than 0 ',
'Max value is 65535'
]}
name="syslog_port"
label="Port"
@@ -499,7 +499,7 @@ class EMSESPSettingsForm extends React.Component<EMSESPSettingsFormProps> {
variant="outlined"
value={data.syslog_port}
type="number"
onChange={handleValueChange("syslog_port")}
onChange={handleValueChange('syslog_port')}
margin="normal"
/>
</Grid>
@@ -510,7 +510,7 @@ class EMSESPSettingsForm extends React.Component<EMSESPSettingsFormProps> {
value={data.syslog_level}
fullWidth
variant="outlined"
onChange={handleValueChange("syslog_level")}
onChange={handleValueChange('syslog_level')}
margin="normal"
>
<MenuItem value={-1}>OFF</MenuItem>
@@ -524,16 +524,16 @@ class EMSESPSettingsForm extends React.Component<EMSESPSettingsFormProps> {
<Grid item xs={6}>
<TextValidator
validators={[
"required",
"isNumber",
"minNumber:0",
"maxNumber:65535",
'required',
'isNumber',
'minNumber:0',
'maxNumber:65535'
]}
errorMessages={[
"Syslog Mark is required",
"Must be a number",
"Must be 0 or higher",
"Max value is 10",
'Syslog Mark is required',
'Must be a number',
'Must be 0 or higher',
'Max value is 10'
]}
name="syslog_mark_interval"
label="Mark Interval seconds (0=off)"
@@ -541,7 +541,7 @@ class EMSESPSettingsForm extends React.Component<EMSESPSettingsFormProps> {
variant="outlined"
value={data.syslog_mark_interval}
type="number"
onChange={handleValueChange("syslog_mark_interval")}
onChange={handleValueChange('syslog_mark_interval')}
margin="normal"
/>
</Grid>
@@ -549,7 +549,7 @@ class EMSESPSettingsForm extends React.Component<EMSESPSettingsFormProps> {
control={
<Checkbox
checked={data.trace_raw}
onChange={handleValueChange("trace_raw")}
onChange={handleValueChange('trace_raw')}
value="trace_raw"
/>
}

View File

@@ -1,39 +1,39 @@
import { Theme } from '@material-ui/core'
import { EMSESPStatus, busConnectionStatus } from './EMSESPtypes'
import { Theme } from '@material-ui/core';
import { EMSESPStatus, busConnectionStatus } from './EMSESPtypes';
export const isConnected = ({ status }: EMSESPStatus) =>
status !== busConnectionStatus.BUS_STATUS_OFFLINE
status !== busConnectionStatus.BUS_STATUS_OFFLINE;
export const busStatusHighlight = ({ status }: EMSESPStatus, theme: Theme) => {
switch (status) {
case busConnectionStatus.BUS_STATUS_TX_ERRORS:
return theme.palette.warning.main
return theme.palette.warning.main;
case busConnectionStatus.BUS_STATUS_CONNECTED:
return theme.palette.success.main
return theme.palette.success.main;
case busConnectionStatus.BUS_STATUS_OFFLINE:
return theme.palette.error.main
return theme.palette.error.main;
default:
return theme.palette.warning.main
return theme.palette.warning.main;
}
}
};
export const busStatus = ({ status }: EMSESPStatus) => {
switch (status) {
case busConnectionStatus.BUS_STATUS_CONNECTED:
return 'Connected'
return 'Connected';
case busConnectionStatus.BUS_STATUS_TX_ERRORS:
return 'Tx Errors'
return 'Tx Errors';
case busConnectionStatus.BUS_STATUS_OFFLINE:
return 'Disconnected'
return 'Disconnected';
default:
return 'Unknown'
return 'Unknown';
}
}
};
export const qualityHighlight = (value: number, theme: Theme) => {
if (value >= 95) {
return theme.palette.success.main
return theme.palette.success.main;
}
return theme.palette.error.main
}
return theme.palette.error.main;
};

View File

@@ -1,30 +1,34 @@
import React, { Component } from 'react';
import { restController, RestControllerProps, RestFormLoader, SectionContent } from '../components';
import {
restController,
RestControllerProps,
RestFormLoader,
SectionContent
} from '../components';
import { ENDPOINT_ROOT } from '../api';
import EMSESPStatusForm from './EMSESPStatusForm';
import { EMSESPStatus } from './EMSESPtypes';
export const EMSESP_STATUS_ENDPOINT = ENDPOINT_ROOT + "emsespStatus";
export const EMSESP_STATUS_ENDPOINT = ENDPOINT_ROOT + 'emsespStatus';
type EMSESPStatusControllerProps = RestControllerProps<EMSESPStatus>;
class EMSESPStatusController extends Component<EMSESPStatusControllerProps> {
componentDidMount() {
this.props.loadData();
}
componentDidMount() {
this.props.loadData();
}
render() {
return (
<SectionContent title="EMS Status">
<RestFormLoader
{...this.props}
render={formProps => <EMSESPStatusForm {...formProps} />}
/>
</SectionContent>
)
}
render() {
return (
<SectionContent title="EMS Status">
<RestFormLoader
{...this.props}
render={(formProps) => <EMSESPStatusForm {...formProps} />}
/>
</SectionContent>
);
}
}
export default restController(EMSESP_STATUS_ENDPOINT, EMSESPStatusController);

View File

@@ -1,6 +1,6 @@
import React, { Component, Fragment } from "react";
import React, { Component, Fragment } from 'react';
import { WithTheme, withTheme } from "@material-ui/core/styles";
import { WithTheme, withTheme } from '@material-ui/core/styles';
import {
TableContainer,
Table,
@@ -13,35 +13,32 @@ import {
ListItemText,
withWidth,
WithWidthProps,
isWidthDown,
} from "@material-ui/core";
isWidthDown
} from '@material-ui/core';
import RefreshIcon from "@material-ui/icons/Refresh";
import DeviceHubIcon from "@material-ui/icons/DeviceHub";
import RefreshIcon from '@material-ui/icons/Refresh';
import DeviceHubIcon from '@material-ui/icons/DeviceHub';
import {
RestFormProps,
FormActions,
FormButton,
HighlightAvatar,
} from "../components";
HighlightAvatar
} from '../components';
import {
busStatus,
busStatusHighlight,
isConnected,
} from "./EMSESPStatus";
import { busStatus, busStatusHighlight, isConnected } from './EMSESPStatus';
import { EMSESPStatus } from "./EMSESPtypes";
import { EMSESPStatus } from './EMSESPtypes';
function formatNumber(num: number) {
return new Intl.NumberFormat().format(num);
}
type EMSESPStatusFormProps = RestFormProps<EMSESPStatus> & WithTheme & WithWidthProps;
type EMSESPStatusFormProps = RestFormProps<EMSESPStatus> &
WithTheme &
WithWidthProps;
class EMSESPStatusForm extends Component<EMSESPStatusFormProps> {
createListItems() {
const { data, theme, width } = this.props;
return (
@@ -52,24 +49,30 @@ class EMSESPStatusForm extends Component<EMSESPStatusFormProps> {
<DeviceHubIcon />
</HighlightAvatar>
</ListItemAvatar>
<ListItemText primary="Connection Status" secondary={busStatus(data)} />
<ListItemText
primary="Connection Status"
secondary={busStatus(data)}
/>
</ListItem>
{isConnected(data) && (
<TableContainer>
<Table size="small" padding={isWidthDown('xs', width!) ? "none" : "default"}>
<Table
size="small"
padding={isWidthDown('xs', width!) ? 'none' : 'default'}
>
<TableBody>
<TableRow>
<TableCell>
# Telegrams Received
</TableCell>
<TableCell align="right">{formatNumber(data.rx_received)}&nbsp;(quality {data.rx_quality}%)
<TableCell># Telegrams Received</TableCell>
<TableCell align="right">
{formatNumber(data.rx_received)}&nbsp;(quality{' '}
{data.rx_quality}%)
</TableCell>
</TableRow>
<TableRow>
<TableCell >
# Telegrams Sent
</TableCell >
<TableCell align="right">{formatNumber(data.tx_sent)}&nbsp;(quality {data.tx_quality}%)
<TableCell># Telegrams Sent</TableCell>
<TableCell align="right">
{formatNumber(data.tx_sent)}&nbsp;(quality {data.tx_quality}
%)
</TableCell>
</TableRow>
</TableBody>
@@ -86,7 +89,11 @@ class EMSESPStatusForm extends Component<EMSESPStatusFormProps> {
<List>{this.createListItems()}</List>
<FormActions>
<FormButton
startIcon={<RefreshIcon />} variant="contained" color="secondary" onClick={this.props.loadData}>
startIcon={<RefreshIcon />}
variant="contained"
color="secondary"
onClick={this.props.loadData}
>
Refresh
</FormButton>
</FormActions>

View File

@@ -1,72 +1,72 @@
export interface EMSESPSettings {
tx_mode: number
tx_delay: number
ems_bus_id: number
syslog_enabled: boolean
syslog_level: number
syslog_mark_interval: number
syslog_host: string
syslog_port: number
master_thermostat: number
shower_timer: boolean
shower_alert: boolean
rx_gpio: number
tx_gpio: number
dallas_gpio: number
dallas_parasite: boolean
led_gpio: number
hide_led: boolean
notoken_api: boolean
analog_enabled: boolean
pbutton_gpio: number
trace_raw: boolean
board_profile: string
tx_mode: number;
tx_delay: number;
ems_bus_id: number;
syslog_enabled: boolean;
syslog_level: number;
syslog_mark_interval: number;
syslog_host: string;
syslog_port: number;
master_thermostat: number;
shower_timer: boolean;
shower_alert: boolean;
rx_gpio: number;
tx_gpio: number;
dallas_gpio: number;
dallas_parasite: boolean;
led_gpio: number;
hide_led: boolean;
notoken_api: boolean;
analog_enabled: boolean;
pbutton_gpio: number;
trace_raw: boolean;
board_profile: string;
}
export enum busConnectionStatus {
BUS_STATUS_CONNECTED = 0,
BUS_STATUS_TX_ERRORS = 1,
BUS_STATUS_OFFLINE = 2,
BUS_STATUS_OFFLINE = 2
}
export interface EMSESPStatus {
status: busConnectionStatus
rx_received: number
tx_sent: number
rx_quality: number
tx_quality: number
status: busConnectionStatus;
rx_received: number;
tx_sent: number;
rx_quality: number;
tx_quality: number;
}
export interface Device {
id: number
type: string
brand: string
name: string
deviceid: number
productid: number
version: string
id: number;
type: string;
brand: string;
name: string;
deviceid: number;
productid: number;
version: string;
}
export interface Sensor {
no: number
id: string
temp: string
no: number;
id: string;
temp: string;
}
export interface EMSESPDevices {
devices: Device[]
sensors: Sensor[]
devices: Device[];
sensors: Sensor[];
}
export interface EMSESPDeviceData {
name: string
data: string[]
name: string;
data: string[];
}
export interface DeviceValue {
id: number
data: string
uom: string
name: string
cmd: string
id: number;
data: string;
uom: string;
name: string;
cmd: string;
}

View File

@@ -1,12 +1,15 @@
import React, { Component } from "react";
import { Link, withRouter, RouteComponentProps } from "react-router-dom";
import { Component } from 'react';
import { Link, withRouter, RouteComponentProps } from 'react-router-dom';
import { List, ListItem, ListItemIcon, ListItemText } from "@material-ui/core";
import { List, ListItem, ListItemIcon, ListItemText } from '@material-ui/core';
import TuneIcon from '@material-ui/icons/Tune';
import DashboardIcon from "@material-ui/icons/Dashboard";
import DashboardIcon from '@material-ui/icons/Dashboard';
import { withAuthenticatedContext, AuthenticatedContextProps } from '../authentication';
import {
withAuthenticatedContext,
AuthenticatedContextProps
} from '../authentication';
type ProjectProps = AuthenticatedContextProps & RouteComponentProps;
@@ -16,13 +19,28 @@ class ProjectMenu extends Component<ProjectProps> {
const path = this.props.match.url;
return (
<List>
<ListItem to='/ems-esp/' selected={path.startsWith('/ems-esp/status') || path.startsWith('/ems-esp/devices') || path.startsWith('/ems-esp/help')} button component={Link}>
<ListItem
to="/ems-esp/"
selected={
path.startsWith('/ems-esp/status') ||
path.startsWith('/ems-esp/devices') ||
path.startsWith('/ems-esp/help')
}
button
component={Link}
>
<ListItemIcon>
<DashboardIcon />
</ListItemIcon>
<ListItemText primary="Dashboard" />
</ListItem>
<ListItem to='/ems-esp/settings' selected={path.startsWith('/ems-esp/settings')} button component={Link} disabled={!authenticatedContext.me.admin}>
<ListItem
to="/ems-esp/settings"
selected={path.startsWith('/ems-esp/settings')}
button
component={Link}
disabled={!authenticatedContext.me.admin}
>
<ListItemIcon>
<TuneIcon />
</ListItemIcon>

View File

@@ -1,4 +1,4 @@
import React, { Component } from 'react';
import { Component } from 'react';
import { Redirect, Switch } from 'react-router';
import { AuthenticatedRoute } from '../authentication';
@@ -7,24 +7,32 @@ import EMSESPDashboard from './EMSESPDashboard';
import EMSESPSettings from './EMSESPSettings';
class ProjectRouting extends Component {
render() {
return (
<Switch>
<AuthenticatedRoute exact path="/ems-esp/status/*" component={EMSESPDashboard} />
<AuthenticatedRoute exact path="/ems-esp/settings" component={EMSESPSettings} />
<AuthenticatedRoute exact path="/ems-esp/*" component={EMSESPDashboard} />
{
/*
* The redirect below caters for the default project route and redirecting invalid paths.
* The "to" property must match one of the routes above for this to work correctly.
*/
}
<AuthenticatedRoute
exact
path="/ems-esp/status/*"
component={EMSESPDashboard}
/>
<AuthenticatedRoute
exact
path="/ems-esp/settings"
component={EMSESPSettings}
/>
<AuthenticatedRoute
exact
path="/ems-esp/*"
component={EMSESPDashboard}
/>
{/*
* The redirect below caters for the default project route and redirecting invalid paths.
* The "to" property must match one of the routes above for this to work correctly.
*/}
<Redirect to={`/ems-esp/status`} />
</Switch>
)
);
}
}
export default ProjectRouting;

View File

@@ -1,64 +1,100 @@
import React, { RefObject } from 'react';
import { TextValidator, ValidatorForm } from 'react-material-ui-form-validator';
import { Dialog, DialogTitle, DialogContent, DialogActions, Box, Typography } from '@material-ui/core';
import {
Dialog,
DialogTitle,
DialogContent,
DialogActions,
Box,
Typography
} from '@material-ui/core';
import { FormButton } from '../components';
import { DeviceValue } from './EMSESPtypes';
interface ValueFormProps {
devicevalue: DeviceValue;
onDoneEditing: () => void;
onCancelEditing: () => void;
handleValueChange: (data: keyof DeviceValue) => (event: React.ChangeEvent<HTMLInputElement>) => void;
devicevalue: DeviceValue;
onDoneEditing: () => void;
onCancelEditing: () => void;
handleValueChange: (
data: keyof DeviceValue
) => (event: React.ChangeEvent<HTMLInputElement>) => void;
}
class ValueForm extends React.Component<ValueFormProps> {
formRef: RefObject<any> = React.createRef();
formRef: RefObject<any> = React.createRef();
submit = () => {
this.formRef.current.submit();
};
submit = () => {
this.formRef.current.submit();
buildLabel = (devicevalue: DeviceValue) => {
if (devicevalue.uom === '' || !devicevalue.uom) {
return 'New value';
}
return 'New value (' + devicevalue.uom + ')';
};
buildLabel = (devicevalue: DeviceValue) => {
if ((devicevalue.uom === "") || (!devicevalue.uom)) {
return "New value";
}
return "New value (" + devicevalue.uom + ")";
}
render() {
const {
devicevalue,
handleValueChange,
onDoneEditing,
onCancelEditing
} = this.props;
return (
<ValidatorForm onSubmit={onDoneEditing} ref={this.formRef}>
<Dialog
maxWidth="xs"
onClose={onCancelEditing}
aria-labelledby="user-form-dialog-title"
open
>
<DialogTitle id="user-form-dialog-title">
Change the {devicevalue.name}
</DialogTitle>
<DialogContent dividers>
<TextValidator
validators={['required']}
errorMessages={['is required']}
name="data"
label={this.buildLabel(devicevalue)}
fullWidth
variant="outlined"
value={devicevalue.data}
margin="normal"
onChange={handleValueChange('data')}
/>
<Box color="warning.main" p={1} pl={0} pr={0} mt={0} mb={0}>
<Typography variant="body2">
<i>
Note: it may take a few seconds before the change is visible.
If nothing happens check the logs.
</i>
</Typography>
</Box>
</DialogContent>
render() {
const { devicevalue, handleValueChange, onDoneEditing, onCancelEditing } = this.props;
return (
<ValidatorForm onSubmit={onDoneEditing} ref={this.formRef}>
<Dialog maxWidth="xs" onClose={onCancelEditing} aria-labelledby="user-form-dialog-title" open>
<DialogTitle id="user-form-dialog-title">Change the {devicevalue.name}</DialogTitle>
<DialogContent dividers>
<TextValidator
validators={['required']}
errorMessages={['is required']}
name="data"
label={this.buildLabel(devicevalue)}
fullWidth
variant="outlined"
value={devicevalue.data}
margin="normal"
onChange={handleValueChange('data')}
/>
<Box color="warning.main" p={1} pl={0} pr={0} mt={0} mb={0}>
<Typography variant="body2">
<i>Note: it may take a few seconds before the change is visible. If nothing happens check the logs.</i>
</Typography>
</Box>
</DialogContent>
<DialogActions>
<FormButton variant="contained" color="secondary" onClick={onCancelEditing}>Cancel</FormButton>
<FormButton variant="contained" color="primary" type="submit" onClick={this.submit}>Done</FormButton>
</DialogActions>
</Dialog>
</ValidatorForm>
);
}
<DialogActions>
<FormButton
variant="contained"
color="secondary"
onClick={onCancelEditing}
>
Cancel
</FormButton>
<FormButton
variant="contained"
color="primary"
type="submit"
onClick={this.submit}
>
Done
</FormButton>
</DialogActions>
</Dialog>
</ValidatorForm>
);
}
}
export default ValueForm;