Files
slide/src/mqttcontroller.cpp
Nathan Coad 806d701535
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
add more immich control options
2026-02-01 21:24:24 +11:00

178 lines
4.1 KiB
C++

#include "mqttcontroller.h"
#include "logger.h"
#include <QMetaObject>
#include <mosquitto.h>
namespace {
bool g_mqttInitialized = false;
}
MqttController::MqttController(const MqttConfig &configIn, QObject *parent)
: QObject(parent),
config(configIn)
{
if (!g_mqttInitialized)
{
mosquitto_lib_init();
g_mqttInitialized = true;
}
controlTopic = QString::fromStdString(config.topic);
immichTopic = QString::fromStdString(config.immichTopic);
}
MqttController::~MqttController()
{
if (client)
{
mosquitto_disconnect(client);
mosquitto_loop_stop(client, true);
mosquitto_destroy(client);
client = nullptr;
}
}
void MqttController::start()
{
if (!config.enabled)
{
Log("MQTT disabled or missing host/topic.");
return;
}
QString clientId = QString::fromStdString(config.clientId);
if (clientId.isEmpty())
clientId = "slide";
client = mosquitto_new(clientId.toUtf8().constData(), true, this);
if (!client)
{
Log("MQTT: failed to create client.");
return;
}
mosquitto_connect_callback_set(client, &MqttController::HandleConnect);
mosquitto_message_callback_set(client, &MqttController::HandleMessage);
if (!config.username.empty())
{
mosquitto_username_pw_set(client, config.username.c_str(),
config.password.empty() ? nullptr : config.password.c_str());
}
mosquitto_reconnect_delay_set(client, 2, 30, true);
int rc = mosquitto_connect_async(client, config.host.c_str(), config.port, config.keepAlive);
if (rc != MOSQ_ERR_SUCCESS)
{
Log("MQTT connect failed: ", mosquitto_strerror(rc));
return;
}
rc = mosquitto_loop_start(client);
if (rc != MOSQ_ERR_SUCCESS)
{
Log("MQTT loop start failed: ", mosquitto_strerror(rc));
return;
}
}
void MqttController::HandleConnect(struct mosquitto *mosq, void *userdata, int rc)
{
auto *self = static_cast<MqttController *>(userdata);
if (!self || !mosq)
return;
if (rc != 0)
{
Log("MQTT connect error: ", mosquitto_strerror(rc));
return;
}
self->connected = true;
self->subscribe();
}
void MqttController::subscribe()
{
if (!client || !connected)
return;
int rc = mosquitto_subscribe(client, nullptr, config.topic.c_str(), config.qos);
if (rc != MOSQ_ERR_SUCCESS)
{
Log("MQTT subscribe failed: ", mosquitto_strerror(rc));
}
else
{
Log("MQTT subscribed to ", config.topic);
}
if (!config.immichTopic.empty() && config.immichTopic != config.topic)
{
rc = mosquitto_subscribe(client, nullptr, config.immichTopic.c_str(), config.qos);
if (rc != MOSQ_ERR_SUCCESS)
{
Log("MQTT subscribe (immich) failed: ", mosquitto_strerror(rc));
}
else
{
Log("MQTT subscribed to ", config.immichTopic);
}
}
}
void MqttController::HandleMessage(struct mosquitto *mosq, void *userdata, const struct mosquitto_message *message)
{
Q_UNUSED(mosq);
auto *self = static_cast<MqttController *>(userdata);
if (!self || !message || !message->payload)
return;
QString payload = QString::fromUtf8(static_cast<const char *>(message->payload), message->payloadlen);
QString topic = QString::fromUtf8(message->topic);
QMetaObject::invokeMethod(self, "handleMessage", Qt::QueuedConnection,
Q_ARG(QString, topic),
Q_ARG(QString, payload));
}
void MqttController::handleMessage(const QString &topic, const QString &payload)
{
QString cmd = payload.trimmed().toLower();
if (cmd.isEmpty())
return;
Log("MQTT message on ", topic.toStdString(), ": ", cmd.toStdString());
if (!immichTopic.isEmpty() && topic == immichTopic)
{
emit immichControl(payload);
return;
}
if (cmd == "play" || cmd == "resume")
{
emit play();
return;
}
if (cmd == "pause")
{
emit pause();
return;
}
if (cmd == "next" || cmd == "skip" || cmd == "next-image")
{
emit nextImage();
return;
}
if (cmd == "next-folder" || cmd == "folder-next" || cmd == "skip-folder")
{
emit nextFolder();
return;
}
if (cmd == "restart" || cmd == "reset")
{
emit restart();
return;
}
Log("MQTT unknown command: ", cmd.toStdString());
}