fix max for ULONG values, save counters every hour, 3.6.1-dev0c

This commit is contained in:
MichaelDvP
2023-09-04 12:21:53 +02:00
parent ced63a6eb0
commit b912779ef9
14 changed files with 103 additions and 77 deletions

View File

@@ -13,7 +13,7 @@ RestartService::RestartService(AsyncWebServer * server, SecurityManager * securi
}
void RestartService::restart(AsyncWebServerRequest * request) {
emsesp::EMSESP::system_.store_boiler_energy();
emsesp::EMSESP::system_.store_nvs_values();
request->onDisconnect(RestartService::restartNow);
request->send(200);
}
@@ -22,7 +22,7 @@ void RestartService::partition(AsyncWebServerRequest * request) {
const esp_partition_t * factory_partition = esp_partition_find_first(ESP_PARTITION_TYPE_APP, ESP_PARTITION_SUBTYPE_APP_FACTORY, NULL);
if (factory_partition) {
esp_ota_set_boot_partition(factory_partition);
emsesp::EMSESP::system_.store_boiler_energy();
emsesp::EMSESP::system_.store_nvs_values();
request->onDisconnect(RestartService::restartNow);
request->send(200);
return;
@@ -39,7 +39,7 @@ void RestartService::partition(AsyncWebServerRequest * request) {
return;
}
esp_ota_set_boot_partition(ota_partition);
emsesp::EMSESP::system_.store_boiler_energy();
emsesp::EMSESP::system_.store_nvs_values();
request->onDisconnect(RestartService::restartNow);
request->send(200);
}

View File

@@ -114,7 +114,7 @@ void UploadFileService::uploadComplete(AsyncWebServerRequest * request) {
// did we complete uploading a json file?
if (request->_tempFile) {
request->_tempFile.close(); // close the file handle as the upload is now done
emsesp::EMSESP::system_.store_boiler_energy();
emsesp::EMSESP::system_.store_nvs_values();
request->onDisconnect(RestartService::restartNow);
AsyncWebServerResponse * response = request->beginResponse(200);
request->send(response);
@@ -124,7 +124,7 @@ void UploadFileService::uploadComplete(AsyncWebServerRequest * request) {
// check if it was a firmware upgrade
// if no error, send the success response as a JSON
if (is_firmware && !request->_tempObject) {
emsesp::EMSESP::system_.store_boiler_energy();
emsesp::EMSESP::system_.store_nvs_values();
request->onDisconnect(RestartService::restartNow);
AsyncWebServerResponse * response = request->beginResponse(200);
request->send(response);

View File

@@ -277,8 +277,8 @@ void AnalogSensor::measure() {
} else if (!sensor.poll_) { // falling edge
if (sensor.type() == AnalogType::COUNTER) {
sensor.set_value(old_value + sensor.factor());
EMSESP::nvs_.putDouble(sensor.name().c_str(), sensor.value());
} else if (sensor.type() == AnalogType::RATE) { // dafault uom: Hz (1/sec) with factor 1
// EMSESP::nvs_.putDouble(sensor.name().c_str(), sensor.value());
} else if (sensor.type() == AnalogType::RATE) { // default uom: Hz (1/sec) with factor 1
sensor.set_value(sensor.factor() * 1000 / (sensor.polltime_ - sensor.last_polltime_));
} else if (sensor.type() == AnalogType::TIMER) { // default seconds with factor 1
sensor.set_value(sensor.factor() * (sensor.polltime_ - sensor.last_polltime_) / 1000);
@@ -294,6 +294,25 @@ void AnalogSensor::measure() {
}
}
}
// store counter-values only every hour to reduce flash wear
static uint8_t lastSaveHour = 0;
time_t now = time(nullptr);
tm * tm_ = localtime(&now);
if (tm_->tm_hour != lastSaveHour) {
lastSaveHour = tm_->tm_hour;
store_counters();
}
}
// store counters to NVS, called every hour, on restart and update
void AnalogSensor::store_counters() {
for (auto & sensor : sensors_) {
if (sensor.type() == AnalogType::COUNTER) {
if (sensor.value() != EMSESP::nvs_.getDouble(sensor.name().c_str())) {
EMSESP::nvs_.putDouble(sensor.name().c_str(), sensor.value());
}
}
}
}
void AnalogSensor::loop() {

View File

@@ -158,6 +158,7 @@ class AnalogSensor {
bool update(uint8_t gpio, const std::string & name, double offset, double factor, uint8_t uom, int8_t type, bool deleted = false);
bool get_value_info(JsonObject & output, const char * cmd, const int8_t id) const;
void store_counters();
#if defined(EMSESP_TEST)
void test();

View File

@@ -850,18 +850,30 @@ Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const
EMSESP::send_read_request(0x1C, device_id); // read maintenance status on start (only published on change)
EMSESP::send_read_request(0xC2, device_id); // read last errorcode on start (only published on errors)
register_telegram_type(0x04, "UBAFactory", true, MAKE_PF_CB(process_UBAFactory));
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &nomPower_, DeviceValueType::UINT, FL_(nomPower), DeviceValueUOM::KW, MAKE_CF_CB(set_nomPower));
if (model() != EMS_DEVICE_FLAG_HEATPUMP) {
register_telegram_type(0x04, "UBAFactory", true, MAKE_PF_CB(process_UBAFactory));
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &nomPower_, DeviceValueType::UINT, FL_(nomPower), DeviceValueUOM::KW, MAKE_CF_CB(set_nomPower));
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &nrgHeat_, DeviceValueType::ULONG, FL_(nrgHeat), DeviceValueUOM::KWH, MAKE_CF_CB(set_nrgHeat));
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &nrgWw_, DeviceValueType::ULONG, FL_(nrgWw), DeviceValueUOM::KWH, MAKE_CF_CB(set_nrgWw));
nrgHeatF_ = EMSESP::nvs_.getDouble(FL_(nrgHeat)[0], 0);
nrgWwF_ = EMSESP::nvs_.getDouble(FL_(nrgWw)[0], 0);
nomPower_ = EMSESP::nvs_.getUChar(FL_(nomPower)[0], 0);
if (nrgHeatF_ < 0 || nrgHeatF_ >= EMS_VALUE_ULONG_NOTSET) {
nrgHeatF_ = 0;
}
if (nrgWwF_ < 0 || nrgWwF_ >= EMS_VALUE_ULONG_NOTSET) {
nrgWwF_ = 0;
}
if (nomPower_ == EMS_VALUE_UINT_NOTSET) {
nomPower_ = 0;
}
store_energy();
// update/publish the values
has_update(nrgHeat_, (uint32_t)nrgHeatF_);
has_update(nrgWw_, (uint32_t)nrgWwF_);
has_update(&nomPower_);
}
}
@@ -927,13 +939,7 @@ void Boiler::check_active() {
static uint8_t heatBurnPow = 0;
static uint8_t wwBurnPow = 0;
static uint8_t lastSaveHour = 0;
if (nrgHeat_ > (uint32_t)nrgHeatF_) {
nrgHeatF_ = nrgHeat_;
}
if (nrgWw_ > (uint32_t)nrgWwF_) {
nrgWwF_ = nrgWw_;
}
// 0.01 Wh = 0.01 Ws / 3600 = (% * kW * ms) / 3600
// resolution needed: 0.01 Wh = 0.01 Ws / 3600 = (% * kW * ms) / 3600
nrgHeatF_ += (double_t)(((uint32_t)heatBurnPow * nomPower_ * (uuid::get_uptime() - powLastReadTime_)) / 3600) / 100000UL;
nrgWwF_ += (double_t)(((uint32_t)wwBurnPow * nomPower_ * (uuid::get_uptime() - powLastReadTime_)) / 3600) / 100000UL;
has_update(nrgHeat_, (uint32_t)(nrgHeatF_));
@@ -962,19 +968,10 @@ void Boiler::process_UBAFactory(std::shared_ptr<const Telegram> telegram) {
if (!telegram->read_value(nomPower, 4)) {
return;
}
if (nomPower == 0 || nomPower == 255) {
nomPower = EMSESP::nvs_.getUChar(FL_(nomPower)[0], 0);
}
if (nomPower != nomPower_ || nomPower == 255) {
if (nomPower == 255) {
nomPower_ = nomPower = 0;
store_energy();
has_update(&nomPower_);
}
if (nomPower > 0) {
has_update(nomPower_, nomPower);
toggle_fetch(telegram->type_id, false); // only read once
// LOG_DEBUG("nominal power set to %d", nomPower_);
}
toggle_fetch(telegram->type_id, false); // only read once
}
// 0x18
@@ -2723,35 +2720,43 @@ bool Boiler::set_wwAltOpPrio(const char * value, const int8_t id) {
return false;
}
// energy counters. Setting an invalid value does not update, but trigger a store.
bool Boiler::set_nrgHeat(const char * value, const int8_t id) {
int v;
if (Helpers::value2number(value, v)) {
nrgHeatF_ = nrgHeat_ = v;
store_energy();
return true;
if (!Helpers::value2number(value, v)) {
return false;
}
return false;
if (v >= 0 && v < EMS_VALUE_ULONG_NOTSET) {
nrgHeatF_ = v;
has_update(nrgHeat_, (uint32_t)nrgHeatF_);
}
store_energy();
return true;
}
bool Boiler::set_nrgWw(const char * value, const int8_t id) {
int v;
if (Helpers::value2number(value, v)) {
nrgWwF_ = nrgWw_ = v;
store_energy();
return true;
if (!Helpers::value2number(value, v)) {
return false;
}
return false;
if (v >= 0 && v < EMS_VALUE_ULONG_NOTSET) {
nrgWwF_ = v;
has_update(nrgWw_, (uint32_t)nrgWwF_);
}
store_energy();
return true;
}
bool Boiler::set_nomPower(const char * value, const int8_t id) {
int v;
if (Helpers::value2number(value, v)) {
nomPower_ = v > 0 ? v : nomPower_;
store_energy();
has_update(&nomPower_);
return true;
if (!Helpers::value2number(value, v)) {
return false;
}
return false;
if (v > 0 && v < EMS_VALUE_UINT_NOTSET) {
has_update(nomPower_, (uint8_t)v);
}
store_energy();
return true;
}
} // namespace emsesp

View File

@@ -488,7 +488,7 @@ void EMSdevice::add_device_value(uint8_t tag, // to b
uint8_t uom, // unit of measure from DeviceValueUOM
const cmd_function_p f, // command function pointer
int16_t min, // min allowed value
uint16_t max // max allowed value
uint32_t max // max allowed value
) {
// initialize the device value depending on it's type
// ignoring DeviceValueType::CMD and DeviceValueType::TIME
@@ -605,7 +605,7 @@ void EMSdevice::register_device_value(uint8_t tag,
uint8_t uom,
const cmd_function_p f,
int16_t min,
uint16_t max) {
uint32_t max) {
// create a multi-list from the options
add_device_value(tag, value_p, type, nullptr, options_single, 0, name, uom, f, min, max);
};
@@ -628,7 +628,7 @@ void EMSdevice::register_device_value(uint8_t tag,
uint8_t uom,
const cmd_function_p f,
int16_t min,
uint16_t max) {
uint32_t max) {
add_device_value(tag, value_p, type, nullptr, nullptr, numeric_operator, name, uom, f, min, max);
}
@@ -645,7 +645,7 @@ void EMSdevice::register_device_value(uint8_t tag,
uint8_t uom,
const cmd_function_p f,
int16_t min,
uint16_t max) {
uint32_t max) {
add_device_value(tag, value_p, type, nullptr, nullptr, 0, name, uom, f, min, max);
};
@@ -660,7 +660,7 @@ void EMSdevice::register_device_value(uint8_t tag,
uint8_t uom,
const cmd_function_p f,
int16_t min,
uint16_t max) {
uint32_t max) {
add_device_value(tag, value_p, type, options, nullptr, 0, name, uom, f, min, max);
}
@@ -931,7 +931,7 @@ void EMSdevice::generate_values_web(JsonObject & output) {
}
// handle INTs
// add min and max values and steps, as integer values
else if (dv.type != DeviceValueType::ULONG) {
else {
if (dv.numeric_operator > 0) {
obj["s"] = (float)1 / dv.numeric_operator;
} else if (dv.numeric_operator < 0) {
@@ -939,7 +939,7 @@ void EMSdevice::generate_values_web(JsonObject & output) {
}
int16_t dv_set_min;
uint16_t dv_set_max;
uint32_t dv_set_max;
if (dv.get_min_max(dv_set_min, dv_set_max)) {
obj["m"] = dv_set_min;
obj["x"] = dv_set_max;
@@ -1030,10 +1030,10 @@ void EMSdevice::generate_values_web_customization(JsonArray & output) {
obj["m"] = dv.state >> 4; // send back the mask state. We're only interested in the high nibble
obj["w"] = dv.has_cmd; // if writable
if (dv.has_cmd && dv.type != DeviceValueType::ULONG && (obj["v"].is<float>() || obj["v"].is<int>())) {
if (dv.has_cmd && (obj["v"].is<float>() || obj["v"].is<int>())) {
// set the min and max values if there are any and if entity has a value
int16_t dv_set_min;
uint16_t dv_set_max;
uint32_t dv_set_max;
if (dv.get_min_max(dv_set_min, dv_set_max)) {
obj["mi"] = dv_set_min;
obj["ma"] = dv_set_max;
@@ -1059,7 +1059,7 @@ void EMSdevice::generate_values_web_customization(JsonArray & output) {
});
}
void EMSdevice::set_climate_minmax(uint8_t tag, int16_t min, uint16_t max) {
void EMSdevice::set_climate_minmax(uint8_t tag, int16_t min, uint32_t max) {
for (auto & dv : devicevalues_) {
if (dv.tag == tag && (strcmp(dv.short_name, FL_(haclimate[0])) == 0)) {
if (dv.min != min || dv.max != max) {
@@ -1236,7 +1236,7 @@ void EMSdevice::dump_value_info() {
// min/max range
int16_t dv_set_min;
uint16_t dv_set_max;
uint32_t dv_set_max;
if (dv.get_min_max(dv_set_min, dv_set_max)) {
Serial.print(" (>=");
Serial.print(dv_set_min);
@@ -1473,7 +1473,7 @@ bool EMSdevice::get_value_info(JsonObject & output, const char * cmd, const int8
// set the min and max only for commands
if (dv.has_cmd) {
int16_t dv_set_min;
uint16_t dv_set_max;
uint32_t dv_set_max;
if (dv.get_min_max(dv_set_min, dv_set_max)) {
json["min"] = dv_set_min;
json["max"] = dv_set_max;

View File

@@ -206,7 +206,7 @@ class EMSdevice {
void list_device_entries(JsonObject & output) const;
void add_handlers_ignored(const uint16_t handler);
void set_climate_minmax(uint8_t tag, int16_t min, uint16_t max);
void set_climate_minmax(uint8_t tag, int16_t min, uint32_t max);
void setCustomEntity(const std::string & entity_id);
void getCustomEntities(std::vector<std::string> & entity_ids);
@@ -232,7 +232,7 @@ class EMSdevice {
uint8_t uom,
const cmd_function_p f,
int16_t min,
uint16_t max);
uint32_t max);
void register_device_value(uint8_t tag,
void * value_p,
@@ -242,7 +242,7 @@ class EMSdevice {
uint8_t uom,
const cmd_function_p f,
int16_t min,
uint16_t max);
uint32_t max);
void
register_device_value(uint8_t tag, void * value_p, uint8_t type, const char * const ** options, const char * const * name, uint8_t uom, const cmd_function_p f);
@@ -265,7 +265,7 @@ class EMSdevice {
uint8_t uom,
const cmd_function_p f,
int16_t min,
uint16_t max);
uint32_t max);
// single list of options
void register_device_value(uint8_t tag,
@@ -285,14 +285,14 @@ class EMSdevice {
uint8_t uom,
const cmd_function_p f,
int16_t min,
uint16_t max);
uint32_t max);
// no options, optional function f
void register_device_value(uint8_t tag, void * value_p, uint8_t type, const char * const * name, uint8_t uom, const cmd_function_p f = nullptr);
// no options, with min/max
void
register_device_value(uint8_t tag, void * value_p, uint8_t type, const char * const * name, uint8_t uom, const cmd_function_p f, int16_t min, uint16_t max);
register_device_value(uint8_t tag, void * value_p, uint8_t type, const char * const * name, uint8_t uom, const cmd_function_p f, int16_t min, uint32_t max);
void write_command(const uint16_t type_id, const uint8_t offset, uint8_t * message_data, const uint8_t message_length, const uint16_t validate_typeid) const;
void write_command(const uint16_t type_id, const uint8_t offset, const uint8_t value, const uint16_t validate_typeid) const;

View File

@@ -36,7 +36,7 @@ DeviceValue::DeviceValue(uint8_t device_type,
uint8_t uom,
bool has_cmd,
int16_t min,
uint16_t max,
uint32_t max,
uint8_t state)
: device_type(device_type)
, tag(tag)
@@ -261,7 +261,7 @@ bool DeviceValue::has_tag() const {
// converts to signed int, which means rounding to an whole integer
// returns false if there is no min/max needed
// Types BOOL, ENUM, STRING and CMD are not used
bool DeviceValue::get_min_max(int16_t & dv_set_min, uint16_t & dv_set_max) {
bool DeviceValue::get_min_max(int16_t & dv_set_min, uint32_t & dv_set_max) {
uint8_t fahrenheit = !EMSESP::system_.fahrenheit() ? 0 : (uom == DeviceValueUOM::DEGREES) ? 2 : (uom == DeviceValueUOM::DEGREES_R) ? 1 : 0;
// if we have individual limits set already, just do the conversion
@@ -340,7 +340,7 @@ bool DeviceValue::get_custom_min(int16_t & val) {
}
// extract custom max from custom_fullname
bool DeviceValue::get_custom_max(uint16_t & val) {
bool DeviceValue::get_custom_max(uint32_t & val) {
auto max_pos = custom_fullname.find('<');
bool has_max = (max_pos != std::string::npos);
uint8_t fahrenheit = !EMSESP::system_.fahrenheit() ? 0 : (uom == DeviceValueUOM::DEGREES) ? 2 : (uom == DeviceValueUOM::DEGREES_R) ? 1 : 0;

View File

@@ -159,7 +159,7 @@ class DeviceValue {
uint8_t uom; // DeviceValueUOM::*
bool has_cmd; // true if there is a Console/MQTT command which matches the short_name
int16_t min; // min range
uint16_t max; // max range
uint32_t max; // max range
uint8_t state; // DeviceValueState::*
DeviceValue(uint8_t device_type,
@@ -175,16 +175,16 @@ class DeviceValue {
uint8_t uom,
bool has_cmd,
int16_t min,
uint16_t max,
uint32_t max,
uint8_t state);
bool hasValue() const;
bool has_tag() const;
bool get_min_max(int16_t & dv_set_min, uint16_t & dv_set_max);
bool get_min_max(int16_t & dv_set_min, uint32_t & dv_set_max);
void set_custom_minmax();
bool get_custom_min(int16_t & val);
bool get_custom_max(uint16_t & val);
bool get_custom_max(uint32_t & val);
std::string get_custom_fullname() const;
std::string get_fullname() const;
static std::string get_name(std::string & entity);

View File

@@ -737,7 +737,7 @@ bool Mqtt::publish_ha_sensor_config(DeviceValue & dv, const char * model, const
// calculate the min and max
int16_t dv_set_min;
uint16_t dv_set_max;
uint32_t dv_set_max;
(void)dv.get_min_max(dv_set_min, dv_set_max);
// determine if we're creating the command topics which we use special HA configs
@@ -788,7 +788,7 @@ bool Mqtt::publish_ha_sensor_config(uint8_t type, // EMSdev
const char * const ** options,
uint8_t options_size,
const int16_t dv_set_min,
const int16_t dv_set_max,
const uint32_t dv_set_max,
const int8_t num_op,
const JsonObject & dev_json) {
// ignore if name (fullname) is empty
@@ -1117,7 +1117,7 @@ bool Mqtt::publish_ha_sensor_config(uint8_t type, // EMSdev
return queue_ha(topic, doc.as<JsonObject>());
}
bool Mqtt::publish_ha_climate_config(const uint8_t tag, const bool has_roomtemp, const bool remove, const int16_t min, const uint16_t max) {
bool Mqtt::publish_ha_climate_config(const uint8_t tag, const bool has_roomtemp, const bool remove, const int16_t min, const uint32_t max) {
uint8_t hc_num = tag - DeviceValueTAG::TAG_HC1 + 1;
char topic[Mqtt::MQTT_TOPIC_MAX_SIZE];

View File

@@ -87,12 +87,12 @@ class Mqtt {
const char * const ** options,
uint8_t options_size,
const int16_t dv_set_min,
const int16_t dv_set_max,
const uint32_t dv_set_max,
const int8_t num_op,
const JsonObject & dev_json);
static bool publish_system_ha_sensor_config(uint8_t type, const char * name, const char * entity, const uint8_t uom);
static bool publish_ha_climate_config(const uint8_t tag, const bool has_roomtemp, const bool remove = false, const int16_t min = 5, const uint16_t max = 30);
static bool publish_ha_climate_config(const uint8_t tag, const bool has_roomtemp, const bool remove = false, const int16_t min = 5, const uint32_t max = 30);
static void show_topic_handlers(uuid::console::Shell & shell, const uint8_t device_type);
static void show_mqtt(uuid::console::Shell & shell);

View File

@@ -231,14 +231,15 @@ bool System::command_watch(const char * value, const int8_t id) {
return false;
}
void System::store_boiler_energy() {
void System::store_nvs_values() {
Command::call(EMSdevice::DeviceType::BOILER, "nompower", "-1"); // trigger a write
EMSESP::analogsensor_.store_counters();
}
// restart EMS-ESP
void System::system_restart() {
LOG_INFO("Restarting EMS-ESP...");
store_boiler_energy();
store_nvs_values();
Shell::loop_all();
delay(1000); // wait a second
#ifndef EMSESP_STANDALONE

View File

@@ -66,7 +66,7 @@ class System {
std::string reset_reason(uint8_t cpu) const;
void store_boiler_energy();
void store_nvs_values();
void system_restart();
void format(uuid::console::Shell & shell);
void upload_status(bool in_progress);

View File

@@ -1 +1 @@
#define EMSESP_APP_VERSION "3.6.1-dev.0b"
#define EMSESP_APP_VERSION "3.6.1-dev.0c"