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.

202 lines
5.5 KiB

6 years ago
6 years ago
  1. /*
  2. API MODULE
  3. Copyright (C) 2016-2018 by Xose Pérez <xose dot perez at gmail dot com>
  4. */
  5. #if WEB_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["apiEnabled"] = getSetting("apiEnabled", API_ENABLED).toInt() == 1;
  22. root["apiKey"] = getSetting("apiKey");
  23. root["apiRealTime"] = getSetting("apiRealTime", API_REAL_TIME_VALUES).toInt() == 1;
  24. }
  25. // -----------------------------------------------------------------------------
  26. // API
  27. // -----------------------------------------------------------------------------
  28. bool _authAPI(AsyncWebServerRequest *request) {
  29. if (getSetting("apiEnabled", API_ENABLED).toInt() == 0) {
  30. DEBUG_MSG_P(PSTR("[WEBSERVER] HTTP API is not enabled\n"));
  31. request->send(403);
  32. return false;
  33. }
  34. if (!request->hasParam("apikey", (request->method() == HTTP_PUT))) {
  35. DEBUG_MSG_P(PSTR("[WEBSERVER] Missing apikey parameter\n"));
  36. request->send(403);
  37. return false;
  38. }
  39. AsyncWebParameter* p = request->getParam("apikey", (request->method() == HTTP_PUT));
  40. if (!p->value().equals(getSetting("apiKey"))) {
  41. DEBUG_MSG_P(PSTR("[WEBSERVER] Wrong apikey parameter\n"));
  42. request->send(403);
  43. return false;
  44. }
  45. return true;
  46. }
  47. bool _asJson(AsyncWebServerRequest *request) {
  48. bool asJson = false;
  49. if (request->hasHeader("Accept")) {
  50. AsyncWebHeader* h = request->getHeader("Accept");
  51. asJson = h->value().equals("application/json");
  52. }
  53. return asJson;
  54. }
  55. ArRequestHandlerFunction _bindAPI(unsigned int apiID) {
  56. return [apiID](AsyncWebServerRequest *request) {
  57. webLog(request);
  58. if (!_authAPI(request)) return;
  59. web_api_t api = _apis[apiID];
  60. // Check if its a PUT
  61. if (api.putFn != NULL) {
  62. if (request->hasParam("value", request->method() == HTTP_PUT)) {
  63. AsyncWebParameter* p = request->getParam("value", request->method() == HTTP_PUT);
  64. (api.putFn)((p->value()).c_str());
  65. }
  66. }
  67. // Get response from callback
  68. char value[API_BUFFER_SIZE] = {0};
  69. (api.getFn)(value, API_BUFFER_SIZE);
  70. // The response will be a 404 NOT FOUND if the resource is not available
  71. if (0 == value[0]) {
  72. DEBUG_MSG_P(PSTR("[API] Sending 404 response\n"));
  73. request->send(404);
  74. return;
  75. }
  76. DEBUG_MSG_P(PSTR("[API] Sending response '%s'\n"), value);
  77. // Format response according to the Accept header
  78. if (_asJson(request)) {
  79. char buffer[64];
  80. if (isNumber(value)) {
  81. snprintf_P(buffer, sizeof(buffer), PSTR("{ \"%s\": %s }"), api.key, value);
  82. } else {
  83. snprintf_P(buffer, sizeof(buffer), PSTR("{ \"%s\": \"%s\" }"), api.key, value);
  84. }
  85. request->send(200, "application/json", buffer);
  86. } else {
  87. request->send(200, "text/plain", value);
  88. }
  89. };
  90. }
  91. void _onAPIs(AsyncWebServerRequest *request) {
  92. webLog(request);
  93. if (!_authAPI(request)) return;
  94. bool asJson = _asJson(request);
  95. char buffer[40];
  96. String output;
  97. if (asJson) {
  98. DynamicJsonBuffer jsonBuffer;
  99. JsonObject& root = jsonBuffer.createObject();
  100. for (unsigned int i=0; i < _apis.size(); i++) {
  101. snprintf_P(buffer, sizeof(buffer), PSTR("/api/%s"), _apis[i].key);
  102. root[_apis[i].key] = String(buffer);
  103. }
  104. root.printTo(output);
  105. jsonBuffer.clear();
  106. request->send(200, "application/json", output);
  107. } else {
  108. for (unsigned int i=0; i < _apis.size(); i++) {
  109. snprintf_P(buffer, sizeof(buffer), PSTR("/api/%s"), _apis[i].key);
  110. output += _apis[i].key + String(" -> ") + String(buffer) + String("\n");
  111. }
  112. request->send(200, "text/plain", output);
  113. }
  114. }
  115. void _onRPC(AsyncWebServerRequest *request) {
  116. webLog(request);
  117. if (!_authAPI(request)) return;
  118. //bool asJson = _asJson(request);
  119. int response = 404;
  120. if (request->hasParam("action")) {
  121. AsyncWebParameter* p = request->getParam("action");
  122. String action = p->value();
  123. DEBUG_MSG_P(PSTR("[RPC] Action: %s\n"), action.c_str());
  124. if (action.equals("reboot")) {
  125. response = 200;
  126. deferredReset(100, CUSTOM_RESET_RPC);
  127. }
  128. }
  129. request->send(response);
  130. }
  131. // -----------------------------------------------------------------------------
  132. void apiRegister(const char * key, api_get_callback_f getFn, api_put_callback_f putFn) {
  133. // Store it
  134. web_api_t api;
  135. char buffer[40];
  136. snprintf_P(buffer, sizeof(buffer), PSTR("/api/%s"), key);
  137. api.key = strdup(key);
  138. api.getFn = getFn;
  139. api.putFn = putFn;
  140. _apis.push_back(api);
  141. // Bind call
  142. unsigned int methods = HTTP_GET;
  143. if (putFn != NULL) methods += HTTP_PUT;
  144. webServer()->on(buffer, methods, _bindAPI(_apis.size() - 1));
  145. }
  146. void apiSetup() {
  147. webServer()->on("/apis", HTTP_GET, _onAPIs);
  148. webServer()->on("/rpc", HTTP_GET, _onRPC);
  149. wsOnSendRegister(_apiWebSocketOnSend);
  150. wsOnReceiveRegister(_apiWebSocketOnReceive);
  151. }
  152. #endif // WEB_SUPPORT