diff --git a/lib/HomeAssistantHelper/HomeAssistantHelper.h b/lib/HomeAssistantHelper/HomeAssistantHelper.h index 8e73c49..564a699 100644 --- a/lib/HomeAssistantHelper/HomeAssistantHelper.h +++ b/lib/HomeAssistantHelper/HomeAssistantHelper.h @@ -4,8 +4,18 @@ class HomeAssistantHelper { public: - HomeAssistantHelper(PubSubClient& client) { - this->client = &client; + HomeAssistantHelper() {} + + HomeAssistantHelper(PubSubClient* client) { + this->setClient(client); + } + + void setClient() { + this->client = nullptr; + } + + void setClient(PubSubClient* client) { + this->client = client; } void setYieldCallback(void(*yieldCallback)(void*)) { @@ -18,14 +28,6 @@ public: this->yieldArg = arg; } - void setBufferedClient() { - this->bClient = nullptr; - } - - void setBufferedClient(BufferingPrint* bClient) { - this->bClient = bClient; - } - void setDevicePrefix(String value) { devicePrefix = value; } @@ -51,6 +53,10 @@ public: } bool publish(const char* topic, JsonDocument& doc) { + if (this->client == nullptr) { + return false; + } + doc[FPSTR(HA_DEVICE)][FPSTR(HA_IDENTIFIERS)][0] = devicePrefix; doc[FPSTR(HA_DEVICE)][FPSTR(HA_SW_VERSION)] = deviceVersion; @@ -70,31 +76,38 @@ public: doc[FPSTR(HA_DEVICE)][FPSTR(HA_CONF_URL)] = deviceConfigUrl; } - if (!client->beginPublish(topic, measureJson(doc), true)) { - if (this->yieldCallback != nullptr) { - this->yieldCallback(yieldArg); + + size_t docSize = measureJson(doc); + uint8_t* buffer = (uint8_t*) malloc(docSize * sizeof(*buffer)); + size_t length = serializeJson(doc, buffer, docSize); + + size_t written = 0; + if (length != 0) { + if (this->client->beginPublish(topic, docSize, true)) { + for (size_t offset = 0; offset < docSize; offset += 128) { + size_t packetSize = offset + 128 <= docSize ? 128 : docSize - offset; + written += this->client->write(buffer + offset, packetSize); + } + + this->client->flush(); } - - return false; } + free(buffer); - if (this->bClient != nullptr) { - serializeJson(doc, *this->bClient); - this->bClient->flush(); + Log.straceln("MQTT", "Publish %u of %u bytes to topic: %s", written, docSize, topic); - } else { - serializeJson(doc, *client); - } - - int pubResult = client->endPublish(); if (this->yieldCallback != nullptr) { this->yieldCallback(yieldArg); } - return pubResult; + return docSize == written; } bool publish(const char* topic) { + if (this->client == nullptr) { + return false; + } + return client->publish(topic, NULL, true); } @@ -114,8 +127,7 @@ public: protected: void(*yieldCallback)(void*) = nullptr; void* yieldArg = nullptr; - PubSubClient* client; - BufferingPrint* bClient = nullptr; + PubSubClient* client = nullptr; String prefix = "homeassistant"; String devicePrefix = ""; String deviceVersion = "1.0"; diff --git a/platformio.ini b/platformio.ini index f654570..2b8a876 100644 --- a/platformio.ini +++ b/platformio.ini @@ -12,7 +12,8 @@ framework = arduino lib_deps = ;arduino-libraries/NTPClient@^3.2.1 - bblanchon/ArduinoJson@^6.20.0 + ;bblanchon/ArduinoJson@^6.21.4 + https://github.com/bblanchon/ArduinoJson/archive/refs/heads/7.x.zip ;ihormelnyk/OpenTherm Library@^1.1.4 https://github.com/Laxilef/opentherm_library/archive/refs/heads/dev.zip knolleary/PubSubClient@^2.8 diff --git a/src/HaHelper.h b/src/HaHelper.h index eecaeec..e5cc220 100644 --- a/src/HaHelper.h +++ b/src/HaHelper.h @@ -5,11 +5,9 @@ class HaHelper : public HomeAssistantHelper { public: static const byte TEMP_SOURCE_HEATING = 0; static const byte TEMP_SOURCE_INDOOR = 1; - - HaHelper(PubSubClient& client) : HomeAssistantHelper(client) {} - + bool publishSelectOutdoorSensorType(bool enabledByDefault = true) { - StaticJsonDocument<1536> doc; + JsonDocument doc; doc[FPSTR(HA_COMMAND_TOPIC)] = devicePrefix + F("/settings/set"); doc[FPSTR(HA_COMMAND_TEMPLATE)] = F("{\"sensors\": {\"outdoor\": {\"type\": {% if value == 'Boiler' %}0{% elif value == 'Manual' %}1{% elif value == 'External' %}2{% endif %}}}}"); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; @@ -27,7 +25,7 @@ public: } bool publishSelectIndoorSensorType(bool enabledByDefault = true) { - StaticJsonDocument<1536> doc; + JsonDocument doc; doc[FPSTR(HA_COMMAND_TOPIC)] = devicePrefix + F("/settings/set"); #if USE_BLE doc[FPSTR(HA_COMMAND_TEMPLATE)] = F("{\"sensors\": {\"indoor\": {\"type\": {% if value == 'Manual' %}1{% elif value == 'External' %}2{% elif value == 'Bluetooth' %}3{% endif %}}}}"); @@ -55,7 +53,7 @@ public: } bool publishNumberOutdoorSensorOffset(bool enabledByDefault = true) { - StaticJsonDocument<1536> doc; + JsonDocument doc; doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = devicePrefix + F("/settings"); doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_VALUE_TEMPLATE)] = F("{{ iif(value_json.sensors.outdoor.type != 1, 'online', 'offline') }}"); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; @@ -79,7 +77,7 @@ public: } bool publishNumberIndoorSensorOffset(bool enabledByDefault = true) { - StaticJsonDocument<1536> doc; + JsonDocument doc; doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = devicePrefix + F("/settings"); doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_VALUE_TEMPLATE)] = F("{{ iif(value_json.sensors.indoor.type != 1, 'online', 'offline') }}"); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; @@ -104,7 +102,7 @@ public: bool publishSwitchDebug(bool enabledByDefault = true) { - StaticJsonDocument<1536> doc; + JsonDocument doc; doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_UNIQUE_ID)] = devicePrefix + F("_debug"); doc[FPSTR(HA_OBJECT_ID)] = devicePrefix + F("_debug"); @@ -124,7 +122,7 @@ public: bool publishSwitchEmergency(bool enabledByDefault = true) { - StaticJsonDocument<1536> doc; + JsonDocument doc; doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_UNIQUE_ID)] = devicePrefix + F("_emergency"); doc[FPSTR(HA_OBJECT_ID)] = devicePrefix + F("_emergency"); @@ -143,7 +141,7 @@ public: } bool publishNumberEmergencyTarget(bool enabledByDefault = true) { - StaticJsonDocument<1536> doc; + JsonDocument doc; doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_UNIQUE_ID)] = devicePrefix + F("_emergency_target"); doc[FPSTR(HA_OBJECT_ID)] = devicePrefix + F("_emergency_target"); @@ -165,7 +163,7 @@ public: } bool publishSwitchEmergencyUseEquitherm(bool enabledByDefault = true) { - StaticJsonDocument<1536> doc; + JsonDocument doc; doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = devicePrefix + F("/settings"); doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_VALUE_TEMPLATE)] = F("{{ iif(value_json.sensors.outdoor.type != 1, 'online', 'offline') }}"); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; @@ -187,7 +185,7 @@ public: bool publishSwitchHeating(bool enabledByDefault = true) { - StaticJsonDocument<1536> doc; + JsonDocument doc; doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = devicePrefix + F("/status"); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_UNIQUE_ID)] = devicePrefix + F("_heating"); @@ -207,7 +205,7 @@ public: } bool publishSwitchHeatingTurbo(bool enabledByDefault = true) { - StaticJsonDocument<1536> doc; + JsonDocument doc; doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = devicePrefix + F("/status"); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_UNIQUE_ID)] = devicePrefix + F("_heating_turbo"); @@ -227,7 +225,7 @@ public: } bool publishNumberHeatingTarget(byte minTemp = 20, byte maxTemp = 90, bool enabledByDefault = true) { - StaticJsonDocument<1536> doc; + JsonDocument doc; doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = devicePrefix + F("/status"); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_UNIQUE_ID)] = devicePrefix + F("_heating_target"); @@ -250,7 +248,7 @@ public: } bool publishNumberHeatingHysteresis(bool enabledByDefault = true) { - StaticJsonDocument<1536> doc; + JsonDocument doc; doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_UNIQUE_ID)] = devicePrefix + F("_heating_hysteresis"); doc[FPSTR(HA_OBJECT_ID)] = devicePrefix + F("_heating_hysteresis"); @@ -272,7 +270,7 @@ public: } bool publishSensorHeatingSetpoint(bool enabledByDefault = true) { - StaticJsonDocument<1536> doc; + JsonDocument doc; doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = devicePrefix + F("/status"); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_UNIQUE_ID)] = devicePrefix + F("_heating_setpoint"); @@ -290,7 +288,7 @@ public: } bool publishSensorCurrentHeatingMinTemp(bool enabledByDefault = true) { - StaticJsonDocument<1536> doc; + JsonDocument doc; doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = devicePrefix + F("/status"); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_UNIQUE_ID)] = devicePrefix + F("_current_heating_min_temp"); @@ -308,7 +306,7 @@ public: } bool publishSensorCurrentHeatingMaxTemp(bool enabledByDefault = true) { - StaticJsonDocument<1536> doc; + JsonDocument doc; doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = devicePrefix + F("/status"); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_UNIQUE_ID)] = devicePrefix + F("_current_heating_max_temp"); @@ -326,7 +324,7 @@ public: } bool publishNumberHeatingMinTemp(bool enabledByDefault = true) { - StaticJsonDocument<1536> doc; + JsonDocument doc; doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_UNIQUE_ID)] = devicePrefix + F("_heating_min_temp"); doc[FPSTR(HA_OBJECT_ID)] = devicePrefix + F("_heating_min_temp"); @@ -348,7 +346,7 @@ public: } bool publishNumberHeatingMaxTemp(bool enabledByDefault = true) { - StaticJsonDocument<1536> doc; + JsonDocument doc; doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_UNIQUE_ID)] = devicePrefix + F("_heating_max_temp"); doc[FPSTR(HA_OBJECT_ID)] = devicePrefix + F("_heating_max_temp"); @@ -370,7 +368,7 @@ public: } bool publishNumberHeatingMaxModulation(bool enabledByDefault = true) { - StaticJsonDocument<1536> doc; + JsonDocument doc; doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_UNIQUE_ID)] = devicePrefix + F("_heating_max_modulation"); doc[FPSTR(HA_OBJECT_ID)] = devicePrefix + F("_heating_max_modulation"); @@ -393,7 +391,7 @@ public: bool publishSwitchDhw(bool enabledByDefault = true) { - StaticJsonDocument<1536> doc; + JsonDocument doc; doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = devicePrefix + F("/status"); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_UNIQUE_ID)] = devicePrefix + F("_dhw"); @@ -413,7 +411,7 @@ public: } bool publishNumberDhwTarget(byte minTemp = 40, byte maxTemp = 60, bool enabledByDefault = true) { - StaticJsonDocument<1536> doc; + JsonDocument doc; doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = devicePrefix + F("/status"); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_UNIQUE_ID)] = devicePrefix + F("_dhw_target"); @@ -436,7 +434,7 @@ public: } bool publishSensorCurrentDhwMinTemp(bool enabledByDefault = true) { - StaticJsonDocument<1536> doc; + JsonDocument doc; doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = devicePrefix + F("/status"); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_UNIQUE_ID)] = devicePrefix + F("_current_dhw_min_temp"); @@ -454,7 +452,7 @@ public: } bool publishSensorCurrentDhwMaxTemp(bool enabledByDefault = true) { - StaticJsonDocument<1536> doc; + JsonDocument doc; doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = devicePrefix + F("/status"); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_UNIQUE_ID)] = devicePrefix + F("_current_dhw_max_temp"); @@ -472,7 +470,7 @@ public: } bool publishNumberDhwMinTemp(bool enabledByDefault = true) { - StaticJsonDocument<1536> doc; + JsonDocument doc; doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_UNIQUE_ID)] = devicePrefix + F("_dhw_min_temp"); doc[FPSTR(HA_OBJECT_ID)] = devicePrefix + F("_dhw_min_temp"); @@ -494,7 +492,7 @@ public: } bool publishNumberDhwMaxTemp(bool enabledByDefault = true) { - StaticJsonDocument<1536> doc; + JsonDocument doc; doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_UNIQUE_ID)] = devicePrefix + F("_dhw_max_temp"); doc[FPSTR(HA_OBJECT_ID)] = devicePrefix + F("_dhw_max_temp"); @@ -517,7 +515,7 @@ public: bool publishSwitchPID(bool enabledByDefault = true) { - StaticJsonDocument<1536> doc; + JsonDocument doc; doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_UNIQUE_ID)] = devicePrefix + F("_pid"); doc[FPSTR(HA_OBJECT_ID)] = devicePrefix + F("_pid"); @@ -536,7 +534,7 @@ public: } bool publishNumberPIDFactorP(bool enabledByDefault = true) { - StaticJsonDocument<1536> doc; + JsonDocument doc; doc[FPSTR(HA_UNIQUE_ID)] = devicePrefix + F("_pid_p"); doc[FPSTR(HA_OBJECT_ID)] = devicePrefix + F("_pid_p"); doc[FPSTR(HA_ENTITY_CATEGORY)] = F("config"); @@ -555,7 +553,7 @@ public: } bool publishNumberPIDFactorI(bool enabledByDefault = true) { - StaticJsonDocument<1536> doc; + JsonDocument doc; doc[FPSTR(HA_UNIQUE_ID)] = devicePrefix + F("_pid_i"); doc[FPSTR(HA_OBJECT_ID)] = devicePrefix + F("_pid_i"); doc[FPSTR(HA_ENTITY_CATEGORY)] = F("config"); @@ -574,7 +572,7 @@ public: } bool publishNumberPIDFactorD(bool enabledByDefault = true) { - StaticJsonDocument<1536> doc; + JsonDocument doc; doc[FPSTR(HA_UNIQUE_ID)] = devicePrefix + F("_pid_d"); doc[FPSTR(HA_OBJECT_ID)] = devicePrefix + F("_pid_d"); doc[FPSTR(HA_ENTITY_CATEGORY)] = F("config"); @@ -593,7 +591,7 @@ public: } bool publishNumberPIDMinTemp(bool enabledByDefault = true) { - StaticJsonDocument<1536> doc; + JsonDocument doc; doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_UNIQUE_ID)] = devicePrefix + F("_pid_min_temp"); doc[FPSTR(HA_OBJECT_ID)] = devicePrefix + F("_pid_min_temp"); @@ -615,7 +613,7 @@ public: } bool publishNumberPIDMaxTemp(bool enabledByDefault = true) { - StaticJsonDocument<1536> doc; + JsonDocument doc; doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_UNIQUE_ID)] = devicePrefix + F("_pid_max_temp"); doc[FPSTR(HA_OBJECT_ID)] = devicePrefix + F("_pid_max_temp"); @@ -638,7 +636,7 @@ public: bool publishSwitchEquitherm(bool enabledByDefault = true) { - StaticJsonDocument<1536> doc; + JsonDocument doc; doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_UNIQUE_ID)] = devicePrefix + F("_equitherm"); doc[FPSTR(HA_OBJECT_ID)] = devicePrefix + F("_equitherm"); @@ -657,7 +655,7 @@ public: } bool publishNumberEquithermFactorN(bool enabledByDefault = true) { - StaticJsonDocument<1536> doc; + JsonDocument doc; doc[FPSTR(HA_UNIQUE_ID)] = devicePrefix + F("_equitherm_n"); doc[FPSTR(HA_OBJECT_ID)] = devicePrefix + F("_equitherm_n"); doc[FPSTR(HA_ENTITY_CATEGORY)] = F("config"); @@ -676,7 +674,7 @@ public: } bool publishNumberEquithermFactorK(bool enabledByDefault = true) { - StaticJsonDocument<1536> doc; + JsonDocument doc; doc[FPSTR(HA_UNIQUE_ID)] = devicePrefix + F("_equitherm_k"); doc[FPSTR(HA_OBJECT_ID)] = devicePrefix + F("_equitherm_k"); doc[FPSTR(HA_ENTITY_CATEGORY)] = F("config"); @@ -695,7 +693,7 @@ public: } bool publishNumberEquithermFactorT(bool enabledByDefault = true) { - StaticJsonDocument<1536> doc; + JsonDocument doc; doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = devicePrefix + F("/settings"); doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_VALUE_TEMPLATE)] = F("{{ iif(value_json.pid.enable, 'offline', 'online') }}"); doc[FPSTR(HA_UNIQUE_ID)] = devicePrefix + F("_equitherm_t"); @@ -717,7 +715,7 @@ public: bool publishSwitchTuning(bool enabledByDefault = true) { - StaticJsonDocument<1536> doc; + JsonDocument doc; doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_UNIQUE_ID)] = devicePrefix + F("_tuning"); doc[FPSTR(HA_OBJECT_ID)] = devicePrefix + F("_tuning"); @@ -736,7 +734,7 @@ public: } bool publishSelectTuningRegulator(bool enabledByDefault = true) { - StaticJsonDocument<1536> doc; + JsonDocument doc; doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = devicePrefix + F("/status"); doc[FPSTR(HA_AVAILABILITY_MODE)] = F("all"); doc[FPSTR(HA_COMMAND_TOPIC)] = devicePrefix + F("/state/set"); @@ -756,7 +754,7 @@ public: bool publishBinSensorStatus(bool enabledByDefault = true) { - StaticJsonDocument<1536> doc; + JsonDocument doc; doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_UNIQUE_ID)] = devicePrefix + F("_status"); doc[FPSTR(HA_OBJECT_ID)] = devicePrefix + F("_status"); @@ -772,7 +770,7 @@ public: } bool publishBinSensorOtStatus(bool enabledByDefault = true) { - StaticJsonDocument<1536> doc; + JsonDocument doc; doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_UNIQUE_ID)] = devicePrefix + F("_ot_status"); doc[FPSTR(HA_OBJECT_ID)] = devicePrefix + F("_ot_status"); @@ -787,7 +785,7 @@ public: } bool publishBinSensorHeating(bool enabledByDefault = true) { - StaticJsonDocument<1536> doc; + JsonDocument doc; doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = devicePrefix + F("/status"); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_UNIQUE_ID)] = devicePrefix + F("_heating"); @@ -803,7 +801,7 @@ public: } bool publishBinSensorDhw(bool enabledByDefault = true) { - StaticJsonDocument<1536> doc; + JsonDocument doc; doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = devicePrefix + F("/status"); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_UNIQUE_ID)] = devicePrefix + F("_dhw"); @@ -819,7 +817,7 @@ public: } bool publishBinSensorFlame(bool enabledByDefault = true) { - StaticJsonDocument<1536> doc; + JsonDocument doc; doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = devicePrefix + F("/status"); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_UNIQUE_ID)] = devicePrefix + F("_flame"); @@ -835,7 +833,7 @@ public: } bool publishBinSensorFault(bool enabledByDefault = true) { - StaticJsonDocument<1536> doc; + JsonDocument doc; doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = devicePrefix + F("/state"); doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_VALUE_TEMPLATE)] = F("{{ iif(value_json.states.otStatus, 'online', 'offline') }}"); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; @@ -852,7 +850,7 @@ public: } bool publishBinSensorDiagnostic(bool enabledByDefault = true) { - StaticJsonDocument<1536> doc; + JsonDocument doc; doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = devicePrefix + F("/status"); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_UNIQUE_ID)] = devicePrefix + F("_diagnostic"); @@ -868,7 +866,7 @@ public: } bool publishSensorFaultCode(bool enabledByDefault = true) { - StaticJsonDocument<1536> doc; + JsonDocument doc; doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = devicePrefix + F("/state"); doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_VALUE_TEMPLATE)] = F("{{ iif(value_json.states.fault, 'online', 'offline') }}"); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; @@ -884,7 +882,7 @@ public: } bool publishSensorRssi(bool enabledByDefault = true) { - StaticJsonDocument<1536> doc; + JsonDocument doc; doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = devicePrefix + F("/status"); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_UNIQUE_ID)] = devicePrefix + F("_rssi"); @@ -902,7 +900,7 @@ public: } bool publishSensorUptime(bool enabledByDefault = true) { - StaticJsonDocument<1536> doc; + JsonDocument doc; doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = devicePrefix + F("/status"); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_UNIQUE_ID)] = devicePrefix + F("_uptime"); @@ -921,7 +919,7 @@ public: bool publishSensorModulation(bool enabledByDefault = true) { - StaticJsonDocument<1536> doc; + JsonDocument doc; doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = devicePrefix + F("/status"); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_UNIQUE_ID)] = devicePrefix + F("_modulation_level"); @@ -939,7 +937,7 @@ public: } bool publishSensorPressure(bool enabledByDefault = true) { - StaticJsonDocument<1536> doc; + JsonDocument doc; doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = devicePrefix + F("/status"); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_UNIQUE_ID)] = devicePrefix + F("_pressure"); @@ -957,7 +955,7 @@ public: } bool publishSensorDhwFlowRate(bool enabledByDefault = true) { - StaticJsonDocument<1024> doc; + JsonDocument doc; doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = devicePrefix + F("/status"); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_UNIQUE_ID)] = devicePrefix + F("_dhw_flow_rate"); @@ -976,7 +974,7 @@ public: bool publishNumberIndoorTemp(bool enabledByDefault = true) { - StaticJsonDocument<1536> doc; + JsonDocument doc; doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_UNIQUE_ID)] = devicePrefix + F("_indoor_temp"); doc[FPSTR(HA_OBJECT_ID)] = devicePrefix + F("_indoor_temp"); @@ -997,7 +995,7 @@ public: } bool publishSensorIndoorTemp(bool enabledByDefault = true) { - StaticJsonDocument<1536> doc; + JsonDocument doc; doc[FPSTR(HA_AVAILABILITY)][0][FPSTR(HA_TOPIC)] = devicePrefix + F("/status"); doc[FPSTR(HA_AVAILABILITY_MODE)] = F("any"); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; @@ -1016,7 +1014,7 @@ public: } bool publishNumberOutdoorTemp(bool enabledByDefault = true) { - StaticJsonDocument<1536> doc; + JsonDocument doc; doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_UNIQUE_ID)] = devicePrefix + F("_outdoor_temp"); doc[FPSTR(HA_OBJECT_ID)] = devicePrefix + F("_outdoor_temp"); @@ -1037,7 +1035,7 @@ public: } bool publishSensorOutdoorTemp(bool enabledByDefault = true) { - StaticJsonDocument<1536> doc; + JsonDocument doc; doc[FPSTR(HA_AVAILABILITY)][0][FPSTR(HA_TOPIC)] = devicePrefix + F("/status"); doc[FPSTR(HA_AVAILABILITY_MODE)] = F("any"); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; @@ -1056,7 +1054,7 @@ public: } bool publishSensorHeatingTemp(bool enabledByDefault = true) { - StaticJsonDocument<1536> doc; + JsonDocument doc; doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = devicePrefix + F("/status"); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_UNIQUE_ID)] = devicePrefix + F("_heating_temp"); @@ -1074,7 +1072,7 @@ public: } bool publishSensorDhwTemp(bool enabledByDefault = true) { - StaticJsonDocument<1536> doc; + JsonDocument doc; doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = devicePrefix + F("/status"); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_UNIQUE_ID)] = devicePrefix + F("_dhw_temp"); @@ -1093,7 +1091,7 @@ public: bool publishClimateHeating(byte minTemp = 20, byte maxTemp = 90, byte currentTempSource = HaHelper::TEMP_SOURCE_HEATING, bool enabledByDefault = true) { - StaticJsonDocument<2560> doc; + JsonDocument doc; doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = devicePrefix + F("/status"); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_UNIQUE_ID)] = devicePrefix + F("_heating"); @@ -1144,7 +1142,7 @@ public: } bool publishClimateDhw(byte minTemp = 40, byte maxTemp = 60, bool enabledByDefault = true) { - StaticJsonDocument<2560> doc; + JsonDocument doc; doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = devicePrefix + F("/status"); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; @@ -1181,7 +1179,7 @@ public: bool publishButtonRestart(bool enabledByDefault = true) { - StaticJsonDocument<1024> doc; + JsonDocument doc; doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_UNIQUE_ID)] = devicePrefix + F("_restart"); doc[FPSTR(HA_OBJECT_ID)] = devicePrefix + F("_restart"); @@ -1195,7 +1193,7 @@ public: } bool publishButtonResetFault(bool enabledByDefault = true) { - StaticJsonDocument<1024> doc; + JsonDocument doc; doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = devicePrefix + F("/state"); doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_VALUE_TEMPLATE)] = F("{{ iif(value_json.states.fault, 'online', 'offline') }}"); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; @@ -1211,7 +1209,7 @@ public: } bool publishButtonResetDiagnostic(bool enabledByDefault = true) { - StaticJsonDocument<1024> doc; + JsonDocument doc; doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = devicePrefix + F("/state"); doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_VALUE_TEMPLATE)] = F("{{ iif(value_json.states.diagnostic, 'online', 'offline') }}"); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; diff --git a/src/MqttTask.h b/src/MqttTask.h index 1a31b40..2347527 100644 --- a/src/MqttTask.h +++ b/src/MqttTask.h @@ -1,27 +1,44 @@ #include #include +#include #include "HaHelper.h" -WiFiClient espClient; -PubSubClient client(espClient); -HaHelper haHelper(client); - class MqttTask : public Task { public: MqttTask(bool _enabled = false, unsigned long _interval = 0) : Task(_enabled, _interval) {} ~MqttTask() { - if (this->bClient != nullptr) { - // todo: delete polymorph? - //delete this->bClient; + if (this->haHelper != nullptr) { + delete this->haHelper; + } + + if (this->client != nullptr) { + if (this->client->connected()) { + this->client->disconnect(); + } + + delete this->client; + } + + if (this->wifiClient != nullptr) { + delete this->wifiClient; } } protected: - BufferingPrint* bClient = nullptr; - unsigned long lastReconnectAttempt = 0; - unsigned long firstFailConnect = 0; + WiFiClient* wifiClient; + PubSubClient* client = nullptr; + HaHelper* haHelper = nullptr; + unsigned long lastReconnectTime = 0; + unsigned long connectedTime = 0; + unsigned long disconnectedTime = 0; + unsigned long prevPubVars = 0; + unsigned long prevPubSettings = 0; + bool connected = false; + bool newConnection = false; + + unsigned short readyForSendTime = 15000; const char* getTaskName() { return "Mqtt"; @@ -35,79 +52,199 @@ protected: return 1; } + bool isReadyForSend() { + return millis() - this->connectedTime > this->readyForSendTime; + } + void setup() { Log.sinfoln("MQTT", F("Started")); - this->bClient = new BufferingPrint(client, 64); + // client settings + this->client = new PubSubClient(); + this->client->setSocketTimeout(2); + this->client->setKeepAlive(5); + this->client->setBufferSize(768); + this->client->setCallback(std::bind(&MqttTask::onMessage, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); - client.setCallback(std::bind(&MqttTask::__callback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); - client.setBufferSize(1024); - client.setSocketTimeout(1); - - haHelper.setYieldCallback([](void* self) { + // ha helper settings + this->haHelper = new HaHelper(); + this->haHelper->setDevicePrefix(settings.mqtt.prefix); + this->haHelper->setDeviceVersion(PROJECT_VERSION); + this->haHelper->setDeviceModel(PROJECT_NAME); + this->haHelper->setDeviceName(PROJECT_NAME); + this->haHelper->setClient(this->client); + this->haHelper->setYieldCallback([](void* self) { MqttTask* task = static_cast(self); - task->delay(50); - - if (client.connected()) { - client.loop(); - } + task->client->loop(); + task->delay(100); }, this); - haHelper.setBufferedClient(this->bClient); - haHelper.setDevicePrefix(settings.mqtt.prefix); - haHelper.setDeviceVersion(PROJECT_VERSION); - haHelper.setDeviceModel(PROJECT_NAME); - haHelper.setDeviceName(PROJECT_NAME); sprintf(buffer, CONFIG_URL, WiFi.localIP().toString().c_str()); - haHelper.setDeviceConfigUrl(buffer); + this->haHelper->setDeviceConfigUrl(buffer); } void loop() { - if (!client.connected() && millis() - lastReconnectAttempt >= MQTT_RECONNECT_INTERVAL) { - Log.sinfoln("MQTT", F("Not connected, state: %i, connecting to server %s..."), client.state(), settings.mqtt.server); + if (this->wifiClient == nullptr || (!this->client->connected() && millis() - this->lastReconnectTime >= MQTT_RECONNECT_INTERVAL)) { + Log.sinfoln("MQTT", F("Not connected, state: %d"), this->client->state()); - client.setServer(settings.mqtt.server, settings.mqtt.port); - if (client.connect(settings.hostname, settings.mqtt.user, settings.mqtt.password)) { - Log.sinfoln("MQTT", F("Connected")); - - client.subscribe(getTopicPath("settings/set").c_str()); - client.subscribe(getTopicPath("state/set").c_str()); - publishHaEntities(); - publishNonStaticHaEntities(true); - - firstFailConnect = 0; - lastReconnectAttempt = 0; - - } else { - Log.swarningln("MQTT", F("Failed to connect to server")); - - if (settings.emergency.enable && !vars.states.emergency) { - if (firstFailConnect == 0) { - firstFailConnect = millis(); - } - - if (millis() - firstFailConnect > EMERGENCY_TIME_TRESHOLD) { - vars.states.emergency = true; - Log.sinfoln("MQTT", F("Emergency mode enabled")); - } - } - - lastReconnectAttempt = millis(); + // bug? + // memory leak at random times if this is not done + if (this->wifiClient != nullptr) { + delete this->wifiClient; } - delay(100); + + this->wifiClient = new WiFiClient(); + this->client->setClient(*this->wifiClient); + this->client->setServer(settings.mqtt.server, settings.mqtt.port); + + Log.sinfoln("MQTT", F("Connecting to %s:%u..."), settings.mqtt.server, settings.mqtt.port); + this->client->connect(settings.hostname, settings.mqtt.user, settings.mqtt.password); + + this->lastReconnectTime = millis(); + return; } + if (!this->client->connected() && this->connected) { + this->connected = false; + this->onDisconnect(); - if (client.connected()) { - if (vars.states.emergency) { - vars.states.emergency = false; + } else if (this->client->connected() && !this->connected) { + this->connected = true; + this->onConnect(); + } - Log.sinfoln("MQTT", F("Emergency mode disabled")); + if (!this->client->connected()) { + if (settings.emergency.enable && !vars.states.emergency) { + if (millis() - this->disconnectedTime > EMERGENCY_TIME_TRESHOLD) { + vars.states.emergency = true; + Log.sinfoln("MQTT", F("Emergency mode enabled")); + } } - client.loop(); - bool published = publishNonStaticHaEntities(); - publish(published); + return; + } + + this->client->loop(); + + // delay for publish data + if (!this->isReadyForSend()) { + return; + } + + // publish variables and status + if (this->newConnection || millis() - this->prevPubVars > settings.mqtt.interval) { + this->client->publish( + this->getTopicPath("status").c_str(), + !vars.states.otStatus ? "offline" : vars.states.fault ? "fault" : "online" + ); + this->client->loop(); + + this->publishVariables(this->getTopicPath("state").c_str()); + this->client->loop(); + this->prevPubVars = millis(); + } + + // publish settings + if (this->newConnection || millis() - this->prevPubSettings > settings.mqtt.interval * 10) { + this->publishSettings(this->getTopicPath("settings").c_str()); + this->client->loop(); + this->prevPubSettings = millis(); + } + + // publish ha entities if not published + if (this->newConnection) { + this->publishHaEntities(); + this->publishNonStaticHaEntities(true); + this->newConnection = false; + + } else { + // publish non static ha entities + this->publishNonStaticHaEntities(); + } + } + + void onConnect() { + this->connectedTime = millis(); + this->newConnection = true; + unsigned long downtime = (millis() - this->disconnectedTime) / 1000; + Log.sinfoln("MQTT", F("Connected (downtime: %u s.)"), downtime); + + if (vars.states.emergency) { + vars.states.emergency = false; + Log.sinfoln("MQTT", F("Emergency mode disabled")); + } + + this->client->subscribe(this->getTopicPath("settings/set").c_str()); + this->client->subscribe(this->getTopicPath("state/set").c_str()); + } + + void onDisconnect() { + this->disconnectedTime = millis(); + + unsigned long uptime = (millis() - this->connectedTime) / 1000; + Log.swarningln("MQTT", F("Disconnected (reason: %d uptime: %u s.)"), this->client->state(), uptime); + } + + void onMessage(char* topic, byte* payload, unsigned int length) { + if (!length) { + return; + } + + if (settings.debug) { + Log.strace("MQTT.MSG", F("Topic: %s\r\n> "), topic); + if (Log.lock()) { + for (size_t i = 0; i < length; i++) { + if (payload[i] == 0) { + break; + } else if (payload[i] == 13) { + continue; + } else if (payload[i] == 10) { + Log.print("\r\n> "); + } else { + Log.print((char) payload[i]); + } + } + Log.print("\r\n\n"); + Log.flush(); + Log.unlock(); + } + } + + JsonDocument doc; + DeserializationError dErr = deserializeJson(doc, payload, length); + if (dErr != DeserializationError::Ok || doc.isNull()) { + const char* errMsg; + switch (dErr.code()) { + case DeserializationError::EmptyInput: + case DeserializationError::IncompleteInput: + case DeserializationError::InvalidInput: + errMsg = "invalid input"; + break; + + case DeserializationError::NoMemory: + errMsg = "no memory"; + break; + + case DeserializationError::TooDeep: + errMsg = "too deep"; + break; + + default: + errMsg = "failed"; + break; + } + Log.swarningln("MQTT.MSG", F("No deserialization: %s"), errMsg); + + return; + } + + if (this->getTopicPath("state/set").compare(topic) == 0) { + this->client->publish(this->getTopicPath("state/set").c_str(), NULL, true); + this->updateVariables(doc); + + } else if (this->getTopicPath("settings/set").compare(topic) == 0) { + this->client->publish(this->getTopicPath("settings/set").c_str(), NULL, true); + this->updateSettings(doc); } } @@ -313,18 +450,16 @@ protected: } } - if (flag) { + this->prevPubSettings = 0; eeSettings.update(); - publish(true); - return true; } return false; } - bool updateVariables(const JsonDocument& doc) { + bool updateVariables(JsonDocument& doc) { bool flag = false; if (!doc["ping"].isNull() && doc["ping"]) { @@ -370,108 +505,83 @@ protected: } if (flag) { - publish(true); - + this->prevPubVars = 0; return true; } return false; } - void publish(bool force = false) { - static unsigned int prevPubVars = 0; - static unsigned int prevPubSettings = 0; - - // publish variables and status - if (force || millis() - prevPubVars > settings.mqtt.interval) { - publishVariables(getTopicPath("state").c_str()); - - if (vars.states.fault) { - client.publish(getTopicPath("status").c_str(), "fault"); - } else { - client.publish(getTopicPath("status").c_str(), vars.states.otStatus ? "online" : "offline"); - } - - prevPubVars = millis(); - } - - // publish settings - if (force || millis() - prevPubSettings > settings.mqtt.interval * 10) { - publishSettings(getTopicPath("settings").c_str()); - prevPubSettings = millis(); - } - } - void publishHaEntities() { // main - haHelper.publishSelectOutdoorSensorType(); - haHelper.publishSelectIndoorSensorType(); - haHelper.publishNumberOutdoorSensorOffset(false); - haHelper.publishNumberIndoorSensorOffset(false); - haHelper.publishSwitchDebug(false); + this->haHelper->publishSelectOutdoorSensorType(); + this->haHelper->publishSelectIndoorSensorType(); + this->haHelper->publishNumberOutdoorSensorOffset(false); + this->haHelper->publishNumberIndoorSensorOffset(false); + this->haHelper->publishSwitchDebug(false); // emergency - haHelper.publishSwitchEmergency(); - haHelper.publishNumberEmergencyTarget(); - haHelper.publishSwitchEmergencyUseEquitherm(); + this->haHelper->publishSwitchEmergency(); + this->haHelper->publishNumberEmergencyTarget(); + this->haHelper->publishSwitchEmergencyUseEquitherm(); // heating - haHelper.publishSwitchHeating(false); - haHelper.publishSwitchHeatingTurbo(); - haHelper.publishNumberHeatingHysteresis(); - haHelper.publishSensorHeatingSetpoint(false); - haHelper.publishSensorCurrentHeatingMinTemp(false); - haHelper.publishSensorCurrentHeatingMaxTemp(false); - haHelper.publishNumberHeatingMinTemp(false); - haHelper.publishNumberHeatingMaxTemp(false); - haHelper.publishNumberHeatingMaxModulation(false); + this->haHelper->publishSwitchHeating(false); + this->haHelper->publishSwitchHeatingTurbo(); + this->haHelper->publishNumberHeatingHysteresis(); + this->haHelper->publishSensorHeatingSetpoint(false); + this->haHelper->publishSensorCurrentHeatingMinTemp(false); + this->haHelper->publishSensorCurrentHeatingMaxTemp(false); + this->haHelper->publishNumberHeatingMinTemp(false); + this->haHelper->publishNumberHeatingMaxTemp(false); + this->haHelper->publishNumberHeatingMaxModulation(false); // pid - haHelper.publishSwitchPID(); - haHelper.publishNumberPIDFactorP(); - haHelper.publishNumberPIDFactorI(); - haHelper.publishNumberPIDFactorD(); - haHelper.publishNumberPIDMinTemp(false); - haHelper.publishNumberPIDMaxTemp(false); + this->haHelper->publishSwitchPID(); + this->haHelper->publishNumberPIDFactorP(); + this->haHelper->publishNumberPIDFactorI(); + this->haHelper->publishNumberPIDFactorD(); + this->haHelper->publishNumberPIDMinTemp(false); + this->haHelper->publishNumberPIDMaxTemp(false); // equitherm - haHelper.publishSwitchEquitherm(); - haHelper.publishNumberEquithermFactorN(); - haHelper.publishNumberEquithermFactorK(); - haHelper.publishNumberEquithermFactorT(); + this->haHelper->publishSwitchEquitherm(); + this->haHelper->publishNumberEquithermFactorN(); + this->haHelper->publishNumberEquithermFactorK(); + this->haHelper->publishNumberEquithermFactorT(); // tuning - haHelper.publishSwitchTuning(); - haHelper.publishSelectTuningRegulator(); + this->haHelper->publishSwitchTuning(); + this->haHelper->publishSelectTuningRegulator(); // states - haHelper.publishBinSensorStatus(); - haHelper.publishBinSensorOtStatus(); - haHelper.publishBinSensorHeating(); - haHelper.publishBinSensorFlame(); - haHelper.publishBinSensorFault(); - haHelper.publishBinSensorDiagnostic(); + this->haHelper->publishBinSensorStatus(); + this->haHelper->publishBinSensorOtStatus(); + this->haHelper->publishBinSensorHeating(); + this->haHelper->publishBinSensorFlame(); + this->haHelper->publishBinSensorFault(); + this->haHelper->publishBinSensorDiagnostic(); // sensors - haHelper.publishSensorModulation(false); - haHelper.publishSensorPressure(false); - haHelper.publishSensorFaultCode(); - haHelper.publishSensorRssi(false); - haHelper.publishSensorUptime(false); + this->haHelper->publishSensorModulation(false); + this->haHelper->publishSensorPressure(false); + this->haHelper->publishSensorFaultCode(); + this->haHelper->publishSensorRssi(false); + this->haHelper->publishSensorUptime(false); // temperatures - haHelper.publishNumberIndoorTemp(); - haHelper.publishSensorHeatingTemp(); + this->haHelper->publishNumberIndoorTemp(); + this->haHelper->publishSensorHeatingTemp(); // buttons - haHelper.publishButtonRestart(false); - haHelper.publishButtonResetFault(); - haHelper.publishButtonResetDiagnostic(); + this->haHelper->publishButtonRestart(false); + this->haHelper->publishButtonResetFault(); + this->haHelper->publishButtonResetDiagnostic(); } bool publishNonStaticHaEntities(bool force = false) { - static byte _heatingMinTemp, _heatingMaxTemp, _dhwMinTemp, _dhwMaxTemp; - static bool _isStupidMode, _editableOutdoorTemp, _editableIndoorTemp, _dhwPresent; + static byte _heatingMinTemp, _heatingMaxTemp, _dhwMinTemp, _dhwMaxTemp = 0; + static bool _isStupidMode, _editableOutdoorTemp, _editableIndoorTemp, _dhwPresent = false; bool published = false; bool isStupidMode = !settings.pid.enable && !settings.equitherm.enable; @@ -484,26 +594,26 @@ protected: _dhwPresent = settings.opentherm.dhwPresent; if (_dhwPresent) { - haHelper.publishSwitchDhw(false); - haHelper.publishSensorCurrentDhwMinTemp(false); - haHelper.publishSensorCurrentDhwMaxTemp(false); - haHelper.publishNumberDhwMinTemp(false); - haHelper.publishNumberDhwMaxTemp(false); - haHelper.publishBinSensorDhw(); - haHelper.publishSensorDhwTemp(); - haHelper.publishSensorDhwFlowRate(false); + this->haHelper->publishSwitchDhw(false); + this->haHelper->publishSensorCurrentDhwMinTemp(false); + this->haHelper->publishSensorCurrentDhwMaxTemp(false); + this->haHelper->publishNumberDhwMinTemp(false); + this->haHelper->publishNumberDhwMaxTemp(false); + this->haHelper->publishBinSensorDhw(); + this->haHelper->publishSensorDhwTemp(); + this->haHelper->publishSensorDhwFlowRate(false); } else { - haHelper.deleteSwitchDhw(); - haHelper.deleteSensorCurrentDhwMinTemp(); - haHelper.deleteSensorCurrentDhwMaxTemp(); - haHelper.deleteNumberDhwMinTemp(); - haHelper.deleteNumberDhwMaxTemp(); - haHelper.deleteBinSensorDhw(); - haHelper.deleteSensorDhwTemp(); - haHelper.deleteNumberDhwTarget(); - haHelper.deleteClimateDhw(); - haHelper.deleteSensorDhwFlowRate(); + this->haHelper->deleteSwitchDhw(); + this->haHelper->deleteSensorCurrentDhwMinTemp(); + this->haHelper->deleteSensorCurrentDhwMaxTemp(); + this->haHelper->deleteNumberDhwMinTemp(); + this->haHelper->deleteNumberDhwMaxTemp(); + this->haHelper->deleteBinSensorDhw(); + this->haHelper->deleteSensorDhwTemp(); + this->haHelper->deleteNumberDhwTarget(); + this->haHelper->deleteClimateDhw(); + this->haHelper->deleteSensorDhwFlowRate(); } published = true; @@ -518,8 +628,8 @@ protected: _heatingMaxTemp = heatingMaxTemp; _isStupidMode = isStupidMode; - haHelper.publishNumberHeatingTarget(heatingMinTemp, heatingMaxTemp, false); - haHelper.publishClimateHeating( + this->haHelper->publishNumberHeatingTarget(heatingMinTemp, heatingMaxTemp, false); + this->haHelper->publishClimateHeating( heatingMinTemp, heatingMaxTemp, isStupidMode ? HaHelper::TEMP_SOURCE_HEATING : HaHelper::TEMP_SOURCE_INDOOR @@ -529,7 +639,7 @@ protected: } else if (_isStupidMode != isStupidMode) { _isStupidMode = isStupidMode; - haHelper.publishClimateHeating( + this->haHelper->publishClimateHeating( heatingMinTemp, heatingMaxTemp, isStupidMode ? HaHelper::TEMP_SOURCE_HEATING : HaHelper::TEMP_SOURCE_INDOOR @@ -542,8 +652,8 @@ protected: _dhwMinTemp = settings.dhw.minTemp; _dhwMaxTemp = settings.dhw.maxTemp; - haHelper.publishNumberDhwTarget(settings.dhw.minTemp, settings.dhw.maxTemp, false); - haHelper.publishClimateDhw(settings.dhw.minTemp, settings.dhw.maxTemp); + this->haHelper->publishNumberDhwTarget(settings.dhw.minTemp, settings.dhw.maxTemp, false); + this->haHelper->publishClimateDhw(settings.dhw.minTemp, settings.dhw.maxTemp); published = true; } @@ -552,11 +662,11 @@ protected: _editableOutdoorTemp = editableOutdoorTemp; if (editableOutdoorTemp) { - haHelper.deleteSensorOutdoorTemp(); - haHelper.publishNumberOutdoorTemp(); + this->haHelper->deleteSensorOutdoorTemp(); + this->haHelper->publishNumberOutdoorTemp(); } else { - haHelper.deleteNumberOutdoorTemp(); - haHelper.publishSensorOutdoorTemp(); + this->haHelper->deleteNumberOutdoorTemp(); + this->haHelper->publishSensorOutdoorTemp(); } published = true; @@ -566,11 +676,11 @@ protected: _editableIndoorTemp = editableIndoorTemp; if (editableIndoorTemp) { - haHelper.deleteSensorIndoorTemp(); - haHelper.publishNumberIndoorTemp(); + this->haHelper->deleteSensorIndoorTemp(); + this->haHelper->publishNumberIndoorTemp(); } else { - haHelper.deleteNumberIndoorTemp(); - haHelper.publishSensorIndoorTemp(); + this->haHelper->deleteNumberIndoorTemp(); + this->haHelper->publishSensorIndoorTemp(); } published = true; @@ -580,8 +690,7 @@ protected: } bool publishSettings(const char* topic) { - StaticJsonDocument<2048> doc; - + JsonDocument doc; doc["debug"] = settings.debug; doc["emergency"]["enable"] = settings.emergency.enable; @@ -619,18 +728,31 @@ protected: doc["sensors"]["indoor"]["type"] = settings.sensors.indoor.type; doc["sensors"]["indoor"]["offset"] = settings.sensors.indoor.offset; - if (!client.beginPublish(topic, measureJson(doc), false)) { - return false; + + size_t docSize = measureJson(doc); + uint8_t* buffer = (uint8_t*) malloc(docSize * sizeof(*buffer)); + size_t length = serializeJson(doc, buffer, docSize); + + size_t written = 0; + if (length != 0) { + if (this->client->beginPublish(topic, docSize, true)) { + for (size_t offset = 0; offset < docSize; offset += 128) { + size_t packetSize = offset + 128 <= docSize ? 128 : docSize - offset; + written += this->client->write(buffer + offset, packetSize); + } + + this->client->flush(); + } } + free(buffer); - serializeJson(doc, *this->bClient); - this->bClient->flush(); + Log.straceln("MQTT", "Publish %u of %u bytes to topic: %s", written, docSize, topic); - return client.endPublish(); + return docSize == written; } bool publishVariables(const char* topic) { - StaticJsonDocument<2048> doc; + JsonDocument doc; doc["tuning"]["enable"] = vars.tuning.enable; doc["tuning"]["regulator"] = vars.tuning.regulator; @@ -661,77 +783,30 @@ protected: doc["parameters"]["dhwMinTemp"] = vars.parameters.dhwMinTemp; doc["parameters"]["dhwMaxTemp"] = vars.parameters.dhwMaxTemp; - if (!client.beginPublish(topic, measureJson(doc), false)) { - return false; - } - serializeJson(doc, *this->bClient); - this->bClient->flush(); - - return client.endPublish(); + size_t docSize = measureJson(doc); + uint8_t* buffer = (uint8_t*) malloc(docSize * sizeof(*buffer)); + size_t length = serializeJson(doc, buffer, docSize); + + size_t written = 0; + if (length != 0) { + if (this->client->beginPublish(topic, docSize, true)) { + for (size_t offset = 0; offset < docSize; offset += 128) { + size_t packetSize = offset + 128 <= docSize ? 128 : docSize - offset; + written += this->client->write(buffer + offset, packetSize); + } + + this->client->flush(); + } + } + free(buffer); + + Log.straceln("MQTT", "Publish %u of %u bytes to topic: %s", written, docSize, topic); + + return docSize == written; } static std::string getTopicPath(const char* topic) { return std::string(settings.mqtt.prefix) + "/" + std::string(topic); } - - void __callback(char* topic, byte* payload, unsigned int length) { - if (!length) { - return; - } - - if (settings.debug) { - Log.strace("MQTT.MSG", F("Topic: %s\r\n> "), topic); - if (Log.lock()) { - for (unsigned int i = 0; i < length; i++) { - if ( payload[i] == 10 ) { - Log.print("\r\n> "); - - } else { - Log.print((char) payload[i]); - } - } - Log.print("\r\n\n"); - Log.flush(); - Log.unlock(); - } - } - - StaticJsonDocument<2048> doc; - DeserializationError dErr = deserializeJson(doc, (const byte*) payload, length); - if (dErr != DeserializationError::Ok || doc.isNull()) { - const char* errMsg; - switch (dErr.code()) { - case DeserializationError::EmptyInput: - case DeserializationError::IncompleteInput: - case DeserializationError::InvalidInput: - errMsg = "invalid input"; - break; - - case DeserializationError::NoMemory: - errMsg = "no memory"; - break; - - case DeserializationError::TooDeep: - errMsg = "too deep"; - break; - - default: - errMsg = "failed"; - break; - } - Log.swarningln("MQTT.MSG", F("No deserialization: %s"), errMsg); - - return; - } - - if (getTopicPath("state/set").compare(topic) == 0) { - updateVariables(doc); - client.publish(getTopicPath("state/set").c_str(), NULL, true); - - } else if (getTopicPath("settings/set").compare(topic) == 0) { - updateSettings(doc); - client.publish(getTopicPath("settings/set").c_str(), NULL, true); - } - } }; \ No newline at end of file