Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[MQTT] Controller improvements #4770

Open
wants to merge 46 commits into
base: mega
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
7f4fa7b
[MQTT] Controller improvements
tonhuisman Aug 18, 2023
7fa8319
Merge branch 'mega' of https://github.com/letscontrolit/ESPEasy into …
tonhuisman Aug 26, 2023
70a99eb
Merge branch 'mega' of https://github.com/letscontrolit/ESPEasy into …
tonhuisman Aug 31, 2023
1f2b98f
Merge branch 'mega' into feature/MQTT-controller-cmd-improvements
tonhuisman Sep 6, 2023
7528480
Merge branch 'feature/MQTT-controller-cmd-improvements' of https://gi…
tonhuisman Sep 6, 2023
8765c20
Merge branch 'mega' into feature/MQTT-controller-cmd-improvements
tonhuisman Sep 9, 2023
b5161be
Merge branch 'feature/MQTT-controller-cmd-improvements' of https://gi…
tonhuisman Sep 10, 2023
1839456
[P086] Format source using uncrustify
tonhuisman Sep 10, 2023
df0fb7a
[MQTT] Add support for P086 events to all MQTT Controllers
tonhuisman Sep 16, 2023
95236d3
[MQTT] Only handle plugin if plugin included
tonhuisman Sep 16, 2023
8a9dfa6
Merge branch 'mega' into feature/MQTT-controller-cmd-improvements
tonhuisman Sep 19, 2023
5a377a6
Merge branch 'mega' of https://github.com/letscontrolit/ESPEasy into …
tonhuisman Sep 29, 2023
c46a8f4
Merge branch 'mega' into feature/MQTT-controller-cmd-improvements
tonhuisman Oct 4, 2023
05d99fa
Merge branch 'mega' of https://github.com/letscontrolit/ESPEasy into …
tonhuisman Oct 11, 2023
7b85651
Merge branch 'mega' of https://github.com/letscontrolit/ESPEasy into …
tonhuisman Oct 30, 2023
92a50d1
[C014] Use getHostname() for %sysname%, some code optimization
tonhuisman Oct 30, 2023
2de6997
Merge branch 'mega' of https://github.com/letscontrolit/ESPEasy into …
tonhuisman Feb 16, 2024
16c31cf
Merge branch 'mega' into feature/MQTT-controller-cmd-improvements
tonhuisman Mar 2, 2024
4dea3a1
[C014] Process %sysname% via parseSystemVariables(), logging improvem…
tonhuisman Mar 2, 2024
175ee1e
Merge branch 'mega' of https://github.com/letscontrolit/ESPEasy into …
tonhuisman Mar 24, 2024
8991cf1
Merge branch 'mega' into feature/MQTT-controller-cmd-improvements
TD-er Mar 30, 2024
d950e53
Merge branch 'feature/MQTT-controller-cmd-improvements' of https://gi…
tonhuisman Mar 30, 2024
4dea9b9
Merge branch 'mega' into feature/MQTT-controller-cmd-improvements
TD-er May 1, 2024
dc07fd9
Merge branch 'mega' into feature/MQTT-controller-cmd-improvements
tonhuisman May 8, 2024
d4c6828
Merge branch 'mega' into feature/MQTT-controller-cmd-improvements
tonhuisman May 24, 2024
05c2de5
Merge branch 'feature/MQTT-controller-cmd-improvements' of https://gi…
tonhuisman May 24, 2024
d944777
[Controllers] Minor improvements and corrections
tonhuisman May 24, 2024
4bb1d38
[C004] Some string optimization
tonhuisman May 29, 2024
4886924
[C004] Add documentation and examples
tonhuisman May 29, 2024
8dd13be
Merge branch 'mega' of https://github.com/letscontrolit/ESPEasy into …
tonhuisman May 29, 2024
ec36a96
Merge branch 'mega' of https://github.com/letscontrolit/ESPEasy into …
tonhuisman May 31, 2024
e3fac16
Merge branch 'mega' of https://github.com/letscontrolit/ESPEasy into …
tonhuisman Jun 22, 2024
671916e
[C007] Emoncms: add configurable url (Controller Publish)
tonhuisman Jun 22, 2024
307bea9
Merge branch 'mega' of https://github.com/letscontrolit/ESPEasy into …
tonhuisman Jul 5, 2024
60af4a6
[Coo4] Fix merge conflict issue
tonhuisman Jul 5, 2024
ecba48b
Merge branch 'mega' into feature/MQTT-controller-cmd-improvements
tonhuisman Jul 28, 2024
d57db07
Merge branch 'mega' of https://github.com/letscontrolit/ESPEasy into …
tonhuisman Sep 25, 2024
bc7d41d
Merge branch 'mega' of https://github.com/letscontrolit/ESPEasy into …
tonhuisman Sep 27, 2024
e6b2df3
Merge branch 'mega' into feature/MQTT-controller-cmd-improvements
TD-er Oct 2, 2024
7047020
Merge branch 'mega' into feature/MQTT-controller-cmd-improvements
TD-er Oct 3, 2024
1050b7f
Merge branch 'mega' into feature/MQTT-controller-cmd-improvements
TD-er Oct 7, 2024
85c1d62
Merge branch 'mega' into feature/MQTT-controller-cmd-improvements
tonhuisman Nov 2, 2024
c1013dd
Merge branch 'mega' of https://github.com/letscontrolit/ESPEasy into …
tonhuisman Nov 10, 2024
3a8e46d
Merge branch 'mega' of https://github.com/letscontrolit/ESPEasy into …
tonhuisman Nov 13, 2024
8ff6c04
Merge branch 'mega' of https://github.com/letscontrolit/ESPEasy into …
tonhuisman Nov 13, 2024
c6bc9f9
Merge mega and fix merge conflict
tonhuisman Dec 1, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 12 additions & 18 deletions src/_C002.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -101,15 +101,15 @@ bool CPlugin_002(CPlugin::Function function, struct EventStruct *event, String&
switch (Settings.getPluginID_for_task(x).value) {
case 1: // temp solution, if input switch, update state
{
action = F("inputSwitchState,");
action = F("gpio,"); // FIXME tonhuisman: Was: InputSwitchState
action += x;
action += ',';
action += nvalue;
break;
}
case 29: // temp solution, if plugin 029, set gpio
{
int baseVar = x * VARS_PER_TASK;
const int baseVar = x * VARS_PER_TASK;

if (switchtype.equalsIgnoreCase(F("dimmer")))
{
Expand All @@ -134,8 +134,7 @@ bool CPlugin_002(CPlugin::Function function, struct EventStruct *event, String&
}

if (checkValidPortRange(PLUGIN_GPIO, Settings.TaskDevicePin1[x])) {
action = F("pwm,");
action += Settings.TaskDevicePin1[x];
action = concat(F("pwm,"), Settings.TaskDevicePin1[x]);
action += ',';
action += pwmValue;
}
Expand All @@ -144,30 +143,25 @@ bool CPlugin_002(CPlugin::Function function, struct EventStruct *event, String&
UserVar[baseVar] = nvalue;

if (checkValidPortRange(PLUGIN_GPIO, Settings.TaskDevicePin1[x])) {
action = F("gpio,");
action += Settings.TaskDevicePin1[x];
action = concat(F("gpio,"), Settings.TaskDevicePin1[x]);
action += ',';
action += static_cast<int>(nvalue);
}
}
break;
}
# if defined(USES_P088) // || defined(USES_P115)
case 88: // Send heatpump IR (P088) if IDX matches
// case 115: // Send heatpump IR (P115) if IDX matches
# if defined(USES_P088)
case 88: // Send heatpump IR (P088) if IDX matches
{
action = F("heatpumpir,");
action += svalue1; // svalue1 is like 'gree,1,1,0,22,0,0'
action = concat(F("heatpumpir,"), svalue1); // svalue1 is like 'gree,1,1,0,22,0,0'
break;
}
# endif // USES_P088 || USES_P115
# endif // if defined(USES_P088)
default:
break;
}

const bool validCommand = action.length() > 0;

if (validCommand) {
if (action.length() != 0) {
mustSendEvent = true;

// Try plugin and internal
Expand Down Expand Up @@ -198,19 +192,19 @@ bool CPlugin_002(CPlugin::Function function, struct EventStruct *event, String&
if (event->idx != 0)
{
String json = serializeDomoticzJson(event);
# ifndef BUILD_NO_DEBUG
# ifndef BUILD_NO_DEBUG

if (loglevelActiveFor(LOG_LEVEL_DEBUG)) {
addLogMove(LOG_LEVEL_DEBUG, concat(F("MQTT : "), json));
}
# endif // ifndef BUILD_NO_DEBUG
# endif // ifndef BUILD_NO_DEBUG

String pubname = CPlugin_002_pubname;
parseControllerVariables(pubname, event, false);

// Publish using move operator, thus pubname and json are empty after this call
success = MQTTpublish(event->ControllerIndex, event->TaskIndex, std::move(pubname), std::move(json), CPlugin_002_mqtt_retainFlag);
} // if ixd !=0
} // if idx !=0
else
{
addLog(LOG_LEVEL_ERROR, F("MQTT : IDX cannot be zero!"));
Expand Down
248 changes: 83 additions & 165 deletions src/_C005.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

# include "src/Commands/InternalCommands.h"
# include "src/Globals/EventQueue.h"
# include "src/Helpers/_CPlugin_Helper_mqtt.h"
# include "src/Helpers/PeriodicalActions.h"
# include "src/Helpers/StringParser.h"
# include "_Plugin_Helper.h"
Expand All @@ -11,6 +12,15 @@
// ################### Controller Plugin 005: Home Assistant (openHAB) MQTT ##############################
// #######################################################################################################

/** Changelog:
* 2023-08-18 tonhuisman: Clean up source for pull request
* 2023-03-15 tonhuisman: Add processing of topic endpoint /set to issue a TaskValueSet,taskname,taskvalue,payload command for
* topic %sysname%/#/taskname/valuename/set
* Move /cmd and /set handling to helper file for generic MQTT use
* Reformatted source using Uncrustify
* 2023-03 Changelog started
*/

# define CPLUGIN_005
# define CPLUGIN_ID_005 5
# define CPLUGIN_NAME_005 "Home Assistant (openHAB) MQTT"
Expand Down Expand Up @@ -80,49 +90,10 @@ bool CPlugin_005(CPlugin::Function function, struct EventStruct *event, String&
break;
}

// String pubname = CPlugin_005_pubname;

String pubname = CPlugin_005_pubname;
bool mqtt_retainFlag = CPlugin_005_mqtt_retainFlag;

parseControllerVariables(pubname, event, false);
success = MQTT_protocol_send(event, CPlugin_005_pubname, CPlugin_005_mqtt_retainFlag);

uint8_t valueCount = getValueCountForTask(event->TaskIndex);

for (uint8_t x = 0; x < valueCount; x++)
{
// MFD: skip publishing for values with empty labels (removes unnecessary publishing of unwanted values)
if (getTaskValueName(event->TaskIndex, x).isEmpty()) {
continue; // we skip values with empty labels
}
String tmppubname = pubname;
parseSingleControllerVariable(tmppubname, event, x, false);
String value;
if (event->sensorType == Sensor_VType::SENSOR_TYPE_STRING) {
value = event->String2.substring(0, 20); // For the log
} else {
value = formatUserVarNoCheck(event, x);
}
# ifndef BUILD_NO_DEBUG

if (loglevelActiveFor(LOG_LEVEL_DEBUG)) {
String log = F("MQTT : ");
log += tmppubname;
log += ' ';
log += value;
addLogMove(LOG_LEVEL_DEBUG, log);
}
# endif // ifndef BUILD_NO_DEBUG

// Small optimization so we don't try to copy potentially large strings
if (event->sensorType == Sensor_VType::SENSOR_TYPE_STRING) {
if (MQTTpublish(event->ControllerIndex, event->TaskIndex, tmppubname.c_str(), event->String2.c_str(), mqtt_retainFlag))
success = true;
} else {
// Publish using move operator, thus tmppubname and value are empty after this call
if (MQTTpublish(event->ControllerIndex, event->TaskIndex, std::move(tmppubname), std::move(value), mqtt_retainFlag))
success = true;
}
}
break;
}

Expand All @@ -145,140 +116,87 @@ bool C005_parse_command(struct EventStruct *event) {

// Topic : event->String1
// Message: event->String2
String cmd;
bool validTopic = false;
const int lastindex = event->String1.lastIndexOf('/');
const String lastPartTopic = event->String1.substring(lastindex + 1);
const bool has_cmd_arg_index = event->String1.lastIndexOf(F("cmd_arg")) != -1;

if (equals(lastPartTopic, F("cmd"))) {
// Example:
// Topic: ESP_Easy/Bathroom_pir_env/cmd
// Message: gpio,14,0
// Full command: gpio,14,0

cmd = event->String2;

// SP_C005a: string= ;cmd=gpio,12,0 ;taskIndex=12 ;string1=ESPT12/cmd ;string2=gpio,12,0
validTopic = true;
} else if (has_cmd_arg_index) {
// Example:
// Topic: ESP_Easy/Bathroom_pir_env/cmd_arg1/GPIO/0
// Message: 14
// Full command: gpio,14,0

uint8_t topic_index = 1;
String topic_folder = parseStringKeepCase(event->String1, topic_index, '/');

while(!topic_folder.startsWith(F("cmd_arg")) && !topic_folder.isEmpty()) {
++topic_index;
topic_folder = parseStringKeepCase(event->String1, topic_index, '/');
}
if (!topic_folder.isEmpty()) {
int cmd_arg_nr = -1;
if (validIntFromString(topic_folder.substring(7), cmd_arg_nr)) {
int constructed_cmd_arg_nr = 0;
bool validTopic = MQTT_handle_topic_commands(event); // default handling of /cmd and /set topics

if (!validTopic) {
String cmd;
const int lastindex = event->String1.lastIndexOf('/');
const String lastPartTopic = event->String1.substring(lastindex + 1);
const bool has_cmd_arg_index = event->String1.lastIndexOf(F("cmd_arg")) != -1;

if (has_cmd_arg_index) {
// Example:
// Topic: ESP_Easy/Bathroom_pir_env/cmd_arg1/GPIO/0
// Message: 14
// Full command: gpio,14,0

uint8_t topic_index = 1;
String topic_folder = parseStringKeepCase(event->String1, topic_index, '/');

while (!topic_folder.startsWith(F("cmd_arg")) && !topic_folder.isEmpty()) {
++topic_index;
topic_folder = parseStringKeepCase(event->String1, topic_index, '/');
bool msg_added = false;
while(!topic_folder.isEmpty()) {
if (constructed_cmd_arg_nr != 0) {
cmd += ',';
}

if (!topic_folder.isEmpty()) {
int cmd_arg_nr = -1;

if (validIntFromString(topic_folder.substring(7), cmd_arg_nr)) {
int constructed_cmd_arg_nr = 0;
++topic_index;
topic_folder = parseStringKeepCase(event->String1, topic_index, '/');
bool msg_added = false;

while (!topic_folder.isEmpty()) {
if (constructed_cmd_arg_nr != 0) {
cmd += ',';
}

if (constructed_cmd_arg_nr == cmd_arg_nr) {
cmd += event->String2;
msg_added = true;
} else {
cmd += topic_folder;
++topic_index;
topic_folder = parseStringKeepCase(event->String1, topic_index, '/');
}
++constructed_cmd_arg_nr;
}
if (constructed_cmd_arg_nr == cmd_arg_nr) {

if (!msg_added) {
cmd += ',';
cmd += event->String2;
msg_added = true;
} else {
cmd += topic_folder;
++topic_index;
topic_folder = parseStringKeepCase(event->String1, topic_index, '/');
}
++constructed_cmd_arg_nr;
}
if (!msg_added) {
cmd += ',';
cmd += event->String2;
}
//addLog(LOG_LEVEL_INFO, String(F("MQTT cmd: ")) + cmd);

validTopic = true;
}
}
} else {
// Example:
// Topic: ESP_Easy/Bathroom_pir_env/GPIO/14
// Message: 0 or 1
// Full command: gpio,14,0
if (lastindex > 0) {
// Topic has at least one separator
int lastPartTopic_int;
float value_f;

if (validFloatFromString(event->String2, value_f) &&
validIntFromString(lastPartTopic, lastPartTopic_int)) {
int prevLastindex = event->String1.lastIndexOf('/', lastindex - 1);
cmd = event->String1.substring(prevLastindex + 1, lastindex);
cmd += ',';
cmd += lastPartTopic_int;
cmd += ',';
cmd += event->String2; // Just use the original format
validTopic = true;
}
}
}
// addLog(LOG_LEVEL_INFO, String(F("MQTT cmd: ")) + cmd);

if (validTopic) {
// in case of event, store to buffer and return...
const String command = parseString(cmd, 1);

if ((equals(command, F("event"))) || (equals(command, F("asyncevent")))) {
if (Settings.UseRules) {
// Need to sanitize the event a bit to allow for sending event values as MQTT messages.
// For example:
// Publish topic: espeasy_node/cmd_arg2/event/myevent/2
// Message: 1
// Actual event: myevent=1,2

// Strip out the "event" or "asyncevent" part, leaving the actual event string
cmd = parseStringToEndKeepCase(cmd, 2);

{
// Get the first part upto a parameter separator
// Example: "myEvent,1,2,3", which needs to be converted to "myEvent=1,2,3"
// N.B. This may contain the first eventvalue too
// e.g. "myEvent=1,2,3" => "myEvent=1"
String eventName = parseStringKeepCase(cmd, 1);
String eventValues = parseStringToEndKeepCase(cmd, 2);
const int equal_pos = eventName.indexOf('=');
if (equal_pos != -1) {
// We found an '=' character, so the actual event name is everything before that char.
eventName = cmd.substring(0, equal_pos);
eventValues = cmd.substring(equal_pos + 1); // Rest of the event, after the '=' char
}
if (eventValues.startsWith(F(","))) {
// Need to reconstruct the event to get rid of calls like these:
// myevent=,1,2
eventValues = eventValues.substring(1);
}
// Now reconstruct the complete event
// Without event values: "myEvent" (no '=' char)
// With event values: "myEvent=1,2,3"

// Re-using the 'cmd' String as that has pre-allocated memory which is
// known to be large enough to hold the entire event.
cmd = eventName;
if (eventValues.length() > 0) {
// Only append an = if there are eventvalues.
cmd += '=';
cmd += eventValues;
}
validTopic = true;
}
// Check for duplicates, as sometimes a node may have multiple subscriptions to the same topic.
// Then it may add several of the same events in a burst.
eventQueue.addMove(std::move(cmd), true);
}
} else {
ExecuteCommand_all(EventValueSource::Enum::VALUE_SOURCE_MQTT, cmd.c_str());
// Example:
// Topic: ESP_Easy/Bathroom_pir_env/GPIO/14
// Message: 0 or 1
// Full command: gpio,14,0
if (lastindex > 0) {
// Topic has at least one separator
int lastPartTopic_int;
float value_f;

if (validFloatFromString(event->String2, value_f) &&
validIntFromString(lastPartTopic, lastPartTopic_int)) {
const int prevLastindex = event->String1.lastIndexOf('/', lastindex - 1);
cmd = strformat(F("%s,%d,%s"),
event->String1.substring(prevLastindex + 1, lastindex).c_str(),
lastPartTopic_int,
event->String2.c_str()); // Just use the original format
validTopic = true;
}
}
}

if (validTopic) {
MQTT_execute_command(cmd);
}
}
return validTopic;
Expand Down
Loading