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.

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