diff --git a/Makefile b/Makefile
index eb8ad586da99dd7927d883877244dfec4345ffab..d76fbd0a8dfef612756c64a02e38547c9f3bc9b7 100644
--- a/Makefile
+++ b/Makefile
@@ -3,8 +3,6 @@ ARCHIVE=osdd-$(VERSION).tar.gz
 
 CFLAGS=-g3 -Wall -W -Wno-parentheses -Wstrict-prototypes -Wmissing-prototypes -Wundef -Wredundant-decls -std=gnu99 -Wformat-truncation=0
 
-#-fsanitize=address 
-
 all: osdd osdc osd-batt osd-alsa
 
 osdd: osdd.o util.o osdd-set.o
@@ -13,7 +11,10 @@ osd-batt: osd-batt.o util.o client.o
 osd-alsa: osd-alsa.o util.o client.o
 
 # OSDD MODULES
-osdd: display.o osdd-file.o
+osdd: display.o
+osdd: osdd-file.o osdd-to-string.o
+osdd: osdd-mqtt.o osdd-to-string.o -lpaho-mqtt3a -lpaho-mqtt3c
+
 
 osdd: LDLIBS+=$(shell pkg-config --libs xft) -lXext -lX11 
 #LDLIBS += -fsanitize=address
@@ -24,7 +25,7 @@ osd-batt: LDLIBS+=-lX11
 osd-alsa.o: CFLAGS+=$(shell pkg-config --cflags alsa)
 osd-alsa: LDLIBS+=$(shell pkg-config --libs alsa) -lX11
 
-display.o osdd-file.o: CFLAGS+=$(shell pkg-config --cflags xft)
+display.o osdd-to-string.o: CFLAGS+=$(shell pkg-config --cflags xft)
 
 clean:
 	rm -f *~ *.o TAGS core osdd osdc osd-batt osd-alsa
diff --git a/osdd-file.c b/osdd-file.c
index a6ca2b1d6d41d8afd8d15889b458f2fd186b8cbb..b3bcef1c37c0339468b7dc2c67f98750512124b0 100644
--- a/osdd-file.c
+++ b/osdd-file.c
@@ -6,99 +6,14 @@
 
 #include "osdd-file.h"
 #include "util.h"
-#include <stdio.h>
-#include <string.h>
-#include <time.h>
-#include <getopt.h>
-#include <stdlib.h>
-
-#include <X11/Xlib.h>
-#include <X11/Xlib.h>
-#include <X11/Xatom.h>
-#include <X11/extensions/shape.h>
-#include <X11/extensions/render.h>
-#include <X11/Xft/Xft.h>
+#include "osdd-to-string.h"
 
 #include <stdio.h>
-#include <stdlib.h>
-#include <stdbool.h>
 #include <string.h>
-#include <time.h>
-#include <X11/Xlib.h>
-#include <X11/Xatom.h>
-#include <X11/extensions/shape.h>
-#include <X11/extensions/render.h>
-#include <X11/Xft/Xft.h>
 #include <getopt.h>
+#include <stdlib.h>
 
 
-static void replace_no_text(char * in)
-{
-	for(;*in;in++)
-	{
-		if(' '== *in) continue;
-		if('a'<= *in&& *in<='z') continue;
-		if('A'<= *in&& *in<='Z') continue;
-		if('0'<= *in&& *in<='9') continue;
-		//if(128 <= (unsigned char)*in) continue;
-		*in='?';
-	}
-}
-
-static
-char * expand_backslash(char * in)
-{
-	char * out = xmalloc(strlen(in)+1);
-	int i=0;
-	for(;*in;i++,in++)
-	{
-		if(in[0]=='\\')
-		{
-			switch(in[1])
-			{
-				case 'n':
-					out[i]='\n';
-					break;
-				case 't':
-					out[i]='\t';
-					break;
-				case '0':
-					out[i]='\0';
-					break;
-				default:
-					out[i]='?';
-					break;
-			}
-			if(in[1]) in++;
-		}
-		else
-			out[i]=*in;
-	}
-	out[i]=0;
-	return out;
-}
-
-static void
-colorToRGB(char * name, int *r, int *g, int *b)
-{
-
-	Display *d = XOpenDisplay(NULL);
-	int s = XDefaultScreen(d);
-  Visual *v = XDefaultVisual(d,s);
-  Colormap cmap = DefaultColormap(d,s);
-
-   XftColor color;
-	if (XftColorAllocName(d, v, cmap, name, &color))
-	{
-				typeof(color.color) c = color.color;
-				*r = c.red;
-				*g = c.green;
-				*b = c.blue;
-	}
-	else *r=*g=*b=-1;
-  XftColorFree(d, v, cmap, &color);
-	XCloseDisplay(d);
-}
 
 // *************************  STATE STRUCT **********
 
@@ -106,101 +21,15 @@ struct file_state
 {
 	char * file_name;
 	char * open_mode;
-	bool clear;
-	char * line_separator;
-	char * msq_separator;
-	char * time_separator;
-
-	//tmp usage:
-	XftColor fg_color;
-	enum {
-		FILE_COLOR_NO,
-		FILE_COLOR_RGB_DEC,
-		FILE_COLOR_RGB_HEX,
-		FILE_COLOR_RGB_HASHTAG_HEX,
-		FILE_COLOR_TERM_RGB,
-		FILE_COLOR_ORIGINAL,
-	} color_mode;
-	enum {
-		FILE_TIME_NO,
-		FILE_TIME_UNIX,
-		FILE_TIME_ISO,
-	} time_mode;
-	bool replace_no_text;
+	struct osd_to_string_state to_string;
 };
 
 // *************************  MAIN FUNCTIONS *******
 
 static
-void file_show(struct file_state *state, struct osd_line * lines, int num_lines)
+void file_write_to_file(struct file_state *state, VECTOR(char) out)
 {
-	VECTOR(char) out = NULL;
-	switch(state->time_mode)
-	{
-		case FILE_TIME_NO:
-			break;
-		case FILE_TIME_UNIX:
-			vprint(&out,"%lld%s",(long long)time(0),state->time_separator);
-			break;
-		case FILE_TIME_ISO:
-			{
-				time_t t = time(NULL);
-				struct tm tm = *localtime(&t);
-				vprint(&out,"%04d-%02d-%02d %02d:%02d:%02d%s", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec,state->time_separator);
-			}
-			break;
-	}
-	for(int i=0;i<num_lines;i++)
-	{
-		struct osd_line * line = lines+i;
-		//if(!(line->log & 1)) continue;
-		if(line->type!=OSD_TYPE_TEXT) continue;
-		int r,g,b;
-		VECTOR(char) text=0;
-		VRESERVE(text,strlen(line->u.text)+1);
-		char color[30];
-		color[0]=0;
-		char * end_color = "";
-
-		vprint(&text,"%s",line->u.text);
-		if(state->replace_no_text) replace_no_text(text);
-		switch(state->color_mode)
-		{
-			case FILE_COLOR_RGB_DEC:
-			case FILE_COLOR_RGB_HEX:
-			case FILE_COLOR_RGB_HASHTAG_HEX:
-			case FILE_COLOR_TERM_RGB:
-				colorToRGB(line->fg_color,&r,&g,&b);
-				break;
-			default:
-				break;
-		}
-		switch(state->color_mode)
-		{
-			case FILE_COLOR_RGB_DEC:
-				sprintf(color,"%d %d %d ",r/256,g/256,b/256);
-				break;
-			case FILE_COLOR_RGB_HEX:
-				sprintf(color,"%02x%02x%02x ",r/256,g/256,b/256);
-				break;
-			case FILE_COLOR_RGB_HASHTAG_HEX:
-				sprintf(color,"#%02x%02x%02x ",r/256,g/256,b/256);
-				break;
-			case FILE_COLOR_TERM_RGB:
-				sprintf(color,"\e[38;2;%d;%d;%dm",r/256,g/256,b/256);
-				end_color = "\e[0m";
-				break;
-			case FILE_COLOR_ORIGINAL:
-				sprintf(color,"%s ",line->fg_color);
-				break;
-			case FILE_COLOR_NO:
-				break;
-		}
-		vprint(&out,"%s%s%s%s", color, text, end_color,state->line_separator);
-		VFREE(text);
-
-	}
-	vprint(&out,"%s",state->msq_separator);
+	if(!out) return;
 	FILE * f = fopen(state->file_name,state->open_mode);
 	if(f)
 	{
@@ -210,11 +39,16 @@ void file_show(struct file_state *state, struct osd_line * lines, int num_lines)
 	VFREE(out);
 }
 
+static
+void file_show(struct file_state *state, struct osd_line * lines, int num_lines)
+{
+	file_write_to_file(state,osd_to_string(&state->to_string, lines, num_lines));
+}
+
 static
 void file_clear(struct file_state *state)
 {
-	if(state->clear)
-		file_show(state,0,0);
+	file_write_to_file(state,osd_to_string_end_msg(&state->to_string));
 }
 
 // *************************  NEW ******************
@@ -225,12 +59,7 @@ void file_new_help(FILE * f)
 	fprintf(f,"\
 Module FILE help:\n\
 -a, --append \t\tAppend to file (do not overwrite)\n\
--c, --color=<{}>\tSet how to print color of messages {no, rgbDEC, rgbHEX, rgbHEXHASHTAG, term, original}\n\
--l, --line-separator=<s>Separato of each line.\n\
--m, --msq-separator=<s>\tSeparato of each message.\n\
--r, --replace\t\tReplace not alphabet and number in output to '?'\n\
--t, --time=<{}>\t\tSet how to print actual time {no, unix, iso}\n\
--T, --time-separator=<s>Separator of time and message text\n\
+" OSDD_TO_STRING_HELP "\
 \n\
 ");
 }
@@ -241,15 +70,10 @@ struct osd_abstract file_new(int argc, char ** argv, Display * nope)
 	(void)nope;
 	argc++;argv--;
 	struct osd_abstract r;
-	static const char short_opts[] = "+ac:l:m:rt:T:";
+	static const char short_opts[] = "+a" OSDD_TO_STRING_SHORTOP;
 	static const struct option long_opts[] = {
 		{ "append",		no_argument,	NULL,	'a' },
-		{ "color",		required_argument,	NULL,	'c' },
-		{ "line-separator",		required_argument,	NULL,	'l' },
-		{ "msq-separator",		required_argument,	NULL,	'm' },
-		{ "replace",		no_argument,	NULL,	'r' },
-		{ "time",		required_argument,	NULL,	't' },
-		{ "time-separator",		required_argument,	NULL,	'T' },
+		OSDD_TO_STRING_LONGOP
 		{ NULL,		0,			NULL,	0   },
 	};
 	memset(&r,0,sizeof(r));
@@ -257,58 +81,18 @@ struct osd_abstract file_new(int argc, char ** argv, Display * nope)
 	memset(state,0,sizeof(state)[0]);
 
 	state->open_mode = "w";
-	state->line_separator = expand_backslash("\\n");
-	state->time_separator = expand_backslash("\\n");
-	state->msq_separator = expand_backslash("");
-	state->clear=0;
+	osd_to_string_state_init(&state->to_string);
 
 	int opt;
 	optind = 0;
 	while ((opt = getopt_long(argc, argv, short_opts, long_opts, NULL)) >= 0)
 	{
+		if(!osd_to_string_parse_arg(&state->to_string,opt))
 		switch (opt)
 		{
 			case 'a':
 				state->open_mode = "a";
 				break;
-			case 'c':
-				if(!strcmp(optarg,"no")) state->color_mode = FILE_COLOR_NO; else
-				if(!strcmp(optarg,"rgbDEC")) state->color_mode = FILE_COLOR_RGB_DEC; else
-				if(!strcmp(optarg,"rgbHEX")) state->color_mode = FILE_COLOR_RGB_HEX; else
-				if(!strcmp(optarg,"rgbHEXHASHTAG")) state->color_mode = FILE_COLOR_RGB_HASHTAG_HEX; else
-				if(!strcmp(optarg,"term")) state->color_mode = FILE_COLOR_TERM_RGB; else
-				if(!strcmp(optarg,"original")) state->color_mode = FILE_COLOR_ORIGINAL; else
-				{
-					fprintf(stderr,"Color mode %s not exist\n\n",optarg);
-					file_new_help(stderr);
-					exit(0);
-				}
-				break;
-			case 'l':
-				free(state->line_separator);
-				state->line_separator = expand_backslash(optarg);
-				break;
-			case 'm':
-				free(state->msq_separator);
-				state->msq_separator = expand_backslash(optarg);
-				break;
-			case 'r':
-				state->replace_no_text = 1;
-				break;
-			case 't':
-				if(!strcmp(optarg,"no")) state->time_mode = FILE_TIME_NO; else
-				if(!strcmp(optarg,"unix")) state->time_mode = FILE_TIME_UNIX; else
-				if(!strcmp(optarg,"iso")) state->time_mode = FILE_TIME_ISO; else
-				{
-					fprintf(stderr,"Time mode %s not exist\n\n",optarg);
-					file_new_help(stderr);
-					exit(0);
-				}
-				break;
-			case 'T':
-				free(state->time_separator);
-				state->time_separator = expand_backslash(optarg);
-				break;
 			default:
 				fprintf(stderr,"Option %c not exist\n\n",opt);
 				file_new_help(stderr);
diff --git a/osdd-mqtt.c b/osdd-mqtt.c
new file mode 100644
index 0000000000000000000000000000000000000000..86a40162d14fc0494f4e331c74fbe7d3296a4541
--- /dev/null
+++ b/osdd-mqtt.c
@@ -0,0 +1,258 @@
+/*
+ *	On-screen Display
+ *
+ *	(c) 2021 Jiri Kalvoda <jirikalvoda@kam.mff.cuni.cz>
+ */
+
+#include "osdd-mqtt.h"
+#include "util.h"
+#include "osdd-to-string.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <getopt.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include <MQTTClient.h>
+
+
+// *************************  STATE STRUCT **********
+
+struct mqtt_state
+{
+	char * mqtt_addres;
+	char * mqtt_user;
+	char * mqtt_passwd;
+	char * mqtt_topic;
+	struct osd_to_string_state to_string;
+	int pipe_fd;
+	bool mqtt_retained;
+	int mqtt_qos;
+};
+
+struct mqtt_fork_state
+{
+	int fd;
+	bool connected;
+	MQTTClient client;
+	struct mqtt_state * state;
+};
+
+// *************************  FORK *****************
+
+static
+void mqtt_fork_connect(struct mqtt_fork_state * fork_state)
+{
+	//fprintf(stderr,"MQTT CONNECT\n");
+	int rc;
+	for(int i=0;i<2;i++)
+	{
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
+		MQTTClient_connectOptions conn_opts = MQTTClient_connectOptions_initializer;
+		conn_opts.httpProxy = 0;
+#pragma GCC diagnostic pop
+
+		static char mqtt_con_name[300] = "";
+		if(!mqtt_con_name[0]) sprintf(mqtt_con_name,"led-include-%d-%d",(1),(2));
+		MQTTClient_create(&fork_state->client, fork_state->state->mqtt_addres, mqtt_con_name,
+				MQTTCLIENT_PERSISTENCE_NONE, NULL);
+
+		conn_opts.keepAliveInterval = 600;
+		conn_opts.cleansession = 0;
+		conn_opts.username = fork_state->state->mqtt_user;
+		conn_opts.password = fork_state->state->mqtt_passwd;
+
+		if ((rc = MQTTClient_connect(fork_state->client , &conn_opts)) == MQTTCLIENT_SUCCESS)
+		{
+			//fprintf(stderr,"MQTT CONNECT OK\n");
+			fork_state->connected = 1;
+			return;
+		}
+		MQTTClient_destroy(&fork_state->client);
+		fprintf(stderr,"MQTT CONNECT FAILD\n");
+	}
+	fork_state->connected = 0;
+	//printf("Failed to connect MQTT, return code %d\n", rc);
+	//throw std::runtime_error("MQTT Connect error");
+}
+
+int TIMEOUT = 10000;
+static
+void mqtt_fork_sent(struct mqtt_fork_state * fork_state, char * data)
+{
+	int rc;
+	for(int i=0;;i++)
+	{
+		if(!fork_state->connected) mqtt_fork_connect(fork_state);
+		if(fork_state->connected)
+			for(int i=0;i<2;i++)
+			{
+				MQTTClient_message pubmsg = MQTTClient_message_initializer;
+				MQTTClient_deliveryToken token;
+				pubmsg.payload = (void *)data;
+				pubmsg.payloadlen = strlen(data);
+				pubmsg.qos = fork_state->state->mqtt_qos;
+				pubmsg.retained = fork_state->state->mqtt_retained;
+				//fprintf(stderr,"RETAINED %d QOS %d\n", pubmsg.retained, pubmsg.qos);
+				MQTTClient_publishMessage(fork_state->client, fork_state->state->mqtt_topic, &pubmsg, &token);
+				rc = MQTTClient_waitForCompletion(fork_state->client, token, TIMEOUT);
+				if(rc == MQTTCLIENT_SUCCESS) return;
+			}
+		if(i==1)
+			return;
+		MQTTClient_destroy(&fork_state->client);
+		mqtt_fork_connect(fork_state);
+	}
+}
+
+static
+void NONRET mqtt_fork_main(struct mqtt_state * state, int fd)
+{
+	struct mqtt_fork_state fork_state;
+	fork_state.fd = fd;
+	fork_state.connected = 0;
+	fork_state.state = state;
+
+	while(1)
+	{
+		int size;
+		read(fd,&size,sizeof(int));
+		char * in = xmalloc(size);
+		read(fd,in,size);
+		//fprintf(stderr,"MQTT |%s|\n",in);
+		mqtt_fork_sent(&fork_state,in);
+	}
+}
+
+// *************************  MAIN FUNCTIONS *******
+
+static
+void mqtt_write_to_mqtt(struct mqtt_state *state, VECTOR(char) out)
+{
+	if(!out) return;
+	int size = VSIZE(out);
+	char * buf = xmalloc(size+sizeof(int));
+	memcpy(buf + sizeof(int), out, size);
+	*(int *) buf = size;
+	write(state->pipe_fd,buf,size+sizeof(int));
+	free(buf);
+	VFREE(out);
+}
+
+static
+void mqtt_show(struct mqtt_state *state, struct osd_line * lines, int num_lines)
+{
+	mqtt_write_to_mqtt(state,osd_to_string(&state->to_string, lines, num_lines));
+}
+
+static
+void mqtt_clear(struct mqtt_state *state)
+{
+	mqtt_write_to_mqtt(state,osd_to_string_end_msg(&state->to_string));
+}
+
+// *************************  NEW ******************
+
+static
+void mqtt_new_help(FILE * f)
+{
+	fprintf(f,"\
+Module MQTT help:\n\
+-R, --retained \t\tSending messages will be retained\n\
+-q, --qos=<{}> \t\tSet QOS of sending messages. {0,1,2}\n\
+" OSDD_TO_STRING_HELP "\
+\n\
+");
+}
+
+static 
+struct osd_abstract mqtt_new(int argc, char ** argv, Display * nope)
+{
+	(void)nope;
+	argc++;argv--;
+	struct osd_abstract r;
+	static const char short_opts[] = "+Rq:" OSDD_TO_STRING_SHORTOP;
+	static const struct option long_opts[] = {
+		{ "retained",		no_argument,	NULL,	'R' },
+		{ "qos",		required_argument,	NULL,	'q' },
+		OSDD_TO_STRING_LONGOP
+		{ NULL,		0,			NULL,	0   },
+	};
+	memset(&r,0,sizeof(r));
+	struct mqtt_state *state = xmalloc(sizeof(*state));
+	memset(state,0,sizeof(state)[0]);
+	state->mqtt_qos = 1;
+	state->mqtt_retained = 0;
+
+	osd_to_string_state_init(&state->to_string);
+
+	int opt;
+	optind = 0;
+	while ((opt = getopt_long(argc, argv, short_opts, long_opts, NULL)) >= 0)
+	{
+		if(!osd_to_string_parse_arg(&state->to_string,opt))
+		switch (opt)
+		{
+			case 'R':
+				state->mqtt_retained = 1;
+				break;
+			case 'q':
+				state->mqtt_qos = atoi(optarg);
+				break;
+			default:
+				fprintf(stderr,"Option %c not exist\n\n",opt);
+				mqtt_new_help(stderr);
+				exit(0);
+		}
+	}
+	int ind = optind;
+	if(ind + 4 > argc)
+	{
+		fprintf(stderr,"Missing positional argument\n\n");
+		mqtt_new_help(stderr);
+		exit(0);
+	}
+	state->mqtt_addres = argv[ind++];
+	state->mqtt_user = argv[ind++];
+	state->mqtt_passwd = argv[ind++];
+	state->mqtt_topic = argv[ind++];
+	if(ind != argc)
+	{
+		fprintf(stderr,"Too many arguments\n\n");
+		mqtt_new_help(stderr);
+		exit(0);
+
+	}
+
+	r.context = state;
+	void (*show)(struct mqtt_state*, struct osd_line*, int) = mqtt_show;
+	r.show = (void (*)(void*, struct osd_line*, int)) show;
+	void (*clear)(struct mqtt_state*) = mqtt_clear;
+	r.clear = (void (*)(void*)) clear;
+	int fd[2];
+	pipe(fd);
+	if(fork() == 0)
+	{
+		close (fd[1]);
+		//write (fd[WRITE], phrase, strlen ( phrase) +1);
+		mqtt_fork_main(state , fd[0]);
+	}
+	close(fd[0]);
+	fcntl(fd[1], F_SETFL, O_NONBLOCK);
+	state->pipe_fd = fd[1];
+	return r;
+}
+
+// *************************  CREATOR ****************************
+struct osd_creator_abstract mqtt_creator_new(void)
+{
+  struct osd_creator_abstract r;
+  memset(&r, 0, sizeof(r));
+  r.name = "MQTT";
+  r.help = mqtt_new_help;
+  r.new.add_one_osd = mqtt_new;
+  return r;
+}
diff --git a/osdd-mqtt.h b/osdd-mqtt.h
new file mode 100644
index 0000000000000000000000000000000000000000..bb9bded944896483b2fefa636a910770ff8ce7bd
--- /dev/null
+++ b/osdd-mqtt.h
@@ -0,0 +1,9 @@
+/*
+ *	On-screen Display
+ *
+ *	(c) 2020--2021 Jiri Kalvoda <jirikalvoda@kam.mff.cuni.cz>
+ */
+
+#include "osdd-set.h"
+
+struct osd_creator_abstract mqtt_creator_new(void);
diff --git a/osdd-run b/osdd-run
index 5a90f472b2cf920de6ef242b5fcfafc47b540885..37c054c290d724c8e87c26baf8619c0ac1645a67 100755
--- a/osdd-run
+++ b/osdd-run
@@ -6,5 +6,16 @@ pkill -f not2osd
 not2osd &
 tmp=$(mktemp)
 echo "Xft.render: False" > $tmp
-XENVIRONMENT=$tmp ./osdd -D DISPLAY_BY_OUTPUTS default nolog [ -n a%d ] FILE default log loglog [ -a -l' ' -m'\n' -T' ' -tiso -cterm ~/.osdd_log  ] FILE default log loglast [ -l'\n' -tunix -T'\n' -crgbHEX -r ~/.osdd_last ] | bash
-
+XENVIRONMENT=$tmp \
+	./osdd -D \
+	DISPLAY_BY_OUTPUTS default nolog [ -n a%d ] \
+	FILE default log loglog [ -a -l' ' -m'\n' -T' ' -tiso -cterm ~/.osdd_log  ] \
+	FILE default log loglast [ -l'\n' -tunix -T'\n' -crgbHEX -r ~/.osdd_last ] \
+	MQTT default mqtt led [ -e'\0' -E -crgbDEC -tunix -R -q0 \
+		$(cat ~/.secret/mqtt/rpi0-all.addres) \
+		$(cat ~/.secret/mqtt/rpi0-led-include.user) \
+		$(cat ~/.secret/mqtt/rpi0-led-include.passwd) \
+		osd/arch ] \
+	| bash
+rm $tmp
+pkill -f not2osd
diff --git a/osdd-to-string.c b/osdd-to-string.c
new file mode 100644
index 0000000000000000000000000000000000000000..c4f6876f135a832fb747eccb43ae0203e27f6c39
--- /dev/null
+++ b/osdd-to-string.c
@@ -0,0 +1,238 @@
+/*
+ *	On-screen Display
+ *
+ *	(c) 2020--2021 Jiri Kalvoda <jirikalvoda@kam.mff.cuni.cz>
+ */
+
+#include "osdd-to-string.h"
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <getopt.h>
+#include <time.h>
+
+#include <X11/Xlib.h>
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+#include <X11/extensions/shape.h>
+#include <X11/extensions/render.h>
+#include <X11/Xft/Xft.h>
+
+static
+char * expand_backslash(char * in)
+{
+	char * out = xmalloc(strlen(in)+1);
+	int i=0;
+	for(;*in;i++,in++)
+	{
+		if(in[0]=='\\')
+		{
+			switch(in[1])
+			{
+				case 'n':
+					out[i]='\n';
+					break;
+				case 't':
+					out[i]='\t';
+					break;
+				case '0':
+					out[i]='\0';
+					break;
+				default:
+					out[i]='?';
+					break;
+			}
+			if(in[1]) in++;
+		}
+		else
+			out[i]=*in;
+	}
+	out[i]=0;
+	return out;
+}
+
+static void replace_no_text(char * in)
+{
+	for(;*in;in++)
+	{
+		if(' '== *in) continue;
+		if('a'<= *in&& *in<='z') continue;
+		if('A'<= *in&& *in<='Z') continue;
+		if('0'<= *in&& *in<='9') continue;
+		//if(128 <= (unsigned char)*in) continue;
+		*in='?';
+	}
+}
+
+
+static void
+colorToRGB(char * name, int *r, int *g, int *b)
+{
+
+	Display *d = XOpenDisplay(NULL);
+	int s = XDefaultScreen(d);
+  Visual *v = XDefaultVisual(d,s);
+  Colormap cmap = DefaultColormap(d,s);
+
+   XftColor color;
+	if (XftColorAllocName(d, v, cmap, name, &color))
+	{
+				typeof(color.color) c = color.color;
+				*r = c.red;
+				*g = c.green;
+				*b = c.blue;
+	}
+	else *r=*g=*b=-1;
+  XftColorFree(d, v, cmap, &color);
+	XCloseDisplay(d);
+}
+
+
+bool osd_to_string_parse_arg(struct osd_to_string_state *state, char arg)
+{
+	switch(arg)
+	{
+		case 'c':
+			if(!strcmp(optarg,"no")) state->color_mode = FILE_COLOR_NO; else
+			if(!strcmp(optarg,"rgbDEC")) state->color_mode = FILE_COLOR_RGB_DEC; else
+			if(!strcmp(optarg,"rgbHEX")) state->color_mode = FILE_COLOR_RGB_HEX; else
+			if(!strcmp(optarg,"rgbHEXHASHTAG")) state->color_mode = FILE_COLOR_RGB_HASHTAG_HEX; else
+			if(!strcmp(optarg,"term")) state->color_mode = FILE_COLOR_TERM_RGB; else
+			if(!strcmp(optarg,"original")) state->color_mode = FILE_COLOR_ORIGINAL; else
+			{
+				fprintf(stderr,"Color mode %s not exist\n\n",optarg);
+				exit(0);
+			}
+			break;
+		case 'l':
+			free(state->line_separator);
+			state->line_separator = expand_backslash(optarg);
+			break;
+		case 'm':
+			free(state->msq_separator);
+			state->msq_separator = expand_backslash(optarg);
+			break;
+		case 'r':
+			state->replace_no_text = 1;
+			break;
+		case 't':
+			if(!strcmp(optarg,"no")) state->time_mode = FILE_TIME_NO; else
+				if(!strcmp(optarg,"unix")) state->time_mode = FILE_TIME_UNIX; else
+					if(!strcmp(optarg,"iso")) state->time_mode = FILE_TIME_ISO; else
+					{
+						fprintf(stderr,"Time mode %s not exist\n\n",optarg);
+						exit(0);
+					}
+			break;
+		case 'T':
+			free(state->time_separator);
+			state->time_separator = expand_backslash(optarg);
+			break;
+		case 'e':
+			if(state->end_of_mes_by) free(state->end_of_mes_by);
+			state->end_of_mes_by = expand_backslash(optarg);
+			break;
+		case 'E':
+			state->end_of_mes_time = 1;
+			break;
+		default:
+			return 0;
+	}
+	return 1;
+}
+
+static
+void osd_to_string_time(struct osd_to_string_state *state, VECTOR(char) * out)
+{
+	switch(state->time_mode)
+	{
+		case FILE_TIME_NO:
+			break;
+		case FILE_TIME_UNIX:
+			vprint(out,"%lld%s",(long long)time(0),state->time_separator);
+			break;
+		case FILE_TIME_ISO:
+			{
+				time_t t = time(NULL);
+				struct tm tm = *localtime(&t);
+				vprint(out,"%04d-%02d-%02d %02d:%02d:%02d%s", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec,state->time_separator);
+			}
+			break;
+	}
+}
+
+VECTOR(char) osd_to_string(struct osd_to_string_state *state, struct osd_line * lines, int num_lines)
+{
+	VECTOR(char) out = NULL;
+	osd_to_string_time(state, &out);
+	for(int i=0;i<num_lines;i++)
+	{
+		struct osd_line * line = lines+i;
+		//if(!(line->log & 1)) continue;
+		if(line->type!=OSD_TYPE_TEXT) continue;
+		int r,g,b;
+		VECTOR(char) text=0;
+		VRESERVE(text,strlen(line->u.text)+1);
+		char color[30];
+		color[0]=0;
+		char * end_color = "";
+
+		vprint(&text,"%s",line->u.text);
+		if(state->replace_no_text) replace_no_text(text);
+		switch(state->color_mode)
+		{
+			case FILE_COLOR_RGB_DEC:
+			case FILE_COLOR_RGB_HEX:
+			case FILE_COLOR_RGB_HASHTAG_HEX:
+			case FILE_COLOR_TERM_RGB:
+				colorToRGB(line->fg_color,&r,&g,&b);
+				break;
+			default:
+				break;
+		}
+		switch(state->color_mode)
+		{
+			case FILE_COLOR_RGB_DEC:
+				sprintf(color,"%d %d %d ",r/256,g/256,b/256);
+				break;
+			case FILE_COLOR_RGB_HEX:
+				sprintf(color,"%02x%02x%02x ",r/256,g/256,b/256);
+				break;
+			case FILE_COLOR_RGB_HASHTAG_HEX:
+				sprintf(color,"#%02x%02x%02x ",r/256,g/256,b/256);
+				break;
+			case FILE_COLOR_TERM_RGB:
+				sprintf(color,"\e[38;2;%d;%d;%dm",r/256,g/256,b/256);
+				end_color = "\e[0m";
+				break;
+			case FILE_COLOR_ORIGINAL:
+				sprintf(color,"%s ",line->fg_color);
+				break;
+			case FILE_COLOR_NO:
+				break;
+		}
+		vprint(&out,"%s%s%s%s", color, text, end_color,state->line_separator);
+		VFREE(text);
+
+	}
+	vprint(&out,"%s",state->msq_separator);
+	return out;
+}
+
+VECTOR(char) osd_to_string_end_msg(struct osd_to_string_state *state)
+{
+	if(!state->end_of_mes_by) return 0;
+	VECTOR(char) out=0;
+	if(state->end_of_mes_time) 
+		osd_to_string_time(state, &out);
+	vprint(&out,"%s",state->end_of_mes_by);
+	return out;
+}
+
+void osd_to_string_state_init(struct osd_to_string_state * state)
+{
+	state->line_separator = expand_backslash("\\n");
+	state->time_separator = expand_backslash("\\n");
+	state->msq_separator = expand_backslash("");
+	state->end_of_mes_by = 0;
+}
diff --git a/osdd-to-string.h b/osdd-to-string.h
new file mode 100644
index 0000000000000000000000000000000000000000..459712d7b55814e4b33b5933564d324259eeb36a
--- /dev/null
+++ b/osdd-to-string.h
@@ -0,0 +1,58 @@
+/*
+ *	On-screen Display
+ *
+ *	(c) 2020--2021 Jiri Kalvoda <jirikalvoda@kam.mff.cuni.cz>
+ */
+
+#include "osdd-set.h"
+#include "util.h"
+
+#define OSDD_TO_STRING_LONGOP \
+		{ "color",		required_argument,	NULL,	'c' },\
+		{ "line-separator",		required_argument,	NULL,	'l' },\
+		{ "msq-separator",		required_argument,	NULL,	'm' },\
+		{ "replace",		no_argument,	NULL,	'r' },\
+		{ "time",		required_argument,	NULL,	't' },\
+		{ "time-separator",		required_argument,	NULL,	'T' },\
+		{ "end",		required_argument,	NULL,	'e' },\
+
+#define OSDD_TO_STRING_SHORTOP \
+	"c:l:m:rt:T:e:E"
+
+#define OSDD_TO_STRING_HELP "\
+-c, --color=<{}>\tSet how to print color of messages {no, rgbDEC, rgbHEX, rgbHEXHASHTAG, term, original}\n\
+-l, --line-separator=<s>Separato of each line.\n\
+-m, --msq-separator=<s>\tSeparato of each message.\n\
+-r, --replace\t\tReplace not alphabet and number in output to '?'\n\
+-t, --time=<{}>\t\tSet how to print actual time {no, unix, iso}\n\
+-T, --time-separator=<s>Separator of time and message text\n\
+-E, --end-time\t\tWrite actual time to end messages (format from -t -T)\n\
+"
+
+struct osd_to_string_state
+{
+	char * line_separator;
+	char * msq_separator;
+	char * time_separator;
+	enum {
+		FILE_COLOR_NO,
+		FILE_COLOR_RGB_DEC,
+		FILE_COLOR_RGB_HEX,
+		FILE_COLOR_RGB_HASHTAG_HEX,
+		FILE_COLOR_TERM_RGB,
+		FILE_COLOR_ORIGINAL,
+	} color_mode;
+	enum {
+		FILE_TIME_NO,
+		FILE_TIME_UNIX,
+		FILE_TIME_ISO,
+	} time_mode;
+	bool replace_no_text;
+	char * end_of_mes_by;
+	bool end_of_mes_time;
+};
+
+VECTOR(char) osd_to_string(struct osd_to_string_state *state, struct osd_line * lines, int num_lines);
+VECTOR(char) osd_to_string_end_msg(struct osd_to_string_state *state);
+bool osd_to_string_parse_arg(struct osd_to_string_state *state, char arg);
+void osd_to_string_state_init(struct osd_to_string_state * state);
diff --git a/osdd.c b/osdd.c
index 705c330b8085d8e763ac2f12737bb4d8360401de..dd25e5dc8126de873d5685c64396fcf048fdaf39 100644
--- a/osdd.c
+++ b/osdd.c
@@ -18,8 +18,10 @@
 #define DEBUG
 #include "util.h"
 #include "osdd-set.h"
+
 #include "display.h"
 #include "osdd-file.h"
+#include "osdd-mqtt.h"
 
 
 static struct osd_set osd;
@@ -431,6 +433,7 @@ main(int argc, char **argv)
   VPB(creator,display_creator_new());
   VPB(creator,display_by_outputs_creator_new());
   VPB(creator,file_creator_new());
+  VPB(creator,mqtt_creator_new());
 
 
   setlocale(LC_CTYPE, "");