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