Просмотр исходного кода

add ability to run multiple UDP servers

Chris Mullins лет назад: 8
Родитель
Сommit
f40c3b5d8c

+ 8 - 0
lib/MiLight/MiLightUdpServer.cpp

@@ -7,10 +7,18 @@ MiLightUdpServer::MiLightUdpServer(MiLightClient*& client, uint16_t port, uint16
     lastGroup(0)
 { }
 
+MiLightUdpServer::~MiLightUdpServer() {
+  stop();
+}
+
 void MiLightUdpServer::begin() {
   socket.begin(this->port);
 }
 
+void MiLightUdpServer::stop() {
+  socket.stop();
+}
+
 void MiLightUdpServer::handleClient() {
   const size_t packetSize = socket.parsePacket();
   

+ 2 - 0
lib/MiLight/MiLightUdpServer.h

@@ -39,7 +39,9 @@ enum MiLightUdpCommands {
 class MiLightUdpServer {
 public:
   MiLightUdpServer(MiLightClient*& client, uint16_t port, uint16_t deviceId);
+  ~MiLightUdpServer();
     
+  void stop();
   void begin();
   void handleClient();
     

+ 43 - 0
lib/Settings/Settings.cpp

@@ -1,6 +1,7 @@
 #include <Settings.h>
 #include <ArduinoJson.h>
 #include <FS.h>
+#include <IntParsing.h>
 
 void Settings::deserialize(Settings& settings, String json) {
   DynamicJsonBuffer jsonBuffer;
@@ -17,6 +18,9 @@ void Settings::deserialize(Settings& settings, JsonObject& parsedSettings) {
     
     JsonArray& arr = parsedSettings["device_ids"];
     settings.updateDeviceIds(arr);
+    
+    JsonArray& gatewayArr = parsedSettings["gateway_configs"];
+    settings.updateGatewayConfigs(gatewayArr);
   }
 }
 
@@ -32,6 +36,28 @@ void Settings::updateDeviceIds(JsonArray& arr) {
   }
 }
 
+void Settings::updateGatewayConfigs(JsonArray& arr) {
+  if (arr.success()) {
+    if (this->gatewayConfigs) {
+      delete[] this->gatewayConfigs;
+    }
+    
+    this->gatewayConfigs = new GatewayConfig*[arr.size()];
+    this->numGatewayConfigs = arr.size();
+    
+    for (size_t i = 0; i < arr.size(); i++) {
+      JsonArray& params = arr[i];
+      
+      if (params.success() && params.size() == 3) {
+        this->gatewayConfigs[i] = new GatewayConfig(parseInt<uint16_t>(params[0]), params[1], params[2]);
+      } else {
+        Serial.print("Settings - skipped parsing gateway ports settings for element #");
+        Serial.println(i);
+      }
+    }
+  }
+}
+
 void Settings::patch(JsonObject& parsedSettings) {
   if (parsedSettings.success()) {
     if (parsedSettings.containsKey("admin_username")) {
@@ -50,6 +76,10 @@ void Settings::patch(JsonObject& parsedSettings) {
       JsonArray& arr = parsedSettings["device_ids"];
       updateDeviceIds(arr);
     }
+    if (parsedSettings.containsKey("gateway_configs")) {
+      JsonArray& arr = parsedSettings["gateway_configs"];
+      updateGatewayConfigs(arr);
+    }
   }
 }
 
@@ -98,6 +128,19 @@ void Settings::serialize(Stream& stream, const bool prettyPrint) {
     root["device_ids"] = arr;
   }
   
+  if (this->gatewayConfigs) {
+    JsonArray& arr = jsonBuffer.createArray();
+    for (size_t i = 0; i < this->numGatewayConfigs; i++) {
+      JsonArray& elmt = jsonBuffer.createArray();
+      elmt.add(this->gatewayConfigs[i]->deviceId);
+      elmt.add(this->gatewayConfigs[i]->port);
+      elmt.add(this->gatewayConfigs[i]->protocolVersion);
+      arr.add(elmt);
+    }
+    
+    root["gateway_configs"] = arr;
+  }
+  
   if (prettyPrint) {
     root.prettyPrintTo(stream);
   } else {

+ 19 - 1
lib/Settings/Settings.h

@@ -10,6 +10,19 @@
 
 #define WEB_INDEX_FILENAME "/index.html"
 
+class GatewayConfig {
+public:
+  GatewayConfig(uint16_t deviceId, uint16_t port, uint8_t protocolVersion) 
+    : deviceId(deviceId),
+      port(port),
+      protocolVersion(protocolVersion)
+    { }
+  
+  const uint16_t deviceId;
+  const uint16_t port;
+  const uint8_t protocolVersion;
+};
+
 class Settings {
 public:
   Settings() :
@@ -19,7 +32,9 @@ public:
     cePin(D0),
     csnPin(D8),
     deviceIds(NULL),
-    numDeviceIds(0)
+    gatewayConfigs(NULL),
+    numDeviceIds(0),
+    numGatewayConfigs(0)
   { }
   
   ~Settings() {
@@ -40,6 +55,7 @@ public:
   String toJson(const bool prettyPrint = true);
   void serialize(Stream& stream, const bool prettyPrint = false);
   void updateDeviceIds(JsonArray& arr);
+  void updateGatewayConfigs(JsonArray& arr);
   void patch(JsonObject& obj);
   
   String adminUsername;
@@ -47,6 +63,8 @@ public:
   uint8_t cePin;
   uint8_t csnPin;
   uint16_t *deviceIds;
+  GatewayConfig **gatewayConfigs;
+  size_t numGatewayConfigs;
   size_t numDeviceIds;
 };
 

+ 0 - 2
lib/WebServer/WebServer.cpp

@@ -18,8 +18,6 @@ void WebServer::disableAuthentication() {
 void WebServer::_handleRequest() {
   if (this->authEnabled 
     && !this->authenticate(this->username.c_str(), this->password.c_str())) {
-      Serial.println(this->username);
-      Serial.println(this->password);
     this->requestAuthentication();
   } else {
     ESP8266WebServer::_handleRequest();

+ 38 - 0
src/main.cpp

@@ -10,6 +10,8 @@
 #include <MiLightUdpServer.h>
 
 MiLightClient* milightClient;
+MiLightUdpServer** udpServers;
+int numUdpServers = 0;
 WiFiManager wifiManager;
 WebServer *server = new WebServer(80);
 File updateFile;
@@ -157,6 +159,33 @@ void onWebUpdated() {
   server->send(200, "text/plain", "success");
 }
 
+void initMilightUdpServers() {
+  if (udpServers) {
+    for (int i = 0; i < numUdpServers; i++) {
+      if (udpServers[i]) {
+        delete udpServers[i];
+      }
+    }
+    
+    delete udpServers;
+  }
+  
+  udpServers = new MiLightUdpServer*[settings.numGatewayConfigs];
+  numUdpServers = settings.numGatewayConfigs;
+  
+  for (size_t i = 0; i < settings.numGatewayConfigs; i++) {
+    GatewayConfig* config = settings.gatewayConfigs[i];
+    
+    if (config->protocolVersion == 0) {
+      udpServers[i] = new MiLightUdpServer(milightClient, config->port, config->deviceId);
+      udpServers[i]->begin();
+    } else {
+      Serial.print("Error initializing milight UDP server - Unsupported protocolVersion: ");
+      Serial.println(config->protocolVersion);
+    }
+  }
+}
+
 void initMilightClient() {
   if (milightClient) {
     delete milightClient;
@@ -175,6 +204,7 @@ void handleUpdateSettings() {
     settings.patch(parsedSettings);
     settings.save();
     initMilightClient();
+    initMilightUdpServers();
     
     if (server->authenticationRequired() && !settings.hasAuthSettings()) {
       server->disableAuthentication();
@@ -194,6 +224,7 @@ void setup() {
   SPIFFS.begin();
   Settings::load(settings);
   initMilightClient();
+  initMilightUdpServers();
   
   server->on("/", HTTP_GET, handleServeFile(WEB_INDEX_FILENAME, "text/html"));
   server->on("/settings", HTTP_GET, handleServeFile(SETTINGS_FILE, "application/json"));
@@ -240,4 +271,11 @@ void setup() {
 
 void loop() {
   server->handleClient();
+  
+  if (udpServers) {
+    for (size_t i = 0; i < settings.numGatewayConfigs; i++) {
+      yield();
+      udpServers[i]->handleClient();
+    }
+  }
 }

+ 123 - 2
web/index.html

@@ -29,6 +29,7 @@
     .error-info { color: #f00; font-size: 0.7em; }
     .error-info:before { content: '('; }
     .error-info:after { content: ')'; }
+    .header-btn { margin: 20px; }
     .btn-secondary { 
       background-color: #fff; 
       border: 1px solid #ccc;
@@ -75,6 +76,10 @@
     var FORM_SETTINGS = ["admin_username", "admin_password", "ce_pin", "csn_pin"];
     
     var selectize;
+    
+    var toHex = function(v) {
+      return "0x" + (v).toString(16).toUpperCase();
+    }
   
     var activeUrl = function() {
       var deviceId = $('#deviceId option:selected').val()
@@ -111,6 +116,23 @@
       });
     };
     
+    var gatewayServerRow = function(deviceId, port) {
+      var elmt = '<tr>';
+      elmt += '<td>';
+      elmt += '<input name="deviceIds[]" class="form-control" value="' + deviceId + '"/>';
+      elmt += '</td>';
+      elmt += '<td>'
+      elmt += '<input name="ports[]" class="form-control" value="' + port + '"/>';;
+      elmt += '</td>';
+      elmt += '<td>';
+      elmt += '<button class="btn btn-danger remove-gateway-server">';
+      elmt += '<i class="glyphicon glyphicon-remove"></i>';
+      elmt += '</button>';
+      elmt += '</td>';
+      elmt += '</tr>';
+      return elmt;
+    }
+    
     var loadSettings = function() {
       $.getJSON('/settings', function(val) {
         Object.keys(val).forEach(function(k) {
@@ -124,14 +146,66 @@
         if (val.device_ids) {
           selectize.clearOptions();
           val.device_ids.forEach(function(v) {
-            var inHex = "0x" + (v).toString(16).toUpperCase();
-            selectize.addOption({text: inHex, value: v});
+            selectize.addOption({text: toHex(v), value: v});
           });
           selectize.refreshOptions();
         }
+        
+        var gatewayForm = $('#gateway-server-configs').html('');
+        if (val.gateway_configs) {
+          val.gateway_configs.forEach(function(v) {
+            gatewayForm.append(gatewayServerRow(toHex(v[0]), v[1]));
+          });
+        }
       });
     };
     
+    var saveGatewayConfigs = function() {
+      var form = $('#gateway-server-form')
+        , errors = false;
+        
+      $('input', form).removeClass('error');
+      
+      var deviceIds = $('input[name="deviceIds[]"]', form).map(function(i, v) {
+        var val = $(v).val();
+        
+        if (isNaN(val)) {
+          errors = true;
+          $(v).addClass('error');
+          return null;
+        } else {
+          return val;
+        }
+      });
+      
+      var ports = $('input[name="ports[]"]', form).map(function(i, v) {
+        var val = $(v).val();
+        
+        if (isNaN(val)) {
+          errors = true;
+          $(v).addClass('error');
+          return null;
+        } else {
+          return val;
+        }
+      });
+        
+      if (!errors) {
+        var data = [];
+        for (var i = 0; i < deviceIds.length; i++) {
+          data[i] = [deviceIds[i], ports[i], 0];
+        }
+        $.ajax(
+          '/settings',
+          {
+            method: 'put',
+            contentType: 'application/json',
+            data: JSON.stringify({gateway_configs: data})
+          }
+        )
+      }
+    };
+    
     var deviceIdError = function(v) {
       if (!v) {
         $('#device-id-label').removeClass('error');
@@ -204,6 +278,14 @@
         }
       });
       
+      $('#add-server-btn').click(function() {
+        $('#gateway-server-configs').append(gatewayServerRow('', ''));
+      });
+      
+      $('body').on('click', '.remove-gateway-server', function() {
+        $(this).closest('tr').remove();
+      });
+      
       selectize = $('#deviceId').selectize({
         create: true,
         sortField: 'text',
@@ -269,6 +351,12 @@
         return false;
       });
       
+      $('#gateway-server-form').submit(function(e) {
+        saveGatewayConfigs();
+        e.preventDefault();
+        return false;
+      });
+      
       loadSettings();
     });
   </script>
@@ -377,6 +465,39 @@
     </div>
     
     <div class="row header-row">
+      <div class="col col-sm-10">
+        <h1>Gateway Servers</h1>
+      </div>
+      
+      <div class="col col-sm-2">
+        <button class="btn btn-success header-btn" id="add-server-btn">
+          <i class="glyphicon glyphicon-plus"></i>
+          Add Server
+        </button>
+      </div>
+    </div>
+    
+    <div class="row">
+      <div class="col col-sm-12">
+        <form id="gateway-server-form">
+          <table class="table">
+            <thead>
+              <tr>
+                <th>Device ID</th>
+                <th>UDP Port</th>
+              </tr>
+            </thead>
+            <tbody id="gateway-server-configs">
+            </tbody>
+          </table>
+          <input type="submit" class="btn btn-success" value="Save" />
+        </form>
+      </div>
+    </div>
+    
+    <div>&nbsp;</div>
+    
+    <div class="row header-row">
       <div class="col-sm-12">
         <h1>Settings</h1>
       </div>