From c4075655aa0dbbf51fae701f66b0266e53e33fa2 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Thu, 12 Aug 2021 22:33:02 +0200 Subject: [PATCH] Initial support for influxdb using ``#define USE_INFLUXDB`` and several ``Ifx`` commands --- CHANGELOG.md | 1 + RELEASENOTES.md | 1 + tasmota/my_user_config.h | 9 + tasmota/settings.h | 2 +- tasmota/xsns_90_influxdb.ino | 449 +++++++++++++++++++++++++++++++++++ 5 files changed, 461 insertions(+), 1 deletion(-) create mode 100644 tasmota/xsns_90_influxdb.ino diff --git a/CHANGELOG.md b/CHANGELOG.md index beece4184..b2776016c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ All notable changes to this project will be documented in this file. - Inital support for Wi-Fi extender (#12784) - Neopool commands ``NPPHRes``, ``NPCLRes`` and ``NPIonRes`` (#12813) - Support for (Yeelight) Mi Desk Pro using binary tasmota32solo1.bin +- Initial support for influxdb using ``#define USE_INFLUXDB`` and several ``Ifx`` commands ### Changed - NeoPixelBus library from v2.6.3 to v2.6.7 diff --git a/RELEASENOTES.md b/RELEASENOTES.md index ecb41e6de..7d4304165 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -106,6 +106,7 @@ The latter links can be used for OTA upgrades too like ``OtaUrl http://ota.tasmo - Neopool commands ``NPPHRes``, ``NPCLRes`` and ``NPIonRes`` [#12813](https://github.com/arendst/Tasmota/issues/12813) - Support for second DNS server - Support for (Yeelight) Mi Desk Pro using binary tasmota32solo1.bin +- Initial support for influxdb using ``#define USE_INFLUXDB`` and several ``Ifx`` commands - Initial support for Tasmota Mesh (TasMesh) providing node/broker communication using ESP-NOW [#11939](https://github.com/arendst/Tasmota/issues/11939) - Berry ESP32 partition manager [#12465](https://github.com/arendst/Tasmota/issues/12465) - Berry ESP32 support for I2S audio mp3 playback diff --git a/tasmota/my_user_config.h b/tasmota/my_user_config.h index d9f4056a2..93a72de70 100644 --- a/tasmota/my_user_config.h +++ b/tasmota/my_user_config.h @@ -390,6 +390,15 @@ // -- OTA ----------------------------------------- //#define USE_ARDUINO_OTA // Add optional support for Arduino OTA (+13k code) +// -- Influxdb ------------------------------------ +//#define USE_INFLUXDB // Enable influxdb support (+5k code) +// #define INFLUXDB_VERSION 1 // Version of Influxdb 1 or 2 +// #define INFLUXDB_HOST "influxdb" // [IfxHost] Influxdb hostname or IP address +// #define INFLUXDB_PORT 8086 // [IfxPort] Influxdb port number +// #define INFLUXDB_ORG "" // [IfxUser, IfxOrg] Influxdb v1 username or v2 organisation +// #define INFLUXDB_TOKEN "" // [IfxPassword, IfxToken] Influxdb v1 password or v2 token +// #define INFLUXDB_BUCKET "db" // [IfxDatabase, IfxBucket] Influxdb v1 database or v2 bucket + // -- MQTT ---------------------------------------- #define MQTT_LWT_OFFLINE "Offline" // MQTT LWT offline topic message #define MQTT_LWT_ONLINE "Online" // MQTT LWT online topic message diff --git a/tasmota/settings.h b/tasmota/settings.h index 7de698b91..a931c9132 100644 --- a/tasmota/settings.h +++ b/tasmota/settings.h @@ -248,7 +248,7 @@ typedef union { uint32_t range_extender : 1; // bit 3 (v9.5.0.5) - CMND_RGXSTATE - Enable range extender uint32_t range_extender_napt : 1; // bit 4 (v9.5.0.5) - CMND_RGXNAPT - Enable range extender NAPT uint32_t sonoff_l1_music_sync : 1; // bit 5 (v9.5.0.5) - CMND_L1MUSICSYNC - Enable sync to music - uint32_t spare06 : 1; // bit 6 + uint32_t influxdb_default : 1; // bit 6 (v9.5.0.5) - Set initial defaults if 0 uint32_t spare07 : 1; // bit 7 uint32_t spare08 : 1; // bit 8 uint32_t spare09 : 1; // bit 9 diff --git a/tasmota/xsns_90_influxdb.ino b/tasmota/xsns_90_influxdb.ino new file mode 100644 index 000000000..cddceb9f9 --- /dev/null +++ b/tasmota/xsns_90_influxdb.ino @@ -0,0 +1,449 @@ +/* + xsns_90_influxdb.ino - Influxdb support for Tasmota + + Copyright (C) 2021 Theo Arends + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifdef USE_INFLUXDB +/*********************************************************************************************\ + * Influxdb support + * + * To save over 80k not supporting https this driver uses a subset of library + * https://github.com/tobiasschuerg/InfluxDB-Client-for-Arduino + * + * The text format for metrics, labels and values is documented at + * https://docs.influxdata.com/influxdb/v1.8/write_protocols/line_protocol_reference/ + * + * Supported commands: + * Ifx - Show current state + * IfxHost - Set Influxdb host name or IP address + * IfxPort - Set Influxdb port + * IfxDatabase - Set Influxdb v1 and database name + * IfxUser - Set Influxdb v1 and userid + * IfxPassword - Set Influxdb v1 and password + * IfxBucket - Set Influxdb v2 and bucket name + * IfxOrg - Set Influxdb v2 and organization + * IfxToken - Set Influxdb v2 and token + * + * Set influxdb update interval with command teleperiod +\*********************************************************************************************/ + +#define XSNS_90 90 + +#define INFLUXDB_INITIAL 12 // Initial number of seconds after restart keeping in mind sensor initialization + +#ifndef INFLUXDB_VERSION +#define INFLUXDB_VERSION 1 // Version of Influxdb 1 or 2 +#endif +#ifndef INFLUXDB_HOST +#define INFLUXDB_HOST "influxdb" // [IfxHost] Influxdb hostname or IP address +#endif +#ifndef INFLUXDB_PORT +#define INFLUXDB_PORT 8086 // [IfxPort] Influxdb port number +#endif +#ifndef INFLUXDB_ORG +#define INFLUXDB_ORG "" // [IfxUser, IfxOrg] Influxdb v1 username or v2 organisation +#endif +#ifndef INFLUXDB_TOKEN +#define INFLUXDB_TOKEN "" // [IfxPassword, IfxToken] Influxdb v1 password or v2 token +#endif +#ifndef INFLUXDB_BUCKET +#define INFLUXDB_BUCKET "db" // [IfxDatabase, IfxBucket] Influxdb v1 database or v2 bucket +#endif + +static const char UninitializedMessage[] PROGMEM = "Unconfigured instance"; +// This cannot be put to PROGMEM due to the way how it is used +static const char RetryAfter[] = "Retry-After"; +static const char TransferEncoding[] = "Transfer-Encoding"; + +WiFiClient *IFDBwifiClient = nullptr; +HTTPClient *IFDBhttpClient = nullptr; + +struct { + String _serverUrl; // Connection info + String _writeUrl; // Cached full write url + String _lastErrorResponse; // Server reponse or library error message for last failed request + uint32_t interval = INFLUXDB_INITIAL; + uint32_t _lastRequestTime = 0; // Last time in ms we made are a request to server + int _lastStatusCode = 0; // HTTP status code of last request to server + int _lastRetryAfter = 0; // Store retry timeout suggested by server after last request + bool _connectionReuse; // true if HTTP connection should be kept open. Usable for frequent writes. Default false + bool init = false; +} IFDB; + +String InfluxDbAuth(void) { + String auth = ""; + if (strlen(SettingsText(SET_INFLUXDB_ORG)) > 0 && strlen(SettingsText(SET_INFLUXDB_TOKEN)) > 0) { + auth = "&u="; + auth += UrlEncode(SettingsText(SET_INFLUXDB_ORG)); + auth += "&p="; + auth += UrlEncode(SettingsText(SET_INFLUXDB_TOKEN)); + } + return auth; +} + +bool InfluxDbInit(void) { + IFDB._serverUrl = "http://"; + IFDB._serverUrl += SettingsText(SET_INFLUXDB_HOST); + IFDB._serverUrl += ":"; + IFDB._serverUrl += Settings->influxdb_port; + if (IFDB._serverUrl.length() == 0 || + (2 == Settings->influxdb_version && (strlen(SettingsText(SET_INFLUXDB_ORG)) == 0 || + strlen(SettingsText(SET_INFLUXDB_BUCKET)) == 0 || + strlen(SettingsText(SET_INFLUXDB_TOKEN)) == 0))) { + AddLog(LOG_LEVEL_DEBUG, PSTR("IFX: Invalid parameters")); + return false; + } + IFDB._writeUrl = IFDB._serverUrl; + if (2 == Settings->influxdb_version) { + IFDB._writeUrl += "/api/v2/write?org="; + IFDB._writeUrl += UrlEncode(SettingsText(SET_INFLUXDB_ORG)); + IFDB._writeUrl += "&bucket="; + IFDB._writeUrl += UrlEncode(SettingsText(SET_INFLUXDB_BUCKET)); + } else { + IFDB._writeUrl += "/write?db="; + IFDB._writeUrl += UrlEncode(SettingsText(SET_INFLUXDB_BUCKET)); + IFDB._writeUrl += InfluxDbAuth(); + } +// AddLog(LOG_LEVEL_DEBUG, PSTR("IFX: Url %s"), IFDB._writeUrl.c_str()); + + IFDBwifiClient = new WiFiClient; + if (!IFDBhttpClient) { + IFDBhttpClient = new HTTPClient; + } + IFDBhttpClient->setReuse(IFDB._connectionReuse); + char server[32]; + snprintf_P(server, sizeof(server), PSTR("Tasmota/%s (%s)"), TasmotaGlobal.version, GetDeviceHardware().c_str()); + IFDBhttpClient->setUserAgent(server); + return true; +} + +void InfluxDbBeforeRequest() { + if (strlen(SettingsText(SET_INFLUXDB_TOKEN)) > 0) { + String auth_token = SettingsText(SET_INFLUXDB_TOKEN); + IFDBhttpClient->addHeader(F("Authorization"), "Token " + auth_token); + } + const char *headerKeys[] = { RetryAfter, TransferEncoding }; + IFDBhttpClient->collectHeaders(headerKeys, 2); +} + +void InfluxDbAfterRequest(int expectedStatusCode, bool modifyLastConnStatus) { + if (modifyLastConnStatus) { + IFDB._lastRequestTime = millis(); +// AddLog(LOG_LEVEL_DEBUG, PSTR("IFX: HTTP status code %d"), IFDB._lastStatusCode); + IFDB._lastRetryAfter = 0; + if (IFDB._lastStatusCode >= 429) { //retryable server errors + if (IFDBhttpClient->hasHeader(RetryAfter)) { + IFDB._lastRetryAfter = IFDBhttpClient->header(RetryAfter).toInt(); + AddLog(LOG_LEVEL_DEBUG, PSTR("IFX: Reply after %d"), IFDB._lastRetryAfter); + } + } + } + IFDB._lastErrorResponse = ""; + if (IFDB._lastStatusCode != expectedStatusCode) { + if (IFDB._lastStatusCode > 0) { + IFDB._lastErrorResponse = IFDBhttpClient->getString(); + AddLog(LOG_LEVEL_DEBUG, PSTR("IFX: Response\n%s"), IFDB._lastErrorResponse.c_str()); + } else { + IFDB._lastErrorResponse = IFDBhttpClient->errorToString(IFDB._lastStatusCode); + AddLog(LOG_LEVEL_DEBUG, PSTR("IFX: Error %s"), IFDB._lastErrorResponse.c_str()); + } + } +} + +bool InfluxDbValidateConnection(void) { + if (!IFDBwifiClient && !InfluxDbInit()) { + IFDB._lastStatusCode = 0; + IFDB._lastErrorResponse = FPSTR(UninitializedMessage); + return false; + } + // on version 1.x /ping will by default return status code 204, without verbose + String url = IFDB._serverUrl + (2 == Settings->influxdb_version ? "/health" : "/ping?verbose=true"); + if (1 == Settings->influxdb_version) { + url += InfluxDbAuth(); + } + // on version 1.8.9 /health works fine +// String url = IFDB._serverUrl + "/health"; + AddLog(LOG_LEVEL_DEBUG, PSTR("IFX: Validating connection to %s"), url.c_str()); + + if (!IFDBhttpClient->begin(*IFDBwifiClient, url)) { + AddLog(LOG_LEVEL_DEBUG, PSTR("IFX: Begin failed")); + return false; + } + IFDBhttpClient->addHeader(F("Accept"), F("application/json")); + + IFDB._lastStatusCode = IFDBhttpClient->GET(); + IFDB._lastErrorResponse = ""; + InfluxDbAfterRequest(200, false); + IFDBhttpClient->end(); + + return IFDB._lastStatusCode == 200; +} + +int InfluxDbPostData(const char *data) { + if (!IFDBwifiClient && !InfluxDbInit()) { + IFDB._lastStatusCode = 0; + IFDB._lastErrorResponse = FPSTR(UninitializedMessage); + return 0; + } + if (data) { + if (!IFDBhttpClient->begin(*IFDBwifiClient, IFDB._writeUrl)) { + AddLog(LOG_LEVEL_DEBUG, PSTR("IFX: Begin failed")); + return false; + } + + AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("IFX: Sending\n%s"), data); + IFDBhttpClient->addHeader(F("Content-Type"), F("text/plain")); + InfluxDbBeforeRequest(); + IFDB._lastStatusCode = IFDBhttpClient->POST((uint8_t*)data, strlen(data)); + InfluxDbAfterRequest(204, true); + IFDBhttpClient->end(); + } + return IFDB._lastStatusCode; +} + +/*********************************************************************************************/ + +void InfluxDbMetrics(void) { + ResponseClear(); + if (!MqttShowSensor()) { return; }; // Pull sensor data + +// AddLog(LOG_LEVEL_DEBUG, PSTR("IFX: Metrics %s"), TasmotaGlobal.mqtt_data.c_str()); + + String jsonStr = TasmotaGlobal.mqtt_data; + JsonParser parser((char *)jsonStr.c_str()); + JsonParserObject root = parser.getRootObject(); + if (root) { + char linebuf[128]; // 'temperature,device=demo,sensor=ds18b20,id=01144A0CB2AA value=26.44\n' + char sensor[64]; // 'ds18b20' + char type[64]; // 'temperature' + char sensor_id[32]; // ',id=01144A0CB2AA' + String data = ""; // Multiple linebufs + + for (auto key1 : root) { + JsonParserToken value1 = key1.getValue(); + if (value1.isObject()) { + JsonParserObject Object2 = value1.getObject(); + for (auto key2 : Object2) { + JsonParserToken value2 = key2.getValue(); + if (value2.isObject()) { + JsonParserObject Object3 = value2.getObject(); + for (auto key3 : Object3) { + const char *value = key3.getValue().getStr(); + if (value != nullptr && isdigit(value[0])) { + // Level 3 + LowerCase(sensor, key2.getStr()); + LowerCase(type, key3.getStr()); + // temperature,device=tasmota1,sensor=DS18B20 value=24.44 + snprintf_P(linebuf, sizeof(linebuf), PSTR("%s,device=%s,sensor=%s value=%s\n"), + type, TasmotaGlobal.mqtt_topic, sensor, value); + data += linebuf; + } + } + } else { + // Level 2 + bool isarray = value2.isArray(); + const char *value = (isarray) ? (value2.getArray())[0].getStr() : value2.getStr(); + if (value != nullptr && isdigit(value[0])) { + LowerCase(sensor, key1.getStr()); + LowerCase(type, key2.getStr()); + + if (strcmp(type, "totalstarttime") != 0) { // Not needed/wanted + if (strcmp(type, "id") == 0) { // Index for DS18B20 + snprintf_P(sensor_id, sizeof(sensor_id), PSTR(",id=%s"), value); + } else { + if (isarray) { + JsonParserArray arr = value2.getArray(); + uint32_t i = 0; + for (auto val : arr) { + i++; + // power1,device=shelly25,sensor=energy value=0.00 + // power2,device=shelly25,sensor=energy value=4.12 + snprintf_P(linebuf, sizeof(linebuf), PSTR("%s%d,device=%s,sensor=%s%s value=%s\n"), + type, i, TasmotaGlobal.mqtt_topic, sensor, sensor_id, val.getStr()); + data += linebuf; + } + } else { + // temperature,device=Wemos10,sensor=ds18b20,id=01144A0CB2AA value=22.63 + snprintf_P(linebuf, sizeof(linebuf), PSTR("%s,device=%s,sensor=%s%s value=%s\n"), + type, TasmotaGlobal.mqtt_topic, sensor, sensor_id, value); + data += linebuf; + } + sensor_id[0] = '\0'; + } + } + } + } + } +/* + } else { + // Level 1 + const char *value = value1.getStr(); + LowerCase(sensor, key1.getStr()); + + // time,device=Wemos10 value=2021-08-11T09:46:29 + // tempunit,device=Wemos10 value=C + snprintf_P(linebuf, sizeof(linebuf), PSTR("%s,device=%s value=%s\n"), + sensor, TasmotaGlobal.mqtt_topic, value); + data += linebuf; +*/ + } + } + if (data.length() > 0 ) { +// AddLog(LOG_LEVEL_DEBUG, PSTR("IFX: Sensor data:\n%s"), data.c_str()); + InfluxDbPostData(data.c_str()); + } + } +} + +void InfluxDbLoop(void) { + if (!IFDB.init) { + IFDB.init = InfluxDbValidateConnection(); + } + InfluxDbMetrics(); +} + +/*********************************************************************************************\ + * Commands +\*********************************************************************************************/ + +#define D_PRFX_INFLUXDB "Ifx" +#define D_CMND_INFLUXDBHOST "Host" +#define D_CMND_INFLUXDBPORT "Port" +#define D_CMND_INFLUXDBUSER "User" +#define D_CMND_INFLUXDBORG "Org" +#define D_CMND_INFLUXDBPASSWORD "Password" +#define D_CMND_INFLUXDBTOKEN "Token" +#define D_CMND_INFLUXDBDATABASE "Database" +#define D_CMND_INFLUXDBBUCKET "Bucket" + +const char kInfluxDbCommands[] PROGMEM = D_PRFX_INFLUXDB "|" // Prefix + "|" + D_CMND_INFLUXDBHOST "|" D_CMND_INFLUXDBPORT "|" + D_CMND_INFLUXDBUSER "|" D_CMND_INFLUXDBORG "|" + D_CMND_INFLUXDBPASSWORD "|" D_CMND_INFLUXDBTOKEN "|" + D_CMND_INFLUXDBDATABASE "|" D_CMND_INFLUXDBBUCKET; + +void (* const InfluxCommand[])(void) PROGMEM = { + &CmndInfluxDbState, + &CmndInfluxDbHost, &CmndInfluxDbPort, + &CmndInfluxDbUser, &CmndInfluxDbUser, + &CmndInfluxDbPassword, &CmndInfluxDbPassword, + &CmndInfluxDbDatabase, &CmndInfluxDbDatabase }; + +void InfluxDbReinit(void) { + IFDB.init = false; + IFDB.interval = 2; +} + +void CmndInfluxDbState(void) { + Response_P(PSTR("{\"" D_PRFX_INFLUXDB "\":{\"" D_CMND_INFLUXDBHOST "\":\"%s\",\"" D_CMND_INFLUXDBPORT "\":%d,\"Version\":%d"), + SettingsText(SET_INFLUXDB_HOST), Settings->influxdb_port, Settings->influxdb_version); + if (1 == Settings->influxdb_version) { + ResponseAppend_P(PSTR(",\"" D_CMND_INFLUXDBDATABASE "\":\"%s\",\"" D_CMND_INFLUXDBUSER "\":\"%s\"}}"), + SettingsText(SET_INFLUXDB_BUCKET), SettingsText(SET_INFLUXDB_ORG)); + } else { + ResponseAppend_P(PSTR(",\"" D_CMND_INFLUXDBBUCKET "\":\"%s\",\"" D_CMND_INFLUXDBORG "\":\"%s\"}}"), + SettingsText(SET_INFLUXDB_BUCKET), SettingsText(SET_INFLUXDB_ORG)); + } +} + +void CmndInfluxDbHost(void) { + if (XdrvMailbox.data_len > 0) { + SettingsUpdateText(SET_INFLUXDB_HOST, (SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? PSTR(INFLUXDB_HOST) : XdrvMailbox.data); + InfluxDbReinit(); + } + ResponseCmndChar(SettingsText(SET_INFLUXDB_HOST)); +} + +void CmndInfluxDbPort(void) { + if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload < 65536)) { + Settings->influxdb_port = (1 == XdrvMailbox.payload) ? INFLUXDB_PORT : XdrvMailbox.payload; + InfluxDbReinit(); + } + ResponseCmndNumber(Settings->influxdb_port); +} + +void CmndInfluxDbUser(void) { + if (XdrvMailbox.data_len > 0) { + Settings->influxdb_version = (XdrvMailbox.command[3] == 'U') ? 1 : 2; // User or Org + SettingsUpdateText(SET_INFLUXDB_ORG, (SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? PSTR(INFLUXDB_ORG) : XdrvMailbox.data); + InfluxDbReinit(); + } + ResponseCmndChar(SettingsText(SET_INFLUXDB_ORG)); +} + +void CmndInfluxDbPassword(void) { + bool show_asterisk = (2 == XdrvMailbox.index); + if (XdrvMailbox.data_len > 0) { + Settings->influxdb_version = (XdrvMailbox.command[3] == 'P') ? 1 : 2; // Password or Token + SettingsUpdateText(SET_INFLUXDB_TOKEN, (SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? PSTR(INFLUXDB_TOKEN) : XdrvMailbox.data); + if (!show_asterisk) { + ResponseCmndChar(SettingsText(SET_INFLUXDB_TOKEN)); + } + InfluxDbReinit(); + } else { + show_asterisk = true; + } + if (show_asterisk) { + Response_P(S_JSON_COMMAND_ASTERISK, XdrvMailbox.command); + } +} + +void CmndInfluxDbDatabase(void) { + if (XdrvMailbox.data_len > 0) { + Settings->influxdb_version = (XdrvMailbox.command[3] == 'D') ? 1 : 2; // Database or Bucket + SettingsUpdateText(SET_INFLUXDB_BUCKET, (SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? PSTR(INFLUXDB_BUCKET) : XdrvMailbox.data); + InfluxDbReinit(); + } + ResponseCmndChar(SettingsText(SET_INFLUXDB_BUCKET)); +} + +/*********************************************************************************************\ + * Interface +\*********************************************************************************************/ + +bool Xsns90(uint8_t function) { + bool result = false; + + if (!Settings->sbflag1.influxdb_default) { + Settings->influxdb_version = INFLUXDB_VERSION; + Settings->influxdb_port = INFLUXDB_PORT; + SettingsUpdateText(SET_INFLUXDB_HOST, PSTR(INFLUXDB_HOST)); + SettingsUpdateText(SET_INFLUXDB_ORG, PSTR(INFLUXDB_ORG)); + SettingsUpdateText(SET_INFLUXDB_TOKEN, PSTR(INFLUXDB_TOKEN)); + SettingsUpdateText(SET_INFLUXDB_BUCKET, PSTR(INFLUXDB_BUCKET)); + Settings->sbflag1.influxdb_default = 1; + } + + switch (function) { + case FUNC_EVERY_SECOND: + if (IFDB.interval) { + IFDB.interval--; + if (0 == IFDB.interval || IFDB.interval > Settings->tele_period) { + IFDB.interval = Settings->tele_period; + InfluxDbLoop(); + } + } + break; + case FUNC_COMMAND: + result = DecodeCommand(kInfluxDbCommands, InfluxCommand); + break; + } + return result; +} + +#endif // USE_INFLUXDB