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.

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