From c84dab21e41110afb3bdef40dad9668454ec4adc Mon Sep 17 00:00:00 2001 From: Sergey Lyubka Date: Sat, 18 Jun 2022 04:25:05 +0100 Subject: [PATCH] Refactor UART example --- examples/uart-bridge/net.c | 93 ++- examples/uart-bridge/packed_fs.c | 813 ++++++++++++++++-------- examples/uart-bridge/web_root/main.js | 112 +++- examples/uart-bridge/web_root/style.css | 33 +- 4 files changed, 747 insertions(+), 304 deletions(-) diff --git a/examples/uart-bridge/net.c b/examples/uart-bridge/net.c index 5f2f0dc7..9ca9b18f 100644 --- a/examples/uart-bridge/net.c +++ b/examples/uart-bridge/net.c @@ -3,13 +3,25 @@ #include "mongoose.h" -static const char *s_tcp_url = "tcp://0.0.0.0:4001"; -static const char *s_ws_url = "ws://0.0.0.0:4002"; -static struct mg_connection *s_tcp_conn; -static struct mg_connection *s_ws_conn; -static int s_rx = 4; -static int s_tx = 5; -static int s_baud = 115200; +#define DEFAULT_TCP "tcp://0.0.0.0:4001" +#define DEFAULT_WEBSOCKET "ws://0.0.0.0:4002" +#define DEFAULT_MQTT "mqtt://broker.hivemq.com:1883?tx=b/tx&rx=b/rx" + +struct endpoint { + char *url; + bool enable; + struct mg_connection *c; +}; + +struct state { + struct endpoint tcp, websocket, mqtt; + int tx, rx, baud; +} s_state = {.tcp = {.enable = true}, + .websocket = {.enable = true}, + .mqtt = {.enable = true}, + .tx = 5, + .rx = 4, + .baud = 115200}; void uart_init(int tx, int rx, int baud); int uart_read(char *buf, size_t len); @@ -46,7 +58,7 @@ static void ws_fn(struct mg_connection *c, int ev, void *evd, void *fnd) { uart_write(wm->data.ptr, wm->data.len); // Send to UART c->recv.len = 0; // Discard received data } else if (ev == MG_EV_CLOSE) { - if (c->is_listening) s_ws_conn = NULL; + if (c->is_listening) s_state.websocket.c = NULL; } (void) fnd; } @@ -60,7 +72,35 @@ static void tcp_fn(struct mg_connection *c, int ev, void *evd, void *fnd) { uart_write(c->recv.buf, c->recv.len); // Send to UART c->recv.len = 0; // Discard received data } else if (ev == MG_EV_CLOSE) { - if (c->is_listening) s_tcp_conn = NULL; + if (c->is_listening) s_state.tcp.c = NULL; + } + (void) fnd, (void) evd; +} + +// Extract RX topic name from the MQTT address +static struct mg_str mqtt_rx_topic(void) { + char *url = s_state.mqtt.url, *p = strrchr(url, ','); + return mg_str(p ? p : "b/rx"); +} + +// Extract TX topic name from the MQTT address +static struct mg_str mqtt_tx_topic(void) { + char *url = s_state.mqtt.url, *p1 = strchr(url, ','), *p2 = strrchr(url, ','); + return mg_str_n(p1 && p2 ? p1 : "b/tx", p1 && p2 ? p2 - p1 + 1 : 4); +} + +// Event handler for MQTT connection +static void mq_fn(struct mg_connection *c, int ev, void *evd, void *fnd) { + if (ev == MG_EV_OPEN) { + // c->is_hexdumping = 1; + c->label[0] = 'M'; + } else if (ev == MG_EV_MQTT_OPEN) { + mg_mqtt_sub(c, mqtt_rx_topic(), 1); // Subscribe to RX topic + } else if (ev == MG_EV_MQTT_MSG) { + struct mg_mqtt_message *mm = evd; // MQTT message + uart_write(mm->data.ptr, mm->data.len); // Send to UART + } else if (ev == MG_EV_CLOSE) { + s_state.mqtt.c = NULL; } (void) fnd, (void) evd; } @@ -69,8 +109,16 @@ static void tcp_fn(struct mg_connection *c, int ev, void *evd, void *fnd) { static void timer_fn(void *param) { // Start listeners if they're stopped for any reason struct mg_mgr *mgr = (struct mg_mgr *) param; - if (s_tcp_conn == NULL) s_tcp_conn = mg_listen(mgr, s_tcp_url, tcp_fn, 0); - if (s_ws_conn == NULL) s_ws_conn = mg_http_listen(mgr, s_ws_url, ws_fn, 0); + if (s_state.tcp.c == NULL && s_state.tcp.enable) { + s_state.tcp.c = mg_listen(mgr, s_state.tcp.url, tcp_fn, 0); + } + if (s_state.websocket.c == NULL && s_state.websocket.enable) { + s_state.websocket.c = mg_http_listen(mgr, s_state.websocket.url, ws_fn, 0); + } + if (s_state.mqtt.c == NULL && s_state.mqtt.enable) { + struct mg_mqtt_opts opts = {.clean = true, .will_qos = 1}; + s_state.mqtt.c = mg_mqtt_connect(mgr, s_state.mqtt.url, &opts, mq_fn, 0); + } // Read UART char buf[512]; @@ -80,6 +128,8 @@ static void timer_fn(void *param) { for (struct mg_connection *c = mgr->conns; c != NULL; c = c->next) { if (c->label[0] == 'W') mg_ws_send(c, buf, len, WEBSOCKET_OP_TEXT); if (c->label[0] == 'T') mg_send(c, buf, len); + if (c->label[0] == 'M') + mg_mqtt_pub(c, mqtt_tx_topic(), mg_str_n(buf, len), 1, false); } } } @@ -88,20 +138,29 @@ static void timer_fn(void *param) { void uart_bridge_fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) { if (ev == MG_EV_OPEN && c->is_listening) { + s_state.tcp.url = strdup(DEFAULT_TCP); + s_state.websocket.url = strdup(DEFAULT_WEBSOCKET); + s_state.mqtt.url = strdup(DEFAULT_MQTT); mg_timer_add(c->mgr, 20, MG_TIMER_REPEAT, timer_fn, c->mgr); - uart_init(s_tx, s_rx, s_baud); + uart_init(s_state.tx, s_state.rx, s_state.baud); } else if (ev == MG_EV_HTTP_MSG) { struct mg_http_message *hm = (struct mg_http_message *) ev_data; if (mg_http_match_uri(hm, "/api/hi")) { mg_http_reply(c, 200, "", "hi\n"); // Testing endpoint } else if (mg_http_match_uri(hm, "/api/config/get")) { - mg_http_reply( - c, 200, "Content-Type: application/json\r\n", - "{\"tcp\":\"%s\",\"ws\":\"%s\",\"rx\":%d,\"tx\":%d,\"baud\":%d}\n", - s_tcp_url, s_ws_url, s_rx, s_tx, s_baud); + mg_http_reply(c, 200, "Content-Type: application/json\r\n", + "{%Q:{%Q:%Q,%Q:%s},%Q:{%Q:%Q,%Q:%s},%Q:{%Q:%Q,%Q:%s}," + "%Q:%d,%Q:%d,%Q:%d}\n", + "tcp", "url", s_state.tcp.url, "enable", + s_state.tcp.enable ? "true" : "false", "ws", "url", + s_state.websocket.url, "enable", + s_state.websocket.enable ? "true" : "false", "mqtt", "url", + s_state.mqtt.url, "enable", + s_state.mqtt.enable ? "true" : "false", "rx", s_state.rx, + "tx", s_state.tx, "baud", s_state.baud); } else { struct mg_http_serve_opts opts = {0}; -#if 1 +#if 0 opts.root_dir = "/web_root"; opts.fs = &mg_fs_packed; #else diff --git a/examples/uart-bridge/packed_fs.c b/examples/uart-bridge/packed_fs.c index f88a758c..c9663a93 100644 --- a/examples/uart-bridge/packed_fs.c +++ b/examples/uart-bridge/packed_fs.c @@ -61,192 +61,424 @@ static const unsigned char v2[] = { 32, 32, 99, 111, 110, 115, 116, 32, 91, 99, 102, 103, // const [cfg 44, 32, 115, 101, 116, 67, 102, 103, 93, 32, 61, 32, // , setCfg] = 117, 115, 101, 83, 116, 97, 116, 101, 40, 123, 116, 99, // useState({tc - 112, 58, 32, 39, 39, 44, 32, 119, 115, 58, 32, 39, // p: '', ws: ' - 39, 125, 41, 59, 10, 32, 32, 99, 111, 110, 115, 116, // '});. const - 32, 91, 109, 101, 115, 115, 97, 103, 101, 115, 44, 32, // [messages, - 115, 101, 116, 77, 101, 115, 115, 97, 103, 101, 115, 93, // setMessages] + 112, 58, 32, 123, 125, 44, 32, 119, 115, 58, 32, 123, // p: {}, ws: { + 125, 44, 32, 109, 113, 116, 116, 58, 32, 123, 125, 125, // }, mqtt: {}} + 41, 59, 10, 32, 32, 99, 111, 110, 115, 116, 32, 91, // );. const [ + 109, 101, 115, 115, 97, 103, 101, 115, 44, 32, 115, 101, // messages, se + 116, 77, 101, 115, 115, 97, 103, 101, 115, 93, 32, 61, // tMessages] = + 32, 117, 115, 101, 83, 116, 97, 116, 101, 40, 91, 93, // useState([] + 41, 59, 10, 32, 32, 99, 111, 110, 115, 116, 32, 91, // );. const [ + 99, 111, 110, 110, 101, 99, 116, 101, 100, 44, 32, 115, // connected, s + 101, 116, 67, 111, 110, 110, 101, 99, 116, 101, 100, 93, // etConnected] 32, 61, 32, 117, 115, 101, 83, 116, 97, 116, 101, 40, // = useState( - 91, 93, 41, 59, 10, 32, 32, 99, 111, 110, 115, 116, // []);. const - 32, 91, 116, 120, 116, 44, 32, 115, 101, 116, 84, 120, // [txt, setTx - 116, 93, 32, 61, 32, 117, 115, 101, 83, 116, 97, 116, // t] = useStat - 101, 40, 39, 39, 41, 59, 10, 32, 32, 99, 111, 110, // e('');. con - 115, 116, 32, 91, 99, 111, 110, 110, 101, 99, 116, 101, // st [connecte - 100, 44, 32, 115, 101, 116, 67, 111, 110, 110, 101, 99, // d, setConnec - 116, 101, 100, 93, 32, 61, 32, 117, 115, 101, 83, 116, // ted] = useSt - 97, 116, 101, 40, 102, 97, 108, 115, 101, 41, 59, 10, // ate(false);. - 32, 32, 99, 111, 110, 115, 116, 32, 91, 119, 115, 44, // const [ws, - 32, 115, 101, 116, 87, 115, 93, 32, 61, 32, 117, 115, // setWs] = us - 101, 83, 116, 97, 116, 101, 40, 110, 117, 108, 108, 41, // eState(null) - 59, 10, 10, 32, 32, 99, 111, 110, 115, 116, 32, 116, // ;.. const t - 99, 112, 95, 112, 111, 114, 116, 32, 61, 32, 99, 102, // cp_port = cf - 103, 46, 116, 99, 112, 46, 115, 112, 108, 105, 116, 40, // g.tcp.split( - 39, 58, 39, 41, 91, 50, 93, 32, 124, 124, 32, 52, // ':')[2] || 4 - 48, 48, 49, 59, 10, 32, 32, 99, 111, 110, 115, 116, // 001;. const - 32, 119, 115, 95, 112, 111, 114, 116, 32, 61, 32, 99, // ws_port = c - 102, 103, 46, 119, 115, 46, 115, 112, 108, 105, 116, 40, // fg.ws.split( - 39, 58, 39, 41, 91, 50, 93, 32, 124, 124, 32, 52, // ':')[2] || 4 - 48, 48, 50, 59, 10, 10, 32, 32, 99, 111, 110, 115, // 002;.. cons - 116, 32, 114, 101, 102, 114, 101, 115, 104, 32, 61, 32, // t refresh = - 40, 41, 32, 61, 62, 10, 32, 32, 32, 32, 32, 32, // () =>. - 102, 101, 116, 99, 104, 40, 39, 47, 97, 112, 105, 47, // fetch('/api/ - 99, 111, 110, 102, 105, 103, 47, 103, 101, 116, 39, 41, // config/get') - 46, 116, 104, 101, 110, 40, 114, 32, 61, 62, 32, 114, // .then(r => r - 46, 106, 115, 111, 110, 40, 41, 41, 46, 116, 104, 101, // .json()).the - 110, 40, 114, 32, 61, 62, 32, 115, 101, 116, 67, 102, // n(r => setCf - 103, 40, 114, 41, 41, 59, 10, 10, 32, 32, 99, 111, // g(r));.. co - 110, 115, 116, 32, 119, 97, 116, 99, 104, 87, 101, 98, // nst watchWeb - 115, 111, 99, 107, 101, 116, 32, 61, 32, 102, 117, 110, // socket = fun - 99, 116, 105, 111, 110, 40, 41, 32, 123, 10, 32, 32, // ction() {. - 32, 32, 47, 47, 32, 67, 111, 110, 110, 101, 99, 116, // // Connect - 32, 116, 111, 32, 119, 101, 98, 115, 111, 99, 107, 101, // to websocke - 114, 32, 112, 111, 114, 116, 44, 32, 116, 111, 32, 105, // r port, to i - 109, 112, 108, 101, 109, 101, 110, 116, 32, 87, 83, 32, // mplement WS - 99, 111, 110, 115, 111, 108, 101, 10, 32, 32, 32, 32, // console. - 118, 97, 114, 32, 108, 32, 61, 32, 119, 105, 110, 100, // var l = wind - 111, 119, 46, 108, 111, 99, 97, 116, 105, 111, 110, 44, // ow.location, - 32, 112, 114, 111, 116, 111, 32, 61, 32, 108, 46, 112, // proto = l.p - 114, 111, 116, 111, 99, 111, 108, 46, 114, 101, 112, 108, // rotocol.repl - 97, 99, 101, 40, 39, 104, 116, 116, 112, 39, 44, 32, // ace('http', - 39, 119, 115, 39, 41, 59, 10, 32, 32, 32, 32, 118, // 'ws');. v + 102, 97, 108, 115, 101, 41, 59, 10, 32, 32, 99, 111, // false);. co + 110, 115, 116, 32, 91, 116, 120, 116, 44, 32, 115, 101, // nst [txt, se + 116, 84, 120, 116, 93, 32, 61, 32, 117, 115, 101, 83, // tTxt] = useS + 116, 97, 116, 101, 40, 39, 39, 41, 59, 10, 32, 32, // tate('');. + 99, 111, 110, 115, 116, 32, 91, 119, 115, 44, 32, 115, // const [ws, s + 101, 116, 87, 115, 93, 32, 61, 32, 117, 115, 101, 83, // etWs] = useS + 116, 97, 116, 101, 40, 110, 117, 108, 108, 41, 59, 10, // tate(null);. + 32, 32, 99, 111, 110, 115, 116, 32, 91, 114, 120, 44, // const [rx, + 32, 115, 101, 116, 82, 120, 93, 32, 61, 32, 117, 115, // setRx] = us + 101, 83, 116, 97, 116, 101, 40, 39, 39, 41, 59, 10, // eState('');. + 32, 32, 99, 111, 110, 115, 116, 32, 91, 116, 120, 44, // const [tx, + 32, 115, 101, 116, 84, 120, 93, 32, 61, 32, 117, 115, // setTx] = us + 101, 83, 116, 97, 116, 101, 40, 39, 39, 41, 59, 10, // eState('');. + 32, 32, 99, 111, 110, 115, 116, 32, 91, 98, 97, 117, // const [bau + 100, 44, 32, 115, 101, 116, 66, 97, 117, 100, 93, 32, // d, setBaud] + 61, 32, 117, 115, 101, 83, 116, 97, 116, 101, 40, 39, // = useState(' + 39, 41, 59, 10, 32, 32, 99, 111, 110, 115, 116, 32, // ');. const + 91, 116, 99, 112, 112, 111, 114, 116, 44, 32, 115, 101, // [tcpport, se + 116, 84, 99, 112, 112, 111, 114, 116, 93, 32, 61, 32, // tTcpport] = + 117, 115, 101, 83, 116, 97, 116, 101, 40, 52, 48, 48, // useState(400 + 49, 41, 59, 10, 32, 32, 99, 111, 110, 115, 116, 32, // 1);. const + 91, 119, 115, 112, 111, 114, 116, 44, 32, 115, 101, 116, // [wsport, set + 87, 115, 112, 111, 114, 116, 93, 32, 61, 32, 117, 115, // Wsport] = us + 101, 83, 116, 97, 116, 101, 40, 52, 48, 48, 50, 41, // eState(4002) + 59, 10, 32, 32, 99, 111, 110, 115, 116, 32, 91, 109, // ;. const [m + 113, 116, 116, 44, 32, 115, 101, 116, 77, 113, 116, 116, // qtt, setMqtt + 93, 32, 61, 32, 117, 115, 101, 83, 116, 97, 116, 101, // ] = useState + 40, 39, 39, 41, 59, 10, 10, 32, 32, 47, 47, 32, // ('');.. // + 99, 111, 110, 115, 116, 32, 116, 99, 112, 95, 112, 111, // const tcp_po + 114, 116, 32, 61, 32, 99, 102, 103, 46, 116, 99, 112, // rt = cfg.tcp + 46, 115, 112, 108, 105, 116, 40, 39, 58, 39, 41, 91, // .split(':')[ + 50, 93, 32, 124, 124, 32, 52, 48, 48, 49, 59, 10, // 2] || 4001;. + 32, 32, 47, 47, 32, 99, 111, 110, 115, 116, 32, 119, // // const w + 115, 95, 112, 111, 114, 116, 32, 61, 32, 99, 102, 103, // s_port = cfg + 46, 119, 115, 46, 115, 112, 108, 105, 116, 40, 39, 58, // .ws.split(': + 39, 41, 91, 50, 93, 32, 124, 124, 32, 52, 48, 48, // ')[2] || 400 + 50, 59, 10, 10, 32, 32, 99, 111, 110, 115, 116, 32, // 2;.. const + 114, 101, 102, 114, 101, 115, 104, 32, 61, 32, 40, 41, // refresh = () + 32, 61, 62, 32, 102, 101, 116, 99, 104, 40, 39, 47, // => fetch('/ + 97, 112, 105, 47, 99, 111, 110, 102, 105, 103, 47, 103, // api/config/g + 101, 116, 39, 41, 46, 116, 104, 101, 110, 40, 114, 32, // et').then(r + 61, 62, 32, 114, 46, 106, 115, 111, 110, 40, 41, 41, // => r.json()) + 46, 116, 104, 101, 110, 40, 114, 32, 61, 62, 32, 123, // .then(r => { + 10, 32, 32, 32, 32, 115, 101, 116, 84, 120, 40, 114, // . setTx(r + 46, 116, 120, 41, 44, 32, 115, 101, 116, 82, 120, 40, // .tx), setRx( + 114, 46, 114, 120, 41, 44, 32, 115, 101, 116, 66, 97, // r.rx), setBa + 117, 100, 40, 114, 46, 98, 97, 117, 100, 41, 44, 32, // ud(r.baud), + 115, 101, 116, 67, 102, 103, 40, 114, 41, 59, 10, 32, // setCfg(r);. + 32, 32, 32, 115, 101, 116, 84, 99, 112, 112, 111, 114, // setTcppor + 116, 40, 114, 46, 116, 99, 112, 46, 117, 114, 108, 46, // t(r.tcp.url. + 115, 112, 108, 105, 116, 40, 39, 58, 39, 41, 91, 50, // split(':')[2 + 93, 32, 124, 124, 32, 52, 48, 48, 49, 41, 59, 10, // ] || 4001);. + 32, 32, 32, 32, 115, 101, 116, 87, 115, 112, 111, 114, // setWspor + 116, 40, 114, 46, 119, 115, 46, 117, 114, 108, 46, 115, // t(r.ws.url.s + 112, 108, 105, 116, 40, 39, 58, 39, 41, 91, 50, 93, // plit(':')[2] + 32, 124, 124, 32, 52, 48, 48, 50, 41, 59, 10, 32, // || 4002);. + 32, 32, 32, 115, 101, 116, 77, 113, 116, 116, 40, 114, // setMqtt(r + 46, 109, 113, 116, 116, 46, 117, 114, 108, 41, 59, 10, // .mqtt.url);. + 32, 32, 125, 41, 59, 10, 10, 32, 32, 99, 111, 110, // });.. con + 115, 116, 32, 119, 97, 116, 99, 104, 87, 101, 98, 115, // st watchWebs + 111, 99, 107, 101, 116, 32, 61, 32, 102, 117, 110, 99, // ocket = func + 116, 105, 111, 110, 40, 41, 32, 123, 10, 32, 32, 32, // tion() {. + 32, 47, 47, 32, 67, 111, 110, 110, 101, 99, 116, 32, // // Connect + 116, 111, 32, 119, 101, 98, 115, 111, 99, 107, 101, 114, // to websocker + 32, 112, 111, 114, 116, 44, 32, 116, 111, 32, 105, 109, // port, to im + 112, 108, 101, 109, 101, 110, 116, 32, 87, 83, 32, 99, // plement WS c + 111, 110, 115, 111, 108, 101, 10, 32, 32, 32, 32, 118, // onsole. v + 97, 114, 32, 114, 101, 99, 111, 110, 110, 101, 99, 116, // ar reconnect + 32, 61, 32, 102, 117, 110, 99, 116, 105, 111, 110, 40, // = function( + 41, 32, 123, 10, 32, 32, 32, 32, 32, 32, 118, 97, // ) {. va + 114, 32, 112, 111, 114, 116, 59, 10, 32, 32, 32, 32, // r port;. + 32, 32, 115, 101, 116, 87, 115, 112, 111, 114, 116, 40, // setWsport( + 120, 32, 61, 62, 32, 112, 111, 114, 116, 32, 61, 32, // x => port = + 120, 41, 59, 10, 32, 32, 32, 32, 32, 32, 118, 97, // x);. va + 114, 32, 108, 32, 61, 32, 119, 105, 110, 100, 111, 119, // r l = window + 46, 108, 111, 99, 97, 116, 105, 111, 110, 44, 32, 112, // .location, p + 114, 111, 116, 111, 32, 61, 32, 108, 46, 112, 114, 111, // roto = l.pro + 116, 111, 99, 111, 108, 46, 114, 101, 112, 108, 97, 99, // tocol.replac + 101, 40, 39, 104, 116, 116, 112, 39, 44, 32, 39, 119, // e('http', 'w + 115, 39, 41, 59, 10, 32, 32, 32, 32, 32, 32, 118, // s');. v 97, 114, 32, 116, 105, 100, 44, 32, 117, 114, 108, 32, // ar tid, url 61, 32, 96, 36, 123, 112, 114, 111, 116, 111, 125, 47, // = `${proto}/ 47, 36, 123, 108, 46, 104, 111, 115, 116, 110, 97, 109, // /${l.hostnam - 101, 125, 58, 36, 123, 119, 115, 95, 112, 111, 114, 116, // e}:${ws_port - 125, 47, 119, 115, 96, 59, 10, 32, 32, 32, 32, 99, // }/ws`;. c - 111, 110, 115, 111, 108, 101, 46, 108, 111, 103, 40, 117, // onsole.log(u - 114, 108, 41, 59, 10, 32, 32, 32, 32, 118, 97, 114, // rl);. var - 32, 114, 101, 99, 111, 110, 110, 101, 99, 116, 32, 61, // reconnect = - 32, 102, 117, 110, 99, 116, 105, 111, 110, 40, 41, 32, // function() - 123, 10, 32, 32, 32, 32, 32, 32, 118, 97, 114, 32, // {. var - 119, 115, 32, 61, 32, 110, 101, 119, 32, 87, 101, 98, // ws = new Web - 83, 111, 99, 107, 101, 116, 40, 117, 114, 108, 41, 59, // Socket(url); - 10, 32, 32, 32, 32, 32, 32, 119, 115, 46, 111, 110, // . ws.on - 111, 112, 101, 110, 32, 61, 32, 40, 41, 32, 61, 62, // open = () => - 32, 123, 10, 32, 32, 32, 32, 32, 32, 32, 32, 115, // {. s - 101, 116, 67, 111, 110, 110, 101, 99, 116, 101, 100, 40, // etConnected( - 116, 114, 117, 101, 41, 59, 10, 32, 32, 32, 32, 32, // true);. - 32, 32, 32, 115, 101, 116, 87, 115, 40, 119, 115, 41, // setWs(ws) - 59, 10, 32, 32, 32, 32, 32, 32, 125, 59, 10, 32, // ;. };. - 32, 32, 32, 32, 32, 119, 115, 46, 111, 110, 109, 101, // ws.onme + 101, 125, 58, 36, 123, 112, 111, 114, 116, 125, 47, 119, // e}:${port}/w + 115, 96, 59, 10, 32, 32, 32, 32, 32, 32, 47, 47, // s`;. // + 32, 99, 111, 110, 115, 111, 108, 101, 46, 108, 111, 103, // console.log + 40, 117, 114, 108, 41, 59, 10, 32, 32, 32, 32, 32, // (url);. + 32, 118, 97, 114, 32, 119, 115, 32, 61, 32, 110, 101, // var ws = ne + 119, 32, 87, 101, 98, 83, 111, 99, 107, 101, 116, 40, // w WebSocket( + 117, 114, 108, 41, 59, 10, 32, 32, 32, 32, 32, 32, // url);. + 119, 115, 46, 111, 110, 111, 112, 101, 110, 32, 61, 32, // ws.onopen = + 40, 41, 32, 61, 62, 32, 123, 10, 32, 32, 32, 32, // () => {. + 32, 32, 32, 32, 115, 101, 116, 67, 111, 110, 110, 101, // setConne + 99, 116, 101, 100, 40, 116, 114, 117, 101, 41, 59, 10, // cted(true);. + 32, 32, 32, 32, 32, 32, 32, 32, 115, 101, 116, 87, // setW + 115, 40, 119, 115, 41, 59, 10, 32, 32, 32, 32, 32, // s(ws);. + 32, 125, 59, 10, 32, 32, 32, 32, 32, 32, 119, 115, // };. ws + 46, 111, 110, 109, 101, 115, 115, 97, 103, 101, 32, 61, // .onmessage = + 32, 101, 118, 32, 61, 62, 32, 123, 10, 32, 32, 32, // ev => {. + 32, 32, 32, 32, 32, 47, 47, 32, 99, 111, 110, 115, // // cons + 111, 108, 101, 46, 108, 111, 103, 40, 101, 118, 44, 32, // ole.log(ev, + 101, 118, 46, 100, 97, 116, 97, 41, 59, 10, 32, 32, // ev.data);. + 32, 32, 32, 32, 32, 32, 115, 101, 116, 77, 101, 115, // setMes + 115, 97, 103, 101, 115, 40, 120, 32, 61, 62, 32, 120, // sages(x => x + 46, 99, 111, 110, 99, 97, 116, 40, 91, 123, 100, 97, // .concat([{da + 116, 97, 58, 32, 101, 118, 46, 100, 97, 116, 97, 44, // ta: ev.data, + 32, 117, 97, 114, 116, 58, 32, 116, 114, 117, 101, 125, // uart: true} + 93, 41, 41, 59, 10, 32, 32, 32, 32, 32, 32, 125, // ]));. } + 59, 10, 32, 32, 32, 32, 32, 32, 119, 115, 46, 111, // ;. ws.o + 110, 99, 108, 111, 115, 101, 32, 61, 32, 102, 117, 110, // nclose = fun + 99, 116, 105, 111, 110, 40, 41, 32, 123, 10, 32, 32, // ction() {. + 32, 32, 32, 32, 32, 32, 99, 108, 101, 97, 114, 84, // clearT + 105, 109, 101, 111, 117, 116, 40, 116, 105, 100, 41, 59, // imeout(tid); + 10, 32, 32, 32, 32, 32, 32, 32, 32, 116, 105, 100, // . tid + 32, 61, 32, 115, 101, 116, 84, 105, 109, 101, 111, 117, // = setTimeou + 116, 40, 114, 101, 99, 111, 110, 110, 101, 99, 116, 44, // t(reconnect, + 32, 49, 48, 48, 48, 41, 59, 10, 32, 32, 32, 32, // 1000);. + 32, 32, 32, 32, 115, 101, 116, 67, 111, 110, 110, 101, // setConne + 99, 116, 101, 100, 40, 102, 97, 108, 115, 101, 41, 59, // cted(false); + 10, 32, 32, 32, 32, 32, 32, 32, 32, 115, 101, 116, // . set + 87, 115, 40, 110, 117, 108, 108, 41, 59, 10, 32, 32, // Ws(null);. + 32, 32, 32, 32, 125, 59, 10, 32, 32, 32, 32, 125, // };. } + 59, 10, 32, 32, 32, 32, 114, 101, 99, 111, 110, 110, // ;. reconn + 101, 99, 116, 40, 41, 59, 10, 32, 32, 125, 59, 10, // ect();. };. + 10, 32, 32, 117, 115, 101, 69, 102, 102, 101, 99, 116, // . useEffect + 40, 40, 41, 32, 61, 62, 32, 123, 10, 32, 32, 32, // (() => {. + 32, 114, 101, 102, 114, 101, 115, 104, 40, 41, 59, 10, // refresh();. + 32, 32, 32, 32, 119, 97, 116, 99, 104, 87, 101, 98, // watchWeb + 115, 111, 99, 107, 101, 116, 40, 41, 59, 10, 32, 32, // socket();. + 125, 44, 32, 91, 93, 41, 59, 10, 10, 10, 32, 32, // }, []);... + 99, 111, 110, 115, 116, 32, 115, 101, 110, 100, 109, 101, // const sendme 115, 115, 97, 103, 101, 32, 61, 32, 101, 118, 32, 61, // ssage = ev = - 62, 32, 123, 10, 32, 32, 32, 32, 32, 32, 32, 32, // > {. - 47, 47, 32, 99, 111, 110, 115, 111, 108, 101, 46, 108, // // console.l - 111, 103, 40, 101, 118, 44, 32, 101, 118, 46, 100, 97, // og(ev, ev.da - 116, 97, 41, 59, 10, 32, 32, 32, 32, 32, 32, 32, // ta);. - 32, 115, 101, 116, 77, 101, 115, 115, 97, 103, 101, 115, // setMessages - 40, 120, 32, 61, 62, 32, 120, 46, 99, 111, 110, 99, // (x => x.conc - 97, 116, 40, 91, 123, 100, 97, 116, 97, 58, 32, 101, // at([{data: e - 118, 46, 100, 97, 116, 97, 44, 32, 117, 97, 114, 116, // v.data, uart - 58, 32, 116, 114, 117, 101, 125, 93, 41, 41, 59, 10, // : true}]));. - 32, 32, 32, 32, 32, 32, 125, 59, 10, 32, 32, 32, // };. - 32, 32, 32, 119, 115, 46, 111, 110, 99, 108, 111, 115, // ws.onclos - 101, 32, 61, 32, 102, 117, 110, 99, 116, 105, 111, 110, // e = function - 40, 41, 32, 123, 10, 32, 32, 32, 32, 32, 32, 32, // () {. - 32, 99, 108, 101, 97, 114, 84, 105, 109, 101, 111, 117, // clearTimeou - 116, 40, 116, 105, 100, 41, 59, 10, 32, 32, 32, 32, // t(tid);. - 32, 32, 32, 32, 116, 105, 100, 32, 61, 32, 115, 101, // tid = se - 116, 84, 105, 109, 101, 111, 117, 116, 40, 114, 101, 99, // tTimeout(rec - 111, 110, 110, 101, 99, 116, 44, 32, 49, 48, 48, 48, // onnect, 1000 - 41, 59, 10, 32, 32, 32, 32, 32, 32, 32, 32, 115, // );. s - 101, 116, 67, 111, 110, 110, 101, 99, 116, 101, 100, 40, // etConnected( - 102, 97, 108, 115, 101, 41, 59, 10, 32, 32, 32, 32, // false);. - 32, 32, 32, 32, 115, 101, 116, 87, 115, 40, 110, 117, // setWs(nu - 108, 108, 41, 59, 10, 32, 32, 32, 32, 32, 32, 125, // ll);. } - 59, 10, 32, 32, 32, 32, 125, 59, 10, 32, 32, 32, // ;. };. - 32, 114, 101, 99, 111, 110, 110, 101, 99, 116, 40, 41, // reconnect() - 59, 10, 32, 32, 125, 59, 10, 10, 32, 32, 117, 115, // ;. };.. us - 101, 69, 102, 102, 101, 99, 116, 40, 40, 41, 32, 61, // eEffect(() = - 62, 32, 123, 10, 32, 32, 32, 32, 114, 101, 102, 114, // > {. refr - 101, 115, 104, 40, 41, 59, 10, 32, 32, 32, 32, 119, // esh();. w - 97, 116, 99, 104, 87, 101, 98, 115, 111, 99, 107, 101, // atchWebsocke - 116, 40, 41, 59, 10, 32, 32, 125, 44, 32, 91, 93, // t();. }, [] - 41, 59, 10, 10, 10, 32, 32, 99, 111, 110, 115, 116, // );... const - 32, 115, 101, 110, 100, 109, 101, 115, 115, 97, 103, 101, // sendmessage - 32, 61, 32, 101, 118, 32, 61, 62, 32, 123, 10, 32, // = ev => {. - 32, 32, 32, 115, 101, 116, 77, 101, 115, 115, 97, 103, // setMessag - 101, 115, 40, 120, 32, 61, 62, 32, 120, 46, 99, 111, // es(x => x.co - 110, 99, 97, 116, 40, 91, 123, 100, 97, 116, 97, 58, // ncat([{data: - 32, 116, 120, 116, 32, 43, 32, 39, 92, 110, 39, 44, // txt + '.n', - 32, 117, 97, 114, 116, 58, 32, 102, 97, 108, 115, 101, // uart: false - 125, 93, 41, 41, 59, 10, 32, 32, 32, 32, 105, 102, // }]));. if - 32, 40, 119, 115, 41, 32, 119, 115, 46, 115, 101, 110, // (ws) ws.sen - 100, 40, 116, 120, 116, 32, 43, 32, 39, 92, 110, 39, // d(txt + '.n' - 41, 59, 10, 32, 32, 32, 32, 115, 101, 116, 84, 120, // );. setTx - 116, 40, 39, 39, 41, 59, 10, 32, 32, 125, 59, 10, // t('');. };. - 10, 32, 32, 114, 101, 116, 117, 114, 110, 32, 104, 116, // . return ht - 109, 108, 96, 10, 60, 100, 105, 118, 32, 99, 108, 97, // ml`.
.

UA - 82, 84, 32, 92, 117, 50, 55, 70, 55, 32, 110, 101, // RT .u27F7 ne - 116, 119, 111, 114, 107, 32, 98, 114, 105, 100, 103, 101, // twork bridge - 32, 60, 47, 104, 49, 62, 10, 32, 32, 60, 104, 51, //

.

UART settin - 103, 115, 60, 47, 104, 51, 62, 10, 32, 32, 60, 112, // gs

.

${JSON - 46, 115, 116, 114, 105, 110, 103, 105, 102, 121, 40, 99, // .stringify(c - 102, 103, 44, 32, 110, 117, 108, 108, 44, 32, 50, 41, // fg, null, 2) - 125, 60, 47, 112, 114, 101, 62, 10, 32, 32, 60, 112, // }.

RX pin ${cf - 103, 46, 114, 120, 125, 44, 32, 84, 88, 32, 112, 105, // g.rx}, TX pi - 110, 32, 36, 123, 99, 102, 103, 46, 116, 120, 125, 44, // n ${cfg.tx}, - 32, 98, 97, 117, 100, 32, 36, 123, 99, 102, 103, 46, // baud ${cfg. - 98, 97, 117, 100, 125, 60, 47, 112, 62, 10, 32, 32, // baud}

. - 60, 104, 51, 62, 84, 67, 80, 32, 108, 105, 115, 116, //

TCP list - 101, 110, 101, 114, 58, 32, 112, 111, 114, 116, 32, 36, // ener: port $ - 123, 116, 99, 112, 95, 112, 111, 114, 116, 125, 60, 47, // {tcp_port}.

Use - 32, 110, 101, 116, 99, 97, 116, 32, 116, 111, 32, 99, // netcat to c - 111, 110, 110, 101, 99, 116, 58, 60, 47, 112, 62, 10, // onnect:

. - 32, 32, 60, 100, 105, 118, 32, 99, 108, 97, 115, 115, //
$ nc - 32, 36, 123, 108, 111, 99, 97, 116, 105, 111, 110, 46, // ${location. - 104, 111, 115, 116, 110, 97, 109, 101, 125, 32, 36, 123, // hostname} ${ - 116, 99, 112, 95, 112, 111, 114, 116, 125, 32, 60, 47, // tcp_port} .

W - 101, 98, 115, 111, 99, 107, 101, 116, 32, 108, 105, 115, // ebsocket lis - 116, 101, 110, 101, 114, 58, 32, 112, 111, 114, 116, 32, // tener: port - 36, 123, 119, 115, 95, 112, 111, 114, 116, 125, 60, 47, // ${ws_port}.
U - 65, 82, 84, 32, 99, 111, 110, 115, 111, 108, 101, 46, // ART console. - 32, 83, 116, 97, 116, 117, 115, 58, 32, 36, 123, 99, // Status: ${c - 111, 110, 110, 101, 99, 116, 101, 100, 32, 63, 32, 39, // onnected ? ' - 99, 111, 110, 110, 101, 99, 116, 101, 100, 39, 32, 58, // connected' : - 32, 39, 100, 105, 115, 99, 111, 110, 110, 101, 99, 116, // 'disconnect - 101, 100, 39, 125, 60, 47, 100, 105, 118, 62, 10, 32, // ed'}
. - 32, 60, 112, 114, 101, 32, 115, 116, 121, 108, 101, 61, //
.   
-  32,  36, 123, 109, 101, 115, 115,  97, 103, 101, 115,  46, //  ${messages.
- 109,  97, 112,  40, 109, 101, 115, 115,  97, 103, 101,  32, // map(message 
-  61,  62,  32, 104,  40,  77, 101, 115, 115,  97, 103, 101, // => h(Message
-  44,  32, 123, 109, 101, 115, 115,  97, 103, 101, 125,  41, // , {message})
-  41, 125,  10,  32,  32,  60,  47, 112, 114, 101,  62,  10, // )}.  
. - 32, 32, 60, 100, 105, 118, 32, 115, 116, 121, 108, 101, //
. - 32, 32, 32, 60, 105, 110, 112, 117, 116, 32, 112, 108, // {. setM + 101, 115, 115, 97, 103, 101, 115, 40, 120, 32, 61, 62, // essages(x => + 32, 120, 46, 99, 111, 110, 99, 97, 116, 40, 91, 123, // x.concat([{ + 100, 97, 116, 97, 58, 32, 116, 120, 116, 32, 43, 32, // data: txt + + 39, 92, 110, 39, 44, 32, 117, 97, 114, 116, 58, 32, // '.n', uart: + 102, 97, 108, 115, 101, 125, 93, 41, 41, 59, 10, 32, // false}]));. + 32, 32, 32, 105, 102, 32, 40, 119, 115, 41, 32, 119, // if (ws) w + 115, 46, 115, 101, 110, 100, 40, 116, 120, 116, 32, 43, // s.send(txt + + 32, 39, 92, 110, 39, 41, 59, 10, 32, 32, 32, 32, // '.n');. + 115, 101, 116, 84, 120, 116, 40, 39, 39, 41, 59, 10, // setTxt('');. + 32, 32, 125, 59, 10, 10, 32, 32, 99, 111, 110, 115, // };.. cons + 116, 32, 111, 110, 99, 104, 97, 110, 103, 101, 32, 61, // t onchange = + 32, 101, 118, 32, 61, 62, 32, 102, 97, 108, 115, 101, // ev => false + 59, 10, 10, 32, 32, 114, 101, 116, 117, 114, 110, 32, // ;.. return + 104, 116, 109, 108, 96, 10, 60, 100, 105, 118, 32, 99, // html`.
.

UART .u27F + 55, 32, 110, 101, 116, 119, 111, 114, 107, 32, 98, 114, // 7 network br + 105, 100, 103, 101, 32, 60, 47, 104, 49, 62, 10, 32, // idge

. + 32, 60, 112, 114, 101, 32, 99, 108, 97, 115, 115, 61, //
${J
+  83,  79,  78,  46, 115, 116, 114, 105, 110, 103, 105, 102, // SON.stringif
+ 121,  40,  99, 102, 103,  44,  32, 110, 117, 108, 108,  44, // y(cfg, null,
+  32,  50,  41, 125,  60,  47, 112, 114, 101,  62,  10,  32, //  2)}
. + 32, 60, 100, 105, 118, 32, 99, 108, 97, 115, 115, 61, //
. < + 100, 105, 118, 32, 99, 108, 97, 115, 115, 61, 34, 99, // div class="c + 111, 108, 32, 99, 111, 108, 45, 52, 34, 62, 10, 32, // ol col-4">. + 32, 32, 32, 32, 32, 60, 104, 51, 62, 85, 65, 82, //

UAR + 84, 32, 99, 111, 110, 102, 105, 103, 117, 114, 97, 116, // T configurat + 105, 111, 110, 60, 47, 104, 51, 62, 10, 32, 32, 32, // ion

. + 32, 32, 32, 60, 100, 105, 118, 32, 99, 108, 97, 115, //
. + 32, 32, 32, 32, 32, 32, 60, 108, 97, 98, 101, 108, // . + 32, 32, 32, 32, 32, 32, 32, 60, 105, 110, 112, 117, // set + 84, 120, 40, 101, 118, 46, 116, 97, 114, 103, 101, 116, // Tx(ev.target + 46, 118, 97, 108, 117, 101, 41, 125, 32, 47, 62, 10, // .value)} />. + 32, 32, 32, 32, 32, 32, 60, 47, 100, 105, 118, 62, //
+ 60, 100, 105, 118, 32, 99, 108, 97, 115, 115, 61, 34, //
. + 32, 32, 32, 60, 108, 97, 98, 101, 108, 32, 99, 108, //
. + 60, 108, 97, 98, 101, 108, 32, 99, 108, 97, 115, 115, //
`; - 10, 125, 59, 10, 10, 119, 105, 110, 100, 111, 119, 46, // .};..window. - 111, 110, 108, 111, 97, 100, 32, 61, 32, 40, 41, 32, // onload = () - 61, 62, 32, 114, 101, 110, 100, 101, 114, 40, 104, 40, // => render(h( - 65, 112, 112, 41, 44, 32, 100, 111, 99, 117, 109, 101, // App), docume - 110, 116, 46, 98, 111, 100, 121, 41, 59, 10, 0 // nt.body);. + 61, 62, 32, 115, 101, 116, 66, 97, 117, 100, 40, 101, // => setBaud(e + 118, 46, 116, 97, 114, 103, 101, 116, 46, 118, 97, 108, // v.target.val + 117, 101, 41, 125, 32, 47, 62, 10, 32, 32, 32, 32, // ue)} />. + 32, 32, 60, 47, 100, 105, 118, 62, 10, 32, 32, 32, //
. + 32, 60, 47, 100, 105, 118, 62, 10, 32, 32, 32, 32, //
. + 60, 100, 105, 118, 32, 99, 108, 97, 115, 115, 61, 34, //
. + 32, 32, 32, 32, 32, 32, 60, 104, 51, 62, 78, 101, //

Ne + 116, 119, 111, 114, 107, 32, 99, 111, 110, 102, 105, 103, // twork config + 117, 114, 97, 116, 105, 111, 110, 60, 47, 104, 51, 62, // uration

+ 10, 32, 32, 32, 32, 32, 32, 60, 100, 105, 118, 32, // .
. + 32, 32, 32, 32, 32, 60, 108, 97, 98, 101, 108, 32, // + 10, 32, 32, 32, 32, 32, 32, 32, 32, 60, 105, 110, // . setT + 99, 112, 112, 111, 114, 116, 40, 101, 118, 46, 116, 97, // cpport(ev.ta + 114, 103, 101, 116, 46, 118, 97, 108, 117, 101, 41, 125, // rget.value)} + 32, 47, 62, 10, 32, 32, 32, 32, 32, 32, 32, 32, // />. + 60, 108, 97, 98, 101, 108, 32, 99, 108, 97, 115, 115, // .
. + 60, 108, 97, 98, 101, 108, 32, 99, 108, 97, 115, 115, //
. < + 108, 97, 98, 101, 108, 32, 99, 108, 97, 115, 115, 61, // label class= + 34, 97, 100, 100, 111, 110, 34, 62, 82, 101, 109, 111, // "addon">Remo + 116, 101, 32, 77, 81, 84, 84, 60, 47, 108, 97, 98, // te MQTT. + 60, 105, 110, 112, 117, 116, 32, 115, 116, 121, 108, 101, // setMqtt( + 101, 118, 46, 116, 97, 114, 103, 101, 116, 46, 118, 97, // ev.target.va + 108, 117, 101, 41, 125, 32, 47, 62, 10, 32, 32, 32, // lue)} />. + 32, 32, 32, 32, 32, 60, 108, 97, 98, 101, 108, 32, //
. + 32, 60, 47, 100, 105, 118, 62, 10, 32, 32, 60, 47, //
. ..
. Note: + 116, 111, 32, 99, 111, 110, 110, 101, 99, 116, 32, 111, // to connect o + 118, 101, 114, 32, 77, 81, 84, 84, 44, 32, 10, 32, // ver MQTT, . + 32, 32, 32, 32, 32, 103, 111, 32, 116, 111, 32, 60, // go to < + 97, 32, 104, 114, 101, 102, 61, 34, 104, 116, 116, 112, // a href="http + 58, 47, 47, 119, 119, 119, 46, 104, 105, 118, 101, 109, // ://www.hivem + 113, 46, 99, 111, 109, 47, 100, 101, 109, 111, 115, 47, // q.com/demos/ + 119, 101, 98, 115, 111, 99, 107, 101, 116, 45, 99, 108, // websocket-cl + 105, 101, 110, 116, 47, 34, 62, 10, 32, 32, 32, 32, // ient/">. + 32, 32, 32, 32, 99, 111, 110, 115, 111, 108, 101, 60, // console< + 47, 97, 62, 44, 32, 115, 117, 98, 115, 99, 114, 105, // /a>, subscri + 98, 101, 32, 116, 111, 32, 98, 47, 116, 120, 32, 97, // be to b/tx a + 110, 100, 32, 112, 117, 98, 108, 105, 115, 104, 32, 116, // nd publish t + 111, 32, 98, 47, 114, 120, 60, 98, 114, 47, 62, 10, // o b/rx
. + 32, 32, 32, 32, 78, 111, 116, 101, 58, 32, 116, 111, // Note: to + 32, 99, 111, 110, 110, 101, 99, 116, 32, 111, 118, 101, // connect ove + 114, 32, 84, 67, 80, 44, 32, 117, 115, 101, 32, 110, // r TCP, use n + 101, 116, 99, 97, 116, 32, 117, 116, 105, 108, 105, 116, // etcat utilit + 121, 58, 60, 98, 114, 47, 62, 10, 32, 32, 32, 32, // y:
. + 36, 32, 110, 99, 32, 36, 123, 108, 111, 99, 97, 116, // $ nc ${locat + 105, 111, 110, 46, 104, 111, 115, 116, 110, 97, 109, 101, // ion.hostname + 125, 32, 36, 123, 116, 99, 112, 112, 111, 114, 116, 125, // } ${tcpport} + 10, 32, 32, 60, 47, 100, 105, 118, 62, 10, 10, 32, // .
.. + 32, 60, 100, 105, 118, 32, 115, 116, 121, 108, 101, 61, //
. + 60, 98, 62, 85, 65, 82, 84, 32, 99, 111, 110, 115, // UART cons + 111, 108, 101, 60, 47, 98, 62, 60, 115, 112, 97, 110, // oleworks . + 32, 32, 32, 111, 118, 101, 114, 32, 116, 104, 101, 32, // over the + 108, 111, 99, 97, 108, 32, 87, 83, 32, 112, 111, 114, // local WS por + 116, 46, 32, 87, 101, 98, 83, 111, 99, 107, 101, 116, // t. WebSocket + 32, 115, 116, 97, 116, 117, 115, 58, 32, 60, 47, 115, // status: . .u2 + 53, 99, 102, 32, 36, 123, 99, 111, 110, 110, 101, 99, // 5cf ${connec + 116, 101, 100, 32, 63, 32, 39, 99, 111, 110, 110, 101, // ted ? 'conne + 99, 116, 101, 100, 39, 32, 58, 32, 39, 100, 105, 115, // cted' : 'dis + 99, 111, 110, 110, 101, 99, 116, 101, 100, 39, 125, 32, // connected'} + 60, 47, 115, 112, 97, 110, 62, 10, 32, 32, 60, 47, // . .
. se + 116, 84, 120, 116, 40, 101, 118, 46, 116, 97, 114, 103, // tTxt(ev.targ + 101, 116, 46, 118, 97, 108, 117, 101, 41, 125, 32, 47, // et.value)} / + 62, 10, 32, 32, 32, 32, 60, 98, 117, 116, 116, 111, // >. setMessage + 115, 40, 91, 93, 41, 125, 62, 99, 108, 101, 97, 114, // s([])}>clear + 60, 47, 98, 117, 116, 116, 111, 110, 62, 10, 32, 32, // . + 60, 47, 100, 105, 118, 62, 10, 32, 32, 60, 112, 114, //
. . ${m + 101, 115, 115, 97, 103, 101, 115, 46, 109, 97, 112, 40, // essages.map( + 109, 101, 115, 115, 97, 103, 101, 32, 61, 62, 32, 104, // message => h + 40, 77, 101, 115, 115, 97, 103, 101, 44, 32, 123, 109, // (Message, {m + 101, 115, 115, 97, 103, 101, 125, 41, 41, 125, 10, 32, // essage}))}. + 32, 60, 47, 112, 114, 101, 62, 10, 10, 60, 47, 100, // ..`;.};..wi + 110, 100, 111, 119, 46, 111, 110, 108, 111, 97, 100, 32, // ndow.onload + 61, 32, 40, 41, 32, 61, 62, 32, 114, 101, 110, 100, // = () => rend + 101, 114, 40, 104, 40, 65, 112, 112, 41, 44, 32, 100, // er(h(App), d + 111, 99, 117, 109, 101, 110, 116, 46, 98, 111, 100, 121, // ocument.body + 41, 59, 10, 0 // );. }; static const unsigned char v3[] = { 118, 97, 114, 32, 101, 44, 110, 44, 95, 44, 116, 44, // var e,n,_,t, @@ -1382,85 +1614,146 @@ static const unsigned char v4[] = { 32, 49, 101, 109, 59, 32, 98, 111, 114, 100, 101, 114, // 1em; border 45, 114, 97, 100, 105, 117, 115, 58, 32, 48, 46, 50, // -radius: 0.2 101, 109, 59, 32, 125, 10, 116, 101, 120, 116, 97, 114, // em; }.textar - 101, 97, 44, 32, 105, 110, 112, 117, 116, 44, 32, 46, // ea, input, . - 97, 100, 100, 111, 110, 32, 123, 32, 102, 111, 110, 116, // addon { font - 45, 115, 105, 122, 101, 58, 32, 49, 53, 112, 120, 59, // -size: 15px; + 101, 97, 44, 32, 105, 110, 112, 117, 116, 32, 123, 32, // ea, input { + 98, 111, 114, 100, 101, 114, 58, 32, 49, 112, 120, 32, // border: 1px + 115, 111, 108, 105, 100, 32, 35, 99, 99, 99, 59, 32, // solid #ccc; + 112, 97, 100, 100, 105, 110, 103, 58, 32, 48, 46, 51, // padding: 0.3 + 101, 109, 32, 48, 46, 53, 101, 109, 59, 125, 10, 116, // em 0.5em;}.t + 101, 120, 116, 97, 114, 101, 97, 44, 32, 105, 110, 112, // extarea, inp + 117, 116, 44, 32, 46, 97, 100, 100, 111, 110, 44, 32, // ut, .addon, + 46, 108, 97, 98, 101, 108, 32, 123, 32, 102, 111, 110, // .label { fon + 116, 45, 115, 105, 122, 101, 58, 32, 49, 52, 112, 120, // t-size: 14px + 59, 32, 125, 10, 97, 44, 32, 97, 58, 118, 105, 115, // ; }.a, a:vis + 105, 116, 101, 100, 44, 32, 97, 58, 97, 99, 116, 105, // ited, a:acti + 118, 101, 32, 123, 32, 99, 111, 108, 111, 114, 58, 32, // ve { color: + 35, 53, 53, 102, 59, 32, 125, 10, 98, 111, 100, 121, // #55f; }.body + 32, 123, 98, 97, 99, 107, 103, 114, 111, 117, 110, 100, // {background + 58, 32, 35, 52, 53, 53, 59, 32, 125, 10, 46, 97, // : #455; }..a + 100, 100, 111, 110, 32, 123, 32, 99, 111, 108, 111, 114, // ddon { color + 58, 32, 35, 57, 57, 57, 59, 32, 109, 105, 110, 45, // : #999; min- + 119, 105, 100, 116, 104, 58, 32, 55, 46, 53, 101, 109, // width: 7.5em + 59, 32, 32, 125, 10, 46, 108, 97, 98, 101, 108, 32, // ; }..label + 123, 32, 99, 111, 108, 111, 114, 58, 32, 35, 57, 57, // { color: #99 + 57, 59, 32, 125, 10, 46, 98, 116, 110, 32, 123, 10, // 9; }..btn {. + 32, 32, 98, 97, 99, 107, 103, 114, 111, 117, 110, 100, // background + 58, 32, 35, 99, 99, 99, 59, 32, 98, 111, 114, 100, // : #ccc; bord + 101, 114, 45, 114, 97, 100, 105, 117, 115, 58, 32, 48, // er-radius: 0 + 46, 51, 101, 109, 59, 32, 98, 111, 114, 100, 101, 114, // .3em; border + 58, 32, 48, 59, 32, 99, 111, 108, 111, 114, 58, 32, // : 0; color: + 35, 102, 102, 102, 59, 32, 99, 117, 114, 115, 111, 114, // #fff; cursor + 58, 32, 112, 111, 105, 110, 116, 101, 114, 59, 10, 32, // : pointer;. + 32, 100, 105, 115, 112, 108, 97, 121, 58, 32, 105, 110, // display: in + 108, 105, 110, 101, 45, 98, 108, 111, 99, 107, 59, 32, // line-block; + 112, 97, 100, 100, 105, 110, 103, 58, 32, 48, 46, 54, // padding: 0.6 + 101, 109, 32, 50, 101, 109, 59, 32, 102, 111, 110, 116, // em 2em; font + 45, 119, 101, 105, 103, 104, 116, 58, 32, 98, 111, 108, // -weight: bol + 100, 101, 114, 59, 10, 125, 10, 46, 98, 116, 110, 91, // der;.}..btn[ + 100, 105, 115, 97, 98, 108, 101, 100, 93, 32, 123, 32, // disabled] { + 111, 112, 97, 99, 105, 116, 121, 58, 32, 48, 46, 53, // opacity: 0.5 + 59, 32, 99, 117, 114, 115, 111, 114, 58, 32, 97, 117, // ; cursor: au + 116, 111, 59, 125, 10, 46, 115, 109, 111, 111, 116, 104, // to;}..smooth + 32, 123, 32, 116, 114, 97, 110, 115, 105, 116, 105, 111, // { transitio + 110, 58, 32, 97, 108, 108, 32, 46, 50, 115, 59, 32, // n: all .2s; + 125, 10, 46, 99, 111, 110, 116, 97, 105, 110, 101, 114, // }..container + 32, 123, 32, 109, 97, 114, 103, 105, 110, 58, 32, 50, // { margin: 2 + 101, 109, 32, 97, 117, 116, 111, 59, 32, 109, 97, 120, // em auto; max + 45, 119, 105, 100, 116, 104, 58, 32, 54, 50, 48, 112, // -width: 620p + 120, 59, 32, 98, 97, 99, 107, 103, 114, 111, 117, 110, // x; backgroun + 100, 58, 32, 119, 104, 105, 116, 101, 59, 32, 112, 97, // d: white; pa + 100, 100, 105, 110, 103, 58, 32, 49, 101, 109, 59, 32, // dding: 1em; + 98, 111, 114, 100, 101, 114, 45, 114, 97, 100, 105, 117, // border-radiu + 115, 58, 32, 48, 46, 53, 101, 109, 59, 32, 125, 10, // s: 0.5em; }. + 46, 100, 45, 102, 108, 101, 120, 32, 123, 32, 100, 105, // .d-flex { di + 115, 112, 108, 97, 121, 58, 32, 102, 108, 101, 120, 59, // splay: flex; + 32, 97, 108, 105, 103, 110, 45, 105, 116, 101, 109, 115, // align-items + 58, 32, 99, 101, 110, 116, 101, 114, 59, 32, 125, 10, // : center; }. + 46, 100, 45, 110, 111, 110, 101, 32, 123, 32, 100, 105, // .d-none { di + 115, 112, 108, 97, 121, 58, 32, 110, 111, 110, 101, 59, // splay: none; + 32, 125, 10, 46, 98, 111, 114, 100, 101, 114, 32, 123, // }..border { 32, 98, 111, 114, 100, 101, 114, 58, 32, 49, 112, 120, // border: 1px - 32, 115, 111, 108, 105, 100, 32, 35, 99, 99, 99, 59, // solid #ccc; - 32, 112, 97, 100, 100, 105, 110, 103, 58, 32, 48, 46, // padding: 0. - 53, 101, 109, 59, 32, 125, 10, 97, 44, 32, 97, 58, // 5em; }.a, a: - 118, 105, 115, 105, 116, 101, 100, 44, 32, 97, 58, 97, // visited, a:a - 99, 116, 105, 118, 101, 32, 123, 32, 99, 111, 108, 111, // ctive { colo - 114, 58, 32, 35, 53, 53, 102, 59, 32, 125, 10, 98, // r: #55f; }.b - 111, 100, 121, 32, 123, 98, 97, 99, 107, 103, 114, 111, // ody {backgro - 117, 110, 100, 58, 32, 35, 52, 53, 53, 59, 32, 125, // und: #455; } - 10, 46, 97, 100, 100, 111, 110, 32, 123, 32, 98, 97, // ..addon { ba - 99, 107, 103, 114, 111, 117, 110, 100, 58, 32, 35, 101, // ckground: #e - 101, 101, 59, 32, 32, 109, 105, 110, 45, 119, 105, 100, // ee; min-wid - 116, 104, 58, 32, 57, 101, 109, 59, 125, 10, 46, 98, // th: 9em;}..b - 116, 110, 32, 123, 10, 32, 32, 98, 97, 99, 107, 103, // tn {. backg - 114, 111, 117, 110, 100, 58, 32, 35, 99, 99, 99, 59, // round: #ccc; - 32, 98, 111, 114, 100, 101, 114, 45, 114, 97, 100, 105, // border-radi - 117, 115, 58, 32, 48, 46, 51, 101, 109, 59, 32, 98, // us: 0.3em; b - 111, 114, 100, 101, 114, 58, 32, 48, 59, 32, 99, 111, // order: 0; co - 108, 111, 114, 58, 32, 35, 102, 102, 102, 59, 32, 99, // lor: #fff; c - 117, 114, 115, 111, 114, 58, 32, 112, 111, 105, 110, 116, // ursor: point - 101, 114, 59, 10, 32, 32, 100, 105, 115, 112, 108, 97, // er;. displa - 121, 58, 32, 105, 110, 108, 105, 110, 101, 45, 98, 108, // y: inline-bl - 111, 99, 107, 59, 32, 112, 97, 100, 100, 105, 110, 103, // ock; padding - 58, 32, 48, 46, 54, 101, 109, 32, 50, 101, 109, 59, // : 0.6em 2em; - 32, 102, 111, 110, 116, 45, 119, 101, 105, 103, 104, 116, // font-weight - 58, 32, 98, 111, 108, 100, 101, 114, 59, 10, 125, 10, // : bolder;.}. - 46, 98, 116, 110, 91, 100, 105, 115, 97, 98, 108, 101, // .btn[disable - 100, 93, 32, 123, 32, 111, 112, 97, 99, 105, 116, 121, // d] { opacity - 58, 32, 48, 46, 53, 59, 32, 99, 117, 114, 115, 111, // : 0.5; curso - 114, 58, 32, 97, 117, 116, 111, 59, 125, 10, 46, 115, // r: auto;}..s - 109, 111, 111, 116, 104, 32, 123, 32, 116, 114, 97, 110, // mooth { tran - 115, 105, 116, 105, 111, 110, 58, 32, 97, 108, 108, 32, // sition: all - 46, 50, 115, 59, 32, 125, 10, 46, 99, 111, 110, 116, // .2s; }..cont - 97, 105, 110, 101, 114, 32, 123, 32, 109, 97, 114, 103, // ainer { marg - 105, 110, 58, 32, 50, 101, 109, 32, 97, 117, 116, 111, // in: 2em auto - 59, 32, 109, 97, 120, 45, 119, 105, 100, 116, 104, 58, // ; max-width: - 32, 54, 56, 48, 112, 120, 59, 32, 98, 97, 99, 107, // 680px; back - 103, 114, 111, 117, 110, 100, 58, 32, 119, 104, 105, 116, // ground: whit - 101, 59, 32, 112, 97, 100, 100, 105, 110, 103, 58, 32, // e; padding: - 49, 101, 109, 59, 32, 98, 111, 114, 100, 101, 114, 45, // 1em; border- - 114, 97, 100, 105, 117, 115, 58, 32, 48, 46, 53, 101, // radius: 0.5e - 109, 59, 32, 125, 10, 46, 100, 45, 102, 108, 101, 120, // m; }..d-flex - 32, 123, 32, 100, 105, 115, 112, 108, 97, 121, 58, 32, // { display: - 102, 108, 101, 120, 59, 32, 125, 10, 46, 100, 45, 110, // flex; }..d-n - 111, 110, 101, 32, 123, 32, 100, 105, 115, 112, 108, 97, // one { displa - 121, 58, 32, 110, 111, 110, 101, 59, 32, 125, 10, 46, // y: none; }.. - 98, 111, 114, 100, 101, 114, 32, 123, 32, 98, 111, 114, // border { bor - 100, 101, 114, 58, 32, 49, 112, 120, 32, 115, 111, 108, // der: 1px sol - 105, 100, 32, 35, 100, 100, 100, 59, 32, 125, 10, 46, // id #ddd; }.. - 114, 111, 117, 110, 100, 101, 100, 32, 123, 32, 98, 111, // rounded { bo - 114, 100, 101, 114, 45, 114, 97, 100, 105, 117, 115, 58, // rder-radius: - 32, 48, 46, 53, 101, 109, 59, 32, 125, 10, 46, 110, // 0.5em; }..n - 111, 119, 114, 97, 112, 32, 123, 32, 119, 104, 105, 116, // owrap { whit - 101, 45, 115, 112, 97, 99, 101, 58, 32, 110, 111, 119, // e-space: now - 114, 97, 112, 59, 32, 125, 10, 46, 109, 115, 103, 32, // rap; }..msg - 123, 32, 98, 97, 99, 107, 103, 114, 111, 117, 110, 100, // { background - 58, 32, 35, 100, 101, 102, 59, 32, 98, 111, 114, 100, // : #def; bord - 101, 114, 45, 108, 101, 102, 116, 58, 32, 53, 112, 120, // er-left: 5px - 32, 115, 111, 108, 105, 100, 32, 35, 53, 57, 100, 59, // solid #59d; - 32, 112, 97, 100, 100, 105, 110, 103, 58, 32, 48, 46, // padding: 0. - 53, 101, 109, 59, 32, 102, 111, 110, 116, 45, 115, 105, // 5em; font-si - 122, 101, 58, 32, 57, 48, 37, 59, 32, 109, 97, 114, // ze: 90%; mar - 103, 105, 110, 58, 32, 49, 101, 109, 32, 48, 59, 32, // gin: 1em 0; - 125, 10, 46, 105, 110, 112, 117, 116, 32, 123, 32, 98, // }..input { b - 97, 99, 107, 103, 114, 111, 117, 110, 100, 58, 32, 35, // ackground: # - 102, 101, 97, 59, 32, 112, 97, 100, 100, 105, 110, 103, // fea; padding - 58, 32, 48, 46, 50, 101, 109, 32, 49, 101, 109, 59, // : 0.2em 1em; - 32, 98, 111, 114, 100, 101, 114, 45, 114, 97, 100, 105, // border-radi - 117, 115, 58, 32, 48, 46, 52, 101, 109, 59, 32, 125, // us: 0.4em; } - 10, 46, 111, 117, 116, 112, 117, 116, 32, 123, 32, 98, // ..output { b - 97, 99, 107, 103, 114, 111, 117, 110, 100, 58, 32, 35, // ackground: # - 97, 101, 102, 59, 32, 112, 97, 100, 100, 105, 110, 103, // aef; padding - 58, 32, 48, 46, 50, 101, 109, 32, 49, 101, 109, 59, // : 0.2em 1em; - 32, 98, 111, 114, 100, 101, 114, 45, 114, 97, 100, 105, // border-radi - 117, 115, 58, 32, 48, 46, 52, 101, 109, 59, 32, 125, // us: 0.4em; } - 10, 0 // . + 32, 115, 111, 108, 105, 100, 32, 35, 100, 100, 100, 59, // solid #ddd; + 32, 125, 10, 46, 114, 111, 117, 110, 100, 101, 100, 32, // }..rounded + 123, 32, 98, 111, 114, 100, 101, 114, 45, 114, 97, 100, // { border-rad + 105, 117, 115, 58, 32, 48, 46, 53, 101, 109, 59, 32, // ius: 0.5em; + 125, 10, 46, 110, 111, 119, 114, 97, 112, 32, 123, 32, // }..nowrap { + 119, 104, 105, 116, 101, 45, 115, 112, 97, 99, 101, 58, // white-space: + 32, 110, 111, 119, 114, 97, 112, 59, 32, 125, 10, 46, // nowrap; }.. + 109, 115, 103, 32, 123, 32, 98, 97, 99, 107, 103, 114, // msg { backgr + 111, 117, 110, 100, 58, 32, 35, 100, 101, 102, 59, 32, // ound: #def; + 98, 111, 114, 100, 101, 114, 45, 108, 101, 102, 116, 58, // border-left: + 32, 53, 112, 120, 32, 115, 111, 108, 105, 100, 32, 35, // 5px solid # + 53, 57, 100, 59, 32, 112, 97, 100, 100, 105, 110, 103, // 59d; padding + 58, 32, 48, 46, 53, 101, 109, 59, 32, 102, 111, 110, // : 0.5em; fon + 116, 45, 115, 105, 122, 101, 58, 32, 57, 48, 37, 59, // t-size: 90%; + 32, 109, 97, 114, 103, 105, 110, 58, 32, 49, 101, 109, // margin: 1em + 32, 48, 59, 32, 99, 111, 108, 111, 114, 58, 32, 35, // 0; color: # + 55, 55, 55, 59, 32, 125, 10, 46, 105, 110, 112, 117, // 777; }..inpu + 116, 32, 123, 32, 98, 97, 99, 107, 103, 114, 111, 117, // t { backgrou + 110, 100, 58, 32, 35, 102, 101, 97, 59, 32, 112, 97, // nd: #fea; pa + 100, 100, 105, 110, 103, 58, 32, 48, 46, 50, 101, 109, // dding: 0.2em + 32, 49, 101, 109, 59, 32, 98, 111, 114, 100, 101, 114, // 1em; border + 45, 114, 97, 100, 105, 117, 115, 58, 32, 48, 46, 52, // -radius: 0.4 + 101, 109, 59, 32, 125, 10, 46, 111, 117, 116, 112, 117, // em; }..outpu + 116, 32, 123, 32, 98, 97, 99, 107, 103, 114, 111, 117, // t { backgrou + 110, 100, 58, 32, 35, 97, 101, 102, 59, 32, 112, 97, // nd: #aef; pa + 100, 100, 105, 110, 103, 58, 32, 48, 46, 50, 101, 109, // dding: 0.2em + 32, 49, 101, 109, 59, 32, 98, 111, 114, 100, 101, 114, // 1em; border + 45, 114, 97, 100, 105, 117, 115, 58, 32, 48, 46, 52, // -radius: 0.4 + 101, 109, 59, 32, 125, 10, 46, 112, 114, 45, 49, 32, // em; }..pr-1 + 123, 32, 112, 97, 100, 100, 105, 110, 103, 45, 114, 105, // { padding-ri + 103, 104, 116, 58, 32, 48, 46, 53, 101, 109, 59, 32, // ght: 0.5em; + 125, 10, 46, 109, 121, 45, 49, 32, 123, 32, 109, 97, // }..my-1 { ma + 114, 103, 105, 110, 45, 116, 111, 112, 58, 32, 48, 46, // rgin-top: 0. + 51, 101, 109, 59, 32, 109, 97, 114, 103, 105, 110, 45, // 3em; margin- + 98, 111, 116, 116, 111, 109, 58, 32, 48, 46, 51, 101, // bottom: 0.3e + 109, 59, 125, 10, 46, 109, 108, 45, 49, 32, 123, 32, // m;}..ml-1 { + 109, 97, 114, 103, 105, 110, 45, 108, 101, 102, 116, 58, // margin-left: + 32, 48, 46, 53, 101, 109, 59, 32, 125, 10, 10, 47, // 0.5em; }../ + 42, 32, 71, 114, 105, 100, 32, 42, 47, 10, 46, 114, // * Grid */..r + 111, 119, 32, 123, 32, 100, 105, 115, 112, 108, 97, 121, // ow { display + 58, 32, 102, 108, 101, 120, 59, 32, 102, 108, 101, 120, // : flex; flex + 45, 119, 114, 97, 112, 58, 32, 119, 114, 97, 112, 59, // -wrap: wrap; + 32, 125, 10, 46, 99, 111, 108, 32, 123, 32, 109, 97, // }..col { ma + 114, 103, 105, 110, 58, 32, 48, 59, 32, 112, 97, 100, // rgin: 0; pad + 100, 105, 110, 103, 58, 32, 48, 59, 32, 111, 118, 101, // ding: 0; ove + 114, 102, 108, 111, 119, 58, 32, 97, 117, 116, 111, 59, // rflow: auto; + 32, 125, 10, 46, 99, 111, 108, 45, 49, 50, 32, 123, // }..col-12 { + 32, 119, 105, 100, 116, 104, 58, 32, 49, 48, 48, 37, // width: 100% + 59, 32, 125, 10, 46, 99, 111, 108, 45, 49, 49, 32, // ; }..col-11 + 123, 32, 119, 105, 100, 116, 104, 58, 32, 57, 49, 46, // { width: 91. + 54, 54, 37, 59, 32, 125, 10, 46, 99, 111, 108, 45, // 66%; }..col- + 49, 48, 32, 123, 32, 119, 105, 100, 116, 104, 58, 32, // 10 { width: + 56, 51, 46, 51, 51, 37, 59, 32, 125, 10, 46, 99, // 83.33%; }..c + 111, 108, 45, 57, 32, 123, 32, 119, 105, 100, 116, 104, // ol-9 { width + 58, 32, 55, 53, 37, 59, 32, 125, 10, 46, 99, 111, // : 75%; }..co + 108, 45, 56, 32, 123, 32, 119, 105, 100, 116, 104, 58, // l-8 { width: + 32, 54, 54, 46, 54, 54, 37, 59, 32, 125, 10, 46, // 66.66%; }.. + 99, 111, 108, 45, 55, 32, 123, 32, 119, 105, 100, 116, // col-7 { widt + 104, 58, 32, 53, 56, 46, 51, 51, 37, 59, 32, 125, // h: 58.33%; } + 10, 46, 99, 111, 108, 45, 54, 32, 123, 32, 119, 105, // ..col-6 { wi + 100, 116, 104, 58, 32, 53, 48, 37, 59, 32, 125, 10, // dth: 50%; }. + 46, 99, 111, 108, 45, 53, 32, 123, 32, 119, 105, 100, // .col-5 { wid + 116, 104, 58, 32, 52, 49, 46, 54, 54, 37, 59, 32, // th: 41.66%; + 125, 10, 46, 99, 111, 108, 45, 52, 32, 123, 32, 119, // }..col-4 { w + 105, 100, 116, 104, 58, 32, 51, 51, 46, 51, 51, 37, // idth: 33.33% + 59, 32, 125, 10, 46, 99, 111, 108, 45, 51, 32, 123, // ; }..col-3 { + 32, 119, 105, 100, 116, 104, 58, 32, 50, 53, 37, 59, // width: 25%; + 32, 125, 10, 46, 99, 111, 108, 45, 50, 32, 123, 32, // }..col-2 { + 119, 105, 100, 116, 104, 58, 32, 49, 54, 46, 54, 54, // width: 16.66 + 37, 59, 32, 125, 10, 46, 99, 111, 108, 45, 49, 32, // %; }..col-1 + 123, 32, 119, 105, 100, 116, 104, 58, 32, 56, 46, 51, // { width: 8.3 + 51, 37, 59, 32, 125, 10, 64, 109, 101, 100, 105, 97, // 3%; }.@media + 32, 40, 109, 105, 110, 45, 119, 105, 100, 116, 104, 58, // (min-width: + 32, 49, 51, 49, 48, 112, 120, 41, 32, 123, 32, 46, // 1310px) { . + 99, 111, 110, 116, 97, 105, 110, 101, 114, 32, 123, 32, // container { + 109, 97, 114, 103, 105, 110, 58, 32, 97, 117, 116, 111, // margin: auto + 59, 32, 119, 105, 100, 116, 104, 58, 32, 49, 50, 55, // ; width: 127 + 48, 112, 120, 59, 32, 125, 32, 125, 10, 64, 109, 101, // 0px; } }.@me + 100, 105, 97, 32, 40, 109, 97, 120, 45, 119, 105, 100, // dia (max-wid + 116, 104, 58, 32, 57, 50, 48, 112, 120, 41, 32, 123, // th: 920px) { + 32, 46, 114, 111, 119, 32, 46, 99, 111, 108, 32, 123, // .row .col { + 32, 119, 105, 100, 116, 104, 58, 32, 49, 48, 48, 37, // width: 100% + 59, 32, 125, 32, 125, 10, 0 // ; } }. }; static const struct packed_file { @@ -1470,9 +1763,9 @@ static const struct packed_file { time_t mtime; } packed_files[] = { {"/web_root/index.html", v1, sizeof(v1), 1654623573}, - {"/web_root/main.js", v2, sizeof(v2), 1654674665}, + {"/web_root/main.js", v2, sizeof(v2), 1655500873}, {"/web_root/preact.min.js", v3, sizeof(v3), 1654623573}, - {"/web_root/style.css", v4, sizeof(v4), 1654653116}, + {"/web_root/style.css", v4, sizeof(v4), 1655197477}, {NULL, NULL, 0, 0} }; diff --git a/examples/uart-bridge/web_root/main.js b/examples/uart-bridge/web_root/main.js index 661aa238..f72020e8 100644 --- a/examples/uart-bridge/web_root/main.js +++ b/examples/uart-bridge/web_root/main.js @@ -7,24 +7,36 @@ const Message = m => html``; const App = function(props) { - const [cfg, setCfg] = useState({tcp: '', ws: ''}); + const [cfg, setCfg] = useState({tcp: {}, ws: {}, mqtt: {}}); const [messages, setMessages] = useState([]); - const [txt, setTxt] = useState(''); const [connected, setConnected] = useState(false); + const [txt, setTxt] = useState(''); const [ws, setWs] = useState(null); + const [rx, setRx] = useState(''); + const [tx, setTx] = useState(''); + const [baud, setBaud] = useState(''); + const [tcpport, setTcpport] = useState(4001); + const [wsport, setWsport] = useState(4002); + const [mqtt, setMqtt] = useState(''); - const tcp_port = cfg.tcp.split(':')[2] || 4001; - const ws_port = cfg.ws.split(':')[2] || 4002; + // const tcp_port = cfg.tcp.split(':')[2] || 4001; + // const ws_port = cfg.ws.split(':')[2] || 4002; - const refresh = () => - fetch('/api/config/get').then(r => r.json()).then(r => setCfg(r)); + const refresh = () => fetch('/api/config/get').then(r => r.json()).then(r => { + setTx(r.tx), setRx(r.rx), setBaud(r.baud), setCfg(r); + setTcpport(r.tcp.url.split(':')[2] || 4001); + setWsport(r.ws.url.split(':')[2] || 4002); + setMqtt(r.mqtt.url); + }); const watchWebsocket = function() { // Connect to websocker port, to implement WS console - var l = window.location, proto = l.protocol.replace('http', 'ws'); - var tid, url = `${proto}//${l.hostname}:${ws_port}/ws`; - console.log(url); var reconnect = function() { + var port; + setWsport(x => port = x); + var l = window.location, proto = l.protocol.replace('http', 'ws'); + var tid, url = `${proto}//${l.hostname}:${port}/ws`; + // console.log(url); var ws = new WebSocket(url); ws.onopen = () => { setConnected(true); @@ -56,25 +68,81 @@ const App = function(props) { setTxt(''); }; + const onchange = ev => false; + return html`
-

UART \u27F7 network bridge

-

UART settings

+

UART \u27F7 network bridge

${JSON.stringify(cfg, null, 2)}
-

RX pin ${cfg.rx}, TX pin ${cfg.tx}, baud ${cfg.baud}

-

TCP listener: port ${tcp_port}

-

Use netcat to connect:

-
$ nc ${location.hostname} ${tcp_port}
-

Websocket listener: port ${ws_port}

-
UART console. Status: ${connected ? 'connected' : 'disconnected'}
+
+
+

UART configuration

+
+ + setTx(ev.target.value)} /> +
+ + setRx(ev.target.value)} /> +
+ + setBaud(ev.target.value)} /> +
+
+
+

Network configuration

+
+ + setTcpport(ev.target.value)} /> + +
+ + setWsport(ev.target.value)} /> + +
+ + setMqtt(ev.target.value)} /> + +
+
+
+ +
+ Note: to connect over MQTT, + go to + console, subscribe to b/tx and publish to b/rx
+ Note: to connect over TCP, use netcat utility:
+ $ nc ${location.hostname} ${tcpport} +
+ +
+ UART consoleworks + over the local WS port. WebSocket status: + \u25cf ${connected ? 'connected' : 'disconnected'} +
+
+ setTxt(ev.target.value)} /> + +
     ${messages.map(message => h(Message, {message}))}
   
-
- setTxt(ev.target.value)} /> -
+
`; }; diff --git a/examples/uart-bridge/web_root/style.css b/examples/uart-bridge/web_root/style.css index 3e5b8c30..5d2dea21 100644 --- a/examples/uart-bridge/web_root/style.css +++ b/examples/uart-bridge/web_root/style.css @@ -2,22 +2,45 @@ html, body { margin: 0; padding: 0; height: 100%; font: 16px sans-serif; } select, input, label::before, textarea { outline: none; box-shadow:none !important; border: 1px solid #ccc !important; } pre { color: #373; font-family: monospace; font-weight: bolder; font-size: smaller; background: #ddd; padding: 1em; border-radius: 0.2em; } -textarea, input, .addon { font-size: 15px; border: 1px solid #ccc; padding: 0.5em; } +textarea, input { border: 1px solid #ccc; padding: 0.3em 0.5em;} +textarea, input, .addon, .label { font-size: 14px; } a, a:visited, a:active { color: #55f; } body {background: #455; } -.addon { background: #eee; min-width: 9em;} +.addon { color: #999; min-width: 7.5em; } +.label { color: #999; } .btn { background: #ccc; border-radius: 0.3em; border: 0; color: #fff; cursor: pointer; display: inline-block; padding: 0.6em 2em; font-weight: bolder; } .btn[disabled] { opacity: 0.5; cursor: auto;} .smooth { transition: all .2s; } -.container { margin: 2em auto; max-width: 680px; background: white; padding: 1em; border-radius: 0.5em; } -.d-flex { display: flex; } +.container { margin: 2em auto; max-width: 620px; background: white; padding: 1em; border-radius: 0.5em; } +.d-flex { display: flex; align-items: center; } .d-none { display: none; } .border { border: 1px solid #ddd; } .rounded { border-radius: 0.5em; } .nowrap { white-space: nowrap; } -.msg { background: #def; border-left: 5px solid #59d; padding: 0.5em; font-size: 90%; margin: 1em 0; } +.msg { background: #def; border-left: 5px solid #59d; padding: 0.5em; font-size: 90%; margin: 1em 0; color: #777; } .input { background: #fea; padding: 0.2em 1em; border-radius: 0.4em; } .output { background: #aef; padding: 0.2em 1em; border-radius: 0.4em; } +.pr-1 { padding-right: 0.5em; } +.my-1 { margin-top: 0.3em; margin-bottom: 0.3em;} +.ml-1 { margin-left: 0.5em; } + +/* Grid */ +.row { display: flex; flex-wrap: wrap; } +.col { margin: 0; padding: 0; overflow: auto; } +.col-12 { width: 100%; } +.col-11 { width: 91.66%; } +.col-10 { width: 83.33%; } +.col-9 { width: 75%; } +.col-8 { width: 66.66%; } +.col-7 { width: 58.33%; } +.col-6 { width: 50%; } +.col-5 { width: 41.66%; } +.col-4 { width: 33.33%; } +.col-3 { width: 25%; } +.col-2 { width: 16.66%; } +.col-1 { width: 8.33%; } +@media (min-width: 1310px) { .container { margin: auto; width: 1270px; } } +@media (max-width: 920px) { .row .col { width: 100%; } }