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.

192 lines
5.2 KiB

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