extend MQTT integration support with type_switch

This commit is contained in:
John
2022-01-22 22:21:59 +01:00
parent c499c4e74b
commit 17b1bdf4a0
3 changed files with 82 additions and 11 deletions
+71 -9
View File
@@ -335,7 +335,7 @@ static const char* knownFieldNames[] = {
/** the number of known field names. */
static const size_t knownFieldCount = sizeof(knownFieldNames) / sizeof(char*);
std::pair<string, int> makeField(string name, bool isField) {
std::pair<string, int> makeField(const string name, bool isField) {
if (!isField) {
return {name, -1};
}
@@ -354,7 +354,7 @@ MqttReplacer::MqttReplacer(bool fillDefault) {
}
}
bool MqttReplacer::parse(const string& templateStr, bool onlyKnown, bool noKnownDuplicates) {
bool MqttReplacer::parse(const string& templateStr, bool onlyKnown, bool noKnownDuplicates, bool emptyIfMissing) {
m_parts.clear();
size_t end = templateStr.length();
bool inField = false;
@@ -404,6 +404,7 @@ bool MqttReplacer::parse(const string& templateStr, bool onlyKnown, bool noKnown
}
}
}
m_emptyIfMissing = emptyIfMissing;
return true;
}
@@ -497,9 +498,17 @@ bool MqttReplacer::reduce(const map<string, string>& values, string& result, boo
}
const auto pos = values.find(it.first);
if (pos==values.cend()) {
result = ret.str();
if (m_emptyIfMissing) {
result = "";
} else {
result = ret.str();
}
return false;
}
if (m_emptyIfMissing && pos->second.empty()) {
result = "";
return true;
}
ret << pos->second;
}
result = ret.str();
@@ -730,10 +739,17 @@ void MqttHandler::parseIntegration(const string& line) {
return;
}
size_t pos = line.find('=');
if (pos==string::npos) {
if (pos==string::npos || pos==0) {
return;
}
string key = line.substr(0, pos);
bool emptyIfMissing = false;
string key;
if (line[pos-1] == '?') {
emptyIfMissing = true;
key = line.substr(0, pos-1);
} else {
key = line.substr(0, pos);
}
FileReader::trim(&key);
string value = line.substr(pos+1);
FileReader::trim(&value);
@@ -741,10 +757,14 @@ void MqttHandler::parseIntegration(const string& line) {
m_replacers.set(key, value); // constant value
} else {
// simple variable
m_replacers.get(key).parse(value, false, false);
m_replacers.get(key).parse(value, false, false, emptyIfMissing);
}
}
static const char* typeNames[] = {
"number", "bits", "string", "date", "time", "datetime",
};
MqttHandler::MqttHandler(UserInfo* userInfo, BusHandler* busHandler, MessageMap* messages)
: DataSink(userInfo, "mqtt"), DataSource(busHandler), WaitThread(), m_messages(messages), m_connected(false),
m_initialConnectFailed(false), m_lastUpdateCheckResult("."), m_lastScanStatus("."), m_lastErrorLogTime(0) {
@@ -788,6 +808,35 @@ MqttHandler::MqttHandler(UserInfo* userInfo, BusHandler* busHandler, MessageMap*
}
parseIntegration(last);
m_replacers.reduce();
if (m_replacers.uses("type_switch")) {
for (auto typeName: typeNames) {
string str = m_replacers.get("type_switch-" + string(typeName), false, false, "type_switch");
if (str.empty()) {
continue;
}
str += '\n'; // add trailing newline to ease the split
size_t from = 0;
do {
pos = str.find('\n', from);
line = str.substr(from, pos-from);
from = pos+1;
FileReader::trim(&line);
if (!line.empty()) {
pos = line.find('=');
if (pos!=string::npos && pos>0) {
string left = line.substr(0, pos);
FileReader::trim(&left);
if (!left.empty()) {
string right = line.substr(pos+1);
FileReader::trim(&right);
FileReader::tolower(&right);
m_typeSwitches[typeName].push_back({left, right});
}
}
}
} while (from<str.length());
}
}
}
}
m_hasDefinitionTopic = !m_replacers.get("definition-topic", false, false).empty();
@@ -1082,6 +1131,7 @@ void MqttHandler::run() {
FileReader::tolower(&filterLevel);
string filterField = m_replacers["filter-field"];
FileReader::tolower(&filterField);
bool usesTypeSwitch = !m_typeSwitches.empty();
m_messages->findAll("", "", "", false, true, true, true, true, true, 0, 0, false, &messages);
for (const auto& message : messages) {
if (message->getCreateTime() <= m_definitionsSince) { // only newer defined
@@ -1147,10 +1197,21 @@ void MqttHandler::run() {
// values.set("min", static_cast<signed>(dt->getMinValue()));
// values.set("max", static_cast<signed>(dt->getMaxValue()));
// }
if (usesTypeSwitch) {
values.reduce();
str = values.get("type_switch-by", false, false);
string typeSwitch;
for (auto& check : m_typeSwitches[typeSuffix]) {
if (FileReader::matches(str, check.second, true, true)) {
typeSwitch = check.first;
break;
}
}
values.set("type_switch", typeSwitch);
}
values.reduce();
str = values.get("type_part_"+typeSuffix, false, false);
str = values.get("type_part-"+typeSuffix, false, false);
values.set("type_part", str);
//TODO Valuelist: => select.options
if (m_publishByField) {
values.set("topic", getTopic(message, "", fieldName)); // TODO already present?
}
@@ -1159,7 +1220,7 @@ void MqttHandler::run() {
string value = values["field_payload"];
if (!value.empty()) {
if (fields.tellp()>0) {
fields << values["field_separator"];
fields << values["field-separator"];
}
fields << value;
}
@@ -1173,6 +1234,7 @@ void MqttHandler::run() {
}
}
time(&m_definitionsSince);
needsWait = true;
}
time(&lastTaskRun);
}
+10 -1
View File
@@ -88,9 +88,11 @@ class MqttReplacer {
* @param templateStr the template string.
* @param onlyKnown true to allow only known field names from @a knownFieldNames.
* @param noKnownDuplicates true to now allow duplicates from @a knownFieldNames.
* @param emptyIfMissing true when the complete result is supposed to be empty when at least one referenced variable
* is empty or not defined.
* @return true on success, false on malformed template string.
*/
bool parse(const string& templateStr, bool onlyKnown = true, bool noKnownDuplicates = true);
bool parse(const string& templateStr, bool onlyKnown = true, bool noKnownDuplicates = true, bool emptyIfMissing = false);
private:
/**
@@ -148,6 +150,10 @@ class MqttReplacer {
* @a knownFieldNames for an unknown field.
*/
vector<std::pair<string, int>> m_parts;
/** true when the complete result is supposed to be empty when at least one referenced variable
* is empty or not defined. */
bool m_emptyIfMissing;
};
@@ -339,6 +345,9 @@ class MqttHandler : public DataSink, public DataSource, public WaitThread {
/** whether the @a m_replacers uses the fields_payload variable. */
bool m_hasDefinitionFieldsPayload;
/** map of type name to a list of pairs of wildcard string and mapped value. */
map<string, vector<std::pair<string, string>>> m_typeSwitches;
/** the subscribed configuration restart topic, or empty. */
string m_subscribeConfigRestartTopic;
+1 -1
View File
@@ -130,7 +130,7 @@ bool FileReader::matches(const string& input, const string& search, bool ignoreC
}
string searchSub = search;
tolower(&inputSub);
return matches(inputSub, searchSub, false, false);
return matches(inputSub, searchSub, false, true);
}
// walk through alternatives
size_t from = 0;