summaryrefslogtreecommitdiff
path: root/mqtt.c
diff options
context:
space:
mode:
Diffstat (limited to 'mqtt.c')
-rw-r--r--mqtt.c232
1 files changed, 232 insertions, 0 deletions
diff --git a/mqtt.c b/mqtt.c
new file mode 100644
index 0000000..f1726f2
--- /dev/null
+++ b/mqtt.c
@@ -0,0 +1,232 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "config.h"
+
+#include "common.h"
+#include "player.h"
+#include "rtsp.h"
+
+#include "rtp.h"
+
+#include "dacp.h"
+#include "mqtt.h"
+#include <mosquitto.h>
+
+// this holds the mosquitto client
+struct mosquitto *global_mosq = NULL;
+char *topic = NULL;
+int connected = 0;
+
+// mosquitto logging
+void _cb_log(__attribute__((unused)) struct mosquitto *mosq, __attribute__((unused)) void *userdata,
+ int level, const char *str) {
+ switch (level) {
+ case MOSQ_LOG_DEBUG:
+ debug(1, str);
+ break;
+ case MOSQ_LOG_INFO:
+ debug(2, str);
+ break;
+ case MOSQ_LOG_NOTICE:
+ debug(3, str);
+ break;
+ case MOSQ_LOG_WARNING:
+ inform(str);
+ break;
+ case MOSQ_LOG_ERR: {
+ die("MQTT: Error: %s\n", str);
+ }
+ }
+}
+
+// mosquitto message handler
+void on_message(__attribute__((unused)) struct mosquitto *mosq,
+ __attribute__((unused)) void *userdata, const struct mosquitto_message *msg) {
+
+ // null-terminate the payload
+ char payload[msg->payloadlen + 1];
+ memcpy(payload, msg->payload, msg->payloadlen);
+ payload[msg->payloadlen] = 0;
+
+ debug(1, "[MQTT]: received Message on topic %s: %s\n", msg->topic, payload);
+
+ // All recognized commands
+ char *commands[] = {"command", "beginff", "beginrew", "mutetoggle", "nextitem",
+ "previtem", "pause", "playpause", "play", "stop",
+ "playresume", "shuffle_songs", "volumedown", "volumeup", NULL};
+
+ int it = 0;
+
+ // send command if it's a valid one
+ while (commands[it] != NULL) {
+ if ((size_t)msg->payloadlen >= strlen(commands[it]) &&
+ strncmp(msg->payload, commands[it], strlen(commands[it])) == 0) {
+ debug(1, "[MQTT]: DACP Command: %s\n", commands[it]);
+ send_simple_dacp_command(commands[it]);
+ break;
+ }
+ it++;
+ }
+}
+
+void on_disconnect(__attribute__((unused)) struct mosquitto *mosq,
+ __attribute__((unused)) void *userdata, __attribute__((unused)) int rc) {
+ connected = 0;
+ debug(1, "[MQTT]: disconnected");
+}
+
+void on_connect(struct mosquitto *mosq, __attribute__((unused)) void *userdata,
+ __attribute__((unused)) int rc) {
+ connected = 1;
+ debug(1, "[MQTT]: connected");
+
+ // subscribe if requested
+ if (config.mqtt_enable_remote) {
+ char remotetopic[strlen(config.mqtt_topic) + 8];
+ snprintf(remotetopic, strlen(config.mqtt_topic) + 8, "%s/remote", config.mqtt_topic);
+ mosquitto_subscribe(mosq, NULL, remotetopic, 0);
+ }
+}
+
+// helper function to publish under a topic and automatically append the main topic
+void mqtt_publish(char *topic, char *data, uint32_t length) {
+ char fulltopic[strlen(config.mqtt_topic) + strlen(topic) + 3];
+ snprintf(fulltopic, strlen(config.mqtt_topic) + strlen(topic) + 2, "%s/%s", config.mqtt_topic,
+ topic);
+ debug(1, "[MQTT]: publishing under %s", fulltopic);
+
+ int rc;
+ if ((rc = mosquitto_publish(global_mosq, NULL, fulltopic, length, data, 0, 0)) !=
+ MOSQ_ERR_SUCCESS) {
+ switch (rc) {
+ case MOSQ_ERR_NO_CONN:
+ debug(1, "[MQTT]: Publish failed: not connected to broker");
+ break;
+ default:
+ debug(1, "[MQTT]: Publish failed: unknown error");
+ break;
+ }
+ }
+}
+
+// handler for incoming metadata
+void mqtt_process_metadata(uint32_t type, uint32_t code, char *data, uint32_t length) {
+ if (global_mosq == NULL || connected != 1) {
+ debug(3, "[MQTT]: Client not connected, skipping metadata handling");
+ return;
+ }
+ if (config.mqtt_publish_raw) {
+ uint32_t val;
+ char topic[] = "____/____";
+
+ val = htonl(type);
+ memcpy(topic, &val, 4);
+ val = htonl(code);
+ memcpy(topic + 5, &val, 4);
+ mqtt_publish(topic, data, length);
+ }
+ if (config.mqtt_publish_parsed) {
+ if (type == 'core') {
+ switch (code) {
+ case 'asar':
+ mqtt_publish("artist", data, length);
+ break;
+ case 'asal':
+ mqtt_publish("album", data, length);
+ break;
+ case 'minm':
+ mqtt_publish("title", data, length);
+ break;
+ case 'asgn':
+ mqtt_publish("genre", data, length);
+ break;
+ case 'asfm':
+ mqtt_publish("format", data, length);
+ break;
+ }
+ } else if (type == 'ssnc') {
+ switch (code) {
+ case 'asal':
+ mqtt_publish("songalbum", data, length);
+ break;
+ case 'pvol':
+ mqtt_publish("volume", data, length);
+ break;
+ case 'clip':
+ mqtt_publish("client_ip", data, length);
+ break;
+ case 'abeg':
+ mqtt_publish("active_start", data, length);
+ break;
+ case 'aend':
+ mqtt_publish("active_end", data, length);
+ break;
+ case 'pbeg':
+ mqtt_publish("play_start", data, length);
+ break;
+ case 'pend':
+ mqtt_publish("play_end", data, length);
+ break;
+ case 'pfls':
+ mqtt_publish("play_flush", data, length);
+ break;
+ case 'prsm':
+ mqtt_publish("play_resume", data, length);
+ break;
+ case 'PICT':
+ if (config.mqtt_publish_cover) {
+ mqtt_publish("cover", data, length);
+ }
+ break;
+ }
+ }
+ }
+
+ return;
+}
+
+int initialise_mqtt() {
+ debug(1, "Initialising MQTT");
+ if (config.mqtt_hostname == NULL) {
+ debug(1, "[MQTT]: Not initialized, as the hostname is not set");
+ return 0;
+ }
+ int keepalive = 60;
+ mosquitto_lib_init();
+ if (!(global_mosq = mosquitto_new(config.service_name, true, NULL))) {
+ die("[MQTT]: FATAL: Could not create mosquitto object! %d\n", global_mosq);
+ }
+
+ if (config.mqtt_cafile != NULL || config.mqtt_capath != NULL || config.mqtt_certfile != NULL ||
+ config.mqtt_keyfile != NULL) {
+ if (mosquitto_tls_set(global_mosq, config.mqtt_cafile, config.mqtt_capath, config.mqtt_certfile,
+ config.mqtt_keyfile, NULL) != MOSQ_ERR_SUCCESS) {
+ die("[MQTT]: TLS Setup failed");
+ }
+ }
+
+ if (config.mqtt_username != NULL || config.mqtt_password != NULL) {
+ if (mosquitto_username_pw_set(global_mosq, config.mqtt_username, config.mqtt_password) !=
+ MOSQ_ERR_SUCCESS) {
+ die("[MQTT]: Username/Password set failed");
+ }
+ }
+ mosquitto_log_callback_set(global_mosq, _cb_log);
+
+ if (config.mqtt_enable_remote) {
+ mosquitto_message_callback_set(global_mosq, on_message);
+ }
+
+ mosquitto_disconnect_callback_set(global_mosq, on_disconnect);
+ mosquitto_connect_callback_set(global_mosq, on_connect);
+ if (mosquitto_connect(global_mosq, config.mqtt_hostname, config.mqtt_port, keepalive)) {
+ inform("[MQTT]: Could not establish a mqtt connection");
+ }
+ if (mosquitto_loop_start(global_mosq) != MOSQ_ERR_SUCCESS) {
+ inform("[MQTT]: Could start MQTT Main loop");
+ }
+
+ return 0;
+}