diff --git a/CHANGELOG.md b/CHANGELOG.md index 1d6bf6743..ceef2c558 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,10 +5,10 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [2.0.0 alpha] +## [2.0.0 beta] ### Changed -- everything. See README +- everything. See `README.md` ## [1.9.5] 30-04-2020 diff --git a/README.md b/README.md index 975956472..d5271a3d2 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,19 @@ # EMS-ESP version 2.0 +## **Breaking changes** + +- MQTT base has been removed. The hostname is only used. + ## **New Features in v2** - A new web interface using React and TypeScript that's now secure as each URL endpoint is protected by issuing a JWT which is then sent using Bearer Authentication. Using a Captive Portal in AP mode or connecting to a local wifi network. -![settings](media/web_settings.PNG) -![status](media/web_status.PNG) + + + + -- A new console. Like in version 1.9 it works with both Serial and Telnet but with a rich set of commands and more intuitive with behavior similar to a Linux-style shell. It supports multiple connections and commands that alter the settings or interact directly with EMS devices are secure behind an admin password. A full list of commands is below, here are the key ones: +- A new console. Like in version 1.9 it works with both Serial and Telnet but with a more intuitive Linux shell like behavior. It supports multiple connections and has basic security to prevent any changes to EMS-ESP. A full list of commands is below, here are the key ones: * `help` lists the commands and keywords * some commands take you into a new context, a bit like a sub-menu. e.g. `system`, `thermostat`. Use `help` to show which commands this context has and `exit` or CTRL-D to get back to the root. * To change a setting use the `set` command. Typing `set` shows the current settings. @@ -112,43 +118,73 @@ thermostat - See if it's easier to use timers instead of millis() based timers, using [polledTimeout](https://github.com/esp8266/Arduino/blob/master/libraries/esp8266/examples/BlinkPolledTimeout/BlinkPolledTimeout.ino). - Port over to ESP-IDF. The Arduino SDK is showing its limitations +### **Features to add** + +- Multi-language. German, Dutch, French +- Click on a device in the Web UI shows it's details +- Publish time can be customized per device (solar, mixing etc) + ### **Customizing the Web UI** The Web is based off Rick's awesome [esp8266-react](https://github.com/rjwats/esp8266-react/) framework. These are the files that are modified: **`interface:`** - * `.env` (project name and project path to ems-esp) - * `.env.development` (CORS URL) + * `.env` project name and project path to ems-esp + * `.env.development` CORS URL **`interface/public:`** - * `app/manifest.json` (ems-esp name) - * `index.html` (ems-esp name) - * `app/icon.png` (256x256 PNG) - * `favicon.ico` + * `app/manifest.json` ems-esp name + * `index.html` ems-esp name + * `app/icon.png` 256x256 PNG + * `favicon.ico` replaced **`interface/src:`** - * `CustomMuiTheme.tsx` (colors for dark mode) - * `interface/src/wifi/WifiSettingsController.tsx` (rename esp8266-react) + * `CustomMuiTheme.tsx` colors for dark mode + * `interface/src/wifi/WifiSettingsController.tsx` rename esp8266-react **`interface/src/project:`** - * `ProjectRouting.tsx` (removed demo, renamed DemoProject to EMSESP) - * `DemoProject.tsx` (remove /demo/ and changed title, renamed to EMSESP.tsx) - * `ProjectMenu.tsx` (title) - * `DemoInformation.tsx` (removed file) - * `types.ts` (add variables) + * `ProjectRouting.tsx` removed demo, added paths to ems-esp/status, ems-esp/settings and * + * `DemoProject.tsx` remove /demo/ and changed title, renamed to EMSESP.tsx + * `ProjectMenu.tsx` title change, added /ems-esp/settings + * `DemoInformation.tsx` removed file + * `types.ts` add variables + * added all custom files starting with EMSESP* + +**`interface/src/mqtt:`** + * `types.ts` added mqtt_fails + * `MqttStatusForm.tsx` added MQTT Publish Errors + * `MqttStatus.ts` added function mqttPublishHighlight + * `MqttSettingsForm.tsx` added publish time, qos, format + +**`interface/src/authentication:`** + * `AuthenticationWrapper.tsx` commented out features.security because we added version + * `AuthenticationContext.tsx` added version + * `MqttSettingsForm.tsx` added publish time, qos, format + +**`interface/src/components:`** + * `MenuAppBar.tsx` added version to toolbar + +**`interface/src/system:`** + * `types.ts` added uptime and free_mem + * `SystemStatusForm.tsx` added system uptime, % free mem **`lib/framework`:** - -* `SystemStatus.h` : added #include -* `SystemStatus.cpp` : added LittleFS.info(fs_info); -* Commented out all `Serial.print`'s -* `features.ini`: -D FT_NTP=0 -* customized `platformio.ini` -* customized `factory_settings.ini` with `ems-esp-neo` as password and `ems-esp` everywhere else + * `SystemStatus.h` added #include , #include , #include "../../src/system.h" + * `SystemStatus.cpp` added LittleFS.info(fs_info); root["uptime"], root["free_mem"] + * Commented out all `Serial.print`'s in all files + * `MqttStatus.h` added #include "../../src/mqtt.h" + * `MqttStatus.cpp` added root["mqtt_fails"] + * `SecuritySettingsService.cpp` added version to the JWT payload + * `SecuritySettingsService.h` #include "../../src/version.h" + * `features.ini`: -D FT_NTP=0 + * `platformio.ini` using our own version + * `factory_settings.ini` modified with `ems-esp-neo` as password and `ems-esp` everywhere else - Info on UI customizations: + UI customization links: * icons: https://material-ui.com/components/material-icons/ * colors: https://material-ui.com/customization/color/ - * tables: https://material-ui.com/components/tables/#dense-table \ No newline at end of file + * tables: https://material-ui.com/components/tables/#dense-table + + diff --git a/interface/src/authentication/AuthenticationContext.tsx b/interface/src/authentication/AuthenticationContext.tsx index aaf22ba7a..10d32b8aa 100644 --- a/interface/src/authentication/AuthenticationContext.tsx +++ b/interface/src/authentication/AuthenticationContext.tsx @@ -3,6 +3,7 @@ import * as React from "react"; export interface Me { username: string; admin: boolean; + version: string; // proddy added } export interface AuthenticationContext { diff --git a/interface/src/authentication/AuthenticationWrapper.tsx b/interface/src/authentication/AuthenticationWrapper.tsx index 266368a14..9a8de065a 100644 --- a/interface/src/authentication/AuthenticationWrapper.tsx +++ b/interface/src/authentication/AuthenticationWrapper.tsx @@ -59,10 +59,11 @@ class AuthenticationWrapper extends React.Component { - if (!this.props.features.security) { - this.setState({ initialized: true, context: { ...this.state.context, me: { admin: true, username: "admin" } } }); - return; - } + // proddy removed + // if (!this.props.features.security) { + // this.setState({ initialized: true, context: { ...this.state.context, me: { admin: true, username: "admin" } } }); + // return; + // } const accessToken = getStorage().getItem(ACCESS_TOKEN) if (accessToken) { authorizedFetch(VERIFY_AUTHORIZATION_ENDPOINT) diff --git a/interface/src/components/MenuAppBar.tsx b/interface/src/components/MenuAppBar.tsx index dfe5f227b..625d99c2a 100644 --- a/interface/src/components/MenuAppBar.tsx +++ b/interface/src/components/MenuAppBar.tsx @@ -124,11 +124,18 @@ class MenuAppBar extends React.Component { {PROJECT_NAME} + {PROJECT_NAME} + + +   {authenticatedContext.me.version} + + + {features.project && ( @@ -149,12 +156,12 @@ class MenuAppBar extends React.Component { {features.ntp && ( - - - - - - + + + + + + )} {features.mqtt && ( diff --git a/interface/src/mqtt/MqttStatus.ts b/interface/src/mqtt/MqttStatus.ts index a029b17ee..00e5408ec 100644 --- a/interface/src/mqtt/MqttStatus.ts +++ b/interface/src/mqtt/MqttStatus.ts @@ -44,7 +44,7 @@ export const disconnectReason = ({ disconnect_reason }: MqttStatus) => { } } -export const mqttStatusHighlight2 = ({ mqtt_fails }: MqttStatus, theme: Theme) => { +export const mqttPublishHighlight = ({ mqtt_fails }: MqttStatus, theme: Theme) => { if (mqtt_fails === 0) return theme.palette.success.main; @@ -53,5 +53,4 @@ export const mqttStatusHighlight2 = ({ mqtt_fails }: MqttStatus, theme: Theme) = return theme.palette.warning.main; return theme.palette.success.main; - } \ No newline at end of file diff --git a/interface/src/mqtt/MqttStatusForm.tsx b/interface/src/mqtt/MqttStatusForm.tsx index 57935f1b0..e17a63999 100644 --- a/interface/src/mqtt/MqttStatusForm.tsx +++ b/interface/src/mqtt/MqttStatusForm.tsx @@ -9,7 +9,7 @@ import ReportIcon from '@material-ui/icons/Report'; import SpeakerNotesOffIcon from "@material-ui/icons/SpeakerNotesOff"; import { RestFormProps, FormActions, FormButton, HighlightAvatar } from '../components'; -import { mqttStatusHighlight, mqttStatus, mqttStatusHighlight2, disconnectReason } from './MqttStatus'; +import { mqttStatusHighlight, mqttStatus, mqttPublishHighlight, disconnectReason } from './MqttStatus'; import { MqttStatus } from './types'; type MqttStatusFormProps = RestFormProps & WithTheme; @@ -30,7 +30,7 @@ class MqttStatusForm extends Component { - + diff --git a/interface/src/project/EMSESP.tsx b/interface/src/project/EMSESP.tsx index f66eaaa5f..fcb8949fd 100644 --- a/interface/src/project/EMSESP.tsx +++ b/interface/src/project/EMSESP.tsx @@ -9,6 +9,7 @@ import { AuthenticatedRoute } from '../authentication'; import EMSESPStatusController from './EMSESPStatusController'; import EMSESPDevicesController from './EMSESPDevicesController'; +import EMSESPHelp from './EMSESPHelp'; class EMSESP extends Component { @@ -20,10 +21,12 @@ class EMSESP extends Component { return ( - + + + diff --git a/interface/src/project/EMSESPDevicesForm.tsx b/interface/src/project/EMSESPDevicesForm.tsx index c4e81d615..41baa437d 100644 --- a/interface/src/project/EMSESPDevicesForm.tsx +++ b/interface/src/project/EMSESPDevicesForm.tsx @@ -115,7 +115,7 @@ class EMSESPDevicesForm extends Component - No EMS devices found. Check connection and for Tx errors. Try forcing a scan. + No EMS devices found. Check the connection and for possible Tx errors and try scanning for new devices. ) @@ -174,8 +174,9 @@ class EMSESPDevicesForm extends Component +

{this.createTableItems()} - +

} variant="contained" color="secondary" onClick={this.props.loadData}> diff --git a/interface/src/project/EMSESPHelp.tsx b/interface/src/project/EMSESPHelp.tsx new file mode 100644 index 000000000..ca8778f83 --- /dev/null +++ b/interface/src/project/EMSESPHelp.tsx @@ -0,0 +1,35 @@ +import React, { Component } from 'react'; +import { Typography, Box, Link } from '@material-ui/core'; +import { SectionContent } from '../components'; + +class EMSESPHelp extends Component { + + render() { + return ( + + + + + EMS-ESP is an open-source firmware to communicate with heating devices that support the EMS protocol, such as equipment from Bosch, Junkers, Nefit, Buderus and Worcester. +

+ Please consider supporting this project via the GitHub page {'http://github.com/proddy/EMS-ESP'}. +
+
+

+ + Check for news and updates on the {'Wiki'}. + + + For live community chat go to {'Gitter'}. + + + To report an issue or feature request go to {'the github project page'}. + + +
+ ) + } + +} + +export default EMSESPHelp; diff --git a/interface/src/project/EMSESPStatusController.tsx b/interface/src/project/EMSESPStatusController.tsx index b7dd8cf32..0b8293b6f 100644 --- a/interface/src/project/EMSESPStatusController.tsx +++ b/interface/src/project/EMSESPStatusController.tsx @@ -17,7 +17,7 @@ class EMSESPStatusController extends Component { render() { return ( - + } diff --git a/interface/src/project/EMSESPStatusForm.tsx b/interface/src/project/EMSESPStatusForm.tsx index 45800b290..28af88cd4 100644 --- a/interface/src/project/EMSESPStatusForm.tsx +++ b/interface/src/project/EMSESPStatusForm.tsx @@ -12,9 +12,6 @@ import { ListItem, ListItemAvatar, ListItemText, - Typography, - Box, - Link } from "@material-ui/core"; import RefreshIcon from "@material-ui/icons/Refresh"; @@ -42,17 +39,7 @@ class EMSESPStatusForm extends Component { const { data, theme } = this.props; return ( - - - Firmware Version is {data.version} -

- Check for news and updates on the {'Wiki'} -
- For live community chat go to {'Gitter'} -
- To report issues, contribute and give kudos visit {'github.com/proddy/EMS-ESP'} -
-
+ diff --git a/interface/src/project/EMSESPtypes.ts b/interface/src/project/EMSESPtypes.ts index 26f64bdf3..91fed67ee 100644 --- a/interface/src/project/EMSESPtypes.ts +++ b/interface/src/project/EMSESPtypes.ts @@ -16,7 +16,6 @@ export enum busConnectionStatus { } export interface EMSESPStatus { - version: string; status: busConnectionStatus; rx_received: number; tx_sent: number; diff --git a/interface/src/wifi/WiFiSettingsController.tsx b/interface/src/wifi/WiFiSettingsController.tsx index 23fdd316b..bac67ea7c 100644 --- a/interface/src/wifi/WiFiSettingsController.tsx +++ b/interface/src/wifi/WiFiSettingsController.tsx @@ -1,8 +1,7 @@ import React, { Component } from 'react'; -import {restController, RestControllerProps, RestFormLoader, SectionContent } from '../components'; +import { restController, RestControllerProps, RestFormLoader, SectionContent } from '../components'; import WiFiSettingsForm from './WiFiSettingsForm'; -import { WiFiConnectionContext } from './WiFiConnectionContext'; import { WIFI_SETTINGS_ENDPOINT } from '../api'; import { WiFiSettings } from './types'; @@ -10,39 +9,15 @@ type WiFiSettingsControllerProps = RestControllerProps; class WiFiSettingsController extends Component { - static contextType = WiFiConnectionContext; - context!: React.ContextType; - componentDidMount() { - const { selectedNetwork } = this.context; - if (selectedNetwork) { - const wifiSettings: WiFiSettings = { - ssid: selectedNetwork.ssid, - password: "", - hostname: "ems-esp", - static_ip_config: false, - } - this.props.setData(wifiSettings); - } else { - this.props.loadData(); - } - } - - deselectNetworkAndLoadData = () => { - this.context.deselectNetwork(); this.props.loadData(); } - componentWillUnmount() { - this.context.deselectNetwork(); - } - render() { return ( } /> @@ -51,4 +26,4 @@ class WiFiSettingsController extends Component { } -export default restController(WIFI_SETTINGS_ENDPOINT, WiFiSettingsController); +export default restController(WIFI_SETTINGS_ENDPOINT, WiFiSettingsController); \ No newline at end of file diff --git a/interface/src/wifi/WiFiSettingsForm.tsx b/interface/src/wifi/WiFiSettingsForm.tsx index f36e13f3c..37d0ebdda 100644 --- a/interface/src/wifi/WiFiSettingsForm.tsx +++ b/interface/src/wifi/WiFiSettingsForm.tsx @@ -24,15 +24,39 @@ class WiFiSettingsForm extends React.Component { static contextType = WiFiConnectionContext; context!: React.ContextType; + constructor(props: WiFiStatusFormProps, context: WiFiConnectionContext) { + super(props); + + const { selectedNetwork } = context; + if (selectedNetwork) { + const wifiSettings: WiFiSettings = { + ssid: selectedNetwork.ssid, + password: "", + hostname: props.data.hostname, + static_ip_config: false, + } + props.setData(wifiSettings); + } + } + componentWillMount() { ValidatorForm.addValidationRule('isIP', isIP); ValidatorForm.addValidationRule('isHostname', isHostname); ValidatorForm.addValidationRule('isOptionalIP', optional(isIP)); } + deselectNetworkAndLoadData = () => { + this.context.deselectNetwork(); + this.props.loadData(); + } + + componentWillUnmount() { + this.context.deselectNetwork(); + } + render() { const { selectedNetwork, deselectNetwork } = this.context; - const { data, handleValueChange, saveData, loadData } = this.props; + const { data, handleValueChange, saveData } = this.props; return ( { @@ -167,7 +191,7 @@ class WiFiSettingsForm extends React.Component { } variant="contained" color="primary" type="submit"> Save - + Reset @@ -176,4 +200,4 @@ class WiFiSettingsForm extends React.Component { } } -export default WiFiSettingsForm; +export default WiFiSettingsForm; \ No newline at end of file diff --git a/lib/framework/MqttStatus.cpp b/lib/framework/MqttStatus.cpp index 30f4f8f47..cbf88cc70 100644 --- a/lib/framework/MqttStatus.cpp +++ b/lib/framework/MqttStatus.cpp @@ -16,7 +16,7 @@ void MqttStatus::mqttStatus(AsyncWebServerRequest * request) { root["client_id"] = _mqttSettingsService->getClientId(); root["disconnect_reason"] = (uint8_t)_mqttSettingsService->getDisconnectReason(); - root["mqtt_fails"] = emsesp::Mqtt::publish_fails(); + root["mqtt_fails"] = emsesp::Mqtt::publish_fails(); // proddy added response->setLength(); request->send(response); diff --git a/lib/framework/MqttStatus.h b/lib/framework/MqttStatus.h index 4cfc6cfbf..cf7e65480 100644 --- a/lib/framework/MqttStatus.h +++ b/lib/framework/MqttStatus.h @@ -9,7 +9,7 @@ #include #endif -#include "../../src/mqtt.h" +#include "../../src/mqtt.h" // proddy added #include #include diff --git a/lib/framework/SecuritySettingsService.cpp b/lib/framework/SecuritySettingsService.cpp index 87027db58..08ba401d8 100644 --- a/lib/framework/SecuritySettingsService.cpp +++ b/lib/framework/SecuritySettingsService.cpp @@ -61,6 +61,7 @@ Authentication SecuritySettingsService::authenticate(const String& username, con inline void populateJWTPayload(JsonObject& payload, User* user) { payload["username"] = user->username; payload["admin"] = user->admin; + payload["version"] = EMSESP_APP_VERSION; // proddy added } boolean SecuritySettingsService::validatePayload(JsonObject& parsedPayload, User* user) { diff --git a/lib/framework/SecuritySettingsService.h b/lib/framework/SecuritySettingsService.h index 236bfe4ef..801e44a3e 100644 --- a/lib/framework/SecuritySettingsService.h +++ b/lib/framework/SecuritySettingsService.h @@ -6,6 +6,8 @@ #include #include +#include "../../src/version.h" // added by proddy + #ifndef FACTORY_ADMIN_USERNAME #define FACTORY_ADMIN_USERNAME "admin" #endif diff --git a/lib/framework/SystemStatus.cpp b/lib/framework/SystemStatus.cpp index 9a083dc9f..dcad9595e 100644 --- a/lib/framework/SystemStatus.cpp +++ b/lib/framework/SystemStatus.cpp @@ -34,13 +34,13 @@ void SystemStatus::systemStatus(AsyncWebServerRequest * request) { root["fs_used"] = SPIFFS.usedBytes(); #elif defined(ESP8266) FSInfo fs_info; - LittleFS.info(fs_info); // TODO added littlefs + LittleFS.info(fs_info); // // proddy added root["fs_total"] = fs_info.totalBytes; root["fs_used"] = fs_info.usedBytes; #endif - root["uptime"] = uuid::log::format_timestamp_ms(uuid::get_uptime_ms(), 3); - root["free_mem"] = emsesp::System::free_mem(); + root["uptime"] = uuid::log::format_timestamp_ms(uuid::get_uptime_ms(), 3); // proddy added + root["free_mem"] = emsesp::System::free_mem(); // proddy added response->setLength(); request->send(response); diff --git a/lib/framework/SystemStatus.h b/lib/framework/SystemStatus.h index 7fa487bd5..ab3f33b58 100644 --- a/lib/framework/SystemStatus.h +++ b/lib/framework/SystemStatus.h @@ -9,7 +9,7 @@ #include #include #include -#include // TODO added littlefs +#include // proddy added #endif #include @@ -17,8 +17,8 @@ #include #include -#include -#include "../../src/system.h" +#include // proddy added +#include "../../src/system.h" // proddy added #define MAX_ESP_STATUS_SIZE 1024 #define SYSTEM_STATUS_SERVICE_PATH "/rest/systemStatus" diff --git a/media/web_devices.PNG b/media/web_devices.PNG new file mode 100644 index 000000000..803ebf878 Binary files /dev/null and b/media/web_devices.PNG differ diff --git a/media/web_mqtt.PNG b/media/web_mqtt.PNG new file mode 100644 index 000000000..1df961cf9 Binary files /dev/null and b/media/web_mqtt.PNG differ diff --git a/media/web_settings.PNG b/media/web_settings.PNG index 6b38ecca7..7cc173aec 100644 Binary files a/media/web_settings.PNG and b/media/web_settings.PNG differ diff --git a/media/web_status.PNG b/media/web_status.PNG index 55f507d56..9211cf9fc 100644 Binary files a/media/web_status.PNG and b/media/web_status.PNG differ diff --git a/platformio.ini b/platformio.ini index 103a6b6d5..ccef5d608 100644 --- a/platformio.ini +++ b/platformio.ini @@ -37,7 +37,7 @@ libs_core = [env] extra_scripts = - pre:scripts/build_interface.py + ; pre:scripts/build_interface.py scripts/main_script.py framework = arduino diff --git a/src/EMSESPStatusService.cpp b/src/EMSESPStatusService.cpp index 34f0de072..381371368 100644 --- a/src/EMSESPStatusService.cpp +++ b/src/EMSESPStatusService.cpp @@ -54,8 +54,6 @@ void EMSESPStatusService::emsespStatusService(AsyncWebServerRequest * request) { AsyncJsonResponse * response = new AsyncJsonResponse(false, MAX_EMSESP_STATUS_SIZE); JsonObject root = response->getRoot(); - root["version"] = EMSESP_APP_VERSION; - root["status"] = EMSESP::bus_status(); // 0, 1 or 2 root["rx_received"] = EMSESP::rxservice_.telegram_count(); root["tx_sent"] = EMSESP::txservice_.telegram_read_count() + EMSESP::txservice_.telegram_write_count(); diff --git a/src/version.h b/src/version.h index a720593ed..b5c0d03c0 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define EMSESP_APP_VERSION "2.0.0_b4" // based off a32 +#define EMSESP_APP_VERSION "2.0.0_b4"