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.

190 lines
5.1 KiB

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