Procházet zdrojové kódy

finish CCT support in REST protocol

Chris Mullins před 8 roky
rodič
revize
e7dae07f55

+ 17 - 0
lib/MiLight/MiLightButtons.h

@@ -32,4 +32,21 @@ enum MiLightRgbwButton {
   RGBW_GROUP_4_MIN_LEVEL = 0x1A,
 };
 
+enum MiLightCctButton {
+  CCT_ALL_ON            = 0x05,
+  CCT_ALL_OFF           = 0x09,
+  CCT_GROUP_1_ON        = 0x08,
+  CCT_GROUP_1_OFF       = 0x0B,
+  CCT_GROUP_2_ON        = 0x0D,
+  CCT_GROUP_2_OFF       = 0x03,
+  CCT_GROUP_3_ON        = 0x07,
+  CCT_GROUP_3_OFF       = 0x0A,
+  CCT_GROUP_4_ON        = 0x02,
+  CCT_GROUP_4_OFF       = 0x06,
+  CCT_BRIGHTNESS_DOWN   = 0x04,
+  CCT_BRIGHTNESS_UP     = 0x0C,
+  CCT_TEMPERATURE_UP    = 0x0E,
+  CCT_TEMPERATURE_DOWN  = 0x0F
+};
+
 #endif

+ 137 - 18
lib/MiLight/MiLightClient.cpp

@@ -84,10 +84,10 @@ void MiLightClient::write(const MiLightRadioType radioType,
   
   for (int i = 0; i < resendCount; i++) {
     radio->write(packetBytes, MILIGHT_PACKET_LENGTH);
+    yield();
   }
 }
 
-
 void MiLightClient::writeRgbw(
   const uint16_t deviceId,
   const uint8_t color,
@@ -96,7 +96,7 @@ void MiLightClient::writeRgbw(
   const uint8_t button) {
   
   MiLightPacket packet;
-  packet.deviceType = RGBW;;
+  packet.deviceType = RGBW;
   packet.deviceId = deviceId;
   packet.b1 = color;
   packet.b2 = (brightness << 3) | (groupId & 0x07);
@@ -105,6 +105,21 @@ void MiLightClient::writeRgbw(
   
   write(RGBW, packet);
 }
+
+void MiLightClient::writeCct(const uint16_t deviceId,
+  const uint8_t groupId,
+  const uint8_t button) {
+  
+  MiLightPacket packet;
+  packet.deviceType = CCT;
+  packet.deviceId = deviceId;
+  packet.b1 = groupId;
+  packet.b2 = button;
+  packet.b3 = nextSequenceNum();
+  packet.sequenceNum = packet.b3;
+  
+  write(CCT, packet);
+}
     
 void MiLightClient::updateColorRaw(const uint16_t deviceId, const uint8_t groupId, const uint16_t color) {
   writeRgbw(deviceId, color, 0, groupId, RGBW_COLOR);
@@ -132,34 +147,138 @@ void MiLightClient::updateBrightness(const uint16_t deviceId, const uint8_t grou
   writeRgbw(deviceId, 0, packetBrightnessValue, groupId, RGBW_BRIGHTNESS);
 }
 
-void MiLightClient::updateStatus(const uint16_t deviceId, const uint8_t groupId, MiLightStatus status) {
-  uint8_t button = RGBW_GROUP_1_ON + ((groupId - 1)*2) + status;
-  writeRgbw(deviceId, 0, 0, groupId, button);
+void MiLightClient::updateStatus(const MiLightRadioType type, const uint16_t deviceId, const uint8_t groupId, MiLightStatus status) {
+  if (type == RGBW) {
+    uint8_t button = RGBW_GROUP_1_ON + ((groupId - 1)*2) + status;
+    writeRgbw(deviceId, 0, 0, groupId, button);
+  } else {
+    writeCct(deviceId, groupId, getCctStatusButton(groupId, status));
+  }
 }
 
 void MiLightClient::updateColorWhite(const uint16_t deviceId, const uint8_t groupId) {
   uint8_t button = RGBW_GROUP_1_MAX_LEVEL + ((groupId - 1)*2);
-  pressButton(deviceId, groupId, button);
+  pressButton(RGBW, deviceId, groupId, button);
 }
 
-void MiLightClient::pair(const uint16_t deviceId, const uint8_t groupId) {
-  updateStatus(deviceId, groupId, ON);
+void MiLightClient::pair(const MiLightRadioType type, const uint16_t deviceId, const uint8_t groupId) {
+  updateStatus(type, deviceId, groupId, ON);
 }
 
-void MiLightClient::unpair(const uint16_t deviceId, const uint8_t groupId) {
-  updateStatus(deviceId, groupId, ON);
-  delay(1);
-  updateColorWhite(deviceId, groupId);
+void MiLightClient::unpair(const MiLightRadioType type, const uint16_t deviceId, const uint8_t groupId) {
+  if (type == RGBW) {
+    updateStatus(RGBW, deviceId, groupId, ON);
+    delay(1);
+    updateColorWhite(deviceId, groupId);
+  } else if (type == CCT) {
+    // Leading nibble is a "held" modifier
+    uint8_t button = 0x10 | getCctStatusButton(groupId, ON);
+    pressButton(CCT, deviceId, groupId, button);
+  }
 }
     
-void MiLightClient::pressButton(const uint16_t deviceId, const uint8_t groupId, const uint8_t button) {
-  writeRgbw(deviceId, 0, 0, groupId, button);
+void MiLightClient::pressButton(const MiLightRadioType type, const uint16_t deviceId, const uint8_t groupId, const uint8_t button) {
+  if (type == RGBW) {
+    writeRgbw(deviceId, 0, 0, groupId, button);
+  } else if (type == CCT) {
+    writeCct(deviceId, groupId, button);
+  }
 }
 
-void MiLightClient::allOn(const uint16_t deviceId) {
-  writeRgbw(deviceId, 0, 0, 0, RGBW_ALL_ON);
+void MiLightClient::allOn(const MiLightRadioType type, const uint16_t deviceId) {
+  if (type == RGBW) {
+    writeRgbw(deviceId, 0, 0, 0, RGBW_ALL_ON);
+  } else if (type == CCT) {
+    writeCct(deviceId, 0, CCT_ALL_ON);
+  }
 }
 
-void MiLightClient::allOff(const uint16_t deviceId) {
-  writeRgbw(deviceId, 0, 0, 0, RGBW_ALL_OFF);
+void MiLightClient::allOff(const MiLightRadioType type, const uint16_t deviceId) {
+  if (type == RGBW) {
+    writeRgbw(deviceId, 0, 0, 0, RGBW_ALL_OFF);
+  } else if (type == CCT) {
+    writeCct(deviceId, 0, CCT_ALL_OFF);
+  }
+}
+
+void MiLightClient::increaseCctBrightness(const uint16_t deviceId, const uint8_t groupId) {
+  writeCct(deviceId, groupId, CCT_BRIGHTNESS_UP);
+}
+
+void MiLightClient::decreaseCctBrightness(const uint16_t deviceId, const uint8_t groupId) {
+  writeCct(deviceId, groupId, CCT_BRIGHTNESS_DOWN);
+}
+
+void MiLightClient::updateCctBrightness(const uint16_t deviceId, const uint8_t groupId, const uint8_t brightness) {
+  for (int i = 0; i < MILIGHT_CCT_INTERVALS; i++) {
+    decreaseCctBrightness(deviceId, groupId);
+  }
+  for (int i = 0; i < brightness/10; i++) {
+    increaseCctBrightness(deviceId, groupId);
+  }
+}
+
+void MiLightClient::increaseTemperature(const uint16_t deviceId, const uint8_t groupId) {
+  writeCct(deviceId, groupId, CCT_TEMPERATURE_UP);
+}
+
+void MiLightClient::decreaseTemperature(const uint16_t deviceId, const uint8_t groupId) {
+  writeCct(deviceId, groupId, CCT_TEMPERATURE_DOWN);
+}
+
+void MiLightClient::updateTemperature(const uint16_t deviceId, const uint8_t groupId, const uint8_t temperature) {
+  for (int i = 0; i < MILIGHT_CCT_INTERVALS; i++) {
+    decreaseTemperature(deviceId, groupId);
+  }
+  for (int i = 0; i < temperature; i++) {
+    increaseTemperature(deviceId, groupId);
+  }
+}
+
+uint8_t MiLightClient::getCctStatusButton(uint8_t groupId, MiLightStatus status) {
+  uint8_t button = 0;
+  
+  if (status == ON) {
+    switch(groupId) {
+      case 1:
+        button = CCT_GROUP_1_ON;
+        break;
+      case 2:
+        button = CCT_GROUP_2_ON;
+        break;
+      case 3:
+        button = CCT_GROUP_3_ON;
+        break;
+      case 4:
+        button = CCT_GROUP_4_ON;
+        break;
+    }
+  } else {
+    switch(groupId) {
+      case 1:
+        button = CCT_GROUP_1_OFF;
+        break;
+      case 2:
+        button = CCT_GROUP_2_OFF;
+        break;
+      case 3:
+        button = CCT_GROUP_3_OFF;
+        break;
+      case 4:
+        button = CCT_GROUP_4_OFF;
+        break;
+    }
+  }
+  
+  return button;
+}
+
+MiLightRadioType MiLightClient::getRadioType(const String& typeName) {
+  if (typeName.equalsIgnoreCase("rgbw")) {
+    return RGBW;
+  } else if (typeName.equalsIgnoreCase("cct")) {
+    return CCT;
+  } else {
+    return UNKNOWN;
+  }
 }

+ 24 - 8
lib/MiLight/MiLightClient.h

@@ -8,8 +8,10 @@
 #define _MILIGHTCLIENT_H
 
 #define MILIGHT_PACKET_LENGTH 7
+#define MILIGHT_CCT_INTERVALS 10
 
 enum MiLightRadioType {
+  UNKNOWN = 0,
   RGBW  = 0xB8,
   CCT   = 0x5A
 };
@@ -68,7 +70,7 @@ class MiLightClient {
     
     bool available(const MiLightRadioType radioType);
     void read(const MiLightRadioType radioType, MiLightPacket& packet);
-    void write(const MiLightRadioType radioType, MiLightPacket& packet, const unsigned int resendCount = 50);
+    void write(const MiLightRadioType radioType, MiLightPacket& packet, const unsigned int resendCount = 10);
     
     void writeRgbw(
       const uint16_t deviceId,
@@ -78,13 +80,19 @@ class MiLightClient {
       const uint8_t button
     );
     
+    void writeCct(
+      const uint16_t deviceId,
+      const uint8_t groupId,
+      const uint8_t button
+    );
+    
     // Common methods
-    void updateStatus(const uint16_t deviceId, const uint8_t groupId, MiLightStatus status);
-    void pair(const uint16_t deviceId, const uint8_t groupId);
-    void unpair(const uint16_t deviceId, const uint8_t groupId);
-    void allOn(const uint16_t deviceId);
-    void allOff(const uint16_t deviceId);
-    void pressButton(const uint16_t deviceId, const uint8_t groupId, uint8_t button);
+    void updateStatus(const MiLightRadioType type,const uint16_t deviceId, const uint8_t groupId, MiLightStatus status);
+    void pair(const MiLightRadioType type,const uint16_t deviceId, const uint8_t groupId);
+    void unpair(const MiLightRadioType type,const uint16_t deviceId, const uint8_t groupId);
+    void allOn(const MiLightRadioType type,const uint16_t deviceId);
+    void allOff(const MiLightRadioType type,const uint16_t deviceId);
+    void pressButton(const MiLightRadioType type, const uint16_t deviceId, const uint8_t groupId, uint8_t button);
     
     // RGBW methods
     void updateHue(const uint16_t deviceId, const uint8_t groupId, const uint16_t hue);
@@ -93,13 +101,21 @@ class MiLightClient {
     void updateColorRaw(const uint16_t deviceId, const uint8_t groupId, const uint16_t color);
 
     // CCT methods
-    void updateColorTemperature(const uint8_t colorTemperature);
+    void updateTemperature(const uint16_t deviceId, const uint8_t groupId, const uint8_t colorTemperature);
+    void decreaseTemperature(const uint16_t deviceId, const uint8_t groupId);
+    void increaseTemperature(const uint16_t deviceId, const uint8_t groupId);
+    void updateCctBrightness(const uint16_t deviceId, const uint8_t groupId, const uint8_t brightness);
+    void decreaseCctBrightness(const uint16_t deviceId, const uint8_t groupId);
+    void increaseCctBrightness(const uint16_t deviceId, const uint8_t groupId);
     
     MiLightRadio* getRadio(const MiLightRadioType type);
     
     static void deserializePacket(const uint8_t rawPacket[], MiLightPacket& packet);
     static void serializePacket(uint8_t rawPacket[], const MiLightPacket& packet);
     
+    static uint8_t getCctStatusButton(uint8_t groupId, MiLightStatus status);
+    static MiLightRadioType getRadioType(const String& typeName);
+    
   private:
     RF24 rf;
     MiLightRadioStack* rgbwRadio;

+ 3 - 3
lib/MiLight/MiLightUdpServer.cpp

@@ -38,7 +38,7 @@ void MiLightUdpServer::handleCommand(uint8_t command, uint8_t commandArg) {
     const MiLightStatus status = (command % 2) == 1 ? ON : OFF;
     const uint8_t groupId = (command - UDP_GROUP_1_ON + 2)/2;
     
-    client->updateStatus(deviceId, groupId, status);
+    client->updateStatus(RGBW, deviceId, groupId, status);
     
     this->lastGroup = groupId;
   } else if (command >= UDP_GROUP_ALL_WHITE && command <= UDP_GROUP_4_WHITE) {
@@ -48,11 +48,11 @@ void MiLightUdpServer::handleCommand(uint8_t command, uint8_t commandArg) {
   } else {
     switch (command) {
       case UDP_ALL_ON:
-        client->allOn(deviceId);
+        client->allOn(RGBW, deviceId);
         break;
       
       case UDP_ALL_OFF:
-        client->allOff(deviceId);
+        client->allOff(RGBW, deviceId);
         break;
       
       case UDP_COLOR:

+ 64 - 24
src/main.cpp

@@ -22,12 +22,21 @@ void handleUpdateGateway(const UrlTokenBindings* urlBindings) {
   JsonObject& request = buffer.parse(server->arg("plain"));
   
   const uint16_t deviceId = parseInt<uint16_t>(urlBindings->get("device_id"));
+  const MiLightRadioType type = MiLightClient::getRadioType(urlBindings->get("type"));
+  
+  if (type == UNKNOWN) {
+    String body = "Unknown device type: ";
+    body += urlBindings->get("type");
+    
+    server->send(400, "text/plain", body);
+    return;
+  }
   
   if (request.containsKey("status")) {
     if (request["status"] == "on") {
-      milightClient->allOn(deviceId);
+      milightClient->allOn(type, deviceId);
     } else if (request["status"] == "off") {
-      milightClient->allOff(deviceId);
+      milightClient->allOff(type, deviceId);
     }
   }
   
@@ -45,42 +54,73 @@ void handleUpdateGroup(const UrlTokenBindings* urlBindings) {
   
   const uint16_t deviceId = parseInt<uint16_t>(urlBindings->get("device_id"));
   const uint8_t groupId = urlBindings->get("group_id").toInt();
+  const MiLightRadioType type = MiLightClient::getRadioType(urlBindings->get("type"));
   
+  if (type == UNKNOWN) {
+    String body = "Unknown device type: ";
+    body += urlBindings->get("type");
+    
+    server->send(400, "text/plain", body);
+    return;
+  }
+    
   if (request.containsKey("status")) {
     const String& statusStr = request.get<String>("status");
     MiLightStatus status = (statusStr == "on" || statusStr == "true") ? ON : OFF;
-    milightClient->updateStatus(deviceId, groupId, status);
-  }
-  
-  if (request.containsKey("hue")) {
-    milightClient->updateHue(deviceId, groupId, request["hue"]);
+    milightClient->updateStatus(type, deviceId, groupId, status);
   }
-  
-  if (request.containsKey("level")) {
-    milightClient->updateBrightness(deviceId, groupId, request["level"]);
+      
+  if (request.containsKey("command")) {
+    if (request["command"] == "unpair") {
+      milightClient->unpair(type, deviceId, groupId);
+    }
+    
+    if (request["command"] == "pair") {
+      milightClient->pair(type, deviceId, groupId);
+    }
   }
   
-  if (request.containsKey("command")) {
-    if (request["command"] == "set_white") {
-      milightClient->updateColorWhite(deviceId, groupId);
+  if (type == RGBW) {
+    if (request.containsKey("hue")) {
+      milightClient->updateHue(deviceId, groupId, request["hue"]);
     }
     
-    if (request["command"] == "all_on") {
-      milightClient->allOn(deviceId);
+    if (request.containsKey("level")) {
+      milightClient->updateBrightness(deviceId, groupId, request["level"]);
     }
     
-    if (request["command"] == "all_off") {
-      milightClient->allOff(deviceId);
+    if (request.containsKey("command")) {
+      if (request["command"] == "set_white") {
+        milightClient->updateColorWhite(deviceId, groupId);
+      }
+    }
+  } else if (type == CCT) {
+    if (request.containsKey("temperature")) {
+      milightClient->updateTemperature(deviceId, groupId, request["temperature"]);
     }
     
-    if (request["command"] == "unpair") {
-      milightClient->unpair(deviceId, groupId);
+    if (request.containsKey("level")) {
+      milightClient->updateCctBrightness(deviceId, groupId, request["level"]);
     }
     
-    if (request["command"] == "pair") {
-      milightClient->pair(deviceId, groupId);
+    if (request.containsKey("command")) {
+      if (request["command"] == "level_up") {
+        milightClient->increaseCctBrightness(deviceId, groupId);
+      }
+      
+      if (request["command"] == "level_down") {
+        milightClient->decreaseCctBrightness(deviceId, groupId);
+      }
+      
+      if (request["command"] == "temperature_up") {
+        milightClient->increaseTemperature(deviceId, groupId);
+      }
+      
+      if (request["command"] == "temperature_down") {
+        milightClient->decreaseTemperature(deviceId, groupId);
+      }
     }
-  }
+  } 
   
   server->send(200, "application/json", "true");
 }
@@ -238,8 +278,8 @@ void setup() {
   server->on("/settings", HTTP_GET, handleServeFile(SETTINGS_FILE, "application/json"));
   server->on("/settings", HTTP_PUT, handleUpdateSettings);
   server->on("/gateway_traffic", HTTP_GET, handleListenGateway);
-  server->onPattern("/gateways/:device_id/:group_id", HTTP_PUT, handleUpdateGroup);
-  server->onPattern("/gateways/:device_id", HTTP_PUT, handleUpdateGateway);
+  server->onPattern("/gateways/:device_id/:type/:group_id", HTTP_PUT, handleUpdateGroup);
+  server->onPattern("/gateways/:device_id/:type", HTTP_PUT, handleUpdateGateway);
   server->on("/web", HTTP_POST, onWebUpdated, handleUpdateFile(WEB_INDEX_FILENAME));
   server->on("/firmware", HTTP_POST, 
     [](){

+ 6 - 7
web/index.html

@@ -84,14 +84,15 @@
   
     var activeUrl = function() {
       var deviceId = $('#deviceId option:selected').val()
-        , groupId = $('#groupId input:checked').data('value');
+        , groupId = $('#groupId input:checked').data('value')
+        , mode = getCurrentMode();
         
       if (deviceId == "") {
         alert("Please enter a device ID.");
         throw "Must enter device ID";
       }
         
-      return "/gateways/" + deviceId + "/" + groupId;
+      return "/gateways/" + deviceId + "/" + mode + "/" + groupId;
     }
     
     var getCurrentMode = function() {
@@ -100,8 +101,6 @@
     
     var updateGroup = _.throttle(
       function(params) {
-        params.mode = getCurrentMode();
-        
         $.ajax(
           activeUrl(),
           {
@@ -467,10 +466,10 @@
       </div>
       <div class="row">
         <div class="col-sm-6">
-          <input class="slider raw-update" name="color-temperature"
+          <input class="slider raw-update" name="temperature"
               data-slider-min="0"
-              data-slider-max="100"
-              data-slider-value="100"
+              data-slider-max="10"
+              data-slider-value="10"
           />
         </div>
       </div>