An ESP32-based Redis-watcher and info-displayer https://rpjios.com
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

248 lines
7.4 KiB

  1. #include "zw_redis.h"
  2. #include "zw_logging.h"
  3. #include <errno.h>
  4. #define REDIS_KEY(x) String(hostname + x).c_str()
  5. #define REDIS_KEY_CREATE_LOCAL(x) \
  6. auto redisKey_local__String_capture = String(hostname + x); \
  7. auto redisKey_local = redisKey_local__String_capture.c_str();
  8. bool ZWRedis::connect()
  9. {
  10. connection.wifi = new WiFiClient();
  11. if (!connection.wifi->connect(configuration.host, configuration.port))
  12. {
  13. dprint("Redis connection failed (wifi): %s (%d)\n", strerror(errno), errno);
  14. perror("redis: wifi");
  15. delete connection.wifi, connection.wifi = nullptr;
  16. return false;
  17. }
  18. else
  19. {
  20. connection.redis = new Redis(*connection.wifi);
  21. if (connection.redis->authenticate(configuration.password) != RedisSuccess)
  22. {
  23. dprint("Redis auth failed");
  24. delete connection.redis, connection.redis = nullptr;
  25. return false;
  26. }
  27. }
  28. return true;
  29. }
  30. extern int _last_free;
  31. void ZWRedis::checkin(
  32. unsigned long ticks,
  33. const char *localIp,
  34. unsigned long immediateLatency,
  35. unsigned long averageLatency,
  36. int expireMessage)
  37. {
  38. auto rKey = String("rpjios.checkin." + hostname);
  39. const char *key = rKey.c_str();
  40. // TODO: error check!
  41. connection.redis->hset(key, "host", hostname.c_str());
  42. connection.redis->hset(key, "up", String(ticks).c_str());
  43. connection.redis->hset(key, "ver", ZEROWATCH_VER);
  44. #define BL 1024
  45. auto cur_free = ESP.getFreeHeap();
  46. char _ifbuf[BL];
  47. bzero(_ifbuf, BL);
  48. snprintf(_ifbuf, BL,
  49. "{ \"wifi\": { \"address\": \"%s\", \"latency\": "
  50. "{ \"immediate\": %ld, \"rollingAvg\": %ld } },"
  51. " \"mem\": { \"current\": %d, \"last\": %d, \"delta\": %d, \"heap\": %d }"
  52. "}",
  53. localIp, immediateLatency, averageLatency,
  54. cur_free, _last_free, cur_free - _last_free, ESP.getHeapSize());
  55. connection.redis->hset(key, "ifaces", _ifbuf);
  56. connection.redis->expire(key, expireMessage);
  57. }
  58. bool ZWRedis::heartbeat(int expire)
  59. {
  60. REDIS_KEY_CREATE_LOCAL(":heartbeat");
  61. if (!connection.redis->set(redisKey_local, String(micros()).c_str()))
  62. return false;
  63. if (expire)
  64. connection.redis->expire(redisKey_local, expire);
  65. return true;
  66. }
  67. int ZWRedis::incrementBootcount(bool reset)
  68. {
  69. REDIS_KEY_CREATE_LOCAL(":bootcount");
  70. int bcNext = 0;
  71. if (!reset)
  72. {
  73. bcNext = connection.redis->get(redisKey_local).toInt() + 1;
  74. }
  75. if (connection.redis->set(redisKey_local, String(bcNext).c_str()))
  76. {
  77. return bcNext;
  78. }
  79. return -1;
  80. }
  81. ZWAppConfig ZWRedis::readConfig()
  82. {
  83. // TODO: error check!
  84. auto bc = connection.redis->get(REDIS_KEY(":config:brightness"));
  85. auto rc = connection.redis->get(REDIS_KEY(":config:refresh"));
  86. auto dg = connection.redis->get(REDIS_KEY(":config:debug"));
  87. auto pl = connection.redis->get(REDIS_KEY(":config:publishLogs"));
  88. auto pu = connection.redis->get(REDIS_KEY(":config:pauseRefresh"));
  89. _lastReadConfig.brightness = bc.toInt();
  90. _lastReadConfig.refresh = rc.toInt();
  91. _lastReadConfig.debug = (bool)dg.toInt();
  92. _lastReadConfig.publishLogs = (bool)pl.toInt();
  93. _lastReadConfig.pauseRefresh = (bool)pu.toInt();
  94. _lastReadConfig.deepSleepMode = (bool)connection.redis->get(REDIS_KEY(":config:deepSleepMode")).toInt();
  95. return _lastReadConfig;
  96. }
  97. int ZWRedis::updateConfig(ZWAppConfig newConfig)
  98. {
  99. int badCount = 0;
  100. #define UPDATE_CHECK_THEN_SET(field) \
  101. if (_lastReadConfig.field != newConfig.field) \
  102. { \
  103. badCount += !connection.redis->set(REDIS_KEY(":config:" #field), String(newConfig.field).c_str()); \
  104. }
  105. UPDATE_CHECK_THEN_SET(brightness);
  106. UPDATE_CHECK_THEN_SET(refresh);
  107. UPDATE_CHECK_THEN_SET(debug);
  108. UPDATE_CHECK_THEN_SET(publishLogs);
  109. UPDATE_CHECK_THEN_SET(pauseRefresh);
  110. return badCount;
  111. }
  112. bool ZWRedis::handleUserKey(const char *keyPostfix, ZWRedisUserKeyHandler handler)
  113. {
  114. if (!keyPostfix || !handler)
  115. {
  116. zlog("ZWRedis::handleUserKey ERROR arguments\n");
  117. return false;
  118. }
  119. auto getReturn = connection.redis->get(REDIS_KEY(keyPostfix));
  120. if (getReturn && getReturn.length())
  121. {
  122. REDIS_KEY_CREATE_LOCAL(keyPostfix + ":" + getReturn);
  123. ///
  124. // TODO: handle this wierd print on things like 'update'...
  125. // and make sure they never write to keys like that!
  126. // e.g.
  127. // ZWRedis::handleUserKey(ezero) (key=:config:update) has return path 'ezero:config:update:{
  128. // "url": "zero_watch_updates/zero_watch-v0.2.0.4.ino.bin",
  129. // "md5": "eb8a0182161c88328c4888cc64e8a822",
  130. // "size": 924368,
  131. // "otp": 619
  132. // }'"
  133. ///
  134. dprint("ZWRedis::handleUserKey(%s) (key=%s) has return path '%s'\n",
  135. hostname.c_str(), keyPostfix, redisKey_local);
  136. ZWRedisResponder responder(*this, redisKey_local__String_capture);
  137. if (handler(getReturn, responder))
  138. {
  139. return connection.redis->del(REDIS_KEY(keyPostfix));
  140. }
  141. }
  142. return false;
  143. }
  144. void ZWRedis::responderHelper(const char *key, const char *msg, int expire)
  145. {
  146. connection.redis->publish(key, msg);
  147. if (!connection.redis->set(key, msg))
  148. {
  149. zlog("ERROR: ZWRedis::responderHelper() set of %s failed\n", key);
  150. return;
  151. }
  152. if (expire > 0)
  153. {
  154. dprint("ZWRedis::responderHelper expiring %s at %d\n", key, expire);
  155. connection.redis->expire(key, expire);
  156. }
  157. }
  158. void ZWRedis::publishLog(const char *msg)
  159. {
  160. connection.redis->publish(REDIS_KEY(":info:publishLogs"), msg);
  161. }
  162. bool ZWRedis::postCompletedUpdate()
  163. {
  164. return connection.redis->del(REDIS_KEY(":config:update"));
  165. }
  166. std::vector<String> ZWRedis::getRange(const char *key, int start, int stop)
  167. {
  168. return connection.redis->lrange(key, start, stop);
  169. }
  170. bool ZWRedis::clearControlPoint()
  171. {
  172. return connection.redis->del(REDIS_KEY(":config:controlPoint"));
  173. }
  174. bool ZWRedis::registerDevice(const char *registryName, const char *hostname, const char *ident)
  175. {
  176. return connection.redis->hset(registryName, ident, hostname);
  177. }
  178. void ZWRedis::logCritical(const char *format, ...)
  179. {
  180. #define BUFLEN 2048
  181. char _buf[BUFLEN];
  182. bzero(_buf, BUFLEN);
  183. va_list args;
  184. va_start(args, format);
  185. vsnprintf(_buf, BUFLEN, format, args);
  186. va_end(args);
  187. // SHIT! need LPUSH
  188. //connection.redis->lset
  189. // I guess this'll work ok for now...
  190. static unsigned long __keyCount = 0;
  191. connection.redis->hset(REDIS_KEY(":criticalLog"), String(++__keyCount).c_str(), _buf);
  192. }
  193. void ZWRedis::getTime(uint8_t *hour, uint8_t *minute, uint8_t *second)
  194. {
  195. if (hour)
  196. *hour = (uint8_t)connection.redis->hget("rpjios.__meta.time", "hour").toInt();
  197. if (minute)
  198. *minute = (uint8_t)connection.redis->hget("rpjios.__meta.time", "minute").toInt();
  199. if (second)
  200. *second = (uint8_t)connection.redis->hget("rpjios.__meta.time", "second").toInt();
  201. }
  202. void ZWRedisResponder::setValue(const char *format, ...)
  203. {
  204. #define BUFLEN 2048
  205. char _buf[BUFLEN];
  206. bzero(_buf, BUFLEN);
  207. va_list args;
  208. va_start(args, format);
  209. vsnprintf(_buf, BUFLEN, format, args);
  210. va_end(args);
  211. redis.responderHelper(key.c_str(), _buf, expire);
  212. }