mirror of
https://github.com/letscontrolit/ESPEasy.git
synced 2026-09-15 02:54:26 +00:00
1006 lines
47 KiB
C++
1006 lines
47 KiB
C++
#include "src/Helpers/_CPlugin_Helper.h"
|
|
#ifdef USES_C014
|
|
|
|
# include "src/Commands/ExecuteCommand.h"
|
|
# include "src/DataTypes/NodeTypeID.h"
|
|
# include "src/Globals/Device.h"
|
|
# include "src/Globals/MQTT.h"
|
|
# include "src/Globals/Plugins.h"
|
|
# include "src/Globals/Statistics.h"
|
|
# include "src/Helpers/_CPlugin_Helper_mqtt.h"
|
|
# include "src/Helpers/PeriodicalActions.h"
|
|
# include "_Plugin_Helper.h"
|
|
|
|
// #######################################################################################################
|
|
// ################################# Controller Plugin 014: Homie 3/4 ####################################
|
|
// #######################################################################################################
|
|
|
|
/** Changelog:
|
|
* 2025-08-23 tonhuisman: Add 10/sec call to poll background connection process while not connected
|
|
* 2025-06-18 tonhuisman: Add support for Unit of Measure attribute, when available in the build, sent in AutoDiscovery
|
|
* Enable sending Derived values when available
|
|
* 2024-03-02 tonhuisman: Fix using parseSystemVariables() for processing %sysname%. Might still break the same configurations,
|
|
* logging improvements
|
|
* 2023-10-30 tonhuisman: Fix using getHostname() instead of getName() for %sysname%. This might break some configurations!
|
|
* minor improvements
|
|
* 2023-08-18 tonhuisman: Clean up source to improve resource usage
|
|
* 2023-03-15 tonhuisman: Replace use of deprecated DummyValueSet with TaskValueSet
|
|
* 2023-03 Changelog started
|
|
*/
|
|
|
|
# define CPLUGIN_014
|
|
# define CPLUGIN_ID_014 14
|
|
|
|
// Define which Homie version to use
|
|
# if !defined(CPLUGIN_014_V3) && !defined(CPLUGIN_014_V4)
|
|
|
|
// #define CPLUGIN_014_V3
|
|
# define CPLUGIN_014_V4
|
|
# endif // if !defined(CPLUGIN_014_V3) && !defined(CPLUGIN_014_V4)
|
|
|
|
# ifdef CPLUGIN_014_V3
|
|
# define CPLUGIN_014_HOMIE_VERSION "3.0.0"
|
|
# define CPLUGIN_NAME_014 "Homie MQTT (Version 3.0.1)"
|
|
# endif // ifdef CPLUGIN_014_V3
|
|
# ifdef CPLUGIN_014_V4
|
|
# define CPLUGIN_014_HOMIE_VERSION "4.0.0"
|
|
# define CPLUGIN_NAME_014 "Homie MQTT (Version 4.0.0 dev)"
|
|
# endif // ifdef CPLUGIN_014_V4
|
|
|
|
// subscribe and publish schemes should not be changed by the user. This will probably break the homie convention. Do @ your own risk;)
|
|
# define CPLUGIN_014_SUBSCRIBE "homie/%sysname%/+/+/set" // only subscribe to /set topics to reduce load by receiving all retained
|
|
// messages
|
|
# define CPLUGIN_014_PUBLISH "homie/%sysname%/%tskname%/%valname%"
|
|
|
|
# define CPLUGIN_014_BASE_TOPIC "homie/%sysname%/#"
|
|
# define CPLUGIN_014_BASE_VALUE "homie/%sysname%/%device%/%node%/%property%"
|
|
# define CPLUGIN_014_INTERVAL "90" // to prevent timeout !ToDo set by lowest plugin interval
|
|
# define CPLUGIN_014_SYSTEM_DEVICE "SYSTEM" // name for system device Plugin for cmd and GIO values
|
|
# define CPLUGIN_014_CMD_VALUE "cmd" // name for command value
|
|
# define CPLUGIN_014_GPIO_VALUE "gpio" // name for gpio value i.e. "gpio1"
|
|
# define CPLUGIN_014_GPIO_VALUE_LEN 4 // length of GPIO to avoid creating a String to get the length
|
|
# define CPLUGIN_014_CMD_VALUE_NAME "Command" // human readabele name for command value
|
|
|
|
# define CPLUGIN_014_GPIO_COMMAND "gpio" // name for gpio command
|
|
# define CPLUGIN_014_TASKVALUESET_COMMAND "taskvalueset" // name for taskvalueset command
|
|
# define CPLUGIN_014_HOMIEVALUESET_COMMAND "homievalueset" // name for homievalueset command
|
|
|
|
uint8_t msgCounter = 0; // counter for send Messages (currently for information / log only!
|
|
|
|
String CPlugin_014_pubname;
|
|
bool CPlugin_014_mqtt_retainFlag = false;
|
|
|
|
void C014_replaceSysname(String& var) {
|
|
parseSystemVariables(var, false); // Used to be getName(), but that doesn't include the UnitNr when configured
|
|
}
|
|
|
|
bool CPlugin_014_sendMQTTdevice(String tmppubname,
|
|
taskIndex_t taskIndex,
|
|
const __FlashStringHelper *topic,
|
|
const String & payload,
|
|
int & errorCounter) {
|
|
tmppubname.replace(F("#"), topic);
|
|
bool mqttReturn = MQTTpublish(CPLUGIN_ID_014, taskIndex, tmppubname.c_str(), payload.c_str(), true);
|
|
|
|
if (mqttReturn) {
|
|
msgCounter++;
|
|
} else {
|
|
errorCounter++;
|
|
}
|
|
|
|
String log;
|
|
|
|
if (loglevelActiveFor(LOG_LEVEL_ERROR)) { // Also true for LOG_LEVEL_DEBUG_MORE
|
|
log = strformat(F("C014 : T:%s P: %s"), String(topic).c_str(), payload.c_str());
|
|
}
|
|
# ifndef BUILD_NO_DEBUG
|
|
|
|
if (loglevelActiveFor(LOG_LEVEL_DEBUG_MORE) && mqttReturn) {
|
|
log += F(" success!");
|
|
addLogMove(LOG_LEVEL_DEBUG_MORE, log);
|
|
}
|
|
# endif // ifndef BUILD_NO_DEBUG
|
|
|
|
if (loglevelActiveFor(LOG_LEVEL_ERROR) && !mqttReturn) {
|
|
log += F(" ERROR!");
|
|
addLogMove(LOG_LEVEL_ERROR, log);
|
|
}
|
|
processMQTTdelayQueue();
|
|
return mqttReturn;
|
|
}
|
|
|
|
// send MQTT Message with CPLUGIN_014_BASE_TOPIC Topic scheme / Payload
|
|
bool CPlugin_014_sendMQTTdevice(const String & tmppubname,
|
|
taskIndex_t taskIndex,
|
|
const __FlashStringHelper *topic,
|
|
const __FlashStringHelper *payload,
|
|
int & errorCounter) {
|
|
return CPlugin_014_sendMQTTdevice(tmppubname, taskIndex, topic, String(payload), errorCounter);
|
|
}
|
|
|
|
// send MQTT Message with CPLUGIN_014_BASE_VALUE Topic scheme / Payload
|
|
bool CPlugin_014_sendMQTTnode(String tmppubname,
|
|
const String& node,
|
|
const String& value,
|
|
const String& topic,
|
|
const String& payload,
|
|
int & errorCounter) {
|
|
tmppubname.replace(F("%device%"), node);
|
|
tmppubname.replace(F("%node%"), value);
|
|
tmppubname.replace(F("/%property%"), topic); // leading forward slash required to send "homie/device/value" topics
|
|
bool mqttReturn = MQTTpublish(CPLUGIN_ID_014, INVALID_TASK_INDEX, tmppubname.c_str(), payload.c_str(), true);
|
|
|
|
if (mqttReturn) { msgCounter++; }
|
|
else { errorCounter++; }
|
|
|
|
String log;
|
|
|
|
if (loglevelActiveFor(LOG_LEVEL_ERROR)) { // Also true for LOG_LEVEL_DEBUG_MORE
|
|
log = strformat(F("C014 : V:%s T: %s P: %s"), value.c_str(), topic.c_str(), payload.c_str());
|
|
}
|
|
# ifndef BUILD_NO_DEBUG
|
|
|
|
if (loglevelActiveFor(LOG_LEVEL_DEBUG_MORE) && mqttReturn) {
|
|
log += F(" success!");
|
|
addLogMove(LOG_LEVEL_DEBUG_MORE, log);
|
|
}
|
|
# endif // ifndef BUILD_NO_DEBUG
|
|
|
|
if (loglevelActiveFor(LOG_LEVEL_ERROR) && !mqttReturn) {
|
|
log += F(" ERROR!");
|
|
addLogMove(LOG_LEVEL_ERROR, log);
|
|
}
|
|
processMQTTdelayQueue();
|
|
return mqttReturn;
|
|
}
|
|
|
|
// and String to a comma separated list
|
|
void C014_addToList(String& valuesList, const String& node) {
|
|
if (valuesList.length() > 0) {
|
|
valuesList += ',';
|
|
}
|
|
valuesList += node;
|
|
}
|
|
|
|
bool CPlugin_014(CPlugin::Function function, struct EventStruct *event, String& string)
|
|
{
|
|
bool success = false;
|
|
int errorCounter = 0;
|
|
String pubname;
|
|
String tmppubname;
|
|
|
|
switch (function)
|
|
{
|
|
case CPlugin::Function::CPLUGIN_PROTOCOL_ADD:
|
|
{
|
|
ProtocolStruct& proto = getProtocolStruct(event->idx); // = CPLUGIN_ID_014;
|
|
proto.usesMQTT = true;
|
|
proto.usesTemplate = true;
|
|
proto.usesAccount = true;
|
|
proto.usesPassword = true;
|
|
proto.usesExtCreds = true;
|
|
proto.defaultPort = 1883;
|
|
proto.usesID = false;
|
|
# if FEATURE_MQTT_TLS
|
|
proto.usesTLS = true;
|
|
# endif // if FEATURE_MQTT_TLS
|
|
# if FEATURE_STRING_VARIABLES
|
|
proto.allowSendDerived = true;
|
|
# endif // if FEATURE_STRING_VARIABLES
|
|
break;
|
|
}
|
|
|
|
case CPlugin::Function::CPLUGIN_GET_DEVICENAME:
|
|
{
|
|
string = F(CPLUGIN_NAME_014);
|
|
break;
|
|
}
|
|
|
|
case CPlugin::Function::CPLUGIN_INIT:
|
|
{
|
|
success = init_mqtt_delay_queue(event->ControllerIndex, CPlugin_014_pubname, CPlugin_014_mqtt_retainFlag);
|
|
break;
|
|
}
|
|
|
|
case CPlugin::Function::CPLUGIN_EXIT:
|
|
{
|
|
exit_mqtt_delay_queue();
|
|
break;
|
|
}
|
|
|
|
case CPlugin::Function::CPLUGIN_INTERVAL:
|
|
{
|
|
if (MQTTclient.connected()) {
|
|
errorCounter = 0;
|
|
|
|
pubname = F(CPLUGIN_014_BASE_TOPIC); // Scheme to form device messages
|
|
C014_replaceSysname(pubname);
|
|
|
|
# ifdef CPLUGIN_014_V3
|
|
|
|
// $stats/uptime Device → Controller Time elapsed in seconds since the boot of the device Yes Yes
|
|
CPlugin_014_sendMQTTdevice(pubname, event->TaskIndex, F("$stats/uptime"), toString(getUptimeMinutes() * 60, 0), errorCounter);
|
|
|
|
// $stats/signal Device → Controller Signal strength in % Yes No
|
|
float RssI = WiFi.RSSI();
|
|
RssI = isnan(RssI) ? -100.0f : RssI;
|
|
RssI = min(max(2 * (RssI + 100.0f), 0.0f), 100.0f);
|
|
|
|
CPlugin_014_sendMQTTdevice(pubname, event->TaskIndex, F("$stats/signal"), toString(RssI, 1), errorCounter);
|
|
# endif // ifdef CPLUGIN_014_V3
|
|
|
|
if (errorCounter > 0) {
|
|
// alert: this is the state the device is when connected to the MQTT broker, but something wrong is happening. E.g. a sensor is
|
|
// not providing data and needs human intervention. You have to send this message when something is wrong.
|
|
CPlugin_014_sendMQTTdevice(pubname, event->TaskIndex, F("$state"), F("alert"), errorCounter);
|
|
} else {
|
|
// ready: this is the state the device is in when it is connected to the MQTT broker, has sent all Homie messages and is ready to
|
|
// operate. You have to send this message after all other announcements message have been sent.
|
|
CPlugin_014_sendMQTTdevice(pubname, event->TaskIndex, F("$state"), F("ready"), errorCounter);
|
|
success = true;
|
|
}
|
|
|
|
# ifndef BUILD_NO_DEBUG
|
|
|
|
if (loglevelActiveFor(LOG_LEVEL_DEBUG)) {
|
|
addLog(LOG_LEVEL_DEBUG,
|
|
strformat(F("C014 : $stats information sent with %s errors! (%d messages)"),
|
|
errorCounter > 0 ? String(errorCounter).c_str() : "no",
|
|
msgCounter));
|
|
}
|
|
# endif // ifndef BUILD_NO_DEBUG
|
|
msgCounter = 0;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case CPlugin::Function::CPLUGIN_GOT_CONNECTED: //// call after connected to mqtt server to publich device autodicover features
|
|
{
|
|
statusLED(true);
|
|
|
|
// send autodiscover header
|
|
pubname = F(CPLUGIN_014_BASE_TOPIC); // Scheme to form device messages
|
|
C014_replaceSysname(pubname);
|
|
int deviceCount = 1; // minimum the SYSTEM device exists
|
|
int nodeCount = 1; // minimum the cmd node exists
|
|
errorCounter = 0;
|
|
|
|
if (lastBootCause != BOOT_CAUSE_DEEP_SLEEP) { // skip sending autodiscover data when returning from deep sleep
|
|
String nodename = F(CPLUGIN_014_BASE_VALUE); // Scheme to form node messages
|
|
C014_replaceSysname(nodename);
|
|
String nodesList; // build comma separated List for nodes
|
|
String valuesList; // build comma separated List for values
|
|
String deviceName; // current Device Name nr:name
|
|
String valueName; // current Value Name
|
|
String unitName; // estimate Units
|
|
|
|
// init: this is the state the device is in when it is connected to the MQTT broker, but has not yet sent all Homie messages and is
|
|
// not yet ready to operate. This is the first message that must that must be sent.
|
|
CPlugin_014_sendMQTTdevice(pubname, event->TaskIndex, F("$state"), F("init"), errorCounter);
|
|
|
|
// $homie Device → Controller Version of the Homie convention the device conforms to Yes Yes
|
|
CPlugin_014_sendMQTTdevice(pubname, event->TaskIndex, F("$homie"), F(CPLUGIN_014_HOMIE_VERSION), errorCounter);
|
|
|
|
// $name Device → Controller Friendly name of the device Yes Yes
|
|
CPlugin_014_sendMQTTdevice(pubname, event->TaskIndex, F("$name"), Settings.getName(), errorCounter);
|
|
|
|
// $localip Device → Controller IP of the device on the local network Yes Yes
|
|
# ifdef CPLUGIN_014_V3
|
|
CPlugin_014_sendMQTTdevice(pubname, event->TaskIndex, F("$localip"), formatIP(NetworkLocalIP()), errorCounter);
|
|
|
|
// $mac Device → Controller Mac address of the device network interface. The format MUST be of the type A1:B2:C3:D4:E5:F6 Yes Yes
|
|
CPlugin_014_sendMQTTdevice(pubname, event->TaskIndex, F("$mac"), ESPEasy::net::NetworkMacAddress(), errorCounter);
|
|
|
|
// $implementation Device → Controller An identifier for the Homie implementation (example esp8266) Yes Yes
|
|
CPlugin_014_sendMQTTdevice(pubname, event->TaskIndex, F("$implementation"),
|
|
# if defined(ESP8266)
|
|
F("ESP8266"),
|
|
# endif // if defined(ESP8266)
|
|
# if defined(ESP32)
|
|
F("ESP32"),
|
|
# endif // if defined(ESP32)
|
|
errorCounter);
|
|
|
|
// $fw/version Device → Controller Version of the firmware running on the device Yes Yes
|
|
CPlugin_014_sendMQTTdevice(pubname, event->TaskIndex, F("$fw/version"), toString(Settings.Build, 0),
|
|
errorCounter);
|
|
|
|
# if FEATURE_ESPEASY_P2P
|
|
|
|
// $fw/name Device → Controller Name of the firmware running on the device. Allowed characters are the same as the device ID Yes Yes
|
|
CPlugin_014_sendMQTTdevice(pubname, event->TaskIndex, F("$fw/name"), getNodeTypeDisplayString(NODE_TYPE_ID),
|
|
errorCounter);
|
|
# endif // if FEATURE_ESPEASY_P2P
|
|
|
|
// $stats/interval Device → Controller Interval in seconds at which the device refreshes its $stats/+: See next section for
|
|
// details about statistical attributes Yes Yes
|
|
CPlugin_014_sendMQTTdevice(pubname, event->TaskIndex, F("$stats/interval"), F(CPLUGIN_014_INTERVAL),
|
|
errorCounter);
|
|
# endif // ifdef CPLUGIN_014_V3
|
|
|
|
// always send the SYSTEM device with the cmd node
|
|
C014_addToList(nodesList, F(CPLUGIN_014_SYSTEM_DEVICE));
|
|
C014_addToList(valuesList, F(CPLUGIN_014_CMD_VALUE));
|
|
|
|
// $name Device → Controller Friendly name of the Node Yes Yes
|
|
CPlugin_014_sendMQTTnode(nodename,
|
|
F(CPLUGIN_014_SYSTEM_DEVICE),
|
|
F("$name"),
|
|
F(""),
|
|
F(CPLUGIN_014_SYSTEM_DEVICE),
|
|
errorCounter);
|
|
|
|
// $name Device → Controller Friendly name of the property. Any String Yes No ("")
|
|
CPlugin_014_sendMQTTnode(nodename,
|
|
F(CPLUGIN_014_SYSTEM_DEVICE),
|
|
F(CPLUGIN_014_CMD_VALUE),
|
|
F("/$name"),
|
|
F(CPLUGIN_014_CMD_VALUE_NAME),
|
|
errorCounter);
|
|
|
|
// $datatype The data type. See Payloads. Enum: [integer, float, boolean, string, enum, color]
|
|
CPlugin_014_sendMQTTnode(nodename,
|
|
F(CPLUGIN_014_SYSTEM_DEVICE),
|
|
F(CPLUGIN_014_CMD_VALUE),
|
|
F("/$datatype"),
|
|
F("string"),
|
|
errorCounter);
|
|
|
|
// $settable Device → Controller Specifies whether the property is settable (true) or readonly (false) true or false Yes No
|
|
// (false)
|
|
CPlugin_014_sendMQTTnode(nodename,
|
|
F(CPLUGIN_014_SYSTEM_DEVICE),
|
|
F(CPLUGIN_014_CMD_VALUE),
|
|
F("/$settable"),
|
|
F("true"),
|
|
errorCounter);
|
|
|
|
// enum all devices
|
|
|
|
// FIRST Standard GPIO tasks
|
|
int gpio = 0;
|
|
|
|
while (gpio <= MAX_GPIO) {
|
|
const PinBootState pinBootState = Settings.getPinBootState(gpio);
|
|
|
|
if (pinBootState != PinBootState::Default_state) { // anything but default
|
|
nodeCount++;
|
|
valueName = concat(F(CPLUGIN_014_GPIO_VALUE), gpio);
|
|
C014_addToList(valuesList, valueName);
|
|
|
|
// $name Device → Controller Friendly name of the property. Any String Yes No ("")
|
|
CPlugin_014_sendMQTTnode(nodename, F(CPLUGIN_014_SYSTEM_DEVICE), valueName, F("/$name"), valueName, errorCounter);
|
|
|
|
// $datatype The data type. See Payloads. Enum: [integer, float, boolean,string, enum, color]
|
|
CPlugin_014_sendMQTTnode(nodename, F(CPLUGIN_014_SYSTEM_DEVICE), valueName, F("/$datatype"), F("boolean"), errorCounter);
|
|
|
|
if (pinBootState != PinBootState::Input) { // defined as output
|
|
// $settable Device → Controller Specifies whether the property is settable (true) or readonly (false) true or
|
|
// false Yes No (false)
|
|
CPlugin_014_sendMQTTnode(nodename, F(CPLUGIN_014_SYSTEM_DEVICE), valueName, F("/$settable"), F("true"), errorCounter);
|
|
}
|
|
}
|
|
++gpio;
|
|
}
|
|
|
|
// $properties Device → Controller Properties the node exposes, with format id separated by a , if there are multiple nodes. Yes Yes
|
|
CPlugin_014_sendMQTTnode(nodename, F(CPLUGIN_014_SYSTEM_DEVICE), F("$properties"), EMPTY_STRING, valuesList, errorCounter);
|
|
valuesList = EMPTY_STRING;
|
|
deviceCount++;
|
|
|
|
// SECOND Plugins
|
|
for (taskIndex_t x = 0; x < TASKS_MAX; ++x) {
|
|
const pluginID_t pluginID = Settings.getPluginID_for_task(x);
|
|
|
|
if (validPluginID_fullcheck(pluginID)) {
|
|
LoadTaskSettings(x);
|
|
const deviceIndex_t DeviceIndex = getDeviceIndex_from_TaskIndex(x);
|
|
|
|
deviceName = getTaskDeviceName(x);
|
|
|
|
if (validDeviceIndex(DeviceIndex) && Settings.TaskDeviceEnabled[x]) { // Device is enabled so send information
|
|
// device enabled
|
|
valuesList = EMPTY_STRING;
|
|
|
|
const uint8_t valueCount = getValueCountForTask(x);
|
|
|
|
if (!Device[DeviceIndex].SendDataOption) { // check if device is not sending data = assume that it can receive.
|
|
constexpr pluginID_t HOMIE_RECEIVER_PLUGIN_ID(86);
|
|
|
|
if (pluginID == HOMIE_RECEIVER_PLUGIN_ID) {
|
|
for (uint8_t varNr = 0; varNr < valueCount; ++varNr) {
|
|
if (validPluginID_fullcheck(Settings.getPluginID_for_task(x))) {
|
|
if (ExtraTaskSettings.TaskDeviceValueNames[varNr][0] != 0) { // do not send if Value Name is empty!
|
|
C014_addToList(valuesList, ExtraTaskSettings.TaskDeviceValueNames[varNr]);
|
|
|
|
// $settable Device → Controller Specifies whether the property is settable (true) or readonly (false) true
|
|
// or false Yes No (false)
|
|
CPlugin_014_sendMQTTnode(nodename,
|
|
deviceName,
|
|
ExtraTaskSettings.TaskDeviceValueNames[varNr],
|
|
F("/$settable"),
|
|
F("true"),
|
|
errorCounter);
|
|
|
|
// $name Device → Controller Friendly name of the property. Any String Yes No ("")
|
|
valueName = F("Homie Receiver: ");
|
|
valueName += ExtraTaskSettings.TaskDeviceValueNames[varNr];
|
|
CPlugin_014_sendMQTTnode(nodename,
|
|
deviceName,
|
|
ExtraTaskSettings.TaskDeviceValueNames[varNr],
|
|
F("/$name"),
|
|
valueName,
|
|
errorCounter);
|
|
|
|
// $datatype The data type. See Payloads. Enum: [integer, float, boolean,string, enum, color]
|
|
unitName.clear();
|
|
|
|
switch (Settings.TaskDevicePluginConfig[x][varNr]) {
|
|
case 0:
|
|
valueName = F("integer");
|
|
|
|
if ((ExtraTaskSettings.TaskDevicePluginConfig[varNr] != 0) ||
|
|
(ExtraTaskSettings.TaskDevicePluginConfig[varNr + 5] != 0)) {
|
|
unitName = ExtraTaskSettings.TaskDevicePluginConfig[varNr];
|
|
unitName += ':';
|
|
unitName += ExtraTaskSettings.TaskDevicePluginConfig[varNr + valueCount];
|
|
}
|
|
break;
|
|
case 1:
|
|
valueName = F("float");
|
|
|
|
if ((ExtraTaskSettings.TaskDevicePluginConfig[varNr] != 0) ||
|
|
(ExtraTaskSettings.TaskDevicePluginConfig[varNr + 5] != 0)) {
|
|
unitName = strformat(F("%d:%d"),
|
|
ExtraTaskSettings.TaskDevicePluginConfig[varNr],
|
|
ExtraTaskSettings.TaskDevicePluginConfig[varNr + valueCount]);
|
|
}
|
|
break;
|
|
case 2: valueName = F("boolean"); break;
|
|
case 3: valueName = F("string"); break;
|
|
case 4:
|
|
valueName = F("enum");
|
|
unitName = ExtraTaskSettings.TaskDeviceFormula[varNr];
|
|
break;
|
|
case 5:
|
|
valueName = F("color");
|
|
unitName = F("rgb");
|
|
break;
|
|
case 6:
|
|
valueName = F("color");
|
|
unitName = F("hsv");
|
|
break;
|
|
}
|
|
CPlugin_014_sendMQTTnode(nodename,
|
|
deviceName,
|
|
ExtraTaskSettings.TaskDeviceValueNames[varNr],
|
|
F("/$datatype"),
|
|
valueName,
|
|
errorCounter);
|
|
|
|
if (!unitName.isEmpty()) {
|
|
CPlugin_014_sendMQTTnode(nodename,
|
|
deviceName,
|
|
ExtraTaskSettings.TaskDeviceValueNames[varNr],
|
|
F("/$format"),
|
|
unitName,
|
|
errorCounter);
|
|
}
|
|
nodeCount++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
// ignore cutom values for now! Assume all Values are standard float.
|
|
// String customValuesStr;
|
|
// customValues = PluginCall(PLUGIN_WEBFORM_SHOW_VALUES, &TempEvent, customValuesStr);
|
|
uint8_t customValues = false;
|
|
|
|
if (!customValues) { // standard Values
|
|
for (uint8_t varNr = 0; varNr < valueCount; ++varNr) {
|
|
const pluginID_t pluginID = Settings.getPluginID_for_task(x);
|
|
|
|
if (validPluginID_fullcheck(pluginID)) {
|
|
if (ExtraTaskSettings.TaskDeviceValueNames[varNr][0] != 0) { // do not send if Value Name is empty!
|
|
C014_addToList(valuesList, ExtraTaskSettings.TaskDeviceValueNames[varNr]);
|
|
|
|
// $name Device → Controller Friendly name of the property. Any String Yes No ("")
|
|
CPlugin_014_sendMQTTnode(nodename,
|
|
deviceName,
|
|
ExtraTaskSettings.TaskDeviceValueNames[varNr],
|
|
F("/$name"),
|
|
ExtraTaskSettings.TaskDeviceValueNames[varNr],
|
|
errorCounter);
|
|
|
|
// $datatype The data type. See Payloads. Enum: [integer, float, boolean,string, enum, color]
|
|
CPlugin_014_sendMQTTnode(nodename,
|
|
deviceName,
|
|
ExtraTaskSettings.TaskDeviceValueNames[varNr],
|
|
F("/$datatype"),
|
|
F("float"),
|
|
errorCounter);
|
|
|
|
constexpr pluginID_t DUMMY_PLUGIN_ID(33);
|
|
|
|
if (pluginID == DUMMY_PLUGIN_ID) { // Dummy Device can send AND receive Data
|
|
CPlugin_014_sendMQTTnode(nodename,
|
|
deviceName,
|
|
ExtraTaskSettings.TaskDeviceValueNames[varNr],
|
|
F("/$settable"),
|
|
F("true"),
|
|
errorCounter);
|
|
}
|
|
|
|
nodeCount++;
|
|
|
|
# if FEATURE_TASKVALUE_UNIT_OF_MEASURE
|
|
|
|
// Use configured Unit of Measure setting per Value (using Homeassistant supported 100+ values)
|
|
unitName = toUnitOfMeasureName(ExtraTaskSettings.getTaskVarUnitOfMeasure(varNr));
|
|
|
|
if (!unitName.isEmpty()) // found a unit match
|
|
{
|
|
// $unit Device → Controller A string containing the unit of this property. You
|
|
// are not limited to the recommended values, although they are the only well known ones
|
|
// that will have to be recognized by any Homie consumer.
|
|
CPlugin_014_sendMQTTnode(nodename,
|
|
deviceName,
|
|
ExtraTaskSettings.TaskDeviceValueNames[varNr],
|
|
F("/$unit"),
|
|
unitName,
|
|
errorCounter);
|
|
}
|
|
unitName.clear();
|
|
# endif // if FEATURE_TASKVALUE_UNIT_OF_MEASURE
|
|
}
|
|
}
|
|
} // end loop throug regular values
|
|
|
|
// Discovery for Derived values, if sending is enabled
|
|
# if FEATURE_STRING_VARIABLES
|
|
|
|
if (Settings.SendDerivedTaskValues(x, event->ControllerIndex)) {
|
|
String taskNameLC(deviceName);
|
|
taskNameLC.toLowerCase();
|
|
String postfix;
|
|
const String search = getDerivedValueSearchAndPostfix(deviceName, postfix);
|
|
|
|
auto it = customStringVar.begin();
|
|
uint8_t varNr = VARS_PER_TASK;
|
|
|
|
while (it != customStringVar.end()) {
|
|
if (it->first.startsWith(search) && it->first.endsWith(postfix)) {
|
|
String valueName = it->first.substring(search.length(), it->first.indexOf('-'));
|
|
String uom;
|
|
String vType;
|
|
const String vname2 = getDerivedValueNameUomAndVType(deviceName, valueName, uom, vType);
|
|
|
|
if (!vname2.isEmpty()) {
|
|
valueName = vname2;
|
|
}
|
|
C014_addToList(valuesList, valueName);
|
|
|
|
// $name Device → Controller Friendly name of the property. Any String Yes No ("")
|
|
CPlugin_014_sendMQTTnode(nodename,
|
|
deviceName,
|
|
valueName,
|
|
F("/$name"),
|
|
valueName,
|
|
errorCounter);
|
|
|
|
// $datatype The data type. See Payloads. Enum: [integer, float, boolean,string, enum, color]
|
|
CPlugin_014_sendMQTTnode(nodename,
|
|
deviceName,
|
|
valueName,
|
|
F("/$datatype"),
|
|
F("float"),
|
|
errorCounter);
|
|
|
|
if (!uom.isEmpty()) {
|
|
CPlugin_014_sendMQTTnode(nodename,
|
|
deviceName,
|
|
valueName,
|
|
F("/$unit"),
|
|
uom,
|
|
errorCounter);
|
|
}
|
|
|
|
++varNr;
|
|
}
|
|
else if (it->first.substring(0, search.length()).compareTo(search) > 0) {
|
|
break;
|
|
}
|
|
++it;
|
|
}
|
|
}
|
|
# endif // if FEATURE_STRING_VARIABLES
|
|
} else { // Device has custom Values
|
|
# ifndef BUILD_NO_DEBUG
|
|
|
|
if (loglevelActiveFor(LOG_LEVEL_DEBUG)) {
|
|
addLog(LOG_LEVEL_DEBUG, strformat(F("C014 : Device has custom values: %s not implemented!"),
|
|
getPluginNameFromDeviceIndex(getDeviceIndex_from_TaskIndex(x)).c_str()));
|
|
}
|
|
# endif // ifndef BUILD_NO_DEBUG
|
|
}
|
|
}
|
|
|
|
if (!valuesList.isEmpty()) {
|
|
// only add device to list if it has nodes!
|
|
// $name Device → Controller Friendly name of the Node Yes Yes
|
|
CPlugin_014_sendMQTTnode(nodename,
|
|
deviceName,
|
|
F("$name"),
|
|
F(""),
|
|
getTaskDeviceName(x),
|
|
errorCounter);
|
|
|
|
// $type Device → Controller Type of the node Yes Yes
|
|
CPlugin_014_sendMQTTnode(nodename,
|
|
deviceName,
|
|
F("$type"),
|
|
F(""),
|
|
getPluginNameFromDeviceIndex(DeviceIndex),
|
|
errorCounter);
|
|
|
|
// add device to device list
|
|
C014_addToList(nodesList, deviceName);
|
|
deviceCount++;
|
|
|
|
// $properties Device → Controller Properties the node exposes, with format id separated by a , if there are multiple
|
|
// nodes. Yes Yes
|
|
CPlugin_014_sendMQTTnode(nodename,
|
|
deviceName,
|
|
F("$properties"),
|
|
F(""),
|
|
valuesList,
|
|
errorCounter);
|
|
valuesList = EMPTY_STRING;
|
|
}
|
|
} else { // device not enabeled
|
|
# ifndef BUILD_NO_DEBUG
|
|
|
|
if (loglevelActiveFor(LOG_LEVEL_DEBUG)) {
|
|
addLog(LOG_LEVEL_DEBUG, strformat(F("C014 : Device Disabled: %s not propagated!"),
|
|
getPluginNameFromDeviceIndex(getDeviceIndex_from_TaskIndex(x)).c_str()));
|
|
}
|
|
# endif // ifndef BUILD_NO_DEBUG
|
|
}
|
|
} // device configured
|
|
} // loop through devices
|
|
|
|
// and finally ...
|
|
// $nodes Device → Controller Nodes the device exposes, with format id separated by a , if there are multiple nodes. To
|
|
// make a node an array, append [] to the ID. Yes Yes
|
|
CPlugin_014_sendMQTTdevice(pubname, event->TaskIndex, F("$nodes"), nodesList, errorCounter);
|
|
}
|
|
|
|
if (errorCounter > 0) {
|
|
// alert: this is the state the device is when connected to the MQTT broker, but something wrong is happening. E.g. a sensor is not
|
|
// providing data and needs human intervention. You have to send this message when something is wrong.
|
|
CPlugin_014_sendMQTTdevice(pubname, event->TaskIndex, F("$state"), F("alert"), errorCounter);
|
|
} else {
|
|
// ready: this is the state the device is in when it is connected to the MQTT broker, has sent all Homie messages and is ready to
|
|
// operate. You have to send this message after all other announcements message have been sent.
|
|
CPlugin_014_sendMQTTdevice(pubname, event->TaskIndex, F("$state"), F("ready"), errorCounter);
|
|
success = true;
|
|
}
|
|
#ifndef BUILD_NO_DEBUG
|
|
if (loglevelActiveFor(LOG_LEVEL_INFO)) {
|
|
addLog(LOG_LEVEL_INFO,
|
|
strformat(F("C014 : autodiscover information of %d Devices and %d Nodes sent with %s errors! (%d messages)"),
|
|
deviceCount,
|
|
nodeCount,
|
|
errorCounter > 0 ? String(errorCounter).c_str() : "no",
|
|
msgCounter)
|
|
);
|
|
}
|
|
#endif
|
|
msgCounter = 0;
|
|
errorCounter = 0;
|
|
break;
|
|
}
|
|
|
|
case CPlugin::Function::CPLUGIN_PROTOCOL_TEMPLATE:
|
|
{
|
|
event->String1 = F(CPLUGIN_014_SUBSCRIBE);
|
|
event->String2 = F(CPLUGIN_014_PUBLISH);
|
|
break;
|
|
}
|
|
|
|
case CPlugin::Function::CPLUGIN_GOT_INVALID:
|
|
{
|
|
pubname = F(CPLUGIN_014_BASE_TOPIC); // Scheme to form device messages
|
|
C014_replaceSysname(pubname);
|
|
|
|
// disconnected: this is the state the device is in when it is cleanly disconnected from the MQTT broker. You must send this message
|
|
// before cleanly disconnecting
|
|
success = CPlugin_014_sendMQTTdevice(pubname, event->TaskIndex, F("$state"), F("disconnected"), errorCounter);
|
|
|
|
if (loglevelActiveFor(LOG_LEVEL_INFO)) {
|
|
String log = strformat(F("C014 : Device: %s got invalid (disconnect%s"),
|
|
Settings.getHostname().c_str(), String(success ? F("ed).") : F(") failed!")).c_str());
|
|
addLogMove(LOG_LEVEL_INFO, log);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case CPlugin::Function::CPLUGIN_FLUSH:
|
|
{
|
|
pubname = F(CPLUGIN_014_BASE_TOPIC); // Scheme to form device messages
|
|
C014_replaceSysname(pubname);
|
|
|
|
// sleeping: this is the state the device is in when the device is sleeping. You have to send this message before sleeping.
|
|
success = CPlugin_014_sendMQTTdevice(pubname, event->TaskIndex, F("$state"), F("sleeping"), errorCounter);
|
|
break;
|
|
}
|
|
|
|
case CPlugin::Function::CPLUGIN_PROTOCOL_RECV:
|
|
{
|
|
const controllerIndex_t ControllerID = findFirstEnabledControllerWithId(CPLUGIN_ID_014);
|
|
bool validTopic = false;
|
|
bool cmdExecuted = false;
|
|
|
|
if (!validControllerIndex(ControllerID)) {
|
|
// Controller is not enabled.
|
|
break;
|
|
} else {
|
|
String cmd;
|
|
int lastindex = event->String1.lastIndexOf('/');
|
|
errorCounter = 0;
|
|
|
|
if (equals(event->String1.substring(lastindex + 1), F("set"))) {
|
|
pubname = event->String1.substring(0, lastindex);
|
|
lastindex = pubname.lastIndexOf('/');
|
|
String nodeName = pubname.substring(0, lastindex);
|
|
const String valueName = pubname.substring(lastindex + 1);
|
|
lastindex = nodeName.lastIndexOf('/');
|
|
nodeName = nodeName.substring(lastindex + 1);
|
|
|
|
String log;
|
|
|
|
if (loglevelActiveFor(LOG_LEVEL_INFO)) {
|
|
log = strformat(F("C014 : MQTT received: /set: N: %s V: %s"), nodeName.c_str(), valueName.c_str());
|
|
}
|
|
|
|
if (equals(nodeName, F(CPLUGIN_014_SYSTEM_DEVICE))) { // msg to a system device
|
|
if (valueName.startsWith(F(CPLUGIN_014_GPIO_VALUE))) { // msg to to set gpio values
|
|
constexpr size_t gpio_value_tag_length = CPLUGIN_014_GPIO_VALUE_LEN; // now uses fixed length or constexpr
|
|
|
|
// get the GPIO
|
|
// Homie spec says state should be 'true' or 'false'...
|
|
cmd = strformat(F("GPIO,%d,%c"),
|
|
valueName.substring(gpio_value_tag_length).toInt(),
|
|
(equals(event->String2, F("true")) || equals(event->String2, '1')) ? '1' : '0');
|
|
validTopic = true;
|
|
} else if (equals(valueName, F(CPLUGIN_014_CMD_VALUE))) { // msg to send a command
|
|
cmd = event->String2;
|
|
validTopic = true;
|
|
} else {
|
|
cmd = strformat(F("SYSTEM/%s unknown!"), valueName.c_str());
|
|
}
|
|
} else { // msg to a receiving plugin
|
|
// Only handle /set case that supports P033 Dummy device and P086 Homie receiver
|
|
cmdExecuted = MQTT_handle_topic_commands(event, false, true);
|
|
validTopic = true; // Avoid error messages
|
|
}
|
|
|
|
if (validTopic) {
|
|
if (loglevelActiveFor(LOG_LEVEL_INFO)) {
|
|
log += strformat(F(" cmd: %s OK"), cmd.c_str());
|
|
addLog(LOG_LEVEL_INFO, log);
|
|
}
|
|
} else if (loglevelActiveFor(LOG_LEVEL_INFO)) {
|
|
log += F(" INVALID MSG");
|
|
addLog(LOG_LEVEL_INFO, log);
|
|
}
|
|
}
|
|
|
|
if (validTopic && !cmdExecuted) { // Don't execute twice
|
|
MQTT_execute_command(cmd, true);
|
|
}
|
|
}
|
|
success = validTopic;
|
|
break;
|
|
}
|
|
|
|
case CPlugin::Function::CPLUGIN_PROTOCOL_SEND:
|
|
{
|
|
if (MQTT_queueFull(event->ControllerIndex)) {
|
|
break;
|
|
}
|
|
|
|
const bool taskRetained = Settings.SendRetainedTaskValues(event->TaskIndex, event->ControllerIndex);
|
|
|
|
success = MQTT_protocol_send(event, CPlugin_014_pubname, CPlugin_014_mqtt_retainFlag || taskRetained);
|
|
|
|
break;
|
|
}
|
|
|
|
case CPlugin::Function::CPLUGIN_ACKNOWLEDGE:
|
|
{
|
|
/* if (loglevelActiveFor(LOG_LEVEL_DEBUG)) {
|
|
String log = F("CPLUGIN_ACKNOWLEDGE: ");
|
|
log += string;
|
|
log += F(" / ");
|
|
log += getTaskDeviceName(event->TaskIndex);
|
|
log += F(" / ");
|
|
log += ExtraTaskSettings.TaskDeviceValueNames[event->Par2-1];
|
|
log += F(" sensorType:");
|
|
log += event->sensorType;
|
|
log += F(" Source:");
|
|
log += event->Source;
|
|
log += F(" idx:");
|
|
log += event->idx;
|
|
log += F(" S1:");
|
|
log += event->String1;
|
|
log += F(" S2:");
|
|
log += event->String2;
|
|
log += F(" S3:");
|
|
log += event->String3;
|
|
log += F(" S4:");
|
|
log += event->String4;
|
|
log += F(" S5:");
|
|
log += event->String5;
|
|
log += F(" P1:");
|
|
log += event->Par1;
|
|
log += F(" P2:");
|
|
log += event->Par2;
|
|
log += F(" P3:");
|
|
log += event->Par3;
|
|
log += F(" P4:");
|
|
log += event->Par4;
|
|
log += F(" P5:");
|
|
log += event->Par5;
|
|
addLog(LOG_LEVEL_DEBUG, log);
|
|
} */
|
|
|
|
if (!string.isEmpty()) {
|
|
const String commandName = parseString(string, 1); // could not find a way to get the command out of the event structure.
|
|
|
|
if (equals(commandName, F(CPLUGIN_014_GPIO_COMMAND))) // !ToDo : As gpio is like any other plugin commands should be integrated
|
|
// below!
|
|
{
|
|
const int port = event->Par1; // parseString(string, 2).toInt();
|
|
const int valueInt = event->Par2; // parseString(string, 3).toInt();
|
|
const String valueBool = boolToString(valueInt == 1);
|
|
|
|
String topic = F(CPLUGIN_014_PUBLISH); // ControllerSettings.Publish not used because it can be modified by the user!
|
|
C014_replaceSysname(topic);
|
|
topic.replace(F("%tskname%"), F(CPLUGIN_014_SYSTEM_DEVICE));
|
|
topic.replace(F("%valname%"), concat(F(CPLUGIN_014_GPIO_VALUE), port));
|
|
|
|
success = MQTTpublish(CPLUGIN_ID_014, INVALID_TASK_INDEX, topic.c_str(), valueBool.c_str(), false);
|
|
|
|
String log = strformat(F("C014 : Acknowledged GPIO%d value:%s (%d)"),
|
|
port, valueBool.c_str(), valueInt);
|
|
|
|
if (loglevelActiveFor(LOG_LEVEL_INFO) && success) {
|
|
log += F(" success!");
|
|
addLogMove(LOG_LEVEL_INFO, log);
|
|
}
|
|
|
|
if (loglevelActiveFor(LOG_LEVEL_ERROR) && !success) {
|
|
log += F(" ERROR!");
|
|
addLogMove(LOG_LEVEL_ERROR, log);
|
|
}
|
|
} else { // not gpio
|
|
const taskVarIndex_t taskVarIndex = event->Par2 - 1;
|
|
|
|
if (validTaskVarIndex(taskVarIndex)) {
|
|
userVarIndex_t userVarIndex = event->BaseVarIndex + taskVarIndex;
|
|
String topic = F(CPLUGIN_014_PUBLISH);
|
|
C014_replaceSysname(topic);
|
|
const int deviceIndex = event->Par1; // parseString(string, 2).toInt();
|
|
LoadTaskSettings(deviceIndex - 1);
|
|
const String deviceName = getTaskDeviceName(event->TaskIndex);
|
|
topic.replace(F("%tskname%"), deviceName);
|
|
const String valueName = ExtraTaskSettings.TaskDeviceValueNames[taskVarIndex]; // parseString(string, 3).toInt()-1];
|
|
topic.replace(F("%valname%"), valueName);
|
|
String valueStr;
|
|
int valueInt = 0;
|
|
|
|
if (equals(commandName, F(CPLUGIN_014_TASKVALUESET_COMMAND))) { // removed dummyvalueset command some time ago...
|
|
valueStr = formatUserVarNoCheck(event, taskVarIndex); // parseString(string, 4);
|
|
success = MQTTpublish(CPLUGIN_ID_014, INVALID_TASK_INDEX, topic.c_str(), valueStr.c_str(), false);
|
|
|
|
String log = strformat(F("C014 : Acknowledged: %s var: %s topic: %s value: %s"),
|
|
deviceName.c_str(), valueName.c_str(), topic.c_str(), valueStr.c_str());
|
|
|
|
if (loglevelActiveFor(LOG_LEVEL_INFO) && success) {
|
|
log += F(" success!");
|
|
addLogMove(LOG_LEVEL_INFO, log);
|
|
}
|
|
|
|
if (loglevelActiveFor(LOG_LEVEL_ERROR) && !success) {
|
|
log += F(" ERROR!");
|
|
addLogMove(LOG_LEVEL_ERROR, log);
|
|
}
|
|
} else if (equals(commandName, F(CPLUGIN_014_HOMIEVALUESET_COMMAND))) { // acknowledges value form P086 Homie Receiver
|
|
switch (Settings.TaskDevicePluginConfig[deviceIndex - 1][taskVarIndex]) {
|
|
case 0: // PLUGIN_085_VALUE_INTEGER
|
|
valueInt = static_cast<int>(UserVar[userVarIndex]);
|
|
valueStr = toString(UserVar[userVarIndex], 0);
|
|
break;
|
|
case 1: // PLUGIN_085_VALUE_FLOAT
|
|
valueStr = formatUserVarNoCheck(event, taskVarIndex);
|
|
break;
|
|
case 2: // PLUGIN_085_VALUE_BOOLEAN
|
|
|
|
valueStr = boolToString(UserVar[userVarIndex] == 1);
|
|
break;
|
|
case 3: // PLUGIN_085_VALUE_STRING
|
|
// valueStr = ExtraTaskSettings.TaskDeviceFormula[taskVarIndex];
|
|
valueStr = parseStringToEndKeepCase(string, 4);
|
|
break;
|
|
case 4: // PLUGIN_085_VALUE_ENUM
|
|
valueInt = static_cast<int>(UserVar[userVarIndex]);
|
|
valueStr = parseStringKeepCase(ExtraTaskSettings.TaskDeviceFormula[taskVarIndex], valueInt);
|
|
break;
|
|
case 5: // PLUGIN_085_VALUE_RGB
|
|
// valueStr = ExtraTaskSettings.TaskDeviceFormula[taskVarIndex];
|
|
valueStr = parseStringToEnd(string, 4);
|
|
break;
|
|
case 6: // PLUGIN_085_VALUE_HSV
|
|
// valueStr = ExtraTaskSettings.TaskDeviceFormula[taskVarIndex];
|
|
valueStr = parseStringToEnd(string, 4);
|
|
break;
|
|
}
|
|
success = MQTTpublish(CPLUGIN_ID_014, INVALID_TASK_INDEX, topic.c_str(), valueStr.c_str(), false);
|
|
|
|
String log = concat(F("C014 : homie acknowledge: "), deviceName);
|
|
|
|
if (loglevelActiveFor(LOG_LEVEL_INFO) && success) {
|
|
log += strformat(F(" taskIndex:%d valueNr:%d valueName:%s valueType:%d topic:%s valueInt:%d valueStr:%s success!"),
|
|
deviceIndex,
|
|
event->Par2,
|
|
valueName.c_str(),
|
|
Settings.TaskDevicePluginConfig[deviceIndex - 1][taskVarIndex],
|
|
topic.c_str(),
|
|
valueInt,
|
|
valueStr.c_str());
|
|
addLogMove(LOG_LEVEL_INFO, log);
|
|
}
|
|
|
|
if (loglevelActiveFor(LOG_LEVEL_ERROR) && !success) {
|
|
log += strformat(F(" var: %s topic: %s value: %s ERROR!"), // was: failed!
|
|
valueName.c_str(), topic.c_str(), valueStr.c_str());
|
|
addLogMove(LOG_LEVEL_ERROR, log);
|
|
}
|
|
} else { // Acknowledge not implemented yet
|
|
/* if (loglevelActiveFor(LOG_LEVEL_INFO)) {
|
|
String log = F("C014 : Plugin acknowledged: ");
|
|
log+=function;
|
|
log+=F(" / ");
|
|
log+=commandName;
|
|
log+=F(" cmd: ");
|
|
log+=string;
|
|
log+=F(" not implemented!");
|
|
addLog(LOG_LEVEL_ERROR, log);
|
|
} */
|
|
success = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
# if FEATURE_MQTT_CONNECT_BACKGROUND
|
|
case CPlugin::Function::CPLUGIN_TEN_PER_SECOND:
|
|
{
|
|
MQTTConnectInBackground(CONTROLLER_MAX, true); // Report state
|
|
break;
|
|
}
|
|
# endif // if FEATURE_MQTT_CONNECT_BACKGROUND
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
#endif // ifdef USES_C014
|