diff --git a/CHANGELOG_LATEST.md b/CHANGELOG_LATEST.md index 756553db9..0de99c8f4 100644 --- a/CHANGELOG_LATEST.md +++ b/CHANGELOG_LATEST.md @@ -1,11 +1,12 @@ # Changelog ## Added - +- Mock API to simulate an ESP, for testing web ## Fixed ## Changed - +- Icon for Network +- MQTT Formatting payload (nested vs single) is a pull-down option - moved mqtt-topics and texts to local_EN, all topics lower case ## Removed diff --git a/interface/src/mqtt/MqttSettingsForm.tsx b/interface/src/mqtt/MqttSettingsForm.tsx index 328af2352..492183abf 100644 --- a/interface/src/mqtt/MqttSettingsForm.tsx +++ b/interface/src/mqtt/MqttSettingsForm.tsx @@ -1,22 +1,31 @@ -import React from 'react'; -import { TextValidator, ValidatorForm, SelectValidator } from 'react-material-ui-form-validator'; +import React from "react"; +import { + TextValidator, + ValidatorForm, + SelectValidator, +} from "react-material-ui-form-validator"; -import { Checkbox, TextField, Typography } from '@material-ui/core'; -import SaveIcon from '@material-ui/icons/Save'; -import MenuItem from '@material-ui/core/MenuItem'; +import { Checkbox, TextField, Typography } from "@material-ui/core"; +import SaveIcon from "@material-ui/icons/Save"; +import MenuItem from "@material-ui/core/MenuItem"; -import { RestFormProps, FormActions, FormButton, BlockFormControlLabel, PasswordValidator } from '../components'; -import { isIP, isHostname, or, isPath } from '../validators'; +import { + RestFormProps, + FormActions, + FormButton, + BlockFormControlLabel, + PasswordValidator, +} from "../components"; +import { isIP, isHostname, or, isPath } from "../validators"; -import { MqttSettings } from './types'; +import { MqttSettings } from "./types"; type MqttSettingsFormProps = RestFormProps; class MqttSettingsForm extends React.Component { - componentDidMount() { - ValidatorForm.addValidationRule('isIPOrHostname', or(isIP, isHostname)); - ValidatorForm.addValidationRule('isPath', isPath); + ValidatorForm.addValidationRule("isIPOrHostname", or(isIP, isHostname)); + ValidatorForm.addValidationRule("isPath", isPath); } render() { @@ -27,44 +36,57 @@ class MqttSettingsForm extends React.Component { control={ } label="Enable MQTT" /> { fullWidth variant="outlined" value={data.username} - onChange={handleValueChange('username')} + onChange={handleValueChange("username")} margin="normal" /> { fullWidth variant="outlined" value={data.password} - onChange={handleValueChange('password')} + onChange={handleValueChange("password")} margin="normal" /> { fullWidth variant="outlined" value={data.client_id} - onChange={handleValueChange('client_id')} + onChange={handleValueChange("client_id")} margin="normal" /> - + onChange={handleValueChange("mqtt_qos")} + margin="normal" + > 0 (default) 1 2 @@ -121,7 +155,7 @@ class MqttSettingsForm extends React.Component { control={ } @@ -131,55 +165,63 @@ class MqttSettingsForm extends React.Component { control={ } label="Retain Flag" />

- + Formatting - - } - label="Nested format (Thermostat & Mixer only)" - /> - + nested on a single topic + as individual topics + + + onChange={handleValueChange("dallas_format")} + margin="normal" + > by Sensor ID by Number - + onChange={handleValueChange("bool_format")} + margin="normal" + > "on"/"off" true/false 1/0 "ON"/"OFF" - + onChange={handleValueChange("subscribe_format")} + margin="normal" + > general device topic individual topics, main heating circuit individual topics, all heating circuits @@ -188,103 +230,170 @@ class MqttSettingsForm extends React.Component { control={ } - label="Home Assistant MQTT Discovery" + label="Use Home Assistant MQTT Discovery" /> - { data.ha_enabled && - + onChange={handleValueChange("ha_climate_format")} + margin="normal" + > use Current temperature (default) use Setpoint temperature Fix to 0 - } + )}

- + Publish Intervals - } variant="contained" color="primary" type="submit"> + } + variant="contained" + color="primary" + type="submit" + > Save diff --git a/interface/src/mqtt/types.ts b/interface/src/mqtt/types.ts index 8d226c40a..b5cbdadeb 100644 --- a/interface/src/mqtt/types.ts +++ b/interface/src/mqtt/types.ts @@ -40,6 +40,6 @@ export interface MqttSettings { mqtt_retain: boolean; ha_enabled: boolean; ha_climate_format: number; - nested_format: boolean; + nested_format: number; subscribe_format: number; } diff --git a/interface/src/project/EMSESPSettingsForm.tsx b/interface/src/project/EMSESPSettingsForm.tsx index 7add38aa5..3eb18d546 100644 --- a/interface/src/project/EMSESPSettingsForm.tsx +++ b/interface/src/project/EMSESPSettingsForm.tsx @@ -1,417 +1,577 @@ -import React from 'react'; -import { ValidatorForm, TextValidator, SelectValidator } from 'react-material-ui-form-validator'; +import React from "react"; +import { + ValidatorForm, + TextValidator, + SelectValidator, +} from "react-material-ui-form-validator"; -import { Checkbox, Typography, Box, Link, withWidth, WithWidthProps } from '@material-ui/core'; -import SaveIcon from '@material-ui/icons/Save'; -import MenuItem from '@material-ui/core/MenuItem'; +import { + Checkbox, + Typography, + Box, + Link, + withWidth, + 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"; +import { + redirectingAuthorizedFetch, + withAuthenticatedContext, + AuthenticatedContextProps, +} from "../authentication"; -import { RestFormProps, FormActions, FormButton, BlockFormControlLabel } from '../components'; +import { + RestFormProps, + FormActions, + FormButton, + 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"; -type EMSESPSettingsFormProps = RestFormProps & AuthenticatedContextProps & WithWidthProps; +type EMSESPSettingsFormProps = RestFormProps & + AuthenticatedContextProps & + WithWidthProps; interface EMSESPSettingsFormState { - processing: boolean; + processing: boolean; } class EMSESPSettingsForm extends React.Component { + state: EMSESPSettingsFormState = { + processing: false, + }; - state: EMSESPSettingsFormState = { - processing: false - } + componentDidMount() { + ValidatorForm.addValidationRule("isOptionalIP", optional(isIP)); + } - componentDidMount() { - ValidatorForm.addValidationRule('isOptionalIP', optional(isIP)); - } + changeBoardProfile = (event: React.ChangeEvent) => { + const { data, setData } = this.props; + setData({ + ...data, + board_profile: event.target.value, + }); - changeBoardProfile = (event: React.ChangeEvent) => { - const { data, setData } = this.props; + if (event.target.value === "CUSTOM") return; + + this.setState({ processing: true }); + redirectingAuthorizedFetch(BOARD_PROFILE_ENDPOINT, { + method: "POST", + body: JSON.stringify({ code: event.target.value }), + headers: { + "Content-Type": "application/json", + }, + }) + .then((response) => { + if (response.status === 200) { + return response.json(); + } + throw Error("Unexpected response code: " + response.status); + }) + .then((json) => { + this.props.enqueueSnackbar("Profile loaded", { variant: "success" }); setData({ - ...data, - board_profile: event.target.value + ...data, + led_gpio: json.led_gpio, + dallas_gpio: json.dallas_gpio, + rx_gpio: json.rx_gpio, + tx_gpio: json.tx_gpio, + pbutton_gpio: json.pbutton_gpio, + board_profile: event.target.value, }); - - if (event.target.value === "CUSTOM") - return; - - this.setState({ processing: true }); - redirectingAuthorizedFetch(BOARD_PROFILE_ENDPOINT, { - method: "POST", - body: JSON.stringify({ code: event.target.value }), - headers: { - "Content-Type": "application/json", - }, - }) - .then((response) => { - if (response.status === 200) { - return response.json(); - } - throw Error("Unexpected response code: " + response.status); - }) - .then((json) => { - this.props.enqueueSnackbar("Profile loaded", { variant: 'success' }); - setData({ - ...data, - led_gpio: json.led_gpio, - dallas_gpio: json.dallas_gpio, - rx_gpio: json.rx_gpio, - tx_gpio: json.tx_gpio, - pbutton_gpio: json.pbutton_gpio, - board_profile: event.target.value - }); - this.setState({ processing: false }); - }) - .catch((error) => { - this.props.enqueueSnackbar( - error.message || "Problem fetching board profile", - { variant: "warning" } - ); - this.setState({ processing: false }); - }); - }; - - render() { - const { data, saveData, handleValueChange } = this.props; - return ( - - - - Modify any of the EMS-ESP settings here. For help refer to the {'online documentation'}. - - - -

- - EMS Bus - - - - - - Off - EMS - EMS+ - HT3 - Hardware - - - - - Service Key (0x0B) - Modem (0x0D) - Terminal (0x0A) - Time Module (0x0F) - Alarm Module (0x12) - - - - - - - -

- - Board Profile - - - - - Select a pre-configured board layout to automatically set the GPIO pins, or set your own custom configuration - - - - - {boardProfileSelectItems()} - Custom... - - - { (data.board_profile === "CUSTOM") && - - - - - - - - - - - - - - - - - - } - -

- - Options - - - { data.dallas_gpio !== 0 && - - } - label="Enable Dallas parasite mode" - /> - } - { data.led_gpio !== 0 && - - } - label = "Hide LED" - /> - } - - - - - } - label="Shower Timer" - /> - {/* - } - label="Shower Alert" - /> */} - - - } - label="Enable API write commands" - /> - - } - label="Enable ADC" - /> -

- - Syslog - - - - } - label="Enable Syslog" - /> - - { data.syslog_enabled && - - - - - - - - - - OFF - ERR - NOTICE - INFO - DEBUG - ALL - - - - - - - } - label="Output EMS telegrams in raw format" - /> - - } - -

- - } variant="contained" color="primary" type="submit"> - Save - - -
+ this.setState({ processing: false }); + }) + .catch((error) => { + this.props.enqueueSnackbar( + error.message || "Problem fetching board profile", + { variant: "warning" } ); - } + this.setState({ processing: false }); + }); + }; + render() { + const { data, saveData, handleValueChange } = this.props; + return ( + + + + Modify any of the EMS-ESP settings here. For help refer to the{" "} + + {"online documentation"} + + . + + + +

+ + EMS Bus + + + + + + Off + EMS + EMS+ + HT3 + Hardware + + + + + Service Key (0x0B) + Modem (0x0D) + Terminal (0x0A) + Time Module (0x0F) + Alarm Module (0x12) + + + + + + + +

+ + Board Profile + + + + + + Select a pre-configured board layout to automatically set the GPIO + pins, or set your own custom configuration + + + + + + {boardProfileSelectItems()} + + Custom... + + + + {data.board_profile === "CUSTOM" && ( + + + + + + + + + + + + + + + + + + )} + +

+ + Options + + + {data.led_gpio !== 0 && ( + + } + label="Hide LED" + /> + )} + + {data.dallas_gpio !== 0 && ( + + } + label="Enable Dallas parasite mode" + /> + )} + + + } + label="Enable API write commands" + /> + + } + label="Enable ADC" + /> + + + } + label="Enable Shower Timer" + /> + + } + label="Enable Shower Alert" + /> + + +

+ + Syslog + + + + } + label="Enable Syslog" + /> + + {data.syslog_enabled && ( + + + + + + + + + + OFF + ERR + NOTICE + INFO + DEBUG + ALL + + + + + + + } + label="Output EMS telegrams in raw format" + /> + + )} + +

+ + } + variant="contained" + color="primary" + type="submit" + > + Save + + +
+ ); + } } export default withAuthenticatedContext(withWidth()(EMSESPSettingsForm)); diff --git a/lib/framework/MqttSettingsService.h b/lib/framework/MqttSettingsService.h index 52e5ce5f9..0c2d53cda 100644 --- a/lib/framework/MqttSettingsService.h +++ b/lib/framework/MqttSettingsService.h @@ -90,7 +90,7 @@ class MqttSettings { uint8_t bool_format; uint8_t ha_climate_format; bool ha_enabled; - bool nested_format; + uint8_t nested_format; uint8_t subscribe_format; static void read(MqttSettings & settings, JsonObject & root); diff --git a/lib_standalone/ESP8266React.h b/lib_standalone/ESP8266React.h index 8d0c48c22..e452a3b1b 100644 --- a/lib_standalone/ESP8266React.h +++ b/lib_standalone/ESP8266React.h @@ -33,7 +33,7 @@ class DummySettings { bool mqtt_retain = false; bool enabled = true; uint8_t dallas_format = 1; - bool nested_format = true; + uint8_t nested_format = 1; uint8_t ha_climate_format = 1; bool ha_enabled = true; String base = "ems-esp"; diff --git a/mock-api/server.js b/mock-api/server.js index d4025c123..d961263a5 100644 --- a/mock-api/server.js +++ b/mock-api/server.js @@ -72,7 +72,7 @@ const mqtt_settings = { "client_id": "ems-esp32", "keep_alive": 60, "clean_session": true, "max_topic_length": 128, "publish_time_boiler": 10, "publish_time_thermostat": 10, "publish_time_solar": 10, "publish_time_mixer": 10, "publish_time_other": 10, "publish_time_sensor": 10, "mqtt_qos": 0, "mqtt_retain": false, "dallas_format": 1, - "bool_format": 1, "ha_climate_format": 1, "ha_enabled": true, "nested_format": true, "subscribe_format": 0 + "bool_format": 1, "ha_climate_format": 1, "ha_enabled": true, "nested_format": 1, "subscribe_format": 0 }; const mqtt_status = { "enabled": true, "connected": true, "client_id": "ems-esp32", "disconnect_reason": 0, "mqtt_fails": 0 diff --git a/src/default_settings.h b/src/default_settings.h index 576c794c3..d5b442127 100644 --- a/src/default_settings.h +++ b/src/default_settings.h @@ -145,7 +145,7 @@ #endif #ifndef EMSESP_DEFAULT_NESTED_FORMAT -#define EMSESP_DEFAULT_NESTED_FORMAT true +#define EMSESP_DEFAULT_NESTED_FORMAT 1 #endif #ifndef EMSESP_DEFAULT_SUBSCRIBE_FORMAT diff --git a/src/devices/thermostat.cpp b/src/devices/thermostat.cpp index 9ca1157bb..6078ae92d 100644 --- a/src/devices/thermostat.cpp +++ b/src/devices/thermostat.cpp @@ -376,7 +376,7 @@ void Thermostat::register_mqtt_ha_config_hc(uint8_t hc_num) { doc["~"] = Mqtt::base(); // ems-esp char topic_t[Mqtt::MQTT_TOPIC_MAX_SIZE]; - if (Mqtt::nested_format()) { + if (Mqtt::nested_format() == 1) { snprintf_P(topic_t, sizeof(topic_t), PSTR("~/%s"), Mqtt::tag_to_topic(EMSdevice::DeviceType::THERMOSTAT, DeviceValueTAG::TAG_NONE).c_str()); char mode_str_tpl[40]; @@ -1148,7 +1148,7 @@ bool Thermostat::set_building(const char * value, const int8_t id) { if ((model() == EMS_DEVICE_FLAG_RC300) || (model() == EMS_DEVICE_FLAG_RC100)) { if (Helpers::value2enum(value, bd, FL_(enum_ibaBuildingType))) { LOG_INFO(F("Setting building to %s"), value); - write_command(0x240, 9, bd , 0x240); + write_command(0x240, 9, bd, 0x240); return true; } } else { diff --git a/src/emsesp.cpp b/src/emsesp.cpp index 3732afab7..aa9fdfb68 100644 --- a/src/emsesp.cpp +++ b/src/emsesp.cpp @@ -435,7 +435,7 @@ void EMSESP::publish_device_values(uint8_t device_type) { JsonObject json = doc.to(); bool need_publish = false; - bool nested = Mqtt::nested_format(); + uint8_t nested = Mqtt::nested_format(); // group by device type for (const auto & emsdevice : emsdevices) { diff --git a/src/mqtt.cpp b/src/mqtt.cpp index 50f570e56..88910655c 100644 --- a/src/mqtt.cpp +++ b/src/mqtt.cpp @@ -39,7 +39,7 @@ uint8_t Mqtt::dallas_format_; uint8_t Mqtt::bool_format_; uint8_t Mqtt::ha_climate_format_; bool Mqtt::ha_enabled_; -bool Mqtt::nested_format_; +uint8_t Mqtt::nested_format_; uint8_t Mqtt::subscribe_format_; std::deque Mqtt::mqtt_messages_; @@ -898,7 +898,7 @@ void Mqtt::publish_mqtt_ha_sensor(uint8_t type, // EMSdevice DynamicJsonDocument doc(EMSESP_JSON_SIZE_HA_CONFIG); bool have_tag = !EMSdevice::tag_to_string(tag).empty() && (device_type != EMSdevice::DeviceType::BOILER); // ignore boiler - bool is_nested = nested_format_ || (device_type == EMSdevice::DeviceType::BOILER); // boiler never uses nested + bool is_nested = (nested_format_ == 1) || (device_type == EMSdevice::DeviceType::BOILER); // boiler never uses nested // create entity by add the tag if present, seperating with a . char new_entity[50]; @@ -1005,7 +1005,7 @@ const std::string Mqtt::tag_to_topic(uint8_t device_type, uint8_t tag) { } // if there is a tag add it - if ((EMSdevice::tag_to_mqtt(tag).empty()) || (nested_format_ && (device_type != EMSdevice::DeviceType::BOILER))) { + if ((EMSdevice::tag_to_mqtt(tag).empty()) || ((nested_format_ == 1) && (device_type != EMSdevice::DeviceType::BOILER))) { return EMSdevice::device_type_2_device_name(device_type) + "_data"; } else { return EMSdevice::device_type_2_device_name(device_type) + "_data_" + EMSdevice::tag_to_mqtt(tag); diff --git a/src/mqtt.h b/src/mqtt.h index 1c2447cee..c45aded12 100644 --- a/src/mqtt.h +++ b/src/mqtt.h @@ -158,11 +158,11 @@ class Mqtt { return bool_format_; } - static bool nested_format() { + static uint8_t nested_format() { return nested_format_; } - static void nested_format(bool nested_format) { + static void nested_format(uint8_t nested_format) { nested_format_ = nested_format; } @@ -274,7 +274,7 @@ class Mqtt { static uint8_t bool_format_; static uint8_t ha_climate_format_; static bool ha_enabled_; - static bool nested_format_; + static uint8_t nested_format_; static uint8_t subscribe_format_; }; diff --git a/src/test/test.cpp b/src/test/test.cpp index da8c97dc7..eb1508ea8 100644 --- a/src/test/test.cpp +++ b/src/test/test.cpp @@ -376,7 +376,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd) { if (command == "boiler") { shell.printfln(F("Testing boiler...")); Mqtt::ha_enabled(false); - Mqtt::nested_format(true); + Mqtt::nested_format(1); run_test("boiler"); shell.invoke_command("show devices"); @@ -414,8 +414,8 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd) { if (command == "ha") { shell.printfln(F("Testing HA discovery")); Mqtt::ha_enabled(true); - // Mqtt::nested_format(true); - Mqtt::nested_format(false); + // Mqtt::nested_format(1); + Mqtt::nested_format(2); // run_test("boiler"); run_test("thermostat"); @@ -436,11 +436,11 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd) { run_test("mixer"); // first with nested - Mqtt::nested_format(true); + Mqtt::nested_format(1); shell.invoke_command("call system publish"); // then without nested - Mqtt::nested_format(false); + Mqtt::nested_format(2); shell.invoke_command("call system publish"); shell.invoke_command("show mqtt"); }