15 #include "metadata_hub.h"
17 #include <mosquitto.h>
19 // this holds the mosquitto client
20 struct mosquitto
*global_mosq
= NULL
;
25 void _cb_log(__attribute__((unused
)) struct mosquitto
*mosq
, __attribute__((unused
)) void *userdata
,
26 int level
, const char *str
) {
37 case MOSQ_LOG_WARNING
:
41 die("MQTT: Error: %s\n", str
);
46 // mosquitto message handler
47 void on_message(__attribute__((unused
)) struct mosquitto
*mosq
,
48 __attribute__((unused
)) void *userdata
, const struct mosquitto_message
*msg
) {
50 // null-terminate the payload
51 char payload
[msg
->payloadlen
+ 1];
52 memcpy(payload
, msg
->payload
, msg
->payloadlen
);
53 payload
[msg
->payloadlen
] = 0;
55 debug(2, "[MQTT]: received Message on topic %s: %s\n", msg
->topic
, payload
);
57 // All recognized commands
58 char *commands
[] = {"command", "beginff", "beginrew", "mutetoggle", "nextitem",
59 "previtem", "pause", "playpause", "play", "stop",
60 "playresume", "shuffle_songs", "volumedown", "volumeup", NULL
};
64 // send command if it's a valid one
65 while (commands
[it
] != NULL
) {
66 if ((size_t)msg
->payloadlen
>= strlen(commands
[it
]) &&
67 strncmp(msg
->payload
, commands
[it
], strlen(commands
[it
])) == 0) {
68 debug(2, "[MQTT]: DACP Command: %s\n", commands
[it
]);
69 send_simple_dacp_command(commands
[it
]);
76 void on_disconnect(__attribute__((unused
)) struct mosquitto
*mosq
,
77 __attribute__((unused
)) void *userdata
, __attribute__((unused
)) int rc
) {
79 debug(2, "[MQTT]: disconnected");
82 void on_connect(struct mosquitto
*mosq
, __attribute__((unused
)) void *userdata
,
83 __attribute__((unused
)) int rc
) {
85 debug(2, "[MQTT]: connected");
87 // subscribe if requested
88 if (config
.mqtt_enable_remote
) {
89 char remotetopic
[strlen(config
.mqtt_topic
) + 8];
90 snprintf(remotetopic
, strlen(config
.mqtt_topic
) + 8, "%s/remote", config
.mqtt_topic
);
91 mosquitto_subscribe(mosq
, NULL
, remotetopic
, 0);
95 // helper function to publish under a topic and automatically append the main topic
96 void mqtt_publish(char *topic
, char *data_in
, uint32_t length_in
) {
98 uint32_t length
= length_in
;
100 if ((length
== 0) && (config
.mqtt_empty_payload_substitute
!= NULL
)) {
101 length
= strlen(config
.mqtt_empty_payload_substitute
);
102 data
= config
.mqtt_empty_payload_substitute
;
105 char fulltopic
[strlen(config
.mqtt_topic
) + strlen(topic
) + 3];
106 snprintf(fulltopic
, strlen(config
.mqtt_topic
) + strlen(topic
) + 2, "%s/%s", config
.mqtt_topic
,
108 debug(2, "[MQTT]: publishing under %s", fulltopic
);
111 if ((rc
= mosquitto_publish(global_mosq
, NULL
, fulltopic
, length
, data
, 0, 0)) !=
114 case MOSQ_ERR_NO_CONN
:
115 debug(1, "[MQTT]: Publish failed: not connected to broker");
118 debug(1, "[MQTT]: Publish failed: unknown error");
124 // handler for incoming metadata
125 void mqtt_process_metadata(uint32_t type
, uint32_t code
, char *data
, uint32_t length
) {
126 if (global_mosq
== NULL
|| connected
!= 1) {
127 debug(3, "[MQTT]: Client not connected, skipping metadata handling");
130 if (config
.mqtt_publish_raw
) {
132 char topic
[] = "____/____";
135 memcpy(topic
, &val
, 4);
137 memcpy(topic
+ 5, &val
, 4);
138 mqtt_publish(topic
, data
, length
);
140 if (config
.mqtt_publish_parsed
) {
141 if (type
== 'core') {
144 char trackidstring
[32];
148 mqtt_publish("artist", data
, length
);
151 mqtt_publish("album", data
, length
);
154 mqtt_publish("format", data
, length
);
157 mqtt_publish("genre", data
, length
);
160 mqtt_publish("title", data
, length
);
163 trackid
= *(uint64_t *)(data
);
164 r
= snprintf(trackidstring
, sizeof(trackidstring
), "%" PRIX64
"", trackid
);
165 mqtt_publish("track_id", trackidstring
, r
);
167 } else if (type
== 'ssnc') {
170 mqtt_publish("active_start", data
, length
);
173 mqtt_publish("active_remote_id", data
, length
);
176 mqtt_publish("active_end", data
, length
);
179 mqtt_publish("songalbum", data
, length
);
182 mqtt_publish("songdatakind", data
,
183 length
); // 0 seem to be a timed item, 1 an untimed stream
186 mqtt_publish("client_ip", data
, length
);
189 mqtt_publish("client_device_id", data
, length
);
192 mqtt_publish("client_mac_address", data
, length
);
195 mqtt_publish("client_model", data
, length
);
198 mqtt_publish("dacp_id", data
, length
);
201 mqtt_publish("frame_position_and_time", data
, length
);
204 mqtt_publish("first_frame_position_and_time", data
, length
);
207 mqtt_publish("output_format", data
, length
);
210 mqtt_publish("output_frame_rate", data
, length
);
213 mqtt_publish("play_start", data
, length
);
216 mqtt_publish("play_end", data
, length
);
219 mqtt_publish("play_flush", data
, length
);
222 if (config
.mqtt_publish_cover
) {
223 mqtt_publish("cover", data
, length
);
227 mqtt_publish("play_resume", data
, length
);
230 mqtt_publish("volume", data
, length
);
233 mqtt_publish("client_name", data
, length
);
236 mqtt_publish("stream_type", data
, length
);
239 mqtt_publish("server_ip", data
, length
);
242 mqtt_publish("service_name", data
, length
);
251 int initialise_mqtt() {
252 debug(1, "Initialising MQTT");
253 if (config
.mqtt_hostname
== NULL
) {
254 debug(1, "[MQTT]: Not initialized, as the hostname is not set");
258 mosquitto_lib_init();
259 if (!(global_mosq
= mosquitto_new(config
.service_name
, true, NULL
))) {
260 die("[MQTT]: FATAL: Could not create mosquitto object! %d\n", global_mosq
);
263 if (config
.mqtt_cafile
!= NULL
|| config
.mqtt_capath
!= NULL
|| config
.mqtt_certfile
!= NULL
||
264 config
.mqtt_keyfile
!= NULL
) {
265 if (mosquitto_tls_set(global_mosq
, config
.mqtt_cafile
, config
.mqtt_capath
, config
.mqtt_certfile
,
266 config
.mqtt_keyfile
, NULL
) != MOSQ_ERR_SUCCESS
) {
267 die("[MQTT]: TLS Setup failed");
271 if (config
.mqtt_username
!= NULL
|| config
.mqtt_password
!= NULL
) {
272 if (mosquitto_username_pw_set(global_mosq
, config
.mqtt_username
, config
.mqtt_password
) !=
274 die("[MQTT]: Username/Password set failed");
277 mosquitto_log_callback_set(global_mosq
, _cb_log
);
279 if (config
.mqtt_enable_remote
) {
280 mosquitto_message_callback_set(global_mosq
, on_message
);
283 mosquitto_disconnect_callback_set(global_mosq
, on_disconnect
);
284 mosquitto_connect_callback_set(global_mosq
, on_connect
);
285 if (mosquitto_connect(global_mosq
, config
.mqtt_hostname
, config
.mqtt_port
, keepalive
)) {
286 inform("[MQTT]: Could not establish a mqtt connection");
288 if (mosquitto_loop_start(global_mosq
) != MOSQ_ERR_SUCCESS
) {
289 inform("[MQTT]: Could start MQTT Main loop");