Merge branch 'v2_web' of https://github.com/proddy/EMS-ESP into web

This commit is contained in:
MichaelDvP
2020-07-06 14:46:45 +02:00
10 changed files with 218 additions and 164 deletions

View File

@@ -1,11 +1,6 @@
- It's slow on ESP8266 - probably due to heap - It's slow on ESP8266 - probably due to heap
- AP not showing correct wifi SSID - AP not showing correct wifi SSID
- remove Serial.*s - remove Serial.print's
- add Boiler web page using web sockets - add Boiler web page using web sockets
- check Chrome Tab errors - check if we need to disabled UART during OTA on an ESP8266 - need to tie into the service
- check if we need to disabled UART during OTA
- fix Makefile for standalone - fix Makefile for standalone
handy git commands
- git pull --tags -f
- git pull upstream master

View File

@@ -2,7 +2,7 @@ import React, { Component } from 'react';
import { CssBaseline } from '@material-ui/core'; import { CssBaseline } from '@material-ui/core';
import { MuiThemeProvider, createMuiTheme, StylesProvider } from '@material-ui/core/styles'; import { MuiThemeProvider, createMuiTheme, StylesProvider } from '@material-ui/core/styles';
import { blueGrey, orange, red, green } from '@material-ui/core/colors'; import { yellow, orange, red, green } from '@material-ui/core/colors';
const theme = createMuiTheme({ const theme = createMuiTheme({
palette: { palette: {
@@ -14,7 +14,7 @@ const theme = createMuiTheme({
main: '#3d5afe', main: '#3d5afe',
}, },
info: { info: {
main: blueGrey[900] main: yellow[200]
}, },
warning: { warning: {
main: orange[500] main: orange[500]

View File

@@ -52,10 +52,10 @@ function EMSESPSettingsControllerForm(props: EMSESPSettingsControllerFormProps)
</Typography> </Typography>
</Box> </Box>
<TextValidator <TextValidator
validators={['required', 'isNumber', 'minNumber:1', 'maxNumber:255']} validators={['required', 'isNumber', 'minNumber:0', 'maxNumber:255']}
errorMessages={['TX mode is required', "Must be a number", "Must be greater than 0", "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" label="Tx mode (0=off)"
fullWidth fullWidth
variant="outlined" variant="outlined"
value={data.tx_mode} value={data.tx_mode}

View File

@@ -1,12 +1,13 @@
import { Theme } from '@material-ui/core'; import { Theme } from '@material-ui/core';
import { EMSESPStatus, busConnectionStatus } from './types'; import { EMSESPStatus, busConnectionStatus } from './types';
export const isConnected = ({ status }: EMSESPStatus) => status === busConnectionStatus.BUS_STATUS_CONNECTED; export const isConnected = ({ status }: EMSESPStatus) => status !== busConnectionStatus.BUS_STATUS_OFFLINE;
export const busStatusHighlight = ({ status }: EMSESPStatus, theme: Theme) => { export const busStatusHighlight = ({ status }: EMSESPStatus, theme: Theme) => {
switch (status) { switch (status) {
case busConnectionStatus.BUS_STATUS_TX_ERRORS: case busConnectionStatus.BUS_STATUS_TX_ERRORS:
return theme.palette.info.main; return theme.palette.warning.main;
case busConnectionStatus.BUS_STATUS_CONNECTED: case busConnectionStatus.BUS_STATUS_CONNECTED:
return theme.palette.success.main; return theme.palette.success.main;
case busConnectionStatus.BUS_STATUS_OFFLINE: case busConnectionStatus.BUS_STATUS_OFFLINE:
@@ -30,6 +31,7 @@ export const busStatus = ({ status }: EMSESPStatus) => {
} }
export const mqttStatusHighlight = ({ mqtt_fails }: EMSESPStatus, theme: Theme) => { export const mqttStatusHighlight = ({ mqtt_fails }: EMSESPStatus, theme: Theme) => {
if (mqtt_fails === 0) if (mqtt_fails === 0)
return theme.palette.success.main; return theme.palette.success.main;

View File

@@ -1,149 +1,161 @@
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 { Table, TableBody, TableCell, TableHead, TableRow, Avatar, Divider, List, ListItem, ListItemAvatar, ListItemText } from '@material-ui/core'; import {
Table,
TableBody,
TableCell,
TableHead,
TableRow,
Avatar,
Divider,
List,
ListItem,
ListItemAvatar,
ListItemText,
} from "@material-ui/core";
import BuildIcon from '@material-ui/icons/Build'; import BuildIcon from "@material-ui/icons/Build";
import RefreshIcon from '@material-ui/icons/Refresh'; import RefreshIcon from "@material-ui/icons/Refresh";
import DeviceHubIcon from '@material-ui/icons/DeviceHub'; import DeviceHubIcon from "@material-ui/icons/DeviceHub";
import SpeakerNotesOffIcon from '@material-ui/icons/SpeakerNotesOff'; import SpeakerNotesOffIcon from "@material-ui/icons/SpeakerNotesOff";
import TimerIcon from '@material-ui/icons/Timer'; import TimerIcon from "@material-ui/icons/Timer";
import BatteryUnknownIcon from '@material-ui/icons/BatteryUnknown'; import BatteryUnknownIcon from "@material-ui/icons/BatteryUnknown";
import { RestFormProps, FormActions, FormButton, HighlightAvatar } from '../components'; import {
import { busStatus, busStatusHighlight, isConnected, mqttStatusHighlight } from './EMSESPStatus'; RestFormProps,
FormActions,
FormButton,
HighlightAvatar,
} from "../components";
import {
busStatus,
busStatusHighlight,
isConnected,
mqttStatusHighlight,
} from "./EMSESPStatus";
import { EMSESPStatus } from './types'; import { EMSESPStatus } from "./types";
type EMSESPStatusFormProps = RestFormProps<EMSESPStatus> & WithTheme; type EMSESPStatusFormProps = RestFormProps<EMSESPStatus> & WithTheme;
class EMSESPStatusForm extends Component<EMSESPStatusFormProps> { class EMSESPStatusForm extends Component<EMSESPStatusFormProps> {
createListItems() {
const { data, theme } = this.props;
return (
<Fragment>
<ListItem>
<ListItemAvatar>
<Avatar>
<BuildIcon />
</Avatar>
</ListItemAvatar>
<ListItemText primary="Firmware Version" secondary={data.version} />
</ListItem>
<Divider variant="inset" component="li" />
<ListItem>
<ListItemAvatar>
<Avatar>
<TimerIcon />
</Avatar>
</ListItemAvatar>
<ListItemText primary="System Uptime" secondary={data.uptime} />
</ListItem>
<Divider variant="inset" component="li" />
<ListItem>
<ListItemAvatar>
<Avatar>
<BatteryUnknownIcon />
</Avatar>
</ListItemAvatar>
<ListItemText
primary="Free System Memory"
secondary={data.free_mem + "%"}
/>
</ListItem>
<Divider variant="inset" component="li" />
<ListItem>
<ListItemAvatar>
<HighlightAvatar color={mqttStatusHighlight(data, theme)}>
<SpeakerNotesOffIcon />
</HighlightAvatar>
</ListItemAvatar>
<ListItemText
primary="MQTT Publish failures"
secondary={data.mqtt_fails}
/>
</ListItem>
<Divider variant="inset" component="li" />
<ListItem>
<ListItemAvatar>
<HighlightAvatar color={busStatusHighlight(data, theme)}>
<DeviceHubIcon />
</HighlightAvatar>
</ListItemAvatar>
<ListItemText primary="EMS Bus Status" secondary={busStatus(data)} />
</ListItem>
{isConnected(data) && (
<Fragment>
<Table size="small" padding="default">
<TableHead>
<TableRow>
<TableCell>Statistic</TableCell>
<TableCell align="center"># Telegrams</TableCell>
<TableCell />
</TableRow>
</TableHead>
<TableBody>
<TableRow>
<TableCell component="th" scope="row">
(Rx) Received telegrams
</TableCell>
<TableCell align="center">{data.rx_received}</TableCell>
</TableRow>
<TableRow>
<TableCell component="th" scope="row">
(Rx) Incomplete telegrams
</TableCell>
<TableCell align="center">{data.crc_errors}</TableCell>
</TableRow>
<TableRow>
<TableCell component="th" scope="row">
(Tx) Successfully sent telegrams
</TableCell>
<TableCell align="center">{data.tx_sent}</TableCell>
</TableRow>
<TableRow>
<TableCell component="th" scope="row">
(Tx) Send failures
</TableCell>
<TableCell align="center">{data.tx_errors}</TableCell>
</TableRow>
</TableBody>
</Table>
</Fragment>
)}
<Divider variant="inset" component="li" />
<ListItem></ListItem>
</Fragment>
);
}
createListItems() { render() {
const { data, theme } = this.props; return (
return ( <Fragment>
<Fragment> <List>{this.createListItems()}</List>
<ListItem> <FormActions>
<ListItemAvatar> <FormButton
<Avatar> startIcon={<RefreshIcon />}
<BuildIcon /> variant="contained"
</Avatar> color="secondary"
</ListItemAvatar> onClick={this.props.loadData}
<ListItemText primary="Firmware Version" secondary={data.version} /> >
</ListItem> Refresh
<Divider variant="inset" component="li" />
<ListItem>
<ListItemAvatar>
<Avatar>
<TimerIcon />
</Avatar>
</ListItemAvatar>
<ListItemText primary="System Uptime" secondary={data.uptime} />
</ListItem>
<Divider variant="inset" component="li" />
<ListItem>
<ListItemAvatar>
<Avatar>
<BatteryUnknownIcon />
</Avatar>
</ListItemAvatar>
<ListItemText primary="Free System Memory" secondary={data.free_mem + '%'} />
</ListItem>
<Divider variant="inset" component="li" />
<ListItem>
<ListItemAvatar>
<HighlightAvatar color={busStatusHighlight(data, theme)}>
<DeviceHubIcon />
</HighlightAvatar>
</ListItemAvatar>
<ListItemText primary="EMS Bus Status" secondary={busStatus(data)} />
</ListItem>
<Divider variant="inset" component="li" />
{
isConnected(data) &&
<Fragment>
<Table size="small" padding="default">
<TableHead>
<TableRow>
<TableCell>Statistic</TableCell>
<TableCell align="center"># Telegrams</TableCell>
<TableCell />
</TableRow>
</TableHead>
<TableBody>
<TableRow>
<TableCell component="th" scope="row">
Rx Received
</TableCell>
<TableCell align="center">
{data.rx_received}
</TableCell>
</TableRow>
<TableRow>
<TableCell component="th" scope="row">
Tx Sent
</TableCell>
<TableCell align="center">
{data.tx_sent}
</TableCell>
</TableRow>
<TableRow>
<TableCell component="th" scope="row">
CRC Errors
</TableCell>
<TableCell align="center">
{data.crc_errors}
</TableCell>
</TableRow>
<TableRow>
<TableCell component="th" scope="row">
Tx Errors
</TableCell>
<TableCell align="center">
{data.tx_errors}
</TableCell>
</TableRow>
</TableBody>
</Table>
</Fragment>
}
<ListItem>
<ListItemAvatar>
<HighlightAvatar color={mqttStatusHighlight(data, theme)}>
<SpeakerNotesOffIcon />
</HighlightAvatar>
</ListItemAvatar>
<ListItemText primary="MQTT Publish fails" secondary={data.mqtt_fails} />
</ListItem>
<Divider variant="inset" component="li" />
<ListItem></ListItem>
</Fragment>
);
}
render() {
return (
<Fragment>
<List>
{this.createListItems()}
</List>
<FormActions>
<FormButton startIcon={<RefreshIcon />} variant="contained" color="secondary" onClick={this.props.loadData}>
Refresh
</FormButton> </FormButton>
</FormActions> </FormActions>
</Fragment> </Fragment>
); );
} }
} }
export default withTheme(EMSESPStatusForm); export default withTheme(EMSESPStatusForm);

View File

@@ -1,27 +1,30 @@
import React, { Component } from 'react'; import React, { Component } from "react";
import { Link, withRouter, RouteComponentProps } from 'react-router-dom'; 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 SettingsRemoteIcon from '@material-ui/icons/SettingsRemote'; import SettingsRemoteIcon from "@material-ui/icons/SettingsRemote";
import { PROJECT_PATH } from '../api'; import { PROJECT_PATH } from "../api";
class ProjectMenu extends Component<RouteComponentProps> { class ProjectMenu extends Component<RouteComponentProps> {
render() { render() {
const path = this.props.match.url; const path = this.props.match.url;
return ( return (
<List> <List>
<ListItem to={`/${PROJECT_PATH}/`} selected={path.startsWith(`/${PROJECT_PATH}/`)} button component={Link}> <ListItem
to={`/${PROJECT_PATH}/`}
selected={path.startsWith(`/${PROJECT_PATH}/`)}
button
component={Link}
>
<ListItemIcon> <ListItemIcon>
<SettingsRemoteIcon /> <SettingsRemoteIcon />
</ListItemIcon> </ListItemIcon>
<ListItemText primary="EMS-ESP" /> <ListItemText primary="EMS-ESP" />
</ListItem> </ListItem>
</List> </List>
) );
} }
} }
export default withRouter(ProjectMenu); export default withRouter(ProjectMenu);

View File

@@ -73,6 +73,9 @@ void Solar::show_values(uuid::console::Shell & shell) {
shell.printfln(F(" Pump working time: %d days %d hours %d minutes"), pumpWorkMin_ / 1440, (pumpWorkMin_ % 1440) / 60, pumpWorkMin_ % 60); shell.printfln(F(" Pump working time: %d days %d hours %d minutes"), pumpWorkMin_ / 1440, (pumpWorkMin_ % 1440) / 60, pumpWorkMin_ % 60);
} }
print_value(shell, 2, F("Tank Heated"), tankHeated_, nullptr, EMS_VALUE_BOOL);
print_value(shell, 2, F("Collector"), collectorOnOff_, nullptr, EMS_VALUE_BOOL);
print_value(shell, 2, F("Energy last hour"), energyLastHour_, F_(wh), 10); print_value(shell, 2, F("Energy last hour"), energyLastHour_, F_(wh), 10);
print_value(shell, 2, F("Energy today"), energyToday_, F_(wh)); print_value(shell, 2, F("Energy today"), energyToday_, F_(wh));
print_value(shell, 2, F("Energy total"), energyTotal_, F_(kwh), 10); print_value(shell, 2, F("Energy total"), energyTotal_, F_(kwh), 10);
@@ -87,30 +90,47 @@ void Solar::publish_values() {
if (Helpers::hasValue(collectorTemp_)) { if (Helpers::hasValue(collectorTemp_)) {
doc["collectortemp"] = (float)collectorTemp_ / 10; doc["collectortemp"] = (float)collectorTemp_ / 10;
} }
if (Helpers::hasValue(bottomTemp_)) { if (Helpers::hasValue(bottomTemp_)) {
doc["bottomtemp"] = (float)bottomTemp_ / 10; doc["bottomtemp"] = (float)bottomTemp_ / 10;
} }
if (Helpers::hasValue(bottomTemp2_)) { if (Helpers::hasValue(bottomTemp2_)) {
doc["bottomtemp2"] = (float)bottomTemp2_ / 10; doc["bottomtemp2"] = (float)bottomTemp2_ / 10;
} }
if (Helpers::hasValue(pumpModulation_)) { if (Helpers::hasValue(pumpModulation_)) {
doc["pumpmodulation"] = pumpModulation_; doc["pumpmodulation"] = pumpModulation_;
} }
if (Helpers::hasValue(pump_, true)) { if (Helpers::hasValue(pump_, true)) {
doc["pump"] = Helpers::render_value(s, pump_, EMS_VALUE_BOOL); doc["pump"] = Helpers::render_value(s, pump_, EMS_VALUE_BOOL);
} }
if (Helpers::hasValue(valveStatus_, true)) { if (Helpers::hasValue(valveStatus_, true)) {
doc["valvestatus"] = Helpers::render_value(s, valveStatus_, EMS_VALUE_BOOL); doc["valvestatus"] = Helpers::render_value(s, valveStatus_, EMS_VALUE_BOOL);
} }
if (Helpers::hasValue(pumpWorkMin_)) { if (Helpers::hasValue(pumpWorkMin_)) {
doc["pumpWorkMin"] = (float)pumpWorkMin_; doc["pumpWorkMin"] = (float)pumpWorkMin_;
} }
if (Helpers::hasValue(tankHeated_, true)) {
doc["tankHeated"] = Helpers::render_value(s, tankHeated_, EMS_VALUE_BOOL);
}
if (Helpers::hasValue(collectorOnOff_, true)) {
doc["collectorOnOff"] = Helpers::render_value(s, collectorOnOff_, EMS_VALUE_BOOL);
}
if (Helpers::hasValue(energyLastHour_)) { if (Helpers::hasValue(energyLastHour_)) {
doc["energylasthour"] = (float)energyLastHour_ / 10; doc["energylasthour"] = (float)energyLastHour_ / 10;
} }
if (Helpers::hasValue(energyToday_)) { if (Helpers::hasValue(energyToday_)) {
doc["energytoday"] = energyToday_; doc["energytoday"] = energyToday_;
} }
if (Helpers::hasValue(energyTotal_)) { if (Helpers::hasValue(energyTotal_)) {
doc["energytotal"] = (float)energyTotal_ / 10; doc["energytotal"] = (float)energyTotal_ / 10;
} }
@@ -186,6 +206,9 @@ void Solar::process_SM100Status(std::shared_ptr<const Telegram> telegram) {
if (pumpmod == 0 && pumpModulation_ == 100) { // mask out boosts if (pumpmod == 0 && pumpModulation_ == 100) { // mask out boosts
pumpModulation_ = 15; // set to minimum pumpModulation_ = 15; // set to minimum
} }
telegram->read_bitvalue(tankHeated_, 3, 1); // issue #422
telegram->read_bitvalue(collectorOnOff_, 3, 0);
} }
/* /*

View File

@@ -56,6 +56,8 @@ class Solar : public EMSdevice {
uint32_t energyToday_ = EMS_VALUE_ULONG_NOTSET; uint32_t energyToday_ = EMS_VALUE_ULONG_NOTSET;
uint32_t energyTotal_ = EMS_VALUE_ULONG_NOTSET; uint32_t energyTotal_ = EMS_VALUE_ULONG_NOTSET;
uint32_t pumpWorkMin_ = EMS_VALUE_ULONG_NOTSET; // Total solar pump operating time uint32_t pumpWorkMin_ = EMS_VALUE_ULONG_NOTSET; // Total solar pump operating time
uint8_t tankHeated_ = EMS_VALUE_BOOL_NOTSET;
uint8_t collectorOnOff_ = EMS_VALUE_BOOL_NOTSET;
uint8_t availabilityFlag_ = EMS_VALUE_BOOL_NOTSET; uint8_t availabilityFlag_ = EMS_VALUE_BOOL_NOTSET;
uint8_t configFlag_ = EMS_VALUE_BOOL_NOTSET; uint8_t configFlag_ = EMS_VALUE_BOOL_NOTSET;

View File

@@ -107,8 +107,10 @@ 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);
EMSuart::stop(); if (tx_mode) {
EMSuart::start(tx_mode); // reset the UART EMSuart::stop();
EMSuart::start(tx_mode); // reset the UART
}
} }
// return status of bus: connected, connected but Tx is broken, disconnected // return status of bus: connected, connected but Tx is broken, disconnected
@@ -117,8 +119,21 @@ uint8_t EMSESP::bus_status() {
return BUS_STATUS_OFFLINE; return BUS_STATUS_OFFLINE;
} }
// check if we have Tx issues // check if we have Tx issues.
if (txservice_.telegram_read_count() < 2) { uint32_t total_sent = txservice_.telegram_read_count() + txservice_.telegram_write_count();
// nothing sent successfully, also no errors - must be ok
if ((total_sent == 0) && (txservice_.telegram_fail_count() == 0)) {
return BUS_STATUS_CONNECTED;
}
// nothing sent successfully, but have Tx errors
if ((total_sent == 0) && (txservice_.telegram_fail_count() != 0)) {
return BUS_STATUS_TX_ERRORS;
}
// Tx Failure rate > 5%
if (((txservice_.telegram_fail_count() * 100) / total_sent) >= 5) {
return BUS_STATUS_TX_ERRORS; return BUS_STATUS_TX_ERRORS;
} }

View File

@@ -231,7 +231,9 @@ void System::start() {
Mqtt::subscribe("cmd", std::bind(&System::mqtt_commands, this, _1)); Mqtt::subscribe("cmd", std::bind(&System::mqtt_commands, this, _1));
#ifndef EMSESP_FORCE_SERIAL #ifndef EMSESP_FORCE_SERIAL
EMSuart::start(tx_mode_); // start UART if (tx_mode_) {
EMSuart::start(tx_mode_); // start UART, if tx_mode is not 0
}
#endif #endif
} }