diff --git a/lib/BufferedWebServer/BufferedWebServer.h b/lib/BufferedWebServer/BufferedWebServer.h index a991556..66edaed 100644 --- a/lib/BufferedWebServer/BufferedWebServer.h +++ b/lib/BufferedWebServer/BufferedWebServer.h @@ -77,10 +77,19 @@ public: return; } - this->webServer->sendContent((const char*)this->buffer, this->bufferPos); - this->bufferPos = 0; #ifdef ARDUINO_ARCH_ESP8266 - ::delay(0); + ::optimistic_yield(1000); + #endif + + auto& client = this->webServer->client(); + if (client.connected()) { + this->webServer->sendContent((const char*)this->buffer, this->bufferPos); + } + + this->bufferPos = 0; + + #ifdef ARDUINO_ARCH_ESP8266 + ::optimistic_yield(1000); #endif } diff --git a/src/HaHelper.h b/src/HaHelper.h index 94c434c..989117d 100644 --- a/src/HaHelper.h +++ b/src/HaHelper.h @@ -37,7 +37,7 @@ public: case Sensors::Purpose::DHW_RETURN_TEMP: case Sensors::Purpose::EXHAUST_TEMP: case Sensors::Purpose::TEMPERATURE: - doc[FPSTR(HA_DEVICE_CLASS)] = F("temperature"); + doc[FPSTR(HA_DEVICE_CLASS)] = FPSTR(S_TEMPERATURE); if (unit == UnitSystem::METRIC) { doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_C); @@ -87,7 +87,7 @@ public: break; case Sensors::Purpose::HUMIDITY: - doc[FPSTR(HA_DEVICE_CLASS)] = F("humidity"); + doc[FPSTR(HA_DEVICE_CLASS)] = FPSTR(S_HUMIDITY); doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_PERCENT); break; @@ -156,23 +156,25 @@ public: String objId = Sensors::makeObjectId(sSensor.name); // state topic - doc[FPSTR(HA_STATE_TOPIC)] = this->getDeviceTopic(F("sensors"), objId.c_str()); + doc[FPSTR(HA_STATE_TOPIC)] = this->getDeviceTopic( + F("sensors"), + objId.c_str() + ); // set device class, name, value template for bluetooth sensors // or name & value template for another sensors - String sName = sSensor.name; - if (sSensor.type == Sensors::Type::BLUETOOTH) { // available state topic doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_TOPIC)] = doc[FPSTR(HA_STATE_TOPIC)]; doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_VALUE_TEMPLATE)] = AVAILABILITY_SENSOR_CONN; + String sName = sSensor.name; switch (vType) { case Sensors::ValueType::TEMPERATURE: - objId = Sensors::makeObjectIdWithSuffix(sSensor.name, F("temp")); + Sensors::makeObjectIdWithSuffix(objId, sSensor.name, F("temp")); sName += F(" temperature"); - doc[FPSTR(HA_DEVICE_CLASS)] = F("temperature"); + doc[FPSTR(HA_DEVICE_CLASS)] = FPSTR(S_TEMPERATURE); if (unit == UnitSystem::METRIC) { doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_C); @@ -184,20 +186,20 @@ public: break; case Sensors::ValueType::HUMIDITY: - objId = Sensors::makeObjectIdWithSuffix(sSensor.name, F("humidity")); + Sensors::makeObjectIdWithSuffix(objId, sSensor.name, FPSTR(S_HUMIDITY)); sName += F(" humidity"); - doc[FPSTR(HA_DEVICE_CLASS)] = F("humidity"); + doc[FPSTR(HA_DEVICE_CLASS)] = FPSTR(S_HUMIDITY); doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_PERCENT); doc[FPSTR(HA_NAME)] = sName; doc[FPSTR(HA_VALUE_TEMPLATE)] = F("{{ value_json.humidity|float(0)|round(2) }}"); break; case Sensors::ValueType::BATTERY: - objId = Sensors::makeObjectIdWithSuffix(sSensor.name, F("battery")); + Sensors::makeObjectIdWithSuffix(objId, sSensor.name, FPSTR(S_BATTERY)); sName += F(" battery"); - doc[FPSTR(HA_DEVICE_CLASS)] = F("battery"); + doc[FPSTR(HA_DEVICE_CLASS)] = FPSTR(S_BATTERY); doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_DIAGNOSTIC); doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_PERCENT); doc[FPSTR(HA_NAME)] = sName; @@ -205,7 +207,7 @@ public: break; case Sensors::ValueType::RSSI: - objId = Sensors::makeObjectIdWithSuffix(sSensor.name, F("rssi")); + Sensors::makeObjectIdWithSuffix(objId, sSensor.name, FPSTR(S_RSSI)); sName += F(" RSSI"); doc[FPSTR(HA_DEVICE_CLASS)] = F("signal_strength"); @@ -223,10 +225,14 @@ public: doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_CONFIG); doc[FPSTR(HA_MODE)] = FPSTR(HA_MODE_BOX); - doc[FPSTR(HA_COMMAND_TOPIC)] = this->getDeviceTopic(F("sensors"), objId.c_str(), F("set")); + doc[FPSTR(HA_COMMAND_TOPIC)] = this->getDeviceTopic( + F("sensors"), + objId.c_str(), + F("set") + ); doc[FPSTR(HA_COMMAND_TEMPLATE)] = F("{\"value\": {{ value }}}"); - doc[FPSTR(HA_NAME)] = sName; + doc[FPSTR(HA_NAME)] = sSensor.name; doc[FPSTR(HA_VALUE_TEMPLATE)] = F("{{ value_json.value|float(0)|round(2) }}"); } else { @@ -234,20 +240,15 @@ public: doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_TOPIC)] = doc[FPSTR(HA_STATE_TOPIC)]; doc[FPSTR(HA_AVAILABILITY)][1][FPSTR(HA_VALUE_TEMPLATE)] = AVAILABILITY_SENSOR_CONN; - doc[FPSTR(HA_NAME)] = sName; + doc[FPSTR(HA_NAME)] = sSensor.name; doc[FPSTR(HA_VALUE_TEMPLATE)] = F("{{ value_json.value|float(0)|round(2) }}"); } - sName.clear(); - // object id's - { - String objIdWithPrefix = this->getObjectIdWithPrefix(objId.c_str()); - doc[FPSTR(HA_UNIQUE_ID)] = objIdWithPrefix; - doc[FPSTR(HA_OBJECT_ID)] = objIdWithPrefix; - } + doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(objId.c_str()); + doc[FPSTR(HA_OBJECT_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; - String configTopic = this->makeConfigTopic( + const String& configTopic = this->makeConfigTopic( sSensor.type == Sensors::Type::MANUAL ? FPSTR(HA_ENTITY_NUMBER) : FPSTR(HA_ENTITY_SENSOR), objId.c_str() ); @@ -264,32 +265,35 @@ public: } bool deleteDynamicSensor(Sensors::Settings& sSensor, Sensors::ValueType vType = Sensors::ValueType::PRIMARY) { - String objId = Sensors::makeObjectId(sSensor.name); + String objId; if (sSensor.type == Sensors::Type::BLUETOOTH) { switch (vType) { case Sensors::ValueType::TEMPERATURE: - objId = Sensors::makeObjectIdWithSuffix(sSensor.name, F("temp")); + Sensors::makeObjectIdWithSuffix(objId, sSensor.name, F("temp")); break; case Sensors::ValueType::HUMIDITY: - objId = Sensors::makeObjectIdWithSuffix(sSensor.name, F("humidity")); + Sensors::makeObjectIdWithSuffix(objId, sSensor.name, FPSTR(S_HUMIDITY)); break; case Sensors::ValueType::BATTERY: - objId = Sensors::makeObjectIdWithSuffix(sSensor.name, F("battery")); + Sensors::makeObjectIdWithSuffix(objId, sSensor.name, FPSTR(S_BATTERY)); break; case Sensors::ValueType::RSSI: - objId = Sensors::makeObjectIdWithSuffix(sSensor.name, F("rssi")); + Sensors::makeObjectIdWithSuffix(objId, sSensor.name, FPSTR(S_RSSI)); break; default: return false; } + + } else { + Sensors::makeObjectId(objId, sSensor.name); } - String configTopic = this->makeConfigTopic( + const String& configTopic = this->makeConfigTopic( sSensor.type == Sensors::Type::MANUAL ? FPSTR(HA_ENTITY_NUMBER) : FPSTR(HA_ENTITY_SENSOR), objId.c_str() ); @@ -303,18 +307,14 @@ public: String objId = Sensors::makeObjectIdWithSuffix(sSensor.name, F("connected")); // object id's - { - String objIdWithPrefix = this->getObjectIdWithPrefix(objId.c_str()); - doc[FPSTR(HA_UNIQUE_ID)] = objIdWithPrefix; - doc[FPSTR(HA_OBJECT_ID)] = objIdWithPrefix; - } + doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(objId.c_str()); + doc[FPSTR(HA_OBJECT_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; // state topic - { - String parentObjId = Sensors::makeObjectId(sSensor.name); - String stateTopic = this->getDeviceTopic(F("sensors"), parentObjId.c_str()); - doc[FPSTR(HA_STATE_TOPIC)] = stateTopic; - } + doc[FPSTR(HA_STATE_TOPIC)] = this->getDeviceTopic( + F("sensors"), + Sensors::makeObjectId(sSensor.name).c_str() + ); // sensor name { @@ -325,7 +325,7 @@ public: doc[FPSTR(HA_NAME)] = sName; } - String configTopic = this->makeConfigTopic(FPSTR(HA_ENTITY_BINARY_SENSOR), objId.c_str()); + const String& configTopic = this->makeConfigTopic(FPSTR(HA_ENTITY_BINARY_SENSOR), objId.c_str()); objId.clear(); @@ -341,9 +341,10 @@ public: } bool deleteConnectionDynamicSensor(Sensors::Settings& sSensor) { - String objId = Sensors::makeObjectIdWithSuffix(sSensor.name, F("connected")); - String configTopic = this->makeConfigTopic(FPSTR(HA_ENTITY_BINARY_SENSOR), objId.c_str()); - objId.clear(); + const String& configTopic = this->makeConfigTopic( + FPSTR(HA_ENTITY_BINARY_SENSOR), + Sensors::makeObjectIdWithSuffix(sSensor.name, F("connected")).c_str() + ); return this->publish(configTopic.c_str()); } @@ -353,18 +354,14 @@ public: String objId = Sensors::makeObjectIdWithSuffix(sSensor.name, F("signal_quality")); // object id's - { - String objIdWithPrefix = this->getObjectIdWithPrefix(objId.c_str()); - doc[FPSTR(HA_UNIQUE_ID)] = objIdWithPrefix; - doc[FPSTR(HA_OBJECT_ID)] = objIdWithPrefix; - } + doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(objId.c_str()); + doc[FPSTR(HA_OBJECT_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; // state topic - { - String parentObjId = Sensors::makeObjectId(sSensor.name); - String stateTopic = this->getDeviceTopic(F("sensors"), parentObjId.c_str()); - doc[FPSTR(HA_STATE_TOPIC)] = stateTopic; - } + doc[FPSTR(HA_STATE_TOPIC)] = this->getDeviceTopic( + F("sensors"), + Sensors::makeObjectId(sSensor.name).c_str() + ); // sensor name { @@ -375,7 +372,7 @@ public: doc[FPSTR(HA_NAME)] = sName; } - String configTopic = this->makeConfigTopic(FPSTR(HA_ENTITY_SENSOR), objId.c_str()); + const String& configTopic = this->makeConfigTopic(FPSTR(HA_ENTITY_SENSOR), objId.c_str()); objId.clear(); @@ -395,9 +392,10 @@ public: bool deleteSignalQualityDynamicSensor(Sensors::Settings& sSensor) { JsonDocument doc; - String objId = Sensors::makeObjectIdWithSuffix(sSensor.name, F("signal_quality")); - String configTopic = this->makeConfigTopic(FPSTR(HA_ENTITY_SENSOR), objId.c_str()); - objId.clear(); + const String& configTopic = this->makeConfigTopic( + FPSTR(HA_ENTITY_SENSOR), + Sensors::makeObjectIdWithSuffix(sSensor.name, F("signal_quality")).c_str() + ); return this->publish(configTopic.c_str()); } @@ -432,7 +430,7 @@ public: doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(F("heating_hysteresis")); doc[FPSTR(HA_OBJECT_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_CONFIG); - doc[FPSTR(HA_DEVICE_CLASS)] = F("temperature"); + doc[FPSTR(HA_DEVICE_CLASS)] = FPSTR(S_TEMPERATURE); if (unit == UnitSystem::METRIC) { doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_C); @@ -488,7 +486,7 @@ public: doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(F("heating_min_temp")); doc[FPSTR(HA_OBJECT_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_CONFIG); - doc[FPSTR(HA_DEVICE_CLASS)] = F("temperature"); + doc[FPSTR(HA_DEVICE_CLASS)] = FPSTR(S_TEMPERATURE); if (unit == UnitSystem::METRIC) { doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_C); @@ -522,7 +520,7 @@ public: doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(F("heating_max_temp")); doc[FPSTR(HA_OBJECT_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_CONFIG); - doc[FPSTR(HA_DEVICE_CLASS)] = F("temperature"); + doc[FPSTR(HA_DEVICE_CLASS)] = FPSTR(S_TEMPERATURE); if (unit == UnitSystem::METRIC) { doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_C); @@ -557,7 +555,7 @@ public: doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(F("dhw_min_temp")); doc[FPSTR(HA_OBJECT_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_CONFIG); - doc[FPSTR(HA_DEVICE_CLASS)] = F("temperature"); + doc[FPSTR(HA_DEVICE_CLASS)] = FPSTR(S_TEMPERATURE); if (unit == UnitSystem::METRIC) { doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_C); @@ -591,7 +589,7 @@ public: doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(F("dhw_max_temp")); doc[FPSTR(HA_OBJECT_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_CONFIG); - doc[FPSTR(HA_DEVICE_CLASS)] = F("temperature"); + doc[FPSTR(HA_DEVICE_CLASS)] = FPSTR(S_TEMPERATURE); if (unit == UnitSystem::METRIC) { doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_C); @@ -742,7 +740,7 @@ public: doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(F("pid_min_temp")); doc[FPSTR(HA_OBJECT_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_CONFIG); - doc[FPSTR(HA_DEVICE_CLASS)] = F("temperature"); + doc[FPSTR(HA_DEVICE_CLASS)] = FPSTR(S_TEMPERATURE); if (unit == UnitSystem::METRIC) { doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_C); @@ -776,7 +774,7 @@ public: doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(F("pid_max_temp")); doc[FPSTR(HA_OBJECT_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_CONFIG); - doc[FPSTR(HA_DEVICE_CLASS)] = F("temperature"); + doc[FPSTR(HA_DEVICE_CLASS)] = FPSTR(S_TEMPERATURE); if (unit == UnitSystem::METRIC) { doc[FPSTR(HA_UNIT_OF_MEASUREMENT)] = FPSTR(HA_UNIT_OF_MEASUREMENT_C); @@ -1119,7 +1117,7 @@ public: JsonDocument doc; doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->statusTopic.c_str(); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; - doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(F("rssi")); + doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(FPSTR(S_RSSI)); doc[FPSTR(HA_OBJECT_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_DIAGNOSTIC); doc[FPSTR(HA_DEVICE_CLASS)] = F("signal_strength"); @@ -1132,7 +1130,7 @@ public: doc[FPSTR(HA_EXPIRE_AFTER)] = this->expireAfter; doc.shrinkToFit(); - return this->publish(this->makeConfigTopic(FPSTR(HA_ENTITY_SENSOR), F("rssi")).c_str(), doc); + return this->publish(this->makeConfigTopic(FPSTR(HA_ENTITY_SENSOR), FPSTR(S_RSSI)).c_str(), doc); } bool publishUptime(bool enabledByDefault = true) { @@ -1257,17 +1255,17 @@ public: JsonDocument doc; doc[FPSTR(HA_AVAILABILITY)][FPSTR(HA_TOPIC)] = this->statusTopic.c_str(); doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; - doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(F("restart")); + doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(FPSTR(S_RESTART)); doc[FPSTR(HA_OBJECT_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_CONFIG); - doc[FPSTR(HA_DEVICE_CLASS)] = F("restart"); + doc[FPSTR(HA_DEVICE_CLASS)] = FPSTR(S_RESTART); doc[FPSTR(HA_NAME)] = F("Restart"); doc[FPSTR(HA_COMMAND_TOPIC)] = this->setStateTopic.c_str(); doc[FPSTR(HA_COMMAND_TEMPLATE)] = F("{\"actions\": {\"restart\": true}}"); doc[FPSTR(HA_EXPIRE_AFTER)] = this->expireAfter; doc.shrinkToFit(); - return this->publish(this->makeConfigTopic(FPSTR(HA_ENTITY_BUTTON), F("restart")).c_str(), doc); + return this->publish(this->makeConfigTopic(FPSTR(HA_ENTITY_BUTTON), FPSTR(S_RESTART)).c_str(), doc); } bool publishResetFaultButton(bool enabledByDefault = true) { @@ -1280,7 +1278,7 @@ public: doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(F("reset_fault")); doc[FPSTR(HA_OBJECT_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_CONFIG); - doc[FPSTR(HA_DEVICE_CLASS)] = F("restart"); + doc[FPSTR(HA_DEVICE_CLASS)] = FPSTR(S_RESTART); doc[FPSTR(HA_NAME)] = F("Reset fault"); doc[FPSTR(HA_COMMAND_TOPIC)] = this->setStateTopic.c_str(); doc[FPSTR(HA_COMMAND_TEMPLATE)] = F("{\"actions\": {\"resetFault\": true}}"); @@ -1300,7 +1298,7 @@ public: doc[FPSTR(HA_UNIQUE_ID)] = this->getObjectIdWithPrefix(F("reset_diagnostic")); doc[FPSTR(HA_OBJECT_ID)] = doc[FPSTR(HA_UNIQUE_ID)]; doc[FPSTR(HA_ENTITY_CATEGORY)] = FPSTR(HA_ENTITY_CATEGORY_CONFIG); - doc[FPSTR(HA_DEVICE_CLASS)] = F("restart"); + doc[FPSTR(HA_DEVICE_CLASS)] = FPSTR(S_RESTART); doc[FPSTR(HA_NAME)] = F("Reset diagnostic"); doc[FPSTR(HA_COMMAND_TOPIC)] = this->setStateTopic.c_str(); doc[FPSTR(HA_COMMAND_TEMPLATE)] = F("{\"actions\": {\"resetDiagnostic\": true}}"); diff --git a/src/MainTask.h b/src/MainTask.h index 7c238f6..09795aa 100644 --- a/src/MainTask.h +++ b/src/MainTask.h @@ -76,17 +76,6 @@ protected: vars.actions.restart = false; this->restartSignalTime = millis(); - // save settings - fsSettings.updateNow(); - - // save sensors settings - fsSensorsSettings.updateNow(); - - // force save network settings - if (fsNetworkSettings.updateNow() == FD_FILE_ERR && LittleFS.begin()) { - fsNetworkSettings.write(); - } - Log.sinfoln(FPSTR(L_MAIN), F("Restart signal received. Restart after 10 sec.")); } @@ -147,8 +136,9 @@ protected: for (Stream* stream : Log.getStreams()) { while (stream->available() > 0) { stream->read(); + #ifdef ARDUINO_ARCH_ESP8266 - ::delay(0); + ::optimistic_yield(1000); #endif } } @@ -159,7 +149,19 @@ protected: // restart if (this->restartSignalTime > 0 && millis() - this->restartSignalTime > 10000) { + // save settings + fsSettings.updateNow(); + + // save sensors settings + fsSensorsSettings.updateNow(); + + // force save network settings + if (fsNetworkSettings.updateNow() == FD_FILE_ERR && LittleFS.begin()) { + fsNetworkSettings.write(); + } + this->restartSignalTime = 0; + this->delay(500); ESP.restart(); } } diff --git a/src/MqttTask.h b/src/MqttTask.h index b201ace..a85c7e5 100644 --- a/src/MqttTask.h +++ b/src/MqttTask.h @@ -129,7 +129,7 @@ protected: #endif this->client->onMessage([this] (void*, size_t length) { - String topic = this->client->messageTopic(); + const String& topic = this->client->messageTopic(); if (!length || length > 2048 || !topic.length()) { return; } @@ -139,7 +139,7 @@ protected: payload[i] = this->client->read(); } - this->onMessage(topic.c_str(), payload, length); + this->onMessage(topic, payload, length); }); // writer settings @@ -153,7 +153,7 @@ protected: Log.straceln(FPSTR(L_MQTT), F("%s publish %u of %u bytes to topic: %s"), result ? F("Successfully") : F("Failed"), written, length, topic); #ifdef ARDUINO_ARCH_ESP8266 - ::delay(0); + ::optimistic_yield(1000); #endif //this->client->poll(); @@ -162,13 +162,13 @@ protected: #ifdef ARDUINO_ARCH_ESP8266 this->writer->setFlushEventCallback([this] (size_t, size_t) { - ::delay(0); + ::optimistic_yield(1000); if (this->wifiClient->connected()) { this->wifiClient->flush(); } - ::delay(0); + ::optimistic_yield(1000); }); #endif @@ -222,7 +222,7 @@ protected: } #ifdef ARDUINO_ARCH_ESP8266 - ::delay(0); + ::optimistic_yield(1000); #endif // publish variables and status @@ -295,14 +295,14 @@ protected: this->haHelper->deleteDynamicSensor(prevSettings, Sensors::ValueType::TEMPERATURE); break; - case Sensors::Type::MANUAL: { - String topic = this->haHelper->getDeviceTopic( - F("sensors"), - Sensors::makeObjectId(prevSettings.name).c_str(), - F("set") + case Sensors::Type::MANUAL: + this->client->unsubscribe( + this->haHelper->getDeviceTopic( + F("sensors"), + Sensors::makeObjectId(prevSettings.name).c_str(), + F("set") + ).c_str() ); - this->client->unsubscribe(topic.c_str()); - } default: this->haHelper->deleteDynamicSensor(prevSettings, Sensors::ValueType::PRIMARY); @@ -331,14 +331,14 @@ protected: this->haHelper->publishDynamicSensor(sSettings, Sensors::ValueType::TEMPERATURE, settings.system.unitSystem); break; - case Sensors::Type::MANUAL: { - String topic = this->haHelper->getDeviceTopic( - F("sensors"), - Sensors::makeObjectId(prevSettings.name).c_str(), - F("set") + case Sensors::Type::MANUAL: + this->client->subscribe( + this->haHelper->getDeviceTopic( + F("sensors"), + Sensors::makeObjectId(prevSettings.name).c_str(), + F("set") + ).c_str() ); - this->client->subscribe(topic.c_str()); - } default: this->haHelper->publishDynamicSensor(sSettings, Sensors::ValueType::PRIMARY, settings.system.unitSystem); @@ -372,13 +372,13 @@ protected: Log.swarningln(FPSTR(L_MQTT), F("Disconnected (reason: %d uptime: %lu s.)"), this->client->connectError(), uptime); } - void onMessage(const char* topic, uint8_t* payload, size_t length) { + void onMessage(const String& topic, uint8_t* payload, size_t length) { if (!length) { return; } if (settings.system.logLevel >= TinyLogger::Level::TRACE) { - Log.strace(FPSTR(L_MQTT_MSG), F("Topic: %s\r\n> "), topic); + Log.strace(FPSTR(L_MQTT_MSG), F("Topic: %s\r\n> "), topic.c_str()); if (Log.lock()) { for (size_t i = 0; i < length; i++) { if (payload[i] == 0) { @@ -409,31 +409,27 @@ protected: } doc.shrinkToFit(); + // delete topic + this->writer->publish(topic.c_str(), nullptr, 0, true); + if (this->haHelper->getDeviceTopic(F("state/set")).equals(topic)) { - this->writer->publish(topic, nullptr, 0, true); - if (jsonToVars(doc, vars)) { this->resetPublishedVarsTime(); } } else if (this->haHelper->getDeviceTopic(F("settings/set")).equals(topic)) { - this->writer->publish(topic, nullptr, 0, true); - if (safeJsonToSettings(doc, settings)) { this->resetPublishedSettingsTime(); fsSettings.update(); } } else { - this->writer->publish(topic, nullptr, 0, true); - - String _topic = topic; - String sensorsTopic = this->haHelper->getDeviceTopic(F("sensors/")); + const String& sensorsTopic = this->haHelper->getDeviceTopic(F("sensors/")); auto stLength = sensorsTopic.length(); - if (_topic.startsWith(sensorsTopic) && _topic.endsWith(F("/set"))) { - if (_topic.length() > stLength + 4) { - String name = _topic.substring(stLength, _topic.indexOf('/', stLength)); + if (topic.startsWith(sensorsTopic) && topic.endsWith(F("/set"))) { + if (topic.length() > stLength + 4) { + const String& name = topic.substring(stLength, topic.indexOf('/', stLength)); int16_t id = Sensors::getIdByObjectId(name.c_str()); if (id == -1) { @@ -515,14 +511,14 @@ protected: this->haHelper->publishDynamicSensor(sSettings, Sensors::ValueType::TEMPERATURE, settings.system.unitSystem); break; - case Sensors::Type::MANUAL: { - String topic = this->haHelper->getDeviceTopic( - F("sensors"), - Sensors::makeObjectId(sSettings.name).c_str(), - F("set") + case Sensors::Type::MANUAL: + this->client->subscribe( + this->haHelper->getDeviceTopic( + F("sensors"), + Sensors::makeObjectId(sSettings.name).c_str(), + F("set") + ).c_str() ); - this->client->subscribe(topic.c_str()); - } default: this->haHelper->publishDynamicSensor(sSettings, Sensors::ValueType::PRIMARY, settings.system.unitSystem); @@ -603,12 +599,14 @@ protected: sensorResultToJson(sensorId, doc); doc.shrinkToFit(); - String topic = this->haHelper->getDeviceTopic( - F("sensors"), - Sensors::makeObjectId(sSettings.name).c_str() + return this->writer->publish( + this->haHelper->getDeviceTopic( + F("sensors"), + Sensors::makeObjectId(sSettings.name).c_str() + ).c_str(), + doc, + true ); - - return this->writer->publish(topic.c_str(), doc, true); } bool publishVariables(const char* topic) { diff --git a/src/PortalTask.h b/src/PortalTask.h index baed4de..fd1a2b2 100644 --- a/src/PortalTask.h +++ b/src/PortalTask.h @@ -232,7 +232,7 @@ protected: } } - String plain = this->webServer->arg(0); + const String& plain = this->webServer->arg(0); Log.straceln(FPSTR(L_PORTAL_WEBSERVER), F("Request /api/backup/restore %d bytes: %s"), plain.length(), plain.c_str()); if (plain.length() < 5) { @@ -246,7 +246,6 @@ protected: JsonDocument doc; DeserializationError dErr = deserializeJson(doc, plain); - plain.clear(); if (dErr != DeserializationError::Ok || doc.isNull() || !doc.size()) { this->webServer->send(400); @@ -254,12 +253,6 @@ protected: } bool changed = false; - if (!doc[FPSTR(S_SETTINGS)].isNull() && jsonToSettings(doc[FPSTR(S_SETTINGS)], settings)) { - vars.actions.restart = true; - fsSettings.update(); - changed = true; - } - if (!doc[FPSTR(S_NETWORK)].isNull() && jsonToNetworkSettings(doc[FPSTR(S_NETWORK)], networkSettings)) { fsNetworkSettings.update(); network->setHostname(networkSettings.hostname) @@ -271,27 +264,40 @@ protected: networkSettings.staticConfig.gateway, networkSettings.staticConfig.subnet, networkSettings.staticConfig.dns - ) - ->reconnect(); + ); + changed = true; + } + + if (!doc[FPSTR(S_SETTINGS)].isNull() && jsonToSettings(doc[FPSTR(S_SETTINGS)], settings)) { + fsSettings.update(); changed = true; } if (!doc[FPSTR(S_SENSORS)].isNull()) { - for (uint8_t sensorId = 0; sensorId <= Sensors::getMaxSensorId(); sensorId++) { - if (doc[FPSTR(S_SENSORS)][sensorId].isNull()) { + for (auto sensor : doc[FPSTR(S_SENSORS)].as()) { + if (!isDigit(sensor.key().c_str())) { continue; } - auto sensorSettingsDoc = doc[FPSTR(S_SENSORS)][sensorId].to(); - if (jsonToSensorSettings(sensorId, sensorSettingsDoc, Sensors::settings[sensorId])){ + int sensorId = atoi(sensor.key().c_str()); + if (sensorId < 0 || sensorId > 255 || !Sensors::isValidSensorId(sensorId)) { + continue; + } + + if (jsonToSensorSettings(sensorId, sensor.value(), Sensors::settings[sensorId])) { + fsSensorsSettings.update(); changed = true; } } } - + doc.clear(); doc.shrinkToFit(); + if (changed) { + vars.actions.restart = true; + } + this->webServer->send(changed ? 201 : 200); }); @@ -317,7 +323,7 @@ protected: } } - String plain = this->webServer->arg(0); + const String& plain = this->webServer->arg(0); Log.straceln(FPSTR(L_PORTAL_WEBSERVER), F("Request /api/network/settings %d bytes: %s"), plain.length(), plain.c_str()); if (plain.length() < 5) { @@ -331,7 +337,6 @@ protected: JsonDocument doc; DeserializationError dErr = deserializeJson(doc, plain); - plain.clear(); if (dErr != DeserializationError::Ok || doc.isNull() || !doc.size()) { this->webServer->send(400); @@ -390,7 +395,7 @@ protected: JsonDocument doc; for (short int i = 0; i < apCount; i++) { - String ssid = WiFi.SSID(i); + const String& ssid = WiFi.SSID(i); doc[i][FPSTR(S_SSID)] = ssid; doc[i][FPSTR(S_BSSID)] = WiFi.BSSIDstr(i); doc[i][FPSTR(S_SIGNAL_QUALITY)] = NetworkMgr::rssiToSignalQuality(WiFi.RSSI(i)); @@ -433,7 +438,7 @@ protected: } } - String plain = this->webServer->arg(0); + const String& plain = this->webServer->arg(0); Log.straceln(FPSTR(L_PORTAL_WEBSERVER), F("Request /api/settings %d bytes: %s"), plain.length(), plain.c_str()); if (plain.length() < 5) { @@ -447,7 +452,6 @@ protected: JsonDocument doc; DeserializationError dErr = deserializeJson(doc, plain); - plain.clear(); if (dErr != DeserializationError::Ok || doc.isNull() || !doc.size()) { this->webServer->send(400); @@ -618,7 +622,7 @@ protected: } } - String plain = this->webServer->arg(0); + const String& plain = this->webServer->arg(0); Log.straceln(FPSTR(L_PORTAL_WEBSERVER), F("Request /api/vars %d bytes: %s"), plain.length(), plain.c_str()); if (plain.length() < 5) { @@ -632,7 +636,6 @@ protected: JsonDocument doc; DeserializationError dErr = deserializeJson(doc, plain); - plain.clear(); if (dErr != DeserializationError::Ok || doc.isNull() || !doc.size()) { this->webServer->send(400); @@ -829,7 +832,7 @@ protected: this->webServer->onNotFound([this]() { Log.straceln(FPSTR(L_PORTAL_WEBSERVER), F("Page not found, uri: %s"), this->webServer->uri().c_str()); - const String uri = this->webServer->uri(); + const String& uri = this->webServer->uri(); if (uri.equals(F("/"))) { this->webServer->send(200, F("text/plain"), F("The file system is not flashed!")); @@ -856,7 +859,7 @@ protected: Log.straceln(FPSTR(L_PORTAL_WEBSERVER), F("Started: AP up or STA connected")); #ifdef ARDUINO_ARCH_ESP8266 - ::delay(0); + ::optimistic_yield(1000); #endif } else if (this->stateWebServer() && !network->isApEnabled() && !network->isStaEnabled()) { @@ -864,7 +867,7 @@ protected: Log.straceln(FPSTR(L_PORTAL_WEBSERVER), F("Stopped: AP and STA down")); #ifdef ARDUINO_ARCH_ESP8266 - ::delay(0); + ::optimistic_yield(1000); #endif } @@ -874,7 +877,7 @@ protected: Log.straceln(FPSTR(L_PORTAL_DNSSERVER), F("Started: AP up")); #ifdef ARDUINO_ARCH_ESP8266 - ::delay(0); + ::optimistic_yield(1000); #endif } else if (this->stateDnsServer() && (!network->isApEnabled() || !this->stateWebServer())) { @@ -882,14 +885,15 @@ protected: Log.straceln(FPSTR(L_PORTAL_DNSSERVER), F("Stopped: AP down")); #ifdef ARDUINO_ARCH_ESP8266 - ::delay(0); + ::optimistic_yield(1000); #endif } if (this->stateDnsServer()) { this->dnsServer->processNextRequest(); + #ifdef ARDUINO_ARCH_ESP8266 - ::delay(0); + ::optimistic_yield(1000); #endif } @@ -907,7 +911,7 @@ protected: } void onCaptivePortal() { - const String uri = this->webServer->uri(); + const String& uri = this->webServer->uri(); if (uri.equals(F("/connecttest.txt"))) { this->webServer->sendHeader(F("Location"), F("http://logout.net")); @@ -927,10 +931,10 @@ protected: } else { String portalUrl = F("http://"); - portalUrl += network->getApIp().toString(); + portalUrl += network->getApIp().toString().c_str(); portalUrl += '/'; - this->webServer->sendHeader(F("Location"), portalUrl.c_str()); + this->webServer->sendHeader(F("Location"), portalUrl); this->webServer->send(302); Log.straceln(FPSTR(L_PORTAL_CAPTIVE), F("Redirect to portal page with 302 code")); diff --git a/src/Sensors.h b/src/Sensors.h index 4c6735c..ed06f34 100644 --- a/src/Sensors.h +++ b/src/Sensors.h @@ -146,9 +146,10 @@ public: return 0; } + String refObjectId; for (uint8_t id = 0; id < getMaxSensorId(); id++) { - String _objectId = Sensors::makeObjectId(settings[id].name); - if (strcmp(_objectId.c_str(), objectId) == 0) { + Sensors::makeObjectId(refObjectId, settings[id].name); + if (refObjectId.equals(objectId)) { return id; } } @@ -351,13 +352,10 @@ public: return false; } - template - static String cleanName(T value, char space = ' ') { - String clean = value; - + static String& cleanName(String& value, char space = ' ') { // only valid symbols - for (uint8_t pos = 0; pos < clean.length(); pos++) { - char symbol = clean.charAt(pos); + for (uint8_t pos = 0; pos < value.length(); pos++) { + char symbol = value.charAt(pos); // 0..9 if (symbol >= 48 && symbol <= 57) { @@ -379,39 +377,68 @@ public: continue; } - clean.setCharAt(pos, space); + value.setCharAt(pos, space); } - clean.trim(); + value.trim(); - return clean; + return value; + } + + template + static String cleanName(T value, char space = ' ') { + String res = value; + return cleanName(res, space); + } + + template + static String& makeObjectId(String& res, T value, char separator = '_') { + res = value; + cleanName(res); + res.toLowerCase(); + res.replace(' ', separator); + + return res; } template static String makeObjectId(T value, char separator = '_') { - auto objId = cleanName(value); - objId.toLowerCase(); - objId.replace(' ', separator); + String res; + makeObjectId(res, value, separator); - return objId; + return res; } template - static auto makeObjectIdWithSuffix(TV value, TS suffix, char separator = '_') { - auto objId = makeObjectId(value, separator); - objId += separator; - objId += suffix; + static String& makeObjectIdWithSuffix(String& res, TV value, TS suffix, char separator = '_') { + res.clear(); + makeObjectId(res, value, separator); + res += separator; + res += suffix; - return objId; + return res; + } + + template + static String makeObjectIdWithSuffix(TV value, TS suffix, char separator = '_') { + String res; + makeObjectIdWithSuffix(res, value, suffix, separator); + return res; } template - static auto makeObjectIdWithPrefix(TV value, TP prefix, char separator = '_') { - String objId = prefix; - objId += separator; - objId += makeObjectId(value, separator); + static String& makeObjectIdWithPrefix(String& res, TV value, TP prefix, char separator = '_') { + res = prefix; + res += separator; + res += makeObjectId(value, separator).c_str(); - return objId; + return res; + } + + template + static String makeObjectIdWithPrefix(TV value, TP prefix, char separator = '_') { + String res; + return makeObjectIdWithPrefix(res, value, prefix, separator); } static uint8_t bluetoothRssiToQuality(int rssi) { diff --git a/src/Settings.h b/src/Settings.h index 7b3f59b..0f22fff 100644 --- a/src/Settings.h +++ b/src/Settings.h @@ -154,7 +154,7 @@ Sensors::Settings sensorsSettings[SENSORS_AMOUNT] = { false, "Outdoor temp", Sensors::Purpose::OUTDOOR_TEMP, - Sensors::Type::DALLAS_TEMP, + Sensors::Type::OT_OUTDOOR_TEMP, DEFAULT_SENSOR_OUTDOOR_GPIO }, { diff --git a/src/utils.h b/src/utils.h index 0eac8c7..d928a21 100644 --- a/src/utils.h +++ b/src/utils.h @@ -1461,7 +1461,8 @@ bool jsonToSensorSettings(const uint8_t sensorId, const JsonVariantConst src, Se // name if (!src[FPSTR(S_NAME)].isNull()) { - String value = Sensors::cleanName(src[FPSTR(S_NAME)].as()); + auto value = src[FPSTR(S_NAME)].as(); + Sensors::cleanName(value); if (value.length() < sizeof(dst.name) && !value.equals(dst.name)) { strcpy(dst.name, value.c_str()); diff --git a/src_data/scripts/utils.js b/src_data/scripts/utils.js index 5bdc88d..122d273 100644 --- a/src_data/scripts/utils.js +++ b/src_data/scripts/utils.js @@ -306,22 +306,6 @@ const setupRestoreBackupForm = (formSelector) => { try { const data = JSON.parse(event.target.result); console.log("Backup: ", data); - - if (data.network != undefined) { - let response = await fetch(url, { - method: 'POST', - cache: 'no-cache', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({"network": data.network}) - }); - - if (!response.ok) { - onFailed(); - return; - } - } if (data.settings != undefined) { let response = await fetch(url, { @@ -362,6 +346,22 @@ const setupRestoreBackupForm = (formSelector) => { } } + if (data.network != undefined) { + let response = await fetch(url, { + method: 'POST', + cache: 'no-cache', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({"network": data.network}) + }); + + if (!response.ok) { + onFailed(); + return; + } + } + onSuccess(); } catch (err) {