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.

236 lines
6.1 KiB

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