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.

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