From fae52bb543f43f3d8307203dd1d0011089b4126c Mon Sep 17 00:00:00 2001
From: Pim van Pelt <pim@ipng.nl>
Date: Sat, 2 Dec 2017 23:34:16 +0100
Subject: [PATCH] Add MQTT buttons!

---
 fs/screen_one.json                 |  8 ++---
 mos.yml                            |  5 ++-
 src/main.c                         | 13 ++++++--
 src/widget.c                       | 23 ++------------
 src/widget_default.c               | 51 ++++++++++++++++++++++++++++--
 unittest/data/TestWidget-MQTT.json |  2 +-
 unittest/test_widget.c             | 40 +++++++++++++++++++++++
 7 files changed, 109 insertions(+), 33 deletions(-)

diff --git a/fs/screen_one.json b/fs/screen_one.json
index def1cbe..dff8059 100644
--- a/fs/screen_one.json
+++ b/fs/screen_one.json
@@ -1,5 +1,5 @@
 {
-  "name": "Heli",
+  "name": "Sticky",
   "widgets": [
     {
       "name": "back",
@@ -19,7 +19,7 @@
       "h": 56,
       "label": "On",
       "type": 1,
-      "mqtt": [ "/s/cmnd/60:01:94:80:85:01/power3 On" ]
+      "mqtt": [ "/s/cmnd/5C:CF:7F:20:29:6E/Power On" ]
     },
     {
       "name": "heli_led_off",
@@ -29,7 +29,7 @@
       "h": 56,
       "label": "Off",
       "type": 1,
-      "mqtt": [ "/s/cmnd/60:01:94:80:85:01/power3 Off" ]
+      "mqtt": [ "/s/cmnd/5C:CF:7F:20:29:6E/Power Off" ]
     },
     {
       "name": "heli_led_toggle",
@@ -39,7 +39,7 @@
       "h": 56,
       "label": "Toggle",
       "type": 1,
-      "mqtt": [ "/s/cmnd/60:01:94:80:85:01/power3 Toggle", "test", "foo", "bar" ]
+      "mqtt": [ "/s/cmnd/5C:CF:7F:20:29:6E/Power Toggle" ]
     }
   ]
 }
diff --git a/mos.yml b/mos.yml
index 132bf12..0faf163 100644
--- a/mos.yml
+++ b/mos.yml
@@ -39,13 +39,15 @@ config_schema:
   - ["app.backlight_pin", "i", {title: "Backlight pin"}]
   - ["app.backlight_pin", 22]
   - ["app.inactivity_timeout", "i", {title: "Inactivity timeout in seconds"}]
-  - ["app.inactivity_timeout", 10]
+  - ["app.inactivity_timeout", 300]
   - ["app.battery_calibration", i, {title: "Battery ADC value at 4000mV"}]
   - ["app.battery_calibration", 2360]
   - ["spi.enable", true]
   - ["spi.cs1_gpio", -1]
   - ["spi.cs2_gpio", -1]
   - ["stmpe610.cs_index", 0]
+  - ["mqtt.enable", true]
+  - ["mqtt.server", "chbtl01.paphosting.net:1883"]
 
 conds:
   - when: mos.platform == "esp32"
@@ -76,6 +78,7 @@ libs:
   - origin: https://github.com/mongoose-os-libs/rpc-service-fs
   - origin: https://github.com/mongoose-os-libs/rpc-uart
   - origin: https://github.com/mongoose-os-libs/prometheus-metrics
+  - origin: https://github.com/mongoose-os-libs/mqtt
   - origin: https://github.com/mongoose-os-libs/pwm
   - origin: https://github.com/mongoose-os-libs/adc
   - origin: https://github.com/mongoose-os-libs/spi
diff --git a/src/main.c b/src/main.c
index ec8920e..168dda2 100644
--- a/src/main.c
+++ b/src/main.c
@@ -2,7 +2,7 @@
 #include <time.h>
 
 #include "mgos.h"
-#include "mgos_pwm.h"
+#include "mgos_mqtt.h"
 #include "mongoose-touch.h"
 #include "fonts/FreeSerifBold9pt7b.h"
 #include "fonts/FreeMonoBold9pt7b.h"
@@ -34,11 +34,9 @@ static void touch_handler(struct mgos_stmpe610_event_data *ed) {
   widget = screen_widget_find_by_xy(s_screen, ed->x, ed->y);
 
   if (ed->direction==TOUCH_DOWN) {
-    widget_network_recv();
     if (widget && widget->handler)
       widget->handler(EV_WIDGET_TOUCH_DOWN, widget, ed);
   } else {
-    widget_network_send();
     if (widget && widget->handler)
       widget->handler(EV_WIDGET_TOUCH_UP, widget, ed);
   }
@@ -64,9 +62,18 @@ void tft_demo(void)
   LOG(LL_INFO, ("Screen '%s' has %d widgets", s_screen->name, screen_get_num_widgets(s_screen)));
 }
 
+static void mqtt_recv_cb(struct mg_connection *nc, const char *topic, int topic_len, const char *msg, int msg_len, void *user_data) {
+  widget_network_recv();
+  LOG(LL_INFO, ("topic='%.*s' msg='%.*s'", topic_len, topic, msg_len, msg));
+  (void) nc;
+  (void) user_data;
+}
+
+
 enum mgos_app_init_result mgos_app_init(void)
 {
   backlight_init();
+  mgos_mqtt_sub("/s/#", mqtt_recv_cb, NULL);
 
   tft_demo();
 
diff --git a/src/widget.c b/src/widget.c
index 63e64c1..7a779a1 100644
--- a/src/widget.c
+++ b/src/widget.c
@@ -91,31 +91,12 @@ struct widget_t *widget_create_from_json(const char *json) {
       widget->user_data = screen;
     }
   } else if (type == WIDGET_TYPE_MQTT_BUTTON) {
-    void *h = NULL;
     char *mqtt = NULL;
-    struct json_token val;
-    int idx;
-
     if (json_scanf(json, strlen(json), "{mqtt:%Q}", &mqtt) != 1) {
       LOG(LL_WARN, ("Widget '%s' is of type MQTT_BUTTON but does not have attribute 'mqtt'", widget->name));
     } else {
-      // Find length of all messages
-      uint16_t len=0;
-      char *p=NULL;
-      while ((h = json_next_elem(json, strlen(json), h, ".mqtt", &idx, &val)) != NULL) {
-        LOG(LL_INFO, ("[%d]: [%.*s]", idx, val.len, val.ptr));
-        len+=val.len;
-      }
-      LOG(LL_INFO, ("%d elements in the list, %d total length", idx, len));
-      p=malloc(len+1+idx);
-      memset(p, 0, len+1+idx);
-      while ((h = json_next_elem(json, strlen(json), h, ".mqtt", &idx, &val)) != NULL) {
-        strncat(p, val.ptr, val.len);
-        strncat(p, "\001", len+1+idx);
-      }
-      LOG(LL_INFO, ("String is '%.*s'", len+idx, p));
-      widget->user_data=p;
-      if(mqtt) free(mqtt);
+      if (widget->user_data) free(widget->user_data);
+      widget->user_data=mqtt;
     }
   }
 
diff --git a/src/widget_default.c b/src/widget_default.c
index edf775d..aee8bdf 100644
--- a/src/widget_default.c
+++ b/src/widget_default.c
@@ -1,4 +1,5 @@
 #include "mgos.h"
+#include "mgos_mqtt.h"
 #include "mongoose-touch.h"
 
 extern GFXfont FreeSerifBold9pt7b;
@@ -106,6 +107,46 @@ static void widget_default_loadscreen(struct widget_t *w, void *ev_data) {
   (void) ev_data;
 }
 
+
+static void widget_default_mqtt_send(struct widget_t *w, void *ev_data) {
+  struct json_token val;
+  int idx=0;
+
+  if (!w)
+    return;
+  if (!w->user_data)
+    return;
+//  LOG(LL_DEBUG, ("MQTT string: '%s'", (char *)w->user_data));
+  // Traverse Array
+  for (idx = 0; json_scanf_array_elem(w->user_data, strlen(w->user_data), "", idx, &val) > 0; idx++) {
+    char *t=NULL, *m=NULL;
+    uint16_t t_len=0, m_len=0;
+    char *topic;
+
+    LOG(LL_DEBUG, ("Index %d, token [%.*s]", idx, val.len, val.ptr));
+    t=(char*)val.ptr;
+    m=strstr(val.ptr, " ");
+    if (m-val.ptr <= val.len) {
+      t_len=m-t;
+      m++;
+      m_len=val.len-t_len-1;
+    } else {
+      t_len=val.len;
+      m_len=0;
+      m=NULL;
+    }
+    if ((topic=malloc(t_len+1))) {
+      memcpy(topic, t, t_len);
+      topic[t_len]=0;
+      LOG(LL_INFO, ("Sending topic='%s', message='%.*s'", topic, m_len, m));
+      mgos_mqtt_pub(topic, m, m_len, 0, false);
+      free(topic);
+      widget_network_send();
+    }
+  }
+  (void) ev_data;
+}
+
 void widget_default_ev(int ev, struct widget_t *w, void *ev_data) {
   char evname[15];
 
@@ -124,10 +165,14 @@ void widget_default_ev(int ev, struct widget_t *w, void *ev_data) {
       widget_default_draw(w, ILI9341_GREEN);
       break;
     case EV_WIDGET_TOUCH_UP:
-      if (w->type == WIDGET_TYPE_LOADSCREEN)
+      if (w->type == WIDGET_TYPE_LOADSCREEN) {
         widget_default_loadscreen(w, ev_data);
-      else
-        widget_default_draw(w, ILI9341_GREEN);
+        break;
+      }
+      if (w->type == WIDGET_TYPE_MQTT_BUTTON) {
+        widget_default_mqtt_send(w, ev_data);
+      }
+      widget_default_draw(w, ILI9341_GREEN);
       break;
     case EV_WIDGET_TOUCH_DOWN:
       widget_default_draw(w, ILI9341_RED);
diff --git a/unittest/data/TestWidget-MQTT.json b/unittest/data/TestWidget-MQTT.json
index 990311f..57e9435 100644
--- a/unittest/data/TestWidget-MQTT.json
+++ b/unittest/data/TestWidget-MQTT.json
@@ -6,5 +6,5 @@
   "h": 56,
   "label": "On",
   "type": 1,
-  "mqtt": [ "/topic1 Hello", "/topic2 World" ]
+  "mqtt": [ "/topic1 Hello", "/t2 Wereld", "/topic3", "", "/top4" ]
 }
diff --git a/unittest/test_widget.c b/unittest/test_widget.c
index 38e2bbb..9a9f254 100644
--- a/unittest/test_widget.c
+++ b/unittest/test_widget.c
@@ -72,6 +72,45 @@ int test_widget() {
   return 0;
 }
 
+static void widget_default_mqtt_send(struct widget_t *w, void *ev_data) {
+  struct json_token val;
+  int idx=0;
+
+  if (!w)
+    return;
+  if (!w->user_data)
+    return;
+  LOG(LL_INFO, ("MQTT string: '%s'", (char *)w->user_data));
+  // Traverse Array
+  for (idx = 0; json_scanf_array_elem(w->user_data, strlen(w->user_data), "", idx, &val) > 0; idx++) {
+    char *t=NULL, *m=NULL;
+    uint16_t t_len=0, m_len=0;
+    char *topic;
+
+    LOG(LL_DEBUG, ("Index %d, token [%.*s]", idx, val.len, val.ptr));
+    t=(char*)val.ptr;
+    m=strstr(val.ptr, " ");
+    if (m-val.ptr <= val.len) {
+      LOG(LL_INFO, ("Space found"));
+      t_len=m-t;
+      m++;
+      m_len=val.len-t_len-1;
+    } else {
+      t_len=val.len;
+      m_len=0;
+      m=NULL;
+    }
+    if ((topic=malloc(t_len+1))) {
+      memcpy(topic, t, t_len);
+      topic[t_len]=0;
+      LOG(LL_INFO, ("Sending topic='%s', message='%.*s'", topic, m_len, m));
+      free(topic);
+    }
+  }
+  (void) ev_data;
+}
+
+
 int test_widget_mqtt() {
   struct widget_t *w;
 
@@ -81,5 +120,6 @@ int test_widget_mqtt() {
   ASSERT(w, "widget_create_from_file()");
   ASSERT(w->x == 240, "'x' field is invalid");
   ASSERT(w->type == WIDGET_TYPE_MQTT_BUTTON, "'type' field is not WIDGET_TYPE_MQTT_BUTTON");
+  widget_default_mqtt_send(w, NULL);
   return 0;
 }