Bladeren bron

Merge branch 'v1.2.0' into multi_protocol

Chris Mullins 8 jaren geleden
bovenliggende
commit
b74cc8c4bf

+ 9 - 6
lib/MiLight/CctPacketFormatter.cpp

@@ -3,7 +3,7 @@
 
 void CctPacketFormatter::initializePacket(uint8_t* packet) {
   size_t packetPtr = 0;
-  
+
   packet[packetPtr++] = CCT;
   packet[packetPtr++] = deviceId >> 8;
   packet[packetPtr++] = deviceId & 0xFF;
@@ -30,16 +30,19 @@ void CctPacketFormatter::updateTemperature(uint8_t value) {
     value / CCT_INTERVALS
   );
 }
-  
+
 void CctPacketFormatter::command(uint8_t command, uint8_t arg) {
   pushPacket();
+  if (held) {
+    command |= 0x80;
+  }
   currentPacket[CCT_COMMAND_INDEX] = command;
 }
 
 void CctPacketFormatter::updateStatus(MiLightStatus status, uint8_t groupId) {
   command(getCctStatusButton(groupId, status), 0);
 }
-  
+
 void CctPacketFormatter::increaseTemperature() {
   command(CCT_TEMPERATURE_UP, 0);
 }
@@ -58,7 +61,7 @@ void CctPacketFormatter::decreaseBrightness() {
 
 uint8_t CctPacketFormatter::getCctStatusButton(uint8_t groupId, MiLightStatus status) {
   uint8_t button = 0;
-  
+
   if (status == ON) {
     switch(groupId) {
       case 1:
@@ -90,10 +93,10 @@ uint8_t CctPacketFormatter::getCctStatusButton(uint8_t groupId, MiLightStatus st
         break;
     }
   }
-  
+
   return button;
 }
 
 void CctPacketFormatter::format(uint8_t const* packet, char* buffer) {
   PacketFormatter::formatV1Packet(packet, buffer);
-}
+}

+ 4 - 0
lib/MiLight/MiLightClient.cpp

@@ -23,6 +23,10 @@ void MiLightClient::begin() {
   }
 }
 
+void MiLightClient::setHeld(bool held) {
+  formatter->setHeld(held);
+}
+
 MiLightRadio* MiLightClient::switchRadio(const MiLightRadioType type) {
   MiLightRadio* radio = NULL;
 

+ 2 - 0
lib/MiLight/MiLightClient.h

@@ -27,6 +27,8 @@ public:
   void read(uint8_t packet[]);
   void write(uint8_t packet[]);
 
+  void setHeld(bool held);
+
   // Common methods
   void updateStatus(MiLightStatus status);
   void updateStatus(MiLightStatus status, uint8_t groupId);

+ 24 - 18
lib/MiLight/PacketFormatter.cpp

@@ -7,8 +7,8 @@ PacketStream::PacketStream()
       numPackets(0),
       packetLength(0),
       currentPacket(0)
-  { }
-  
+{ }
+
 bool PacketStream::hasNext() {
   return currentPacket < numPackets;
 }
@@ -22,18 +22,23 @@ uint8_t* PacketStream::next() {
 PacketFormatter::PacketFormatter(const size_t packetLength, const size_t maxPackets)
   : packetLength(packetLength),
     numPackets(0),
-    currentPacket(NULL)
-{ 
+    currentPacket(NULL),
+    held(false)
+{
   packetStream.packetLength = packetLength;
   packetStream.packetStream = PACKET_BUFFER;
 }
-  
+
 void PacketFormatter::finalizePacket(uint8_t* packet) { }
-  
-void PacketFormatter::updateStatus(MiLightStatus status) { 
+
+void PacketFormatter::updateStatus(MiLightStatus status) {
   updateStatus(status, groupId);
 }
 
+void PacketFormatter::setHeld(bool held) {
+  this->held = held;
+}
+
 void PacketFormatter::updateStatus(MiLightStatus status, uint8_t groupId) { }
 void PacketFormatter::updateBrightness(uint8_t value) { }
 void PacketFormatter::updateMode(uint8_t value) { }
@@ -54,33 +59,33 @@ void PacketFormatter::decreaseBrightness() { }
 
 void PacketFormatter::updateTemperature(uint8_t value) { }
 void PacketFormatter::updateSaturation(uint8_t value) { }
-  
-void PacketFormatter::pair() { 
+
+void PacketFormatter::pair() {
   for (size_t i = 0; i < 5; i++) {
     updateStatus(ON);
   }
 }
 
-void PacketFormatter::unpair() { 
+void PacketFormatter::unpair() {
   pair();
 }
-  
+
 PacketStream& PacketFormatter::buildPackets() {
   if (numPackets > 0) {
     finalizePacket(currentPacket);
   }
-  
+
   packetStream.numPackets = numPackets;
   packetStream.currentPacket = 0;
-  
+
   return packetStream;
 }
-  
+
 void PacketFormatter::valueByStepFunction(StepFunction increase, StepFunction decrease, uint8_t numSteps, uint8_t value) {
   for (size_t i = 0; i < numSteps; i++) {
     (this->*decrease)();
   }
-  
+
   for (size_t i = 0; i < value; i++) {
     (this->*increase)();
   }
@@ -95,13 +100,14 @@ void PacketFormatter::prepare(uint16_t deviceId, uint8_t groupId) {
 void PacketFormatter::reset() {
   this->numPackets = 0;
   this->currentPacket = currentPacket;
+  this->held = false;
 }
 
 void PacketFormatter::pushPacket() {
   if (numPackets > 0) {
     finalizePacket(currentPacket);
   }
-  
+
   currentPacket = PACKET_BUFFER + (numPackets * packetLength);
   numPackets++;
   initializePacket(currentPacket);
@@ -123,7 +129,7 @@ void PacketFormatter::formatV1Packet(uint8_t const* packet, char* buffer) {
   buffer += sprintf_P(buffer, "b3            : %02X\n", packet[5]);
   buffer += sprintf_P(buffer, "Sequence Num. : %02X", packet[6]);
 }
-  
+
 size_t PacketFormatter::getPacketLength() const {
   return packetLength;
-}
+}

+ 23 - 20
lib/MiLight/PacketFormatter.h

@@ -6,14 +6,14 @@
 #define PACKET_FORMATTER_BUFFER_SIZE 48
 
 #ifndef _PACKET_FORMATTER_H
-#define _PACKET_FORMATTER_H 
+#define _PACKET_FORMATTER_H
 
 struct PacketStream {
   PacketStream();
-  
+
   uint8_t* next();
   bool hasNext();
-  
+
   uint8_t* packetStream;
   size_t numPackets;
   size_t packetLength;
@@ -23,69 +23,72 @@ struct PacketStream {
 class PacketFormatter {
 public:
   PacketFormatter(const size_t packetLength, const size_t maxPackets = 1);
-  
+
   typedef void (PacketFormatter::*StepFunction)();
-  
+
   void updateStatus(MiLightStatus status);
   virtual void updateStatus(MiLightStatus status, uint8_t groupId);
   virtual void command(uint8_t command, uint8_t arg);
-  
+
+  virtual void setHeld(bool held);
+
   // Mode
   virtual void updateMode(uint8_t value);
   virtual void modeSpeedDown();
   virtual void modeSpeedUp();
   virtual void nextMode();
   virtual void previousMode();
-  
+
   virtual void pair();
   virtual void unpair();
-  
+
   // Color
   virtual void updateHue(uint16_t value);
   virtual void updateColorRaw(uint8_t value);
   virtual void updateColorWhite();
-  
+
   // White temperature
   virtual void increaseTemperature();
   virtual void decreaseTemperature();
   virtual void updateTemperature(uint8_t value);
-  
+
   // Brightness
   virtual void updateBrightness(uint8_t value);
   virtual void increaseBrightness();
   virtual void decreaseBrightness();
-  
+
   virtual void updateSaturation(uint8_t value);
-  
+
   virtual void reset();
-  
+
   virtual PacketStream& buildPackets();
   virtual void prepare(uint16_t deviceId, uint8_t groupId);
   virtual void format(uint8_t const* packet, char* buffer);
-  
+
   static void formatV1Packet(uint8_t const* packet, char* buffer);
-  
+
   template <typename T>
   static T rescale(T value, uint8_t newMax, float oldMax = 255.0) {
     return round(value * (newMax / oldMax));
   }
-  
+
   size_t getPacketLength() const;
-  
+
 protected:
   static uint8_t* PACKET_BUFFER;
-  
+
   uint8_t* currentPacket;
   size_t packetLength;
   uint16_t deviceId;
   uint8_t groupId;
   uint8_t sequenceNum;
   size_t numPackets;
+  bool held;
   PacketStream packetStream;
-  
+
   void pushPacket();
   void valueByStepFunction(StepFunction increase, StepFunction decrease, uint8_t numSteps, uint8_t value);
-  
+
   virtual void initializePacket(uint8_t* packetStart) = 0;
   virtual void finalizePacket(uint8_t* packet);
 };

+ 20 - 17
lib/MiLight/RgbCctPacketFormatter.cpp

@@ -19,10 +19,10 @@ uint8_t const RgbCctPacketFormatter::V2_OFFSETS[][4] = {
 
 void RgbCctPacketFormatter::initializePacket(uint8_t* packet) {
   size_t packetPtr = 0;
-  
+
   // Always encode with 0x00 key. No utility in varying it.
   packet[packetPtr++] = 0x00;
-  
+
   packet[packetPtr++] = 0x20;
   packet[packetPtr++] = deviceId >> 8;
   packet[packetPtr++] = deviceId & 0xFF;
@@ -33,14 +33,17 @@ void RgbCctPacketFormatter::initializePacket(uint8_t* packet) {
   packet[packetPtr++] = 0;
 }
 
-void RgbCctPacketFormatter::unpair() { 
+void RgbCctPacketFormatter::unpair() {
   for (size_t i = 0; i < 5; i++) {
     updateStatus(ON, 0);
   }
 }
-  
+
 void RgbCctPacketFormatter::command(uint8_t command, uint8_t arg) {
   pushPacket();
+  if (held) {
+    command |= 0x80;
+  }
   currentPacket[RGB_CCT_COMMAND_INDEX] = command;
   currentPacket[RGB_CCT_ARGUMENT_INDEX] = arg;
 }
@@ -48,7 +51,7 @@ void RgbCctPacketFormatter::command(uint8_t command, uint8_t arg) {
 void RgbCctPacketFormatter::updateStatus(MiLightStatus status, uint8_t groupId) {
   command(RGB_CCT_ON, groupId + (status == OFF ? 5 : 0));
 }
-  
+
 void RgbCctPacketFormatter::modeSpeedDown() {
   command(RGB_CCT_ON, RGB_CCT_MODE_SPEED_DOWN);
 }
@@ -73,7 +76,7 @@ void RgbCctPacketFormatter::previousMode() {
 void RgbCctPacketFormatter::updateBrightness(uint8_t brightness) {
   command(RGB_CCT_BRIGHTNESS, 0x8F + brightness);
 }
-  
+
 void RgbCctPacketFormatter::updateHue(uint16_t value) {
   uint8_t remapped = rescale(value, 255, 360);
   updateColorRaw(remapped);
@@ -82,7 +85,7 @@ void RgbCctPacketFormatter::updateHue(uint16_t value) {
 void RgbCctPacketFormatter::updateColorRaw(uint8_t value) {
   command(RGB_CCT_COLOR, 0x5F + value);
 }
-  
+
 void RgbCctPacketFormatter::updateTemperature(uint8_t value) {
   command(RGB_CCT_KELVIN, 0x94 - (value*2));
 }
@@ -91,11 +94,11 @@ void RgbCctPacketFormatter::updateSaturation(uint8_t value) {
   uint8_t remapped = value + 0xD;
   command(RGB_CCT_SATURATION, remapped);
 }
-  
+
 void RgbCctPacketFormatter::updateColorWhite() {
   updateTemperature(0);
 }
-  
+
 void RgbCctPacketFormatter::finalizePacket(uint8_t* packet) {
   encodeV2Packet(packet);
 }
@@ -116,7 +119,7 @@ uint8_t RgbCctPacketFormatter::decodeByte(uint8_t byte, uint8_t s1, uint8_t xorK
   uint8_t value = byte - s2;
   value = value ^ xorKey;
   value = value - s1;
-  
+
   return value;
 }
 
@@ -124,13 +127,13 @@ uint8_t RgbCctPacketFormatter::encodeByte(uint8_t byte, uint8_t s1, uint8_t xorK
   uint8_t value = byte + s1;
   value = value ^ xorKey;
   value = value + s2;
-  
+
   return value;
 }
 
 void RgbCctPacketFormatter::decodeV2Packet(uint8_t *packet) {
   uint8_t key = xorKey(packet[0]);
-  
+
   for (size_t i = 1; i <= 8; i++) {
     packet[i] = decodeByte(packet[i], 0, key, V2_OFFSET(i, packet[0], V2_OFFSET_JUMP_START));
   }
@@ -139,12 +142,12 @@ void RgbCctPacketFormatter::decodeV2Packet(uint8_t *packet) {
 void RgbCctPacketFormatter::encodeV2Packet(uint8_t *packet) {
   uint8_t key = xorKey(packet[0]);
   uint8_t sum = key;
-  
+
   for (size_t i = 1; i <= 7; i++) {
     sum += packet[i];
     packet[i] = encodeByte(packet[i], 0, key, V2_OFFSET(i, packet[0], V2_OFFSET_JUMP_START));
   }
-  
+
   packet[8] = encodeByte(sum, 2, key, V2_OFFSET(8, packet[0], 0));
 }
 
@@ -153,12 +156,12 @@ void RgbCctPacketFormatter::format(uint8_t const* packet, char* buffer) {
   for (int i = 0; i < packetLength; i++) {
     buffer += sprintf_P(buffer, PSTR("%02X "), packet[i]);
   }
-  
+
   uint8_t decodedPacket[packetLength];
   memcpy(decodedPacket, packet, packetLength);
-  
+
   decodeV2Packet(decodedPacket);
-  
+
   buffer += sprintf_P(buffer, PSTR("\n\nDecoded:\n"));
   buffer += sprintf_P(buffer, PSTR("Key      : %02X\n"), decodedPacket[0]);
   buffer += sprintf_P(buffer, PSTR("b1       : %02X\n"), decodedPacket[1]);

+ 10 - 7
lib/MiLight/RgbPacketFormatter.cpp

@@ -2,7 +2,7 @@
 
 void RgbPacketFormatter::initializePacket(uint8_t *packet) {
   size_t packetPtr = 0;
-  
+
   packet[packetPtr++] = RGB;
   packet[packetPtr++] = deviceId >> 8;
   packet[packetPtr++] = deviceId & 0xFF;
@@ -11,13 +11,13 @@ void RgbPacketFormatter::initializePacket(uint8_t *packet) {
   packet[packetPtr++] = sequenceNum++;
 }
 
-void RgbPacketFormatter::pair() { 
+void RgbPacketFormatter::pair() {
   for (size_t i = 0; i < 5; i++) {
     command(RGB_SPEED_UP, 0);
   }
 }
 
-void RgbPacketFormatter::unpair() { 
+void RgbPacketFormatter::unpair() {
   for (size_t i = 0; i < 5; i++) {
     command(RGB_SPEED_UP | 0x10, 0);
   }
@@ -29,9 +29,12 @@ void RgbPacketFormatter::updateStatus(MiLightStatus status, uint8_t groupId) {
 
 void RgbPacketFormatter::command(uint8_t command, uint8_t arg) {
   pushPacket();
+  if (held) {
+    command |= 0x80;
+  }
   currentPacket[RGB_COMMAND_INDEX] = command;
 }
-  
+
 void RgbPacketFormatter::updateHue(uint16_t value) {
   const int16_t remappedColor = (value + 40) % 360;
   updateColorRaw(rescale(remappedColor, 255, 360));
@@ -58,11 +61,11 @@ void RgbPacketFormatter::increaseBrightness() {
 void RgbPacketFormatter::decreaseBrightness() {
   command(RGB_BRIGHTNESS_DOWN, 0);
 }
-  
+
 void RgbPacketFormatter::modeSpeedDown() {
   command(RGB_SPEED_DOWN, 0);
 }
- 
+
 void RgbPacketFormatter::modeSpeedUp() {
   command(RGB_SPEED_UP, 0);
 }
@@ -81,4 +84,4 @@ void RgbPacketFormatter::format(uint8_t const* packet, char* buffer) {
   buffer += sprintf_P(buffer, "Color    : %02X\n", packet[3]);
   buffer += sprintf_P(buffer, "Command  : %02X\n", packet[4]);
   buffer += sprintf_P(buffer, "Sequence : %02X\n", packet[5]);
-}
+}

+ 10 - 7
lib/MiLight/RgbwPacketFormatter.cpp

@@ -3,7 +3,7 @@
 
 void RgbwPacketFormatter::initializePacket(uint8_t* packet) {
   size_t packetPtr = 0;
-  
+
   packet[packetPtr++] = RGBW;
   packet[packetPtr++] = deviceId >> 8;
   packet[packetPtr++] = deviceId & 0xFF;
@@ -13,11 +13,11 @@ void RgbwPacketFormatter::initializePacket(uint8_t* packet) {
   packet[packetPtr++] = sequenceNum++;
 }
 
-void RgbwPacketFormatter::unpair() { 
+void RgbwPacketFormatter::unpair() {
   PacketFormatter::updateStatus(ON);
   updateColorWhite();
 }
-  
+
 void RgbwPacketFormatter::modeSpeedDown() {
   command(RGBW_SPEED_DOWN, 0);
 }
@@ -39,7 +39,7 @@ void RgbwPacketFormatter::updateStatus(MiLightStatus status, uint8_t groupId) {
   uint8_t button = RGBW_GROUP_1_ON + ((groupId - 1)*2) + status;
   command(button, 0);
 }
-  
+
 void RgbwPacketFormatter::updateBrightness(uint8_t value) {
   // Expect an input value in [0, 100]. Map it down to [0, 25].
   const uint8_t adjustedBrightness = rescale(value, 25, 100);
@@ -49,16 +49,19 @@ void RgbwPacketFormatter::updateBrightness(uint8_t value) {
   const uint8_t packetBrightnessValue = (
     ((31 - adjustedBrightness) + 17) % 32
   );
-  
+
   command(RGBW_BRIGHTNESS, 0);
   currentPacket[RGBW_BRIGHTNESS_GROUP_INDEX] |= (packetBrightnessValue << 3);
 }
 
 void RgbwPacketFormatter::command(uint8_t command, uint8_t arg) {
   pushPacket();
+  if (held) {
+    command |= 0x80;
+  }
   currentPacket[RGBW_COMMAND_INDEX] = command;
 }
-  
+
 void RgbwPacketFormatter::updateHue(uint16_t value) {
   const int16_t remappedColor = (value + 40) % 360;
   updateColorRaw(rescale(remappedColor, 255, 360));
@@ -76,4 +79,4 @@ void RgbwPacketFormatter::updateColorWhite() {
 
 void RgbwPacketFormatter::format(uint8_t const* packet, char* buffer) {
   PacketFormatter::formatV1Packet(packet, buffer);
-}
+}

+ 3 - 1
lib/Udp/V6CctCommandHandler.cpp

@@ -13,9 +13,11 @@ bool V6CctCommandHandler::handleCommand(
     uint32_t command,
     uint32_t commandArg)
 {
-  const uint8_t cmd = command & 0xFF;
+  const uint8_t cmd = command & 0x7F;
   const uint8_t arg = commandArg >> 24;
 
+  client->setHeld((command & 0x80) == 0x80);
+
   if (cmd == V2_CCT_COMMAND_PREFIX) {
     switch (arg) {
       case V2_CCT_ON:

+ 63 - 56
lib/Udp/V6MiLightUdpServer.cpp

@@ -12,14 +12,14 @@ V6CommandDemuxer V6MiLightUdpServer::COMMAND_DEMUXER = V6CommandDemuxer(
   V6CommandHandler::ALL_HANDLERS,
   V6CommandHandler::NUM_HANDLERS
 );
-  
+
 uint8_t V6MiLightUdpServer::START_SESSION_COMMAND[] = {
-  0x20, 0x00, 0x00, 0x00, 0x16, 0x02, 0x62, 0x3A, 0xD5, 0xED, 0xA3, 0x01, 0xAE, 
+  0x20, 0x00, 0x00, 0x00, 0x16, 0x02, 0x62, 0x3A, 0xD5, 0xED, 0xA3, 0x01, 0xAE,
   0x08, 0x2D, 0x46, 0x61, 0x41, 0xA7, 0xF6, 0xDC, 0xAF
 };
 
 uint8_t V6MiLightUdpServer::START_SESSION_RESPONSE[] = {
-   0x28, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 
+   0x28, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,  // should be replaced with hw addr
    0x69, 0xF0, 0x3C, 0x23, 0x00, 0x01,
    0xFF, 0xFF, // should be replaced with a session ID
@@ -49,20 +49,19 @@ uint8_t V6MiLightUdpServer::SEARCH_COMMAND[] = {
 };
 
 uint8_t V6MiLightUdpServer::SEARCH_RESPONSE[] = {
-  0x18, 0x00, 0x00, 0x00, 0x40, 0x02, 
+  0x18, 0x00, 0x00, 0x00, 0x40, 0x02,
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // mac address
-  0x00, 0x20, 0x39, 0x38, 0x35, 0x62, 
-  0x31, 0x35, 0x37, 0x62, 0x66, 0x36, 
-  0x66, 0x63, 0x34, 0x33, 0x33, 0x36, 
-  0x38, 0x61, 0x36, 0x33, 0x34, 0x36, 
-  0x37, 0x65, 0x61, 0x33, 0x62, 0x31, 
-  0x39, 0x64, 0x30, 0x64, 0x01, 0x00, 
-  0x01, 
-  0x17, 0x63,  // this is 5987 in hex. specifying a different value seems to 
+  0x00, 0x20, 0x39, 0x38, 0x35, 0x62,
+  0x31, 0x35, 0x37, 0x62, 0x66, 0x36,
+  0x66, 0x63, 0x34, 0x33, 0x33, 0x36,
+  0x38, 0x61, 0x36, 0x33, 0x34, 0x36,
+  0x37, 0x65, 0x61, 0x33, 0x62, 0x31,
+  0x39, 0x64, 0x30, 0x64, 0x01, 0x00,
+  0x01,
+  0x17, 0x63,  // this is 5987 in hex. specifying a different value seems to
                // cause client to connect on a different port for some commands
-  0x00, 0xFF,
-  0x00, 0x00, 0x05, 0x00, 0x09, 0x78, 
-  0x6C, 0x69, 0x6E, 0x6B, 0x5F, 0x64, 
+  0x00, 0x00, 0x05, 0x00, 0x09, 0x78,
+  0x6C, 0x69, 0x6E, 0x6B, 0x5F, 0x64,
   0x65, 0x76, 0x07, 0x5B, 0xCD, 0x15
 };
 
@@ -70,13 +69,13 @@ uint8_t V6MiLightUdpServer::OPEN_COMMAND_RESPONSE[] = {
   0x80, 0x00, 0x00, 0x00, 0x15,
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // mac address
   0x05, 0x02, 0x00, 0x34, 0x00, 0x00,
-  0x00, 0x00 ,0x00 ,0x00, 0x00, 0x00, 
+  0x00, 0x00 ,0x00 ,0x00, 0x00, 0x00,
   0x00, 0x00, 0x34
 };
 
 V6MiLightUdpServer::~V6MiLightUdpServer() {
   V6Session* cur = firstSession;
-  
+
   while (cur != NULL) {
     V6Session* next = cur->next;
     delete cur;
@@ -88,45 +87,45 @@ template <typename T>
 T V6MiLightUdpServer::readInt(uint8_t* packet) {
   size_t numBytes = sizeof(T);
   T value = 0;
-  
+
   for (size_t i = 0; i < numBytes; i++) {
     value |= packet[i] << (8 * (numBytes - i - 1));
   }
-  
+
   return value;
 }
 
 template <typename T>
 uint8_t* V6MiLightUdpServer::writeInt(const T& value, uint8_t* packet) {
   size_t numBytes = sizeof(T);
-  
+
   for (size_t i = 0; i < numBytes; i++) {
     packet[i] = (value >> (8 * (numBytes - i - 1))) & 0xFF;
   }
-  
+
   return packet + numBytes;
 }
-    
+
 uint16_t V6MiLightUdpServer::beginSession() {
   const uint16_t id = sessionId++;
-  
+
   V6Session* session = new V6Session(socket.remoteIP(), socket.remotePort(), id);
   session->next = firstSession;
   firstSession = session;
-  
+
   if (numSessions >= V6_MAX_SESSIONS) {
     V6Session* cur = firstSession;
-    
+
     for (size_t i = 1; i < V6_MAX_SESSIONS; i++) {
       cur = cur->next;
     }
-    
+
     delete cur->next;
     cur->next = NULL;
   } else {
     numSessions++;
   }
-  
+
   return id;
 }
 
@@ -134,8 +133,8 @@ void V6MiLightUdpServer::handleSearch() {
   const size_t packetLen = size(SEARCH_RESPONSE);
   uint8_t response[packetLen];
   memcpy(response, SEARCH_RESPONSE, packetLen);
-  WiFi.macAddress(response + 6);
-  
+  writeMacAddr(response + 6);
+
   socket.beginPacket(socket.remoteIP(), socket.remotePort());
   socket.write(response, packetLen);
   socket.endPacket();
@@ -145,39 +144,40 @@ void V6MiLightUdpServer::handleStartSession() {
   size_t len = size(START_SESSION_RESPONSE);
   uint8_t response[len];
   uint16_t sessionId = beginSession();
-  
+
   memcpy(response, START_SESSION_RESPONSE, len);
-  WiFi.macAddress(response + 7);
+  writeMacAddr(response + 7);
+
   response[19] = sessionId >> 8;
   response[20] = sessionId & 0xFF;
-  
+
   sendResponse(sessionId, response, len);
 }
-  
+
 bool V6MiLightUdpServer::sendResponse(uint16_t sessionId, uint8_t* responseBuffer, size_t responseSize) {
   V6Session* session = firstSession;
-  
+
   while (session != NULL) {
     if (session->sessionId == sessionId) {
       break;
     }
     session = session->next;
   }
-  
+
   if (session == NULL || session->sessionId != sessionId) {
     Serial.print("Received request with untracked session ID: ");
     Serial.println(sessionId);
     return false;
   }
-  
+
 #ifdef MILIGHT_UDP_DEBUG
   printf_P("Sending response to %s:%d\n", session->ipAddr.toString().c_str(), session->port);
 #endif
-  
+
   socket.beginPacket(session->ipAddr, session->port);
   socket.write(responseBuffer, responseSize);
   socket.endPacket();
-  
+
   return true;
 }
 
@@ -185,11 +185,11 @@ bool V6MiLightUdpServer::handleOpenCommand(uint16_t sessionId) {
   size_t len = size(OPEN_COMMAND_RESPONSE);
   uint8_t response[len];
   memcpy(response, OPEN_COMMAND_RESPONSE, len);
-  WiFi.macAddress(response + 5);
-  
+  writeMacAddr(response + 5);
+
   return sendResponse(sessionId, response, len);
 }
-  
+
 void V6MiLightUdpServer::handleCommand(
   uint16_t sessionId,
   uint8_t sequenceNum,
@@ -197,17 +197,17 @@ void V6MiLightUdpServer::handleCommand(
   uint8_t group,
   uint8_t checksum
 ) {
-  
+
   uint8_t cmdType = readInt<uint8_t>(cmd);
   uint32_t cmdHeader = readInt<uint32_t>(cmd+1);
   uint32_t cmdArg = readInt<uint32_t>(cmd+5);
-  
+
 #ifdef MILIGHT_UDP_DEBUG
   printf_P("Command cmdType: %02X, cmdHeader: %08X, cmdArg: %08X\n", cmdType, cmdHeader, cmdArg);
 #endif
-  
+
   bool handled = false;
-  
+
   if (cmdHeader == 0) {
     handled = handleOpenCommand(sessionId);
   } else {
@@ -220,17 +220,17 @@ void V6MiLightUdpServer::handleCommand(
       cmdArg
     );
   }
-  
+
   if (handled) {
     size_t len = size(COMMAND_RESPONSE);
     memcpy(responseBuffer, COMMAND_RESPONSE, len);
     responseBuffer[6] = sequenceNum;
-    
+
     sendResponse(sessionId, responseBuffer, len);
-    
+
     return;
   }
-  
+
 #ifdef MILIGHT_UDP_DEBUG
   printf_P("V6MiLightUdpServer - Unhandled command: ");
   for (size_t i = 0; i < V6_COMMAND_LEN; i++) {
@@ -243,12 +243,13 @@ void V6MiLightUdpServer::handleCommand(
 void V6MiLightUdpServer::handleHeartbeat(uint16_t sessionId) {
   char header[] = { 0xD8, 0x00, 0x00, 0x00, 0x07 };
   memcpy(responseBuffer, header, size(header));
-  WiFi.macAddress(responseBuffer+5);
-  responseBuffer[11] = 0;
+  writeMacAddr(responseBuffer + 5);
   
+  responseBuffer[11] = 0;
+
   sendResponse(sessionId, responseBuffer, 12);
 }
-  
+
 bool V6MiLightUdpServer::matchesPacket(uint8_t* packet1, size_t packet1Len, uint8_t* packet2, size_t packet2Len) {
   return packet2Len >= packet1Len && memcmp(packet1, packet2, packet1Len) == 0;
 }
@@ -257,7 +258,7 @@ void V6MiLightUdpServer::handlePacket(uint8_t* packet, size_t packetSize) {
 #ifdef MILIGHT_UDP_DEBUG
   printf_P("Packet size: %d\n", packetSize);
 #endif
-  
+
   if (MATCHES_PACKET(START_SESSION_COMMAND)) {
     handleStartSession();
   } else if (MATCHES_PACKET(HEARTBEAT_HEADER) || MATCHES_PACKET(HEARTBEAT_HEADER2)) {
@@ -271,13 +272,19 @@ void V6MiLightUdpServer::handlePacket(uint8_t* packet, size_t packetSize) {
     uint8_t* cmd = packet+10;
     uint8_t group = packet[19];
     uint8_t checksum = packet[21];
-    
+
 #ifdef MILIGHT_UDP_DEBUG
     printf_P("session: %04X, sequence: %d, group: %d, checksum: %d\n", sessionId, sequenceNum, group, checksum);
 #endif
-    
+
     handleCommand(sessionId, sequenceNum, cmd, group, checksum);
   } else {
     Serial.println(F("Unhandled V6 packet"));
   }
-}
+}
+
+void V6MiLightUdpServer::writeMacAddr(uint8_t* packet) {
+  memset(packet, 0, 6);
+  packet[4] = deviceId >> 8;
+  packet[5] = deviceId;
+}

+ 14 - 14
lib/Udp/V6MiLightUdpServer.h

@@ -12,7 +12,7 @@
 #define V6_MAX_SESSIONS 10
 
 #ifndef _V6_MILIGHT_UDP_SERVER
-#define _V6_MILIGHT_UDP_SERVER 
+#define _V6_MILIGHT_UDP_SERVER
 
 struct V6Session {
   V6Session(IPAddress ipAddr, uint16_t port, uint16_t sessionId)
@@ -21,7 +21,7 @@ struct V6Session {
       sessionId(sessionId),
       next(NULL)
   { }
-  
+
   IPAddress ipAddr;
   uint16_t port;
   uint16_t sessionId;
@@ -36,21 +36,21 @@ public:
       numSessions(0),
       firstSession(NULL)
   { }
-  
+
   ~V6MiLightUdpServer();
-  
+
   // Should return size of the response packet
   virtual void handlePacket(uint8_t* packet, size_t packetSize);
-  
+
   template <typename T>
   static T readInt(uint8_t* packet);
-  
+
   template <typename T>
   static uint8_t* writeInt(const T& value, uint8_t* packet);
-    
+
 protected:
   static V6CommandDemuxer COMMAND_DEMUXER PROGMEM;
-  
+
   static uint8_t START_SESSION_COMMAND[] PROGMEM;
   static uint8_t START_SESSION_RESPONSE[] PROGMEM;
   static uint8_t COMMAND_HEADER[] PROGMEM;
@@ -58,21 +58,21 @@ protected:
   static uint8_t LOCAL_SEARCH_COMMAND[] PROGMEM;
   static uint8_t HEARTBEAT_HEADER[] PROGMEM;
   static uint8_t HEARTBEAT_HEADER2[] PROGMEM;
-  
+
   static uint8_t SEARCH_COMMAND[] PROGMEM;
   static uint8_t SEARCH_RESPONSE[] PROGMEM;
-  
+
   static uint8_t OPEN_COMMAND_RESPONSE[] PROGMEM;
-  
+
   V6Session* firstSession;
   size_t numSessions;
   uint16_t sessionId;
-  
+
   uint16_t beginSession();
   bool sendResponse(uint16_t sessionId, uint8_t* responseBuffer, size_t responseSize);
-  
   bool matchesPacket(uint8_t* packet1, size_t packet1Len, uint8_t* packet2, size_t packet2Len);
-  
+  void writeMacAddr(uint8_t* packet);
+
   void handleSearch();
   void handleStartSession();
   bool handleOpenCommand(uint16_t sessionId);

+ 3 - 1
lib/Udp/V6RgbCctCommandHandler.cpp

@@ -31,9 +31,11 @@ bool V6RgbCctCommandHandler::handleCommand(
     uint32_t command,
     uint32_t commandArg)
 {
-  const uint8_t cmd = command & 0xFF;
+  const uint8_t cmd = command & 0x7F;
   const uint8_t arg = commandArg >> 24;
 
+  client->setHeld((command & 0x80) == 0x80);
+
   if (cmd == V2_STATUS) {
     switch (arg) {
       case V2_RGB_CCT_ON:

+ 3 - 1
lib/Udp/V6RgbCommandHandler.cpp

@@ -12,9 +12,11 @@ bool V6RgbCommandHandler::handleCommand(
     uint32_t command,
     uint32_t commandArg)
 {
-  const uint8_t cmd = command & 0xFF;
+  const uint8_t cmd = command & 0x7F;
   const uint8_t arg = commandArg >> 24;
 
+  client->setHeld((command & 0x80) == 0x80);
+
   if (cmd == V2_RGB_COMMAND_PREFIX) {
     switch (arg) {
       case V2_RGB_ON:

+ 3 - 1
lib/Udp/V6RgbwCommandHandler.cpp

@@ -23,9 +23,11 @@ bool V6RgbwCommandHandler::handleCommand(
     uint32_t command,
     uint32_t commandArg)
 {
-  const uint8_t cmd = command & 0xFF;
+  const uint8_t cmd = command & 0x7F;
   const uint8_t arg = commandArg >> 24;
 
+  client->setHeld((command & 0x80) == 0x80);
+
   if (cmd == V2_RGBW_COMMAND_PREFIX) {
     switch (arg) {
       case V2_RGBW_ON:

+ 2 - 2
lib/WebServer/MiLightHttpServer.cpp

@@ -17,8 +17,8 @@ void MiLightHttpServer::begin() {
   server.on("/settings", HTTP_POST, [this]() { server.send(200, "text/plain", "success"); }, handleUpdateFile(SETTINGS_FILE));
   server.on("/radio_configs", HTTP_GET, [this]() { handleGetRadioConfigs(); });
   server.onPattern("/gateway_traffic/:type", HTTP_GET, [this](const UrlTokenBindings* b) { handleListenGateway(b); });
-  server.onPattern("/gateways/:device_id/:type/:group_id", HTTP_PUT, [this](const UrlTokenBindings* b) { handleUpdateGroup(b); });
-  server.onPattern("/raw_commands/:type", HTTP_PUT, [this](const UrlTokenBindings* b) { handleSendRaw(b); });
+  server.onPattern("/gateways/:device_id/:type/:group_id", HTTP_ANY, [this](const UrlTokenBindings* b) { handleUpdateGroup(b); });
+  server.onPattern("/raw_commands/:type", HTTP_ANY, [this](const UrlTokenBindings* b) { handleSendRaw(b); });
   server.onPattern("/download_update/:component", HTTP_GET, [this](const UrlTokenBindings* b) { handleDownloadUpdate(b); });
   server.on("/web", HTTP_POST, [this]() { server.send(200, "text/plain", "success"); }, handleUpdateFile(WEB_INDEX_FILENAME));
   server.on("/about", HTTP_GET, [this]() { handleAbout(); });

+ 23 - 23
lib/WebServer/PatternHandler.cpp

@@ -1,42 +1,42 @@
 #include <PatternHandler.h>
-  
+
 PatternHandler::PatternHandler(
-    const String& pattern, 
-    const HTTPMethod method, 
+    const String& pattern,
+    const HTTPMethod method,
     const PatternHandler::TPatternHandlerFn fn
   ) : method(method), fn(fn), tokenPositions(NULL) {
   Vector<StringToken>* tokenPositions = new Vector<StringToken>();
   tokenize(pattern, tokenPositions);
-  
+
   numPatternTokens = tokenPositions->size();
   patternTokens = new String[numPatternTokens];
-  
+
   for (int i = 0; i < tokenPositions->size(); i++) {
     patternTokens[i] = (*tokenPositions)[i].extract(pattern);
   }
-  
+
   delete tokenPositions;
 }
-  
+
 bool PatternHandler::canHandle(HTTPMethod requestMethod, String requestUri) {
-  if (requestMethod != HTTP_ANY && requestMethod != this->method) {
+  if (this->method != HTTP_ANY && requestMethod != this->method) {
     return false;
   }
-  
+
   if (tokenPositions) {
     delete tokenPositions;
   }
-  
+
   bool canHandle = true;
-  
+
   tokenPositions = new Vector<StringToken>();
   tokenize(requestUri, tokenPositions);
-  
+
   if (numPatternTokens == tokenPositions->size()) {
     for (int i = 0; i < numPatternTokens; i++) {
       const StringToken urlTokenP = (*tokenPositions)[i];
-      
-      if (!patternTokens[i].startsWith(":") 
+
+      if (!patternTokens[i].startsWith(":")
         && patternTokens[i] != urlTokenP.extract(requestUri)) {
         canHandle = false;
         break;
@@ -45,25 +45,25 @@ bool PatternHandler::canHandle(HTTPMethod requestMethod, String requestUri) {
   } else {
     canHandle = false;
   }
-  
+
   return canHandle;
 }
-  
+
 bool PatternHandler::handle(ESP8266WebServer& server, HTTPMethod requestMethod, String requestUri) {
   if (! canHandle(requestMethod, requestUri)) {
     return false;
   }
-  
+
   UrlTokenBindings* bindings = new UrlTokenBindings(patternTokens, tokenPositions, requestUri);
   fn(bindings);
-  
+
   delete bindings;
 }
 
 void PatternHandler::tokenize(const String& path, Vector<StringToken>* tokenPositions) {
   int lastStart = 0;
   int currentPosition = 0;
-  
+
   for (int i = 0; i < path.length(); i++) {
     if (path.charAt(i) == '/' || i == path.length()-1) {
       // If we're in the last position, include the last character if it isn't
@@ -71,15 +71,15 @@ void PatternHandler::tokenize(const String& path, Vector<StringToken>* tokenPosi
       if (path.charAt(i) != '/') {
         currentPosition++;
       }
-      
+
       if (lastStart > 0 && currentPosition > lastStart) {
         StringToken token(lastStart, currentPosition);
         tokenPositions->push_back(token);
       }
-      
+
       lastStart = i+1;
     }
-      
+
     currentPosition++;
   }
-}
+}

+ 2 - 2
lib/WebServer/WebServer.cpp

@@ -16,7 +16,7 @@ void WebServer::disableAuthentication() {
 }
 
 void WebServer::_handleRequest() {
-  if (this->authEnabled 
+  if (this->authEnabled
     && !this->authenticate(this->username.c_str(), this->password.c_str())) {
     this->requestAuthentication();
   } else {
@@ -82,4 +82,4 @@ void WebServer::handleClient() {
       return;
     }
   }
-}
+}