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.

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