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.

256 lines
6.7 KiB

  1. /*
  2. API MODULE
  3. Copyright (C) 2016-2019 by Xose Pérez <xose dot perez at gmail dot com>
  4. */
  5. #if API_SUPPORT
  6. #include <ESPAsyncTCP.h>
  7. #include <ESPAsyncWebServer.h>
  8. #include <ArduinoJson.h>
  9. #include <vector>
  10. typedef struct {
  11. char * key;
  12. api_get_callback_f getFn = NULL;
  13. api_put_callback_f putFn = NULL;
  14. } web_api_t;
  15. std::vector<web_api_t> _apis;
  16. // -----------------------------------------------------------------------------
  17. bool _apiWebSocketOnKeyCheck(const char * key, JsonVariant& value) {
  18. return (strncmp(key, "api", 3) == 0);
  19. }
  20. void _apiWebSocketOnConnected(JsonObject& root) {
  21. root["apiEnabled"] = getSetting("apiEnabled", API_ENABLED).toInt() == 1;
  22. root["apiKey"] = getSetting("apiKey");
  23. root["apiRealTime"] = getSetting("apiRealTime", API_REAL_TIME_VALUES).toInt() == 1;
  24. root["apiRestFul"] = getSetting("apiRestFul", API_RESTFUL).toInt() == 1;
  25. }
  26. void _apiConfigure() {
  27. // Nothing to do
  28. }
  29. // -----------------------------------------------------------------------------
  30. // API
  31. // -----------------------------------------------------------------------------
  32. bool _authAPI(AsyncWebServerRequest *request) {
  33. if (getSetting("apiEnabled", API_ENABLED).toInt() == 0) {
  34. DEBUG_MSG_P(PSTR("[WEBSERVER] HTTP API is not enabled\n"));
  35. request->send(403);
  36. return false;
  37. }
  38. if (!request->hasParam("apikey", (request->method() == HTTP_PUT))) {
  39. DEBUG_MSG_P(PSTR("[WEBSERVER] Missing apikey parameter\n"));
  40. request->send(403);
  41. return false;
  42. }
  43. AsyncWebParameter* p = request->getParam("apikey", (request->method() == HTTP_PUT));
  44. if (!p->value().equals(getSetting("apiKey"))) {
  45. DEBUG_MSG_P(PSTR("[WEBSERVER] Wrong apikey parameter\n"));
  46. request->send(403);
  47. return false;
  48. }
  49. return true;
  50. }
  51. bool _asJson(AsyncWebServerRequest *request) {
  52. bool asJson = false;
  53. if (request->hasHeader("Accept")) {
  54. AsyncWebHeader* h = request->getHeader("Accept");
  55. asJson = h->value().equals("application/json");
  56. }
  57. return asJson;
  58. }
  59. void _onAPIsText(AsyncWebServerRequest *request) {
  60. AsyncResponseStream *response = request->beginResponseStream("text/plain");
  61. String output;
  62. output.reserve(48);
  63. for (unsigned int i=0; i < _apis.size(); i++) {
  64. output = "";
  65. output += _apis[i].key;
  66. output += " -> ";
  67. output += "/api/";
  68. output += _apis[i].key;
  69. output += '\n';
  70. response->write(output.c_str());
  71. }
  72. request->send(response);
  73. }
  74. constexpr const size_t API_JSON_BUFFER_SIZE = 1024;
  75. void _onAPIsJson(AsyncWebServerRequest *request) {
  76. DynamicJsonBuffer jsonBuffer(API_JSON_BUFFER_SIZE);
  77. JsonObject& root = jsonBuffer.createObject();
  78. constexpr const int BUFFER_SIZE = 48;
  79. for (unsigned int i=0; i < _apis.size(); i++) {
  80. char buffer[BUFFER_SIZE] = {0};
  81. int res = snprintf(buffer, sizeof(buffer), "/api/%s", _apis[i].key);
  82. if ((res < 0) || (res > (BUFFER_SIZE - 1))) {
  83. request->send(500);
  84. return;
  85. }
  86. root[_apis[i].key] = buffer;
  87. }
  88. AsyncResponseStream *response = request->beginResponseStream("application/json");
  89. root.printTo(*response);
  90. request->send(response);
  91. }
  92. void _onAPIs(AsyncWebServerRequest *request) {
  93. webLog(request);
  94. if (!_authAPI(request)) return;
  95. bool asJson = _asJson(request);
  96. String output;
  97. if (asJson) {
  98. _onAPIsJson(request);
  99. } else {
  100. _onAPIsText(request);
  101. }
  102. }
  103. void _onRPC(AsyncWebServerRequest *request) {
  104. webLog(request);
  105. if (!_authAPI(request)) return;
  106. //bool asJson = _asJson(request);
  107. int response = 404;
  108. if (request->hasParam("action")) {
  109. AsyncWebParameter* p = request->getParam("action");
  110. String action = p->value();
  111. DEBUG_MSG_P(PSTR("[RPC] Action: %s\n"), action.c_str());
  112. if (action.equals("reboot")) {
  113. response = 200;
  114. deferredReset(100, CUSTOM_RESET_RPC);
  115. }
  116. }
  117. request->send(response);
  118. }
  119. bool _apiRequestCallback(AsyncWebServerRequest *request) {
  120. String url = request->url();
  121. // Main API entry point
  122. if (url.equals("/api") || url.equals("/apis")) {
  123. _onAPIs(request);
  124. return true;
  125. }
  126. // Main RPC entry point
  127. if (url.equals("/rpc")) {
  128. _onRPC(request);
  129. return true;
  130. }
  131. // Not API request
  132. if (!url.startsWith("/api/")) return false;
  133. for (unsigned char i=0; i < _apis.size(); i++) {
  134. // Search API url
  135. web_api_t api = _apis[i];
  136. if (!url.endsWith(api.key)) continue;
  137. // Log and check credentials
  138. webLog(request);
  139. if (!_authAPI(request)) return false;
  140. // Check if its a PUT
  141. if (api.putFn != NULL) {
  142. if ((getSetting("apiRestFul", API_RESTFUL).toInt() != 1) || (request->method() == HTTP_PUT)) {
  143. if (request->hasParam("value", request->method() == HTTP_PUT)) {
  144. AsyncWebParameter* p = request->getParam("value", request->method() == HTTP_PUT);
  145. (api.putFn)((p->value()).c_str());
  146. }
  147. }
  148. }
  149. // Get response from callback
  150. char value[API_BUFFER_SIZE] = {0};
  151. (api.getFn)(value, API_BUFFER_SIZE);
  152. // The response will be a 404 NOT FOUND if the resource is not available
  153. if (0 == value[0]) {
  154. DEBUG_MSG_P(PSTR("[API] Sending 404 response\n"));
  155. request->send(404);
  156. return false;
  157. }
  158. DEBUG_MSG_P(PSTR("[API] Sending response '%s'\n"), value);
  159. // Format response according to the Accept header
  160. if (_asJson(request)) {
  161. char buffer[64];
  162. if (isNumber(value)) {
  163. snprintf_P(buffer, sizeof(buffer), PSTR("{ \"%s\": %s }"), api.key, value);
  164. } else {
  165. snprintf_P(buffer, sizeof(buffer), PSTR("{ \"%s\": \"%s\" }"), api.key, value);
  166. }
  167. request->send(200, "application/json", buffer);
  168. } else {
  169. request->send(200, "text/plain", value);
  170. }
  171. return true;
  172. }
  173. return false;
  174. }
  175. // -----------------------------------------------------------------------------
  176. void apiRegister(const char * key, api_get_callback_f getFn, api_put_callback_f putFn) {
  177. // Store it
  178. web_api_t api;
  179. api.key = strdup(key);
  180. api.getFn = getFn;
  181. api.putFn = putFn;
  182. _apis.push_back(api);
  183. }
  184. void apiSetup() {
  185. _apiConfigure();
  186. wsRegister()
  187. .onVisible([](JsonObject& root) { root["apiVisible"] = 1; })
  188. .onConnected(_apiWebSocketOnConnected)
  189. .onKeyCheck(_apiWebSocketOnKeyCheck);
  190. webRequestRegister(_apiRequestCallback);
  191. espurnaRegisterReload(_apiConfigure);
  192. }
  193. #endif // API_SUPPORT