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.

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