From 2cddbda37918ded5eeeac25c8d58279da61ccc4e Mon Sep 17 00:00:00 2001 From: timpur Date: Fri, 22 Dec 2017 00:15:30 +1100 Subject: [PATCH] Set Device Stats Interval (#451) (#455) --- docs/configuration/json-configuration-file.md | 2 + src/Homie/Boot/BootNormal.cpp | 305 +++++++++--------- src/Homie/Config.cpp | 104 +++--- src/Homie/Constants.hpp | 2 +- src/Homie/Datatypes/ConfigStruct.hpp | 1 + src/Homie/Utils/Validation.cpp | 5 + 6 files changed, 219 insertions(+), 200 deletions(-) diff --git a/docs/configuration/json-configuration-file.md b/docs/configuration/json-configuration-file.md index 87361caa..2924eef8 100644 --- a/docs/configuration/json-configuration-file.md +++ b/docs/configuration/json-configuration-file.md @@ -6,6 +6,7 @@ Below is the format of the JSON configuration you will have to provide: { "name": "The kitchen light", "device_id": "kitchen-light", + "device_stats_interval": 60, "wifi": { "ssid": "Network_1", "password": "I'm a Wi-Fi password!", @@ -50,6 +51,7 @@ Here are the rules: Default values if not provided: * `device_id`: the hardware device ID (eg. `1a2b3c4d5e6f`) +* `device_stats_interval`: 60 seconds * `mqtt.port`: `1883` * `mqtt.base_topic`: `homie/` * `mqtt.auth`: `false` diff --git a/src/Homie/Boot/BootNormal.cpp b/src/Homie/Boot/BootNormal.cpp index 8f8f4fb7..026489c5 100644 --- a/src/Homie/Boot/BootNormal.cpp +++ b/src/Homie/Boot/BootNormal.cpp @@ -21,7 +21,6 @@ BootNormal::BootNormal() , _mqttPayloadBuffer(nullptr) , _mqttTopicLevels(nullptr) , _mqttTopicLevelsCount(0) { - _statsTimer.setInterval(STATS_SEND_INTERVAL); strlcpy(_fwChecksum, ESP.getSketchMD5().c_str(), sizeof(_fwChecksum)); _fwChecksum[sizeof(_fwChecksum) - 1] = '\0'; } @@ -34,6 +33,8 @@ void BootNormal::setup() { Update.runAsync(true); + _statsTimer.setInterval(Interface::get().getConfig().get().deviceStatsInterval * 1000); + if (Interface::get().led.enabled) Interface::get().getBlinker().start(LED_WIFI_DELAY); // Generate topic buffer @@ -201,31 +202,31 @@ void BootNormal::_endOtaUpdate(bool success, uint8_t update_error) { int code; String info; switch (update_error) { - case UPDATE_ERROR_SIZE: // new firmware size is zero - case UPDATE_ERROR_MAGIC_BYTE: // new firmware does not have 0xE9 in first byte - case UPDATE_ERROR_NEW_FLASH_CONFIG: // bad new flash config (does not match flash ID) - code = 400; // 400 Bad Request - info.concat(F("BAD_FIRMWARE")); - break; - case UPDATE_ERROR_MD5: - code = 400; // 400 Bad Request - info.concat(F("BAD_CHECKSUM")); - break; - case UPDATE_ERROR_SPACE: - code = 400; // 400 Bad Request - info.concat(F("NOT_ENOUGH_SPACE")); - break; - case UPDATE_ERROR_WRITE: - case UPDATE_ERROR_ERASE: - case UPDATE_ERROR_READ: - code = 500; // 500 Internal Server Error - info.concat(F("FLASH_ERROR")); - break; - default: - code = 500; // 500 Internal Server Error - info.concat(F("INTERNAL_ERROR ")); - info.concat(update_error); - break; + case UPDATE_ERROR_SIZE: // new firmware size is zero + case UPDATE_ERROR_MAGIC_BYTE: // new firmware does not have 0xE9 in first byte + case UPDATE_ERROR_NEW_FLASH_CONFIG: // bad new flash config (does not match flash ID) + code = 400; // 400 Bad Request + info.concat(F("BAD_FIRMWARE")); + break; + case UPDATE_ERROR_MD5: + code = 400; // 400 Bad Request + info.concat(F("BAD_CHECKSUM")); + break; + case UPDATE_ERROR_SPACE: + code = 400; // 400 Bad Request + info.concat(F("NOT_ENOUGH_SPACE")); + break; + case UPDATE_ERROR_WRITE: + case UPDATE_ERROR_ERASE: + case UPDATE_ERROR_READ: + code = 500; // 500 Internal Server Error + info.concat(F("FLASH_ERROR")); + break; + default: + code = 500; // 500 Internal Server Error + info.concat(F("INTERNAL_ERROR ")); + info.concat(update_error); + break; } _publishOtaStatus(code, info.c_str()); @@ -320,140 +321,144 @@ void BootNormal::_mqttConnect() { void BootNormal::_advertise() { uint16_t packetId; switch (_advertisementProgress.globalStep) { - case AdvertisementProgress::GlobalStep::PUB_HOMIE: - packetId = Interface::get().getMqttClient().publish(_prefixMqttTopic(PSTR("/$homie")), 1, true, HOMIE_VERSION); - if (packetId != 0) _advertisementProgress.globalStep = AdvertisementProgress::GlobalStep::PUB_MAC; - break; - case AdvertisementProgress::GlobalStep::PUB_MAC: - packetId = Interface::get().getMqttClient().publish(_prefixMqttTopic(PSTR("/$mac")), 1, true, WiFi.macAddress().c_str()); - if (packetId != 0) _advertisementProgress.globalStep = AdvertisementProgress::GlobalStep::PUB_NAME; - break; - case AdvertisementProgress::GlobalStep::PUB_NAME: - packetId = Interface::get().getMqttClient().publish(_prefixMqttTopic(PSTR("/$name")), 1, true, Interface::get().getConfig().get().name); - if (packetId != 0) _advertisementProgress.globalStep = AdvertisementProgress::GlobalStep::PUB_LOCALIP; - break; - case AdvertisementProgress::GlobalStep::PUB_LOCALIP: { - IPAddress localIp = WiFi.localIP(); - char localIpStr[MAX_IP_STRING_LENGTH]; - Helpers::ipToString(localIp, localIpStr); - packetId = Interface::get().getMqttClient().publish(_prefixMqttTopic(PSTR("/$localip")), 1, true, localIpStr); - if (packetId != 0) _advertisementProgress.globalStep = AdvertisementProgress::GlobalStep::PUB_STATS_INTERVAL; - break; - } - case AdvertisementProgress::GlobalStep::PUB_STATS_INTERVAL: - char statsIntervalStr[3 + 1]; - itoa(STATS_SEND_INTERVAL / 1000, statsIntervalStr, 10); - packetId = Interface::get().getMqttClient().publish(_prefixMqttTopic(PSTR("/$stats/interval")), 1, true, statsIntervalStr); - if (packetId != 0) _advertisementProgress.globalStep = AdvertisementProgress::GlobalStep::PUB_FW_NAME; - break; - case AdvertisementProgress::GlobalStep::PUB_FW_NAME: - packetId = Interface::get().getMqttClient().publish(_prefixMqttTopic(PSTR("/$fw/name")), 1, true, Interface::get().firmware.name); - if (packetId != 0) _advertisementProgress.globalStep = AdvertisementProgress::GlobalStep::PUB_FW_VERSION; - break; - case AdvertisementProgress::GlobalStep::PUB_FW_VERSION: - packetId = Interface::get().getMqttClient().publish(_prefixMqttTopic(PSTR("/$fw/version")), 1, true, Interface::get().firmware.version); - if (packetId != 0) _advertisementProgress.globalStep = AdvertisementProgress::GlobalStep::PUB_FW_CHECKSUM; - break; - case AdvertisementProgress::GlobalStep::PUB_FW_CHECKSUM: - packetId = Interface::get().getMqttClient().publish(_prefixMqttTopic(PSTR("/$fw/checksum")), 1, true, _fwChecksum); - if (packetId != 0) _advertisementProgress.globalStep = AdvertisementProgress::GlobalStep::PUB_IMPLEMENTATION; - break; - case AdvertisementProgress::GlobalStep::PUB_IMPLEMENTATION: - packetId = Interface::get().getMqttClient().publish(_prefixMqttTopic(PSTR("/$implementation")), 1, true, "esp8266"); - if (packetId != 0) _advertisementProgress.globalStep = AdvertisementProgress::GlobalStep::PUB_IMPLEMENTATION_CONFIG; - break; - case AdvertisementProgress::GlobalStep::PUB_IMPLEMENTATION_CONFIG: { - char* safeConfigFile = Interface::get().getConfig().getSafeConfigFile(); - packetId = Interface::get().getMqttClient().publish(_prefixMqttTopic(PSTR("/$implementation/config")), 1, true, safeConfigFile); - free(safeConfigFile); - if (packetId != 0) _advertisementProgress.globalStep = AdvertisementProgress::GlobalStep::PUB_IMPLEMENTATION_VERSION; - break; - } - case AdvertisementProgress::GlobalStep::PUB_IMPLEMENTATION_VERSION: - packetId = Interface::get().getMqttClient().publish(_prefixMqttTopic(PSTR("/$implementation/version")), 1, true, HOMIE_ESP8266_VERSION); - if (packetId != 0) _advertisementProgress.globalStep = AdvertisementProgress::GlobalStep::PUB_IMPLEMENTATION_OTA_ENABLED; - break; - case AdvertisementProgress::GlobalStep::PUB_IMPLEMENTATION_OTA_ENABLED: - packetId = Interface::get().getMqttClient().publish(_prefixMqttTopic(PSTR("/$implementation/ota/enabled")), 1, true, Interface::get().getConfig().get().ota.enabled ? "true" : "false"); - if (packetId != 0) { - if (HomieNode::nodes.size()) { // skip if no nodes to publish - _advertisementProgress.globalStep = AdvertisementProgress::GlobalStep::PUB_NODES; - _advertisementProgress.nodeStep = AdvertisementProgress::NodeStep::PUB_TYPE; - _advertisementProgress.currentNodeIndex = 0; - } else { - _advertisementProgress.globalStep = AdvertisementProgress::GlobalStep::SUB_IMPLEMENTATION_OTA; - } + case AdvertisementProgress::GlobalStep::PUB_HOMIE: + packetId = Interface::get().getMqttClient().publish(_prefixMqttTopic(PSTR("/$homie")), 1, true, HOMIE_VERSION); + if (packetId != 0) _advertisementProgress.globalStep = AdvertisementProgress::GlobalStep::PUB_MAC; + break; + case AdvertisementProgress::GlobalStep::PUB_MAC: + packetId = Interface::get().getMqttClient().publish(_prefixMqttTopic(PSTR("/$mac")), 1, true, WiFi.macAddress().c_str()); + if (packetId != 0) _advertisementProgress.globalStep = AdvertisementProgress::GlobalStep::PUB_NAME; + break; + case AdvertisementProgress::GlobalStep::PUB_NAME: + packetId = Interface::get().getMqttClient().publish(_prefixMqttTopic(PSTR("/$name")), 1, true, Interface::get().getConfig().get().name); + if (packetId != 0) _advertisementProgress.globalStep = AdvertisementProgress::GlobalStep::PUB_LOCALIP; + break; + case AdvertisementProgress::GlobalStep::PUB_LOCALIP: + { + IPAddress localIp = WiFi.localIP(); + char localIpStr[MAX_IP_STRING_LENGTH]; + Helpers::ipToString(localIp, localIpStr); + packetId = Interface::get().getMqttClient().publish(_prefixMqttTopic(PSTR("/$localip")), 1, true, localIpStr); + if (packetId != 0) _advertisementProgress.globalStep = AdvertisementProgress::GlobalStep::PUB_STATS_INTERVAL; + break; } - break; - case AdvertisementProgress::GlobalStep::PUB_NODES: { - HomieNode* node = HomieNode::nodes[_advertisementProgress.currentNodeIndex]; - std::unique_ptr subtopic = std::unique_ptr(new char[1 + strlen(node->getId()) + 12 + 1]); // /id/$properties - switch (_advertisementProgress.nodeStep) { - case AdvertisementProgress::NodeStep::PUB_TYPE: - strcpy_P(subtopic.get(), PSTR("/")); - strcat(subtopic.get(), node->getId()); - strcat_P(subtopic.get(), PSTR("/$type")); - packetId = Interface::get().getMqttClient().publish(_prefixMqttTopic(subtopic.get()), 1, true, node->getType()); - if (packetId != 0) _advertisementProgress.nodeStep = AdvertisementProgress::NodeStep::PUB_PROPERTIES; + case AdvertisementProgress::GlobalStep::PUB_STATS_INTERVAL: + char statsIntervalStr[3 + 1]; + itoa(STATS_SEND_INTERVAL_SEC / 1000, statsIntervalStr, 10); + packetId = Interface::get().getMqttClient().publish(_prefixMqttTopic(PSTR("/$stats/interval")), 1, true, statsIntervalStr); + if (packetId != 0) _advertisementProgress.globalStep = AdvertisementProgress::GlobalStep::PUB_FW_NAME; break; - case AdvertisementProgress::NodeStep::PUB_PROPERTIES: - strcpy_P(subtopic.get(), PSTR("/")); - strcat(subtopic.get(), node->getId()); - strcat_P(subtopic.get(), PSTR("/$properties")); - String properties; - for (Property* iProperty : node->getProperties()) { - properties.concat(iProperty->getProperty()); - if (iProperty->isRange()) { - properties.concat("["); - properties.concat(iProperty->getLower()); - properties.concat("-"); - properties.concat(iProperty->getUpper()); - properties.concat("]"); - } - if (iProperty->isSettable()) properties.concat(":settable"); - properties.concat(","); - } - if (node->getProperties().size() >= 1) properties.remove(properties.length() - 1); - packetId = Interface::get().getMqttClient().publish(_prefixMqttTopic(subtopic.get()), 1, true, properties.c_str()); + case AdvertisementProgress::GlobalStep::PUB_FW_NAME: + packetId = Interface::get().getMqttClient().publish(_prefixMqttTopic(PSTR("/$fw/name")), 1, true, Interface::get().firmware.name); + if (packetId != 0) _advertisementProgress.globalStep = AdvertisementProgress::GlobalStep::PUB_FW_VERSION; + break; + case AdvertisementProgress::GlobalStep::PUB_FW_VERSION: + packetId = Interface::get().getMqttClient().publish(_prefixMqttTopic(PSTR("/$fw/version")), 1, true, Interface::get().firmware.version); + if (packetId != 0) _advertisementProgress.globalStep = AdvertisementProgress::GlobalStep::PUB_FW_CHECKSUM; + break; + case AdvertisementProgress::GlobalStep::PUB_FW_CHECKSUM: + packetId = Interface::get().getMqttClient().publish(_prefixMqttTopic(PSTR("/$fw/checksum")), 1, true, _fwChecksum); + if (packetId != 0) _advertisementProgress.globalStep = AdvertisementProgress::GlobalStep::PUB_IMPLEMENTATION; + break; + case AdvertisementProgress::GlobalStep::PUB_IMPLEMENTATION: + packetId = Interface::get().getMqttClient().publish(_prefixMqttTopic(PSTR("/$implementation")), 1, true, "esp8266"); + if (packetId != 0) _advertisementProgress.globalStep = AdvertisementProgress::GlobalStep::PUB_IMPLEMENTATION_CONFIG; + break; + case AdvertisementProgress::GlobalStep::PUB_IMPLEMENTATION_CONFIG: + { + char* safeConfigFile = Interface::get().getConfig().getSafeConfigFile(); + packetId = Interface::get().getMqttClient().publish(_prefixMqttTopic(PSTR("/$implementation/config")), 1, true, safeConfigFile); + free(safeConfigFile); + if (packetId != 0) _advertisementProgress.globalStep = AdvertisementProgress::GlobalStep::PUB_IMPLEMENTATION_VERSION; + break; + } + case AdvertisementProgress::GlobalStep::PUB_IMPLEMENTATION_VERSION: + packetId = Interface::get().getMqttClient().publish(_prefixMqttTopic(PSTR("/$implementation/version")), 1, true, HOMIE_ESP8266_VERSION); + if (packetId != 0) _advertisementProgress.globalStep = AdvertisementProgress::GlobalStep::PUB_IMPLEMENTATION_OTA_ENABLED; + break; + case AdvertisementProgress::GlobalStep::PUB_IMPLEMENTATION_OTA_ENABLED: + packetId = Interface::get().getMqttClient().publish(_prefixMqttTopic(PSTR("/$implementation/ota/enabled")), 1, true, Interface::get().getConfig().get().ota.enabled ? "true" : "false"); if (packetId != 0) { - if (_advertisementProgress.currentNodeIndex < HomieNode::nodes.size() - 1) { - _advertisementProgress.currentNodeIndex++; + if (HomieNode::nodes.size()) { // skip if no nodes to publish + _advertisementProgress.globalStep = AdvertisementProgress::GlobalStep::PUB_NODES; _advertisementProgress.nodeStep = AdvertisementProgress::NodeStep::PUB_TYPE; + _advertisementProgress.currentNodeIndex = 0; } else { _advertisementProgress.globalStep = AdvertisementProgress::GlobalStep::SUB_IMPLEMENTATION_OTA; } } break; + case AdvertisementProgress::GlobalStep::PUB_NODES: + { + HomieNode* node = HomieNode::nodes[_advertisementProgress.currentNodeIndex]; + std::unique_ptr subtopic = std::unique_ptr(new char[1 + strlen(node->getId()) + 12 + 1]); // /id/$properties + switch (_advertisementProgress.nodeStep) { + case AdvertisementProgress::NodeStep::PUB_TYPE: + strcpy_P(subtopic.get(), PSTR("/")); + strcat(subtopic.get(), node->getId()); + strcat_P(subtopic.get(), PSTR("/$type")); + packetId = Interface::get().getMqttClient().publish(_prefixMqttTopic(subtopic.get()), 1, true, node->getType()); + if (packetId != 0) _advertisementProgress.nodeStep = AdvertisementProgress::NodeStep::PUB_PROPERTIES; + break; + case AdvertisementProgress::NodeStep::PUB_PROPERTIES: + strcpy_P(subtopic.get(), PSTR("/")); + strcat(subtopic.get(), node->getId()); + strcat_P(subtopic.get(), PSTR("/$properties")); + String properties; + for (Property* iProperty : node->getProperties()) { + properties.concat(iProperty->getProperty()); + if (iProperty->isRange()) { + properties.concat("["); + properties.concat(iProperty->getLower()); + properties.concat("-"); + properties.concat(iProperty->getUpper()); + properties.concat("]"); + } + if (iProperty->isSettable()) properties.concat(":settable"); + properties.concat(","); + } + if (node->getProperties().size() >= 1) properties.remove(properties.length() - 1); + packetId = Interface::get().getMqttClient().publish(_prefixMqttTopic(subtopic.get()), 1, true, properties.c_str()); + if (packetId != 0) { + if (_advertisementProgress.currentNodeIndex < HomieNode::nodes.size() - 1) { + _advertisementProgress.currentNodeIndex++; + _advertisementProgress.nodeStep = AdvertisementProgress::NodeStep::PUB_TYPE; + } else { + _advertisementProgress.globalStep = AdvertisementProgress::GlobalStep::SUB_IMPLEMENTATION_OTA; + } + } + break; + } + break; } - break; - } - case AdvertisementProgress::GlobalStep::SUB_IMPLEMENTATION_OTA: - packetId = Interface::get().getMqttClient().subscribe(_prefixMqttTopic(PSTR("/$implementation/ota/firmware/+")), 1); - if (packetId != 0) _advertisementProgress.globalStep = AdvertisementProgress::GlobalStep::SUB_IMPLEMENTATION_RESET; - break; - case AdvertisementProgress::GlobalStep::SUB_IMPLEMENTATION_RESET: - packetId = Interface::get().getMqttClient().subscribe(_prefixMqttTopic(PSTR("/$implementation/reset")), 1); - if (packetId != 0) _advertisementProgress.globalStep = AdvertisementProgress::GlobalStep::SUB_IMPLEMENTATION_CONFIG_SET; - break; - case AdvertisementProgress::GlobalStep::SUB_IMPLEMENTATION_CONFIG_SET: - packetId = Interface::get().getMqttClient().subscribe(_prefixMqttTopic(PSTR("/$implementation/config/set")), 1); - if (packetId != 0) _advertisementProgress.globalStep = AdvertisementProgress::GlobalStep::SUB_SET; - break; - case AdvertisementProgress::GlobalStep::SUB_SET: - packetId = Interface::get().getMqttClient().subscribe(_prefixMqttTopic(PSTR("/+/+/set")), 2); - if (packetId != 0) _advertisementProgress.globalStep = AdvertisementProgress::GlobalStep::SUB_BROADCAST; - break; - case AdvertisementProgress::GlobalStep::SUB_BROADCAST: { - String broadcast_topic(Interface::get().getConfig().get().mqtt.baseTopic); - broadcast_topic.concat("$broadcast/+"); - packetId = Interface::get().getMqttClient().subscribe(broadcast_topic.c_str(), 2); - if (packetId != 0) _advertisementProgress.globalStep = AdvertisementProgress::GlobalStep::PUB_ONLINE; - break; - } - case AdvertisementProgress::GlobalStep::PUB_ONLINE: - packetId = Interface::get().getMqttClient().publish(_prefixMqttTopic(PSTR("/$online")), 1, true, "true"); - if (packetId != 0) _advertisementProgress.done = true; - break; + case AdvertisementProgress::GlobalStep::SUB_IMPLEMENTATION_OTA: + packetId = Interface::get().getMqttClient().subscribe(_prefixMqttTopic(PSTR("/$implementation/ota/firmware/+")), 1); + if (packetId != 0) _advertisementProgress.globalStep = AdvertisementProgress::GlobalStep::SUB_IMPLEMENTATION_RESET; + break; + case AdvertisementProgress::GlobalStep::SUB_IMPLEMENTATION_RESET: + packetId = Interface::get().getMqttClient().subscribe(_prefixMqttTopic(PSTR("/$implementation/reset")), 1); + if (packetId != 0) _advertisementProgress.globalStep = AdvertisementProgress::GlobalStep::SUB_IMPLEMENTATION_CONFIG_SET; + break; + case AdvertisementProgress::GlobalStep::SUB_IMPLEMENTATION_CONFIG_SET: + packetId = Interface::get().getMqttClient().subscribe(_prefixMqttTopic(PSTR("/$implementation/config/set")), 1); + if (packetId != 0) _advertisementProgress.globalStep = AdvertisementProgress::GlobalStep::SUB_SET; + break; + case AdvertisementProgress::GlobalStep::SUB_SET: + packetId = Interface::get().getMqttClient().subscribe(_prefixMqttTopic(PSTR("/+/+/set")), 2); + if (packetId != 0) _advertisementProgress.globalStep = AdvertisementProgress::GlobalStep::SUB_BROADCAST; + break; + case AdvertisementProgress::GlobalStep::SUB_BROADCAST: + { + String broadcast_topic(Interface::get().getConfig().get().mqtt.baseTopic); + broadcast_topic.concat("$broadcast/+"); + packetId = Interface::get().getMqttClient().subscribe(broadcast_topic.c_str(), 2); + if (packetId != 0) _advertisementProgress.globalStep = AdvertisementProgress::GlobalStep::PUB_ONLINE; + break; + } + case AdvertisementProgress::GlobalStep::PUB_ONLINE: + packetId = Interface::get().getMqttClient().publish(_prefixMqttTopic(PSTR("/$online")), 1, true, "true"); + if (packetId != 0) _advertisementProgress.done = true; + break; } } diff --git a/src/Homie/Config.cpp b/src/Homie/Config.cpp index 1d60c0f9..0c6d5465 100644 --- a/src/Homie/Config.cpp +++ b/src/Homie/Config.cpp @@ -3,9 +3,9 @@ using namespace HomieInternals; Config::Config() -: _configStruct() -, _spiffsBegan(false) -, _valid(false) { + : _configStruct() + , _spiffsBegan(false) + , _valid(false) { } bool Config::_spiffsBegin() { @@ -67,6 +67,10 @@ bool Config::load() { if (parsedJson.containsKey("device_id")) { reqDeviceId = parsedJson["device_id"]; } + uint16_t regDeviceStatsInterval = STATS_SEND_INTERVAL_SEC; //device_stats_interval + if (parsedJson.containsKey(F("device_stats_interval"))) { + regDeviceStatsInterval = parsedJson[F("device_stats_interval")]; + } const char* reqWifiBssid = ""; if (parsedJson["wifi"].as().containsKey("bssid")) { @@ -125,6 +129,7 @@ bool Config::load() { strlcpy(_configStruct.name, reqName, MAX_FRIENDLY_NAME_LENGTH); strlcpy(_configStruct.deviceId, reqDeviceId, MAX_DEVICE_ID_LENGTH); + _configStruct.deviceStatsInterval = regDeviceStatsInterval; strlcpy(_configStruct.wifi.ssid, reqWifiSsid, MAX_WIFI_SSID_LENGTH); if (reqWifiPassword) strlcpy(_configStruct.wifi.password, reqWifiPassword, MAX_WIFI_PASSWORD_LENGTH); strlcpy(_configStruct.wifi.bssid, reqWifiBssid, MAX_MAC_STRING_LENGTH + 6); @@ -254,62 +259,62 @@ void Config::write(const JsonObject& config) { } bool Config::patch(const char* patch) { - if (!_spiffsBegin()) { return false; } + if (!_spiffsBegin()) { return false; } - StaticJsonBuffer patchJsonBuffer; - JsonObject& patchObject = patchJsonBuffer.parseObject(patch); + StaticJsonBuffer patchJsonBuffer; + JsonObject& patchObject = patchJsonBuffer.parseObject(patch); - if (!patchObject.success()) { - Interface::get().getLogger() << F("✖ Invalid or too big JSON") << endl; - return false; - } + if (!patchObject.success()) { + Interface::get().getLogger() << F("✖ Invalid or too big JSON") << endl; + return false; + } - File configFile = SPIFFS.open(CONFIG_FILE_PATH, "r"); - if (!configFile) { - Interface::get().getLogger() << F("✖ Cannot open config file") << endl; - return false; - } + File configFile = SPIFFS.open(CONFIG_FILE_PATH, "r"); + if (!configFile) { + Interface::get().getLogger() << F("✖ Cannot open config file") << endl; + return false; + } - size_t configSize = configFile.size(); - - char configJson[MAX_JSON_CONFIG_FILE_SIZE]; - configFile.readBytes(configJson, configSize); - configFile.close(); - configJson[configSize] = '\0'; - - StaticJsonBuffer configJsonBuffer; - JsonObject& configObject = configJsonBuffer.parseObject(configJson); - - // To do alow object that dont currently exist to be added like settings. - // if settings wasnt there origionally then it should be allowed to be added by incremental. - for (JsonObject::iterator it = patchObject.begin(); it != patchObject.end(); ++it) { - if (patchObject[it->key].is()) { - JsonObject& subObject = patchObject[it->key].as(); - for (JsonObject::iterator it2 = subObject.begin(); it2 != subObject.end(); ++it2) { - if (!configObject.containsKey(it->key) || !configObject[it->key].is()) { - String error = "✖ Config does not contain a "; - error.concat(it->key); - error.concat(" object"); - Interface::get().getLogger() << error << endl; - return false; - } - JsonObject& subConfigObject = configObject[it->key].as(); - subConfigObject[it2->key] = it2->value; + size_t configSize = configFile.size(); + + char configJson[MAX_JSON_CONFIG_FILE_SIZE]; + configFile.readBytes(configJson, configSize); + configFile.close(); + configJson[configSize] = '\0'; + + StaticJsonBuffer configJsonBuffer; + JsonObject& configObject = configJsonBuffer.parseObject(configJson); + + // To do alow object that dont currently exist to be added like settings. + // if settings wasnt there origionally then it should be allowed to be added by incremental. + for (JsonObject::iterator it = patchObject.begin(); it != patchObject.end(); ++it) { + if (patchObject[it->key].is()) { + JsonObject& subObject = patchObject[it->key].as(); + for (JsonObject::iterator it2 = subObject.begin(); it2 != subObject.end(); ++it2) { + if (!configObject.containsKey(it->key) || !configObject[it->key].is()) { + String error = "✖ Config does not contain a "; + error.concat(it->key); + error.concat(" object"); + Interface::get().getLogger() << error << endl; + return false; } - } else { - configObject[it->key] = it->value; + JsonObject& subConfigObject = configObject[it->key].as(); + subConfigObject[it2->key] = it2->value; } + } else { + configObject[it->key] = it->value; } + } - ConfigValidationResult configValidationResult = Validation::validateConfig(configObject); - if (!configValidationResult.valid) { - Interface::get().getLogger() << F("✖ Config file is not valid, reason: ") << configValidationResult.reason << endl; - return false; - } + ConfigValidationResult configValidationResult = Validation::validateConfig(configObject); + if (!configValidationResult.valid) { + Interface::get().getLogger() << F("✖ Config file is not valid, reason: ") << configValidationResult.reason << endl; + return false; + } - write(configObject); + write(configObject); - return true; + return true; } bool Config::isValid() const { @@ -321,6 +326,7 @@ void Config::log() const { Interface::get().getLogger() << F(" • Hardware device ID: ") << DeviceId::get() << endl; Interface::get().getLogger() << F(" • Device ID: ") << _configStruct.deviceId << endl; Interface::get().getLogger() << F(" • Name: ") << _configStruct.name << endl; + Interface::get().getLogger() << F(" • Device Stats Interval: ") << _configStruct.deviceStatsInterval << F(" sec") << endl; Interface::get().getLogger() << F(" • Wi-Fi: ") << endl; Interface::get().getLogger() << F(" ◦ SSID: ") << _configStruct.wifi.ssid << endl; diff --git a/src/Homie/Constants.hpp b/src/Homie/Constants.hpp index 042af268..20f4ffc3 100644 --- a/src/Homie/Constants.hpp +++ b/src/Homie/Constants.hpp @@ -18,7 +18,7 @@ namespace HomieInternals { const char DEFAULT_BRAND[] = "Homie"; const uint16_t CONFIG_SCAN_INTERVAL = 20 * 1000; - const uint32_t STATS_SEND_INTERVAL = 1 * 60 * 1000; + const uint32_t STATS_SEND_INTERVAL_SEC = 1 * 60; const uint16_t MQTT_RECONNECT_INITIAL_INTERVAL = 1000; const uint8_t MQTT_RECONNECT_MAX_BACKOFF = 6; diff --git a/src/Homie/Datatypes/ConfigStruct.hpp b/src/Homie/Datatypes/ConfigStruct.hpp index 06e8df98..5d68c638 100644 --- a/src/Homie/Datatypes/ConfigStruct.hpp +++ b/src/Homie/Datatypes/ConfigStruct.hpp @@ -7,6 +7,7 @@ namespace HomieInternals { struct ConfigStruct { char name[MAX_FRIENDLY_NAME_LENGTH]; char deviceId[MAX_DEVICE_ID_LENGTH]; + uint16_t deviceStatsInterval; struct WiFi { char ssid[MAX_WIFI_SSID_LENGTH]; diff --git a/src/Homie/Utils/Validation.cpp b/src/Homie/Utils/Validation.cpp index 4195cbc4..84f67696 100644 --- a/src/Homie/Utils/Validation.cpp +++ b/src/Homie/Utils/Validation.cpp @@ -48,6 +48,11 @@ ConfigValidationResult Validation::_validateConfigRoot(const JsonObject& object) return result; } + if (object.containsKey(F("device_stats_interval")) && !object[F("device_stats_interval")].is()) { + result.reason = F("device_stats_interval is not an integer"); + return result; + } + result.valid = true; return result; }