diff --git a/.gitignore b/.gitignore index e69de29..89cc49c 100644 --- a/.gitignore +++ b/.gitignore @@ -0,0 +1,5 @@ +.pio +.vscode/.browse.c_cpp.db* +.vscode/c_cpp_properties.json +.vscode/launch.json +.vscode/ipch diff --git a/assets/BOM.xlsx b/assets/BOM.xlsx new file mode 100644 index 0000000..8dc2136 Binary files /dev/null and b/assets/BOM.xlsx differ diff --git a/assets/equitherm calc.xlsx b/assets/equitherm calc.xlsx new file mode 100644 index 0000000..832aeb8 Binary files /dev/null and b/assets/equitherm calc.xlsx differ diff --git a/build/otgateway.ino.bin b/build/otgateway.ino.bin deleted file mode 100644 index b3f2431..0000000 Binary files a/build/otgateway.ino.bin and /dev/null differ diff --git a/lib/CustomOpenTherm/CustomOpenTherm.h b/lib/CustomOpenTherm/CustomOpenTherm.h new file mode 100644 index 0000000..5fc1e3b --- /dev/null +++ b/lib/CustomOpenTherm/CustomOpenTherm.h @@ -0,0 +1,106 @@ +#include +#include + +extern SchedulerClass Scheduler; + +class CustomOpenTherm : public OpenTherm { +private: + unsigned long send_ts = millis(); + void(*handleSendRequestCallback)(unsigned long, unsigned long, OpenThermResponseStatus status, byte attempt); + +public: + CustomOpenTherm(int inPin = 4, int outPin = 5, bool isSlave = false) : OpenTherm(inPin, outPin, isSlave) {} + void setHandleSendRequestCallback(void(*handleSendRequestCallback)(unsigned long, unsigned long, OpenThermResponseStatus status, byte attempt)) { + this->handleSendRequestCallback = handleSendRequestCallback; + } + + unsigned long sendRequest(unsigned long request, byte attempts = 5, byte _attempt = 0) { + _attempt++; + while (send_ts > 0 && millis() - send_ts < 200) { + Scheduler.yield(); + } + + unsigned long _response; + if (!sendRequestAync(request)) { + _response = 0; + } else { + while (!isReady()) { + Scheduler.yield(); + process(); + } + + _response = getLastResponse(); + } + + if (handleSendRequestCallback != NULL) { + handleSendRequestCallback(request, _response, getLastResponseStatus(), _attempt); + } + + send_ts = millis(); + if (getLastResponseStatus() == OpenThermResponseStatus::SUCCESS || _attempt >= attempts) { + return _response; + } else { + return sendRequest(request, attempts, _attempt); + } + } + + unsigned long setBoilerStatus(bool enableCentralHeating, bool enableHotWater, bool enableCooling, bool enableOutsideTemperatureCompensation, bool enableCentralHeating2, bool summerWinterMode, bool dhwBlocking) { + return sendRequest(buildSetBoilerStatusRequest(enableCentralHeating, enableHotWater, enableCooling, enableOutsideTemperatureCompensation, enableCentralHeating2, summerWinterMode, dhwBlocking)); + } + + unsigned long buildSetBoilerStatusRequest(bool enableCentralHeating, bool enableHotWater, bool enableCooling, bool enableOutsideTemperatureCompensation, bool enableCentralHeating2, bool summerWinterMode, bool dhwBlocking) { + unsigned int data = enableCentralHeating | (enableHotWater << 1) | (enableCooling << 2) | (enableOutsideTemperatureCompensation << 3) | (enableCentralHeating2 << 4) | (summerWinterMode << 5) | (dhwBlocking << 6); + data <<= 8; + return buildRequest(OpenThermMessageType::READ_DATA, OpenThermMessageID::Status, data); + } + + bool setBoilerTemperature(float temperature) { + unsigned int data = temperatureToData(temperature); + unsigned long response = sendRequest(buildRequest(OpenThermMessageType::WRITE_DATA, OpenThermMessageID::TSet, data)); + return isValidResponse(response); + } + + bool setBoilerTemperature2(float temperature) { + unsigned int data = temperatureToData(temperature); + unsigned long response = sendRequest(buildRequest(OpenThermMessageType::WRITE_DATA, OpenThermMessageID::TsetCH2, data)); + return isValidResponse(response); + } + + bool sendBoilerReset() { + unsigned int data = 1; + data <<= 8; + unsigned long response = sendRequest(buildRequest(OpenThermMessageType::WRITE_DATA, OpenThermMessageID::Command, data)); + return isValidResponse(response); + } + + bool sendServiceReset() { + unsigned int data = 10; + data <<= 8; + unsigned long response = sendRequest(buildRequest(OpenThermMessageType::WRITE_DATA, OpenThermMessageID::Command, data)); + return isValidResponse(response); + } + + bool sendWaterFilling() { + unsigned int data = 2; + data <<= 8; + unsigned long response = sendRequest(buildRequest(OpenThermMessageType::WRITE_DATA, OpenThermMessageID::Command, data)); + return isValidResponse(response); + } + + // converters + float f88(unsigned long response) { + const byte valueLB = response & 0xFF; + const byte valueHB = (response >> 8) & 0xFF; + + float value = (int8_t) valueHB; + return value + (float)valueLB / 256.0; + } + + int16_t s16(unsigned long response) { + const byte valueLB = response & 0xFF; + const byte valueHB = (response >> 8) & 0xFF; + + int16_t value = valueHB; + return ((value << 8) + valueLB); + } +}; diff --git a/src/lib/Equitherm.h b/lib/Equitherm/Equitherm.h similarity index 96% rename from src/lib/Equitherm.h rename to lib/Equitherm/Equitherm.h index 864a4f3..41cb046 100644 --- a/src/lib/Equitherm.h +++ b/lib/Equitherm/Equitherm.h @@ -1,4 +1,3 @@ -#pragma once #include #if defined(EQUITHERM_INTEGER) @@ -11,6 +10,13 @@ typedef float datatype; class Equitherm { public: + datatype targetTemp = 0; + datatype indoorTemp = 0; + datatype outdoorTemp = 0; + float Kn = 0.0; + float Kk = 0.0; + float Kt = 0.0; + Equitherm() {} // kn, kk, kt @@ -26,13 +32,6 @@ public: _maxOut = max_output; } - datatype targetTemp = 0; - datatype indoorTemp = 0; - datatype outdoorTemp = 0; - float Kn = 0.0; - float Kk = 0.0; - float Kt = 0.0; - // возвращает новое значение при вызове datatype getResult() { datatype output = getResultN() + getResultK() + getResultT(); @@ -40,6 +39,9 @@ public: return output; } +private: + int _minOut = 20, _maxOut = 90; + // температура контура отопления в зависимости от наружной температуры datatype getResultN() { float a = (-0.21 * Kn) - 0.06; // a = -0,21k — 0,06 @@ -56,9 +58,6 @@ public: // Расчет поправки (ошибки) термостата datatype getResultT() { - return (targetTemp - indoorTemp) * Kt; + return constrain((targetTemp - indoorTemp), -2, 2) * Kt; } - -private: - int _minOut = 20, _maxOut = 90; }; \ No newline at end of file diff --git a/otgateway.ino b/otgateway.ino deleted file mode 100644 index 6068fe7..0000000 --- a/otgateway.ino +++ /dev/null @@ -1,4 +0,0 @@ -/* - This file is needed by the Arduino IDE because the ino file needs to be named as the directory name. - Don't worry, the Arduino compiler will "merge" all files, including src/main.cpp -*/ diff --git a/platformio.ini b/platformio.ini new file mode 100644 index 0000000..7a46eef --- /dev/null +++ b/platformio.ini @@ -0,0 +1,28 @@ +; PlatformIO Project Configuration File +; +; Build options: build flags, source filter +; Upload options: custom upload port, speed and extra flags +; Library options: dependencies, extra library storages +; Advanced options: extra scripting +; +; Please visit documentation for the other options and examples +; https://docs.platformio.org/page/projectconf.html + +[env:d1_mini_pro] +platform = espressif8266 +board = d1_mini_pro +framework = arduino +lib_deps = + nrwiersma/ESP8266Scheduler@^1.0 + arduino-libraries/NTPClient@^3.2.1 + bblanchon/ArduinoJson@^6.20.0 + ihormelnyk/OpenTherm Library@^1.1.4 + knolleary/PubSubClient@^2.8 + jandrassy/TelnetStream@^1.2.4 + gyverlibs/EEManager@^2.0 + gyverlibs/GyverPID@^3.3 + gyverlibs/microDS18B20@^3.10 + https://github.com/tzapu/WiFiManager.git#v2.0.16-rc.2 +build_flags = -D PIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH +upload_speed = 921600 +monitor_speed = 115200 diff --git a/src/HomeAssistantHelper.h b/src/HomeAssistantHelper.h index c00a723..a3d6263 100644 --- a/src/HomeAssistantHelper.h +++ b/src/HomeAssistantHelper.h @@ -29,28 +29,28 @@ public: bool publishSelectOutdoorTempSource(bool enabledByDefault = true) { StaticJsonDocument<1536> doc; - doc["command_topic"] = _prefix + "/settings/set"; - doc["command_template"] = F("{\"outdoorTempSource\": {% if value == 'Boiler' %}0{% elif value == 'Manual' %}1{% elif value == 'External' %}2{% endif %}}"); - doc["device"]["identifiers"][0] = _prefix; - doc["device"]["sw_version"] = _deviceVersion; - doc["device"]["manufacturer"] = _deviceManufacturer; - doc["device"]["model"] = _deviceModel; - doc["device"]["name"] = _deviceName; + doc[F("command_topic")] = _prefix + F("/settings/set"); + doc[F("command_template")] = F("{\"outdoorTempSource\": {% if value == 'Boiler' %}0{% elif value == 'Manual' %}1{% elif value == 'External' %}2{% endif %}}"); + doc[F("device")][F("identifiers")][0] = _prefix; + doc[F("device")][F("sw_version")] = _deviceVersion; + doc[F("device")][F("manufacturer")] = _deviceManufacturer; + doc[F("device")][F("model")] = _deviceModel; + doc[F("device")][F("name")] = _deviceName; if (_deviceConfigUrl) { - doc["device"]["configuration_url"] = _deviceConfigUrl; + doc[F("device")][F("configuration_url")] = _deviceConfigUrl; } - doc["enabled_by_default"] = enabledByDefault; - doc["unique_id"] = _prefix + "_outdoorTempSource"; - doc["object_id"] = _prefix + "_outdoorTempSource"; - doc["entity_category"] = "config"; - doc["name"] = "Outdoor temperature source"; - doc["state_topic"] = _prefix + F("/settings"); - doc["value_template"] = F("{% if value_json.outdoorTempSource == 0 %}Boiler{% elif value_json.outdoorTempSource == 1 %}Manual{% elif value_json.outdoorTempSource == 2 %}External{% endif %}"); - doc["options"][0] = F("Boiler"); - doc["options"][1] = F("Manual"); - doc["options"][2] = F("External"); + doc[F("enabled_by_default")] = enabledByDefault; + doc[F("unique_id")] = _prefix + F("_outdoorTempSource"); + doc[F("object_id")] = _prefix + F("_outdoorTempSource"); + doc[F("entity_category")] = F("config"); + doc[F("name")] = F("Outdoor temperature source"); + doc[F("state_topic")] = _prefix + F("/settings"); + doc[F("value_template")] = F("{% if value_json.outdoorTempSource == 0 %}Boiler{% elif value_json.outdoorTempSource == 1 %}Manual{% elif value_json.outdoorTempSource == 2 %}External{% endif %}"); + doc[F("options")][0] = F("Boiler"); + doc[F("options")][1] = F("Manual"); + doc[F("options")][2] = F("External"); - client.beginPublish((F("homeassistant/select/") + _prefix + "/outdoorTempSource/config").c_str(), measureJson(doc), true); + client.beginPublish((F("homeassistant/select/") + _prefix + F("/outdoorTempSource/config")).c_str(), measureJson(doc), true); //BufferingPrint bufferedClient(client, 32); //serializeJson(doc, bufferedClient); //bufferedClient.flush(); @@ -60,29 +60,29 @@ public: bool publishSwitchDebug(bool enabledByDefault = true) { StaticJsonDocument<1536> doc; - doc["device"]["identifiers"][0] = _prefix; - doc["device"]["sw_version"] = _deviceVersion; - doc["device"]["manufacturer"] = _deviceManufacturer; - doc["device"]["model"] = _deviceModel; - doc["device"]["name"] = _deviceName; + doc[F("device")][F("identifiers")][0] = _prefix; + doc[F("device")][F("sw_version")] = _deviceVersion; + doc[F("device")][F("manufacturer")] = _deviceManufacturer; + doc[F("device")][F("model")] = _deviceModel; + doc[F("device")][F("name")] = _deviceName; if (_deviceConfigUrl) { - doc["device"]["configuration_url"] = _deviceConfigUrl; + doc[F("device")][F("configuration_url")] = _deviceConfigUrl; } - doc["enabled_by_default"] = enabledByDefault; - doc["unique_id"] = _prefix + "_debug"; - doc["object_id"] = _prefix + "_debug"; - doc["entity_category"] = "config"; - doc["name"] = "Debug"; - doc["icon"] = "mdi:code-braces"; - doc["state_topic"] = _prefix + F("/settings"); - doc["state_on"] = true; - doc["state_off"] = false; - doc["value_template"] = "{{ value_json.debug }}"; - doc["command_topic"] = _prefix + "/settings/set"; - doc["payload_on"] = "{\"debug\": true}"; - doc["payload_off"] = "{\"debug\": false}"; + doc[F("enabled_by_default")] = enabledByDefault; + doc[F("unique_id")] = _prefix + F("_debug"); + doc[F("object_id")] = _prefix + F("_debug"); + doc[F("entity_category")] = F("config"); + doc[F("name")] = F("Debug"); + doc[F("icon")] = F("mdi:code-braces"); + doc[F("state_topic")] = _prefix + F("/settings"); + doc[F("state_on")] = true; + doc[F("state_off")] = false; + doc[F("value_template")] = F("{{ value_json.debug }}"); + doc[F("command_topic")] = _prefix + F("/settings/set"); + doc[F("payload_on")] = F("{\"debug\": true}"); + doc[F("payload_off")] = F("{\"debug\": false}"); - client.beginPublish((F("homeassistant/switch/") + _prefix + "/debug/config").c_str(), measureJson(doc), true); + client.beginPublish((F("homeassistant/switch/") + _prefix + F("/debug/config")).c_str(), measureJson(doc), true); //BufferingPrint bufferedClient(client, 32); //serializeJson(doc, bufferedClient); //bufferedClient.flush(); @@ -93,29 +93,29 @@ public: bool publishSwitchEmergency(bool enabledByDefault = true) { StaticJsonDocument<1536> doc; - doc["device"]["identifiers"][0] = _prefix; - doc["device"]["sw_version"] = _deviceVersion; - doc["device"]["manufacturer"] = _deviceManufacturer; - doc["device"]["model"] = _deviceModel; - doc["device"]["name"] = _deviceName; + doc[F("device")][F("identifiers")][0] = _prefix; + doc[F("device")][F("sw_version")] = _deviceVersion; + doc[F("device")][F("manufacturer")] = _deviceManufacturer; + doc[F("device")][F("model")] = _deviceModel; + doc[F("device")][F("name")] = _deviceName; if (_deviceConfigUrl) { - doc["device"]["configuration_url"] = _deviceConfigUrl; + doc[F("device")][F("configuration_url")] = _deviceConfigUrl; } - doc["enabled_by_default"] = enabledByDefault; - doc["unique_id"] = _prefix + "_emergency"; - doc["object_id"] = _prefix + "_emergency"; - doc["entity_category"] = "config"; - doc["name"] = "Use emergency"; - doc["icon"] = "mdi:sun-snowflake-variant"; - doc["state_topic"] = _prefix + F("/settings"); - doc["state_on"] = true; - doc["state_off"] = false; - doc["value_template"] = "{{ value_json.emergency.enable }}"; - doc["command_topic"] = _prefix + "/settings/set"; - doc["payload_on"] = "{\"emergency\": {\"enable\" : true}}"; - doc["payload_off"] = "{\"emergency\": {\"enable\" : false}}"; + doc[F("enabled_by_default")] = enabledByDefault; + doc[F("unique_id")] = _prefix + F("_emergency"); + doc[F("object_id")] = _prefix + F("_emergency"); + doc[F("entity_category")] = F("config"); + doc[F("name")] = F("Use emergency"); + doc[F("icon")] = F("mdi:sun-snowflake-variant"); + doc[F("state_topic")] = _prefix + F("/settings"); + doc[F("state_on")] = true; + doc[F("state_off")] = false; + doc[F("value_template")] = F("{{ value_json.emergency.enable }}"); + doc[F("command_topic")] = _prefix + F("/settings/set"); + doc[F("payload_on")] = F("{\"emergency\": {\"enable\" : true}}"); + doc[F("payload_off")] = F("{\"emergency\": {\"enable\" : false}}"); - client.beginPublish((F("homeassistant/switch/") + _prefix + "/emergency/config").c_str(), measureJson(doc), true); + client.beginPublish((F("homeassistant/switch/") + _prefix + F("/emergency/config")).c_str(), measureJson(doc), true); //BufferingPrint bufferedClient(client, 32); //serializeJson(doc, bufferedClient); //bufferedClient.flush(); @@ -125,31 +125,31 @@ public: bool publishNumberEmergencyTarget(bool enabledByDefault = true) { StaticJsonDocument<1536> doc; - doc["device"]["identifiers"][0] = _prefix; - doc["device"]["sw_version"] = _deviceVersion; - doc["device"]["manufacturer"] = _deviceManufacturer; - doc["device"]["model"] = _deviceModel; - doc["device"]["name"] = _deviceName; + doc[F("device")][F("identifiers")][0] = _prefix; + doc[F("device")][F("sw_version")] = _deviceVersion; + doc[F("device")][F("manufacturer")] = _deviceManufacturer; + doc[F("device")][F("model")] = _deviceModel; + doc[F("device")][F("name")] = _deviceName; if (_deviceConfigUrl) { - doc["device"]["configuration_url"] = _deviceConfigUrl; + doc[F("device")][F("configuration_url")] = _deviceConfigUrl; } - doc["enabled_by_default"] = enabledByDefault; - doc["unique_id"] = _prefix + "_emergency_target"; - doc["object_id"] = _prefix + "_emergency_target"; - doc["entity_category"] = "config"; - doc["device_class"] = "temperature"; - doc["unit_of_measurement"] = "°C"; - doc["name"] = "Emergency target temp"; - doc["icon"] = "mdi:thermometer-alert"; - doc["state_topic"] = _prefix + F("/settings"); - doc["value_template"] = "{{ value_json.emergency.target|float(0)|round(1) }}"; - doc["command_topic"] = _prefix + "/settings/set"; - doc["command_template"] = "{\"emergency\": {\"target\" : {{ value }}}}"; - doc["min"] = 5; - doc["max"] = 50; - doc["step"] = 0.5; + doc[F("enabled_by_default")] = enabledByDefault; + doc[F("unique_id")] = _prefix + F("_emergency_target"); + doc[F("object_id")] = _prefix + F("_emergency_target"); + doc[F("entity_category")] = F("config"); + doc[F("device_class")] = F("temperature"); + doc[F("unit_of_measurement")] = F("°C"); + doc[F("name")] = F("Emergency target temp"); + doc[F("icon")] = F("mdi:thermometer-alert"); + doc[F("state_topic")] = _prefix + F("/settings"); + doc[F("value_template")] = F("{{ value_json.emergency.target|float(0)|round(1) }}"); + doc[F("command_topic")] = _prefix + F("/settings/set"); + doc[F("command_template")] = F("{\"emergency\": {\"target\" : {{ value }}}}"); + doc[F("min")] = 5; + doc[F("max")] = 50; + doc[F("step")] = 0.5; - client.beginPublish((F("homeassistant/number/") + _prefix + "/emergency_target/config").c_str(), measureJson(doc), true); + client.beginPublish((F("homeassistant/number/") + _prefix + F("/emergency_target/config")).c_str(), measureJson(doc), true); //BufferingPrint bufferedClient(client, 32); //serializeJson(doc, bufferedClient); //bufferedClient.flush(); @@ -159,31 +159,31 @@ public: bool publishSwitchEmergencyUseEquitherm(bool enabledByDefault = true) { StaticJsonDocument<1536> doc; - doc["availability"]["topic"] = _prefix + F("/settings"); - doc["availability"]["value_template"] = F("{{ iif(value_json.outdoorTempSource != 1, 'online', 'offline') }}"); - doc["device"]["identifiers"][0] = _prefix; - doc["device"]["sw_version"] = _deviceVersion; - doc["device"]["manufacturer"] = _deviceManufacturer; - doc["device"]["model"] = _deviceModel; - doc["device"]["name"] = _deviceName; + doc[F("availability")][F("topic")] = _prefix + F("/settings"); + doc[F("availability")][F("value_template")] = F("{{ iif(value_json.outdoorTempSource != 1, 'online', 'offline') }}"); + doc[F("device")][F("identifiers")][0] = _prefix; + doc[F("device")][F("sw_version")] = _deviceVersion; + doc[F("device")][F("manufacturer")] = _deviceManufacturer; + doc[F("device")][F("model")] = _deviceModel; + doc[F("device")][F("name")] = _deviceName; if (_deviceConfigUrl) { - doc["device"]["configuration_url"] = _deviceConfigUrl; + doc[F("device")][F("configuration_url")] = _deviceConfigUrl; } - doc["enabled_by_default"] = enabledByDefault; - doc["unique_id"] = _prefix + "_emergency_use_equitherm"; - doc["object_id"] = _prefix + "_emergency_use_equitherm"; - doc["entity_category"] = "config"; - doc["name"] = "Use equitherm in emergency"; - doc["icon"] = "mdi:snowflake-alert"; - doc["state_topic"] = _prefix + F("/settings"); - doc["state_on"] = true; - doc["state_off"] = false; - doc["value_template"] = "{{ value_json.emergency.useEquitherm }}"; - doc["command_topic"] = _prefix + "/settings/set"; - doc["payload_on"] = "{\"emergency\": {\"useEquitherm\" : true}}"; - doc["payload_off"] = "{\"emergency\": {\"useEquitherm\" : false}}"; + doc[F("enabled_by_default")] = enabledByDefault; + doc[F("unique_id")] = _prefix + F("_emergency_use_equitherm"); + doc[F("object_id")] = _prefix + F("_emergency_use_equitherm"); + doc[F("entity_category")] = F("config"); + doc[F("name")] = F("Use equitherm in emergency"); + doc[F("icon")] = F("mdi:snowflake-alert"); + doc[F("state_topic")] = _prefix + F("/settings"); + doc[F("state_on")] = true; + doc[F("state_off")] = false; + doc[F("value_template")] = F("{{ value_json.emergency.useEquitherm }}"); + doc[F("command_topic")] = _prefix + F("/settings/set"); + doc[F("payload_on")] = F("{\"emergency\": {\"useEquitherm\" : true}}"); + doc[F("payload_off")] = F("{\"emergency\": {\"useEquitherm\" : false}}"); - client.beginPublish((F("homeassistant/switch/") + _prefix + "/emergency_use_equitherm/config").c_str(), measureJson(doc), true); + client.beginPublish((F("homeassistant/switch/") + _prefix + F("/emergency_use_equitherm/config")).c_str(), measureJson(doc), true); //BufferingPrint bufferedClient(client, 32); //serializeJson(doc, bufferedClient); //bufferedClient.flush(); @@ -194,30 +194,63 @@ public: bool publishSwitchHeating(bool enabledByDefault = true) { StaticJsonDocument<1536> doc; - doc["availability"]["topic"] = _prefix + F("/status"); - doc["device"]["identifiers"][0] = _prefix; - doc["device"]["sw_version"] = _deviceVersion; - doc["device"]["manufacturer"] = _deviceManufacturer; - doc["device"]["model"] = _deviceModel; - doc["device"]["name"] = _deviceName; + doc[F("availability")][F("topic")] = _prefix + F("/status"); + doc[F("device")][F("identifiers")][0] = _prefix; + doc[F("device")][F("sw_version")] = _deviceVersion; + doc[F("device")][F("manufacturer")] = _deviceManufacturer; + doc[F("device")][F("model")] = _deviceModel; + doc[F("device")][F("name")] = _deviceName; if (_deviceConfigUrl) { - doc["device"]["configuration_url"] = _deviceConfigUrl; + doc[F("device")][F("configuration_url")] = _deviceConfigUrl; } - doc["enabled_by_default"] = enabledByDefault; - doc["unique_id"] = _prefix + "_heating"; - doc["object_id"] = _prefix + "_heating"; - doc["entity_category"] = "config"; - doc["name"] = "Heating"; - doc["icon"] = "mdi:radiator"; - doc["state_topic"] = _prefix + F("/settings"); - doc["state_on"] = true; - doc["state_off"] = false; - doc["value_template"] = "{{ value_json.heating.enable }}"; - doc["command_topic"] = _prefix + "/settings/set"; - doc["payload_on"] = "{\"heating\": {\"enable\" : true}}"; - doc["payload_off"] = "{\"heating\": {\"enable\" : false}}"; + doc[F("enabled_by_default")] = enabledByDefault; + doc[F("unique_id")] = _prefix + F("_heating"); + doc[F("object_id")] = _prefix + F("_heating"); + doc[F("entity_category")] = F("config"); + doc[F("name")] = F("Heating"); + doc[F("icon")] = F("mdi:radiator"); + doc[F("state_topic")] = _prefix + F("/settings"); + doc[F("state_on")] = true; + doc[F("state_off")] = false; + doc[F("value_template")] = F("{{ value_json.heating.enable }}"); + doc[F("command_topic")] = _prefix + F("/settings/set"); + doc[F("payload_on")] = F("{\"heating\": {\"enable\" : true}}"); + doc[F("payload_off")] = F("{\"heating\": {\"enable\" : false}}"); - client.beginPublish((F("homeassistant/switch/") + _prefix + "/heating/config").c_str(), measureJson(doc), true); + client.beginPublish((F("homeassistant/switch/") + _prefix + F("/heating/config")).c_str(), measureJson(doc), true); + //BufferingPrint bufferedClient(client, 32); + //serializeJson(doc, bufferedClient); + //bufferedClient.flush(); + serializeJson(doc, client); + return client.endPublish(); + } + + bool publishSwitchHeatingTurbo(bool enabledByDefault = true) { + StaticJsonDocument<1536> doc; + doc[F("availability")][F("topic")] = _prefix + F("/status"); + doc[F("device")][F("identifiers")][0] = _prefix; + doc[F("device")][F("sw_version")] = _deviceVersion; + doc[F("device")][F("manufacturer")] = _deviceManufacturer; + doc[F("device")][F("model")] = _deviceModel; + doc[F("device")][F("name")] = _deviceName; + if (_deviceConfigUrl) { + doc[F("device")][F("configuration_url")] = _deviceConfigUrl; + } + doc[F("enabled_by_default")] = enabledByDefault; + doc[F("unique_id")] = _prefix + F("_heating_turbo"); + doc[F("object_id")] = _prefix + F("_heating_turbo"); + doc[F("entity_category")] = F("config"); + doc[F("name")] = F("Turbo heating"); + doc[F("icon")] = F("mdi:rocket-launch-outline"); + doc[F("state_topic")] = _prefix + F("/settings"); + doc[F("state_on")] = true; + doc[F("state_off")] = false; + doc[F("value_template")] = F("{{ value_json.heating.turbo }}"); + doc[F("command_topic")] = _prefix + F("/settings/set"); + doc[F("payload_on")] = F("{\"heating\": {\"turbo\" : true}}"); + doc[F("payload_off")] = F("{\"heating\": {\"turbo\" : false}}"); + + client.beginPublish((F("homeassistant/switch/") + _prefix + F("/heating_turbo/config")).c_str(), measureJson(doc), true); //BufferingPrint bufferedClient(client, 32); //serializeJson(doc, bufferedClient); //bufferedClient.flush(); @@ -227,32 +260,32 @@ public: bool publishNumberHeatingTarget(byte minTemp = 20, byte maxTemp = 90, bool enabledByDefault = true) { StaticJsonDocument<1536> doc; - doc["availability"]["topic"] = _prefix + F("/status"); - doc["device"]["identifiers"][0] = _prefix; - doc["device"]["sw_version"] = _deviceVersion; - doc["device"]["manufacturer"] = _deviceManufacturer; - doc["device"]["model"] = _deviceModel; - doc["device"]["name"] = _deviceName; + doc[F("availability")][F("topic")] = _prefix + F("/status"); + doc[F("device")][F("identifiers")][0] = _prefix; + doc[F("device")][F("sw_version")] = _deviceVersion; + doc[F("device")][F("manufacturer")] = _deviceManufacturer; + doc[F("device")][F("model")] = _deviceModel; + doc[F("device")][F("name")] = _deviceName; if (_deviceConfigUrl) { - doc["device"]["configuration_url"] = _deviceConfigUrl; + doc[F("device")][F("configuration_url")] = _deviceConfigUrl; } - doc["enabled_by_default"] = enabledByDefault; - doc["unique_id"] = _prefix + "_heating_target"; - doc["object_id"] = _prefix + "_heating_target"; - doc["entity_category"] = "config"; - doc["device_class"] = "temperature"; - doc["unit_of_measurement"] = "°C"; - doc["name"] = "Heating target"; - doc["icon"] = "mdi:radiator"; - doc["state_topic"] = _prefix + F("/settings"); - doc["value_template"] = "{{ value_json.heating.target|float(0)|round(1) }}"; - doc["command_topic"] = _prefix + "/settings/set"; - doc["command_template"] = "{\"heating\": {\"target\" : {{ value }}}}"; - doc["min"] = minTemp; - doc["max"] = maxTemp; - doc["step"] = 0.5; + doc[F("enabled_by_default")] = enabledByDefault; + doc[F("unique_id")] = _prefix + F("_heating_target"); + doc[F("object_id")] = _prefix + F("_heating_target"); + doc[F("entity_category")] = F("config"); + doc[F("device_class")] = F("temperature"); + doc[F("unit_of_measurement")] = F("°C"); + doc[F("name")] = F("Heating target"); + doc[F("icon")] = F("mdi:radiator"); + doc[F("state_topic")] = _prefix + F("/settings"); + doc[F("value_template")] = F("{{ value_json.heating.target|float(0)|round(1) }}"); + doc[F("command_topic")] = _prefix + F("/settings/set"); + doc[F("command_template")] = F("{\"heating\": {\"target\" : {{ value }}}}"); + doc[F("min")] = minTemp; + doc[F("max")] = maxTemp; + doc[F("step")] = 0.5; - client.beginPublish((F("homeassistant/number/") + _prefix + "/heating_target/config").c_str(), measureJson(doc), true); + client.beginPublish((F("homeassistant/number/") + _prefix + F("/heating_target/config")).c_str(), measureJson(doc), true); //BufferingPrint bufferedClient(client, 32); //serializeJson(doc, bufferedClient); //bufferedClient.flush(); @@ -262,31 +295,31 @@ public: bool publishNumberHeatingHysteresis(bool enabledByDefault = true) { StaticJsonDocument<1536> doc; - doc["device"]["identifiers"][0] = _prefix; - doc["device"]["sw_version"] = _deviceVersion; - doc["device"]["manufacturer"] = _deviceManufacturer; - doc["device"]["model"] = _deviceModel; - doc["device"]["name"] = _deviceName; + doc[F("device")][F("identifiers")][0] = _prefix; + doc[F("device")][F("sw_version")] = _deviceVersion; + doc[F("device")][F("manufacturer")] = _deviceManufacturer; + doc[F("device")][F("model")] = _deviceModel; + doc[F("device")][F("name")] = _deviceName; if (_deviceConfigUrl) { - doc["device"]["configuration_url"] = _deviceConfigUrl; + doc[F("device")][F("configuration_url")] = _deviceConfigUrl; } - doc["enabled_by_default"] = enabledByDefault; - doc["unique_id"] = _prefix + "_heating_hysteresis"; - doc["object_id"] = _prefix + "_heating_hysteresis"; - doc["entity_category"] = "config"; - doc["device_class"] = "temperature"; - doc["unit_of_measurement"] = "°C"; - doc["name"] = "Heating hysteresis"; - doc["icon"] = "mdi:altimeter"; - doc["state_topic"] = _prefix + F("/settings"); - doc["value_template"] = "{{ value_json.heating.hysteresis|float(0)|round(1) }}"; - doc["command_topic"] = _prefix + "/settings/set"; - doc["command_template"] = "{\"heating\": {\"hysteresis\" : {{ value }}}}"; - doc["min"] = 0; - doc["max"] = 5; - doc["step"] = 0.1; + doc[F("enabled_by_default")] = enabledByDefault; + doc[F("unique_id")] = _prefix + F("_heating_hysteresis"); + doc[F("object_id")] = _prefix + F("_heating_hysteresis"); + doc[F("entity_category")] = F("config"); + doc[F("device_class")] = F("temperature"); + doc[F("unit_of_measurement")] = F("°C"); + doc[F("name")] = F("Heating hysteresis"); + doc[F("icon")] = F("mdi:altimeter"); + doc[F("state_topic")] = _prefix + F("/settings"); + doc[F("value_template")] = F("{{ value_json.heating.hysteresis|float(0)|round(1) }}"); + doc[F("command_topic")] = _prefix + F("/settings/set"); + doc[F("command_template")] = F("{\"heating\": {\"hysteresis\" : {{ value }}}}"); + doc[F("min")] = 0; + doc[F("max")] = 5; + doc[F("step")] = 0.1; - client.beginPublish((F("homeassistant/number/") + _prefix + "/heating_hysteresis/config").c_str(), measureJson(doc), true); + client.beginPublish((F("homeassistant/number/") + _prefix + F("/heating_hysteresis/config")).c_str(), measureJson(doc), true); //BufferingPrint bufferedClient(client, 32); //serializeJson(doc, bufferedClient); //bufferedClient.flush(); @@ -296,28 +329,28 @@ public: bool publishSensorHeatingSetpoint(bool enabledByDefault = true) { StaticJsonDocument<1536> doc; - doc["availability"]["topic"] = _prefix + F("/status"); - doc["device"]["identifiers"][0] = _prefix; - doc["device"]["sw_version"] = _deviceVersion; - doc["device"]["manufacturer"] = _deviceManufacturer; - doc["device"]["model"] = _deviceModel; - doc["device"]["name"] = _deviceName; + doc[F("availability")][F("topic")] = _prefix + F("/status"); + doc[F("device")][F("identifiers")][0] = _prefix; + doc[F("device")][F("sw_version")] = _deviceVersion; + doc[F("device")][F("manufacturer")] = _deviceManufacturer; + doc[F("device")][F("model")] = _deviceModel; + doc[F("device")][F("name")] = _deviceName; if (_deviceConfigUrl) { - doc["device"]["configuration_url"] = _deviceConfigUrl; + doc[F("device")][F("configuration_url")] = _deviceConfigUrl; } - doc["enabled_by_default"] = enabledByDefault; - doc["unique_id"] = _prefix + "_heating_setpoint"; - doc["object_id"] = _prefix + "_heating_setpoint"; - doc["entity_category"] = "diagnostic"; - doc["device_class"] = "temperature"; - doc["state_class"] = "measurement"; - doc["unit_of_measurement"] = "°C"; - doc["name"] = "Heating setpoint"; - doc["icon"] = "mdi:coolant-temperature"; - doc["state_topic"] = _prefix + F("/state"); - doc["value_template"] = "{{ value_json.parameters.heatingSetpoint|int(0) }}"; + doc[F("enabled_by_default")] = enabledByDefault; + doc[F("unique_id")] = _prefix + F("_heating_setpoint"); + doc[F("object_id")] = _prefix + F("_heating_setpoint"); + doc[F("entity_category")] = F("diagnostic"); + doc[F("device_class")] = F("temperature"); + doc[F("state_class")] = F("measurement"); + doc[F("unit_of_measurement")] = F("°C"); + doc[F("name")] = F("Heating setpoint"); + doc[F("icon")] = F("mdi:coolant-temperature"); + doc[F("state_topic")] = _prefix + F("/state"); + doc[F("value_template")] = F("{{ value_json.parameters.heatingSetpoint|int(0) }}"); - client.beginPublish((F("homeassistant/sensor/") + _prefix + "/heating_setpoint/config").c_str(), measureJson(doc), true); + client.beginPublish((F("homeassistant/sensor/") + _prefix + F("/heating_setpoint/config")).c_str(), measureJson(doc), true); //BufferingPrint bufferedClient(client, 32); //serializeJson(doc, bufferedClient); //bufferedClient.flush(); @@ -328,30 +361,30 @@ public: bool publishSwitchDHW(bool enabledByDefault = true) { StaticJsonDocument<1536> doc; - doc["availability"]["topic"] = _prefix + F("/status"); - doc["device"]["identifiers"][0] = _prefix; - doc["device"]["sw_version"] = _deviceVersion; - doc["device"]["manufacturer"] = _deviceManufacturer; - doc["device"]["model"] = _deviceModel; - doc["device"]["name"] = _deviceName; + doc[F("availability")][F("topic")] = _prefix + F("/status"); + doc[F("device")][F("identifiers")][0] = _prefix; + doc[F("device")][F("sw_version")] = _deviceVersion; + doc[F("device")][F("manufacturer")] = _deviceManufacturer; + doc[F("device")][F("model")] = _deviceModel; + doc[F("device")][F("name")] = _deviceName; if (_deviceConfigUrl) { - doc["device"]["configuration_url"] = _deviceConfigUrl; + doc[F("device")][F("configuration_url")] = _deviceConfigUrl; } - doc["enabled_by_default"] = enabledByDefault; - doc["unique_id"] = _prefix + "_dhw"; - doc["object_id"] = _prefix + "_dhw"; - doc["entity_category"] = "config"; - doc["name"] = "DHW"; - doc["icon"] = "mdi:water-pump"; - doc["state_topic"] = _prefix + F("/settings"); - doc["state_on"] = true; - doc["state_off"] = false; - doc["value_template"] = "{{ value_json.dhw.enable }}"; - doc["command_topic"] = _prefix + "/settings/set"; - doc["payload_on"] = "{\"dhw\": {\"enable\" : true}}"; - doc["payload_off"] = "{\"dhw\": {\"enable\" : false}}"; + doc[F("enabled_by_default")] = enabledByDefault; + doc[F("unique_id")] = _prefix + F("_dhw"); + doc[F("object_id")] = _prefix + F("_dhw"); + doc[F("entity_category")] = F("config"); + doc[F("name")] = F("DHW"); + doc[F("icon")] = F("mdi:water-pump"); + doc[F("state_topic")] = _prefix + F("/settings"); + doc[F("state_on")] = true; + doc[F("state_off")] = false; + doc[F("value_template")] = F("{{ value_json.dhw.enable }}"); + doc[F("command_topic")] = _prefix + F("/settings/set"); + doc[F("payload_on")] = F("{\"dhw\": {\"enable\" : true}}"); + doc[F("payload_off")] = F("{\"dhw\": {\"enable\" : false}}"); - client.beginPublish((F("homeassistant/switch/") + _prefix + "/dhw/config").c_str(), measureJson(doc), true); + client.beginPublish((F("homeassistant/switch/") + _prefix + F("/dhw/config")).c_str(), measureJson(doc), true); //BufferingPrint bufferedClient(client, 32); //serializeJson(doc, bufferedClient); //bufferedClient.flush(); @@ -361,32 +394,32 @@ public: bool publishNumberDHWTarget(byte minTemp = 40, byte maxTemp = 60, bool enabledByDefault = true) { StaticJsonDocument<1536> doc; - doc["availability"]["topic"] = _prefix + F("/status"); - doc["device"]["identifiers"][0] = _prefix; - doc["device"]["sw_version"] = _deviceVersion; - doc["device"]["manufacturer"] = _deviceManufacturer; - doc["device"]["model"] = _deviceModel; - doc["device"]["name"] = _deviceName; + doc[F("availability")][F("topic")] = _prefix + F("/status"); + doc[F("device")][F("identifiers")][0] = _prefix; + doc[F("device")][F("sw_version")] = _deviceVersion; + doc[F("device")][F("manufacturer")] = _deviceManufacturer; + doc[F("device")][F("model")] = _deviceModel; + doc[F("device")][F("name")] = _deviceName; if (_deviceConfigUrl) { - doc["device"]["configuration_url"] = _deviceConfigUrl; + doc[F("device")][F("configuration_url")] = _deviceConfigUrl; } - doc["enabled_by_default"] = enabledByDefault; - doc["unique_id"] = _prefix + "_dhw_target"; - doc["object_id"] = _prefix + "_dhw_target"; - doc["entity_category"] = "config"; - doc["device_class"] = "temperature"; - doc["unit_of_measurement"] = "°C"; - doc["name"] = "DHW target"; - doc["icon"] = "mdi:water-pump"; - doc["state_topic"] = _prefix + F("/settings"); - doc["value_template"] = "{{ value_json.dhw.target|int(0) }}"; - doc["command_topic"] = _prefix + "/settings/set"; - doc["command_template"] = "{\"dhw\": {\"target\" : {{ value|int(0) }}}}"; - doc["min"] = minTemp; - doc["max"] = maxTemp; - doc["step"] = 1; + doc[F("enabled_by_default")] = enabledByDefault; + doc[F("unique_id")] = _prefix + F("_dhw_target"); + doc[F("object_id")] = _prefix + F("_dhw_target"); + doc[F("entity_category")] = F("config"); + doc[F("device_class")] = F("temperature"); + doc[F("unit_of_measurement")] = F("°C"); + doc[F("name")] = F("DHW target"); + doc[F("icon")] = F("mdi:water-pump"); + doc[F("state_topic")] = _prefix + F("/settings"); + doc[F("value_template")] = F("{{ value_json.dhw.target|int(0) }}"); + doc[F("command_topic")] = _prefix + F("/settings/set"); + doc[F("command_template")] = F("{\"dhw\": {\"target\" : {{ value|int(0) }}}}"); + doc[F("min")] = minTemp; + doc[F("max")] = maxTemp; + doc[F("step")] = 1; - client.beginPublish((F("homeassistant/number/") + _prefix + "/dhw_target/config").c_str(), measureJson(doc), true); + client.beginPublish((F("homeassistant/number/") + _prefix + F("/dhw_target/config")).c_str(), measureJson(doc), true); //BufferingPrint bufferedClient(client, 32); //serializeJson(doc, bufferedClient); //bufferedClient.flush(); @@ -397,29 +430,29 @@ public: bool publishSwitchPID(bool enabledByDefault = true) { StaticJsonDocument<1536> doc; - doc["device"]["identifiers"][0] = _prefix; - doc["device"]["sw_version"] = _deviceVersion; - doc["device"]["manufacturer"] = _deviceManufacturer; - doc["device"]["model"] = _deviceModel; - doc["device"]["name"] = _deviceName; + doc[F("device")][F("identifiers")][0] = _prefix; + doc[F("device")][F("sw_version")] = _deviceVersion; + doc[F("device")][F("manufacturer")] = _deviceManufacturer; + doc[F("device")][F("model")] = _deviceModel; + doc[F("device")][F("name")] = _deviceName; if (_deviceConfigUrl) { - doc["device"]["configuration_url"] = _deviceConfigUrl; + doc[F("device")][F("configuration_url")] = _deviceConfigUrl; } - doc["enabled_by_default"] = enabledByDefault; - doc["unique_id"] = _prefix + "_pid"; - doc["object_id"] = _prefix + "_pid"; - doc["entity_category"] = "config"; - doc["name"] = "PID"; - doc["icon"] = "mdi:chart-bar-stacked"; - doc["state_topic"] = _prefix + F("/settings"); - doc["state_on"] = true; - doc["state_off"] = false; - doc["value_template"] = "{{ value_json.pid.enable }}"; - doc["command_topic"] = _prefix + "/settings/set"; - doc["payload_on"] = "{\"pid\": {\"enable\" : true}}"; - doc["payload_off"] = "{\"pid\": {\"enable\" : false}}"; + doc[F("enabled_by_default")] = enabledByDefault; + doc[F("unique_id")] = _prefix + F("_pid"); + doc[F("object_id")] = _prefix + F("_pid"); + doc[F("entity_category")] = F("config"); + doc[F("name")] = F("PID"); + doc[F("icon")] = F("mdi:chart-bar-stacked"); + doc[F("state_topic")] = _prefix + F("/settings"); + doc[F("state_on")] = true; + doc[F("state_off")] = false; + doc[F("value_template")] = F("{{ value_json.pid.enable }}"); + doc[F("command_topic")] = _prefix + F("/settings/set"); + doc[F("payload_on")] = F("{\"pid\": {\"enable\" : true}}"); + doc[F("payload_off")] = F("{\"pid\": {\"enable\" : false}}"); - client.beginPublish((F("homeassistant/switch/") + _prefix + "/pid/config").c_str(), measureJson(doc), true); + client.beginPublish((F("homeassistant/switch/") + _prefix + F("/pid/config")).c_str(), measureJson(doc), true); //BufferingPrint bufferedClient(client, 32); //serializeJson(doc, bufferedClient); //bufferedClient.flush(); @@ -429,28 +462,28 @@ public: bool publishNumberPIDFactorP(bool enabledByDefault = true) { StaticJsonDocument<1536> doc; - doc["device"]["identifiers"][0] = _prefix; - doc["device"]["sw_version"] = _deviceVersion; - doc["device"]["manufacturer"] = _deviceManufacturer; - doc["device"]["model"] = _deviceModel; - doc["device"]["name"] = _deviceName; + doc[F("device")][F("identifiers")][0] = _prefix; + doc[F("device")][F("sw_version")] = _deviceVersion; + doc[F("device")][F("manufacturer")] = _deviceManufacturer; + doc[F("device")][F("model")] = _deviceModel; + doc[F("device")][F("name")] = _deviceName; if (_deviceConfigUrl) { - doc["device"]["configuration_url"] = _deviceConfigUrl; + doc[F("device")][F("configuration_url")] = _deviceConfigUrl; } - doc["unique_id"] = _prefix + "_pid_p"; - doc["object_id"] = _prefix + "_pid_p"; - doc["entity_category"] = "config"; - doc["name"] = "PID factor P"; - doc["icon"] = "mdi:alpha-p-circle-outline"; - doc["state_topic"] = _prefix + F("/settings"); - doc["value_template"] = "{{ value_json.pid.p_factor|float(0)|round(3) }}"; - doc["command_topic"] = _prefix + "/settings/set"; - doc["command_template"] = "{\"pid\": {\"p_factor\" : {{ value }}}}"; - doc["min"] = 0.001; - doc["max"] = 10; - doc["step"] = 0.001; + doc[F("unique_id")] = _prefix + F("_pid_p"); + doc[F("object_id")] = _prefix + F("_pid_p"); + doc[F("entity_category")] = F("config"); + doc[F("name")] = F("PID factor P"); + doc[F("icon")] = F("mdi:alpha-p-circle-outline"); + doc[F("state_topic")] = _prefix + F("/settings"); + doc[F("value_template")] = F("{{ value_json.pid.p_factor|float(0)|round(3) }}"); + doc[F("command_topic")] = _prefix + F("/settings/set"); + doc[F("command_template")] = F("{\"pid\": {\"p_factor\" : {{ value }}}}"); + doc[F("min")] = 0.001; + doc[F("max")] = 10; + doc[F("step")] = 0.001; - client.beginPublish((F("homeassistant/number/") + _prefix + "/pid_p_factor/config").c_str(), measureJson(doc), true); + client.beginPublish((F("homeassistant/number/") + _prefix + F("/pid_p_factor/config")).c_str(), measureJson(doc), true); //BufferingPrint bufferedClient(client, 32); //serializeJson(doc, bufferedClient); //bufferedClient.flush(); @@ -460,28 +493,28 @@ public: bool publishNumberPIDFactorI(bool enabledByDefault = true) { StaticJsonDocument<1536> doc; - doc["device"]["identifiers"][0] = _prefix; - doc["device"]["sw_version"] = _deviceVersion; - doc["device"]["manufacturer"] = _deviceManufacturer; - doc["device"]["model"] = _deviceModel; - doc["device"]["name"] = _deviceName; + doc[F("device")][F("identifiers")][0] = _prefix; + doc[F("device")][F("sw_version")] = _deviceVersion; + doc[F("device")][F("manufacturer")] = _deviceManufacturer; + doc[F("device")][F("model")] = _deviceModel; + doc[F("device")][F("name")] = _deviceName; if (_deviceConfigUrl) { - doc["device"]["configuration_url"] = _deviceConfigUrl; + doc[F("device")][F("configuration_url")] = _deviceConfigUrl; } - doc["unique_id"] = _prefix + "_pid_i"; - doc["object_id"] = _prefix + "_pid_i"; - doc["entity_category"] = "config"; - doc["name"] = "PID factor I"; - doc["icon"] = "mdi:alpha-i-circle-outline"; - doc["state_topic"] = _prefix + F("/settings"); - doc["value_template"] = "{{ value_json.pid.i_factor|float(0)|round(3) }}"; - doc["command_topic"] = _prefix + "/settings/set"; - doc["command_template"] = "{\"pid\": {\"i_factor\" : {{ value }}}}"; - doc["min"] = 0; - doc["max"] = 10; - doc["step"] = 0.001; + doc[F("unique_id")] = _prefix + F("_pid_i"); + doc[F("object_id")] = _prefix + F("_pid_i"); + doc[F("entity_category")] = F("config"); + doc[F("name")] = F("PID factor I"); + doc[F("icon")] = F("mdi:alpha-i-circle-outline"); + doc[F("state_topic")] = _prefix + F("/settings"); + doc[F("value_template")] = F("{{ value_json.pid.i_factor|float(0)|round(3) }}"); + doc[F("command_topic")] = _prefix + F("/settings/set"); + doc[F("command_template")] = F("{\"pid\": {\"i_factor\" : {{ value }}}}"); + doc[F("min")] = 0; + doc[F("max")] = 10; + doc[F("step")] = 0.001; - client.beginPublish((F("homeassistant/number/") + _prefix + "/pid_i_factor/config").c_str(), measureJson(doc), true); + client.beginPublish((F("homeassistant/number/") + _prefix + F("/pid_i_factor/config")).c_str(), measureJson(doc), true); //BufferingPrint bufferedClient(client, 32); //serializeJson(doc, bufferedClient); //bufferedClient.flush(); @@ -491,28 +524,28 @@ public: bool publishNumberPIDFactorD(bool enabledByDefault = true) { StaticJsonDocument<1536> doc; - doc["device"]["identifiers"][0] = _prefix; - doc["device"]["sw_version"] = _deviceVersion; - doc["device"]["manufacturer"] = _deviceManufacturer; - doc["device"]["model"] = _deviceModel; - doc["device"]["name"] = _deviceName; + doc[F("device")][F("identifiers")][0] = _prefix; + doc[F("device")][F("sw_version")] = _deviceVersion; + doc[F("device")][F("manufacturer")] = _deviceManufacturer; + doc[F("device")][F("model")] = _deviceModel; + doc[F("device")][F("name")] = _deviceName; if (_deviceConfigUrl) { - doc["device"]["configuration_url"] = _deviceConfigUrl; + doc[F("device")][F("configuration_url")] = _deviceConfigUrl; } - doc["unique_id"] = _prefix + "_pid_d"; - doc["object_id"] = _prefix + "_pid_d"; - doc["entity_category"] = "config"; - doc["name"] = "PID factor D"; - doc["icon"] = "mdi:alpha-d-circle-outline"; - doc["state_topic"] = _prefix + F("/settings"); - doc["value_template"] = "{{ value_json.pid.d_factor|float(0)|round(3) }}"; - doc["command_topic"] = _prefix + "/settings/set"; - doc["command_template"] = "{\"pid\": {\"d_factor\" : {{ value }}}}"; - doc["min"] = 0; - doc["max"] = 10; - doc["step"] = 0.001; + doc[F("unique_id")] = _prefix + F("_pid_d"); + doc[F("object_id")] = _prefix + F("_pid_d"); + doc[F("entity_category")] = F("config"); + doc[F("name")] = F("PID factor D"); + doc[F("icon")] = F("mdi:alpha-d-circle-outline"); + doc[F("state_topic")] = _prefix + F("/settings"); + doc[F("value_template")] = F("{{ value_json.pid.d_factor|float(0)|round(3) }}"); + doc[F("command_topic")] = _prefix + F("/settings/set"); + doc[F("command_template")] = F("{\"pid\": {\"d_factor\" : {{ value }}}}"); + doc[F("min")] = 0; + doc[F("max")] = 10; + doc[F("step")] = 0.001; - client.beginPublish((F("homeassistant/number/") + _prefix + "/pid_d_factor/config").c_str(), measureJson(doc), true); + client.beginPublish((F("homeassistant/number/") + _prefix + F("/pid_d_factor/config")).c_str(), measureJson(doc), true); //BufferingPrint bufferedClient(client, 32); //serializeJson(doc, bufferedClient); //bufferedClient.flush(); @@ -523,29 +556,29 @@ public: bool publishSwitchEquitherm(bool enabledByDefault = true) { StaticJsonDocument<1536> doc; - doc["device"]["identifiers"][0] = _prefix; - doc["device"]["sw_version"] = _deviceVersion; - doc["device"]["manufacturer"] = _deviceManufacturer; - doc["device"]["model"] = _deviceModel; - doc["device"]["name"] = _deviceName; + doc[F("device")][F("identifiers")][0] = _prefix; + doc[F("device")][F("sw_version")] = _deviceVersion; + doc[F("device")][F("manufacturer")] = _deviceManufacturer; + doc[F("device")][F("model")] = _deviceModel; + doc[F("device")][F("name")] = _deviceName; if (_deviceConfigUrl) { - doc["device"]["configuration_url"] = _deviceConfigUrl; + doc[F("device")][F("configuration_url")] = _deviceConfigUrl; } - doc["enabled_by_default"] = enabledByDefault; - doc["unique_id"] = _prefix + "_equitherm"; - doc["object_id"] = _prefix + "_equitherm"; - doc["entity_category"] = "config"; - doc["name"] = "Equitherm"; - doc["icon"] = "mdi:sun-snowflake-variant"; - doc["state_topic"] = _prefix + F("/settings"); - doc["state_on"] = true; - doc["state_off"] = false; - doc["value_template"] = "{{ value_json.equitherm.enable }}"; - doc["command_topic"] = _prefix + "/settings/set"; - doc["payload_on"] = "{\"equitherm\": {\"enable\" : true}}"; - doc["payload_off"] = "{\"equitherm\": {\"enable\" : false}}"; + doc[F("enabled_by_default")] = enabledByDefault; + doc[F("unique_id")] = _prefix + F("_equitherm"); + doc[F("object_id")] = _prefix + F("_equitherm"); + doc[F("entity_category")] = F("config"); + doc[F("name")] = F("Equitherm"); + doc[F("icon")] = F("mdi:sun-snowflake-variant"); + doc[F("state_topic")] = _prefix + F("/settings"); + doc[F("state_on")] = true; + doc[F("state_off")] = false; + doc[F("value_template")] = F("{{ value_json.equitherm.enable }}"); + doc[F("command_topic")] = _prefix + F("/settings/set"); + doc[F("payload_on")] = F("{\"equitherm\": {\"enable\" : true}}"); + doc[F("payload_off")] = F("{\"equitherm\": {\"enable\" : false}}"); - client.beginPublish((F("homeassistant/switch/") + _prefix + "/equitherm/config").c_str(), measureJson(doc), true); + client.beginPublish((F("homeassistant/switch/") + _prefix + F("/equitherm/config")).c_str(), measureJson(doc), true); //BufferingPrint bufferedClient(client, 32); //serializeJson(doc, bufferedClient); //bufferedClient.flush(); @@ -555,28 +588,28 @@ public: bool publishNumberEquithermFactorN(bool enabledByDefault = true) { StaticJsonDocument<1536> doc; - doc["device"]["identifiers"][0] = _prefix; - doc["device"]["sw_version"] = _deviceVersion; - doc["device"]["manufacturer"] = _deviceManufacturer; - doc["device"]["model"] = _deviceModel; - doc["device"]["name"] = _deviceName; + doc[F("device")][F("identifiers")][0] = _prefix; + doc[F("device")][F("sw_version")] = _deviceVersion; + doc[F("device")][F("manufacturer")] = _deviceManufacturer; + doc[F("device")][F("model")] = _deviceModel; + doc[F("device")][F("name")] = _deviceName; if (_deviceConfigUrl) { - doc["device"]["configuration_url"] = _deviceConfigUrl; + doc[F("device")][F("configuration_url")] = _deviceConfigUrl; } - doc["unique_id"] = _prefix + "_equitherm_n"; - doc["object_id"] = _prefix + "_equitherm_n"; - doc["entity_category"] = "config"; - doc["name"] = "Equitherm factor N"; - doc["icon"] = "mdi:alpha-n-circle-outline"; - doc["state_topic"] = _prefix + F("/settings"); - doc["value_template"] = "{{ value_json.equitherm.n_factor|float(0)|round(3) }}"; - doc["command_topic"] = _prefix + "/settings/set"; - doc["command_template"] = "{\"equitherm\": {\"n_factor\" : {{ value }}}}"; - doc["min"] = 0.001; - doc["max"] = 5; - doc["step"] = 0.001; + doc[F("unique_id")] = _prefix + F("_equitherm_n"); + doc[F("object_id")] = _prefix + F("_equitherm_n"); + doc[F("entity_category")] = F("config"); + doc[F("name")] = F("Equitherm factor N"); + doc[F("icon")] = F("mdi:alpha-n-circle-outline"); + doc[F("state_topic")] = _prefix + F("/settings"); + doc[F("value_template")] = F("{{ value_json.equitherm.n_factor|float(0)|round(3) }}"); + doc[F("command_topic")] = _prefix + F("/settings/set"); + doc[F("command_template")] = F("{\"equitherm\": {\"n_factor\" : {{ value }}}}"); + doc[F("min")] = 0.001; + doc[F("max")] = 5; + doc[F("step")] = 0.001; - client.beginPublish((F("homeassistant/number/") + _prefix + "/equitherm_n_factor/config").c_str(), measureJson(doc), true); + client.beginPublish((F("homeassistant/number/") + _prefix + F("/equitherm_n_factor/config")).c_str(), measureJson(doc), true); //BufferingPrint bufferedClient(client, 32); //serializeJson(doc, bufferedClient); //bufferedClient.flush(); @@ -586,28 +619,28 @@ public: bool publishNumberEquithermFactorK(bool enabledByDefault = true) { StaticJsonDocument<1536> doc; - doc["device"]["identifiers"][0] = _prefix; - doc["device"]["sw_version"] = _deviceVersion; - doc["device"]["manufacturer"] = _deviceManufacturer; - doc["device"]["model"] = _deviceModel; - doc["device"]["name"] = _deviceName; + doc[F("device")][F("identifiers")][0] = _prefix; + doc[F("device")][F("sw_version")] = _deviceVersion; + doc[F("device")][F("manufacturer")] = _deviceManufacturer; + doc[F("device")][F("model")] = _deviceModel; + doc[F("device")][F("name")] = _deviceName; if (_deviceConfigUrl) { - doc["device"]["configuration_url"] = _deviceConfigUrl; + doc[F("device")][F("configuration_url")] = _deviceConfigUrl; } - doc["unique_id"] = _prefix + "_equitherm_k"; - doc["object_id"] = _prefix + "_equitherm_k"; - doc["entity_category"] = "config"; - doc["name"] = "Equitherm factor K"; - doc["icon"] = "mdi:alpha-k-circle-outline"; - doc["state_topic"] = _prefix + F("/settings"); - doc["value_template"] = "{{ value_json.equitherm.k_factor|float(0)|round(2) }}"; - doc["command_topic"] = _prefix + "/settings/set"; - doc["command_template"] = "{\"equitherm\": {\"k_factor\" : {{ value }}}}"; - doc["min"] = 0; - doc["max"] = 10; - doc["step"] = 0.01; + doc[F("unique_id")] = _prefix + F("_equitherm_k"); + doc[F("object_id")] = _prefix + F("_equitherm_k"); + doc[F("entity_category")] = F("config"); + doc[F("name")] = F("Equitherm factor K"); + doc[F("icon")] = F("mdi:alpha-k-circle-outline"); + doc[F("state_topic")] = _prefix + F("/settings"); + doc[F("value_template")] = F("{{ value_json.equitherm.k_factor|float(0)|round(2) }}"); + doc[F("command_topic")] = _prefix + F("/settings/set"); + doc[F("command_template")] = F("{\"equitherm\": {\"k_factor\" : {{ value }}}}"); + doc[F("min")] = 0; + doc[F("max")] = 10; + doc[F("step")] = 0.01; - client.beginPublish((F("homeassistant/number/") + _prefix + "/equitherm_k_factor/config").c_str(), measureJson(doc), true); + client.beginPublish((F("homeassistant/number/") + _prefix + F("/equitherm_k_factor/config")).c_str(), measureJson(doc), true); //BufferingPrint bufferedClient(client, 32); //serializeJson(doc, bufferedClient); //bufferedClient.flush(); @@ -617,30 +650,30 @@ public: bool publishNumberEquithermFactorT(bool enabledByDefault = true) { StaticJsonDocument<1536> doc; - doc["availability"]["topic"] = _prefix + F("/settings"); - doc["availability"]["value_template"] = F("{{ iif(value_json.pid.enable, 'offline', 'online') }}"); - doc["device"]["identifiers"][0] = _prefix; - doc["device"]["sw_version"] = _deviceVersion; - doc["device"]["manufacturer"] = _deviceManufacturer; - doc["device"]["model"] = _deviceModel; - doc["device"]["name"] = _deviceName; + doc[F("availability")][F("topic")] = _prefix + F("/settings"); + doc[F("availability")][F("value_template")] = F("{{ iif(value_json.pid.enable, 'offline', 'online') }}"); + doc[F("device")][F("identifiers")][0] = _prefix; + doc[F("device")][F("sw_version")] = _deviceVersion; + doc[F("device")][F("manufacturer")] = _deviceManufacturer; + doc[F("device")][F("model")] = _deviceModel; + doc[F("device")][F("name")] = _deviceName; if (_deviceConfigUrl) { - doc["device"]["configuration_url"] = _deviceConfigUrl; + doc[F("device")][F("configuration_url")] = _deviceConfigUrl; } - doc["unique_id"] = _prefix + "_equitherm_t"; - doc["object_id"] = _prefix + "_equitherm_t"; - doc["entity_category"] = "config"; - doc["name"] = "Equitherm factor T"; - doc["icon"] = "mdi:alpha-t-circle-outline"; - doc["state_topic"] = _prefix + F("/settings"); - doc["value_template"] = "{{ value_json.equitherm.t_factor|float(0)|round(2) }}"; - doc["command_topic"] = _prefix + "/settings/set"; - doc["command_template"] = "{\"equitherm\": {\"t_factor\" : {{ value }}}}"; - doc["min"] = 0; - doc["max"] = 10; - doc["step"] = 0.01; + doc[F("unique_id")] = _prefix + F("_equitherm_t"); + doc[F("object_id")] = _prefix + F("_equitherm_t"); + doc[F("entity_category")] = F("config"); + doc[F("name")] = F("Equitherm factor T"); + doc[F("icon")] = F("mdi:alpha-t-circle-outline"); + doc[F("state_topic")] = _prefix + F("/settings"); + doc[F("value_template")] = F("{{ value_json.equitherm.t_factor|float(0)|round(2) }}"); + doc[F("command_topic")] = _prefix + F("/settings/set"); + doc[F("command_template")] = F("{\"equitherm\": {\"t_factor\" : {{ value }}}}"); + doc[F("min")] = 0; + doc[F("max")] = 10; + doc[F("step")] = 0.01; - client.beginPublish((F("homeassistant/number/") + _prefix + "/equitherm_t_factor/config").c_str(), measureJson(doc), true); + client.beginPublish((F("homeassistant/number/") + _prefix + F("/equitherm_t_factor/config")).c_str(), measureJson(doc), true); //BufferingPrint bufferedClient(client, 32); //serializeJson(doc, bufferedClient); //bufferedClient.flush(); @@ -651,29 +684,29 @@ public: bool publishSwitchTuning(bool enabledByDefault = true) { StaticJsonDocument<1536> doc; - doc["device"]["identifiers"][0] = _prefix; - doc["device"]["sw_version"] = _deviceVersion; - doc["device"]["manufacturer"] = _deviceManufacturer; - doc["device"]["model"] = _deviceModel; - doc["device"]["name"] = _deviceName; + doc[F("device")][F("identifiers")][0] = _prefix; + doc[F("device")][F("sw_version")] = _deviceVersion; + doc[F("device")][F("manufacturer")] = _deviceManufacturer; + doc[F("device")][F("model")] = _deviceModel; + doc[F("device")][F("name")] = _deviceName; if (_deviceConfigUrl) { - doc["device"]["configuration_url"] = _deviceConfigUrl; + doc[F("device")][F("configuration_url")] = _deviceConfigUrl; } - doc["enabled_by_default"] = enabledByDefault; - doc["unique_id"] = _prefix + "_tuning"; - doc["object_id"] = _prefix + "_tuning"; - doc["entity_category"] = "config"; - doc["name"] = "Tuning"; - doc["icon"] = "mdi:tune-vertical"; - doc["state_topic"] = _prefix + F("/state"); - doc["state_on"] = true; - doc["state_off"] = false; - doc["value_template"] = "{{ value_json.tuning.enable }}"; - doc["command_topic"] = _prefix + "/state/set"; - doc["payload_on"] = "{\"tuning\": {\"enable\" : true}}"; - doc["payload_off"] = "{\"tuning\": {\"enable\" : false}}"; + doc[F("enabled_by_default")] = enabledByDefault; + doc[F("unique_id")] = _prefix + F("_tuning"); + doc[F("object_id")] = _prefix + F("_tuning"); + doc[F("entity_category")] = F("config"); + doc[F("name")] = F("Tuning"); + doc[F("icon")] = F("mdi:tune-vertical"); + doc[F("state_topic")] = _prefix + F("/state"); + doc[F("state_on")] = true; + doc[F("state_off")] = false; + doc[F("value_template")] = F("{{ value_json.tuning.enable }}"); + doc[F("command_topic")] = _prefix + F("/state/set"); + doc[F("payload_on")] = F("{\"tuning\": {\"enable\" : true}}"); + doc[F("payload_off")] = F("{\"tuning\": {\"enable\" : false}}"); - client.beginPublish((F("homeassistant/switch/") + _prefix + "/tuning/config").c_str(), measureJson(doc), true); + client.beginPublish((F("homeassistant/switch/") + _prefix + F("/tuning/config")).c_str(), measureJson(doc), true); //BufferingPrint bufferedClient(client, 32); //serializeJson(doc, bufferedClient); //bufferedClient.flush(); @@ -683,29 +716,29 @@ public: bool publishSelectTuningRegulator(bool enabledByDefault = true) { StaticJsonDocument<1536> doc; - doc["availability"]["topic"] = _prefix + F("/status"); - doc["availability_mode"] = F("all"); - doc["command_topic"] = _prefix + "/state/set"; - doc["command_template"] = F("{\"tuning\": {\"regulator\": {% if value == 'Equitherm' %}0{% elif value == 'PID' %}1{% endif %}}}"); - doc["device"]["identifiers"][0] = _prefix; - doc["device"]["sw_version"] = _deviceVersion; - doc["device"]["manufacturer"] = _deviceManufacturer; - doc["device"]["model"] = _deviceModel; - doc["device"]["name"] = _deviceName; + doc[F("availability")][F("topic")] = _prefix + F("/status"); + doc[F("availability_mode")] = F("all"); + doc[F("command_topic")] = _prefix + F("/state/set"); + doc[F("command_template")] = F("{\"tuning\": {\"regulator\": {% if value == 'Equitherm' %}0{% elif value == 'PID' %}1{% endif %}}}"); + doc[F("device")][F("identifiers")][0] = _prefix; + doc[F("device")][F("sw_version")] = _deviceVersion; + doc[F("device")][F("manufacturer")] = _deviceManufacturer; + doc[F("device")][F("model")] = _deviceModel; + doc[F("device")][F("name")] = _deviceName; if (_deviceConfigUrl) { - doc["device"]["configuration_url"] = _deviceConfigUrl; + doc[F("device")][F("configuration_url")] = _deviceConfigUrl; } - doc["enabled_by_default"] = enabledByDefault; - doc["unique_id"] = _prefix + "_tuning_regulator"; - doc["object_id"] = _prefix + "_tuning_regulator"; - doc["entity_category"] = "config"; - doc["name"] = "Tuning regulator"; - doc["state_topic"] = _prefix + F("/state"); - doc["value_template"] = F("{% if value_json.tuning.regulator == 0 %}Equitherm{% elif value_json.tuning.regulator == 1 %}PID{% endif %}"); - doc["options"][0] = F("Equitherm"); - doc["options"][1] = F("PID"); + doc[F("enabled_by_default")] = enabledByDefault; + doc[F("unique_id")] = _prefix + F("_tuning_regulator"); + doc[F("object_id")] = _prefix + F("_tuning_regulator"); + doc[F("entity_category")] = F("config"); + doc[F("name")] = F("Tuning regulator"); + doc[F("state_topic")] = _prefix + F("/state"); + doc[F("value_template")] = F("{% if value_json.tuning.regulator == 0 %}Equitherm{% elif value_json.tuning.regulator == 1 %}PID{% endif %}"); + doc[F("options")][0] = F("Equitherm"); + doc[F("options")][1] = F("PID"); - client.beginPublish((F("homeassistant/select/") + _prefix + "/tuning_regulator/config").c_str(), measureJson(doc), true); + client.beginPublish((F("homeassistant/select/") + _prefix + F("/tuning_regulator/config")).c_str(), measureJson(doc), true); //BufferingPrint bufferedClient(client, 32); //serializeJson(doc, bufferedClient); //bufferedClient.flush(); @@ -716,26 +749,26 @@ public: bool publishBinSensorStatus(bool enabledByDefault = true) { StaticJsonDocument<1536> doc; - doc["device"]["identifiers"][0] = _prefix; - doc["device"]["sw_version"] = _deviceVersion; - doc["device"]["manufacturer"] = _deviceManufacturer; - doc["device"]["model"] = _deviceModel; - doc["device"]["name"] = _deviceName; + doc[F("device")][F("identifiers")][0] = _prefix; + doc[F("device")][F("sw_version")] = _deviceVersion; + doc[F("device")][F("manufacturer")] = _deviceManufacturer; + doc[F("device")][F("model")] = _deviceModel; + doc[F("device")][F("name")] = _deviceName; if (_deviceConfigUrl) { - doc["device"]["configuration_url"] = _deviceConfigUrl; + doc[F("device")][F("configuration_url")] = _deviceConfigUrl; } - doc["enabled_by_default"] = enabledByDefault; - doc["unique_id"] = _prefix + "_status"; - doc["object_id"] = _prefix + "_status"; - doc["entity_category"] = "diagnostic"; - doc["device_class"] = "problem"; - doc["name"] = "Status"; - doc["icon"] = "mdi:list-status"; - doc["state_topic"] = _prefix + F("/status"); - doc["value_template"] = "{{ iif(value == 'online', 'OFF', 'ON') }}"; - doc["expire_after"] = 60; + doc[F("enabled_by_default")] = enabledByDefault; + doc[F("unique_id")] = _prefix + F("_status"); + doc[F("object_id")] = _prefix + F("_status"); + doc[F("entity_category")] = F("diagnostic"); + doc[F("device_class")] = F("problem"); + doc[F("name")] = F("Status"); + doc[F("icon")] = F("mdi:list-status"); + doc[F("state_topic")] = _prefix + F("/status"); + doc[F("value_template")] = F("{{ iif(value == 'online', 'OFF', 'ON') }}"); + doc[F("expire_after")] = 60; - client.beginPublish((F("homeassistant/binary_sensor/") + _prefix + "/status/config").c_str(), measureJson(doc), true); + client.beginPublish((F("homeassistant/binary_sensor/") + _prefix + F("/status/config")).c_str(), measureJson(doc), true); //BufferingPrint bufferedClient(client, 32); //serializeJson(doc, bufferedClient); //bufferedClient.flush(); @@ -745,25 +778,25 @@ public: bool publishBinSensorOtStatus(bool enabledByDefault = true) { StaticJsonDocument<1536> doc; - doc["device"]["identifiers"][0] = _prefix; - doc["device"]["sw_version"] = _deviceVersion; - doc["device"]["manufacturer"] = _deviceManufacturer; - doc["device"]["model"] = _deviceModel; - doc["device"]["name"] = _deviceName; + doc[F("device")][F("identifiers")][0] = _prefix; + doc[F("device")][F("sw_version")] = _deviceVersion; + doc[F("device")][F("manufacturer")] = _deviceManufacturer; + doc[F("device")][F("model")] = _deviceModel; + doc[F("device")][F("name")] = _deviceName; if (_deviceConfigUrl) { - doc["device"]["configuration_url"] = _deviceConfigUrl; + doc[F("device")][F("configuration_url")] = _deviceConfigUrl; } - doc["enabled_by_default"] = enabledByDefault; - doc["unique_id"] = _prefix + "_ot_status"; - doc["object_id"] = _prefix + "_ot_status"; - doc["entity_category"] = "diagnostic"; - doc["device_class"] = "problem"; - doc["name"] = "Opentherm status"; - doc["icon"] = "mdi:list-status"; - doc["state_topic"] = _prefix + F("/state"); - doc["value_template"] = "{{ iif(value_json.states.otStatus, 'OFF', 'ON') }}"; + doc[F("enabled_by_default")] = enabledByDefault; + doc[F("unique_id")] = _prefix + F("_ot_status"); + doc[F("object_id")] = _prefix + F("_ot_status"); + doc[F("entity_category")] = F("diagnostic"); + doc[F("device_class")] = F("problem"); + doc[F("name")] = F("Opentherm status"); + doc[F("icon")] = F("mdi:list-status"); + doc[F("state_topic")] = _prefix + F("/state"); + doc[F("value_template")] = F("{{ iif(value_json.states.otStatus, 'OFF', 'ON') }}"); - client.beginPublish((F("homeassistant/binary_sensor/") + _prefix + "/ot_status/config").c_str(), measureJson(doc), true); + client.beginPublish((F("homeassistant/binary_sensor/") + _prefix + F("/ot_status/config")).c_str(), measureJson(doc), true); //BufferingPrint bufferedClient(client, 32); //serializeJson(doc, bufferedClient); //bufferedClient.flush(); @@ -773,26 +806,26 @@ public: bool publishBinSensorHeating(bool enabledByDefault = true) { StaticJsonDocument<1536> doc; - doc["availability"]["topic"] = _prefix + F("/status"); - doc["device"]["identifiers"][0] = _prefix; - doc["device"]["sw_version"] = _deviceVersion; - doc["device"]["manufacturer"] = _deviceManufacturer; - doc["device"]["model"] = _deviceModel; - doc["device"]["name"] = _deviceName; + doc[F("availability")][F("topic")] = _prefix + F("/status"); + doc[F("device")][F("identifiers")][0] = _prefix; + doc[F("device")][F("sw_version")] = _deviceVersion; + doc[F("device")][F("manufacturer")] = _deviceManufacturer; + doc[F("device")][F("model")] = _deviceModel; + doc[F("device")][F("name")] = _deviceName; if (_deviceConfigUrl) { - doc["device"]["configuration_url"] = _deviceConfigUrl; + doc[F("device")][F("configuration_url")] = _deviceConfigUrl; } - doc["enabled_by_default"] = enabledByDefault; - doc["unique_id"] = _prefix + "_heating"; - doc["object_id"] = _prefix + "_heating"; - doc["entity_category"] = "diagnostic"; - doc["device_class"] = "running"; - doc["name"] = "Heating"; - doc["icon"] = "mdi:radiator"; - doc["state_topic"] = _prefix + F("/state"); - doc["value_template"] = F("{{ iif(value_json.states.heating, 'ON', 'OFF') }}"); + doc[F("enabled_by_default")] = enabledByDefault; + doc[F("unique_id")] = _prefix + F("_heating"); + doc[F("object_id")] = _prefix + F("_heating"); + doc[F("entity_category")] = F("diagnostic"); + doc[F("device_class")] = F("running"); + doc[F("name")] = F("Heating"); + doc[F("icon")] = F("mdi:radiator"); + doc[F("state_topic")] = _prefix + F("/state"); + doc[F("value_template")] = F("{{ iif(value_json.states.heating, 'ON', 'OFF') }}"); - client.beginPublish((F("homeassistant/binary_sensor/") + _prefix + "/heating/config").c_str(), measureJson(doc), true); + client.beginPublish((F("homeassistant/binary_sensor/") + _prefix + F("/heating/config")).c_str(), measureJson(doc), true); //BufferingPrint bufferedClient(client, 32); //serializeJson(doc, bufferedClient); //bufferedClient.flush(); @@ -802,26 +835,26 @@ public: bool publishBinSensorDHW(bool enabledByDefault = true) { StaticJsonDocument<1536> doc; - doc["availability"]["topic"] = _prefix + F("/status"); - doc["device"]["identifiers"][0] = _prefix; - doc["device"]["sw_version"] = _deviceVersion; - doc["device"]["manufacturer"] = _deviceManufacturer; - doc["device"]["model"] = _deviceModel; - doc["device"]["name"] = _deviceName; + doc[F("availability")][F("topic")] = _prefix + F("/status"); + doc[F("device")][F("identifiers")][0] = _prefix; + doc[F("device")][F("sw_version")] = _deviceVersion; + doc[F("device")][F("manufacturer")] = _deviceManufacturer; + doc[F("device")][F("model")] = _deviceModel; + doc[F("device")][F("name")] = _deviceName; if (_deviceConfigUrl) { - doc["device"]["configuration_url"] = _deviceConfigUrl; + doc[F("device")][F("configuration_url")] = _deviceConfigUrl; } - doc["enabled_by_default"] = enabledByDefault; - doc["unique_id"] = _prefix + "_dhw"; - doc["object_id"] = _prefix + "_dhw"; - doc["entity_category"] = "diagnostic"; - doc["device_class"] = "running"; - doc["name"] = "DHW"; - doc["icon"] = "mdi:water-pump"; - doc["state_topic"] = _prefix + F("/state"); - doc["value_template"] = F("{{ iif(value_json.states.dhw, 'ON', 'OFF') }}"); + doc[F("enabled_by_default")] = enabledByDefault; + doc[F("unique_id")] = _prefix + F("_dhw"); + doc[F("object_id")] = _prefix + F("_dhw"); + doc[F("entity_category")] = F("diagnostic"); + doc[F("device_class")] = F("running"); + doc[F("name")] = F("DHW"); + doc[F("icon")] = F("mdi:water-pump"); + doc[F("state_topic")] = _prefix + F("/state"); + doc[F("value_template")] = F("{{ iif(value_json.states.dhw, 'ON', 'OFF') }}"); - client.beginPublish((F("homeassistant/binary_sensor/") + _prefix + "/dhw/config").c_str(), measureJson(doc), true); + client.beginPublish((F("homeassistant/binary_sensor/") + _prefix + F("/dhw/config")).c_str(), measureJson(doc), true); //BufferingPrint bufferedClient(client, 32); //serializeJson(doc, bufferedClient); //bufferedClient.flush(); @@ -831,26 +864,26 @@ public: bool publishBinSensorFlame(bool enabledByDefault = true) { StaticJsonDocument<1536> doc; - doc["availability"]["topic"] = _prefix + F("/status"); - doc["device"]["identifiers"][0] = _prefix; - doc["device"]["sw_version"] = _deviceVersion; - doc["device"]["manufacturer"] = _deviceManufacturer; - doc["device"]["model"] = _deviceModel; - doc["device"]["name"] = _deviceName; + doc[F("availability")][F("topic")] = _prefix + F("/status"); + doc[F("device")][F("identifiers")][0] = _prefix; + doc[F("device")][F("sw_version")] = _deviceVersion; + doc[F("device")][F("manufacturer")] = _deviceManufacturer; + doc[F("device")][F("model")] = _deviceModel; + doc[F("device")][F("name")] = _deviceName; if (_deviceConfigUrl) { - doc["device"]["configuration_url"] = _deviceConfigUrl; + doc[F("device")][F("configuration_url")] = _deviceConfigUrl; } - doc["enabled_by_default"] = enabledByDefault; - doc["unique_id"] = _prefix + "_flame"; - doc["object_id"] = _prefix + "_flame"; - doc["entity_category"] = "diagnostic"; - doc["device_class"] = "running"; - doc["name"] = "Flame"; - doc["icon"] = "mdi:fire"; - doc["state_topic"] = _prefix + F("/state"); - doc["value_template"] = F("{{ iif(value_json.states.flame, 'ON', 'OFF') }}"); + doc[F("enabled_by_default")] = enabledByDefault; + doc[F("unique_id")] = _prefix + F("_flame"); + doc[F("object_id")] = _prefix + F("_flame"); + doc[F("entity_category")] = F("diagnostic"); + doc[F("device_class")] = F("running"); + doc[F("name")] = F("Flame"); + doc[F("icon")] = F("mdi:fire"); + doc[F("state_topic")] = _prefix + F("/state"); + doc[F("value_template")] = F("{{ iif(value_json.states.flame, 'ON', 'OFF') }}"); - client.beginPublish((F("homeassistant/binary_sensor/") + _prefix + "/flame/config").c_str(), measureJson(doc), true); + client.beginPublish((F("homeassistant/binary_sensor/") + _prefix + F("/flame/config")).c_str(), measureJson(doc), true); //BufferingPrint bufferedClient(client, 32); //serializeJson(doc, bufferedClient); //bufferedClient.flush(); @@ -860,27 +893,27 @@ public: bool publishBinSensorFault(bool enabledByDefault = true) { StaticJsonDocument<1536> doc; - doc["availability"]["topic"] = _prefix + F("/state"); - doc["availability"]["value_template"] = F("{{ iif(value_json.states.otStatus, 'online', 'offline') }}"); - doc["device"]["identifiers"][0] = _prefix; - doc["device"]["sw_version"] = _deviceVersion; - doc["device"]["manufacturer"] = _deviceManufacturer; - doc["device"]["model"] = _deviceModel; - doc["device"]["name"] = _deviceName; + doc[F("availability")][F("topic")] = _prefix + F("/state"); + doc[F("availability")][F("value_template")] = F("{{ iif(value_json.states.otStatus, 'online', 'offline') }}"); + doc[F("device")][F("identifiers")][0] = _prefix; + doc[F("device")][F("sw_version")] = _deviceVersion; + doc[F("device")][F("manufacturer")] = _deviceManufacturer; + doc[F("device")][F("model")] = _deviceModel; + doc[F("device")][F("name")] = _deviceName; if (_deviceConfigUrl) { - doc["device"]["configuration_url"] = _deviceConfigUrl; + doc[F("device")][F("configuration_url")] = _deviceConfigUrl; } - doc["enabled_by_default"] = enabledByDefault; - doc["unique_id"] = _prefix + "_fault"; - doc["object_id"] = _prefix + "_fault"; - doc["entity_category"] = "diagnostic"; - doc["device_class"] = "problem"; - doc["name"] = "Fault"; - doc["icon"] = "mdi:water-boiler-alert"; - doc["state_topic"] = _prefix + F("/state"); - doc["value_template"] = "{{ iif(value_json.states.fault, 'ON', 'OFF') }}"; + doc[F("enabled_by_default")] = enabledByDefault; + doc[F("unique_id")] = _prefix + F("_fault"); + doc[F("object_id")] = _prefix + F("_fault"); + doc[F("entity_category")] = F("diagnostic"); + doc[F("device_class")] = F("problem"); + doc[F("name")] = F("Fault"); + doc[F("icon")] = F("mdi:water-boiler-alert"); + doc[F("state_topic")] = _prefix + F("/state"); + doc[F("value_template")] = F("{{ iif(value_json.states.fault, 'ON', 'OFF') }}"); - client.beginPublish((F("homeassistant/binary_sensor/") + _prefix + "/fault/config").c_str(), measureJson(doc), true); + client.beginPublish((F("homeassistant/binary_sensor/") + _prefix + F("/fault/config")).c_str(), measureJson(doc), true); //BufferingPrint bufferedClient(client, 32); //serializeJson(doc, bufferedClient); //bufferedClient.flush(); @@ -890,26 +923,26 @@ public: bool publishBinSensorDiagnostic(bool enabledByDefault = true) { StaticJsonDocument<1536> doc; - doc["availability"]["topic"] = _prefix + F("/status"); - doc["device"]["identifiers"][0] = _prefix; - doc["device"]["sw_version"] = _deviceVersion; - doc["device"]["manufacturer"] = _deviceManufacturer; - doc["device"]["model"] = _deviceModel; - doc["device"]["name"] = _deviceName; + doc[F("availability")][F("topic")] = _prefix + F("/status"); + doc[F("device")][F("identifiers")][0] = _prefix; + doc[F("device")][F("sw_version")] = _deviceVersion; + doc[F("device")][F("manufacturer")] = _deviceManufacturer; + doc[F("device")][F("model")] = _deviceModel; + doc[F("device")][F("name")] = _deviceName; if (_deviceConfigUrl) { - doc["device"]["configuration_url"] = _deviceConfigUrl; + doc[F("device")][F("configuration_url")] = _deviceConfigUrl; } - doc["enabled_by_default"] = enabledByDefault; - doc["unique_id"] = _prefix + "_diagnostic"; - doc["object_id"] = _prefix + "_diagnostic"; - doc["entity_category"] = "diagnostic"; - doc["device_class"] = "problem"; - doc["name"] = "Diagnostic"; - doc["icon"] = "mdi:account-wrench"; - doc["state_topic"] = _prefix + F("/state"); - doc["value_template"] = "{{ iif(value_json.states.diagnostic, 'ON', 'OFF') }}"; + doc[F("enabled_by_default")] = enabledByDefault; + doc[F("unique_id")] = _prefix + F("_diagnostic"); + doc[F("object_id")] = _prefix + F("_diagnostic"); + doc[F("entity_category")] = F("diagnostic"); + doc[F("device_class")] = F("problem"); + doc[F("name")] = F("Diagnostic"); + doc[F("icon")] = F("mdi:account-wrench"); + doc[F("state_topic")] = _prefix + F("/state"); + doc[F("value_template")] = F("{{ iif(value_json.states.diagnostic, 'ON', 'OFF') }}"); - client.beginPublish((F("homeassistant/binary_sensor/") + _prefix + "/diagnostic/config").c_str(), measureJson(doc), true); + client.beginPublish((F("homeassistant/binary_sensor/") + _prefix + F("/diagnostic/config")).c_str(), measureJson(doc), true); //BufferingPrint bufferedClient(client, 32); //serializeJson(doc, bufferedClient); //bufferedClient.flush(); @@ -919,8 +952,35 @@ public: bool publishSensorFaultCode(bool enabledByDefault = true) { StaticJsonDocument<1536> doc; - doc["availability"]["topic"] = _prefix + F("/state"); - doc["availability"]["value_template"] = F("{{ iif(value_json.states.fault, 'online', 'offline') }}"); + doc[F("availability")][F("topic")] = _prefix + F("/state"); + doc[F("availability")][F("value_template")] = F("{{ iif(value_json.states.fault, 'online', 'offline') }}"); + doc[F("device")][F("identifiers")][0] = _prefix; + doc[F("device")][F("sw_version")] = _deviceVersion; + doc[F("device")][F("manufacturer")] = _deviceManufacturer; + doc[F("device")][F("model")] = _deviceModel; + doc[F("device")][F("name")] = _deviceName; + if (_deviceConfigUrl) { + doc[F("device")][F("configuration_url")] = _deviceConfigUrl; + } + doc[F("enabled_by_default")] = enabledByDefault; + doc[F("unique_id")] = _prefix + F("_fault_code"); + doc[F("object_id")] = _prefix + F("_fault_code"); + doc[F("entity_category")] = F("diagnostic"); + doc[F("name")] = F("Fault code"); + doc[F("icon")] = F("mdi:chat-alert-outline"); + doc[F("state_topic")] = _prefix + F("/state"); + doc[F("value_template")] = F("{{ \"E%02d\"|format(value_json.states.faultCode) }}"); + + client.beginPublish((F("homeassistant/sensor/") + _prefix + F("/fault_code/config")).c_str(), measureJson(doc), true); + //BufferingPrint bufferedClient(client, 32); + //serializeJson(doc, bufferedClient); + //bufferedClient.flush(); + serializeJson(doc, client); + return client.endPublish(); + } + + bool publishSensorRssi(bool enabledByDefault = false) { + StaticJsonDocument<1536> doc; doc["device"]["identifiers"][0] = _prefix; doc["device"]["sw_version"] = _deviceVersion; doc["device"]["manufacturer"] = _deviceManufacturer; @@ -929,16 +989,20 @@ public: if (_deviceConfigUrl) { doc["device"]["configuration_url"] = _deviceConfigUrl; } - doc["enabled_by_default"] = enabledByDefault; - doc["unique_id"] = _prefix + "_fault_code"; - doc["object_id"] = _prefix + "_fault_code"; - doc["entity_category"] = "diagnostic"; - doc["name"] = "Fault code"; - doc["icon"] = "mdi:chat-alert-outline"; - doc["state_topic"] = _prefix + F("/state"); - doc["value_template"] = F("{{ \"E%02d\"|format(value_json.states.faultCode) }}"); - client.beginPublish((F("homeassistant/sensor/") + _prefix + "/fault_code/config").c_str(), measureJson(doc), true); + doc["enabled_by_default"] = enabledByDefault; + doc["unique_id"] = _prefix + "_rssi"; + doc["object_id"] = _prefix + "_rssi"; + doc["entity_category"] = "diagnostic"; + doc["device_class"] = "signal_strength"; + doc["state_class"] = "measurement"; + doc["unit_of_measurement"] = "dBm"; + doc["name"] = "RSSI"; + doc["icon"] = "mdi:signal"; + doc["state_topic"] = _prefix + F("/state"); + doc["value_template"] = "{{ value_json.states.rssi|float(0)|round(1) }}"; + + client.beginPublish((F("homeassistant/sensor/") + _prefix + "/rssi/config").c_str(), measureJson(doc), true); //BufferingPrint bufferedClient(client, 32); //serializeJson(doc, bufferedClient); //bufferedClient.flush(); @@ -949,28 +1013,28 @@ public: bool publishSensorModulation(bool enabledByDefault = true) { StaticJsonDocument<1536> doc; - doc["availability"]["topic"] = _prefix + F("/status"); - doc["device"]["identifiers"][0] = _prefix; - doc["device"]["sw_version"] = _deviceVersion; - doc["device"]["manufacturer"] = _deviceManufacturer; - doc["device"]["model"] = _deviceModel; - doc["device"]["name"] = _deviceName; + doc[F("availability")][F("topic")] = _prefix + F("/status"); + doc[F("device")][F("identifiers")][0] = _prefix; + doc[F("device")][F("sw_version")] = _deviceVersion; + doc[F("device")][F("manufacturer")] = _deviceManufacturer; + doc[F("device")][F("model")] = _deviceModel; + doc[F("device")][F("name")] = _deviceName; if (_deviceConfigUrl) { - doc["device"]["configuration_url"] = _deviceConfigUrl; + doc[F("device")][F("configuration_url")] = _deviceConfigUrl; } - doc["enabled_by_default"] = enabledByDefault; - doc["unique_id"] = _prefix + "_modulation_level"; - doc["object_id"] = _prefix + "_modulation_level"; - doc["entity_category"] = "diagnostic"; - doc["device_class"] = "power_factor"; - doc["state_class"] = "measurement"; - doc["unit_of_measurement"] = "%"; - doc["name"] = "Modulation level"; - doc["icon"] = "mdi:fire-circle"; - doc["state_topic"] = _prefix + F("/state"); - doc["value_template"] = "{{ value_json.sensors.modulation|float(0)|round(0) }}"; + doc[F("enabled_by_default")] = enabledByDefault; + doc[F("unique_id")] = _prefix + F("_modulation_level"); + doc[F("object_id")] = _prefix + F("_modulation_level"); + doc[F("entity_category")] = F("diagnostic"); + doc[F("device_class")] = F("power_factor"); + doc[F("state_class")] = F("measurement"); + doc[F("unit_of_measurement")] = F("%"); + doc[F("name")] = F("Modulation level"); + doc[F("icon")] = F("mdi:fire-circle"); + doc[F("state_topic")] = _prefix + F("/state"); + doc[F("value_template")] = F("{{ value_json.sensors.modulation|float(0)|round(0) }}"); - client.beginPublish((F("homeassistant/sensor/") + _prefix + "/modulation/config").c_str(), measureJson(doc), true); + client.beginPublish((F("homeassistant/sensor/") + _prefix + F("/modulation/config")).c_str(), measureJson(doc), true); //BufferingPrint bufferedClient(client, 32); //serializeJson(doc, bufferedClient); //bufferedClient.flush(); @@ -980,28 +1044,28 @@ public: bool publishSensorPressure(bool enabledByDefault = true) { StaticJsonDocument<1536> doc; - doc["availability"]["topic"] = _prefix + F("/status"); - doc["device"]["identifiers"][0] = _prefix; - doc["device"]["sw_version"] = _deviceVersion; - doc["device"]["manufacturer"] = _deviceManufacturer; - doc["device"]["model"] = _deviceModel; - doc["device"]["name"] = _deviceName; + doc[F("availability")][F("topic")] = _prefix + F("/status"); + doc[F("device")][F("identifiers")][0] = _prefix; + doc[F("device")][F("sw_version")] = _deviceVersion; + doc[F("device")][F("manufacturer")] = _deviceManufacturer; + doc[F("device")][F("model")] = _deviceModel; + doc[F("device")][F("name")] = _deviceName; if (_deviceConfigUrl) { - doc["device"]["configuration_url"] = _deviceConfigUrl; + doc[F("device")][F("configuration_url")] = _deviceConfigUrl; } - doc["enabled_by_default"] = enabledByDefault; - doc["unique_id"] = _prefix + "_pressure"; - doc["object_id"] = _prefix + "_pressure"; - doc["entity_category"] = "diagnostic"; - doc["device_class"] = "pressure"; - doc["state_class"] = "measurement"; - doc["unit_of_measurement"] = "bar"; - doc["name"] = "Pressure"; - doc["icon"] = "mdi:gauge"; - doc["state_topic"] = _prefix + F("/state"); - doc["value_template"] = "{{ value_json.sensors.pressure|float(0)|round(2) }}"; + doc[F("enabled_by_default")] = enabledByDefault; + doc[F("unique_id")] = _prefix + F("_pressure"); + doc[F("object_id")] = _prefix + F("_pressure"); + doc[F("entity_category")] = F("diagnostic"); + doc[F("device_class")] = F("pressure"); + doc[F("state_class")] = F("measurement"); + doc[F("unit_of_measurement")] = F("bar"); + doc[F("name")] = F("Pressure"); + doc[F("icon")] = F("mdi:gauge"); + doc[F("state_topic")] = _prefix + F("/state"); + doc[F("value_template")] = F("{{ value_json.sensors.pressure|float(0)|round(2) }}"); - client.beginPublish((F("homeassistant/sensor/") + _prefix + "/pressure/config").c_str(), measureJson(doc), true); + client.beginPublish((F("homeassistant/sensor/") + _prefix + F("/pressure/config")).c_str(), measureJson(doc), true); //BufferingPrint bufferedClient(client, 32); //serializeJson(doc, bufferedClient); //bufferedClient.flush(); @@ -1012,31 +1076,31 @@ public: bool publishNumberIndoorTemp(bool enabledByDefault = true) { StaticJsonDocument<1536> doc; - //doc["availability"]["topic"] = _prefix + F("/status"); - doc["device"]["identifiers"][0] = _prefix; - doc["device"]["sw_version"] = _deviceVersion; - doc["device"]["manufacturer"] = _deviceManufacturer; - doc["device"]["model"] = _deviceModel; - doc["device"]["name"] = _deviceName; + //doc[F("availability")][F("topic")] = _prefix + F("/status"); + doc[F("device")][F("identifiers")][0] = _prefix; + doc[F("device")][F("sw_version")] = _deviceVersion; + doc[F("device")][F("manufacturer")] = _deviceManufacturer; + doc[F("device")][F("model")] = _deviceModel; + doc[F("device")][F("name")] = _deviceName; if (_deviceConfigUrl) { - doc["device"]["configuration_url"] = _deviceConfigUrl; + doc[F("device")][F("configuration_url")] = _deviceConfigUrl; } - doc["enabled_by_default"] = enabledByDefault; - doc["unique_id"] = _prefix + "_indoor_temp"; - doc["object_id"] = _prefix + "_indoor_temp"; - doc["entity_category"] = "config"; - //doc["entity_registry_visible_default"] = false; - doc["unit_of_measurement"] = "°C"; - doc["name"] = "Indoor temperature"; - doc["icon"] = "mdi:home-thermometer"; - doc["state_topic"] = _prefix + F("/state"); - doc["value_template"] = "{{ value_json.temperatures.indoor|float(0)|round(1) }}"; - doc["command_topic"] = _prefix + "/state/set"; - doc["command_template"] = "{\"temperatures\": {\"indoor\":{{ value }}}}"; - doc["min"] = -70; - doc["max"] = 50; + doc[F("enabled_by_default")] = enabledByDefault; + doc[F("unique_id")] = _prefix + F("_indoor_temp"); + doc[F("object_id")] = _prefix + F("_indoor_temp"); + doc[F("entity_category")] = F("config"); + //doc[F("entity_registry_visible_default")] = false; + doc[F("unit_of_measurement")] = F("°C"); + doc[F("name")] = F("Indoor temperature"); + doc[F("icon")] = F("mdi:home-thermometer"); + doc[F("state_topic")] = _prefix + F("/state"); + doc[F("value_template")] = F("{{ value_json.temperatures.indoor|float(0)|round(1) }}"); + doc[F("command_topic")] = _prefix + F("/state/set"); + doc[F("command_template")] = F("{\"temperatures\": {\"indoor\":{{ value }}}}"); + doc[F("min")] = -70; + doc[F("max")] = 50; - client.beginPublish((F("homeassistant/number/") + _prefix + "/indoor_temp/config").c_str(), measureJson(doc), true); + client.beginPublish((F("homeassistant/number/") + _prefix + F("/indoor_temp/config")).c_str(), measureJson(doc), true); //BufferingPrint bufferedClient(client, 32); //serializeJson(doc, bufferedClient); //bufferedClient.flush(); @@ -1046,30 +1110,30 @@ public: bool publishNumberOutdoorTemp(bool enabledByDefault = true) { StaticJsonDocument<1536> doc; - doc["device"]["identifiers"][0] = _prefix; - doc["device"]["sw_version"] = _deviceVersion; - doc["device"]["manufacturer"] = _deviceManufacturer; - doc["device"]["model"] = _deviceModel; - doc["device"]["name"] = _deviceName; + doc[F("device")][F("identifiers")][0] = _prefix; + doc[F("device")][F("sw_version")] = _deviceVersion; + doc[F("device")][F("manufacturer")] = _deviceManufacturer; + doc[F("device")][F("model")] = _deviceModel; + doc[F("device")][F("name")] = _deviceName; if (_deviceConfigUrl) { - doc["device"]["configuration_url"] = _deviceConfigUrl; + doc[F("device")][F("configuration_url")] = _deviceConfigUrl; } - doc["enabled_by_default"] = enabledByDefault; - doc["unique_id"] = _prefix + "_outdoor_temp"; - doc["object_id"] = _prefix + "_outdoor_temp"; - doc["entity_category"] = "config"; - //doc["entity_registry_visible_default"] = false; - doc["unit_of_measurement"] = "°C"; - doc["name"] = "Outdoor temperature"; - doc["icon"] = "mdi:home-thermometer-outline"; - doc["state_topic"] = _prefix + F("/state"); - doc["value_template"] = "{{ value_json.temperatures.outdoor|float(0)|round(1) }}"; - doc["command_topic"] = _prefix + "/state/set"; - doc["command_template"] = "{\"temperatures\": {\"outdoor\":{{ value }}}}"; - doc["min"] = -70; - doc["max"] = 50; + doc[F("enabled_by_default")] = enabledByDefault; + doc[F("unique_id")] = _prefix + F("_outdoor_temp"); + doc[F("object_id")] = _prefix + F("_outdoor_temp"); + doc[F("entity_category")] = F("config"); + //doc[F("entity_registry_visible_default")] = false; + doc[F("unit_of_measurement")] = F("°C"); + doc[F("name")] = F("Outdoor temperature"); + doc[F("icon")] = F("mdi:home-thermometer-outline"); + doc[F("state_topic")] = _prefix + F("/state"); + doc[F("value_template")] = F("{{ value_json.temperatures.outdoor|float(0)|round(1) }}"); + doc[F("command_topic")] = _prefix + F("/state/set"); + doc[F("command_template")] = F("{\"temperatures\": {\"outdoor\":{{ value }}}}"); + doc[F("min")] = -70; + doc[F("max")] = 50; - client.beginPublish((F("homeassistant/number/") + _prefix + "/outdoor_temp/config").c_str(), measureJson(doc), true); + client.beginPublish((F("homeassistant/number/") + _prefix + F("/outdoor_temp/config")).c_str(), measureJson(doc), true); //BufferingPrint bufferedClient(client, 32); //serializeJson(doc, bufferedClient); //bufferedClient.flush(); @@ -1079,31 +1143,31 @@ public: bool publishSensorOutdoorTemp(bool enabledByDefault = true) { StaticJsonDocument<1536> doc; - doc["availability"][0]["topic"] = _prefix + F("/status"); - doc["availability"][1]["topic"] = _prefix + F("/settings"); - doc["availability"][1]["value_template"] = F("{{ iif(value_json.outdoorTempSource == 2, 'online', 'offline') }}"); - doc["availability_mode"] = "any"; - doc["device"]["identifiers"][0] = _prefix; - doc["device"]["sw_version"] = _deviceVersion; - doc["device"]["manufacturer"] = _deviceManufacturer; - doc["device"]["model"] = _deviceModel; - doc["device"]["name"] = _deviceName; + doc[F("availability")][0][F("topic")] = _prefix + F("/status"); + doc[F("availability")][1][F("topic")] = _prefix + F("/settings"); + doc[F("availability")][1][F("value_template")] = F("{{ iif(value_json.outdoorTempSource == 2, 'online', 'offline') }}"); + doc[F("availability_mode")] = F("any"); + doc[F("device")][F("identifiers")][0] = _prefix; + doc[F("device")][F("sw_version")] = _deviceVersion; + doc[F("device")][F("manufacturer")] = _deviceManufacturer; + doc[F("device")][F("model")] = _deviceModel; + doc[F("device")][F("name")] = _deviceName; if (_deviceConfigUrl) { - doc["device"]["configuration_url"] = _deviceConfigUrl; + doc[F("device")][F("configuration_url")] = _deviceConfigUrl; } - doc["enabled_by_default"] = enabledByDefault; - doc["unique_id"] = _prefix + "_outdoor_temp"; - doc["object_id"] = _prefix + "_outdoor_temp"; - doc["entity_category"] = "diagnostic"; - doc["device_class"] = "temperature"; - doc["state_class"] = "measurement"; - doc["unit_of_measurement"] = "°C"; - doc["name"] = "Outdoor temperature"; - doc["icon"] = "mdi:home-thermometer-outline"; - doc["state_topic"] = _prefix + F("/state"); - doc["value_template"] = "{{ value_json.temperatures.outdoor|float(0)|round(1) }}"; + doc[F("enabled_by_default")] = enabledByDefault; + doc[F("unique_id")] = _prefix + F("_outdoor_temp"); + doc[F("object_id")] = _prefix + F("_outdoor_temp"); + doc[F("entity_category")] = F("diagnostic"); + doc[F("device_class")] = F("temperature"); + doc[F("state_class")] = F("measurement"); + doc[F("unit_of_measurement")] = F("°C"); + doc[F("name")] = F("Outdoor temperature"); + doc[F("icon")] = F("mdi:home-thermometer-outline"); + doc[F("state_topic")] = _prefix + F("/state"); + doc[F("value_template")] = F("{{ value_json.temperatures.outdoor|float(0)|round(1) }}"); - client.beginPublish((F("homeassistant/sensor/") + _prefix + "/outdoor_temp/config").c_str(), measureJson(doc), true); + client.beginPublish((F("homeassistant/sensor/") + _prefix + F("/outdoor_temp/config")).c_str(), measureJson(doc), true); //BufferingPrint bufferedClient(client, 32); //serializeJson(doc, bufferedClient); //bufferedClient.flush(); @@ -1113,28 +1177,28 @@ public: bool publishSensorHeatingTemp(bool enabledByDefault = true) { StaticJsonDocument<1536> doc; - doc["availability"]["topic"] = _prefix + F("/status"); - doc["device"]["identifiers"][0] = _prefix; - doc["device"]["sw_version"] = _deviceVersion; - doc["device"]["manufacturer"] = _deviceManufacturer; - doc["device"]["model"] = _deviceModel; - doc["device"]["name"] = _deviceName; + doc[F("availability")][F("topic")] = _prefix + F("/status"); + doc[F("device")][F("identifiers")][0] = _prefix; + doc[F("device")][F("sw_version")] = _deviceVersion; + doc[F("device")][F("manufacturer")] = _deviceManufacturer; + doc[F("device")][F("model")] = _deviceModel; + doc[F("device")][F("name")] = _deviceName; if (_deviceConfigUrl) { - doc["device"]["configuration_url"] = _deviceConfigUrl; + doc[F("device")][F("configuration_url")] = _deviceConfigUrl; } - doc["enabled_by_default"] = enabledByDefault; - doc["unique_id"] = _prefix + "_heating_temp"; - doc["object_id"] = _prefix + "_heating_temp"; - doc["entity_category"] = "diagnostic"; - doc["device_class"] = "temperature"; - doc["state_class"] = "measurement"; - doc["unit_of_measurement"] = "°C"; - doc["name"] = "Heating temperature"; - doc["icon"] = "mdi:radiator"; - doc["state_topic"] = _prefix + F("/state"); - doc["value_template"] = "{{ value_json.temperatures.heating|float(0)|round(2) }}"; + doc[F("enabled_by_default")] = enabledByDefault; + doc[F("unique_id")] = _prefix + F("_heating_temp"); + doc[F("object_id")] = _prefix + F("_heating_temp"); + doc[F("entity_category")] = F("diagnostic"); + doc[F("device_class")] = F("temperature"); + doc[F("state_class")] = F("measurement"); + doc[F("unit_of_measurement")] = F("°C"); + doc[F("name")] = F("Heating temperature"); + doc[F("icon")] = F("mdi:radiator"); + doc[F("state_topic")] = _prefix + F("/state"); + doc[F("value_template")] = F("{{ value_json.temperatures.heating|float(0)|round(2) }}"); - client.beginPublish((F("homeassistant/sensor/") + _prefix + "/heating_temp/config").c_str(), measureJson(doc), true); + client.beginPublish((F("homeassistant/sensor/") + _prefix + F("/heating_temp/config")).c_str(), measureJson(doc), true); //BufferingPrint bufferedClient(client, 32); //serializeJson(doc, bufferedClient); //bufferedClient.flush(); @@ -1144,28 +1208,28 @@ public: bool publishSensorDHWTemp(bool enabledByDefault = true) { StaticJsonDocument<1536> doc; - doc["availability"]["topic"] = _prefix + F("/status"); - doc["device"]["identifiers"][0] = _prefix; - doc["device"]["sw_version"] = _deviceVersion; - doc["device"]["manufacturer"] = _deviceManufacturer; - doc["device"]["model"] = _deviceModel; - doc["device"]["name"] = _deviceName; + doc[F("availability")][F("topic")] = _prefix + F("/status"); + doc[F("device")][F("identifiers")][0] = _prefix; + doc[F("device")][F("sw_version")] = _deviceVersion; + doc[F("device")][F("manufacturer")] = _deviceManufacturer; + doc[F("device")][F("model")] = _deviceModel; + doc[F("device")][F("name")] = _deviceName; if (_deviceConfigUrl) { - doc["device"]["configuration_url"] = _deviceConfigUrl; + doc[F("device")][F("configuration_url")] = _deviceConfigUrl; } - doc["enabled_by_default"] = enabledByDefault; - doc["unique_id"] = _prefix + "_dhw_temp"; - doc["object_id"] = _prefix + "_dhw_temp"; - doc["entity_category"] = "diagnostic"; - doc["device_class"] = "temperature"; - doc["state_class"] = "measurement"; - doc["unit_of_measurement"] = "°C"; - doc["name"] = "DHW temperature"; - doc["icon"] = "mdi:water-pump"; - doc["state_topic"] = _prefix + F("/state"); - doc["value_template"] = "{{ value_json.temperatures.dhw|float(0)|round(2) }}"; + doc[F("enabled_by_default")] = enabledByDefault; + doc[F("unique_id")] = _prefix + F("_dhw_temp"); + doc[F("object_id")] = _prefix + F("_dhw_temp"); + doc[F("entity_category")] = F("diagnostic"); + doc[F("device_class")] = F("temperature"); + doc[F("state_class")] = F("measurement"); + doc[F("unit_of_measurement")] = F("°C"); + doc[F("name")] = F("DHW temperature"); + doc[F("icon")] = F("mdi:water-pump"); + doc[F("state_topic")] = _prefix + F("/state"); + doc[F("value_template")] = F("{{ value_json.temperatures.dhw|float(0)|round(2) }}"); - client.beginPublish((F("homeassistant/sensor/") + _prefix + "/dhw_temp/config").c_str(), measureJson(doc), true); + client.beginPublish((F("homeassistant/sensor/") + _prefix + F("/dhw_temp/config")).c_str(), measureJson(doc), true); //BufferingPrint bufferedClient(client, 32); //serializeJson(doc, bufferedClient); //bufferedClient.flush(); @@ -1175,43 +1239,55 @@ public: bool publishClimateHeating(byte minTemp = 20, byte maxTemp = 90, bool enabledByDefault = true) { - StaticJsonDocument<1536> doc; - doc["availability"]["topic"] = _prefix + F("/status"); - doc["device"]["identifiers"][0] = _prefix; - doc["device"]["sw_version"] = _deviceVersion; - doc["device"]["manufacturer"] = _deviceManufacturer; - doc["device"]["model"] = _deviceModel; - doc["device"]["name"] = _deviceName; + StaticJsonDocument<2048> doc; + doc[F("availability")][F("topic")] = _prefix + F("/status"); + doc[F("device")][F("identifiers")][0] = _prefix; + doc[F("device")][F("sw_version")] = _deviceVersion; + doc[F("device")][F("manufacturer")] = _deviceManufacturer; + doc[F("device")][F("model")] = _deviceModel; + doc[F("device")][F("name")] = _deviceName; if (_deviceConfigUrl) { - doc["device"]["configuration_url"] = _deviceConfigUrl; + doc[F("device")][F("configuration_url")] = _deviceConfigUrl; } - doc["enabled_by_default"] = enabledByDefault; - doc["unique_id"] = _prefix + "_heating"; - doc["object_id"] = _prefix + "_heating"; - doc["name"] = "Heating"; - doc["icon"] = "mdi:radiator"; + doc[F("enabled_by_default")] = enabledByDefault; + doc[F("unique_id")] = _prefix + F("_heating"); + doc[F("object_id")] = _prefix + F("_heating"); + doc[F("name")] = F("Heating"); + doc[F("icon")] = F("mdi:radiator"); - doc["current_temperature_topic"] = _prefix + F("/state"); - doc["value_template"] = F("{{ value_json.temperatures.indoor|float(0)|round(2) }}"); + doc[F("current_temperature_topic")] = _prefix + F("/state"); + doc[F("current_temperature_template")] = F("{% if value_json.temperatures.indoor|float(0) != 0 %}{{ value_json.temperatures.indoor|float(0)|round(2) }}" + "{% else %}{{ value_json.temperatures.heating|float(0)|round(2) }}{% endif %}"); - doc["temperature_command_topic"] = _prefix + "/settings/set"; - doc["temperature_command_template"] = "{\"heating\": {\"target\" : {{ value }}}}"; + doc[F("temperature_command_topic")] = _prefix + F("/settings/set"); + doc[F("temperature_command_template")] = F("{\"heating\": {\"target\" : {{ value }}}}"); - doc["temperature_state_topic"] = _prefix + F("/settings"); - doc["temperature_state_template"] = F("{{ value_json.heating.target|float(0)|round(1) }}"); + doc[F("temperature_state_topic")] = _prefix + F("/settings"); + doc[F("temperature_state_template")] = F("{{ value_json.heating.target|float(0)|round(1) }}"); - doc["mode_command_topic"] = _prefix + "/settings/set"; - doc["mode_command_template"] = F("{% if value == 'heat' %}{\"heating\": {\"enable\" : true}}" + doc[F("mode_command_topic")] = _prefix + F("/settings/set"); + doc[F("mode_command_template")] = F("{% if value == 'heat' %}{\"heating\": {\"enable\" : true}}" "{% elif value == 'off' %}{\"heating\": {\"enable\" : false}}{% endif %}"); - doc["mode_state_topic"] = _prefix + F("/settings"); - doc["mode_state_template"] = F("{{ iif(value_json.heating.enable, 'heat', 'off') }}"); - doc["modes"][0] = "off"; - doc["modes"][1] = "heat"; - doc["min_temp"] = minTemp; - doc["max_temp"] = maxTemp; - doc["temp_step"] = 0.5; + doc[F("mode_state_topic")] = _prefix + F("/settings"); + doc[F("mode_state_template")] = F("{{ iif(value_json.heating.enable, 'heat', 'off') }}"); + doc[F("modes")][0] = F("off"); + doc[F("modes")][1] = F("heat"); - client.beginPublish((F("homeassistant/climate/") + _prefix + "_heating/config").c_str(), measureJson(doc), true); + doc[F("action_topic")] = _prefix + F("/state"); + doc[F("action_template")] = F("{{ iif(value_json.states.heating, 'heating', 'idle') }}"); + + doc[F("preset_mode_command_topic")] = _prefix + F("/settings/set"); + doc[F("preset_mode_command_template")] = F("{% if value == 'boost' %}{\"heating\": {\"turbo\" : true}}" + "{% elif value == 'none' %}{\"heating\": {\"turbo\" : false}}{% endif %}"); + doc[F("preset_mode_state_topic")] = _prefix + F("/settings"); + doc[F("preset_mode_value_template")] = F("{{ iif(value_json.heating.turbo, 'boost', 'none') }}"); + doc[F("preset_modes")][0] = F("boost"); + + doc[F("min_temp")] = minTemp; + doc[F("max_temp")] = maxTemp; + doc[F("temp_step")] = 0.5; + + client.beginPublish((F("homeassistant/climate/") + _prefix + F("_heating/config")).c_str(), measureJson(doc), true); //BufferingPrint bufferedClient(client, 32); //serializeJson(doc, bufferedClient); //bufferedClient.flush(); @@ -1221,42 +1297,46 @@ public: bool publishClimateDHW(byte minTemp = 40, byte maxTemp = 60, bool enabledByDefault = true) { StaticJsonDocument<1536> doc; - doc["availability"]["topic"] = _prefix + F("/status"); - doc["device"]["identifiers"][0] = _prefix; - doc["device"]["sw_version"] = _deviceVersion; - doc["device"]["manufacturer"] = _deviceManufacturer; - doc["device"]["model"] = _deviceModel; - doc["device"]["name"] = _deviceName; + doc[F("availability")][F("topic")] = _prefix + F("/status"); + doc[F("device")][F("identifiers")][0] = _prefix; + doc[F("device")][F("sw_version")] = _deviceVersion; + doc[F("device")][F("manufacturer")] = _deviceManufacturer; + doc[F("device")][F("model")] = _deviceModel; + doc[F("device")][F("name")] = _deviceName; if (_deviceConfigUrl) { - doc["device"]["configuration_url"] = _deviceConfigUrl; + doc[F("device")][F("configuration_url")] = _deviceConfigUrl; } - doc["enabled_by_default"] = enabledByDefault; - doc["unique_id"] = _prefix + "_dhw"; - doc["object_id"] = _prefix + "_dhw"; - doc["name"] = "DHW"; - doc["icon"] = "mdi:water-pump"; + doc[F("enabled_by_default")] = enabledByDefault; + doc[F("unique_id")] = _prefix + F("_dhw"); + doc[F("object_id")] = _prefix + F("_dhw"); + doc[F("name")] = F("DHW"); + doc[F("icon")] = F("mdi:water-pump"); - doc["current_temperature_topic"] = _prefix + F("/state"); - doc["value_template"] = F("{{ value_json.temperatures.dhw|float(0)|round(1) }}"); + doc[F("current_temperature_topic")] = _prefix + F("/state"); + doc[F("current_temperature_template")] = F("{{ value_json.temperatures.dhw|float(0)|round(1) }}"); - doc["temperature_command_topic"] = _prefix + "/settings/set"; - doc["temperature_command_template"] = "{\"dhw\": {\"target\" : {{ value|int(0) }}}}"; + doc[F("temperature_command_topic")] = _prefix + F("/settings/set"); + doc[F("temperature_command_template")] = F("{\"dhw\": {\"target\" : {{ value|int(0) }}}}"); - doc["temperature_state_topic"] = _prefix + F("/settings"); - doc["temperature_state_template"] = F("{{ value_json.dhw.target|int(0) }}"); + doc[F("temperature_state_topic")] = _prefix + F("/settings"); + doc[F("temperature_state_template")] = F("{{ value_json.dhw.target|int(0) }}"); - doc["mode_command_topic"] = _prefix + "/settings/set"; - doc["mode_command_template"] = F("{% if value == 'heat' %}{\"dhw\": {\"enable\" : true}}" + doc[F("mode_command_topic")] = _prefix + F("/settings/set"); + doc[F("mode_command_template")] = F("{% if value == 'heat' %}{\"dhw\": {\"enable\" : true}}" "{% elif value == 'off' %}{\"dhw\": {\"enable\" : false}}{% endif %}"); - doc["mode_state_topic"] = _prefix + F("/settings"); - doc["mode_state_template"] = F("{{ iif(value_json.dhw.enable, 'heat', 'off') }}"); - doc["modes"][0] = "off"; - doc["modes"][1] = "heat"; - doc["min_temp"] = minTemp; - doc["max_temp"] = maxTemp; + doc[F("mode_state_topic")] = _prefix + F("/settings"); + doc[F("mode_state_template")] = F("{{ iif(value_json.dhw.enable, 'heat', 'off') }}"); + doc[F("modes")][0] = F("off"); + doc[F("modes")][1] = F("heat"); - client.beginPublish((F("homeassistant/climate/") + _prefix + "_dhw/config").c_str(), measureJson(doc), true); + doc[F("action_topic")] = _prefix + F("/state"); + doc[F("action_template")] = F("{{ iif(value_json.states.dhw, 'heating', 'idle') }}"); + + doc[F("min_temp")] = minTemp; + doc[F("max_temp")] = maxTemp; + + client.beginPublish((F("homeassistant/climate/") + _prefix + F("_dhw/config")).c_str(), measureJson(doc), true); //BufferingPrint bufferedClient(client, 32); //serializeJson(doc, bufferedClient); //bufferedClient.flush(); @@ -1266,11 +1346,11 @@ public: bool deleteNumberOutdoorTemp() { - return client.publish((F("homeassistant/number/") + _prefix + "/outdoor_temp/config").c_str(), NULL, true); + return client.publish((F("homeassistant/number/") + _prefix + F("/outdoor_temp/config")).c_str(), NULL, true); } bool deleteSensorOutdoorTemp() { - return client.publish((F("homeassistant/sensor/") + _prefix + "/outdoor_temp/config").c_str(), NULL, true); + return client.publish((F("homeassistant/sensor/") + _prefix + F("/outdoor_temp/config")).c_str(), NULL, true); } private: diff --git a/src/MainTask.h b/src/MainTask.h index 2cafa75..26e8fc2 100644 --- a/src/MainTask.h +++ b/src/MainTask.h @@ -1,16 +1,22 @@ extern MqttTask* tMqtt; extern SensorsTask* tSensors; +extern OpenThermTask* tOt; -class MainTask : public Task { +class MainTask: public Task { public: - MainTask(bool _enabled = false, unsigned long _interval = 0) : Task(_enabled, _interval) {} + MainTask(bool _enabled = false, unsigned long _interval = 0): Task(_enabled, _interval) {} protected: + unsigned long lastHeapInfo = 0; + unsigned long firstFailConnect = 0; + unsigned short minFreeHeapSize = 65535; + + void setup() { + pinMode(LED_STATUS_PIN, OUTPUT); + //pinMode(LED_OT_RX_PIN, OUTPUT); + } void loop() { - static unsigned long lastHeapInfo = 0; - static unsigned short minFreeHeapSize = 65535; - if (eeSettings.tick()) { INFO("Settings updated (EEPROM)"); } @@ -20,12 +26,27 @@ protected: tMqtt->enable(); } + if ( firstFailConnect != 0 ) { + firstFailConnect = 0; + } + + vars.states.rssi = WiFi.RSSI(); + } else { if (tMqtt->isEnabled()) { tMqtt->disable(); } - vars.states.emergency = true; + if (settings.emergency.enable && !vars.states.emergency) { + if (firstFailConnect == 0) { + firstFailConnect = millis(); + } + + if (millis() - firstFailConnect > EMERGENCY_TIME_TRESHOLD) { + vars.states.emergency = true; + INFO("Emergency mode enabled"); + } + } } if (!tSensors->isEnabled() && settings.outdoorTempSource == 2) { @@ -34,6 +55,12 @@ protected: tSensors->disable(); } + if (!tOt->isEnabled() && settings.opentherm.inPin > 0 && settings.opentherm.outPin > 0 && settings.opentherm.inPin != settings.opentherm.outPin) { + tOt->enable(); + } + + ledStatus(); + #ifdef USE_TELNET yield(); @@ -58,4 +85,57 @@ protected: } } } + + void ledStatus() { + static byte blinkLeft = 0; + static bool ledOn = false; + static unsigned long changeTime = 0; + + byte errNo = 0; + if (!vars.states.otStatus) { + errNo = 1; + } else if (vars.states.fault) { + errNo = 2; + } else if (vars.states.emergency) { + errNo = 3; + } + + if (errNo == 0) { + if (!ledOn) { + digitalWrite(LED_STATUS_PIN, true); + ledOn = true; + } + + if (blinkLeft > 0) { + blinkLeft = 0; + } + + } else { + if (blinkLeft == 0) { + if (ledOn) { + digitalWrite(LED_STATUS_PIN, false); + ledOn = false; + changeTime = millis(); + } + + if (millis() - changeTime >= 3000) { + blinkLeft = errNo; + } + } + + if (blinkLeft > 0 && millis() - changeTime >= 500) { + if (ledOn) { + digitalWrite(LED_STATUS_PIN, false); + ledOn = false; + blinkLeft--; + + } else { + digitalWrite(LED_STATUS_PIN, true); + ledOn = true; + } + + changeTime = millis(); + } + } + } }; \ No newline at end of file diff --git a/src/MqttTask.h b/src/MqttTask.h index 75f8936..3114f3f 100644 --- a/src/MqttTask.h +++ b/src/MqttTask.h @@ -8,15 +8,17 @@ PubSubClient client(espClient); HomeAssistantHelper haHelper; -class MqttTask : public Task { +class MqttTask: public Task { public: - MqttTask(bool _enabled = false, unsigned long _interval = 0) : Task(_enabled, _interval) {} + MqttTask(bool _enabled = false, unsigned long _interval = 0): Task(_enabled, _interval) {} protected: unsigned long lastReconnectAttempt = 0; - unsigned short int reconnectAttempts = 0; + unsigned long firstFailConnect = 0; void setup() { + DEBUG("[MQTT] Started"); + client.setServer(settings.mqtt.server, settings.mqtt.port); client.setCallback(__callback); haHelper.setPrefix(settings.mqtt.prefix); @@ -38,15 +40,21 @@ protected: publishHaEntities(); publishNonStaticHaEntities(true); - reconnectAttempts = 0; + firstFailConnect = 0; lastReconnectAttempt = 0; } else { INFO("Failed to connect to MQTT server\n"); - if (!vars.states.emergency && ++reconnectAttempts >= EMERGENCY_TRESHOLD) { - vars.states.emergency = true; - INFO("Emergency mode enabled"); + if (settings.emergency.enable && !vars.states.emergency) { + if (firstFailConnect == 0) { + firstFailConnect = millis(); + } + + if (millis() - firstFailConnect > EMERGENCY_TIME_TRESHOLD) { + vars.states.emergency = true; + INFO("Emergency mode enabled"); + } } forceARP(); @@ -117,6 +125,11 @@ protected: flag = true; } + if (!doc["heating"]["turbo"].isNull() && doc["heating"]["turbo"].is()) { + settings.heating.turbo = doc["heating"]["turbo"].as(); + flag = true; + } + if (!doc["heating"]["target"].isNull() && (doc["heating"]["target"].is() || doc["heating"]["target"].is())) { settings.heating.target = round(doc["heating"]["target"].as() * 10) / 10; flag = true; @@ -221,7 +234,7 @@ protected: DEBUG("Received restart message..."); Scheduler.delay(10000); DEBUG("Restart..."); - + eeSettings.updateNow(); ESP.restart(); } @@ -272,6 +285,7 @@ protected: // heating haHelper.publishSwitchHeating(false); + haHelper.publishSwitchHeatingTurbo(); //haHelper.publishNumberHeatingTarget(false); haHelper.publishNumberHeatingHysteresis(); haHelper.publishSensorHeatingSetpoint(false); @@ -305,6 +319,7 @@ protected: haHelper.publishBinSensorFault(); haHelper.publishBinSensorDiagnostic(); haHelper.publishSensorFaultCode(); + haHelper.publishSensorRssi(); // sensors haHelper.publishSensorModulation(false); @@ -383,6 +398,7 @@ protected: doc["emergency"]["useEquitherm"] = settings.emergency.useEquitherm; doc["heating"]["enable"] = settings.heating.enable; + doc["heating"]["turbo"] = settings.heating.turbo; doc["heating"]["target"] = settings.heating.target; doc["heating"]["hysteresis"] = settings.heating.hysteresis; @@ -420,6 +436,7 @@ protected: doc["states"]["fault"] = vars.states.fault; doc["states"]["diagnostic"] = vars.states.diagnostic; doc["states"]["faultCode"] = vars.states.faultCode; + doc["states"]["rssi"] = vars.states.rssi; doc["sensors"]["modulation"] = vars.sensors.modulation; doc["sensors"]["pressure"] = vars.sensors.pressure; @@ -454,7 +471,7 @@ protected: if (settings.debug) { DEBUG_F("MQTT received message\n\r Topic: %s\n\r Data: ", topic); - for (int i = 0; i < length; i++) { + for (unsigned int i = 0; i < length; i++) { DEBUG_STREAM.print((char)payload[i]); } DEBUG_STREAM.print("\n"); diff --git a/src/OpenThermTask.h b/src/OpenThermTask.h index e5f3685..32fa8dc 100644 --- a/src/OpenThermTask.h +++ b/src/OpenThermTask.h @@ -1,107 +1,133 @@ -#include "lib/CustomOpenTherm.h" +#include +#include -CustomOpenTherm ot(OPENTHERM_IN_PIN, OPENTHERM_OUT_PIN); +CustomOpenTherm* ot; -class OpenThermTask : public Task { +class OpenThermTask: public Task { public: - OpenThermTask(bool _enabled = false, unsigned long _interval = 0) : Task(_enabled, _interval) {} + OpenThermTask(bool _enabled = false, unsigned long _interval = 0): Task(_enabled, _interval) {} protected: void setup() { - ot.begin(handleInterrupt, responseCallback); - ot.setHandleSendRequestCallback(sendRequestCallback); + ot = new CustomOpenTherm(settings.opentherm.inPin, settings.opentherm.outPin); + + ot->begin(handleInterrupt, responseCallback); + ot->setHandleSendRequestCallback(sendRequestCallback); + +#ifdef LED_OT_RX_PIN + pinMode(LED_OT_RX_PIN, OUTPUT); +#endif } void loop() { static byte currentHeatingTemp, currentDHWTemp = 0; - byte newHeatingTemp, newDHWTemp = 0; unsigned long localResponse; - setMasterMemberIdCode(); - DEBUG_F("Slave member id code: %u \n", vars.parameters.slaveMemberIdCode); + if ( setMasterMemberIdCode() ) { + DEBUG_F("Slave member id code: %u\r\n", vars.parameters.slaveMemberIdCode); + DEBUG_F("Master member id code: %u\r\n", settings.opentherm.memberIdCode > 0 ? settings.opentherm.memberIdCode : vars.parameters.slaveMemberIdCode); - localResponse = ot.setBoilerStatus( - settings.heating.enable && pump, - settings.dhw.enable + } else { + WARN("Slave member id failed"); + } + + bool heatingEnable = (vars.states.emergency || settings.heating.enable) && pump && isReady(); + localResponse = ot->setBoilerStatus( + heatingEnable, + settings.dhw.enable, + false, false, true, false, false ); - - if (!ot.isValidResponse(localResponse)) { + if (!ot->isValidResponse(localResponse)) { + WARN_F("Invalid response after setBoilerStatus: %s\r\n", ot->statusToString(ot->getLastResponseStatus())); return; } - vars.states.heating = ot.isCentralHeatingActive(localResponse); - vars.states.dhw = ot.isHotWaterActive(localResponse); - vars.states.flame = ot.isFlameOn(localResponse); - vars.states.fault = ot.isFault(localResponse); - vars.states.diagnostic = ot.isDiagnostic(localResponse); + INFO_F("Heating enabled: %d\r\n", heatingEnable); + setMaxModulationLevel(heatingEnable ? 100 : 0); - /*if (vars.dump_request.value) - { - testSupportedIDs(); - vars.dump_request.value = false; - }*/ - - - - /*if ( ot.isValidResponse(localResponse) ) { - vars.SlaveMemberIDcode.value = localResponse >> 0 & 0xFF; - uint8_t flags = (localResponse & 0xFFFF) >> 8 & 0xFF; - vars.dhw_present.value = flags & 0x01; - vars.control_type.value = flags & 0x02; - vars.cooling_present.value = flags & 0x04; - vars.dhw_tank_present.value = flags & 0x08; - vars.pump_control_present.value = flags & 0x10; - vars.ch2_present.value = flags & 0x20; - }*/ + vars.states.heating = ot->isCentralHeatingActive(localResponse); + vars.states.dhw = ot->isHotWaterActive(localResponse); + vars.states.flame = ot->isFlameOn(localResponse); + vars.states.fault = ot->isFault(localResponse); + vars.states.diagnostic = ot->isDiagnostic(localResponse); + yield(); // Команды чтения данных котла - if (millis() - prevUpdateNonEssentialVars > 30000) { + if (millis() - prevUpdateNonEssentialVars > 60000) { updateSlaveParameters(); updateMasterParameters(); - // crash? - DEBUG_F("Master type: %u, version: %u \n", vars.parameters.masterType, vars.parameters.masterVersion); - DEBUG_F("Slave type: %u, version: %u \n", vars.parameters.slaveType, vars.parameters.slaveVersion); + + DEBUG_F("Master type: %u, version: %u\r\n", vars.parameters.masterType, vars.parameters.masterVersion); + DEBUG_F("Slave type: %u, version: %u\r\n", vars.parameters.slaveType, vars.parameters.slaveVersion); updateMinMaxDhwTemp(); updateMinMaxHeatingTemp(); + if (settings.outdoorTempSource == 0) { updateOutsideTemp(); } if (vars.states.fault) { updateFaultCode(); + ot->sendBoilerReset(); + } + + if ( vars.states.diagnostic ) { + ot->sendServiceReset(); } - updatePressure(); prevUpdateNonEssentialVars = millis(); + yield(); } - updateHeatingTemp(); - updateDHWTemp(); - updateModulationLevel(); + + updatePressure(); + if ( settings.dhw.enable || settings.heating.enable || heatingEnable ) { + updateModulationLevel(); + } + + if ( settings.dhw.enable ) { + updateDHWTemp(); + } else { + vars.temperatures.dhw = 0; + } + + if ( settings.heating.enable || heatingEnable ) { + updateHeatingTemp(); + } else { + vars.temperatures.heating = 0; + } + + yield(); // // Температура ГВС - newDHWTemp = settings.dhw.target; - if (newDHWTemp != currentDHWTemp) { + byte newDHWTemp = settings.dhw.target; + if (settings.dhw.enable && newDHWTemp != currentDHWTemp) { if (newDHWTemp < vars.parameters.dhwMinTemp || newDHWTemp > vars.parameters.dhwMaxTemp) { newDHWTemp = constrain(newDHWTemp, vars.parameters.dhwMinTemp, vars.parameters.dhwMaxTemp); } - INFO_F("Set DHW temp = %u \n", newDHWTemp); + INFO_F("Set DHW temp = %u\r\n", newDHWTemp); // Записываем заданную температуру ГВС - if (ot.setDHWSetpoint(newDHWTemp)) { + if (ot->setDHWSetpoint(newDHWTemp)) { currentDHWTemp = newDHWTemp; + + } else { + WARN("Failed set DHW temp"); } } // // Температура отопления - if (fabs(vars.parameters.heatingSetpoint - currentHeatingTemp) > 0.0001) { - INFO_F("Set heating temp = %u \n", vars.parameters.heatingSetpoint); + if (heatingEnable && fabs(vars.parameters.heatingSetpoint - currentHeatingTemp) > 0.0001) { + INFO_F("Setting heating temp = %u \n", vars.parameters.heatingSetpoint); // Записываем заданную температуру - if (ot.setBoilerTemperature(vars.parameters.heatingSetpoint)) { + if (ot->setBoilerTemperature(vars.parameters.heatingSetpoint)) { currentHeatingTemp = vars.parameters.heatingSetpoint; + + } else { + WARN("Failed set heating temp"); } } @@ -111,35 +137,51 @@ protected: float halfHyst = settings.heating.hysteresis / 2; if (pump && vars.temperatures.indoor - settings.heating.target + 0.0001 >= halfHyst) { pump = false; + } else if (!pump && vars.temperatures.indoor - settings.heating.target - 0.0001 <= -(halfHyst)) { pump = true; } + } else if (!pump) { pump = true; } } void static IRAM_ATTR handleInterrupt() { - ot.handleInterrupt(); + ot->handleInterrupt(); } void static sendRequestCallback(unsigned long request, unsigned long response, OpenThermResponseStatus status, byte attempt) { - printRequestDetail(ot.getDataID(request), status, request, response, attempt); + printRequestDetail(ot->getDataID(request), status, request, response, attempt); } void static responseCallback(unsigned long result, OpenThermResponseStatus status) { static byte attempt = 0; + switch (status) { case OpenThermResponseStatus::TIMEOUT: - if (++attempt > OPENTHERM_OFFLINE_TRESHOLD) { + if (vars.states.otStatus && ++attempt > OPENTHERM_OFFLINE_TRESHOLD) { vars.states.otStatus = false; attempt = OPENTHERM_OFFLINE_TRESHOLD; } break; + case OpenThermResponseStatus::SUCCESS: attempt = 0; - vars.states.otStatus = true; + if (!vars.states.otStatus) { + vars.states.otStatus = true; + } + +#ifdef LED_OT_RX_PIN + { + digitalWrite(LED_OT_RX_PIN, true); + unsigned long ts = millis(); + while (millis() - ts < 2) {} + digitalWrite(LED_OT_RX_PIN, false); + } +#endif break; + default: break; } @@ -148,9 +190,15 @@ protected: protected: bool pump = true; unsigned long prevUpdateNonEssentialVars = 0; + unsigned long startupTime = millis(); + + + bool isReady() { + return millis() - startupTime > 60000; + } void static printRequestDetail(OpenThermMessageID id, OpenThermResponseStatus status, unsigned long request, unsigned long response, byte attempt) { - sprintf(buffer, "OT REQUEST ID: %4d Request: %8x Response: %8x Attempt: %2d Status: %s", id, request, response, attempt, ot.statusToString(status)); + sprintf(buffer, "OT REQUEST ID: %4d Request: %8lx Response: %8lx Attempt: %2d Status: %s", id, request, response, attempt, ot->statusToString(status)); if (status != OpenThermResponseStatus::SUCCESS) { //WARN(buffer); DEBUG(buffer); @@ -159,267 +207,7 @@ protected: } } - /* - bool getBoilerTemp() - { - unsigned long response; - return sendRequest(ot.buildGetBoilerTemperatureRequest(),response); - } - - bool getDHWTemp() - { - unsigned long response; - unsigned long request = ot.buildRequest(OpenThermRequestType::READ, OpenThermMessageID::Tdhw, 0); - return sendRequest(request,response); - } - - bool getOutsideTemp() - { - unsigned long response; - unsigned long request = ot.buildRequest(OpenThermRequestType::READ, OpenThermMessageID::Toutside, 0); - return sendRequest(request,response); - } - - bool setDHWTemp(float val) - { - unsigned long request = ot.buildRequest(OpenThermRequestType::WRITE, OpenThermMessageID::TdhwSet, ot.temperatureToData(val)); - unsigned long response; - return sendRequest(request,response); - } - - bool getFaultCode() - { - unsigned long response; - unsigned long request = ot.buildRequest(OpenThermRequestType::READ, OpenThermMessageID::ASFflags, 0); - return sendRequest(request,response); - } - - bool getModulationLevel() { - unsigned long response; - unsigned long request = ot.buildRequest(OpenThermRequestType::READ, OpenThermMessageID::RelModLevel, 0); - return sendRequest(request,response); - } - - bool getPressure() { - unsigned long response; - unsigned long request = ot.buildRequest(OpenThermRequestType::READ, OpenThermMessageID::CHPressure, 0); - return sendRequest(request,response); - } - - bool sendRequest(unsigned long request, unsigned long& response) - { - send_newts = millis(); - if (send_newts - send_ts < 200) { - // Преждем чем слать что то - надо подождать 100ms согласно специфиикации протокола ОТ - delay(200 - (send_newts - send_ts)); - } - - bool result = ot.sendRequestAync(request); - if(!result) { - WARN("Не могу отправить запрос"); - WARN("Шина " + ot.isReady() ? "готова" : "не готова"); - return false; - } - while (!ot.isReady()) - { - ot.process(); - yield(); // This is local Task yield() call which allow us to switch to another task in scheduler - } - send_ts = millis(); - response = ot_response; - //printRequestDetail(ot.getDataID(request), request, response); - - return true; // Response is global variable - } - - void testSupportedIDs() - { - // Basic - unsigned long request; - unsigned long response; - OpenThermMessageID id; - //Command - id = OpenThermMessageID::Command; - request = ot.buildRequest(OpenThermRequestType::READ, id, 0); - if(sendRequest(request,response)) - printRequestDetail(id, ot.getLastResponseStatus(), request, response); - //ASFlags - id = OpenThermMessageID::ASFflags; - request = ot.buildRequest(OpenThermRequestType::READ, id, 0); - if(sendRequest(request,response)) - printRequestDetail(id, ot.getLastResponseStatus(), request, response); - - //TrOverride - id = OpenThermMessageID::TrOverride; - request = ot.buildRequest(OpenThermRequestType::READ, id, 0); - if(sendRequest(request,response)) - printRequestDetail(id, ot.getLastResponseStatus(), request, response); - - //TSP - id = OpenThermMessageID::TSP; - request = ot.buildRequest(OpenThermRequestType::READ, id, 0); - if(sendRequest(request,response)) - printRequestDetail(id, ot.getLastResponseStatus(), request, response); - - //TSPindexTSPvalue - id = OpenThermMessageID::TSPindexTSPvalue; - request = ot.buildRequest(OpenThermRequestType::READ, id, 0); - if(sendRequest(request,response)) - printRequestDetail(id, ot.getLastResponseStatus(), request, response); - - //FHBsize - id = OpenThermMessageID::FHBsize; - request = ot.buildRequest(OpenThermRequestType::READ, id, 0); - if(sendRequest(request,response)) - printRequestDetail(id, ot.getLastResponseStatus(), request, response); - - //FHBindexFHBvalue - id = OpenThermMessageID::FHBindexFHBvalue; - request = ot.buildRequest(OpenThermRequestType::READ, id, 0); - if(sendRequest(request,response)) - printRequestDetail(id, ot.getLastResponseStatus(), request, response); - - //MaxCapacityMinModLevel - id = OpenThermMessageID::MaxCapacityMinModLevel; - request = ot.buildRequest(OpenThermRequestType::READ, id, 0); - if(sendRequest(request,response)) - printRequestDetail(id, ot.getLastResponseStatus(), request, response); - - //TrSet - id = OpenThermMessageID::TrSet; - request = ot.buildRequest(OpenThermRequestType::WRITE, id, ot.temperatureToData(21)); - if(sendRequest(request,response)) - printRequestDetail(id, ot.getLastResponseStatus(), request, response); - - //RelModLevel - id = OpenThermMessageID::RelModLevel; - request = ot.buildRequest(OpenThermRequestType::READ, id, 0); - if(sendRequest(request,response)) - printRequestDetail(id, ot.getLastResponseStatus(), request, response); - - //CHPressure - id = OpenThermMessageID::CHPressure; - request = ot.buildRequest(OpenThermRequestType::READ, id, 0); - if(sendRequest(request,response)) - printRequestDetail(id, ot.getLastResponseStatus(), request, response); - - //DHWFlowRate - id = OpenThermMessageID::DHWFlowRate; - request = ot.buildRequest(OpenThermRequestType::READ, id, 0); - if(sendRequest(request,response)) - printRequestDetail(id, ot.getLastResponseStatus(), request, response); - - //DayTime - id = OpenThermMessageID::DayTime; - request = ot.buildRequest(OpenThermRequestType::READ, id, 0); - if(sendRequest(request,response)) - printRequestDetail(id, ot.getLastResponseStatus(), request, response); - - //Date - id = OpenThermMessageID::Date; - request = ot.buildRequest(OpenThermRequestType::READ, id, 0); - if(sendRequest(request,response)) - printRequestDetail(id, ot.getLastResponseStatus(), request, response); - - //Year - id = OpenThermMessageID::Year; - request = ot.buildRequest(OpenThermRequestType::READ, id, 0); - if(sendRequest(request,response)) - printRequestDetail(id, ot.getLastResponseStatus(), request, response); - - //TrSetCH2 - id = OpenThermMessageID::TrSetCH2; - request = ot.buildRequest(OpenThermRequestType::WRITE, id, ot.temperatureToData(21)); - if(sendRequest(request,response)) - printRequestDetail(id, ot.getLastResponseStatus(), request, response); - - //Tr - id = OpenThermMessageID::Tr; - request = ot.buildRequest(OpenThermRequestType::WRITE, id, ot.temperatureToData(21)); - if(sendRequest(request,response)) - printRequestDetail(id, ot.getLastResponseStatus(), request, response); - - //Tret - id = OpenThermMessageID::Tret; - request = ot.buildRequest(OpenThermRequestType::READ, id, 0); - if(sendRequest(request,response)) - printRequestDetail(id, ot.getLastResponseStatus(), request, response); - - //Texhaust - id = OpenThermMessageID::Texhaust; - request = ot.buildRequest(OpenThermRequestType::READ, id, 0); - if(sendRequest(request,response)) - printRequestDetail(id, ot.getLastResponseStatus(), request, response); - - //Hcratio - id = OpenThermMessageID::Hcratio; - request = ot.buildRequest(OpenThermRequestType::READ, id, 0); - if(sendRequest(request,response)) - printRequestDetail(id, ot.getLastResponseStatus(), request, response); - - //RemoteOverrideFunction - id = OpenThermMessageID::RemoteOverrideFunction; - request = ot.buildRequest(OpenThermRequestType::READ, id, 0); - if(sendRequest(request,response)) - printRequestDetail(id, ot.getLastResponseStatus(), request, response); - - //OEMDiagnosticCode - id = OpenThermMessageID::OEMDiagnosticCode; - request = ot.buildRequest(OpenThermRequestType::READ, id, 0); - if(sendRequest(request,response)) - printRequestDetail(id, ot.getLastResponseStatus(), request, response); - - //BurnerStarts - id = OpenThermMessageID::BurnerStarts; - request = ot.buildRequest(OpenThermRequestType::READ, id, 0); - if(sendRequest(request,response)) - printRequestDetail(id, ot.getLastResponseStatus(), request, response); - - //CHPumpStarts - id = OpenThermMessageID::CHPumpStarts; - request = ot.buildRequest(OpenThermRequestType::READ, id, 0); - if(sendRequest(request,response)) - printRequestDetail(id, ot.getLastResponseStatus(), request, response); - - //DHWPumpValveStarts - id = OpenThermMessageID::DHWPumpValveStarts; - request = ot.buildRequest(OpenThermRequestType::READ, id, 0); - if(sendRequest(request,response)) - printRequestDetail(id, ot.getLastResponseStatus(), request, response); - - //DHWBurnerStarts - id = OpenThermMessageID::DHWBurnerStarts; - request = ot.buildRequest(OpenThermRequestType::READ, id, 0); - if(sendRequest(request,response)) - printRequestDetail(id, ot.getLastResponseStatus(), request, response); - - //BurnerOperationHours - id = OpenThermMessageID::BurnerOperationHours; - request = ot.buildRequest(OpenThermRequestType::READ, id, 0); - if(sendRequest(request,response)) - printRequestDetail(id, ot.getLastResponseStatus(), request, response); - - //CHPumpOperationHours - id = OpenThermMessageID::CHPumpOperationHours; - request = ot.buildRequest(OpenThermRequestType::READ, id, 0); - if(sendRequest(request,response)) - printRequestDetail(id, ot.getLastResponseStatus(), request, response); - - //DHWPumpValveOperationHours - id = OpenThermMessageID::DHWPumpValveOperationHours; - request = ot.buildRequest(OpenThermRequestType::READ, id, 0); - if(sendRequest(request,response)) - printRequestDetail(id, ot.getLastResponseStatus(), request, response); - - //DHWBurnerOperationHours - id = OpenThermMessageID::DHWBurnerOperationHours; - request = ot.buildRequest(OpenThermRequestType::READ, id, 0); - if(sendRequest(request,response)) - printRequestDetail(id, ot.getLastResponseStatus(), request, response); - } - */ - - void setMasterMemberIdCode() { + bool setMasterMemberIdCode() { //======================================================================================= // Эта группа элементов данных определяет информацию о конфигурации как на ведомых, так // и на главных сторонах. Каждый из них имеет группу флагов конфигурации (8 бит) @@ -430,40 +218,88 @@ protected: // с "кодом идентификатора участника", который идентифицирует производителя устройства. //======================================================================================= - unsigned long response = ot.sendRequest(ot.buildRequest(OpenThermRequestType::READ, OpenThermMessageID::SConfigSMemberIDcode, 0)); // 0xFFFF - if (!ot.isValidResponse(response)) { - return; - } + unsigned long response = ot->sendRequest(ot->buildRequest(OpenThermRequestType::READ, OpenThermMessageID::SConfigSMemberIDcode, 0)); // 0xFFFF + /*uint8_t flags = (response & 0xFFFF) >> 8; + DEBUG_F( + "MasterMemberIdCode:\r\n DHW present: %u\r\n Control type: %u\r\n Cooling configuration: %u\r\n DHW configuration: %u\r\n Pump control: %u\r\n CH2 present: %u\r\n Remote water filling function: %u\r\n Heat/cool mode control: %u\r\n Slave MemberID Code: %u\r\n", + flags & 0x01, + flags & 0x02, + flags & 0x04, + flags & 0x08, + flags & 0x10, + flags & 0x20, + flags & 0x40, + flags & 0x80, + response & 0xFF + );*/ - vars.parameters.slaveMemberIdCode = response >> 0 & 0xFF; - ot.sendRequest(ot.buildRequest(OpenThermRequestType::WRITE, OpenThermMessageID::MConfigMMemberIDcode, vars.parameters.slaveMemberIdCode)); + if (ot->isValidResponse(response)) { + vars.parameters.slaveMemberIdCode = response & 0xFF; + + } else if ( settings.opentherm.memberIdCode <= 0 ) { + return false; + } + + response = ot->sendRequest(ot->buildRequest( + OpenThermRequestType::WRITE, + OpenThermMessageID::MConfigMMemberIDcode, + settings.opentherm.memberIdCode > 0 ? settings.opentherm.memberIdCode : vars.parameters.slaveMemberIdCode + )); + + return ot->isValidResponse(response); } - void updateMasterParameters() { - unsigned long response = ot.sendRequest(ot.buildRequest(OpenThermRequestType::WRITE, OpenThermMessageID::MasterVersion, 0x013F)); - if (!ot.isValidResponse(response)) { - return; + bool setMaxModulationLevel(byte value) { + unsigned long response = ot->sendRequest(ot->buildRequest(OpenThermRequestType::WRITE, OpenThermMessageID::MaxRelModLevelSetting, (unsigned int)(value * 256))); + + return ot->isValidResponse(response); + } + + bool setOpenThermVersionMaster() { + unsigned long response; + response = ot->sendRequest(ot->buildRequest(OpenThermRequestType::READ, OpenThermMessageID::OpenThermVersionSlave, 0)); + if (!ot->isValidResponse(response)) { + return false; + } + // INFO_F("Opentherm version slave: %f\n", ot->getFloat(response)); + + response = ot->sendRequest(ot->buildRequest(OpenThermRequestType::WRITE_DATA, OpenThermMessageID::OpenThermVersionMaster, response)); + if (!ot->isValidResponse(response)) { + return false; + } + // INFO_F("Opentherm version master: %f\n", ot->getFloat(response)); + + return true; + } + + bool updateMasterParameters() { + unsigned long response = ot->sendRequest(ot->buildRequest(OpenThermRequestType::WRITE, OpenThermMessageID::MasterVersion, 0x013F)); + if (!ot->isValidResponse(response)) { + return false; } vars.parameters.masterType = (response & 0xFFFF) >> 8; vars.parameters.masterVersion = response & 0xFF; + + return true; } - void updateSlaveParameters() { - unsigned long response = ot.sendRequest(ot.buildRequest(OpenThermRequestType::READ, OpenThermMessageID::SlaveVersion, 0)); - if (!ot.isValidResponse(response)) { - return; + bool updateSlaveParameters() { + unsigned long response = ot->sendRequest(ot->buildRequest(OpenThermRequestType::READ, OpenThermMessageID::SlaveVersion, 0)); + if (!ot->isValidResponse(response)) { + return false; } vars.parameters.slaveType = (response & 0xFFFF) >> 8; vars.parameters.slaveVersion = response & 0xFF; + + return true; } - void updateMinMaxDhwTemp() { - unsigned long response = ot.sendRequest(ot.buildRequest(OpenThermRequestType::READ, OpenThermMessageID::TdhwSetUBTdhwSetLB, 0)); - - if (!ot.isValidResponse(response)) { - return; + bool updateMinMaxDhwTemp() { + unsigned long response = ot->sendRequest(ot->buildRequest(OpenThermRequestType::READ, OpenThermMessageID::TdhwSetUBTdhwSetLB, 0)); + if (!ot->isValidResponse(response)) { + return false; } byte minTemp = response & 0xFF; @@ -472,14 +308,17 @@ protected: if (minTemp >= 0 && maxTemp > 0 && maxTemp > minTemp) { vars.parameters.dhwMinTemp = minTemp; vars.parameters.dhwMaxTemp = maxTemp; + + return true; } + + return false; } - void updateMinMaxHeatingTemp() { - unsigned long response = ot.sendRequest(ot.buildRequest(OpenThermRequestType::READ, OpenThermMessageID::MaxTSetUBMaxTSetLB, 0)); - - if (!ot.isValidResponse(response)) { - return; + bool updateMinMaxHeatingTemp() { + unsigned long response = ot->sendRequest(ot->buildRequest(OpenThermRequestType::READ, OpenThermMessageID::MaxTSetUBMaxTSetLB, 0)); + if (!ot->isValidResponse(response)) { + return false; } byte minTemp = response & 0xFF; @@ -488,55 +327,80 @@ protected: if (minTemp >= 0 && maxTemp > 0 && maxTemp > minTemp) { vars.parameters.heatingMinTemp = minTemp; vars.parameters.heatingMaxTemp = maxTemp; + + return true; } + + return false; } - void updateOutsideTemp() { - unsigned long response = ot.sendRequest(ot.buildRequest(OpenThermRequestType::READ, OpenThermMessageID::Toutside, 0)); - - if (ot.isValidResponse(response)) { - vars.temperatures.outdoor = ot.getFloat(response); + bool updateOutsideTemp() { + unsigned long response = ot->sendRequest(ot->buildRequest(OpenThermRequestType::READ, OpenThermMessageID::Toutside, 0)); + if (!ot->isValidResponse(response)) { + return false; } + + vars.temperatures.outdoor = ot->getFloat(response); + return true; } - void updateHeatingTemp() { - unsigned long response = ot.sendRequest(ot.buildGetBoilerTemperatureRequest()); - - if (ot.isValidResponse(response)) { - vars.temperatures.heating = ot.getFloat(response); + bool updateHeatingTemp() { + unsigned long response = ot->sendRequest(ot->buildGetBoilerTemperatureRequest()); + if (!ot->isValidResponse(response)) { + return false; } + + vars.temperatures.heating = ot->getFloat(response); + return true; } - void updateDHWTemp() { - unsigned long response = ot.sendRequest(ot.buildRequest(OpenThermMessageType::READ_DATA, OpenThermMessageID::Tdhw, 0)); - - if (ot.isValidResponse(response)) { - vars.temperatures.dhw = ot.getFloat(response); + bool updateDHWTemp() { + unsigned long response = ot->sendRequest(ot->buildRequest(OpenThermMessageType::READ, OpenThermMessageID::Tdhw, 0)); + if (!ot->isValidResponse(response)) { + return false; } + + vars.temperatures.dhw = ot->getFloat(response); + return true; } - void updateFaultCode() { - unsigned long response = ot.sendRequest(ot.buildRequest(OpenThermRequestType::READ, OpenThermMessageID::ASFflags, 0)); + bool updateFaultCode() { + unsigned long response = ot->sendRequest(ot->buildRequest(OpenThermRequestType::READ, OpenThermMessageID::ASFflags, 0)); - if (ot.isValidResponse(response)) { - vars.states.faultCode = response & 0xFF; + if (!ot->isValidResponse(response)) { + return false; } + + vars.states.faultCode = response & 0xFF; + return true; } - void updateModulationLevel() { - unsigned long response = ot.sendRequest(ot.buildRequest(OpenThermRequestType::READ, OpenThermMessageID::RelModLevel, 0)); + bool updateModulationLevel() { + unsigned long response = ot->sendRequest(ot->buildRequest(OpenThermRequestType::READ, OpenThermMessageID::RelModLevel, 0)); - if (ot.isValidResponse(response)) { - vars.sensors.modulation = ot.getFloat(response); + if (!ot->isValidResponse(response)) { + return false; } + + float modulation = ot->f88(response); + if (!vars.states.flame) { + vars.sensors.modulation = 0; + } else { + vars.sensors.modulation = modulation; + } + + return true; } - void updatePressure() { - unsigned long response = ot.sendRequest(ot.buildRequest(OpenThermRequestType::READ, OpenThermMessageID::CHPressure, 0)); + bool updatePressure() { + unsigned long response = ot->sendRequest(ot->buildRequest(OpenThermRequestType::READ, OpenThermMessageID::CHPressure, 0)); - if (ot.isValidResponse(response)) { - vars.sensors.pressure = ot.getFloat(response); + if (!ot->isValidResponse(response)) { + return false; } + + vars.sensors.pressure = ot->getFloat(response); + return true; } }; diff --git a/src/RegulatorTask.h b/src/RegulatorTask.h index d9ed965..8095967 100644 --- a/src/RegulatorTask.h +++ b/src/RegulatorTask.h @@ -1,4 +1,4 @@ -#include "lib/Equitherm.h" +#include #include #include @@ -6,9 +6,9 @@ Equitherm etRegulator; GyverPID pidRegulator(0, 0, 0, 10000); PIDtuner pidTuner; -class RegulatorTask : public LeanTask { +class RegulatorTask: public LeanTask { public: - RegulatorTask(bool _enabled = false, unsigned long _interval = 0) : LeanTask(_enabled, _interval) {} + RegulatorTask(bool _enabled = false, unsigned long _interval = 0): LeanTask(_enabled, _interval) {} protected: bool tunerInit = false; @@ -21,21 +21,39 @@ protected: void setup() {} void loop() { - byte newTemp; + byte newTemp = vars.parameters.heatingSetpoint; if (vars.states.emergency) { + if (settings.heating.turbo) { + settings.heating.turbo = false; + + INFO("[REGULATOR] Turbo mode auto disabled"); + } + newTemp = getEmergencyModeTemp(); } else { - if ( vars.tuning.enable || tunerInit ) { + if (vars.tuning.enable || tunerInit) { + if (settings.heating.turbo) { + settings.heating.turbo = false; + + INFO("[REGULATOR] Turbo mode auto disabled"); + } + newTemp = getTuningModeTemp(); - if ( newTemp == 0 ) { + if (newTemp == 0) { vars.tuning.enable = false; } } - - if ( !vars.tuning.enable ) { + + if (!vars.tuning.enable) { + if (settings.heating.turbo && (fabs(settings.heating.target - vars.temperatures.indoor) < 1 || (settings.equitherm.enable && settings.pid.enable))) { + settings.heating.turbo = false; + + INFO("[REGULATOR] Turbo mode auto disabled"); + } + newTemp = getNormalModeTemp(); } } @@ -56,13 +74,13 @@ protected: // if use equitherm if (settings.emergency.useEquitherm && settings.outdoorTempSource != 1) { - float etResult = getEquithermTemp(); + float etResult = getEquithermTemp(vars.parameters.heatingMinTemp, vars.parameters.heatingMaxTemp); if (fabs(prevEtResult - etResult) + 0.0001 >= 0.5) { prevEtResult = etResult; newTemp += etResult; - INFO_F("[REGULATOR][EQUITHERM] New emergency result: %u (%f) \n", (byte) round(etResult), etResult); + INFO_F("[REGULATOR][EQUITHERM] New emergency result: %u (%f) \n", (int)round(etResult), etResult); } else { newTemp += prevEtResult; @@ -81,19 +99,23 @@ protected: if (fabs(prevHeatingTarget - settings.heating.target) > 0.0001) { prevHeatingTarget = settings.heating.target; - INFO_F("[REGULATOR] New target: %f \n", settings.heating.target); + + if (settings.equitherm.enable && settings.pid.enable) { + pidRegulator.integral = 0; + INFO_F("[REGULATOR][PID] Integral sum has been reset"); + } } // if use equitherm if (settings.equitherm.enable) { - float etResult = getEquithermTemp(); + float etResult = getEquithermTemp(vars.parameters.heatingMinTemp, vars.parameters.heatingMaxTemp); if (fabs(prevEtResult - etResult) + 0.0001 >= 0.5) { prevEtResult = etResult; newTemp += etResult; - INFO_F("[REGULATOR][EQUITHERM] New result: %u (%f) \n", (byte) round(etResult), etResult); + INFO_F("[REGULATOR][EQUITHERM] New result: %u (%f) \n", (int)round(etResult), etResult); } else { newTemp += prevEtResult; @@ -102,13 +124,16 @@ protected: // if use pid if (settings.pid.enable) { - float pidResult = getPidTemp(); + float pidResult = getPidTemp( + settings.equitherm.enable ? -30 : vars.parameters.heatingMinTemp, + settings.equitherm.enable ? 30 : vars.parameters.heatingMaxTemp + ); if (fabs(prevPidResult - pidResult) + 0.0001 >= 0.5) { prevPidResult = pidResult; newTemp += pidResult; - INFO_F("[REGULATOR][PID] New result: %u (%f) \n", (byte) round(pidResult), pidResult); + INFO_F("[REGULATOR][PID] New result: %u (%f) \n", (int)round(pidResult), pidResult); } else { newTemp += prevPidResult; @@ -124,8 +149,8 @@ protected: } byte getTuningModeTemp() { - if ( tunerInit && (!vars.tuning.enable || vars.tuning.regulator != tunerRegulator) ) { - if ( tunerRegulator == 0 ) { + if (tunerInit && (!vars.tuning.enable || vars.tuning.regulator != tunerRegulator)) { + if (tunerRegulator == 0) { pidTuner.reset(); } @@ -135,19 +160,21 @@ protected: INFO(F("[REGULATOR][TUNING] Stopped")); } - if ( !vars.tuning.enable ) { + if (!vars.tuning.enable) { return 0; } - if ( vars.tuning.regulator == 0 ) { + if (vars.tuning.regulator == 0) { // @TODO дописать INFO(F("[REGULATOR][TUNING][EQUITHERM] Not implemented")); return 0; - } else if ( vars.tuning.regulator == 1 ) { + } else if (vars.tuning.regulator == 1) { // PID tuner - float defaultTemp = settings.equitherm.enable ? getEquithermTemp() : settings.heating.target; + float defaultTemp = settings.equitherm.enable + ? getEquithermTemp(vars.parameters.heatingMinTemp, vars.parameters.heatingMaxTemp) + : settings.heating.target; if (tunerInit && pidTuner.getState() == 3) { INFO(F("[REGULATOR][TUNING][PID] Finished")); @@ -203,8 +230,8 @@ protected: } } - float getEquithermTemp() { - if ( vars.states.emergency ) { + float getEquithermTemp(int minTemp, int maxTemp) { + if (vars.states.emergency) { etRegulator.Kt = 0; etRegulator.indoorTemp = 0; etRegulator.outdoorTemp = vars.temperatures.outdoor; @@ -215,12 +242,16 @@ protected: etRegulator.outdoorTemp = round(vars.temperatures.outdoor); } else { - etRegulator.Kt = settings.equitherm.t_factor; + if (settings.heating.turbo) { + etRegulator.Kt = 10; + } else { + etRegulator.Kt = settings.equitherm.t_factor; + } etRegulator.indoorTemp = vars.temperatures.indoor; etRegulator.outdoorTemp = vars.temperatures.outdoor; } - etRegulator.setLimits(vars.parameters.heatingMinTemp, vars.parameters.heatingMaxTemp); + etRegulator.setLimits(minTemp, maxTemp); etRegulator.Kn = settings.equitherm.n_factor; // etRegulator.Kn = tuneEquithermN(etRegulator.Kn, vars.temperatures.indoor, settings.heating.target, 300, 1800, 0.01, 1); etRegulator.Kk = settings.equitherm.k_factor; @@ -229,12 +260,12 @@ protected: return etRegulator.getResult(); } - float getPidTemp() { + float getPidTemp(int minTemp, int maxTemp) { pidRegulator.Kp = settings.pid.p_factor; pidRegulator.Ki = settings.pid.i_factor; pidRegulator.Kd = settings.pid.d_factor; - pidRegulator.setLimits(vars.parameters.heatingMinTemp, vars.parameters.heatingMaxTemp); + pidRegulator.setLimits(minTemp, maxTemp); pidRegulator.input = vars.temperatures.indoor; pidRegulator.setpoint = settings.heating.target; diff --git a/src/SensorsTask.h b/src/SensorsTask.h index c6d5913..fc565fe 100644 --- a/src/SensorsTask.h +++ b/src/SensorsTask.h @@ -2,9 +2,9 @@ MicroDS18B20 outdoorSensor; -class SensorsTask : public LeanTask { +class SensorsTask: public LeanTask { public: - SensorsTask(bool _enabled = false, unsigned long _interval = 0) : LeanTask(_enabled, _interval) {} + SensorsTask(bool _enabled = false, unsigned long _interval = 0): LeanTask(_enabled, _interval) {} protected: float filteredOutdoorTemp = 0; @@ -16,9 +16,9 @@ protected: if (outdoorSensor.online()) { if (outdoorSensor.readTemp()) { float rawTemp = outdoorSensor.getTemp(); - INFO_F("[SENSORS][DS18B20] Raw temp: %f \n", rawTemp); + DEBUG_F("[SENSORS][DS18B20] Raw temp: %f \n", rawTemp); - if ( emptyOutdoorTemp ) { + if (emptyOutdoorTemp) { filteredOutdoorTemp = rawTemp; emptyOutdoorTemp = false; @@ -27,8 +27,8 @@ protected: } filteredOutdoorTemp = floor(filteredOutdoorTemp * 100) / 100; - - if ( fabs(vars.temperatures.outdoor - filteredOutdoorTemp) > 0.099 ) { + + if (fabs(vars.temperatures.outdoor - filteredOutdoorTemp) > 0.099) { vars.temperatures.outdoor = filteredOutdoorTemp; INFO_F("[SENSORS][DS18B20] New temp: %f \n", filteredOutdoorTemp); } diff --git a/src/Settings.h b/src/Settings.h index d19f7c2..d986c80 100644 --- a/src/Settings.h +++ b/src/Settings.h @@ -4,6 +4,12 @@ struct Settings { byte outdoorTempSource = 0; char hostname[80] = "opentherm"; + struct { + byte inPin = 5; + byte outPin = 4; + unsigned int memberIdCode = 4; + } opentherm; + struct { char server[80]; int port = 1883; @@ -21,6 +27,7 @@ struct Settings { struct { bool enable = true; + bool turbo = false; float target = 40.0f; float hysteresis = 0.5f; } heating; @@ -39,9 +46,9 @@ struct Settings { struct { bool enable = false; - float n_factor = 0.67f; - float k_factor = 1.0f; - float t_factor = 5.0f; + float n_factor = 0.7f; + float k_factor = 3.0f; + float t_factor = 2.0f; } equitherm; } settings; @@ -61,6 +68,7 @@ struct Variables { bool fault = false; bool diagnostic = false; byte faultCode = 0; + int8_t rssi = 0; } states; struct { diff --git a/src/WifiManagerTask.h b/src/WifiManagerTask.h index d5ef20a..24a4fb7 100644 --- a/src/WifiManagerTask.h +++ b/src/WifiManagerTask.h @@ -1,21 +1,20 @@ -// #include -#define WM_MDNS #include -//#include -//#include // Wifimanager WiFiManager wm; -WiFiManagerParameter *wmHostname; -WiFiManagerParameter *wmMqttServer; -WiFiManagerParameter *wmMqttPort; -WiFiManagerParameter *wmMqttUser; -WiFiManagerParameter *wmMqttPassword; -WiFiManagerParameter *wmMqttPrefix; +WiFiManagerParameter* wmHostname; +WiFiManagerParameter* wmOtInPin; +WiFiManagerParameter* wmOtOutPin; +WiFiManagerParameter* wmOtMemberIdCode; +WiFiManagerParameter* wmMqttServer; +WiFiManagerParameter* wmMqttPort; +WiFiManagerParameter* wmMqttUser; +WiFiManagerParameter* wmMqttPassword; +WiFiManagerParameter* wmMqttPrefix; -class WifiManagerTask : public Task { +class WifiManagerTask: public Task { public: - WifiManagerTask(bool _enabled = false, unsigned long _interval = 0) : Task(_enabled, _interval) {} + WifiManagerTask(bool _enabled = false, unsigned long _interval = 0): Task(_enabled, _interval) {} protected: void setup() { @@ -25,6 +24,18 @@ protected: wmHostname = new WiFiManagerParameter("hostname", "Hostname", settings.hostname, 80); wm.addParameter(wmHostname); + sprintf(buffer, "%d", settings.opentherm.inPin); + wmOtInPin = new WiFiManagerParameter("ot_in_pin", "Opentherm pin IN", buffer, 1); + wm.addParameter(wmOtInPin); + + sprintf(buffer, "%d", settings.opentherm.outPin); + wmOtOutPin = new WiFiManagerParameter("ot_out_pin", "Opentherm pin OUT", buffer, 1); + wm.addParameter(wmOtOutPin); + + sprintf(buffer, "%d", settings.opentherm.memberIdCode); + wmOtMemberIdCode = new WiFiManagerParameter("ot_member_id_code", "Opentherm member id code", buffer, 5); + wm.addParameter(wmOtMemberIdCode); + wmMqttServer = new WiFiManagerParameter("mqtt_server", "MQTT server", settings.mqtt.server, 80); wm.addParameter(wmMqttServer); @@ -46,15 +57,22 @@ protected: wm.setHostname(settings.hostname); wm.setWiFiAutoReconnect(true); + wm.setAPClientCheck(true); wm.setConfigPortalBlocking(false); wm.setSaveParamsCallback(saveParamsCallback); - wm.setConfigPortalTimeout(300); - wm.setDisableConfigPortal(false); + wm.setConfigPortalTimeout(180); + //wm.setDisableConfigPortal(false); - wm.autoConnect(AP_SSID); + wm.autoConnect(AP_SSID, AP_PASSWORD); } void loop() { + static unsigned short prevCounterLiters = 0; + + /*if (WiFi.status() != WL_CONNECTED && !wm.getWebPortalActive() && !wm.getConfigPortalActive()) { + wm.autoConnect(AP_SSID); + }*/ + if (connected && WiFi.status() != WL_CONNECTED) { connected = false; INFO("[wifi] Disconnected"); @@ -62,6 +80,10 @@ protected: } else if (!connected && WiFi.status() == WL_CONNECTED) { connected = true; + if (wm.getConfigPortalActive()) { + wm.stopConfigPortal(); + } + INFO_F("[wifi] Connected. IP address: %s, RSSI: %d\n", WiFi.localIP().toString().c_str(), WiFi.RSSI()); } @@ -73,14 +95,34 @@ protected: } void static saveParamsCallback() { - strcpy(settings.hostname, (*wmHostname).getValue()); - strcpy(settings.mqtt.server, (*wmMqttServer).getValue()); - settings.mqtt.port = atoi((*wmMqttPort).getValue()); - strcpy(settings.mqtt.user, (*wmMqttUser).getValue()); - strcpy(settings.mqtt.password, (*wmMqttPassword).getValue()); - strcpy(settings.mqtt.prefix, (*wmMqttPrefix).getValue()); + strcpy(settings.hostname, wmHostname->getValue()); + settings.opentherm.inPin = atoi(wmOtInPin->getValue()); + settings.opentherm.outPin = atoi(wmOtOutPin->getValue()); + settings.opentherm.memberIdCode = atoi(wmOtMemberIdCode->getValue()); + strcpy(settings.mqtt.server, wmMqttServer->getValue()); + settings.mqtt.port = atoi(wmMqttPort->getValue()); + strcpy(settings.mqtt.user, wmMqttUser->getValue()); + strcpy(settings.mqtt.password, wmMqttPassword->getValue()); + strcpy(settings.mqtt.prefix, wmMqttPrefix->getValue()); - INFO_F("Settings\nHostname: %s, Server: %s, port: %d, user: %s, pass: %s\n", settings.hostname, settings.mqtt.server, settings.mqtt.port, settings.mqtt.user, settings.mqtt.password); + INFO_F( + "New settings:\r\n" + " Hostname: %s\r\n" + " OT in pin: %d" + " OT out pin: %d" + " OT member id code: %d" + " Mqtt server: %s:%d\r\n" + " Mqtt user: %s\r\n" + " Mqtt pass: %s\r\n", + settings.hostname, + settings.opentherm.inPin, + settings.opentherm.outPin, + settings.opentherm.memberIdCode, + settings.mqtt.server, + settings.mqtt.port, + settings.mqtt.user, + settings.mqtt.password + ); eeSettings.updateNow(); INFO(F("Settings saved")); } diff --git a/src/defines.h b/src/defines.h index 5b20709..536e7e4 100644 --- a/src/defines.h +++ b/src/defines.h @@ -1,13 +1,12 @@ -#define OT_GATEWAY_VERSION "1.0.7" +#define OT_GATEWAY_VERSION "1.2.1" #define AP_SSID "OpenTherm Gateway" +#define AP_PASSWORD "otgateway123456" #define USE_TELNET -#define EMERGENCY_TRESHOLD 10 +#define EMERGENCY_TIME_TRESHOLD 120000 #define MQTT_RECONNECT_INTERVAL 5000 #define MQTT_KEEPALIVE 30 -#define OPENTHERM_IN_PIN 4 -#define OPENTHERM_OUT_PIN 5 #define OPENTHERM_OFFLINE_TRESHOLD 10 #define DS18B20_PIN 2 @@ -16,6 +15,9 @@ #define DS_CHECK_CRC true #define DS_CRC_USE_TABLE true +#define LED_STATUS_PIN 13 +#define LED_OT_RX_PIN 15 + #define CONFIG_URL "http://%s/" diff --git a/src/lib/CustomOpenTherm.h b/src/lib/CustomOpenTherm.h deleted file mode 100644 index 17b7fa6..0000000 --- a/src/lib/CustomOpenTherm.h +++ /dev/null @@ -1,47 +0,0 @@ -#pragma once -#include -#include - -extern SchedulerClass Scheduler; - -class CustomOpenTherm : public OpenTherm { -private: - unsigned long send_ts = millis(); - void(*handleSendRequestCallback)(unsigned long, unsigned long, OpenThermResponseStatus status, byte attempt); - -public: - CustomOpenTherm(int inPin = 4, int outPin = 5, bool isSlave = false) : OpenTherm(inPin, outPin, isSlave) {} - void setHandleSendRequestCallback(void(*handleSendRequestCallback)(unsigned long, unsigned long, OpenThermResponseStatus status, byte attempt)) { - this->handleSendRequestCallback = handleSendRequestCallback; - } - - unsigned long sendRequest(unsigned long request, byte attempts = 5, byte _attempt = 0) { - _attempt++; - while (send_ts > 0 && millis() - send_ts < 200) { - Scheduler.yield(); - } - - unsigned long _response; - if (!sendRequestAync(request)) { - _response = 0; - } else { - while (!isReady()) { - Scheduler.yield(); - process(); - } - - _response = getLastResponse(); - } - - if (handleSendRequestCallback != NULL) { - handleSendRequestCallback(request, _response, getLastResponseStatus(), _attempt); - } - - send_ts = millis(); - if (getLastResponseStatus() == OpenThermResponseStatus::SUCCESS || _attempt >= attempts) { - return _response; - } else { - return sendRequest(request, attempts, _attempt); - } - } -}; diff --git a/src/main.cpp b/src/main.cpp index 9cc301e..673eda5 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,3 +1,4 @@ +#include #include "defines.h" #include #include @@ -52,7 +53,7 @@ void setup() { tMqtt = new MqttTask(false); Scheduler.start(tMqtt); - tOt = new OpenThermTask(true); + tOt = new OpenThermTask(false); Scheduler.start(tOt); tSensors = new SensorsTask(false, DS18B20_INTERVAL);