From 6898ff834928a16d38cdaa4107f7af446f7d4025 Mon Sep 17 00:00:00 2001
From: Pim van Pelt <pim@ipng.nl>
Date: Sun, 25 Feb 2018 19:59:32 +0100
Subject: [PATCH] Add a simple Prometheus exporter.

---
 mos.yml    |  2 ++
 src/dht.c  | 44 ++++++++++++++++++++++++++++++++++----------
 src/main.c |  9 ++++++++-
 3 files changed, 44 insertions(+), 11 deletions(-)

diff --git a/mos.yml b/mos.yml
index ba97061..c658e12 100644
--- a/mos.yml
+++ b/mos.yml
@@ -43,6 +43,7 @@ config_schema:
   - ["app.config", "s", {title: "Application specific config file"}]
   - ["app.config", "sonoff-basic.json"]
   - ["app.dht_gpio", "s", "5,4,14", {title: "Comma Separated list of GPIO pins to enable DHT22/AM2302 sensors on"}]
+  - ["prometheus.pushgateway", "chbtl01.paphosting.net:9091"]
 
 
 #build_vars:
@@ -58,6 +59,7 @@ libs:
   - origin: https://github.com/mongoose-os-libs/rpc-mqtt
   - origin: https://github.com/mongoose-os-libs/mqtt
   - origin: https://github.com/mongoose-os-libs/dht
+  - origin: https://github.com/mongoose-os-libs/prometheus-metrics
   - origin: libs/rpc-service-ota
 
 # Used by the mos tool to catch mos binaries incompatible with this file format
diff --git a/src/dht.c b/src/dht.c
index ef158da..d704928 100644
--- a/src/dht.c
+++ b/src/dht.c
@@ -4,6 +4,7 @@
 #include "main.h"
 #include "mgos_dht.h"
 #include "mgos_config.h"
+#include "mgos_prometheus_metrics.h"
 #include "mqtt.h"
 
 #define MAX_DHT 8
@@ -11,18 +12,39 @@
 static struct mgos_dht *s_dht[MAX_DHT];
 static int s_num_dht = 0;
 
-static void dht_cb(void *ud) {
-  int dht_idx = (int)ud;
-  float t, h;
+static void print_chunk(struct mg_connection *nc, char *name, char *fmt, ...) {
+  char chunk[500];
+  int chunklen=0;
+  va_list ap;
 
-  if (!s_dht[dht_idx]) {
-    LOG(LL_ERROR, ("No DHT handle to work with!"));
-    return;
+  snprintf(chunk, sizeof(chunk), "%s%s", name, fmt[0]=='{' ? "" : " ");
+  va_start(ap, fmt);
+  vsnprintf(chunk+strlen(chunk), sizeof(chunk)-strlen(chunk), fmt, ap);
+  va_end(ap);
+  strncat(chunk, "\n", sizeof(chunk));
+  chunklen=strlen(chunk);
+  LOG(LL_DEBUG, ("Chunk '%s' with length %d", chunk, chunklen));
+  mg_printf(nc, "%X\r\n%s\r\n", chunklen, chunk);
+
+}
+
+static void dht_prometheus_metrics(struct mg_connection *nc, void *user_data) {
+  int i;
+
+  // BUG -- repeated HELP and TYPE makes Prometheus parser bork :(
+  mgos_prometheus_metrics_printf(nc, GAUGE,
+    "temperature", "Temperature in celcius",
+    "{sensor=\"0\",type=\"DHT\"} %f", mgos_dht_get_temp(s_dht[0]));
+  mgos_prometheus_metrics_printf(nc, GAUGE,
+    "humidity", "Relative humidity percentage",
+    "{sensor=\"0\",type=\"DHT\"} %f", mgos_dht_get_humidity(s_dht[0]));
+
+  for (i=1; i<s_num_dht; i++) {
+    print_chunk(nc, "temperature", "{sensor=\"%d\",type=\"DHT\"} %f", i, mgos_dht_get_temp(s_dht[i]));
+    print_chunk(nc, "humidity", "{sensor=\"%d\",type=\"DHT\"} %f", i, mgos_dht_get_humidity(s_dht[i]));
   }
-  t = mgos_dht_get_temp(s_dht[dht_idx]);
-  h = mgos_dht_get_humidity(s_dht[dht_idx]);
 
-  mqtt_publish_stat("dht", "{idx:%d, temp:%.2f, humidity:%.1f}", dht_idx, t, h);
+  (void) user_data;
 }
 
 static bool dht_create(int pin, enum dht_type type) {
@@ -38,7 +60,6 @@ static bool dht_create(int pin, enum dht_type type) {
     return false;
   }
   s_dht[s_num_dht] = dht;
-  mgos_set_timer(5000, true, dht_cb, (void *)s_num_dht);
   s_num_dht++;
 
   return true;
@@ -56,6 +77,9 @@ void dht_init() {
     dht_create(gpio, AM2302);
     mgos_msleep(250);
   }
+
+  if (s_num_dht>0)
+    mgos_prometheus_metrics_add_handler(dht_prometheus_metrics, NULL);
 }
 
 #else
diff --git a/src/main.c b/src/main.c
index a1caa0f..602f294 100644
--- a/src/main.c
+++ b/src/main.c
@@ -1,12 +1,19 @@
+#include "mgos_config.h"
+#include "mgos_prometheus_metrics.h"
 #include "main.h"
 #include "mqtt.h"
-#include "mgos_config.h"
 #include "rpc.h"
 
+static void timer_cb(void *user_data) {
+  mgos_prometheus_metrics_push("lightswitch", mgos_sys_config_get_device_id());
+  (void) user_data;
+}
+
 enum mgos_app_init_result mgos_app_init(void) {
   channel_init(mgos_sys_config_get_app_config());
   mqtt_init();
   rpc_init();
   dht_init();
+  mgos_set_timer(5000, true, timer_cb, NULL);
   return MGOS_APP_INIT_SUCCESS;
 }