Merge pull request #797 from MichaelDvP/dev

Some actual additions/changes, see description
This commit is contained in:
MichaelDvP
2022-12-09 10:11:40 +01:00
committed by GitHub
30 changed files with 565 additions and 420 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -8,7 +8,7 @@
"@emotion/styled": "^11.10.5",
"@msgpack/msgpack": "^2.8.0",
"@mui/icons-material": "^5.10.16",
"@mui/material": "^5.10.16",
"@mui/material": "^5.10.17",
"@table-library/react-table-library": "4.0.23",
"@types/lodash": "^4.14.191",
"@types/node": "^18.11.11",

View File

@@ -162,10 +162,6 @@ const MqttSettingsForm: FC = () => {
</ValidatedTextField>
</Grid>
</Grid>
<BlockFormControlLabel
control={<Checkbox name="multiple_instances" checked={data.multiple_instances} onChange={updateFormValue} />}
label={LL.MQTT_MULTIPLE_INSTANCES()}
/>
<BlockFormControlLabel
control={<Checkbox name="clean_session" checked={data.clean_session} onChange={updateFormValue} />}
label={LL.MQTT_CLEAN_SESSION()}
@@ -224,17 +220,25 @@ const MqttSettingsForm: FC = () => {
/>
</Grid>
{data.ha_enabled && (
<Grid item sx={{ pb: 3 }}>
<ValidatedTextField
name="discovery_prefix"
label={LL.MQTT_PUBLISH_TEXT_4()}
fullWidth
variant="outlined"
value={data.discovery_prefix}
onChange={updateFormValue}
size="small"
/>
</Grid>
<>
<Grid item>
<BlockFormControlLabel
control={<Checkbox name="multiple_instances" checked={data.multiple_instances} onChange={updateFormValue} />}
label={LL.MQTT_MULTIPLE_INSTANCES()}
/>
</Grid>
<Grid item sx={{ pb: 3 }}>
<ValidatedTextField
name="discovery_prefix"
label={LL.MQTT_PUBLISH_TEXT_4()}
fullWidth
variant="outlined"
value={data.discovery_prefix}
onChange={updateFormValue}
size="small"
/>
</Grid>
</>
)}
</Grid>
)}
@@ -242,6 +246,22 @@ const MqttSettingsForm: FC = () => {
{LL.MQTT_PUBLISH_INTERVALS()}&nbsp;(0=auto)
</Typography>
<Grid container spacing={1} direction="row" justifyContent="flex-start" alignItems="flex-start">
<Grid item xs={6} sm={4}>
<ValidatedTextField
fieldErrors={fieldErrors}
name="publish_time_heartbeat"
label={LL.MQTT_INT_HEARTBEAT()}
InputProps={{
endAdornment: <InputAdornment position="end">{LL.SECONDS()}</InputAdornment>
}}
fullWidth
variant="outlined"
value={numberValue(data.publish_time_heartbeat)}
type="number"
onChange={updateFormValue}
margin="normal"
/>
</Grid>
<Grid item xs={6} sm={4}>
<ValidatedTextField
fieldErrors={fieldErrors}

View File

@@ -1,4 +1,5 @@
import { FC, useContext, useEffect, useState } from 'react';
import { useSnackbar } from 'notistack';
import {
Avatar,
@@ -18,6 +19,7 @@ import LockOpenIcon from '@mui/icons-material/LockOpen';
import DeleteIcon from '@mui/icons-material/Delete';
import SaveIcon from '@mui/icons-material/Save';
import LockIcon from '@mui/icons-material/Lock';
import PowerSettingsNewIcon from '@mui/icons-material/PowerSettingsNew';
import {
BlockFormControlLabel,
@@ -25,11 +27,13 @@ import {
FormLoader,
SectionContent,
ValidatedPasswordField,
ValidatedTextField
ValidatedTextField,
MessageBox
} from '../../components';
import { NetworkSettings } from '../../types';
import * as NetworkApi from '../../api/network';
import { numberValue, updateValue, useRest } from '../../utils';
import * as EMSESP from '../../project/api';
import { WiFiConnectionContext } from './WiFiConnectionContext';
import { isNetworkOpen, networkSecurityMode } from './WiFiNetworkSelector';
@@ -38,14 +42,17 @@ import { validate } from '../../validators';
import { createNetworkSettingsValidator } from '../../validators/network';
import { useI18nContext } from '../../i18n/i18n-react';
import RestartMonitor from '../system/RestartMonitor';
const WiFiSettingsForm: FC = () => {
const { LL } = useI18nContext();
const { enqueueSnackbar } = useSnackbar();
const { selectedNetwork, deselectNetwork } = useContext(WiFiConnectionContext);
const [initialized, setInitialized] = useState(false);
const { loadData, saving, data, setData, saveData, errorMessage } = useRest<NetworkSettings>({
const [restarting, setRestarting] = useState(false);
const { loadData, saving, data, setData, saveData, errorMessage, restartNeeded } = useRest<NetworkSettings>({
read: NetworkApi.readNetworkSettings,
update: NetworkApi.updateNetworkSettings
});
@@ -62,7 +69,9 @@ const WiFiSettingsForm: FC = () => {
bandwidth20: false,
tx_power: 20,
nosleep: false,
enableMDNS: true
enableMDNS: true,
enableCORS: false,
CORSOrigin: '*'
});
}
setInitialized(true);
@@ -90,6 +99,15 @@ const WiFiSettingsForm: FC = () => {
}
};
const restart = async () => {
try {
await EMSESP.restart();
setRestarting(true);
} catch (error) {
enqueueSnackbar(LL.PROBLEM_UPDATING(), { variant: 'error' });
}
};
return (
<>
<Typography sx={{ pt: 2 }} variant="h6" color="primary">
@@ -162,11 +180,6 @@ const WiFiSettingsForm: FC = () => {
label={LL.NETWORK_LOW_BAND()}
/>
<BlockFormControlLabel
control={<Checkbox name="enableMDNS" checked={data.enableMDNS} onChange={updateFormValue} />}
label={LL.NETWORK_USE_DNS()}
/>
<Typography sx={{ pt: 2 }} variant="h6" color="primary">
{LL.GENERAL_OPTIONS()}
</Typography>
@@ -182,6 +195,28 @@ const WiFiSettingsForm: FC = () => {
margin="normal"
/>
<BlockFormControlLabel
control={<Checkbox name="enableMDNS" checked={data.enableMDNS} onChange={updateFormValue} />}
label={LL.NETWORK_USE_DNS()}
/>
<BlockFormControlLabel
control={<Checkbox name="enableCORS" checked={data.enableCORS} onChange={updateFormValue} />}
label={LL.NETWORK_ENABLE_CORS()}
/>
{data.enableCORS && (
<ValidatedTextField
fieldErrors={fieldErrors}
name="CORSOrigin"
label={LL.NETWORK_CORS_ORIGIN()}
fullWidth
variant="outlined"
value={data.CORSOrigin}
onChange={updateFormValue}
margin="normal"
/>
)}
<BlockFormControlLabel
control={<Checkbox name="enableIPv6" checked={data.enableIPv6} onChange={updateFormValue} />}
label={LL.NETWORK_ENABLE_IPV6()}
@@ -245,6 +280,14 @@ const WiFiSettingsForm: FC = () => {
/>
</>
)}
{restartNeeded && (
<MessageBox my={2} level="warning" message={LL.RESTART_TEXT()}>
<Button startIcon={<PowerSettingsNewIcon />} variant="contained" color="error" onClick={restart}>
{LL.RESTART()}
</Button>
</MessageBox>
)}
{!restartNeeded && (
<ButtonRow>
<Button
startIcon={<SaveIcon />}
@@ -257,13 +300,14 @@ const WiFiSettingsForm: FC = () => {
{LL.SAVE()}
</Button>
</ButtonRow>
)}
</>
);
};
return (
<SectionContent title={LL.SETTINGS_OF(LL.NETWORK(1))} titleGutter>
{content()}
{restarting ? <RestartMonitor /> : content()}
</SectionContent>
);
};

View File

@@ -3,6 +3,7 @@ import type { Translation } from '../i18n-types';
/* eslint-disable */
const de: Translation = {
// ...en as Translation,
LANGUAGE: 'Sprache',
RETRY: 'Neuer Versuch',
LOADING: 'Laden',
@@ -190,8 +191,8 @@ const de: Translation = {
SYSTEM_FACTORY_TEXT_DIALOG: 'Sind Sie sicher alle Einstellungen auf Werkseinstellung zu setzen?',
VERSION_CHECK: 'Versionsprüfung',
THE_LATEST: 'Die neueste',
OFFICIAL: 'official',
DEVELOPMENT: 'development',
OFFICIAL: 'offizielle',
DEVELOPMENT: 'Entwicklungs',
VERSION_IS: 'Version ist',
RELEASE_NOTES: 'Versionshinweise',
EMS_ESP_VER: 'EMS-ESP Version',
@@ -245,9 +246,10 @@ const de: Translation = {
MQTT_INT_THERMOSTATS: 'Thermostate',
MQTT_INT_SOLAR: 'Solarmodule',
MQTT_INT_MIXER: 'Mischermodule',
MQTT_INT_HEARTBEAT: 'Heartbeat',
MQTT_QUEUE: 'MQTT Queue',
DEFAULT: 'Standard',
MQTT_MULTIPLE_INSTANCES: 'Enable Multiple Instances of EMS-ESP',
MQTT_MULTIPLE_INSTANCES: 'Erlaube EMS-ESP Mehrfachinstanzen',
MQTT_CLEAN_SESSION: 'Setze `Clean Session`',
MQTT_RETAIN_FLAG: 'Setze `Retain flag` immer',
INACTIVE: 'Inaktiv',
@@ -283,6 +285,8 @@ const de: Translation = {
NETWORK_DISABLE_SLEEP: 'Deaktiviere WiFi Schlafmodus',
NETWORK_LOW_BAND: 'Verwende niedrige WiFi Bandbreite',
NETWORK_USE_DNS: 'Aktiviere mDNS Service',
NETWORK_ENABLE_CORS: 'Aktiviere CORS',
NETWORK_CORS_ORIGIN: 'CORS origin',
NETWORK_ENABLE_IPV6: 'Aktiviere IPv6 Unterstützung',
NETWORK_FIXED_IP: 'Feste IP Adresse',
NETWORK_GATEWAY: 'Gateway',

View File

@@ -245,6 +245,7 @@ const en: Translation = {
MQTT_INT_THERMOSTATS: 'Thermostats',
MQTT_INT_SOLAR: 'Solar Modules',
MQTT_INT_MIXER: 'Mixer Modules',
MQTT_INT_HEARTBEAT: 'Heartbeat',
MQTT_QUEUE: 'MQTT Queue',
DEFAULT: 'Default',
MQTT_MULTIPLE_INSTANCES: 'Enable Multiple Instances of EMS-ESP',
@@ -283,6 +284,8 @@ const en: Translation = {
NETWORK_DISABLE_SLEEP: 'Disable WiFi Sleep Mode',
NETWORK_LOW_BAND: 'Use Lower WiFi Bandwidth',
NETWORK_USE_DNS: 'Enable mDNS Service',
NETWORK_ENABLE_CORS: 'Enable CORS',
NETWORK_CORS_ORIGIN: 'CORS origin',
NETWORK_ENABLE_IPV6: 'Enable IPv6 support',
NETWORK_FIXED_IP: 'Use Fixed IP address',
NETWORK_GATEWAY: 'Gateway',

View File

@@ -245,6 +245,7 @@ const nl: Translation = {
MQTT_INT_THERMOSTATS: 'Thermostaten',
MQTT_INT_SOLAR: 'Solar Modules',
MQTT_INT_MIXER: 'Mixer Modules',
MQTT_INT_HEARTBEAT: 'Heartbeat',
MQTT_QUEUE: 'MQTT Queue',
DEFAULT: 'Default',
MQTT_MULTIPLE_INSTANCES: 'Enable Multiple Instances of EMS-ESP',
@@ -283,6 +284,8 @@ const nl: Translation = {
NETWORK_DISABLE_SLEEP: 'WiFi Sleep Mode uitzetten',
NETWORK_LOW_BAND: 'Lagere WiFi bandbreedte gebruiken',
NETWORK_USE_DNS: 'Activeer mDNS Service',
NETWORK_ENABLE_CORS: 'Activeer CORS',
NETWORK_CORS_ORIGIN: 'CORS origin',
NETWORK_ENABLE_IPV6: 'Activeer IPv6 support',
NETWORK_FIXED_IP: 'Gebruik vast IP addres',
NETWORK_GATEWAY: 'Gateway',

View File

@@ -245,6 +245,7 @@ const no: Translation = {
MQTT_INT_THERMOSTATS: 'Termostat',
MQTT_INT_SOLAR: 'Solpaneler',
MQTT_INT_MIXER: 'Blandeventil',
MQTT_INT_HEARTBEAT: 'Heartbeat',
MQTT_QUEUE: 'MQTT Queue',
DEFAULT: 'Standard',
MQTT_MULTIPLE_INSTANCES: 'Enable Multiple Instances of EMS-ESP',
@@ -283,6 +284,8 @@ const no: Translation = {
NETWORK_DISABLE_SLEEP: 'Hindre at trådløst nettverk går i Sleep Mode',
NETWORK_LOW_BAND: 'Benytt smalere båndbredde på trådløst nettverk',
NETWORK_USE_DNS: 'Aktiviser mDNS Service',
NETWORK_ENABLE_CORS: 'Aktiviser CORS',
NETWORK_CORS_ORIGIN: 'CORS origin',
NETWORK_ENABLE_IPV6: 'Aktiviser IPv6 støtte',
NETWORK_FIXED_IP: 'Benytt statisk IP adresse',
NETWORK_GATEWAY: 'Gateway',

View File

@@ -245,6 +245,7 @@ const pl: BaseTranslation = {
MQTT_INT_THERMOSTATS: 'Termostaty',
MQTT_INT_SOLAR: 'Panele solarne',
MQTT_INT_MIXER: 'Mieszacze',
MQTT_INT_HEARTBEAT: 'Heartbeat',
MQTT_QUEUE: 'Kolejka MQTT',
DEFAULT: '{{Pozostałe|Domyślna|}}',
MQTT_MULTIPLE_INSTANCES: 'Enable Multiple Instances of EMS-ESP',
@@ -283,6 +284,8 @@ const pl: BaseTranslation = {
NETWORK_DISABLE_SLEEP: 'Wyłącz tryb usypiania WiFi',
NETWORK_LOW_BAND: 'Używaj zmniejszonej przepustowości WiFi',
NETWORK_USE_DNS: 'Włącz wsparcie dla mDNS',
NETWORK_ENABLE_CORS: 'Włącz wsparcie dla CORS',
NETWORK_CORS_ORIGIN: 'CORS origin',
NETWORK_ENABLE_IPV6: 'Włącz wsparcie dla IPv6',
NETWORK_FIXED_IP: 'Użyj stałego adresu IP',
NETWORK_GATEWAY: 'Brama',

View File

@@ -245,6 +245,7 @@ const se: Translation = {
MQTT_INT_THERMOSTATS: 'Termostater',
MQTT_INT_SOLAR: 'Solpaneler',
MQTT_INT_MIXER: 'Blandarventiler',
MQTT_INT_HEARTBEAT: 'Heartbeat',
MQTT_QUEUE: 'MQTT Queue',
DEFAULT: 'Standard',
MQTT_MULTIPLE_INSTANCES: 'Enable Multiple Instances of EMS-ESP',
@@ -283,6 +284,8 @@ const se: Translation = {
NETWORK_DISABLE_SLEEP: 'Inaktivera sömnläge',
NETWORK_LOW_BAND: 'Använd lägre bandbredd',
NETWORK_USE_DNS: 'Aktivera mDNS-tjänsten',
NETWORK_ENABLE_CORS: 'Aktivera CORS',
NETWORK_CORS_ORIGIN: 'CORS origin',
NETWORK_ENABLE_IPV6: 'Aktivera IPv6-support',
NETWORK_FIXED_IP: 'Använd statiskt IP',
NETWORK_GATEWAY: 'Gateway',

View File

@@ -36,6 +36,7 @@ export interface MqttSettings {
publish_time_mixer: number;
publish_time_other: number;
publish_time_sensor: number;
publish_time_heartbeat: number;
mqtt_qos: number;
mqtt_retain: boolean;
ha_enabled: boolean;

View File

@@ -48,6 +48,8 @@ export interface NetworkSettings {
dns_ip_1?: string;
dns_ip_2?: string;
enableMDNS: boolean;
enableCORS: boolean;
CORSOrigin: string;
}
export interface WiFiNetworkList {

View File

@@ -11,5 +11,9 @@ export const MQTT_SETTINGS_VALIDATOR = new Schema({
keep_alive: [
{ required: true, message: 'Keep alive is required' },
{ type: 'number', min: 1, max: 86400, message: 'Keep alive must be between 1 and 86400' }
],
publish_time_heartbeat: [
{ required: true, message: 'Heartbeat is required' },
{ type: 'number', min: 10, max: 86400, message: 'Heartbeat must be between 10 and 86400' }
]
});

View File

@@ -46,6 +46,13 @@ ESP8266React::ESP8266React(AsyncWebServer * server, FS * fs)
void ESP8266React::begin() {
_networkSettingsService.begin();
_networkSettingsService.read([&](NetworkSettings & networkSettings) {
if (networkSettings.enableCORS) {
DefaultHeaders::Instance().addHeader("Access-Control-Allow-Origin", networkSettings.CORSOrigin);
DefaultHeaders::Instance().addHeader("Access-Control-Allow-Headers", "Accept, Content-Type, Authorization");
DefaultHeaders::Instance().addHeader("Access-Control-Allow-Credentials", "true");
}
});
_apSettingsService.begin();
_ntpSettingsService.begin();
_otaSettingsService.begin();

View File

@@ -163,6 +163,7 @@ void MqttSettings::read(MqttSettings & settings, JsonObject & root) {
root["publish_time_mixer"] = settings.publish_time_mixer;
root["publish_time_other"] = settings.publish_time_other;
root["publish_time_sensor"] = settings.publish_time_sensor;
root["publish_time_heartbeat"] = settings.publish_time_heartbeat;
root["mqtt_qos"] = settings.mqtt_qos;
root["mqtt_retain"] = settings.mqtt_retain;
root["ha_enabled"] = settings.ha_enabled;
@@ -197,6 +198,7 @@ StateUpdateResult MqttSettings::update(JsonObject & root, MqttSettings & setting
newSettings.publish_time_mixer = root["publish_time_mixer"] | EMSESP_DEFAULT_PUBLISH_TIME;
newSettings.publish_time_other = root["publish_time_other"] | EMSESP_DEFAULT_PUBLISH_TIME;
newSettings.publish_time_sensor = root["publish_time_sensor"] | EMSESP_DEFAULT_PUBLISH_TIME;
newSettings.publish_time_heartbeat = root["publish_time_heartbeat"] | EMSESP_DEFAULT_PUBLISH_HEARTBEAT;
newSettings.ha_enabled = root["ha_enabled"] | EMSESP_DEFAULT_HA_ENABLED;
newSettings.nested_format = root["nested_format"] | EMSESP_DEFAULT_NESTED_FORMAT;
@@ -285,6 +287,11 @@ StateUpdateResult MqttSettings::update(JsonObject & root, MqttSettings & setting
changed = true;
}
if (newSettings.publish_time_heartbeat != settings.publish_time_heartbeat) {
emsesp::EMSESP::mqtt_.set_publish_time_heartbeat(newSettings.publish_time_heartbeat);
changed = true;
}
if (changed) {
emsesp::EMSESP::mqtt_.reset_mqtt();
}

View File

@@ -90,6 +90,7 @@ class MqttSettings {
uint16_t publish_time_mixer;
uint16_t publish_time_other;
uint16_t publish_time_sensor;
uint16_t publish_time_heartbeat;
uint8_t mqtt_qos;
bool mqtt_retain;
bool ha_enabled;

View File

@@ -39,6 +39,8 @@ class NetworkSettings {
int8_t tx_power;
bool nosleep;
bool enableMDNS;
bool enableCORS;
String CORSOrigin;
// optional configuration for static IP address
IPAddress localIP;
@@ -58,6 +60,8 @@ class NetworkSettings {
root["tx_power"] = settings.tx_power;
root["nosleep"] = settings.nosleep;
root["enableMDNS"] = settings.enableMDNS;
root["enableCORS"] = settings.enableCORS;
root["CORSOrigin"] = settings.CORSOrigin;
// extended settings
JsonUtils::writeIP(root, "local_ip", settings.localIP);
@@ -68,6 +72,8 @@ class NetworkSettings {
}
static StateUpdateResult update(JsonObject & root, NetworkSettings & settings) {
auto enableCORS = settings.enableCORS;
auto CORSOrigin = settings.CORSOrigin;
settings.ssid = root["ssid"] | FACTORY_WIFI_SSID;
settings.password = root["password"] | FACTORY_WIFI_PASSWORD;
settings.hostname = root["hostname"] | FACTORY_WIFI_HOSTNAME;
@@ -77,6 +83,8 @@ class NetworkSettings {
settings.tx_power = root["tx_power"] | 20;
settings.nosleep = root["nosleep"] | false;
settings.enableMDNS = root["enableMDNS"] | true;
settings.enableCORS = root["enableCORS"] | false;
settings.CORSOrigin = root["CORSOrigin"] | "*";
// extended settings
JsonUtils::readIP(root, "local_ip", settings.localIP);
@@ -97,6 +105,9 @@ class NetworkSettings {
if (settings.staticIPConfig && (IPUtils::isNotSet(settings.localIP) || IPUtils::isNotSet(settings.gatewayIP) || IPUtils::isNotSet(settings.subnetMask))) {
settings.staticIPConfig = false;
}
if (enableCORS != settings.enableCORS || CORSOrigin != settings.CORSOrigin) {
return StateUpdateResult::CHANGED_RESTART; // tell WebUI that a restart is needed
}
return StateUpdateResult::CHANGED;
}

View File

@@ -89,6 +89,7 @@ class DummySettings {
uint16_t publish_time_mixer = 10;
uint16_t publish_time_other = 10;
uint16_t publish_time_sensor = 10;
uint16_t publish_time_heartbeat = 60;
String hostname = "ems-esp";
String jwtSecret = "ems-esp";
@@ -102,6 +103,8 @@ class DummySettings {
String dnsIP2 = "";
bool enableIPv6 = false;
bool enableMDNS = true;
bool enableCORS = false;
String CORSOrigin = "*";
uint8_t phy_type = 0;
uint8_t eth_power = 0; // 0 means -1

View File

@@ -122,7 +122,11 @@ network_settings = {
hostname: 'ems-esp',
nosleep: true,
tx_power: 20,
bandwidth20: false,
static_ip_config: false,
enableMDNS: true,
enableCORS: false,
CORSOrigin: '*',
}
const network_status = {
status: 3,
@@ -226,6 +230,7 @@ mqtt_settings = {
publish_time_mixer: 10,
publish_time_other: 10,
publish_time_sensor: 10,
publish_time_heartbeat: 60,
mqtt_qos: 0,
mqtt_retain: false,
ha_enabled: true,

View File

@@ -296,7 +296,7 @@ void AnalogSensor::loop() {
}
// update analog information name and offset
bool AnalogSensor::update(uint8_t gpio, const std::string & name, float offset, float factor, uint8_t uom, int8_t type) {
bool AnalogSensor::update(uint8_t gpio, const std::string & name, double offset, double factor, uint8_t uom, int8_t type) {
boolean found_sensor = false; // see if we can find the sensor in our customization list
EMSESP::webCustomizationService.update(
@@ -375,7 +375,7 @@ void AnalogSensor::publish_sensor(const Sensor & sensor) const {
snprintf(topic, sizeof(topic), "%s%s/%s", F_(analogsensor), "_data", sensor.name().c_str());
}
char payload[10];
Mqtt::publish(topic, Helpers::render_value(payload, sensor.value(), 2)); // always publish as floats
Mqtt::publish(topic, Helpers::render_value(payload, sensor.value(), 2)); // always publish as doubles
}
}
@@ -422,7 +422,7 @@ void AnalogSensor::publish_values(const bool force) {
case AnalogType::PWM_0:
case AnalogType::PWM_1:
case AnalogType::PWM_2:
dataSensor["value"] = serialized(Helpers::render_value(s, sensor.value(), 2)); // float
dataSensor["value"] = serialized(Helpers::render_value(s, sensor.value(), 2)); // double
break;
default:
dataSensor["value"] = (uint8_t)sensor.value(); // convert to char for 1 or 0
@@ -586,7 +586,7 @@ bool AnalogSensor::command_info(const char * value, const int8_t id, JsonObject
}
// this creates the sensor, initializing everything
AnalogSensor::Sensor::Sensor(const uint8_t gpio, const std::string & name, const float offset, const float factor, const uint8_t uom, const int8_t type)
AnalogSensor::Sensor::Sensor(const uint8_t gpio, const std::string & name, const double offset, const double factor, const uint8_t uom, const int8_t type)
: gpio_(gpio)
, name_(name)
, offset_(offset)

View File

@@ -35,10 +35,10 @@ class AnalogSensor {
public:
class Sensor {
public:
Sensor(const uint8_t gpio, const std::string & name, const float offset, const float factor, const uint8_t uom, const int8_t type);
Sensor(const uint8_t gpio, const std::string & name, const double offset, const double factor, const uint8_t uom, const int8_t type);
~Sensor() = default;
void set_offset(const float offset) {
void set_offset(const double offset) {
offset_ = offset;
}
@@ -52,23 +52,23 @@ class AnalogSensor {
return gpio_;
}
float value() const {
double value() const {
return value_;
}
void set_value(float value) {
void set_value(const double value) {
value_ = value;
}
float factor() const {
double factor() const {
return factor_;
}
void set_factor(float factor) {
void set_factor(const double factor) {
factor_ = factor;
}
float offset() const {
double offset() const {
return offset_;
}
@@ -84,7 +84,7 @@ class AnalogSensor {
return type_;
}
void set_type(int8_t type) {
void set_type(const int8_t type) {
type_ = type;
}
@@ -101,10 +101,10 @@ class AnalogSensor {
private:
uint8_t gpio_;
std::string name_;
float offset_;
float factor_;
double offset_;
double factor_;
uint8_t uom_;
float value_; // float because of the factor is a float
double value_; // double because of the factor is a double
int8_t type_;
};
@@ -157,7 +157,7 @@ class AnalogSensor {
return sensors_.size();
}
bool update(uint8_t gpio, const std::string & name, float offset, float factor, uint8_t uom, int8_t type);
bool update(uint8_t gpio, const std::string & name, double offset, double factor, uint8_t uom, int8_t type);
bool get_value_info(JsonObject & output, const char * cmd, const int8_t id) const;
#ifdef EMSESP_DEBUG

View File

@@ -157,6 +157,10 @@
#define EMSESP_DEFAULT_PUBLISH_TIME 10
#endif
#ifndef EMSESP_DEFAULT_PUBLISH_HEARTBEAT
#define EMSESP_DEFAULT_PUBLISH_HEARTBEAT 60
#endif
#ifndef EMSESP_DEFAULT_NESTED_FORMAT
#define EMSESP_DEFAULT_NESTED_FORMAT 1
#endif

View File

@@ -244,7 +244,7 @@ char * Helpers::render_value(char * result, uint8_t value, int8_t format, const
// float: convert float to char
// format is the precision, 0 to 8
char * Helpers::render_value(char * result, const float value, const int8_t format) {
char * Helpers::render_value(char * result, const double value, const int8_t format) {
if (format > 8) {
return nullptr;
}

View File

@@ -27,7 +27,7 @@ namespace emsesp {
class Helpers {
public:
static char * render_value(char * result, const float value, const int8_t format); // format is the precision
static char * render_value(char * result, const double value, const int8_t format); // format is the precision
static char * render_value(char * result, const uint8_t value, const int8_t format, const uint8_t fahrenheit = 0);
static char * render_value(char * result, const int8_t value, const int8_t format, const uint8_t fahrenheit = 0);
static char * render_value(char * result, const uint16_t value, const int8_t format, const uint8_t fahrenheit = 0);

View File

@@ -36,6 +36,7 @@ uint32_t Mqtt::publish_time_solar_;
uint32_t Mqtt::publish_time_mixer_;
uint32_t Mqtt::publish_time_sensor_;
uint32_t Mqtt::publish_time_other_;
uint32_t Mqtt::publish_time_heartbeat_;
bool Mqtt::mqtt_enabled_;
bool Mqtt::multiple_instances_;
bool Mqtt::ha_enabled_;
@@ -147,6 +148,12 @@ void Mqtt::loop() {
process_queue();
}
// send heartbeat
if ((currentMillis - last_publish_heartbeat_ > publish_time_heartbeat_)) {
last_publish_heartbeat_ = currentMillis;
EMSESP::system_.send_heartbeat(); // send heartbeat
}
// dallas publish on change
if (!publish_time_sensor_) {
EMSESP::publish_sensor_values(false);
@@ -432,6 +439,7 @@ void Mqtt::load_settings() {
publish_time_mixer_ = mqttSettings.publish_time_mixer * 1000;
publish_time_other_ = mqttSettings.publish_time_other * 1000;
publish_time_sensor_ = mqttSettings.publish_time_sensor * 1000;
publish_time_heartbeat_ = mqttSettings.publish_time_heartbeat * 1000;
});
// create basename from base
@@ -514,6 +522,10 @@ void Mqtt::set_publish_time_sensor(uint16_t publish_time) {
publish_time_sensor_ = publish_time * 1000; // convert to milliseconds
}
void Mqtt::set_publish_time_heartbeat(uint16_t publish_time) {
publish_time_heartbeat_ = publish_time * 1000; // convert to milliseconds
}
bool Mqtt::get_publish_onchange(uint8_t device_type) {
if (publish_single_ && !ha_enabled_) {
return false;
@@ -653,7 +665,7 @@ std::shared_ptr<const MqttMessage> Mqtt::queue_message(const uint8_t operation,
std::shared_ptr<MqttMessage> message;
message = std::make_shared<MqttMessage>(operation, topic, payload, retain);
#ifdef EMSESP_DEBUG
#if defined(EMSESP_DEBUG)
if (operation == Operation::PUBLISH) {
if (message->payload.empty()) {
LOG_INFO("[DEBUG] Adding to queue: (Publish) topic='%s' empty payload", message->topic.c_str());
@@ -848,7 +860,9 @@ void Mqtt::process_queue() {
mqtt_message.retry_count_ + 1,
message->payload.size(),
packet_id);
#if defined(EMSESP_DEBUG)
LOG_DEBUG("Payload:%s", message->payload.c_str()); // extra message for #784
#endif
if (packet_id == 0) {
// it failed. if we retried n times, give up. remove from queue
if (mqtt_message.retry_count_ == (MQTT_PUBLISH_MAX_RETRY - 1)) {

View File

@@ -65,6 +65,7 @@ class Mqtt {
void set_publish_time_mixer(uint16_t publish_time);
void set_publish_time_other(uint16_t publish_time);
void set_publish_time_sensor(uint16_t publish_time);
void set_publish_time_heartbeat(uint16_t publish_time);
bool get_publish_onchange(uint8_t device_type);
enum Operation : uint8_t { PUBLISH, SUBSCRIBE, UNSUBSCRIBE };
@@ -292,6 +293,7 @@ class Mqtt {
uint32_t last_publish_mixer_ = 0;
uint32_t last_publish_other_ = 0;
uint32_t last_publish_sensor_ = 0;
uint32_t last_publish_heartbeat_ = 0;
uint32_t last_publish_queue_ = 0;
static bool connecting_;
@@ -315,6 +317,7 @@ class Mqtt {
static uint32_t publish_time_mixer_;
static uint32_t publish_time_other_;
static uint32_t publish_time_sensor_;
static uint32_t publish_time_heartbeat_;
static bool mqtt_enabled_;
static bool ha_enabled_;
static uint8_t nested_format_;

View File

@@ -512,12 +512,6 @@ void System::loop() {
led_monitor(); // check status and report back using the LED
system_check(); // check system health
// send out heartbeat
uint32_t currentMillis = uuid::get_uptime();
if (!last_heartbeat_ || (currentMillis - last_heartbeat_ > SYSTEM_HEARTBEAT_INTERVAL)) {
last_heartbeat_ = currentMillis;
send_heartbeat();
}
#ifndef EMSESP_STANDALONE
#if defined(EMSESP_DEBUG)
@@ -1110,6 +1104,11 @@ bool System::command_info(const char * value, const int8_t id, JsonObject & outp
node["enable IPv6"] = settings.enableIPv6;
node["low bandwidth"] = settings.bandwidth20;
node["disable sleep"] = settings.nosleep;
node["enable MDNS"] = settings.enableMDNS;
node["enable CORS"] = settings.enableCORS;
if (settings.enableCORS) {
node["CORS origin"] = settings.CORSOrigin;
}
});
#ifndef EMSESP_STANDALONE
EMSESP::esp8266React.getAPSettingsService()->read([&](APSettings & settings) {
@@ -1160,6 +1159,7 @@ bool System::command_info(const char * value, const int8_t id, JsonObject & outp
node["ha enabled"] = settings.ha_enabled;
node["mqtt qos"] = settings.mqtt_qos;
node["mqtt retain"] = settings.mqtt_retain;
node["publish time heartbeat"] = settings.publish_time_heartbeat;
node["publish time boiler"] = settings.publish_time_boiler;
node["publish time thermostat"] = settings.publish_time_thermostat;
node["publish time solar"] = settings.publish_time_solar;

View File

@@ -250,7 +250,6 @@ class System {
static constexpr uint32_t HEALTHCHECK_LED_FLASH_DUARATION = 150;
static constexpr uint8_t HEALTHCHECK_NO_BUS = (1 << 0); // 1
static constexpr uint8_t HEALTHCHECK_NO_NETWORK = (1 << 1); // 2
static constexpr uint32_t SYSTEM_HEARTBEAT_INTERVAL = 60000; // in milliseconds, how often the MQTT heartbeat is sent (1 min)
static constexpr uint8_t LED_ON = HIGH; // LED on
#ifndef EMSESP_STANDALONE
@@ -263,7 +262,6 @@ class System {
int8_t wifi_quality(int8_t dBm);
uint8_t healthcheck_ = HEALTHCHECK_NO_NETWORK | HEALTHCHECK_NO_BUS; // start with all flags set, no wifi and no ems bus connection
uint32_t last_heartbeat_ = 0;
uint32_t last_system_check_ = 0;
bool upload_status_ = false; // true if we're in the middle of a OTA firmware upload

View File

@@ -44,8 +44,8 @@ class AnalogCustomization {
public:
uint8_t gpio;
std::string name;
float offset;
float factor;
double offset;
double factor;
uint8_t uom; // 0 is none
int8_t type; // -1 is for deletion

View File

@@ -284,8 +284,8 @@ void WebDataService::write_analog(AsyncWebServerRequest * request, JsonVariant &
uint8_t gpio = analog["gpio"]; // this is the unique key, the GPIO
std::string name = analog["name"];
float factor = analog["factor"];
float offset = analog["offset"];
double factor = analog["factor"];
double offset = analog["offset"];
uint8_t uom = analog["uom"];
int8_t type = analog["type"];
ok = EMSESP::analogsensor_.update(gpio, name, offset, factor, uom, type);