/ firmware / src / services / http / api / networking.cpp
networking.cpp
  1  #include "api.h"
  2  #include <config.h>
  3  #include <networking/wifi.h>
  4  #include <identity.h>
  5  
  6  #include <Arduino.h>
  7  #include <WiFi.h>
  8  #include <AsyncJson.h>
  9  #include <ArduinoJson.h>
 10  
 11  namespace {
 12  
 13  void fill_wireless_status(JsonObject &data) {
 14    NetworkStatusSnapshot snapshot = {};
 15    ::networking::wifi::accessSnapshot(&snapshot);
 16    data["connected"] = snapshot.connected;
 17    data["mode"] = snapshot.ap.active ? "ap_sta" : "sta";
 18    data["tenant"] = config::cloudevents::TENANT;
 19    data["sta_ssid"] = snapshot.ssid;
 20    data["sta_bssid"] = snapshot.bssid;
 21    data["sta_ipv4"] = snapshot.ip;
 22    data["wifi_rssi"] = snapshot.rssi;
 23    data["ap_active"] = snapshot.ap.active;
 24    data["ap_ssid"] = snapshot.ap.ssid;
 25    data["ap_ipv4"] = snapshot.ap.ip;
 26    data["ap_clients"] = snapshot.ap.clients;
 27    data["ap_hostname"] = snapshot.ap.hostname;
 28    data["ap_mac"] = snapshot.ap.mac;
 29  }
 30  
 31  void handle_wifi(AsyncWebServerRequest *request) {
 32    AsyncJsonResponse *response = new AsyncJsonResponse();
 33    JsonObject root = response->getRoot().to<JsonObject>();
 34  
 35    NetworkStatusSnapshot snapshot = {};
 36    ::networking::wifi::accessSnapshot(&snapshot);
 37    root["connected"] = snapshot.connected;
 38    root["ssid"] = snapshot.ssid;
 39    root["ip"] = snapshot.ip;
 40    root["rssi"] = snapshot.rssi;
 41    root["mac"] = snapshot.mac;
 42  
 43    response->setLength();
 44    request->send(response);
 45  }
 46  
 47  void handle_wireless_status(AsyncWebServerRequest *request) {
 48    AsyncJsonResponse *response = new AsyncJsonResponse();
 49    JsonObject root = response->getRoot().to<JsonObject>();
 50    root["ok"] = true;
 51    JsonObject data = root["data"].to<JsonObject>();
 52    fill_wireless_status(data);
 53    response->setLength();
 54    request->send(response);
 55  }
 56  
 57  void handle_wireless_scan(AsyncWebServerRequest *request) {
 58      WifiScanResult results[16] = {};
 59      WifiScanCommand command = {
 60        .results = results,
 61        .max_results = 16,
 62        .result_count = -1,
 63      };
 64      ::networking::wifi::scan(&command);
 65      int16_t count = command.result_count;
 66  
 67    AsyncJsonResponse *response = new AsyncJsonResponse();
 68    JsonObject root = response->getRoot().to<JsonObject>();
 69    root["ok"] = (count >= 0);
 70    JsonObject data = root["data"].to<JsonObject>();
 71    data["scan_count"] = (count >= 0) ? count : 0;
 72  
 73    JsonArray networks = data["networks"].to<JsonArray>();
 74    int16_t limit = (count < 16) ? count : 16;
 75    for (int16_t index = 0; index < limit; index++) {
 76      JsonObject network = networks.add<JsonObject>();
 77      network["ssid"] = results[index].ssid;
 78      network["bssid"] = results[index].bssid;
 79      network["rssi"] = results[index].rssi;
 80      network["channel"] = results[index].channel;
 81      network["encryption"] = results[index].encryption;
 82      network["open"] = results[index].open;
 83    }
 84    response->setLength();
 85    request->send(response);
 86  }
 87  
 88  void handle_ap_config_get(AsyncWebServerRequest *request) {
 89    APSnapshot snapshot = {};
 90    ::networking::wifi::ap::accessSnapshot(&snapshot);
 91  
 92    AsyncJsonResponse *response = new AsyncJsonResponse();
 93    JsonObject root = response->getRoot().to<JsonObject>();
 94    root["ok"] = true;
 95    JsonObject data = root["data"].to<JsonObject>();
 96    data["ssid"] = snapshot.ssid;
 97    data["enabled"] = snapshot.active;
 98    data["active"] = snapshot.active;
 99    data["ip"] = snapshot.ip;
100    data["clients"] = snapshot.clients;
101    data["hostname"] = snapshot.hostname;
102    data["mac"] = snapshot.mac;
103    response->setLength();
104    request->send(response);
105  }
106  
107  }
108  
109  void services::http::api::networking::registerRoutes(AsyncWebServer &server,
110                                                       AsyncRateLimitMiddleware &scan_limit) {
111    server.on("/api/wifi", HTTP_GET, handle_wifi);
112    server.on("/api/wireless/status", HTTP_GET, handle_wireless_status);
113    server.on("/api/wireless/actions/scan", HTTP_POST, handle_wireless_scan)
114      .addMiddleware(&scan_limit);
115    server.on("/api/ap/config", HTTP_GET, handle_ap_config_get);
116  
117    AsyncCallbackJsonWebHandler &ap_config_handler =
118        server.on("/api/ap/config", HTTP_POST,
119            [](AsyncWebServerRequest *request, JsonVariant &json) {
120      JsonObject body = json.as<JsonObject>();
121  
122      if (!body["ssid"].isNull() && !body["password"].isNull()) {
123        APConfigureCommand command = {
124          .config = {},
125          .snapshot = {},
126        };
127        strlcpy(command.config.ssid, body["ssid"] | "", sizeof(command.config.ssid));
128        strlcpy(command.config.password, body["password"] | "", sizeof(command.config.password));
129        ::networking::wifi::ap::applyConfig(&command);
130      }
131  
132      if (!body["enabled"].isNull()) {
133        APEnabledCommand command = {
134          .enabled = body["enabled"] | true,
135          .snapshot = {},
136        };
137        ::networking::wifi::ap::setEnabled(&command);
138      }
139  
140      APSnapshot snapshot = {};
141      ::networking::wifi::ap::accessSnapshot(&snapshot);
142  
143      AsyncJsonResponse *response = new AsyncJsonResponse();
144      JsonObject root = response->getRoot().to<JsonObject>();
145      root["ok"] = true;
146      JsonObject data = root["data"].to<JsonObject>();
147      data["ssid"] = snapshot.ssid;
148      data["enabled"] = snapshot.active;
149      data["active"] = snapshot.active;
150      data["ip"] = snapshot.ip;
151      data["clients"] = snapshot.clients;
152      response->setLength();
153      request->send(response);
154    });
155    ap_config_handler.setMaxContentLength(512);
156  
157    AsyncCallbackJsonWebHandler &connect_handler =
158        server.on("/api/wireless/actions/connect", HTTP_POST,
159            [](AsyncWebServerRequest *request, JsonVariant &json) {
160      JsonObject body = json.as<JsonObject>();
161      String ssid = body["ssid"] | "";
162      String password = body["password"] | "";
163      ssid.trim();
164  
165      if (ssid.isEmpty()) {
166        request->send(400, asyncsrv::T_application_json,
167                      "{\"ok\":false,\"error\":{\"code\":\"SSID_REQUIRED\","
168                      "\"message\":\"Missing ssid parameter\"}}");
169        return;
170      }
171  
172      WifiConnectCommand command = {
173        .request = {
174          .ssid = ssid.c_str(),
175          .password = password.c_str(),
176          .enable_ap_fallback = true,
177        },
178        .result = {},
179      };
180      ::networking::wifi::connect(&command);
181  
182      AsyncJsonResponse *response = new AsyncJsonResponse();
183      JsonObject root = response->getRoot().to<JsonObject>();
184      root["ok"] = command.result.connected;
185      JsonObject data = root["data"].to<JsonObject>();
186      data["attempted_ssid"] = ssid;
187      data["status_code"] = command.result.status_code;
188      fill_wireless_status(data);
189  
190      response->setLength();
191      request->send(response);
192    });
193    connect_handler.setMaxContentLength(512);
194  }