diff --git a/CHANGELOG_LATEST.md b/CHANGELOG_LATEST.md
index 89d33a09b..74ecb6350 100644
--- a/CHANGELOG_LATEST.md
+++ b/CHANGELOG_LATEST.md
@@ -22,6 +22,10 @@
- Help text for string commands in WebUI [#320](https://github.com/emsesp/EMS-ESP32/issues/320)
- Germany translations (at compile time)
- #entities added to system/info` endpoint [#322](https://github.com/emsesp/EMS-ESP32/issues/322)
+- analog outputs digital/pwm/dac
+- remove MQTT retained configs if discovery is disabled
+- timeout 10 min for MQTT-QoS wait
+- Moduline 300 auto-temperatures T1-T4, RC300 romminfluencefactor
### Fixed
@@ -38,6 +42,7 @@
- Fix uploading firmware on OSX [#345](https://github.com/emsesp/EMS-ESP32/issues/345)
- Non-nested MQTT would corrupt the json [#354](https://github.com/emsesp/EMS-ESP32/issues/354)
- Burner selected max power can have a value higher than 100% [#314](https://github.com/emsesp/EMS-ESP32/issues/314)
+- some missing fahrenheit calculations
### Changed
@@ -51,6 +56,8 @@
- Show ems tx reads and writes separatly
- Show ems device handlers separated for received, fetched and pending handlers.
- Wired renamed to Ethernet
+- removed system/pin command, new commands in analogsensors
+- system/info device-info split to name/version/brand
## **BREAKING CHANGES:**
diff --git a/interface/src/framework/mqtt/MqttSettingsForm.tsx b/interface/src/framework/mqtt/MqttSettingsForm.tsx
index 1d609ff33..e251bbcf9 100644
--- a/interface/src/framework/mqtt/MqttSettingsForm.tsx
+++ b/interface/src/framework/mqtt/MqttSettingsForm.tsx
@@ -183,29 +183,43 @@ const MqttSettingsForm: FC = () => {
control={}
label="Publish command output to a 'response' topic"
/>
- }
- label="Publish single value topics on change"
- />
-
- }
- label="Enable MQTT Discovery (for Home Assistant, Domoticz)"
- />
-
- {data.ha_enabled && (
-
-
+
+ }
+ label="Publish single value topics on change"
/>
- )}
+ {data.publish_single && (
+
+ }
+ label="publish to command topics (ioBroker)"
+ />
+
+ )}
+
+
+
+ }
+ label="Enable MQTT Discovery (for Home Assistant, Domoticz)"
+ />
+
+ {data.ha_enabled && (
+
+
+
+ )}
+
Publish Intervals (in seconds, 0=automatic)
diff --git a/interface/src/project/DashboardData.tsx b/interface/src/project/DashboardData.tsx
index 36614e0f0..fab64a359 100644
--- a/interface/src/project/DashboardData.tsx
+++ b/interface/src/project/DashboardData.tsx
@@ -59,7 +59,8 @@ import {
DeviceValue,
DeviceValueUOM,
DeviceValueUOM_s,
- AnalogTypes,
+ AnalogType,
+ AnalogTypeNames,
Sensor,
Analog
} from './types';
@@ -613,7 +614,7 @@ const DashboardData: FC = () => {
{analog_data.i}
{analog_data.n}
- {AnalogTypes[analog_data.t]}
+ {AnalogTypeNames[analog_data.t]}
{formatValue(analog_data.v, analog_data.u)}
))}
@@ -708,14 +709,14 @@ const DashboardData: FC = () => {
- {AnalogTypes.map((val, i) => (
+ {AnalogTypeNames.map((val, i) => (
))}
- {analog.t === 3 && (
+ {analog.t >= AnalogType.COUNTER && analog.t <= AnalogType.RATE && (
<>
@@ -726,22 +727,37 @@ const DashboardData: FC = () => {
))}
-
-
- mV
- }}
- />
-
+ {analog.t === AnalogType.ADC && (
+
+ mV
+ }}
+ />
+
+ )}
+ {analog.t === AnalogType.COUNTER && (
+
+
+
+ )}
{
>
)}
+ {analog.t === AnalogType.DIGITAL_OUT && (analog.i === 25 || analog.i === 26) && (
+ <>
+
+
+
+ >
+ )}
+ {analog.t === AnalogType.DIGITAL_OUT && analog.i !== 25 && analog.i !== 26 && (
+ <>
+
+
+
+ >
+ )}
+ {analog.t >= AnalogType.PWM_0 && (
+ <>
+
+ Hz
+ }}
+ />
+
+
+ %
+ }}
+
+ />
+
+ >
+ )}
Warning: be careful when assigning a GPIO!
diff --git a/interface/src/project/types.ts b/interface/src/project/types.ts
index 1c0871691..b5f3a9497 100644
--- a/interface/src/project/types.ts
+++ b/interface/src/project/types.ts
@@ -176,7 +176,9 @@ export enum DeviceValueUOM {
DBM,
FAHRENHEIT,
MV,
- SQM
+ SQM,
+ M3,
+ L
}
export const DeviceValueUOM_s = [
@@ -199,10 +201,36 @@ export const DeviceValueUOM_s = [
'°F',
'mV',
'sqm',
- "o'clock"
+ "m3",
+ "l"
+];
+
+export enum AnalogType {
+ NOTUSED = 0,
+ DIGITAL_IN,
+ COUNTER,
+ ADC,
+ TIMER,
+ RATE,
+ DIGITAL_OUT,
+ PWM_0,
+ PWM_1,
+ PWM_2
+}
+
+export const AnalogTypeNames = [
+ '(disabled)',
+ 'Digital in',
+ 'Counter',
+ 'ADC',
+ 'Timer',
+ 'Rate',
+ 'Digital out',
+ 'PWM 0',
+ 'PWM 1',
+ 'PWM 2'
];
-export const AnalogTypes = ['(disabled)', 'Digital in', 'Counter', 'ADC'];
type BoardProfiles = {
[name: string]: string;
diff --git a/interface/src/types/mqtt.ts b/interface/src/types/mqtt.ts
index a372f217f..1e2b50a00 100644
--- a/interface/src/types/mqtt.ts
+++ b/interface/src/types/mqtt.ts
@@ -40,5 +40,6 @@ export interface MqttSettings {
nested_format: number;
send_response: boolean;
publish_single: boolean;
+ publish_single2cmd: boolean;
discovery_prefix: string;
}
diff --git a/lib/framework/MqttSettingsService.cpp b/lib/framework/MqttSettingsService.cpp
index df9866958..561d7926d 100644
--- a/lib/framework/MqttSettingsService.cpp
+++ b/lib/framework/MqttSettingsService.cpp
@@ -176,6 +176,7 @@ void MqttSettings::read(MqttSettings & settings, JsonObject & root) {
root["nested_format"] = settings.nested_format;
root["discovery_prefix"] = settings.discovery_prefix;
root["publish_single"] = settings.publish_single;
+ root["publish_single2cmd"] = settings.publish_single2cmd;
root["send_response"] = settings.send_response;
}
@@ -203,11 +204,12 @@ StateUpdateResult MqttSettings::update(JsonObject & root, MqttSettings & setting
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.ha_enabled = root["ha_enabled"] | EMSESP_DEFAULT_HA_ENABLED;
- newSettings.nested_format = root["nested_format"] | EMSESP_DEFAULT_NESTED_FORMAT;
- newSettings.discovery_prefix = root["discovery_prefix"] | EMSESP_DEFAULT_DISCOVERY_PREFIX;
- newSettings.publish_single = root["publish_single"] | EMSESP_DEFAULT_PUBLISH_SINGLE;
- newSettings.send_response = root["send_response"] | EMSESP_DEFAULT_SEND_RESPONSE;
+ newSettings.ha_enabled = root["ha_enabled"] | EMSESP_DEFAULT_HA_ENABLED;
+ newSettings.nested_format = root["nested_format"] | EMSESP_DEFAULT_NESTED_FORMAT;
+ newSettings.discovery_prefix = root["discovery_prefix"] | EMSESP_DEFAULT_DISCOVERY_PREFIX;
+ newSettings.publish_single = root["publish_single"] | EMSESP_DEFAULT_PUBLISH_SINGLE;
+ newSettings.publish_single2cmd = root["publish_single2cmd"] | EMSESP_DEFAULT_PUBLISH_SINGLE2CMD;
+ newSettings.send_response = root["send_response"] | EMSESP_DEFAULT_SEND_RESPONSE;
if (newSettings.enabled != settings.enabled) {
changed = true;
@@ -230,6 +232,10 @@ StateUpdateResult MqttSettings::update(JsonObject & root, MqttSettings & setting
changed = true;
}
+ if (newSettings.publish_single2cmd != settings.publish_single2cmd) {
+ changed = true;
+ }
+
if (newSettings.send_response != settings.send_response) {
changed = true;
}
diff --git a/lib/framework/MqttSettingsService.h b/lib/framework/MqttSettingsService.h
index f51efb6a8..560e29c96 100644
--- a/lib/framework/MqttSettingsService.h
+++ b/lib/framework/MqttSettingsService.h
@@ -90,6 +90,7 @@ class MqttSettings {
uint8_t nested_format;
String discovery_prefix;
bool publish_single;
+ bool publish_single2cmd;
bool send_response;
static void read(MqttSettings & settings, JsonObject & root);
diff --git a/src/analogsensor.cpp b/src/analogsensor.cpp
index 734b9e441..147440e82 100644
--- a/src/analogsensor.cpp
+++ b/src/analogsensor.cpp
@@ -27,7 +27,7 @@ void AnalogSensor::start() {
reload(); // fetch the list of sensors from our customization service
if (analog_enabled_) {
- analogSetAttenuation(ADC_2_5db); // for all channels
+ analogSetAttenuation(ADC_2_5db); // for all channels 1.5V
}
LOG_INFO(F("Starting Analog sensor service"));
@@ -40,10 +40,15 @@ void AnalogSensor::start() {
F_(info_cmd));
Command::add(
EMSdevice::DeviceType::ANALOGSENSOR,
- F_(counter),
- [&](const char * value, const int8_t id) { return command_counter(value, id); },
- F("set counter value"),
+ F_(setvalue),
+ [&](const char * value, const int8_t id) { return command_setvalue(value, id); },
+ F("set io value"),
CommandFlag::ADMIN_ONLY);
+ Command::add(
+ EMSdevice::DeviceType::ANALOGSENSOR,
+ F_(commands),
+ [&](const char * value, const int8_t id, JsonObject & output) { return command_commands(value, id, output); },
+ F_(commands_cmd));
}
// load settings from the customization file, sorts them and initializes the GPIOs
@@ -58,11 +63,47 @@ void AnalogSensor::reload() {
// and store them locally and then activate them
EMSESP::webCustomizationService.read([&](WebCustomization & settings) {
auto sensors = settings.analogCustomizations;
- sensors_.clear(); // start with an empty list
- if (sensors.size() != 0) {
- for (auto & sensor : sensors) {
+ auto it = sensors_.begin();
+ for (auto & sensor_ : sensors_) {
+ // update existing sensors
+ bool found = false;
+ for (auto & sensor : sensors) { //search customlist
+ if (sensor_.id() == sensor.id) {
+ // for output sensors set value to new start-value
+ if ((sensor.type == AnalogType::COUNTER || sensor.type >= AnalogType::DIGITAL_OUT)
+ && (sensor_.type() != sensor.type || sensor_.offset() != sensor.offset || sensor_.factor() != sensor.factor)) {
+ sensor_.set_value(sensor.offset);
+ }
+ sensor_.set_name(sensor.name);
+ sensor_.set_type(sensor.type);
+ sensor_.set_offset(sensor.offset);
+ sensor_.set_factor(sensor.factor);
+ sensor_.set_uom(sensor.uom);
+ sensor_.ha_registered = false;
+ found = true;
+ }
+ }
+ if (!found) {
+ sensors_.erase(it);
+ }
+ it++;
+ }
+ // add new sensors from list
+ for (auto & sensor : sensors) {
+ bool found = false;
+ for (auto & sensor_ : sensors_) {
+ if (sensor_.id() == sensor.id) {
+ found = true;
+ }
+ }
+ if (!found) {
sensors_.emplace_back(sensor.id, sensor.name, sensor.offset, sensor.factor, sensor.uom, sensor.type);
sensors_.back().ha_registered = false; // this will trigger recrate of the HA config
+ if (sensor.type == AnalogType::COUNTER || sensor.type >= AnalogType::DIGITAL_OUT) {
+ sensors_.back().set_value(sensor.offset);
+ } else {
+ sensors_.back().set_value(0); // reset value only for new sensors
+ }
}
}
return true;
@@ -82,11 +123,21 @@ void AnalogSensor::reload() {
} else if (sensor.type() == AnalogType::COUNTER) {
LOG_DEBUG(F("Adding analog I/O Counter sensor on GPIO%d"), sensor.id());
pinMode(sensor.id(), INPUT_PULLUP);
- sensor.set_value(0); // reset count
- sensor.set_uom(0); // no uom, just for safe measures
+ if (sensor.id() == 25 || sensor.id() == 26) {
+ dacWrite(sensor.id(), 255);
+ }
sensor.polltime_ = 0;
sensor.poll_ = digitalRead(sensor.id());
publish_sensor(sensor);
+ } else if (sensor.type() == AnalogType::TIMER || sensor.type() == AnalogType::RATE) {
+ LOG_DEBUG(F("Adding analog Timer/Rate sensor on GPIO%d"), sensor.id());
+ pinMode(sensor.id(), INPUT_PULLUP);
+ sensor.polltime_ = uuid::get_uptime();
+ sensor.last_polltime_ = uuid::get_uptime();
+ sensor.poll_ = digitalRead(sensor.id());
+ sensor.set_offset(0);
+ sensor.set_value(0);
+ publish_sensor(sensor);
} else if (sensor.type() == AnalogType::DIGITAL_IN) {
LOG_DEBUG(F("Adding analog Read sensor on GPIO%d"), sensor.id());
pinMode(sensor.id(), INPUT_PULLUP);
@@ -95,18 +146,49 @@ void AnalogSensor::reload() {
sensor.polltime_ = 0;
sensor.poll_ = digitalRead(sensor.id());
publish_sensor(sensor);
+ } else if (sensor.type() == AnalogType::DIGITAL_OUT) {
+ LOG_DEBUG(F("Adding analog Write sensor on GPIO%d"), sensor.id());
+ pinMode(sensor.id(), OUTPUT);
+ if (sensor.id() == 25 || sensor.id() == 26) {
+ if (sensor.offset() > 255) {
+ sensor.set_offset(255);
+ } else if (sensor.offset() < 0) {
+ sensor.set_offset(0);
+ }
+ dacWrite(sensor.id(), sensor.offset());
+ sensor.set_value(sensor.offset());
+ } else {
+ digitalWrite(sensor.id(), sensor.offset() > 0 ? 1 : 0);
+ sensor.set_value(digitalRead(sensor.id()));
+ }
+ sensor.set_uom(0); // no uom, just for safe measures
+ publish_sensor(sensor);
+ } else if (sensor.type() >= AnalogType::PWM_0) {
+ LOG_DEBUG(F("Adding PWM output sensor on GPIO%d"), sensor.id());
+ uint channel = sensor.type() - AnalogType::PWM_0;
+ ledcSetup(channel, sensor.factor(), 13);
+ ledcAttachPin(sensor.id(), channel);
+ if (sensor.offset() > 100) {
+ sensor.set_offset(100);
+ } else if (sensor.offset() < 0) {
+ sensor.set_offset(0);
+ }
+ ledcWrite(channel, (uint32_t)(sensor.offset() * 8191 / 100));
+ sensor.set_value(sensor.offset());
+ sensor.set_uom(DeviceValueUOM::PERCENT);
+ publish_sensor(sensor);
}
}
}
-// measure and moving average adc
+// measure input sensors and moving average adc
void AnalogSensor::measure() {
static uint32_t measure_last_ = 0;
- // measure interval 500ms for analog sensors
+ // measure interval 500ms for adc sensors
if (!measure_last_ || (uuid::get_uptime() - measure_last_) >= MEASURE_ANALOG_INTERVAL) {
measure_last_ = uuid::get_uptime();
- // go through the list of ADC sensors
+ // go through the list of adc sensors
for (auto & sensor : sensors_) {
if (sensor.type() == AnalogType::ADC) {
uint16_t a = analogReadMilliVolts(sensor.id()); // e.g. ADC1_CHANNEL_0_GPIO_NUM
@@ -128,34 +210,38 @@ void AnalogSensor::measure() {
}
}
}
- // poll digital io every time
+ // poll digital io every time with debounce
// go through the list of digital sensors
for (auto & sensor : sensors_) {
- if (sensor.type() == AnalogType::DIGITAL_IN || sensor.type() == AnalogType::COUNTER) {
+ if (sensor.type() == AnalogType::DIGITAL_IN || sensor.type() == AnalogType::COUNTER || sensor.type() == AnalogType::TIMER
+ || sensor.type() == AnalogType::RATE) {
auto old_value = sensor.value(); // remember current value before reading
auto current_reading = digitalRead(sensor.id());
- if (sensor.poll_ != current_reading) { // check for pinchange
- sensor.polltime_ = uuid::get_uptime();
+ if (sensor.poll_ != current_reading) { // check for pinchange
+ sensor.polltime_ = uuid::get_uptime(); // remember time of pinchange
sensor.poll_ = current_reading;
}
- if (uuid::get_uptime() - sensor.polltime_ >= 15) { // debounce
+ // debounce and check for real pinchange
+ if (uuid::get_uptime() - sensor.polltime_ >= 15 && sensor.poll_ != sensor.last_reading_) {
+ sensor.last_reading_ = sensor.poll_;
if (sensor.type() == AnalogType::DIGITAL_IN) {
sensor.set_value(sensor.poll_);
- } else if (sensor.type() == AnalogType::COUNTER) {
- // capture reading and compare with the last one to see if there is high/low change
- if (sensor.poll_ != sensor.last_reading_) {
- sensor.last_reading_ = sensor.poll_;
- if (!sensor.poll_) {
- sensor.set_value(old_value + 1);
- }
+ } else if (!sensor.poll_) { // falling edge
+ if (sensor.type() == AnalogType::COUNTER) {
+ sensor.set_value(old_value + sensor.factor());
+ } else if (sensor.type() == AnalogType::RATE) { // dafault 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);
}
+ sensor.last_polltime_ = sensor.polltime_;
}
- // see if there is a change and increment # reads
- if (old_value != sensor.value()) {
- sensorreads_++;
- changed_ = true;
- publish_sensor(sensor);
- }
+ }
+ // see if there is a change and increment # reads
+ if (old_value != sensor.value()) {
+ sensorreads_++;
+ changed_ = true;
+ publish_sensor(sensor);
}
}
}
@@ -170,7 +256,7 @@ void AnalogSensor::loop() {
}
// update analog information name and offset
-bool AnalogSensor::update(uint8_t id, const std::string & name, uint16_t offset, float factor, uint8_t uom, int8_t type) {
+bool AnalogSensor::update(uint8_t id, const std::string & name, float offset, float 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(
@@ -240,7 +326,11 @@ bool AnalogSensor::updated_values() {
void AnalogSensor::publish_sensor(const Sensor & sensor) {
if (Mqtt::publish_single()) {
char topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
- snprintf(topic, sizeof(topic), "%s/%s", read_flash_string(F_(analogsensor)).c_str(), sensor.name().c_str());
+ if (Mqtt::publish_single2cmd()) {
+ snprintf(topic, sizeof(topic), "%s/%s", read_flash_string(F_(analogsensor)).c_str(), sensor.name().c_str());
+ } else {
+ snprintf(topic, sizeof(topic), "%s%s/%s", read_flash_string(F_(analogsensor)).c_str(), "_data", sensor.name().c_str());
+ }
char payload[10];
Mqtt::publish(topic, Helpers::render_value(payload, sensor.value(), 2)); // always publish as floats
}
@@ -285,12 +375,16 @@ void AnalogSensor::publish_values(const bool force) {
dataSensor["name"] = sensor.name();
switch (sensor.type()) {
case AnalogType::COUNTER:
- dataSensor["value"] = (uint16_t)sensor.value(); // convert to integer
- break;
+ case AnalogType::TIMER:
+ case AnalogType::RATE:
case AnalogType::ADC:
+ case AnalogType::PWM_0:
+ case AnalogType::PWM_1:
+ case AnalogType::PWM_2:
dataSensor["value"] = (float)sensor.value(); // float
break;
case AnalogType::DIGITAL_IN:
+ case AnalogType::DIGITAL_OUT:
default:
dataSensor["value"] = (uint8_t)sensor.value(); // convert to char for 1 or 0
break;
@@ -373,6 +467,15 @@ bool AnalogSensor::command_info(const char * value, const int8_t id, JsonObject
dataSensor["uom"] = EMSdevice::uom_to_string(sensor.uom());
dataSensor["offset"] = sensor.offset();
dataSensor["factor"] = sensor.factor();
+ } else if (sensor.type() == AnalogType::COUNTER) {
+ dataSensor["uom"] = EMSdevice::uom_to_string(sensor.uom());
+ dataSensor["start_value"] = sensor.offset();
+ dataSensor["factor"] = sensor.factor();
+ } else if (sensor.type() == AnalogType::TIMER || sensor.type() == AnalogType::RATE) {
+ dataSensor["factor"] = sensor.factor();
+ } else if (sensor.type() >= AnalogType::PWM_0) {
+ dataSensor["uom"] = EMSdevice::uom_to_string(sensor.uom());
+ dataSensor["frequency"] = sensor.factor();
}
dataSensor["value"] = sensor.value();
} else {
@@ -384,7 +487,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 id, const std::string & name, const uint16_t offset, const float factor, const uint8_t uom, const int8_t type)
+AnalogSensor::Sensor::Sensor(const uint8_t id, const std::string & name, const float offset, const float factor, const uint8_t uom, const int8_t type)
: id_(id)
, name_(name)
, offset_(offset)
@@ -405,28 +508,70 @@ std::string AnalogSensor::Sensor::name() const {
}
// set the counter value, id is gpio-no
-bool AnalogSensor::command_counter(const char * value, const int8_t id) {
- int val;
- if (!Helpers::value2number(value, val)) {
+bool AnalogSensor::command_setvalue(const char * value, const int8_t id) {
+ float val;
+ if (!Helpers::value2float(value, val)) {
return false;
}
for (auto & sensor : sensors_) {
- if (sensor.type() == AnalogType::COUNTER && sensor.id() == id) {
- if (val < 0) { // negative values corrects
- sensor.set_value(sensor.value() + val);
- } else { // positive values are set
+ if (sensor.id() == id) {
+ if (sensor.type() == AnalogType::COUNTER) {
+ if (val < 0 || value[0] == '+') { // sign corrects values
+ sensor.set_offset(sensor.value() + val);
+ sensor.set_value(sensor.value() + val);
+ } else { // positive values are set
+ sensor.set_offset(val);
+ sensor.set_value(val);
+ }
+ publish_sensor(sensor);
+ return true;
+ } else if (sensor.type() == AnalogType::ADC) {
+ sensor.set_offset(val);
+ return true;
+ } else if (sensor.type() == AnalogType::DIGITAL_OUT) {
+ uint8_t v = val;
+ if ((sensor.id() == 25 || sensor.id() == 26) && v <= 255) {
+ sensor.set_offset(v);
+ sensor.set_value(v);
+ pinMode(sensor.id(), OUTPUT);
+ dacWrite(sensor.id(), sensor.offset());
+ publish_sensor(sensor);
+ return true;
+ } else if (v == 0 || v == 1) {
+ sensor.set_offset(v);
+ sensor.set_value(v);
+ pinMode(sensor.id(), OUTPUT);
+ digitalWrite(sensor.id(), sensor.offset() > 0 ? 1 : 0);
+ publish_sensor(sensor);
+ return true;
+ }
+ } else if (sensor.type() >= AnalogType::PWM_0) {
+ uint8_t channel = sensor.type() - AnalogType::PWM_0;
+ if (val > 100) {
+ val = 100;
+ } else if (val < 0) {
+ val = 0;
+ }
+ sensor.set_offset(val);
sensor.set_value(val);
+ ledcWrite(channel, (uint32_t)(val * 8191 / 100));
+ publish_sensor(sensor);
+ return true;
}
- return true;
}
}
return false;
}
+// list commands
+bool AnalogSensor::command_commands(const char * value, const int8_t id, JsonObject & output) {
+ return Command::list(EMSdevice::DeviceType::ANALOGSENSOR, output);
+}
+
// hard coded tests
#ifdef EMSESP_DEBUG
void AnalogSensor::test() {
- // Sensor(const uint8_t id, const std::string & name, const uint16_t offset, const float factor, const uint8_t uom, const int8_t type);
+ // Sensor(const uint8_t id, const std::string & name, const float offset, const float factor, const uint8_t uom, const int8_t type);
sensors_.emplace_back(36, "test12", 0, 0.1, 17, AnalogType::ADC);
sensors_.back().set_value(12.4);
diff --git a/src/analogsensor.h b/src/analogsensor.h
index 70e94fd95..e48acd7e9 100644
--- a/src/analogsensor.h
+++ b/src/analogsensor.h
@@ -36,10 +36,10 @@ class AnalogSensor {
public:
class Sensor {
public:
- Sensor(const uint8_t id, const std::string & name, const uint16_t offset, const float factor, const uint8_t uom, const int8_t type);
+ Sensor(const uint8_t id, const std::string & name, const float offset, const float factor, const uint8_t uom, const int8_t type);
~Sensor() = default;
- void set_offset(const uint16_t offset) {
+ void set_offset(const float offset) {
offset_ = offset;
}
@@ -68,7 +68,7 @@ class AnalogSensor {
factor_ = factor;
}
- uint16_t offset() const {
+ float offset() const {
return offset_;
}
@@ -90,16 +90,18 @@ class AnalogSensor {
bool ha_registered = false;
- uint16_t analog_ = 0; // ADC - average value
- uint32_t sum_ = 0; // ADC - rolling sum
- uint16_t last_reading_ = 0; // IO COUNTER & ADC - last reading
- uint32_t polltime_ = 0; // digital IO & COUNTER debounce time
- int poll_ = 0;
+ uint16_t analog_ = 0; // ADC - average value
+ uint32_t sum_ = 0; // ADC - rolling sum
+ uint16_t last_reading_ = 0; // IO COUNTER & ADC - last reading
+ uint16_t count_ = 0; // counter raw counts
+ uint32_t polltime_ = 0; // digital IO & COUNTER debounce time
+ int poll_ = 0;
+ uint32_t last_polltime_ = 0; // for timer
private:
uint8_t id_;
std::string name_;
- uint16_t offset_;
+ float offset_;
float factor_;
uint8_t uom_;
float value_; // float because of the factor is a float
@@ -112,9 +114,15 @@ class AnalogSensor {
enum AnalogType : int8_t {
MARK_DELETED = -1, // mark for deletion
NOTUSED, // 0 - disabled
- DIGITAL_IN, // 1
- COUNTER, // 2
- ADC // 3
+ DIGITAL_IN,
+ COUNTER,
+ ADC,
+ TIMER,
+ RATE,
+ DIGITAL_OUT,
+ PWM_0,
+ PWM_1,
+ PWM_2
};
void start();
@@ -149,7 +157,7 @@ class AnalogSensor {
return sensors_.size();
}
- bool update(uint8_t id, const std::string & name, uint16_t offset, float factor, uint8_t uom, int8_t type);
+ bool update(uint8_t id, const std::string & name, float offset, float factor, uint8_t uom, int8_t type);
bool get_value_info(JsonObject & output, const char * cmd, const int8_t id);
#ifdef EMSESP_DEBUG
@@ -163,9 +171,10 @@ class AnalogSensor {
static uuid::log::Logger logger_;
void remove_ha_topic(const uint8_t id);
- bool command_counter(const char * value, const int8_t id);
+ bool command_setvalue(const char * value, const int8_t id);
void measure();
bool command_info(const char * value, const int8_t id, JsonObject & output);
+ bool command_commands(const char * value, const int8_t id, JsonObject & output);
std::vector sensors_; // our list of sensors
diff --git a/src/command.cpp b/src/command.cpp
index 6164134ed..0efece5cd 100644
--- a/src/command.cpp
+++ b/src/command.cpp
@@ -317,14 +317,13 @@ void Command::add(const uint8_t device_type, const __FlashStringHelper * cmd, co
}
// add a command to the list, which does return a json object as output
-// flag is fixed to MqttSubFlag::MQTT_SUB_FLAG_NOSUB so there will be no topic subscribed to this
void Command::add(const uint8_t device_type, const __FlashStringHelper * cmd, const cmd_json_function_p cb, const __FlashStringHelper * description, uint8_t flags) {
// if the command already exists for that device type don't add it
if (find_command(device_type, read_flash_string(cmd).c_str()) != nullptr) {
return;
}
- cmdfunctions_.emplace_back(device_type, (CommandFlag::MQTT_SUB_FLAG_NOSUB | flags), cmd, nullptr, cb, description); // callback for json is included
+ cmdfunctions_.emplace_back(device_type, flags, cmd, nullptr, cb, description); // callback for json is included
}
// see if a command exists for that device type
diff --git a/src/command.h b/src/command.h
index 6889be6c5..bc1bf7e7c 100644
--- a/src/command.h
+++ b/src/command.h
@@ -32,10 +32,9 @@ enum CommandFlag : uint8_t {
MQTT_SUB_FLAG_DEFAULT = 0, // 0 no flags set, always subscribe to MQTT
MQTT_SUB_FLAG_HC = (1 << 0), // 1 TAG_HC1 - TAG_HC8
MQTT_SUB_FLAG_WWC = (1 << 1), // 2 TAG_WWC1 - TAG_WWC4
- MQTT_SUB_FLAG_NOSUB = (1 << 2), // 4
- MQTT_SUB_FLAG_WW = (1 << 3), // 8 TAG_DEVICE_DATA_WW
- HIDDEN = (1 << 4), // 16 do not show in API or Web
- ADMIN_ONLY = (1 << 5) // 32 requires authentication
+ MQTT_SUB_FLAG_WW = (1 << 2), // 4 TAG_DEVICE_DATA_WW
+ HIDDEN = (1 << 3), // 8 do not show in API or Web
+ ADMIN_ONLY = (1 << 4) // 16 requires authentication
};
diff --git a/src/dallassensor.cpp b/src/dallassensor.cpp
index d1eb9215f..d8e9722ba 100644
--- a/src/dallassensor.cpp
+++ b/src/dallassensor.cpp
@@ -361,10 +361,10 @@ bool DallasSensor::command_info(const char * value, const int8_t id, JsonObject
JsonObject dataSensor = output.createNestedObject(sensor.name());
dataSensor["id_str"] = sensor.id_str();
if (Helpers::hasValue(sensor.temperature_c)) {
- dataSensor["temp"] = (float)(sensor.temperature_c) / 10;
+ dataSensor["temp"] = Helpers::round2((float)(sensor.temperature_c), 10, EMSESP::system_.fahrenheit() ? 2 : 0);
}
} else if (Helpers::hasValue(sensor.temperature_c)) {
- output[sensor.name()] = (float)(sensor.temperature_c) / 10;
+ output[sensor.name()] = Helpers::round2((float)(sensor.temperature_c), 10, EMSESP::system_.fahrenheit() ? 2 : 0);
}
}
@@ -378,11 +378,11 @@ bool DallasSensor::get_value_info(JsonObject & output, const char * cmd, const i
output["id_str"] = sensor.id_str();
output["name"] = sensor.name();
if (Helpers::hasValue(sensor.temperature_c)) {
- output["value"] = (float)(sensor.temperature_c) / 10;
+ output["value"] = Helpers::round2((float)(sensor.temperature_c), 10, EMSESP::system_.fahrenheit() ? 2 : 0);
}
output["type"] = F_(number);
- output["min"] = -55;
- output["max"] = 125;
+ output["min"] = Helpers::round2(-55, 0, EMSESP::system_.fahrenheit() ? 2 : 0);
+ output["max"] = Helpers::round2(125, 0, EMSESP::system_.fahrenheit() ? 2 : 0);
output["unit"] = EMSdevice::uom_to_string(DeviceValueUOM::DEGREES);
output["writeable"] = false;
return true;
@@ -395,7 +395,11 @@ bool DallasSensor::get_value_info(JsonObject & output, const char * cmd, const i
void DallasSensor::publish_sensor(const Sensor & sensor) {
if (Mqtt::publish_single()) {
char topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
- snprintf(topic, sizeof(topic), "%s/%s", read_flash_string(F_(dallassensor)).c_str(), sensor.name().c_str());
+ if (Mqtt::publish_single2cmd()) {
+ snprintf(topic, sizeof(topic), "%s/%s", read_flash_string(F_(dallassensor)).c_str(), sensor.name().c_str());
+ } else {
+ snprintf(topic, sizeof(topic), "%s%s/%s", read_flash_string(F_(dallassensor)).c_str(), "_data", sensor.name().c_str());
+ }
char payload[10];
Mqtt::publish(topic, Helpers::render_value(payload, sensor.temperature_c, 10, EMSESP::system_.fahrenheit() ? 2 : 0));
}
@@ -440,10 +444,10 @@ void DallasSensor::publish_values(const bool force) {
JsonObject dataSensor = doc.createNestedObject(sensor.id_str());
dataSensor["name"] = sensor.name();
if (has_value) {
- dataSensor["temp"] = (float)(sensor.temperature_c) / 10;
+ dataSensor["temp"] = Helpers::round2((float)(sensor.temperature_c), 10, EMSESP::system_.fahrenheit() ? 2 : 0);
}
} else if (has_value) {
- doc[sensor.name()] = (float)(sensor.temperature_c) / 10;
+ doc[sensor.name()] = Helpers::round2((float)(sensor.temperature_c), 10, EMSESP::system_.fahrenheit() ? 2 : 0);
}
// create the HA MQTT config
@@ -459,11 +463,7 @@ void DallasSensor::publish_values(const bool force) {
snprintf(stat_t, sizeof(stat_t), "%s/dallassensor_data", Mqtt::base().c_str());
config["stat_t"] = stat_t;
- if (EMSESP::system_.fahrenheit()) {
- config["unit_of_meas"] = FJSON("°F");
- } else {
- config["unit_of_meas"] = FJSON("°C");
- }
+ config["unit_of_meas"] = EMSdevice::uom_to_string(DeviceValueUOM::DEGREES);
char str[50];
snprintf(str, sizeof(str), "{{value_json['%s'].temp}}", sensor.id_str().c_str());
diff --git a/src/default_settings.h b/src/default_settings.h
index d399f9ace..b70fef920 100644
--- a/src/default_settings.h
+++ b/src/default_settings.h
@@ -128,10 +128,6 @@
#define EMSESP_DEFAULT_BOOL_FORMAT 1 // on/off
#endif
-#ifndef EMSESP_DEFAULT_HA_CLIMATE_FORMAT
-#define EMSESP_DEFAULT_HA_CLIMATE_FORMAT 1 // current temp
-#endif
-
#ifndef EMSESP_DEFAULT_MQTT_QOS
#define EMSESP_DEFAULT_MQTT_QOS 0
#endif
@@ -160,6 +156,10 @@
#define EMSESP_DEFAULT_PUBLISH_SINGLE false
#endif
+#ifndef EMSESP_DEFAULT_PUBLISH_SINGLE2CMD
+#define EMSESP_DEFAULT_PUBLISH_SINGLE2CMD false
+#endif
+
#ifndef EMSESP_DEFAULT_SEND_RESPONSE
#define EMSESP_DEFAULT_SEND_RESPONSE false
#endif
diff --git a/src/devices/boiler.cpp b/src/devices/boiler.cpp
index 6ddebf5d7..b562e64ad 100644
--- a/src/devices/boiler.cpp
+++ b/src/devices/boiler.cpp
@@ -150,8 +150,15 @@ Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const
MAKE_CF_CB(set_burn_period));
register_device_value(
DeviceValueTAG::TAG_BOILER_DATA, &burnMinPower_, DeviceValueType::UINT, nullptr, FL_(burnMinPower), DeviceValueUOM::PERCENT, MAKE_CF_CB(set_min_power));
- register_device_value(
- DeviceValueTAG::TAG_BOILER_DATA, &burnMaxPower_, DeviceValueType::UINT, nullptr, FL_(burnMaxPower), DeviceValueUOM::PERCENT, MAKE_CF_CB(set_max_power));
+ register_device_value(DeviceValueTAG::TAG_BOILER_DATA,
+ &burnMaxPower_,
+ DeviceValueType::UINT,
+ nullptr,
+ FL_(burnMaxPower),
+ DeviceValueUOM::PERCENT,
+ MAKE_CF_CB(set_max_power),
+ 0,
+ 130);
register_device_value(
DeviceValueTAG::TAG_BOILER_DATA, &boilHystOn_, DeviceValueType::INT, nullptr, FL_(boilHystOn), DeviceValueUOM::DEGREES_R, MAKE_CF_CB(set_hyst_on));
register_device_value(
@@ -318,7 +325,9 @@ Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const
nullptr,
FL_(wwMaxPower),
DeviceValueUOM::PERCENT,
- MAKE_CF_CB(set_ww_maxpower));
+ MAKE_CF_CB(set_ww_maxpower),
+ 0,
+ 130);
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA_WW,
&wwCircPump_,
DeviceValueType::BOOL,
diff --git a/src/devices/solar.cpp b/src/devices/solar.cpp
index 0eeeb2a22..68650deaa 100644
--- a/src/devices/solar.cpp
+++ b/src/devices/solar.cpp
@@ -155,10 +155,6 @@ Solar::Solar(uint8_t device_type, uint8_t device_id, uint8_t product_id, const c
FL_(solarPumpTurnoffDiff),
DeviceValueUOM::DEGREES_R,
MAKE_CF_CB(set_TurnoffDiff));
- register_device_value(
- DeviceValueTAG::TAG_NONE, &setting3_, DeviceValueType::UINT, nullptr, FL_(setting3), DeviceValueUOM::NONE, MAKE_CF_CB(set_CollectorMaxTemp));
- register_device_value(
- DeviceValueTAG::TAG_NONE, &setting4_, DeviceValueType::UINT, nullptr, FL_(setting4), DeviceValueUOM::NONE, MAKE_CF_CB(set_CollectorMinTemp));
register_device_value(DeviceValueTAG::TAG_NONE, &solarPower_, DeviceValueType::SHORT, nullptr, FL_(solarPower), DeviceValueUOM::W);
register_device_value(DeviceValueTAG::TAG_NONE, &energyLastHour_, DeviceValueType::ULONG, FL_(div10), FL_(energyLastHour), DeviceValueUOM::WH);
register_device_value(DeviceValueTAG::TAG_NONE, &maxFlow_, DeviceValueType::UINT, FL_(div10), FL_(maxFlow), DeviceValueUOM::LMIN, MAKE_CF_CB(set_SM10MaxFlow));
@@ -177,10 +173,16 @@ Solar::Solar(uint8_t device_type, uint8_t device_id, uint8_t product_id, const c
DeviceValueUOM::NONE,
MAKE_CF_CB(set_solarEnabled));
+ /* unknown values for testing and logging. Used by MichaelDvP
+ register_device_value(
+ DeviceValueTAG::TAG_NONE, &setting3_, DeviceValueType::UINT, nullptr, FL_(setting3), DeviceValueUOM::NONE, MAKE_CF_CB(set_CollectorMaxTemp));
+ register_device_value(
+ DeviceValueTAG::TAG_NONE, &setting4_, DeviceValueType::UINT, nullptr, FL_(setting4), DeviceValueUOM::NONE, MAKE_CF_CB(set_CollectorMinTemp));
register_device_value(DeviceValueTAG::TAG_NONE, &data11_, DeviceValueType::UINT, nullptr, FL_(data11), DeviceValueUOM::NONE);
register_device_value(DeviceValueTAG::TAG_NONE, &data12_, DeviceValueType::UINT, nullptr, FL_(data12), DeviceValueUOM::NONE);
register_device_value(DeviceValueTAG::TAG_NONE, &data1_, DeviceValueType::UINT, nullptr, FL_(data1), DeviceValueUOM::NONE);
register_device_value(DeviceValueTAG::TAG_NONE, &data0_, DeviceValueType::UINT, nullptr, FL_(data0), DeviceValueUOM::NONE);
+ */
}
if (flags == EMSdevice::EMS_DEVICE_FLAG_ISM) {
register_device_value(DeviceValueTAG::TAG_NONE, &energyLastHour_, DeviceValueType::ULONG, FL_(div10), FL_(energyLastHour), DeviceValueUOM::WH);
@@ -400,6 +402,7 @@ void Solar::process_SM10Config(std::shared_ptr telegram) {
}
// SM10Monitor - type 0x97
+// Solar(0x30) -> All(0x00), SM10Monitor(0x97), data: 00 00 00 22 00 00 D2 01 00 F6 2A 00 00
void Solar::process_SM10Monitor(std::shared_ptr telegram) {
uint8_t solarpumpmod = solarPumpMod_;
@@ -436,11 +439,11 @@ void Solar::process_SM10Monitor(std::shared_ptr telegram) {
energy.pop_front();
}
energy.push_back(solarPower_);
- uint32_t sum = 0;
+ int32_t sum = 0;
for (auto e : energy) {
sum += e;
}
- energyLastHour_ = sum / 6; // counts in 0.1 Wh
+ energyLastHour_ = sum > 0 ? sum / 6 : 0; // counts in 0.1 Wh
has_update(&solarPower_);
has_update(&energyLastHour_);
}
diff --git a/src/devices/thermostat.cpp b/src/devices/thermostat.cpp
index 0c6e14ea1..279ead77c 100644
--- a/src/devices/thermostat.cpp
+++ b/src/devices/thermostat.cpp
@@ -80,10 +80,14 @@ Thermostat::Thermostat(uint8_t device_type, uint8_t device_id, uint8_t product_i
} else if (model == EMSdevice::EMS_DEVICE_FLAG_RC20) {
monitor_typeids = {0x91};
set_typeids = {0xA8};
+ curve_typeids = {0x90};
+ timer_typeids = {0x8F};
if (actual_master_thermostat == device_id) {
for (uint8_t i = 0; i < monitor_typeids.size(); i++) {
register_telegram_type(monitor_typeids[i], F("RC20Monitor"), false, MAKE_PF_CB(process_RC20Monitor));
register_telegram_type(set_typeids[i], F("RC20Set"), false, MAKE_PF_CB(process_RC20Set));
+ register_telegram_type(curve_typeids[i], F("RC20Temp"), false, MAKE_PF_CB(process_RC20Temp));
+ register_telegram_type(timer_typeids[i], F("RC20Timer"), false, MAKE_PF_CB(process_RC20Timer));
}
}
// remote thermostat uses only 0xAF, register it also for master (in case of early detect)
@@ -345,6 +349,9 @@ std::shared_ptr Thermostat::heating_circuit(std::sha
}
// set the flag saying we want its data during the next auto fetch
+ // monitor is broadcasted, but not frequently in some thermostats (IVT, #356)
+ toggle_fetch(monitor_typeids[hc_num - 1], toggle_);
+
if (set_typeids.size()) {
toggle_fetch(set_typeids[hc_num - 1], toggle_);
}
@@ -384,7 +391,7 @@ void Thermostat::publish_ha_config_hc(std::shared_ptrroomTemp);
- if (Mqtt::nested_format() == 1) {
+ if (Mqtt::is_nested()) {
// nested format
snprintf(hc_mode_s, sizeof(hc_mode_s), "value_json.hc%d.mode", hc_num);
snprintf(seltemp_s, sizeof(seltemp_s), "{{value_json.hc%d.seltemp}}", hc_num);
@@ -727,6 +734,48 @@ void Thermostat::process_RC20Set(std::shared_ptr telegram) {
has_update(telegram, hc->manualtemp, 29);
}
+// 0x90 - for reading curve temperature from the RC20 thermostat (0x17)
+//
+void Thermostat::process_RC20Temp(std::shared_ptr telegram) {
+ std::shared_ptr hc = heating_circuit(telegram);
+ if (hc == nullptr) {
+ return;
+ }
+ has_update(telegram, hc->nighttemp, 3); // 0:off, 1:manual, 2:auto
+ has_update(telegram, hc->daylowtemp, 4);
+ has_update(telegram, hc->daymidtemp, 5);
+ has_update(telegram, hc->daytemp, 6);
+}
+
+// 0x8F - for reading timer from the RC20 thermostat (0x17)
+// data: 04 5D 01 78 24 5D 21 6E 43 5D 41 78 64 5D 61 78 84 5D 81 78 E7 90 E7 90 E7 90 E7
+// data: 90 E7 90 E7 90 E7 90 E7 90 E7 90 E7 90 E7 90 E7 90 E7 90 E7 90 E7 90 E7 90 E7 90 (offset 27)
+// data: E7 90 E7 90 E7 90 E7 90 E7 90 E7 90 E7 90 E7 90 E7 90 E7 90 E7 90 E7 90 E7 90 E7 (offset 54)
+// data: 90 E7 90 01 00 00 01 01 00 01 01 00 01 01 00 01 01 00 00 (offset 81)
+void Thermostat::process_RC20Timer(std::shared_ptr telegram) {
+ std::shared_ptr hc = heating_circuit(telegram);
+ if (hc == nullptr) {
+ return;
+ }
+ if ((telegram->message_length == 2 && telegram->offset < 83 && !(telegram->offset & 1))
+ || (!telegram->offset && telegram->message_length > 1 && !strlen(hc->switchtime1))) {
+ char data[sizeof(hc->switchtime1)];
+ uint8_t no = telegram->offset / 2;
+ uint8_t day = telegram->message_data[0] >> 5;
+ uint8_t temp = telegram->message_data[0] & 1;
+ uint8_t time = telegram->message_data[1];
+
+ std::string sday = read_flash_string(FL_(enum_dayOfWeek)[day]);
+ if (day == 7) {
+ snprintf(data, sizeof(data), "%02d not_set", no);
+ } else {
+ snprintf(data, sizeof(data), "%02d %s %02d:%02d T%d", no, sday.c_str(), time / 6, 10 * (time % 6), temp);
+ }
+ strlcpy(hc->switchtime1, data, sizeof(hc->switchtime1));
+ has_update(hc->switchtime1); // always publish
+ }
+}
+
// type 0xAE - data from the RC20 thermostat (0x17) - not for RC20's
// 17 00 AE 00 80 12 2E 00 D0 00 00 64 (#data=8)
// https://github.com/emsesp/EMS-ESP/issues/361
@@ -1022,6 +1071,7 @@ void Thermostat::process_RC300Summer(std::shared_ptr telegram) {
}
has_update(telegram, hc->roominfluence, 0);
+ has_update(telegram, hc->roominfl_factor, 1); // is * 10
has_update(telegram, hc->offsettemp, 2);
if (!is_fetch(summer2_typeids[hc->hc()])) {
has_update(telegram, hc->summertemp, 6);
@@ -1911,6 +1961,23 @@ bool Thermostat::set_datetime(const char * value, const int8_t id) {
return true;
}
+// set RC300 roominfluence factor
+bool Thermostat::set_roominfl_factor(const char * value, const int8_t id) {
+ uint8_t hc_num = (id == -1) ? AUTO_HEATING_CIRCUIT : id;
+ std::shared_ptr hc = heating_circuit(hc_num);
+ if (hc == nullptr) {
+ return false;
+ }
+ float val = 0;
+ if (!Helpers::value2float(value, val)) {
+ return false;
+ }
+
+ write_command(summer_typeids[hc->hc()], 1, (uint8_t)(val * 10));
+
+ return true;
+}
+
// sets the thermostat working mode, where mode is a string
// converts string mode to HeatingCircuit::Mode
bool Thermostat::set_mode(const char * value, const int8_t id) {
@@ -2302,14 +2369,25 @@ bool Thermostat::set_switchtime(const char * value, const uint16_t type_id, char
data[1] = time;
}
- if (no > 41 || time > 0x90 || (on > 1 && on != 7)) {
- LOG_WARNING(F("Setting switchtime: Invalid data: %s"), value);
+ uint8_t max_on = 3;
+ if ((model() == EMS_DEVICE_FLAG_RC35) || (model() == EMS_DEVICE_FLAG_RC30_N)) {
+ max_on = 1;
+ }
+ if (no > 41 || time > 0x90 || (on > max_on && on != 7)) {
+ // LOG_WARNING(F("Setting switchtime: Invalid data: %s"), value);
// LOG_WARNING(F("Setting switchtime: Invalid data: %02d.%1d.0x%02X.%1d"), no, day, time, on);
return false;
}
if (data[0] != 0xE7) {
std::string sday = read_flash_string(FL_(enum_dayOfWeek)[day]);
- snprintf(out, len, "%02d %s %02d:%02d %s", no, sday.c_str(), time / 6, 10 * (time % 6), on ? "on" : "off");
+ if ((model() == EMS_DEVICE_FLAG_RC35) || (model() == EMS_DEVICE_FLAG_RC30_N)) {
+ snprintf(out, len, "%02d %s %02d:%02d %s", no, sday.c_str(), time / 6, 10 * (time % 6), on ? "on" : "off");
+ } else if (model() == EMS_DEVICE_FLAG_RC20) {
+ snprintf(out, len, "%02d %s %02d:%02d T%d", no, sday.c_str(), time / 6, 10 * (time % 6), on);
+ } else {
+ std::string son = read_flash_string(FL_(enum_switchmode)[on]);
+ snprintf(out, len, "%02d %s %02d:%02d %s", no, sday.c_str(), time / 6, 10 * (time % 6), son.c_str());
+ }
} else {
snprintf(out, len, "%02d not_set", no);
}
@@ -2449,6 +2527,26 @@ bool Thermostat::set_temperature(const float temperature, const uint8_t mode, co
}
} else if (model == EMS_DEVICE_FLAG_RC20) {
switch (mode) {
+ case HeatingCircuit::Mode::NIGHT:
+ offset = 3;
+ set_typeid = curve_typeids[hc->hc()];
+ validate_typeid = set_typeid;
+ break;
+ case HeatingCircuit::Mode::DAYLOW:
+ offset = 4;
+ set_typeid = curve_typeids[hc->hc()];
+ validate_typeid = set_typeid;
+ break;
+ case HeatingCircuit::Mode::DAYMID:
+ offset = 5;
+ set_typeid = curve_typeids[hc->hc()];
+ validate_typeid = set_typeid;
+ break;
+ case HeatingCircuit::Mode::DAY:
+ offset = 6;
+ set_typeid = curve_typeids[hc->hc()];
+ validate_typeid = set_typeid;
+ break;
case HeatingCircuit::Mode::MANUAL:
offset = EMS_OFFSET_RC20Set_temp_manual;
break;
@@ -2482,7 +2580,7 @@ bool Thermostat::set_temperature(const float temperature, const uint8_t mode, co
offset = 0x0A; // manual offset
break;
case HeatingCircuit::Mode::TEMPAUTO:
- offset = 0x08; // manual offset
+ offset = 0x08; // auto offset
if (temperature == -1) {
factor = 1; // to write 0xFF
}
@@ -2758,6 +2856,14 @@ bool Thermostat::set_daytemp(const char * value, const int8_t id) {
return set_temperature_value(value, id, HeatingCircuit::Mode::DAY);
}
+bool Thermostat::set_daylowtemp(const char * value, const int8_t id) {
+ return set_temperature_value(value, id, HeatingCircuit::Mode::DAYLOW);
+}
+
+bool Thermostat::set_daymidtemp(const char * value, const int8_t id) {
+ return set_temperature_value(value, id, HeatingCircuit::Mode::DAYMID);
+}
+
bool Thermostat::set_comforttemp(const char * value, const int8_t id) {
return set_temperature_value(value, id, HeatingCircuit::Mode::COMFORT);
}
@@ -3101,7 +3207,9 @@ void Thermostat::register_device_values() {
FL_(div10),
FL_(ibaCalIntTemperature),
DeviceValueUOM::DEGREES_R,
- MAKE_CF_CB(set_calinttemp));
+ MAKE_CF_CB(set_calinttemp),
+ -5,
+ 5);
register_device_value(DeviceValueTAG::TAG_THERMOSTAT_DATA,
&ibaMinExtTemperature_,
DeviceValueType::INT,
@@ -3269,6 +3377,8 @@ void Thermostat::register_device_values_hc(std::shared_ptrminflowtemp, DeviceValueType::UINT, nullptr, FL_(minflowtemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_minflowtemp));
register_device_value(tag, &hc->maxflowtemp, DeviceValueType::UINT, nullptr, FL_(maxflowtemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_maxflowtemp));
register_device_value(tag, &hc->roominfluence, DeviceValueType::UINT, nullptr, FL_(roominfluence), DeviceValueUOM::DEGREES_R, MAKE_CF_CB(set_roominfluence));
+ register_device_value(
+ tag, &hc->roominfl_factor, DeviceValueType::UINT, FL_(div10), FL_(roominfl_factor), DeviceValueUOM::NONE, MAKE_CF_CB(set_roominfl_factor));
register_device_value(tag, &hc->curroominfl, DeviceValueType::SHORT, FL_(div10), FL_(curroominfl), DeviceValueUOM::DEGREES_R);
register_device_value(tag, &hc->nofrosttemp, DeviceValueType::INT, nullptr, FL_(nofrosttemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_nofrosttemp));
register_device_value(tag, &hc->targetflowtemp, DeviceValueType::UINT, nullptr, FL_(targetflowtemp), DeviceValueUOM::DEGREES);
@@ -3292,6 +3402,11 @@ void Thermostat::register_device_values_hc(std::shared_ptrmode, DeviceValueType::ENUM, FL_(enum_mode2), FL_(mode), DeviceValueUOM::NONE, MAKE_CF_CB(set_mode));
register_device_value(tag, &hc->manualtemp, DeviceValueType::UINT, FL_(div2), FL_(manualtemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_manualtemp));
+ register_device_value(tag, &hc->daylowtemp, DeviceValueType::UINT, FL_(div2), FL_(daylowtemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_daylowtemp));
+ register_device_value(tag, &hc->daymidtemp, DeviceValueType::UINT, FL_(div2), FL_(daymidtemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_daymidtemp));
+ register_device_value(tag, &hc->daytemp, DeviceValueType::UINT, FL_(div2), FL_(dayhightemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_daytemp));
+ register_device_value(tag, &hc->nighttemp, DeviceValueType::UINT, FL_(div2), FL_(nighttemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_nighttemp));
+ register_device_value(tag, &hc->switchtime1, DeviceValueType::STRING, FL_(tpl_switchtime), FL_(switchtime), DeviceValueUOM::NONE, MAKE_CF_CB(set_switchtime1));
break;
case EMS_DEVICE_FLAG_RC20_N:
register_device_value(tag, &hc->mode, DeviceValueType::ENUM, FL_(enum_mode2), FL_(mode), DeviceValueUOM::NONE, MAKE_CF_CB(set_mode));
diff --git a/src/devices/thermostat.h b/src/devices/thermostat.h
index 9b38069b9..dc2d20771 100644
--- a/src/devices/thermostat.h
+++ b/src/devices/thermostat.h
@@ -43,6 +43,8 @@ class Thermostat : public EMSdevice {
uint8_t summermode;
uint8_t holidaymode;
uint8_t daytemp;
+ uint8_t daylowtemp;
+ uint8_t daymidtemp;
uint8_t nighttemp;
uint8_t holidaytemp;
uint8_t heatingtype; // type of heating: 1 radiator, 2 convectors, 3 floors, 4 room supply
@@ -54,6 +56,7 @@ class Thermostat : public EMSdevice {
uint8_t manualtemp;
uint8_t summer_setmode;
uint8_t roominfluence;
+ uint8_t roominfl_factor;
int16_t curroominfl;
uint8_t flowtempoffset;
uint8_t minflowtemp;
@@ -125,6 +128,8 @@ class Thermostat : public EMSdevice {
TEMPAUTO,
NOREDUCE,
ON,
+ DAYLOW,
+ DAYMID,
UNKNOWN
};
@@ -313,6 +318,8 @@ class Thermostat : public EMSdevice {
void process_RC30Set(std::shared_ptr telegram);
void process_RC20Monitor(std::shared_ptr telegram);
void process_RC20Set(std::shared_ptr telegram);
+ void process_RC20Temp(std::shared_ptr telegram);
+ void process_RC20Timer(std::shared_ptr telegram);
void process_RC20Remote(std::shared_ptr telegram);
void process_RC20Monitor_2(std::shared_ptr telegram);
void process_RC20Set_2(std::shared_ptr telegram);
@@ -358,6 +365,8 @@ class Thermostat : public EMSdevice {
bool set_temp(const char * value, const int8_t id);
bool set_nighttemp(const char * value, const int8_t id);
bool set_daytemp(const char * value, const int8_t id);
+ bool set_daylowtemp(const char * value, const int8_t id);
+ bool set_daymidtemp(const char * value, const int8_t id);
bool set_comforttemp(const char * value, const int8_t id);
bool set_nofrosttemp(const char * value, const int8_t id);
bool set_ecotemp(const char * value, const int8_t id);
@@ -371,6 +380,7 @@ class Thermostat : public EMSdevice {
bool set_noreducetemp(const char * value, const int8_t id);
bool set_remotetemp(const char * value, const int8_t id);
bool set_roominfluence(const char * value, const int8_t id);
+ bool set_roominfl_factor(const char * value, const int8_t id);
bool set_flowtempoffset(const char * value, const int8_t id);
bool set_minflowtemp(const char * value, const int8_t id);
bool set_maxflowtemp(const char * value, const int8_t id);
diff --git a/src/emsdevice.cpp b/src/emsdevice.cpp
index 52ed4be07..17a0f4434 100644
--- a/src/emsdevice.cpp
+++ b/src/emsdevice.cpp
@@ -52,6 +52,9 @@ const std::string EMSdevice::tag_to_mqtt(uint8_t tag) {
}
const std::string EMSdevice::uom_to_string(uint8_t uom) {
+ if (EMSESP::system_.fahrenheit() && (uom == DeviceValueUOM::DEGREES || uom == DeviceValueUOM::DEGREES_R)) {
+ return read_flash_string(DeviceValue::DeviceValueUOM_s[DeviceValueUOM::FAHRENHEIT]);
+ }
return read_flash_string(DeviceValue::DeviceValueUOM_s[uom]);
}
@@ -306,11 +309,7 @@ void EMSdevice::list_device_entries(JsonObject & output) {
// add uom
if (!uom_to_string(dv.uom).empty() && uom_to_string(dv.uom) != " ") {
- if (EMSESP::system_.fahrenheit() && (dv.uom == DeviceValueUOM::DEGREES || dv.uom == DeviceValueUOM::DEGREES_R)) {
- details.add(EMSdevice::uom_to_string(DeviceValueUOM::FAHRENHEIT));
- } else {
- details.add(EMSdevice::uom_to_string(dv.uom));
- }
+ details.add(EMSdevice::uom_to_string(dv.uom));
}
}
}
@@ -533,16 +532,26 @@ void EMSdevice::publish_value(void * value_p) {
for (auto & dv : devicevalues_) {
if (dv.value_p == value_p && dv.has_state(DeviceValueState::DV_VISIBLE)) {
char topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
- if ((dv.tag >= DeviceValueTAG::TAG_HC1 && dv.tag <= DeviceValueTAG::TAG_HC8)
- || (dv.tag >= DeviceValueTAG::TAG_WWC1 && dv.tag <= DeviceValueTAG::TAG_WWC4)) {
+ if (Mqtt::publish_single2cmd()) {
+ if ((dv.tag >= DeviceValueTAG::TAG_HC1 && dv.tag <= DeviceValueTAG::TAG_WWC4)) {
+ snprintf(topic,
+ sizeof(topic),
+ "%s/%s/%s",
+ device_type_2_device_name(device_type_).c_str(),
+ tag_to_mqtt(dv.tag).c_str(),
+ read_flash_string(dv.short_name).c_str());
+ } else {
+ snprintf(topic, sizeof(topic), "%s/%s", device_type_2_device_name(device_type_).c_str(), read_flash_string(dv.short_name).c_str());
+ }
+ } else if (Mqtt::is_nested() && dv.tag >= DeviceValueTAG::TAG_HC1) {
snprintf(topic,
sizeof(topic),
"%s/%s/%s",
- device_type_2_device_name(device_type_).c_str(),
+ Mqtt::tag_to_topic(device_type_, dv.tag).c_str(),
tag_to_mqtt(dv.tag).c_str(),
read_flash_string(dv.short_name).c_str());
} else {
- snprintf(topic, sizeof(topic), "%s/%s", device_type_2_device_name(device_type_).c_str(), read_flash_string(dv.short_name).c_str());
+ snprintf(topic, sizeof(topic), "%s/%s", Mqtt::tag_to_topic(device_type_, dv.tag).c_str(), read_flash_string(dv.short_name).c_str());
}
int8_t divider = (dv.options_size == 1) ? Helpers::atoint(read_flash_string(dv.options[0]).c_str()) : 0;
@@ -1014,7 +1023,7 @@ bool EMSdevice::get_value_info(JsonObject & output, const char * cmd, const int8
// add uom if it's not a " " (single space)
if (!uom_to_string(dv.uom).empty() && uom_to_string(dv.uom) != " ") {
- json["uom"] = fahrenheit ? "°F" : uom_to_string(dv.uom);
+ json["uom"] = uom_to_string(dv.uom);
}
json["writeable"] = dv.has_cmd;
diff --git a/src/emsdevicevalue.h b/src/emsdevicevalue.h
index a9b4b029c..c6bf629b6 100644
--- a/src/emsdevicevalue.h
+++ b/src/emsdevicevalue.h
@@ -66,7 +66,9 @@ class DeviceValue {
DBM, // 15
FAHRENHEIT, // 16
MV, // 17
- SQM // 18
+ SQM, // 18 squaremeter
+ M3, // 19 cubic meter
+ L // 20
};
// TAG mapping - maps to DeviceValueTAG_s in emsdevice.cpp
diff --git a/src/emsesp.cpp b/src/emsesp.cpp
index b6ec5f953..9a9c53ddd 100644
--- a/src/emsesp.cpp
+++ b/src/emsesp.cpp
@@ -1136,6 +1136,9 @@ bool EMSESP::add_device(const uint8_t device_id, const uint8_t product_id, const
fetch_device_values(device_id); // go and fetch its data
+ // Print to LOG showing we've added a new device
+ LOG_INFO(F("Recognized new %s with deviceID 0x%02X"), EMSdevice::device_type_2_device_name(device_type).c_str(), device_id);
+
// add command commands for all devices, except for connect, controller and gateway
if ((device_type == DeviceType::CONNECT) || (device_type == DeviceType::CONTROLLER) || (device_type == DeviceType::GATEWAY)) {
return true;
@@ -1170,9 +1173,6 @@ bool EMSESP::add_device(const uint8_t device_id, const uint8_t product_id, const
// MQTT subscribe to the device e.g. "ems-esp/boiler/#"
Mqtt::subscribe(device_type, EMSdevice::device_type_2_device_name(device_type) + "/#", nullptr);
- // Print to LOG showing we've added a new device
- LOG_INFO(F("Recognized new %s with deviceID 0x%02X"), EMSdevice::device_type_2_device_name(device_type).c_str(), device_id);
-
return true;
}
@@ -1413,6 +1413,28 @@ void EMSESP::start() {
webServer.begin(); // start the web server
}
+// fetch devices one by one
+void EMSESP::scheduled_fetch_values() {
+ static uint8_t no = 0;
+ if (no || (uuid::get_uptime() - last_fetch_ > EMS_FETCH_FREQUENCY)) {
+ if (!no) {
+ last_fetch_ = uuid::get_uptime();
+ no = 1;
+ }
+ if (txservice_.tx_queue_empty()) {
+ uint8_t i = 0;
+ for (const auto & emsdevice : emsdevices) {
+ if (emsdevice && ++i >= no) {
+ emsdevice->fetch_values();
+ no++;
+ return;
+ }
+ }
+ no = 0;
+ }
+ }
+}
+
// main loop calling all services
void EMSESP::loop() {
esp8266React.loop(); // web services
@@ -1429,10 +1451,7 @@ void EMSESP::loop() {
mqtt_.loop(); // sends out anything in the MQTT queue
// force a query on the EMS devices to fetch latest data at a set interval (1 min)
- if ((uuid::get_uptime() - last_fetch_ > EMS_FETCH_FREQUENCY)) {
- last_fetch_ = uuid::get_uptime();
- fetch_device_values();
- }
+ scheduled_fetch_values();
}
console_.loop(); // telnet/serial console
diff --git a/src/emsesp.h b/src/emsesp.h
index f9fc2fc31..3395824d5 100644
--- a/src/emsesp.h
+++ b/src/emsesp.h
@@ -206,6 +206,7 @@ class EMSESP {
static void fetch_device_values(const uint8_t device_id = 0);
static void fetch_device_values_type(const uint8_t device_type);
static bool valid_device(const uint8_t device_id);
+ static void scheduled_fetch_values();
static bool add_device(const uint8_t device_id, const uint8_t product_id, const char * version, const uint8_t brand);
static void scan_devices();
diff --git a/src/helpers.cpp b/src/helpers.cpp
index 343b5a01a..0f9b339f3 100644
--- a/src/helpers.cpp
+++ b/src/helpers.cpp
@@ -526,6 +526,10 @@ bool Helpers::value2float(const char * v, float & value) {
value = atof((char *)v);
return true;
}
+ if (v[0] == '+' && (v[1] == '.' || (v[1] >= '0' && v[1] <= '9'))) {
+ value = atof((char *)(v + 1));
+ return true;
+ }
return false;
}
diff --git a/src/locale_DE.h b/src/locale_DE.h
index 82345d198..c57701bfd 100644
--- a/src/locale_DE.h
+++ b/src/locale_DE.h
@@ -70,7 +70,7 @@ MAKE_PSTR_WORD(pin)
MAKE_PSTR_WORD(publish)
MAKE_PSTR_WORD(timeout)
MAKE_PSTR_WORD(board_profile)
-MAKE_PSTR_WORD(counter)
+MAKE_PSTR_WORD(setvalue)
// for commands
MAKE_PSTR_WORD(call)
@@ -121,7 +121,7 @@ MAKE_STR(productid_fmt, "%s EMS ProductID")
MAKE_PSTR_LIST(enum_syslog_level, F_(off), F("emerg"), F("alert"), F("crit"), F_(error), F("warn"), F("notice"), F_(info), F_(debug), F("trace"), F("all"))
MAKE_PSTR_LIST(enum_watch, F_(off), F_(on), F_(raw), F_(unknown))
-MAKE_PSTR_LIST(enum_sensortype, F("none"), F("digital in"), F("counter"), F("adc"))
+MAKE_PSTR_LIST(enum_sensortype, F("none"), F("digital in"), F("counter"), F("adc"), F("timer"), F("rate"), F("digital out"), F("pwm 0"), F("pwm 1"), F("pwm 2"))
// strings
MAKE_PSTR(EMSESP, "EMS-ESP")
@@ -196,6 +196,8 @@ MAKE_PSTR(dbm, "dBm")
MAKE_PSTR(fahrenheit, "°F")
MAKE_PSTR(mv, "mV")
MAKE_PSTR(sqm, "sqm")
+MAKE_PSTR(m3, "m3")
+MAKE_PSTR(l, "l")
// MAKE_PSTR(times, "mal")
// MAKE_PSTR(oclock, "Uhr")
@@ -585,6 +587,9 @@ MAKE_PSTR_LIST(mode, F("mode"), F("modus"))
MAKE_PSTR_LIST(modetype, F("modetype"), F("modus Typ"))
MAKE_PSTR_LIST(fastheatup, F("fastheatup"), F("fast heatup"))
MAKE_PSTR_LIST(daytemp, F("daytemp"), F("Tagestemperatur"))
+MAKE_PSTR_LIST(daylowtemp, F("daytemp2"), F("Tagestemperatur T2"))
+MAKE_PSTR_LIST(daymidtemp, F("daytemp3"), F("Tagestemperatur T3"))
+MAKE_PSTR_LIST(dayhightemp, F("daytemp4"), F("Tagestemperatur T4"))
MAKE_PSTR_LIST(heattemp, F("heattemp"), F("Heizen Temperatur"))
MAKE_PSTR_LIST(nighttemp, F("nighttemp"), F("Nachttemperatur"))
MAKE_PSTR_LIST(ecotemp, F("ecotemp"), F("eco Temperatur"))
@@ -597,7 +602,8 @@ MAKE_PSTR_LIST(offsettemp, F("offsettemp"), F("Temperaturanhebung"))
MAKE_PSTR_LIST(minflowtemp, F("minflowtemp"), F("min Flusstemperatur"))
MAKE_PSTR_LIST(maxflowtemp, F("maxflowtemp"), F("max Flusstemperatur"))
MAKE_PSTR_LIST(roominfluence, F("roominfluence"), F("Raumeinfluss"))
-MAKE_PSTR_LIST(curroominfl, F("curroominfl"), F("current room influence"))
+MAKE_PSTR_LIST(roominfl_factor, F("roominflfactor"), F("Raumeinfluss Factor"))
+MAKE_PSTR_LIST(curroominfl, F("curroominfl"), F("aktueller Raumeinfluss"))
MAKE_PSTR_LIST(nofrosttemp, F("nofrosttemp"), F("Frostschutztemperatur"))
MAKE_PSTR_LIST(targetflowtemp, F("targetflowtemp"), F("berechnete Flusstemperatur"))
MAKE_PSTR_LIST(heatingtype, F("heatingtype"), F("Heizungstyp"))
diff --git a/src/locale_EN.h b/src/locale_EN.h
index e1356b759..e6908a12c 100644
--- a/src/locale_EN.h
+++ b/src/locale_EN.h
@@ -70,7 +70,7 @@ MAKE_PSTR_WORD(pin)
MAKE_PSTR_WORD(publish)
MAKE_PSTR_WORD(timeout)
MAKE_PSTR_WORD(board_profile)
-MAKE_PSTR_WORD(counter)
+MAKE_PSTR_WORD(setvalue)
// for commands
MAKE_PSTR_WORD(call)
@@ -121,7 +121,7 @@ MAKE_STR(productid_fmt, "%s EMS ProductID")
MAKE_PSTR_LIST(enum_syslog_level, F_(off), F("emerg"), F("alert"), F("crit"), F_(error), F("warn"), F("notice"), F_(info), F_(debug), F("trace"), F("all"))
MAKE_PSTR_LIST(enum_watch, F_(off), F_(on), F_(raw), F_(unknown))
-MAKE_PSTR_LIST(enum_sensortype, F("none"), F("digital in"), F("counter"), F("adc"))
+MAKE_PSTR_LIST(enum_sensortype, F("none"), F("digital in"), F("counter"), F("adc"), F("timer"), F("rate"), F("digital out"), F("pwm 0"), F("pwm 1"), F("pwm 2"))
// strings
MAKE_PSTR(EMSESP, "EMS-ESP")
@@ -196,6 +196,8 @@ MAKE_PSTR(dbm, "dBm")
MAKE_PSTR(fahrenheit, "°F")
MAKE_PSTR(mv, "mV")
MAKE_PSTR(sqm, "sqm")
+MAKE_PSTR(m3, "m3")
+MAKE_PSTR(l, "l")
// MAKE_PSTR(times, "times")
// MAKE_PSTR(oclock, "o'clock")
@@ -545,6 +547,7 @@ MAKE_PSTR_LIST(wwMaxTemp, F("wwmaxtemp"), F("maximum temperature"))
MAKE_PSTR_LIST(wwOneTimeKey, F("wwonetimekey"), F("one time key function"))
// mqtt values / commands
+MAKE_PSTR_LIST(switchtime, F("switchtime"), F("program switchtime"))
MAKE_PSTR_LIST(switchtime1, F("switchtime1"), F("own1 program switchtime"))
MAKE_PSTR_LIST(switchtime2, F("switchtime2"), F("own2 program switchtime"))
MAKE_PSTR_LIST(wwswitchtime, F("wwswitchtime"), F("program switchtime"))
@@ -585,6 +588,9 @@ MAKE_PSTR_LIST(mode, F("mode"), F("mode"))
MAKE_PSTR_LIST(modetype, F("modetype"), F("mode type"))
MAKE_PSTR_LIST(fastheatup, F("fastheatup"), F("fast heatup"))
MAKE_PSTR_LIST(daytemp, F("daytemp"), F("day temperature"))
+MAKE_PSTR_LIST(daylowtemp, F("daytemp2"), F("day temperature T2"))
+MAKE_PSTR_LIST(daymidtemp, F("daytemp3"), F("day temperature T3"))
+MAKE_PSTR_LIST(dayhightemp, F("daytemp4"), F("day temperature T4"))
MAKE_PSTR_LIST(heattemp, F("heattemp"), F("heat temperature"))
MAKE_PSTR_LIST(nighttemp, F("nighttemp"), F("night temperature"))
MAKE_PSTR_LIST(ecotemp, F("ecotemp"), F("eco temperature"))
@@ -597,6 +603,7 @@ MAKE_PSTR_LIST(offsettemp, F("offsettemp"), F("offset temperature"))
MAKE_PSTR_LIST(minflowtemp, F("minflowtemp"), F("min flow temperature"))
MAKE_PSTR_LIST(maxflowtemp, F("maxflowtemp"), F("max flow temperature"))
MAKE_PSTR_LIST(roominfluence, F("roominfluence"), F("room influence"))
+MAKE_PSTR_LIST(roominfl_factor, F("roominflfactor"), F("room influence factor"))
MAKE_PSTR_LIST(curroominfl, F("curroominfl"), F("current room influence"))
MAKE_PSTR_LIST(nofrosttemp, F("nofrosttemp"), F("nofrost temperature"))
MAKE_PSTR_LIST(targetflowtemp, F("targetflowtemp"), F("target flow temperature"))
diff --git a/src/mqtt.cpp b/src/mqtt.cpp
index 5158a3643..ddd6d1b69 100644
--- a/src/mqtt.cpp
+++ b/src/mqtt.cpp
@@ -41,6 +41,7 @@ uint8_t Mqtt::nested_format_;
std::string Mqtt::discovery_prefix_;
bool Mqtt::send_response_;
bool Mqtt::publish_single_;
+bool Mqtt::publish_single2cmd_;
std::deque Mqtt::mqtt_messages_;
std::vector Mqtt::mqtt_subfunctions_;
@@ -264,6 +265,14 @@ void Mqtt::on_message(const char * topic, const char * payload, size_t len) {
LOG_DEBUG(F("Received topic `%s`"), topic);
}
#endif
+ // remove HA topics if we don't use discovery
+ if (strncmp(topic, discovery_prefix().c_str(), discovery_prefix().size()) == 0) {
+ if (!ha_enabled_ && len) { // don't ping pong the empty message
+ queue_publish_message(topic, "", true);
+ LOG_DEBUG(F("Remove topic %s"), topic);
+ }
+ return;
+ }
// check first againts any of our subscribed topics
for (const auto & mf : mqtt_subfunctions_) {
@@ -290,7 +299,7 @@ void Mqtt::on_message(const char * topic, const char * payload, size_t len) {
// convert payload into a json doc
// if the payload doesn't not contain the key 'value' or 'data', treat the whole payload as the 'value'
if (len != 0) {
- DeserializationError error = deserializeJson(input_doc, message);
+ DeserializationError error = deserializeJson(input_doc, (const char *)message);
if ((!input_doc.containsKey("value") && !input_doc.containsKey("data")) || error) {
input_doc.clear();
input_doc["value"] = (const char *)message; // always a string
@@ -387,15 +396,16 @@ void Mqtt::reset_mqtt() {
void Mqtt::load_settings() {
EMSESP::esp8266React.getMqttSettingsService()->read([&](MqttSettings & mqttSettings) {
- mqtt_base_ = mqttSettings.base.c_str(); // Convert String to std::string
- mqtt_qos_ = mqttSettings.mqtt_qos;
- mqtt_retain_ = mqttSettings.mqtt_retain;
- mqtt_enabled_ = mqttSettings.enabled;
- ha_enabled_ = mqttSettings.ha_enabled;
- nested_format_ = mqttSettings.nested_format;
- publish_single_ = mqttSettings.publish_single;
- send_response_ = mqttSettings.send_response;
- discovery_prefix_ = mqttSettings.discovery_prefix.c_str();
+ mqtt_base_ = mqttSettings.base.c_str(); // Convert String to std::string
+ mqtt_qos_ = mqttSettings.mqtt_qos;
+ mqtt_retain_ = mqttSettings.mqtt_retain;
+ mqtt_enabled_ = mqttSettings.enabled;
+ ha_enabled_ = mqttSettings.ha_enabled;
+ nested_format_ = mqttSettings.nested_format;
+ publish_single_ = mqttSettings.publish_single;
+ publish_single2cmd_ = mqttSettings.publish_single2cmd;
+ send_response_ = mqttSettings.send_response;
+ discovery_prefix_ = mqttSettings.discovery_prefix.c_str();
// convert to milliseconds
publish_time_boiler_ = mqttSettings.publish_time_boiler * 1000;
@@ -444,14 +454,6 @@ void Mqtt::start() {
if (reason == AsyncMqttClientDisconnectReason::MQTT_NOT_AUTHORIZED) {
LOG_INFO(F("MQTT disconnected: Not authorized"));
}
- // remove message with pending ack
- if (!mqtt_messages_.empty()) {
- auto mqtt_message = mqtt_messages_.front();
- if (mqtt_message.packet_id_ != 0) {
- mqtt_messages_.pop_front();
- }
- }
- // mqtt_messages_.clear();
});
// create will_topic with the base prefixed. It has to be static because asyncmqttclient destroys the reference
@@ -570,9 +572,23 @@ void Mqtt::on_connect() {
#endif
publish(F_(info), doc.as()); // topic called "info"
- // create the EMS-ESP device in HA, which is MQTT retained
- if (ha_enabled()) {
- ha_status();
+ if (ha_enabled_) {
+ queue_unsubscribe_message(discovery_prefix_ + "/climate/" + mqtt_base_ + "/#");
+ queue_unsubscribe_message(discovery_prefix_ + "/sensor/" + mqtt_base_ + "/#");
+ queue_unsubscribe_message(discovery_prefix_ + "/binary_sensor/" + mqtt_base_ + "/#");
+ queue_unsubscribe_message(discovery_prefix_ + "/number/" + mqtt_base_ + "/#");
+ queue_unsubscribe_message(discovery_prefix_ + "/select/" + mqtt_base_ + "/#");
+ queue_unsubscribe_message(discovery_prefix_ + "/switch/" + mqtt_base_ + "/#");
+ EMSESP::reset_mqtt_ha(); // re-create all HA devices if there are any
+ ha_status(); // create the EMS-ESP device in HA, which is MQTT retained
+ } else {
+ queue_subscribe_message(discovery_prefix_ + "/climate/" + mqtt_base_ + "/#");
+ queue_subscribe_message(discovery_prefix_ + "/sensor/" + mqtt_base_ + "/#");
+ queue_subscribe_message(discovery_prefix_ + "/binary_sensor/" + mqtt_base_ + "/#");
+ queue_subscribe_message(discovery_prefix_ + "/number/" + mqtt_base_ + "/#");
+ queue_subscribe_message(discovery_prefix_ + "/select/" + mqtt_base_ + "/#");
+ queue_subscribe_message(discovery_prefix_ + "/switch/" + mqtt_base_ + "/#");
+ LOG_INFO(F("start removing topics %s/+/%s/#"), discovery_prefix_.c_str(), mqtt_base_.c_str());
}
// send initial MQTT messages for some of our services
@@ -582,8 +598,6 @@ void Mqtt::on_connect() {
// re-subscribe to all custom registered MQTT topics
resubscribe();
- EMSESP::reset_mqtt_ha(); // re-create all HA devices if there are any
-
publish_retain(F("status"), "online", true); // say we're alive to the Last Will topic, with retain on
mqtt_publish_fails_ = 0; // reset fail count to 0
@@ -669,6 +683,7 @@ std::shared_ptr Mqtt::queue_message(const uint8_t operation,
if (mqtt_messages_.size() >= MAX_MQTT_MESSAGES) {
mqtt_messages_.pop_front();
LOG_WARNING(F("Queue overflow, removing one message"));
+ mqtt_publish_fails_++;
}
mqtt_messages_.emplace_back(mqtt_message_id_++, std::move(message));
@@ -688,6 +703,11 @@ std::shared_ptr Mqtt::queue_subscribe_message(const std::stri
return queue_message(Operation::SUBSCRIBE, topic, "", false); // no payload
}
+// add MQTT unsubscribe message to queue
+std::shared_ptr Mqtt::queue_unsubscribe_message(const std::string & topic) {
+ return queue_message(Operation::UNSUBSCRIBE, topic, "", false); // no payload
+}
+
// MQTT Publish, using a user's retain flag
void Mqtt::publish(const std::string & topic, const std::string & payload) {
queue_publish_message(topic, payload, mqtt_retain_);
@@ -712,11 +732,6 @@ void Mqtt::publish(const std::string & topic, const JsonObject & payload) {
publish_retain(topic, payload, mqtt_retain_);
}
-// no payload
-void Mqtt::publish(const std::string & topic) {
- queue_publish_message(topic, "", false);
-}
-
// MQTT Publish, using a specific retain flag, topic is a flash string, forcing retain flag
void Mqtt::publish_retain(const __FlashStringHelper * topic, const std::string & payload, bool retain) {
queue_publish_message(read_flash_string(topic), payload, retain);
@@ -750,7 +765,7 @@ void Mqtt::publish_ha(const std::string & topic) {
LOG_DEBUG(F("[DEBUG] Publishing empty HA topic=%s"), fulltopic.c_str());
#endif
- publish(fulltopic);
+ queue_publish_message(fulltopic, "", true); // publish with retain to remove from broker
}
// publish a Home Assistant config topic and payload, with retain flag off.
@@ -792,12 +807,29 @@ void Mqtt::process_queue() {
snprintf(topic, MQTT_TOPIC_MAX_SIZE, "%s/%s", mqtt_base_.c_str(), message->topic.c_str());
}
+ // if this has already been published and we're waiting for an ACK, don't publish again
+ // it will have a real packet ID
+ if (mqtt_message.packet_id_ > 0) {
+#if defined(EMSESP_DEBUG)
+ LOG_DEBUG(F("[DEBUG] Waiting for QOS-ACK"));
+#endif
+ // if we don't get the ack within 10 minutes, republish with new packet_id
+ if (uuid::get_uptime_sec() - last_publish_queue_ < 600) {
+ return;
+ }
+ }
+ last_publish_queue_ = uuid::get_uptime_sec();
+
// if we're subscribing...
if (message->operation == Operation::SUBSCRIBE) {
LOG_DEBUG(F("Subscribing to topic '%s'"), topic);
uint16_t packet_id = mqttClient_->subscribe(topic, mqtt_qos_);
if (!packet_id) {
+ if (++mqtt_messages_.front().retry_count_ < MQTT_PUBLISH_MAX_RETRY) {
+ return;
+ }
LOG_ERROR(F("Error subscribing to topic '%s'"), topic);
+ mqtt_publish_fails_++; // increment failure counter
}
mqtt_messages_.pop_front(); // remove the message from the queue
@@ -805,12 +837,20 @@ void Mqtt::process_queue() {
return;
}
- // if this has already been published and we're waiting for an ACK, don't publish again
- // it will have a real packet ID
- if (mqtt_message.packet_id_ > 0) {
-#if defined(EMSESP_DEBUG)
- LOG_DEBUG(F("[DEBUG] Waiting for QOS-ACK"));
-#endif
+ // if we're unsubscribing...
+ if (message->operation == Operation::UNSUBSCRIBE) {
+ LOG_DEBUG(F("Subscribing to topic '%s'"), topic);
+ uint16_t packet_id = mqttClient_->unsubscribe(topic);
+ if (!packet_id) {
+ if (++mqtt_messages_.front().retry_count_ < MQTT_PUBLISH_MAX_RETRY) {
+ return;
+ }
+ LOG_ERROR(F("Error unsubscribing to topic '%s'"), topic);
+ mqtt_publish_fails_++; // increment failure counter
+ }
+
+ mqtt_messages_.pop_front(); // remove the message from the queue
+
return;
}
@@ -992,8 +1032,7 @@ void Mqtt::publish_ha_sensor_config(uint8_t type,
return;
}
- bool have_tag = !EMSdevice::tag_to_string(tag).empty();
- bool is_nested = (nested_format_ == 1); // nested_format is 1 if nested, otherwise 2 for single topics
+ bool have_tag = !EMSdevice::tag_to_string(tag).empty();
// build the payload
DynamicJsonDocument doc(EMSESP_JSON_SIZE_HA_CONFIG);
@@ -1069,7 +1108,7 @@ void Mqtt::publish_ha_sensor_config(uint8_t type,
// value template
// if its nested mqtt format then use the appended entity name, otherwise take the original
char val_tpl[75];
- if (is_nested) {
+ if (is_nested()) {
snprintf(val_tpl, sizeof(val_tpl), "{{value_json.%s}}", new_entity);
} else {
snprintf(val_tpl, sizeof(val_tpl), "{{value_json.%s}}", read_flash_string(entity).c_str());
@@ -1193,10 +1232,11 @@ 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_ == 1) && (device_type != EMSdevice::DeviceType::BOILER))) {
- return EMSdevice::device_type_2_device_name(device_type) + "_data";
- } else {
+ if (!EMSdevice::tag_to_mqtt(tag).empty()
+ && ((device_type == EMSdevice::DeviceType::BOILER && tag == DeviceValueTAG::TAG_DEVICE_DATA_WW) || (!is_nested() && tag >= DeviceValueTAG::TAG_HC1))) {
return EMSdevice::device_type_2_device_name(device_type) + "_data_" + EMSdevice::tag_to_mqtt(tag);
+ } else {
+ return EMSdevice::device_type_2_device_name(device_type) + "_data";
}
}
diff --git a/src/mqtt.h b/src/mqtt.h
index 95b461de4..8fed88ec0 100644
--- a/src/mqtt.h
+++ b/src/mqtt.h
@@ -29,9 +29,6 @@
using uuid::console::Shell;
-// time between HA publishes
-#define MQTT_HA_PUBLISH_DELAY 50
-
// size of queue
#define MAX_MQTT_MESSAGES 300
@@ -70,16 +67,10 @@ class Mqtt {
void set_publish_time_sensor(uint16_t publish_time);
bool get_publish_onchange(uint8_t device_type);
- enum Operation { PUBLISH, SUBSCRIBE };
+ enum Operation : uint8_t { PUBLISH, SUBSCRIBE, UNSUBSCRIBE };
+ enum NestedFormat : uint8_t { NESTED = 1, SINGLE };
- enum HA_Climate_Format : uint8_t {
- CURRENT = 1, // 1
- SETPOINT, // 2
- ZERO // 3
-
- };
-
- static constexpr uint8_t MQTT_TOPIC_MAX_SIZE = 128; // note this should really match the user setting in mqttSettings.maxTopicLength
+ static constexpr uint8_t MQTT_TOPIC_MAX_SIZE = FACTORY_MQTT_MAX_TOPIC_LENGTH; // fixed, not a user setting anymore
static void on_connect();
@@ -92,7 +83,6 @@ class Mqtt {
static void publish(const std::string & topic, const JsonObject & payload);
static void publish(const __FlashStringHelper * topic, const JsonObject & payload);
static void publish(const __FlashStringHelper * topic, const std::string & payload);
- static void publish(const std::string & topic);
static void publish_retain(const std::string & topic, const JsonObject & payload, bool retain);
static void publish_retain(const __FlashStringHelper * topic, const std::string & payload, bool retain);
static void publish_retain(const __FlashStringHelper * topic, const JsonObject & payload, bool retain);
@@ -125,10 +115,6 @@ class Mqtt {
static void ha_status();
- void disconnect() {
- mqttClient_->disconnect();
- }
-
#if defined(EMSESP_DEBUG)
void incoming(const char * topic, const char * payload = ""); // for testing only
#endif
@@ -179,13 +165,10 @@ class Mqtt {
static void reset_mqtt();
- // nested_format is 1 if nested, otherwise 2 for single topics
- static uint8_t nested_format() {
- return nested_format_;
- }
static bool is_nested() {
- return nested_format_ == 1;
+ return nested_format_ == NestedFormat::NESTED;
}
+
static void nested_format(uint8_t nested_format) {
nested_format_ = nested_format;
}
@@ -193,6 +176,11 @@ class Mqtt {
static bool publish_single() {
return publish_single_;
}
+
+ static bool publish_single2cmd() {
+ return publish_single2cmd_;
+ }
+
static void publish_single(bool publish_single) {
publish_single_ = publish_single;
}
@@ -200,6 +188,7 @@ class Mqtt {
static bool ha_enabled() {
return ha_enabled_;
}
+
static void ha_enabled(bool ha_enabled) {
ha_enabled_ = ha_enabled;
}
@@ -207,6 +196,7 @@ class Mqtt {
static bool send_response() {
return send_response_;
}
+
static void send_response(bool send_response) {
send_response_ = send_response;
}
@@ -232,7 +222,7 @@ class Mqtt {
uint16_t packet_id_;
~QueuedMqttMessage() = default;
- QueuedMqttMessage(uint16_t id, std::shared_ptr && content)
+ QueuedMqttMessage(uint32_t id, std::shared_ptr && content)
: id_(id)
, content_(std::move(content)) {
retry_count_ = 0;
@@ -254,6 +244,7 @@ class Mqtt {
static std::shared_ptr queue_message(const uint8_t operation, const std::string & topic, const std::string & payload, bool retain);
static std::shared_ptr queue_publish_message(const std::string & topic, const std::string & payload, bool retain);
static std::shared_ptr queue_subscribe_message(const std::string & topic);
+ static std::shared_ptr queue_unsubscribe_message(const std::string & topic);
void on_publish(uint16_t packetId);
void on_message(const char * topic, const char * payload, size_t len);
@@ -281,6 +272,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_queue_ = 0;
static bool connecting_;
static bool initialized_;
@@ -303,6 +295,7 @@ class Mqtt {
static uint8_t nested_format_;
static std::string discovery_prefix_;
static bool publish_single_;
+ static bool publish_single2cmd_;
static bool send_response_;
};
diff --git a/src/system.cpp b/src/system.cpp
index 6b56724a9..d05dffb29 100644
--- a/src/system.cpp
+++ b/src/system.cpp
@@ -73,18 +73,18 @@ bool System::command_pin(const char * value, const int8_t id) {
} else if (Helpers::value2bool(value, v)) {
pinMode(id, OUTPUT);
digitalWrite(id, v);
- LOG_INFO(F("GPIO %d set to %s"), id, v ? "HIGH" : "LOW");
+ // LOG_INFO(F("GPIO %d set to %s"), id, v ? "HIGH" : "LOW");
return true;
} else if (Helpers::value2string(value, v1)) {
if (v1 == "input" || v1 == "in" || v1 == "-1") {
pinMode(id, INPUT);
v = digitalRead(id);
- LOG_INFO(F("GPIO %d set input, state %s"), id, v ? "HIGH" : "LOW");
+ // LOG_INFO(F("GPIO %d set input, state %s"), id, v ? "HIGH" : "LOW");
return true;
}
}
- LOG_INFO(F("GPIO %d: invalid value"), id);
+ // LOG_INFO(F("GPIO %d: invalid value"), id);
#endif
return false;
@@ -162,13 +162,19 @@ bool System::command_publish(const char * value, const int8_t id) {
bool System::command_syslog_level(const char * value, const int8_t id) {
uint8_t s = 0xff;
if (Helpers::value2enum(value, s, FL_(enum_syslog_level))) {
+ bool changed = false;
EMSESP::webSettingsService.update(
[&](WebSettings & settings) {
- settings.syslog_level = (int8_t)s - 1;
+ if (settings.syslog_level != (int8_t)s - 1) {
+ settings.syslog_level = (int8_t)s - 1;
+ changed = true;
+ }
return StateUpdateResult::CHANGED;
},
"local");
- EMSESP::system_.syslog_init();
+ if (changed) {
+ EMSESP::system_.syslog_init();
+ }
return true;
}
return false;
@@ -176,29 +182,35 @@ bool System::command_syslog_level(const char * value, const int8_t id) {
// watch
bool System::command_watch(const char * value, const int8_t id) {
- uint8_t w = 0xff;
+ uint8_t w = 0xff;
+ uint16_t i = Helpers::hextoint(value);
if (Helpers::value2enum(value, w, FL_(enum_watch))) {
if (w == 0 || EMSESP::watch() == EMSESP::Watch::WATCH_OFF) {
EMSESP::watch_id(0);
}
- EMSESP::watch(w);
- if (Mqtt::publish_single()) {
- Mqtt::publish(F("system/watch"), read_flash_string(FL_(enum_watch)[w]).c_str());
+ if (Mqtt::publish_single() && w != EMSESP::watch()) {
+ if (Mqtt::publish_single2cmd()) {
+ Mqtt::publish(F("system/watch"),
+ EMSESP::system_.enum_format() == ENUM_FORMAT_INDEX ? Helpers::itoa(w) : read_flash_string(FL_(enum_watch)[w]).c_str());
+ } else {
+ Mqtt::publish(F("system_data/watch"),
+ EMSESP::system_.enum_format() == ENUM_FORMAT_INDEX ? Helpers::itoa(w) : read_flash_string(FL_(enum_watch)[w]).c_str());
+ }
}
+ EMSESP::watch(w);
return true;
- }
- uint16_t i = Helpers::hextoint(value);
- if (i) {
+ } else if (i) {
+ if (Mqtt::publish_single() && i != EMSESP::watch_id()) {
+ if (Mqtt::publish_single2cmd()) {
+ Mqtt::publish(F("system/watch"), Helpers::hextoa(i));
+ } else {
+ Mqtt::publish(F("system_data/watch"), Helpers::hextoa(i));
+ }
+ }
EMSESP::watch_id(i);
if (EMSESP::watch() == EMSESP::Watch::WATCH_OFF) {
EMSESP::watch(EMSESP::Watch::WATCH_ON);
}
- if (Mqtt::publish_single()) {
- char s[10];
- snprintf(s, sizeof(s), "0x%04X", i);
- Mqtt::publish(F("system/watch"), s);
- // Mqtt::publish(F("system/watch"), read_flash_string(FL_(enum_watch)[EMSESP::watch()]).c_str());
- }
return true;
}
return false;
@@ -273,13 +285,25 @@ void System::syslog_init() {
}
if (Mqtt::publish_single()) {
- Mqtt::publish(F("system/syslog"), syslog_enabled_ ? read_flash_string(FL_(enum_syslog_level)[syslog_level_ + 1]).c_str() : "off");
- if (EMSESP::watch_id() == 0 || EMSESP::watch() == 0) {
- Mqtt::publish(F("system/watch"), read_flash_string(FL_(enum_watch)[EMSESP::watch()]).c_str());
+ if (Mqtt::publish_single2cmd()) {
+ Mqtt::publish(F("system/syslog"), syslog_enabled_ ? read_flash_string(FL_(enum_syslog_level)[syslog_level_ + 1]).c_str() : "off");
+ if (EMSESP::watch_id() == 0 || EMSESP::watch() == 0) {
+ Mqtt::publish(F("system/watch"),
+ EMSESP::system_.enum_format() == ENUM_FORMAT_INDEX ? Helpers::itoa(EMSESP::watch())
+ : read_flash_string(FL_(enum_watch)[EMSESP::watch()]).c_str());
+ } else {
+ Mqtt::publish(F("system/watch"), Helpers::hextoa(EMSESP::watch_id()));
+ }
+
} else {
- char s[10];
- snprintf(s, sizeof(s), "0x%04X", EMSESP::watch_id());
- Mqtt::publish(F("system/watch"), s);
+ Mqtt::publish(F("system_data/syslog"), syslog_enabled_ ? read_flash_string(FL_(enum_syslog_level)[syslog_level_ + 1]).c_str() : "off");
+ if (EMSESP::watch_id() == 0 || EMSESP::watch() == 0) {
+ Mqtt::publish(F("system_data/watch"),
+ EMSESP::system_.enum_format() == ENUM_FORMAT_INDEX ? Helpers::itoa(EMSESP::watch())
+ : read_flash_string(FL_(enum_watch)[EMSESP::watch()]).c_str());
+ } else {
+ Mqtt::publish(F("system_data/watch"), Helpers::hextoa(EMSESP::watch_id()));
+ }
}
}
#endif
@@ -524,7 +548,7 @@ bool System::heartbeat_json(JsonObject & output) {
output["txfails"] = EMSESP::txservice_.telegram_read_fail_count() + EMSESP::txservice_.telegram_write_fail_count();
if (Mqtt::enabled()) {
- output["mqttfails"] = Mqtt::publish_fails();
+ output["mqttcount"] = Mqtt::publish_count();
output["mqttfails"] = Mqtt::publish_fails();
}
output["apicalls"] = WebAPIService::api_count(); // + WebAPIService::api_fails();
@@ -636,16 +660,12 @@ void System::system_check() {
// commands - takes static function pointers
void System::commands_init() {
- Command::add(EMSdevice::DeviceType::SYSTEM,
- F_(pin),
- System::command_pin,
- F("set a GPIO on/off"),
- CommandFlag::MQTT_SUB_FLAG_NOSUB | CommandFlag::ADMIN_ONLY); // dont create a MQTT topic for this
-
+ // Command::add(EMSdevice::DeviceType::SYSTEM, F_(pin), System::command_pin, F("set a GPIO on/off"), CommandFlag::ADMIN_ONLY);
Command::add(EMSdevice::DeviceType::SYSTEM, F_(send), System::command_send, F("send a telegram"), CommandFlag::ADMIN_ONLY);
Command::add(EMSdevice::DeviceType::SYSTEM, F_(fetch), System::command_fetch, F("refresh all EMS values"), CommandFlag::ADMIN_ONLY);
Command::add(EMSdevice::DeviceType::SYSTEM, F_(restart), System::command_restart, F("restart EMS-ESP"), CommandFlag::ADMIN_ONLY);
Command::add(EMSdevice::DeviceType::SYSTEM, F_(watch), System::command_watch, F("watch incoming telegrams"));
+ // register syslog command in syslog init
// Command::add(EMSdevice::DeviceType::SYSTEM, F_(syslog), System::command_syslog_level, F("set syslog level"), CommandFlag::ADMIN_ONLY);
if (Mqtt::enabled()) {
@@ -822,7 +842,7 @@ void System::show_system(uuid::console::Shell & shell) {
// show Ethernet if connected
if (ethernet_connected_) {
shell.println();
- shell.printfln(F(" Wired Network: connected"));
+ shell.printfln(F(" Ethernet Network: connected"));
shell.printfln(F(" MAC address: %s"), ETH.macAddress().c_str());
shell.printfln(F(" Hostname: %s"), ETH.getHostname());
shell.printfln(F(" IPv4 address: %s/%s"), uuid::printable_to_string(ETH.localIP()).c_str(), uuid::printable_to_string(ETH.subnetMask()).c_str());
@@ -1021,6 +1041,13 @@ bool System::command_customizations(const char * value, const int8_t id, JsonObj
sensorJson["offset"] = sensor.offset;
sensorJson["factor"] = sensor.factor;
sensorJson["uom"] = EMSdevice::uom_to_string(sensor.uom);
+ } else if (sensor.type == AnalogSensor::AnalogType::COUNTER || sensor.type == AnalogSensor::AnalogType::TIMER
+ || sensor.type == AnalogSensor::AnalogType::RATE) {
+ sensorJson["factor"] = sensor.factor;
+ sensorJson["uom"] = EMSdevice::uom_to_string(sensor.uom);
+ } else if (sensor.type >= AnalogSensor::AnalogType::PWM_0) {
+ sensorJson["frequency"] = sensor.factor;
+ sensorJson["factor"] = sensor.factor;
}
}
@@ -1154,10 +1181,14 @@ bool System::command_info(const char * value, const int8_t id, JsonObject & outp
for (const auto & device_class : EMSFactory::device_handlers()) {
for (const auto & emsdevice : EMSESP::emsdevices) {
if ((emsdevice) && (emsdevice->device_type() == device_class.first)) {
- JsonObject obj = devices.createNestedObject();
- obj["type"] = emsdevice->device_type_name();
- obj["name"] = emsdevice->to_string();
- obj["entities"] = emsdevice->count_entities();
+ JsonObject obj = devices.createNestedObject();
+ obj["type"] = emsdevice->device_type_name();
+ // obj["name"] = emsdevice->to_string();
+ obj["name"] = emsdevice->name();
+ obj["device id"] = Helpers::hextoa(emsdevice->device_id());
+ obj["product id"] = emsdevice->product_id();
+ obj["version"] = emsdevice->version();
+ obj["entities"] = emsdevice->count_entities();
char result[200];
(void)emsdevice->show_telegram_handlers(result, EMSdevice::Handlers::RECEIVED);
if (result[0] != '\0') {
diff --git a/src/telegram.cpp b/src/telegram.cpp
index 0fa1b531e..59c0921b8 100644
--- a/src/telegram.cpp
+++ b/src/telegram.cpp
@@ -536,13 +536,13 @@ void TxService::add(uint8_t operation, const uint8_t * data, const uint8_t lengt
void TxService::read_request(const uint16_t type_id, const uint8_t dest, const uint8_t offset, const uint8_t length) {
LOG_DEBUG(F("Tx read request to device 0x%02X for type ID 0x%02X"), dest, type_id);
- uint8_t message_data[1] = {EMS_MAX_TELEGRAM_LENGTH}; // request all data, 32 bytes
+ uint8_t message_data = (type_id > 0xFF) ? (EMS_MAX_TELEGRAM_MESSAGE_LENGTH - 2) : EMS_MAX_TELEGRAM_MESSAGE_LENGTH;
// if length set, publish result and set telegram to front
if (length) {
- message_data[0] = length;
+ message_data = length;
EMSESP::set_read_id(type_id);
}
- add(Telegram::Operation::TX_READ, dest, type_id, offset, message_data, 1, 0, length != 0);
+ add(Telegram::Operation::TX_READ, dest, type_id, offset, &message_data, 1, 0, length != 0);
}
// Send a raw telegram to the bus, telegram is a text string of hex values
@@ -663,9 +663,10 @@ uint16_t TxService::post_send_query() {
if (post_typeid) {
uint8_t dest = (this->telegram_last_->dest & 0x7F);
// when set a value with large offset before and validate on same type, we have to add offset 0, 26, 52, ...
- uint8_t offset = (this->telegram_last_->type_id == post_typeid) ? ((this->telegram_last_->offset / 26) * 26) : 0;
- uint8_t message_data[1] = {EMS_MAX_TELEGRAM_LENGTH}; // request all data, 32 bytes
- this->add(Telegram::Operation::TX_READ, dest, post_typeid, offset, message_data, 1, 0, true); // add to top/front of queue
+ uint8_t offset = (this->telegram_last_->type_id == post_typeid) ? ((this->telegram_last_->offset / 26) * 26) : 0;
+ uint8_t message_data =
+ (this->telegram_last_->type_id > 0xFF) ? (EMS_MAX_TELEGRAM_MESSAGE_LENGTH - 2) : EMS_MAX_TELEGRAM_MESSAGE_LENGTH; // request all data, 32 bytes
+ this->add(Telegram::Operation::TX_READ, dest, post_typeid, offset, &message_data, 1, 0, true); // add to top/front of queue
// read_request(telegram_last_post_send_query_, dest, 0); // no offset
LOG_DEBUG(F("Sending post validate read, type ID 0x%02X to dest 0x%02X"), post_typeid, dest);
set_post_send_query(0); // reset
diff --git a/src/telegram.h b/src/telegram.h
index 5ce46c5ad..e75f53008 100644
--- a/src/telegram.h
+++ b/src/telegram.h
@@ -399,6 +399,10 @@ class TxService : public EMSbus {
return tx_telegrams_;
}
+ bool tx_queue_empty() {
+ return tx_telegrams_.size() == 0;
+ }
+
#if defined(EMSESP_DEBUG)
static constexpr uint8_t MAXIMUM_TX_RETRIES = 0; // when compiled with EMSESP_DEBUG don't retry
#else
diff --git a/src/version.h b/src/version.h
index 79659168a..ead90ccd1 100644
--- a/src/version.h
+++ b/src/version.h
@@ -1 +1 @@
-#define EMSESP_APP_VERSION "3.4.0b5"
+#define EMSESP_APP_VERSION "3.4.0b6"
diff --git a/src/web/WebCustomizationService.h b/src/web/WebCustomizationService.h
index 5371e74bb..b2bd869da 100644
--- a/src/web/WebCustomizationService.h
+++ b/src/web/WebCustomizationService.h
@@ -44,7 +44,7 @@ class AnalogCustomization {
public:
uint8_t id;
std::string name;
- uint16_t offset;
+ float offset;
float factor;
uint8_t uom; // 0 is none
int8_t type; // -1 is for deletion
diff --git a/src/web/WebDataService.cpp b/src/web/WebDataService.cpp
index c2efaffef..85989b9c7 100644
--- a/src/web/WebDataService.cpp
+++ b/src/web/WebDataService.cpp
@@ -144,7 +144,7 @@ void WebDataService::sensor_data(AsyncWebServerRequest * request) {
obj["t"] = sensor.type();
if (sensor.type() != AnalogSensor::AnalogType::NOTUSED) {
- obj["v"] = Helpers::round2(sensor.value(), 1); // is optional and is a float
+ obj["v"] = Helpers::round2(sensor.value(), 0); // is optional and is a float
}
}
}
@@ -275,7 +275,7 @@ void WebDataService::write_analog(AsyncWebServerRequest * request, JsonVariant &
uint8_t id = analog["id"]; // this is the unique key
std::string name = analog["name"];
float factor = analog["factor"];
- int16_t offset = analog["offset"];
+ float offset = analog["offset"];
uint8_t uom = analog["uom"];
int8_t type = analog["type"];
ok = EMSESP::analogsensor_.update(id, name, offset, factor, uom, type);