Fork of the espurna firmware for `mhsw` switches
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.

315 lines
8.5 KiB

5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
  1. /*
  2. RPN RULES MODULE
  3. Use RPNLib library (https://github.com/xoseperez/rpnlib)
  4. Copyright (C) 2019 by Xose Pérez <xose dot perez at gmail dot com>
  5. */
  6. #if RPN_RULES_SUPPORT
  7. #include "rpnlib.h"
  8. // -----------------------------------------------------------------------------
  9. // Custom commands
  10. // -----------------------------------------------------------------------------
  11. rpn_context _rpn_ctxt;
  12. bool _rpn_run = false;
  13. unsigned long _rpn_delay = RPN_DELAY;
  14. unsigned long _rpn_last = 0;
  15. // -----------------------------------------------------------------------------
  16. bool _rpnWebSocketOnKeyCheck(const char * key, JsonVariant& value) {
  17. return (strncmp(key, "rpn", 3) == 0);
  18. }
  19. void _rpnWebSocketOnConnected(JsonObject& root) {
  20. root["rpnSticky"] = getSetting("rpnSticky", 1).toInt();
  21. root["rpnDelay"] = getSetting("rpnDelay", RPN_DELAY).toInt();
  22. JsonArray& rules = root.createNestedArray("rpnRules");
  23. unsigned char i = 0;
  24. String rule = getSetting("rpnRule", i, "");
  25. while (rule.length()) {
  26. rules.add(rule);
  27. rule = getSetting("rpnRule", ++i, "");
  28. }
  29. #if MQTT_SUPPORT
  30. i=0;
  31. JsonArray& topics = root.createNestedArray("rpnTopics");
  32. JsonArray& names = root.createNestedArray("rpnNames");
  33. String rpn_topic = getSetting("rpnTopic", i, "");
  34. while (rpn_topic.length() > 0) {
  35. String rpn_name = getSetting("rpnName", i, "");
  36. topics.add(rpn_topic);
  37. names.add(rpn_name);
  38. rpn_topic = getSetting("rpnTopic", ++i, "");
  39. }
  40. #endif
  41. }
  42. #if MQTT_SUPPORT
  43. void _rpnMQTTSubscribe() {
  44. unsigned char i = 0;
  45. String rpn_topic = getSetting("rpnTopic", i, "");
  46. while (rpn_topic.length()) {
  47. mqttSubscribeRaw(rpn_topic.c_str());
  48. rpn_topic = getSetting("rpnTopic", ++i, "");
  49. }
  50. }
  51. void _rpnMQTTCallback(unsigned int type, const char * topic, const char * payload) {
  52. if (type == MQTT_CONNECT_EVENT) {
  53. _rpnMQTTSubscribe();
  54. }
  55. if (type == MQTT_MESSAGE_EVENT) {
  56. unsigned char i = 0;
  57. String rpn_topic = getSetting("rpnTopic", i, "");
  58. while (rpn_topic.length()) {
  59. if (rpn_topic.equals(topic)) {
  60. String rpn_name = getSetting("rpnName", i, "");
  61. if (rpn_name.length()) {
  62. rpn_variable_set(_rpn_ctxt, rpn_name.c_str(), atof(payload));
  63. _rpn_last = millis();
  64. _rpn_run = true;
  65. break;
  66. }
  67. }
  68. rpn_topic = getSetting("rpnTopic", ++i, "");
  69. }
  70. }
  71. }
  72. #endif // MQTT_SUPPORT
  73. void _rpnConfigure() {
  74. #if MQTT_SUPPORT
  75. if (mqttConnected()) _rpnMQTTSubscribe();
  76. #endif
  77. _rpn_delay = getSetting("rpnDelay", RPN_DELAY).toInt();
  78. }
  79. void _rpnBrokerCallback(const unsigned char type, const char * topic, unsigned char id, const char * payload) {
  80. char name[32] = {0};
  81. if (BROKER_MSG_TYPE_STATUS == type || BROKER_MSG_TYPE_SENSOR == type) {
  82. snprintf(name, sizeof(name), "%s%d", topic, id);
  83. rpn_variable_set(_rpn_ctxt, name, atof(payload));
  84. } else if (BROKER_MSG_TYPE_DATETIME == type) {
  85. // Timestamp is always available via de "now" operator
  86. } else {
  87. return;
  88. }
  89. _rpn_last = millis();
  90. _rpn_run = true;
  91. }
  92. void _rpnInit() {
  93. // Init context
  94. rpn_init(_rpn_ctxt);
  95. // Time functions
  96. rpn_operator_set(_rpn_ctxt, "now", 0, [](rpn_context & ctxt) {
  97. if (!ntpSynced()) return false;
  98. rpn_stack_push(ctxt, now());
  99. return true;
  100. });
  101. rpn_operator_set(_rpn_ctxt, "utc", 0, [](rpn_context & ctxt) {
  102. if (!ntpSynced()) return false;
  103. rpn_stack_push(ctxt, ntpLocal2UTC(now()));
  104. return true;
  105. });
  106. rpn_operator_set(_rpn_ctxt, "dow", 1, [](rpn_context & ctxt) {
  107. float a;
  108. rpn_stack_pop(ctxt, a);
  109. unsigned char dow = (weekday(int(a)) + 5) % 7;
  110. rpn_stack_push(ctxt, dow);
  111. return true;
  112. });
  113. rpn_operator_set(_rpn_ctxt, "hour", 1, [](rpn_context & ctxt) {
  114. float a;
  115. rpn_stack_pop(ctxt, a);
  116. rpn_stack_push(ctxt, hour(int(a)));
  117. return true;
  118. });
  119. rpn_operator_set(_rpn_ctxt, "minute", 1, [](rpn_context & ctxt) {
  120. float a;
  121. rpn_stack_pop(ctxt, a);
  122. rpn_stack_push(ctxt, minute(int(a)));
  123. return true;
  124. });
  125. // Debug
  126. rpn_operator_set(_rpn_ctxt, "debug", 0, [](rpn_context & ctxt) {
  127. _rpnDump();
  128. return true;
  129. });
  130. // Relay operators
  131. rpn_operator_set(_rpn_ctxt, "relay", 2, [](rpn_context & ctxt) {
  132. float a, b;
  133. rpn_stack_pop(ctxt, b); // relay number
  134. rpn_stack_pop(ctxt, a); // new status
  135. if (int(a) == 2) {
  136. relayToggle(int(b));
  137. } else {
  138. relayStatus(int(b), int(a) == 1);
  139. }
  140. return true;
  141. });
  142. // Channel operators
  143. #if RELAY_PROVIDER == RELAY_PROVIDER_LIGHT
  144. rpn_operator_set(_rpn_ctxt, "update", 0, [](rpn_context & ctxt) {
  145. lightUpdate(true, true);
  146. return true;
  147. });
  148. rpn_operator_set(_rpn_ctxt, "black", 0, [](rpn_context & ctxt) {
  149. lightColor((unsigned long) 0);
  150. return true;
  151. });
  152. rpn_operator_set(_rpn_ctxt, "channel", 2, [](rpn_context & ctxt) {
  153. float a, b;
  154. rpn_stack_pop(ctxt, b); // channel number
  155. rpn_stack_pop(ctxt, a); // new value
  156. lightChannel(int(b), int(a));
  157. return true;
  158. });
  159. #endif
  160. }
  161. #if TERMINAL_SUPPORT
  162. void _rpnInitCommands() {
  163. terminalRegisterCommand(F("RPN.VARS"), [](Embedis* e) {
  164. unsigned char num = rpn_variables_size(_rpn_ctxt);
  165. if (0 == num) {
  166. DEBUG_MSG_P(PSTR("[RPN] No variables\n"));
  167. } else {
  168. DEBUG_MSG_P(PSTR("[RPN] Variables:\n"));
  169. for (unsigned char i=0; i<num; i++) {
  170. char * name = rpn_variable_name(_rpn_ctxt, i);
  171. float value;
  172. rpn_variable_get(_rpn_ctxt, name, value);
  173. DEBUG_MSG_P(PSTR(" %s: %s\n"), name, String(value).c_str());
  174. }
  175. }
  176. terminalOK();
  177. });
  178. terminalRegisterCommand(F("RPN.OPS"), [](Embedis* e) {
  179. unsigned char num = _rpn_ctxt.operators.size();
  180. DEBUG_MSG_P(PSTR("[RPN] Operators:\n"));
  181. for (unsigned char i=0; i<num; i++) {
  182. DEBUG_MSG_P(PSTR(" %s (%d)\n"), _rpn_ctxt.operators[i].name, _rpn_ctxt.operators[i].argc);
  183. }
  184. terminalOK();
  185. });
  186. terminalRegisterCommand(F("RPN.TEST"), [](Embedis* e) {
  187. if (e->argc == 2) {
  188. DEBUG_MSG_P(PSTR("[RPN] Running \"%s\"\n"), e->argv[1]);
  189. rpn_process(_rpn_ctxt, e->argv[1], true);
  190. _rpnDump();
  191. rpn_stack_clear(_rpn_ctxt);
  192. terminalOK();
  193. } else {
  194. terminalError(F("Wrong arguments"));
  195. }
  196. });
  197. }
  198. #endif
  199. void _rpnDump() {
  200. float value;
  201. DEBUG_MSG_P(PSTR("[RPN] Stack:\n"));
  202. unsigned char num = rpn_stack_size(_rpn_ctxt);
  203. if (0 == num) {
  204. DEBUG_MSG_P(PSTR(" (empty)\n"));
  205. } else {
  206. unsigned char index = num - 1;
  207. while (rpn_stack_get(_rpn_ctxt, index, value)) {
  208. DEBUG_MSG_P(PSTR(" %02d: %s\n"), index--, String(value).c_str());
  209. }
  210. }
  211. }
  212. void _rpnRun() {
  213. unsigned char i = 0;
  214. String rule = getSetting("rpnRule", i, "");
  215. while (rule.length()) {
  216. //DEBUG_MSG_P(PSTR("[RPN] Running \"%s\"\n"), rule.c_str());
  217. rpn_process(_rpn_ctxt, rule.c_str(), true);
  218. //_rpnDump();
  219. rule = getSetting("rpnRule", ++i, "");
  220. rpn_stack_clear(_rpn_ctxt);
  221. }
  222. if (getSetting("rpnSticky", 1).toInt() == 0) {
  223. rpn_variables_clear(_rpn_ctxt);
  224. }
  225. }
  226. void _rpnLoop() {
  227. if (_rpn_run && (millis() - _rpn_last > _rpn_delay)) {
  228. _rpnRun();
  229. _rpn_run = false;
  230. }
  231. }
  232. void rpnSetup() {
  233. // Init context
  234. _rpnInit();
  235. // Load & cache settings
  236. _rpnConfigure();
  237. // Terminal commands
  238. #if TERMINAL_SUPPORT
  239. _rpnInitCommands();
  240. #endif
  241. // Websockets
  242. #if WEB_SUPPORT
  243. wsRegister()
  244. .onVisible([](JsonObject& root) { root["rpnVisible"] = 1; })
  245. .onConnected(_rpnWebSocketOnConnected)
  246. .onKeyCheck(_rpnWebSocketOnKeyCheck);
  247. #endif
  248. // MQTT
  249. #if MQTT_SUPPORT
  250. mqttRegister(_rpnMQTTCallback);
  251. #endif
  252. brokerRegister(_rpnBrokerCallback);
  253. espurnaRegisterReload(_rpnConfigure);
  254. espurnaRegisterLoop(_rpnLoop);
  255. }
  256. #endif // RPN_RULES_SUPPORT