diff --git a/CHANGELOG_LATEST.md b/CHANGELOG_LATEST.md
index 6e4ea2a1e..d662aa539 100644
--- a/CHANGELOG_LATEST.md
+++ b/CHANGELOG_LATEST.md
@@ -4,10 +4,11 @@
## Added
-- Translations in Web UI and all device entity names to German. [#22](https://github.com/emsesp/EMS-ESP32/issues/22)
+- Translations in Web UI and all device entity names (DE, NL, SE, PL, NO, ...). [#22](https://github.com/emsesp/EMS-ESP32/issues/22)
- Add support for Lolin C3 mini [#620](https://github.com/emsesp/EMS-ESP32/pull/620)
-- Add Greenstar 30Ri boiler
+- Add devices: Greenstar 30Ri boiler, Junkers FW500 thermostat, Buderus BC30 controller
- Add program memory info
+- Add mqtt queue and connection infos
- Add min/max setting to customizations
- Adapt min/max if ems-value is not in this range
- Add heatpump settings for inputs and limits
@@ -20,6 +21,7 @@
- Discovery in HomeAssistant don't work with custom base topic. [#596](https://github.com/emsesp/EMS-ESP32/issues/596) Base topic containing `/` are changed to `_`
- RF room temperature sensor are shown as thermostat
+- render mqtt float json values with trailing zero
## **BREAKING CHANGES:**
diff --git a/interface/src/SignIn.tsx b/interface/src/SignIn.tsx
index 297658e0e..3e2b02dd2 100644
--- a/interface/src/SignIn.tsx
+++ b/interface/src/SignIn.tsx
@@ -24,6 +24,8 @@ import { ReactComponent as NLflag } from './i18n/NL.svg';
import { ReactComponent as DEflag } from './i18n/DE.svg';
import { ReactComponent as GBflag } from './i18n/GB.svg';
import { ReactComponent as SEflag } from './i18n/SE.svg';
+import { ReactComponent as PLflag } from './i18n/PL.svg';
+import { ReactComponent as NOflag } from './i18n/NO.svg';
const SignIn: FC = () => {
const authenticationContext = useContext(AuthenticationContext);
@@ -125,6 +127,14 @@ const SignIn: FC = () => {
SE
+
+
telegram) {
return;
}
- has_update(telegram, hc->daytemp, 17); // is * 2
- has_update(telegram, hc->nighttemp, 16); // is * 2
- has_update(telegram, hc->nofrosttemp, 15); // is * 2
- has_update(telegram, hc->control, 1); // remote: 0-off, 1-FB10, 2-FB100
- has_enumupdate(telegram, hc->program, 13, 1); // 1-6: 1 = A, 2 = B,...
- has_enumupdate(telegram, hc->mode, 14, 1); // 0 = nofrost, 1 = eco, 2 = heat, 3 = auto
- has_update(telegram, hc->roomsensor, 9); // 1-intern, 2-extern, 3-autoselect the lower value
+ has_update(telegram, hc->daytemp, 17); // is * 2
+ has_update(telegram, hc->nighttemp, 16); // is * 2
+ has_update(telegram, hc->nofrosttemp, 15); // is * 2
+ has_update(telegram, hc->control, 1); // remote: 0-off, 1-FB10, 2-FB100
+ has_enumupdate(telegram, hc->program, 13, 1); // 1-6: 1 = A, 2 = B,...
+ has_enumupdate(telegram, hc->mode, 14, 1); // 0 = nofrost, 1 = eco, 2 = heat, 3 = auto
+ has_enumupdate(telegram, hc->roomsensor, 9, 1); // 1-intern, 2-extern, 3-autoselect the lower value
}
// type 0x0179, ff
@@ -1364,10 +1364,11 @@ void Thermostat::process_RCTime(std::shared_ptr telegram) {
strftime(newdatetime, sizeof(dateTime_), "%d.%m.%G %H:%M", tm_);
has_update(dateTime_, newdatetime, sizeof(dateTime_));
- bool ivtclock = (telegram->message_data[0] & 0x80) == 0x80; // dont sync ivt-clock, #439
- time_t ttime = mktime(tm_); // thermostat time
+ bool ivtclock = (telegram->message_data[0] & 0x80) == 0x80; // dont sync ivt-clock, #439
+ bool junkersclock = model() == EMSdevice::EMS_DEVICE_FLAG_JUNKERS;
+ time_t ttime = mktime(tm_); // thermostat time
// correct thermostat clock if we have valid ntp time, and could write the command
- if (!ivtclock && tset_ && EMSESP::system_.ntp_connected() && !EMSESP::system_.readonly_mode() && has_command(&dateTime_)) {
+ if (!ivtclock && !junkersclock && tset_ && EMSESP::system_.ntp_connected() && !EMSESP::system_.readonly_mode() && has_command(&dateTime_)) {
double difference = difftime(now, ttime);
if (difference > 15 || difference < -15) {
set_datetime("ntp", -1); // set from NTP
@@ -1717,7 +1718,7 @@ bool Thermostat::set_roomsensor(const char * value, const int8_t id) {
uint8_t ctrl = 0;
if (model() == EMS_DEVICE_FLAG_JUNKERS && !has_flags(EMS_DEVICE_FLAG_JUNKERS_OLD)) {
if (Helpers::value2enum(value, ctrl, FL_(enum_roomsensor))) {
- write_command(set_typeids[hc->hc()], 9, ctrl);
+ write_command(set_typeids[hc->hc()], 9, ctrl + 1);
return true;
}
}
@@ -2165,6 +2166,10 @@ bool Thermostat::set_datetime(const char * value, const int8_t id) {
data[5] = tm_->tm_sec;
data[6] = (tm_->tm_wday + 6) % 7; // Bosch counts from Mo, time from Su
data[7] = tm_->tm_isdst + 2; // set DST and flag for ext. clock
+ if (model() == EMSdevice::EMS_DEVICE_FLAG_JUNKERS) {
+ data[6]++; // Junkers use 1-7;
+ data[7] = 0;
+ }
} else if (dt.length() == 23) {
data[0] = (dt[7] - '0') * 100 + (dt[8] - '0') * 10 + (dt[9] - '0'); // year
data[1] = (dt[3] - '0') * 10 + (dt[4] - '0'); // month
@@ -2174,6 +2179,9 @@ bool Thermostat::set_datetime(const char * value, const int8_t id) {
data[5] = (dt[17] - '0') * 10 + (dt[18] - '0'); // sec
data[6] = (dt[20] - '0'); // day of week, Mo:0
data[7] = (dt[22] - '0') + 2; // DST and flag
+ if (model() == EMSdevice::EMS_DEVICE_FLAG_JUNKERS) {
+ data[7] = 0;
+ }
} else {
LOG_WARNING(F("Set date: invalid data, wrong length"));
return false;
@@ -3846,8 +3854,9 @@ void Thermostat::register_device_values() {
MAKE_CF_CB(set_wwVacation));
break;
case EMS_DEVICE_FLAG_JUNKERS:
+ // FR100 is not writable, see. https://github.com/emsesp/EMS-ESP32/issues/536
+ // FW500 is not writable, see. https://github.com/emsesp/EMS-ESP32/issues/666
if (has_flags(EMS_DEVICE_FLAG_JUNKERS_OLD)) {
- // FR100 is not writable, see. https://github.com/emsesp/EMS-ESP32/issues/536
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &dateTime_, DeviceValueType::STRING, FL_(tpl_datetime), FL_(dateTime), DeviceValueUOM::NONE);
} else {
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA,
diff --git a/src/emsdevice.cpp b/src/emsdevice.cpp
index b07b97ec6..3cae2c594 100644
--- a/src/emsdevice.cpp
+++ b/src/emsdevice.cpp
@@ -800,9 +800,9 @@ void EMSdevice::generate_values_web(JsonObject & output) {
} else if ((dv.type == DeviceValueType::USHORT) && Helpers::hasValue(*(uint16_t *)(dv.value_p))) {
obj["v"] = Helpers::transformNumFloat(*(uint16_t *)(dv.value_p), dv.numeric_operator, fahrenheit);
} else if ((dv.type == DeviceValueType::ULONG) && Helpers::hasValue(*(uint32_t *)(dv.value_p))) {
- obj["v"] = Helpers::transformNumFloat(*(uint32_t *)(dv.value_p), dv.numeric_operator, fahrenheit);
+ obj["v"] = Helpers::transformNumFloat(*(uint32_t *)(dv.value_p), dv.numeric_operator);
} else if ((dv.type == DeviceValueType::TIME) && Helpers::hasValue(*(uint32_t *)(dv.value_p))) {
- obj["v"] = dv.numeric_operator ? (*(uint32_t *)(dv.value_p) / dv.numeric_operator) : *(uint32_t *)(dv.value_p);
+ obj["v"] = Helpers::transformNumFloat(*(uint32_t *)(dv.value_p), dv.numeric_operator);
} else {
obj["v"] = ""; // must have a value for sorting to work
}
@@ -901,40 +901,19 @@ void EMSdevice::generate_values_web_customization(JsonArray & output) {
// handle Integers and Floats
else {
- int8_t num_op = dv.numeric_operator;
- bool make_float;
- if (num_op == 0) {
- // no changes to number
- make_float = false;
- num_op = 1; // so it gets *1
- } else if (num_op < 0) {
- // negative numbers, convert to a positive multiplier
- make_float = false;
- num_op *= -1;
- } else {
- // has a divider, make it a float
- make_float = true;
- }
-
- // always convert temperatures to floats with 1 decimal place
- if ((dv.uom == DeviceValueUOM::DEGREES) || (dv.uom == DeviceValueUOM::DEGREES_R)) {
- make_float = true;
- }
-
if (dv.type == DeviceValueType::INT) {
- obj["v"] = make_float ? Helpers::transformNumFloat(*(int8_t *)(dv.value_p), num_op, fahrenheit) : *(int8_t *)(dv.value_p) * num_op;
+ obj["v"] = Helpers::transformNumFloat(*(int8_t *)(dv.value_p), dv.numeric_operator, fahrenheit);
} else if (dv.type == DeviceValueType::UINT) {
- obj["v"] = make_float ? Helpers::transformNumFloat(*(uint8_t *)(dv.value_p), num_op, fahrenheit) : *(uint8_t *)(dv.value_p) * num_op;
+ obj["v"] = Helpers::transformNumFloat(*(uint8_t *)(dv.value_p), dv.numeric_operator, fahrenheit);
} else if (dv.type == DeviceValueType::SHORT) {
- obj["v"] = make_float ? Helpers::transformNumFloat(*(int16_t *)(dv.value_p), num_op, fahrenheit) : *(int16_t *)(dv.value_p) * num_op;
+ obj["v"] = Helpers::transformNumFloat(*(int16_t *)(dv.value_p), dv.numeric_operator, fahrenheit);
} else if (dv.type == DeviceValueType::USHORT) {
- obj["v"] = make_float ? Helpers::transformNumFloat(*(uint16_t *)(dv.value_p), num_op, fahrenheit) : *(uint16_t *)(dv.value_p) * num_op;
+ obj["v"] = Helpers::transformNumFloat(*(uint16_t *)(dv.value_p), dv.numeric_operator, fahrenheit);
} else if (dv.type == DeviceValueType::ULONG) {
- obj["v"] = make_float ? Helpers::transformNumFloat(*(uint32_t *)(dv.value_p), num_op) : *(uint32_t *)(dv.value_p) * num_op;
+ obj["v"] = Helpers::transformNumFloat(*(uint32_t *)(dv.value_p), dv.numeric_operator);
} else if (dv.type == DeviceValueType::TIME) {
// sometimes we need to divide by 60
- obj["v"] = (num_op == DeviceValueNumOp::DV_NUMOP_DIV60) ? (uint32_t)Helpers::transformNumFloat(*(uint32_t *)(dv.value_p), num_op)
- : *(uint32_t *)(dv.value_p);
+ obj["v"] = Helpers::transformNumFloat(*(uint32_t *)(dv.value_p), dv.numeric_operator);
}
}
}
@@ -1095,8 +1074,6 @@ bool EMSdevice::get_value_info(JsonObject & output, const char * cmd, const int8
// search device value with this tag
for (auto & dv : devicevalues_) {
if (strcmp(command_s, Helpers::toLower(read_flash_string(dv.short_name)).c_str()) == 0 && (tag <= 0 || tag == dv.tag)) {
- int8_t num_op = dv.numeric_operator;
-
uint8_t fahrenheit = !EMSESP::system_.fahrenheit() ? 0 : (dv.uom == DeviceValueUOM::DEGREES) ? 2 : (dv.uom == DeviceValueUOM::DEGREES_R) ? 1 : 0;
const char * type = "type";
@@ -1117,6 +1094,7 @@ bool EMSdevice::get_value_info(JsonObject & output, const char * cmd, const int8
json["circuit"] = tag_to_mqtt(dv.tag);
}
+ char val[10];
switch (dv.type) {
case DeviceValueType::ENUM: {
if (*(uint8_t *)(dv.value_p) < dv.options_size) {
@@ -1136,35 +1114,35 @@ bool EMSdevice::get_value_info(JsonObject & output, const char * cmd, const int8
case DeviceValueType::USHORT:
if (Helpers::hasValue(*(uint16_t *)(dv.value_p))) {
- json[value] = Helpers::transformNumFloat(*(uint16_t *)(dv.value_p), num_op, fahrenheit);
+ json[value] = serialized(Helpers::render_value(val, *(uint16_t *)(dv.value_p), dv.numeric_operator, fahrenheit));
}
json[type] = F_(number);
break;
case DeviceValueType::UINT:
if (Helpers::hasValue(*(uint8_t *)(dv.value_p))) {
- json[value] = Helpers::transformNumFloat(*(uint8_t *)(dv.value_p), num_op, fahrenheit);
+ json[value] = serialized(Helpers::render_value(val, *(uint8_t *)(dv.value_p), dv.numeric_operator, fahrenheit));
}
json[type] = F_(number);
break;
case DeviceValueType::SHORT:
if (Helpers::hasValue(*(int16_t *)(dv.value_p))) {
- json[value] = Helpers::transformNumFloat(*(int16_t *)(dv.value_p), num_op, fahrenheit);
+ json[value] = serialized(Helpers::render_value(val, *(int16_t *)(dv.value_p), dv.numeric_operator, fahrenheit));
}
json[type] = F_(number);
break;
case DeviceValueType::INT:
if (Helpers::hasValue(*(int8_t *)(dv.value_p))) {
- json[value] = Helpers::transformNumFloat(*(int8_t *)(dv.value_p), num_op, fahrenheit);
+ json[value] = serialized(Helpers::render_value(val, *(int8_t *)(dv.value_p), dv.numeric_operator, fahrenheit));
}
json[type] = F_(number);
break;
case DeviceValueType::ULONG:
if (Helpers::hasValue(*(uint32_t *)(dv.value_p))) {
- json[value] = Helpers::transformNumFloat(*(uint32_t *)(dv.value_p), num_op);
+ json[value] = serialized(Helpers::render_value(val, *(uint32_t *)(dv.value_p), dv.numeric_operator));
}
json[type] = F_(number);
break;
@@ -1186,7 +1164,7 @@ bool EMSdevice::get_value_info(JsonObject & output, const char * cmd, const int8
case DeviceValueType::TIME:
if (Helpers::hasValue(*(uint32_t *)(dv.value_p))) {
- json[value] = Helpers::transformNumFloat(*(uint32_t *)(dv.value_p), num_op);
+ json[value] = serialized(Helpers::render_value(val, *(uint32_t *)(dv.value_p), dv.numeric_operator));
}
json[type] = F_(number);
break;
@@ -1368,64 +1346,22 @@ bool EMSdevice::generate_values(JsonObject & output, const uint8_t tag_filter, c
: (dv.uom == DeviceValueUOM::DEGREES) ? 2
: (dv.uom == DeviceValueUOM::DEGREES_R) ? 1
: 0;
-
- int8_t num_op = dv.numeric_operator;
- bool make_float;
- if (num_op == 0) {
- // no changes to number
- make_float = false;
- num_op = 1; // so it gets *1
- } else if (num_op < 0) {
- // negative numbers, convert to a positive multiplier
- make_float = false;
- num_op *= -1;
- } else {
- // has a divider, make it a float
- make_float = true;
- }
-
- // always convert temperatures to floats with 1 decimal place
- if ((dv.uom == DeviceValueUOM::DEGREES) || (dv.uom == DeviceValueUOM::DEGREES_R)) {
- make_float = true;
- }
-
+ char val[10];
if (dv.type == DeviceValueType::INT) {
- if (make_float) {
- json[name] = Helpers::transformNumFloat(*(int8_t *)(dv.value_p), num_op, fahrenheit);
- } else {
- json[name] = *(int8_t *)(dv.value_p) * num_op;
- }
+ json[name] = serialized(Helpers::render_value(val, *(int8_t *)(dv.value_p), dv.numeric_operator, fahrenheit));
} else if (dv.type == DeviceValueType::UINT) {
- if (make_float) {
- json[name] = Helpers::transformNumFloat(*(uint8_t *)(dv.value_p), num_op, fahrenheit);
- } else {
- json[name] = *(uint8_t *)(dv.value_p) * num_op;
- }
+ json[name] = serialized(Helpers::render_value(val, *(uint8_t *)(dv.value_p), dv.numeric_operator, fahrenheit));
} else if (dv.type == DeviceValueType::SHORT) {
- if (make_float) {
- json[name] = Helpers::transformNumFloat(*(int16_t *)(dv.value_p), num_op, fahrenheit);
- } else {
- json[name] = *(int16_t *)(dv.value_p) * num_op;
- }
+ json[name] = serialized(Helpers::render_value(val, *(int16_t *)(dv.value_p), dv.numeric_operator, fahrenheit));
} else if (dv.type == DeviceValueType::USHORT) {
- if (make_float) {
- json[name] = Helpers::transformNumFloat(*(uint16_t *)(dv.value_p), num_op, fahrenheit);
- } else {
- json[name] = *(uint16_t *)(dv.value_p) * num_op;
- }
+ json[name] = serialized(Helpers::render_value(val, *(uint16_t *)(dv.value_p), dv.numeric_operator, fahrenheit));
} else if (dv.type == DeviceValueType::ULONG) {
- if (make_float) {
- json[name] = Helpers::transformNumFloat(*(uint32_t *)(dv.value_p), num_op, fahrenheit);
- } else {
- json[name] = *(uint32_t *)(dv.value_p) * num_op;
- }
+ json[name] = serialized(Helpers::render_value(val, *(uint32_t *)(dv.value_p), dv.numeric_operator));
} else if ((dv.type == DeviceValueType::TIME) && Helpers::hasValue(*(uint32_t *)(dv.value_p))) {
- uint32_t time_value;
- if (num_op == DeviceValueNumOp::DV_NUMOP_DIV60) {
+ uint32_t time_value = *(uint32_t *)(dv.value_p);
+ if (dv.numeric_operator == DeviceValueNumOp::DV_NUMOP_DIV60) {
// sometimes we need to divide by 60
- time_value = Helpers::transformNumFloat(*(uint32_t *)(dv.value_p), num_op);
- } else {
- time_value = *(uint32_t *)(dv.value_p);
+ time_value /= 60;
}
if (output_target == OUTPUT_TARGET::API_VERBOSE || output_target == OUTPUT_TARGET::CONSOLE) {
char time_s[60];