From 083c42df575309aa58c443a423e258d90f12815d Mon Sep 17 00:00:00 2001 From: WarmUpTill <19472752+WarmUpTill@users.noreply.github.com> Date: Tue, 22 Apr 2025 15:24:19 +0200 Subject: [PATCH] Add MQTT condition and action --- data/locale/en-US.ini | 40 +++- plugins/CMakeLists.txt | 1 + plugins/mqtt/macro-action-mqtt.cpp | 202 +++++++++++++++++++ plugins/mqtt/macro-action-mqtt.hpp | 73 +++++++ plugins/mqtt/macro-condition-mqtt.cpp | 268 ++++++++++++++++++++++++++ plugins/mqtt/macro-condition-mqtt.hpp | 86 +++++++++ 6 files changed, 669 insertions(+), 1 deletion(-) create mode 100644 plugins/mqtt/macro-action-mqtt.cpp create mode 100644 plugins/mqtt/macro-action-mqtt.hpp create mode 100644 plugins/mqtt/macro-condition-mqtt.cpp create mode 100644 plugins/mqtt/macro-condition-mqtt.hpp diff --git a/data/locale/en-US.ini b/data/locale/en-US.ini index df2c0f66..c78c90d3 100644 --- a/data/locale/en-US.ini +++ b/data/locale/en-US.ini @@ -774,9 +774,11 @@ AdvSceneSwitcher.condition.streamDeck.stopListen="Stop listening" AdvSceneSwitcher.condition.streamDeck.pluginDownload="

The Stream Deck plugin can be found here on GitHub.

" AdvSceneSwitcher.condition.gameCapture="Game capture" AdvSceneSwitcher.condition.gameCapture.entry="{{sources}}hooked a game." - AdvSceneSwitcher.condition.screenshot="Screenshot" AdvSceneSwitcher.condition.screenshot.entry="A screenshot was taken" +AdvSceneSwitcher.condition.mqtt="MQTT" +AdvSceneSwitcher.condition.mqtt.layout.match="Message was received from{{connection}} which matches{{regex}}:" +AdvSceneSwitcher.condition.mqtt.layout.listen="Set message selection to incoming message:{{listenButton}}" # Macro Actions AdvSceneSwitcher.action.unknown="Unknown action" @@ -1208,6 +1210,11 @@ AdvSceneSwitcher.action.window.entry="{{actions}}{{windows}}{{regex}}" AdvSceneSwitcher.action.log="Log" AdvSceneSwitcher.action.log.placeholder="My log message!" AdvSceneSwitcher.action.log.entry="Write to OBS log:{{logMessage}}" +AdvSceneSwitcher.action.mqtt="MQTT" +AdvSceneSwitcher.action.mqtt.topic="Topic:" +AdvSceneSwitcher.action.mqtt.qos="QoS:" +AdvSceneSwitcher.action.mqtt.retain="Retained message:" +AdvSceneSwitcher.action.mqtt.layout="Send message to{{connection}}" # Hotkey AdvSceneSwitcher.hotkey.startSwitcherHotkey="Start the Advanced Scene Switcher" @@ -1364,6 +1371,35 @@ AdvSceneSwitcher.osc.message.type.false="False" AdvSceneSwitcher.osc.message.type.infinity="Infinitum" AdvSceneSwitcher.osc.message.type.null="Nil" +AdvSceneSwitcher.mqttConnection.startListen="Start listening" +AdvSceneSwitcher.mqttConnection.stopListen="Stop listening" +AdvSceneSwitcher.mqttConnection.select="--select connection--" +AdvSceneSwitcher.mqttConnection.add="Add new connection" +AdvSceneSwitcher.mqttConnection.configure="Configure connection settings" +AdvSceneSwitcher.mqttConnection.invalid="Invalid connection selection" +AdvSceneSwitcher.mqttConnection.name="Name:" +AdvSceneSwitcher.mqttConnection.address="Address:" +AdvSceneSwitcher.mqttConnection.username="Username:" +AdvSceneSwitcher.mqttConnection.password="Password:" +AdvSceneSwitcher.mqttConnection.reconnect="Reconnect automatically:" +AdvSceneSwitcher.mqttConnection.reconnectDelay="Automatically reconnect after:" +AdvSceneSwitcher.mqttConnection.connectOnStart="Connect on startup:" +AdvSceneSwitcher.mqttConnection.test="Test connection" +AdvSceneSwitcher.mqttConnection.status.connected="Connected!" +AdvSceneSwitcher.mqttConnection.status.connecting="Connecting ..." +AdvSceneSwitcher.mqttConnection.status.disconnected="Could not connect!" +AdvSceneSwitcher.mqttConnection.topic="Topic" +AdvSceneSwitcher.mqttConnection.qos="QoS" +AdvSceneSwitcher.mqttConnection.topics="Topic subscriptions:" +AdvSceneSwitcher.mqttConnection.inputWarning.emptyTopic="Topic cannot be empty." +AdvSceneSwitcher.mqttConnection.inputWarning.emptyTopic.title="Input Error" +AdvSceneSwitcher.mqttConnection.inputWarning.duplicateTopic="This topic already exists." +AdvSceneSwitcher.mqttConnection.inputWarning.duplicateTopic.title="Duplicate Topic" +AdvSceneSwitcher.mqttConnection.inputWarning.qosRange="QoS must be 0, 1, or 2." +AdvSceneSwitcher.mqttConnection.inputWarning.qosRange.title="Invalid QoS" +AdvSceneSwitcher.mqttConnection.add.layout="Add topic subscription{{topic}}with QoS{{QoS}}" +AdvSceneSwitcher.mqttConnection.add.title="Add MQTT Topic Subscription" + AdvSceneSwitcher.twitchToken.name="Account name:" AdvSceneSwitcher.twitchToken.nameNotAvailable="Account already in use" AdvSceneSwitcher.twitchToken.select="--select Twitch connection--" @@ -2066,6 +2102,8 @@ AdvSceneSwitcher.tempVar.http.body="Message body" AdvSceneSwitcher.tempVar.http.error="Error" AdvSceneSwitcher.tempVar.http.error.description="Empty when no error occurred.\nOther possible values:\n\n * Could not establish connection\n * Failed to bind IP address\n * Failed to read connection\n * Failed to write connection\n * Maximum redirect count exceeded\n * Connection handling canceled\n * SSL connection failed\n * SSL certificate loading failed\n * SSL server verification failed\n * Unsupported HTTP multipart boundary characters\n * Compression failed\n * Connection timed out\n * Proxy connection failed\n * Unknown" +AdvSceneSwitcher.tempVar.mqtt.message="Message" + AdvSceneSwitcher.selectScene="--select scene--" AdvSceneSwitcher.selectPreviousScene="Previous Scene" AdvSceneSwitcher.selectCurrentScene="Current Scene" diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 1900afd3..2f6a38f9 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -30,6 +30,7 @@ install_advss_plugin_dependency(...) add_plugin(http) add_plugin(midi) +add_plugin(mqtt) add_plugin(openvr) add_plugin(stream-deck) add_plugin(twitch) diff --git a/plugins/mqtt/macro-action-mqtt.cpp b/plugins/mqtt/macro-action-mqtt.cpp new file mode 100644 index 00000000..bbdfc0f9 --- /dev/null +++ b/plugins/mqtt/macro-action-mqtt.cpp @@ -0,0 +1,202 @@ +#include "macro-action-mqtt.hpp" +#include "layout-helpers.hpp" +#include "mqtt-helpers.hpp" +#include "ui-helpers.hpp" + +namespace advss { + +const std::string MacroActionMqtt::id = "mqtt"; + +bool MacroActionMqtt::_registered = MacroActionFactory::Register( + MacroActionMqtt::id, + {MacroActionMqtt::Create, MacroActionMqttEdit::Create, + "AdvSceneSwitcher.action.mqtt"}); + +bool MacroActionMqtt::PerformAction() +{ + auto connection = _connection.lock(); + if (!connection) { + return true; + } + + int qos = _qos; + if (qos > 2 || qos < 0) { + qos = 1; + blog(LOG_WARNING, + "%s: use QoS 1 isntead of invalid QoS value %d", __func__, + (int)_qos); + } + + (void)connection->SendMessage(_topic, _message, qos, _retain); + return true; +} + +void MacroActionMqtt::LogAction() const +{ + ablog(LOG_INFO, + "send MQTT message to \"%s\" (topic: \"%s\" - payload: \"%s\" - qos: %d - retain: %d)", + GetWeakMqttConnectionName(_connection).c_str(), _topic.c_str(), + _message.c_str(), (int)_qos, _retain); +} + +bool MacroActionMqtt::Save(obs_data_t *obj) const +{ + MacroAction::Save(obj); + obs_data_set_string(obj, "connection", + GetWeakMqttConnectionName(_connection).c_str()); + _message.Save(obj, "message"); + _topic.Save(obj, "topic"); + _qos.Save(obj, "qos"); + obs_data_set_bool(obj, "retain", _retain); + return true; +} + +bool MacroActionMqtt::Load(obs_data_t *obj) +{ + MacroAction::Load(obj); + SetConnection(obs_data_get_string(obj, "connection")); + _message.Load(obj, "message"); + _topic.Load(obj, "topic"); + _qos.Load(obj, "qos"); + _retain = obs_data_get_bool(obj, "retain"); + return true; +} + +void MacroActionMqtt::SetConnection(const std::string &name) +{ + _connection = GetWeakMqttConnectionByName(name); +} + +std::weak_ptr MacroActionMqtt::GetConnection() const +{ + return _connection; +} + +std::string MacroActionMqtt::GetShortDesc() const +{ + return GetWeakMqttConnectionName(_connection); +} + +void MacroActionMqtt::ResolveVariablesToFixedValues() +{ + _message.ResolveVariables(); + _topic.ResolveVariables(); + _qos.ResolveVariables(); +} + +std::shared_ptr MacroActionMqtt::Create(Macro *m) +{ + return std::make_shared(m); +} + +std::shared_ptr MacroActionMqtt::Copy() const +{ + return std::make_shared(*this); +} + +MacroActionMqttEdit::MacroActionMqttEdit( + QWidget *parent, std::shared_ptr entryData) + : QWidget(parent), + _connection(new MqttConnectionSelection(this)), + _message(new VariableTextEdit(this, 5, 1, 1)), + _topic(new VariableLineEdit(this)), + _qos(new VariableSpinBox(this)), + _retain(new QCheckBox(this)) +{ + _qos->setMinimum(0); + _qos->setMaximum(2); + + QWidget::connect(_connection, SIGNAL(SelectionChanged(const QString &)), + this, + SLOT(ConnectionSelectionChanged(const QString &))); + QWidget::connect(_message, SIGNAL(textChanged()), this, + SLOT(MqttMessageChanged())); + QWidget::connect(_topic, SIGNAL(editingFinished()), this, + SLOT(MqttTopicChanged())); + QWidget::connect( + _qos, + SIGNAL(NumberVariableChanged(const NumberVariable &)), + this, SLOT(QoSChanged(const NumberVariable &))); + QWidget::connect(_retain, SIGNAL(stateChanged(int)), this, + SLOT(RetainChanged(int))); + + auto sendLayout = new QHBoxLayout; + PlaceWidgets(obs_module_text("AdvSceneSwitcher.action.mqtt.layout"), + sendLayout, {{"{{connection}}", _connection}}); + + int row = 0; + auto grid = new QGridLayout; + grid->addWidget(new QLabel(obs_module_text( + "AdvSceneSwitcher.action.mqtt.topic")), + row, 0); + grid->addWidget(_topic, row, 1); + row++; + grid->addWidget( + new QLabel(obs_module_text("AdvSceneSwitcher.action.mqtt.qos")), + row, 0); + grid->addWidget(_qos, row, 1); + row++; + grid->addWidget(new QLabel(obs_module_text( + "AdvSceneSwitcher.action.mqtt.retain")), + row, 0); + grid->addWidget(_retain, row, 1); + + auto mainLayout = new QVBoxLayout; + mainLayout->addLayout(sendLayout); + mainLayout->addWidget(_message); + mainLayout->addLayout(grid); + setLayout(mainLayout); + + _entryData = entryData; + UpdateEntryData(); + _loading = false; +} + +void MacroActionMqttEdit::UpdateEntryData() +{ + if (!_entryData) { + return; + } + + _connection->SetConnection(_entryData->GetConnection()); + _message->setPlainText(_entryData->_message); + _topic->setText(_entryData->_topic); + _qos->SetValue(_entryData->_qos); + _retain->setChecked(_entryData->_retain); + + adjustSize(); + updateGeometry(); +} + +void MacroActionMqttEdit::ConnectionSelectionChanged(const QString &connection) +{ + GUARD_LOADING_AND_LOCK(); + _entryData->SetConnection(connection.toStdString()); + emit(HeaderInfoChanged(connection)); +} + +void MacroActionMqttEdit::MqttMessageChanged() +{ + GUARD_LOADING_AND_LOCK(); + _entryData->_message = _message->toPlainText().toStdString(); +} + +void MacroActionMqttEdit::MqttTopicChanged() +{ + GUARD_LOADING_AND_LOCK(); + _entryData->_topic = _topic->text().toStdString(); +} + +void MacroActionMqttEdit::QoSChanged(const IntVariable &value) +{ + GUARD_LOADING_AND_LOCK(); + _entryData->_qos = value; +} + +void MacroActionMqttEdit::RetainChanged(int value) +{ + GUARD_LOADING_AND_LOCK(); + _entryData->_retain = value; +} + +} // namespace advss diff --git a/plugins/mqtt/macro-action-mqtt.hpp b/plugins/mqtt/macro-action-mqtt.hpp new file mode 100644 index 00000000..f9bcff0f --- /dev/null +++ b/plugins/mqtt/macro-action-mqtt.hpp @@ -0,0 +1,73 @@ +#pragma once +#include "macro-action-edit.hpp" +#include "mqtt-helpers.hpp" +#include "variable-line-edit.hpp" +#include "variable-spinbox.hpp" +#include "variable-text-edit.hpp" + +namespace advss { + +class MacroActionMqtt : public MacroAction { +public: + MacroActionMqtt(Macro *m) : MacroAction(m, true) {} + bool PerformAction(); + void LogAction() const; + bool Save(obs_data_t *obj) const; + bool Load(obs_data_t *obj); + void SetConnection(const std::string &name); + std::weak_ptr GetConnection() const; + std::string GetShortDesc() const; + void ResolveVariablesToFixedValues(); + std::string GetId() const { return id; }; + static std::shared_ptr Create(Macro *m); + std::shared_ptr Copy() const; + + StringVariable _message = "payload"; + StringVariable _topic = "/topic"; + IntVariable _qos = 1; + bool _retain = false; + +private: + std::weak_ptr _connection; + + static bool _registered; + static const std::string id; +}; + +class MacroActionMqttEdit : public QWidget { + Q_OBJECT + +public: + MacroActionMqttEdit( + QWidget *parent, + std::shared_ptr entryData = nullptr); + void UpdateEntryData(); + static QWidget *Create(QWidget *parent, + std::shared_ptr action) + { + return new MacroActionMqttEdit( + parent, + std::dynamic_pointer_cast(action)); + } + +private slots: + void ConnectionSelectionChanged(const QString &); + void MqttMessageChanged(); + void MqttTopicChanged(); + void QoSChanged(const NumberVariable &value); + void RetainChanged(int value); +signals: + void HeaderInfoChanged(const QString &); + +private: + MqttConnectionSelection *_connection; + VariableTextEdit *_message; + VariableLineEdit *_topic; + VariableSpinBox *_qos; + QCheckBox *_retain; + + std::shared_ptr _entryData; + bool _loading = true; +}; + +} // namespace advss diff --git a/plugins/mqtt/macro-condition-mqtt.cpp b/plugins/mqtt/macro-condition-mqtt.cpp new file mode 100644 index 00000000..51b82926 --- /dev/null +++ b/plugins/mqtt/macro-condition-mqtt.cpp @@ -0,0 +1,268 @@ +#include "macro-condition-mqtt.hpp" +#include "layout-helpers.hpp" +#include "macro-helpers.hpp" +#include "ui-helpers.hpp" + +namespace advss { + +const std::string MacroConditionMqtt::id = "mqtt"; + +bool MacroConditionMqtt::_registered = MacroConditionFactory::Register( + MacroConditionMqtt::id, + {MacroConditionMqtt::Create, MacroConditionMqttEdit::Create, + "AdvSceneSwitcher.condition.mqtt"}); + +bool MacroConditionMqtt::CheckCondition() +{ + if (!_messageBuffer) { + return false; + } + + const bool macroWasPausedSinceLastCheck = + MacroWasPausedSince(GetMacro(), _lastCheck); + _lastCheck = std::chrono::high_resolution_clock::now(); + if (macroWasPausedSinceLastCheck) { + _messageBuffer->Clear(); + return false; + } + + const auto messageMatches = [this](const std::string &message) -> bool { + if (_regex.Enabled()) { + return _regex.Matches(message, _message); + } + return message != std::string(_message); + }; + + while (!_messageBuffer->Empty()) { + auto message = _messageBuffer->ConsumeMessage(); + if (!message) { + continue; + } + + if (!messageMatches(*message)) { + continue; + } + + SetTempVarValue("message", *message); + if (_clearBufferOnMatch) { + _messageBuffer->Clear(); + } + return true; + } + + return false; +} + +bool MacroConditionMqtt::Save(obs_data_t *obj) const +{ + MacroCondition::Save(obj); + _message.Save(obj, "message"); + _regex.Save(obj); + obs_data_set_string(obj, "connection", + GetWeakMqttConnectionName(_connection).c_str()); + obs_data_set_bool(obj, "clearBufferOnMatch", _clearBufferOnMatch); + return true; +} + +bool MacroConditionMqtt::Load(obs_data_t *obj) +{ + MacroCondition::Load(obj); + _message.Load(obj, "message"); + _regex.Load(obj); + _clearBufferOnMatch = obs_data_get_bool(obj, "clearBufferOnMatch"); + SetConnection(obs_data_get_string(obj, "connection")); + return true; +} + +std::string MacroConditionMqtt::GetShortDesc() const +{ + return GetWeakMqttConnectionName(_connection); +} + +void MacroConditionMqtt::SetConnection(const std::string &name) +{ + _connection = GetWeakMqttConnectionByName(name); + auto connection = _connection.lock(); + if (!connection) { + return; + } + _messageBuffer = connection->RegisterForEvents(); +} + +std::weak_ptr MacroConditionMqtt::GetConnection() const +{ + return _connection; +} + +void MacroConditionMqtt::SetupTempVars() +{ + MacroCondition::SetupTempVars(); + AddTempvar("message", + obs_module_text("AdvSceneSwitcher.tempVar.mqtt.message")); +} + +MacroConditionMqttEdit::MacroConditionMqttEdit( + QWidget *parent, std::shared_ptr entryData) + : QWidget(parent), + _connection(new MqttConnectionSelection(this)), + _message(new VariableTextEdit(this, 5, 1, 1)), + _regex(new RegexConfigWidget(parent)), + _listen(new QPushButton(obs_module_text( + "AdvSceneSwitcher.mqttConnection.startListen"))), + _clearBufferOnMatch(new QCheckBox( + obs_module_text("AdvSceneSwitcher.clearBufferOnMatch"))) +{ + QWidget::connect(_message, SIGNAL(textChanged()), this, + SLOT(MqttMessageChanged())); + QWidget::connect(_regex, + SIGNAL(RegexConfigChanged(const RegexConfig &)), this, + SLOT(RegexChanged(const RegexConfig &))); + QWidget::connect(_connection, SIGNAL(SelectionChanged(const QString &)), + this, + SLOT(ConnectionSelectionChanged(const QString &))); + QWidget::connect(_listen, SIGNAL(clicked()), this, + SLOT(ToggleListen())); + QWidget::connect(_clearBufferOnMatch, SIGNAL(stateChanged(int)), this, + SLOT(ClearBufferOnMatchChanged(int))); + QWidget::connect(&_listenTimer, SIGNAL(timeout()), this, + SLOT(SetMessageSelectionToLastReceived())); + + auto entryLayout = new QHBoxLayout; + PlaceWidgets( + obs_module_text("AdvSceneSwitcher.condition.mqtt.layout.match"), + entryLayout, + {{"{{connection}}", _connection}, {"{{regex}}", _regex}}); + auto listenLayout = new QHBoxLayout; + PlaceWidgets(obs_module_text( + "AdvSceneSwitcher.condition.mqtt.layout.listen"), + listenLayout, {{"{{listenButton}}", _listen}}); + + auto mainLayout = new QVBoxLayout; + mainLayout->addLayout(entryLayout); + mainLayout->addWidget(_message); + mainLayout->addLayout(listenLayout); + mainLayout->addWidget(_clearBufferOnMatch); + setLayout(mainLayout); + + _listenTimer.setInterval(100); + + _entryData = entryData; + UpdateEntryData(); + _loading = false; +} + +MacroConditionMqttEdit::~MacroConditionMqttEdit() +{ + EnableListening(false); +} + +void MacroConditionMqttEdit::UpdateEntryData() +{ + if (!_entryData) { + return; + } + + _message->setPlainText(_entryData->_message); + _connection->SetConnection(_entryData->GetConnection()); + _regex->SetRegexConfig(_entryData->_regex); + _clearBufferOnMatch->setChecked(_entryData->_clearBufferOnMatch); + + adjustSize(); + updateGeometry(); +} + +void MacroConditionMqttEdit::ConnectionSelectionChanged( + const QString &connection) +{ + GUARD_LOADING_AND_LOCK(); + _entryData->SetConnection(connection.toStdString()); + emit(HeaderInfoChanged(connection)); +} + +void MacroConditionMqttEdit::MqttMessageChanged() +{ + GUARD_LOADING_AND_LOCK(); + _entryData->_message = _message->toPlainText().toStdString(); +} + +void MacroConditionMqttEdit::ClearBufferOnMatchChanged(int value) +{ + GUARD_LOADING_AND_LOCK(); + _entryData->_clearBufferOnMatch = value; +} + +void MacroConditionMqttEdit::RegexChanged(const RegexConfig &conf) +{ + GUARD_LOADING_AND_LOCK(); + _entryData->_regex = conf; + + adjustSize(); + updateGeometry(); +} + +void MacroConditionMqttEdit::EnableListening(bool enable) +{ + if (_currentlyListening == enable) { + return; + } + if (enable) { + auto connectionItem = _connection->GetCurrentItem(); + if (!connectionItem) { + return; + } + auto weakConnection = GetWeakMqttConnectionByQString( + QString::fromStdString(connectionItem->Name())); + auto connection = weakConnection.lock(); + if (!connection) { + return; + } + _messageBuffer = connection->RegisterForEvents(); + _listenTimer.start(); + } else { + _messageBuffer.reset(); + _listenTimer.stop(); + } +} + +void MacroConditionMqttEdit::ToggleListen() +{ + if (!_entryData) { + return; + } + + _listen->setText( + _currentlyListening + ? obs_module_text( + "AdvSceneSwitcher.mqttConnection.startListen") + : obs_module_text( + "AdvSceneSwitcher.mqttConnection.stopListen")); + EnableListening(!_currentlyListening); + _currentlyListening = !_currentlyListening; + _message->setDisabled(_currentlyListening); +} + +void MacroConditionMqttEdit::SetMessageSelectionToLastReceived() +{ + auto lock = LockContext(); + if (!_entryData || !_messageBuffer || _messageBuffer->Empty()) { + return; + } + + std::optional message; + while (!_messageBuffer->Empty()) { + message = _messageBuffer->ConsumeMessage(); + if (!message) { + continue; + } + } + + if (!message) { + return; + } + + const QSignalBlocker blocker(_message); + _message->setPlainText(*message); + _entryData->_message = *message; +} + +} // namespace advss diff --git a/plugins/mqtt/macro-condition-mqtt.hpp b/plugins/mqtt/macro-condition-mqtt.hpp new file mode 100644 index 00000000..2f0013f7 --- /dev/null +++ b/plugins/mqtt/macro-condition-mqtt.hpp @@ -0,0 +1,86 @@ +#pragma once +#include "macro-condition-edit.hpp" +#include "mqtt-helpers.hpp" +#include "regex-config.hpp" +#include "variable-text-edit.hpp" + +#include +#include +#include + +namespace advss { + +class MacroConditionMqtt : public MacroCondition { +public: + MacroConditionMqtt(Macro *m) : MacroCondition(m, true) {} + bool CheckCondition(); + bool Save(obs_data_t *obj) const; + bool Load(obs_data_t *obj); + std::string GetShortDesc() const; + std::string GetId() const { return id; }; + static std::shared_ptr Create(Macro *m) + { + return std::make_shared(m); + } + + void SetConnection(const std::string &); + std::weak_ptr GetConnection() const; + + StringVariable _message; + RegexConfig _regex; + bool _clearBufferOnMatch = true; + +private: + void SetupTempVars(); + + std::weak_ptr _connection; + MqttMessageBuffer _messageBuffer; + std::chrono::high_resolution_clock::time_point _lastCheck{}; + static bool _registered; + static const std::string id; +}; + +class MacroConditionMqttEdit : public QWidget { + Q_OBJECT + +public: + MacroConditionMqttEdit( + QWidget *parent, + std::shared_ptr cond = nullptr); + virtual ~MacroConditionMqttEdit(); + void UpdateEntryData(); + static QWidget *Create(QWidget *parent, + std::shared_ptr cond) + { + return new MacroConditionMqttEdit( + parent, + std::dynamic_pointer_cast(cond)); + } + +private slots: + void ConnectionSelectionChanged(const QString &); + void MqttMessageChanged(); + void ClearBufferOnMatchChanged(int); + void RegexChanged(const RegexConfig &conf); + void ToggleListen(); + void SetMessageSelectionToLastReceived(); +signals: + void HeaderInfoChanged(const QString &); + +private: + void EnableListening(bool); + + MqttConnectionSelection *_connection; + VariableTextEdit *_message; + RegexConfigWidget *_regex; + QPushButton *_listen; + QCheckBox *_clearBufferOnMatch; + + std::shared_ptr _entryData; + QTimer _listenTimer; + MqttMessageBuffer _messageBuffer; + bool _currentlyListening = false; + bool _loading = true; +}; + +} // namespace advss