Explorar o código

Add hex field color

Christopher Mullins %!s(int64=6) %!d(string=hai) anos
pai
achega
6c16e258e5

+ 1 - 1
lib/MiLight/MiLightClient.cpp

@@ -280,7 +280,7 @@ void MiLightClient::updateColor(JsonVariant json) {
   ParsedColor color = ParsedColor::fromJson(json);
 
   if (!color.success) {
-    Serial.println(F("Error parsing JSON color"));
+    Serial.println(F("Error parsing color field, unrecognized format"));
     return;
   }
 

+ 17 - 0
lib/MiLightState/GroupState.cpp

@@ -172,6 +172,7 @@ bool GroupState::clearField(GroupStateField field) {
     case GroupStateField::COLOR:
     case GroupStateField::HUE:
     case GroupStateField::OH_COLOR:
+    case GroupStateField::HEX_COLOR:
       clearedAny = isSetHue();
       state.fields._isSetHue = 0;
       break;
@@ -228,6 +229,7 @@ bool GroupState::isSetField(GroupStateField field) const {
     case GroupStateField::COLOR:
     case GroupStateField::HUE:
     case GroupStateField::OH_COLOR:
+    case GroupStateField::HEX_COLOR:
       return isSetHue();
     case GroupStateField::SATURATION:
       return isSetSaturation();
@@ -823,6 +825,15 @@ void GroupState::applyOhColor(JsonObject state) const {
   state[GroupStateFieldNames::COLOR] = ohColorStr;
 }
 
+void GroupState::applyHexColor(JsonObject state) const {
+  ParsedColor color = getColor();
+
+  char hexColor[8];
+  sprintf(hexColor, "#%02X%02X%02X", color.r, color.g, color.b);
+
+  state[GroupStateFieldNames::COLOR] = hexColor;
+}
+
 // gather partial state for a single field; see GroupState::applyState to gather many fields
 void GroupState::applyField(JsonObject partialState, const BulbId& bulbId, GroupStateField field) const {
   if (isSetField(field)) {
@@ -856,6 +867,12 @@ void GroupState::applyField(JsonObject partialState, const BulbId& bulbId, Group
         }
         break;
 
+      case GroupStateField::HEX_COLOR:
+        if (getBulbMode() == BULB_MODE_COLOR) {
+          applyHexColor(partialState);
+        }
+        break;
+
       case GroupStateField::COMPUTED_COLOR:
         if (getBulbMode() == BULB_MODE_COLOR) {
           applyColor(partialState);

+ 2 - 0
lib/MiLightState/GroupState.h

@@ -216,6 +216,8 @@ private:
   void applyColor(JsonObject state) const;
   // Apply OpenHAB-style color, e.g., {"color":"0,0,0"}
   void applyOhColor(JsonObject state) const;
+  // Apply hex color, e.g., {"color":"#FF0000"}
+  void applyHexColor(JsonObject state) const;
 };
 
 extern const BulbId DEFAULT_BULB_ID;

+ 2 - 1
lib/Types/GroupStateField.cpp

@@ -19,7 +19,8 @@ static const char* STATE_NAMES[] = {
   GroupStateFieldNames::DEVICE_ID,
   GroupStateFieldNames::GROUP_ID,
   GroupStateFieldNames::DEVICE_TYPE,
-  GroupStateFieldNames::OH_COLOR
+  GroupStateFieldNames::OH_COLOR,
+  GroupStateFieldNames::HEX_COLOR
 };
 
 GroupStateField GroupStateFieldHelpers::getFieldByName(const char* name) {

+ 3 - 1
lib/Types/GroupStateField.h

@@ -21,6 +21,7 @@ namespace GroupStateFieldNames {
   static const char GROUP_ID[] = "group_id";
   static const char DEVICE_TYPE[] = "device_type";
   static const char OH_COLOR[] = "oh_color";
+  static const char HEX_COLOR[] = "hex_color";
   static const char COMMAND[] = "command";
   static const char COMMANDS[] = "commands";
 };
@@ -43,7 +44,8 @@ enum class GroupStateField {
   DEVICE_ID,
   GROUP_ID,
   DEVICE_TYPE,
-  OH_COLOR
+  OH_COLOR,
+  HEX_COLOR
 };
 
 class GroupStateFieldHelpers {

+ 20 - 10
lib/Types/ParsedColor.cpp

@@ -2,6 +2,7 @@
 #include <RGBConverter.h>
 #include <TokenIterator.h>
 #include <GroupStateField.h>
+#include <IntParsing.h>
 
 ParsedColor ParsedColor::fromRgb(uint16_t r, uint16_t g, uint16_t b) {
   double hsv[3];
@@ -34,19 +35,28 @@ ParsedColor ParsedColor::fromJson(JsonVariant json) {
     const char* colorStr = json.as<const char*>();
     const size_t len = strlen(colorStr);
 
-    char colorCStr[len+1];
-    uint8_t parsedRgbColors[3] = {0, 0, 0};
+    if (colorStr[0] == '#' && len == 7) {
+      uint8_t parsedHex[3];
+      hexStrToBytes<uint8_t>(colorStr+1, len-1, parsedHex, 3);
 
-    strcpy(colorCStr, colorStr);
-    TokenIterator colorValueItr(colorCStr, len, ',');
+      r = parsedHex[0];
+      g = parsedHex[1];
+      b = parsedHex[2];
+    } else {
+      char colorCStr[len+1];
+      uint8_t parsedRgbColors[3] = {0, 0, 0};
 
-    for (size_t i = 0; i < 3 && colorValueItr.hasNext(); ++i) {
-      parsedRgbColors[i] = atoi(colorValueItr.nextToken());
-    }
+      strcpy(colorCStr, colorStr);
+      TokenIterator colorValueItr(colorCStr, len, ',');
+
+      for (size_t i = 0; i < 3 && colorValueItr.hasNext(); ++i) {
+        parsedRgbColors[i] = atoi(colorValueItr.nextToken());
+      }
 
-    r = parsedRgbColors[0];
-    g = parsedRgbColors[1];
-    b = parsedRgbColors[2];
+      r = parsedRgbColors[0];
+      g = parsedRgbColors[1];
+      b = parsedRgbColors[2];
+    }
   } else {
     Serial.println(F("GroupState::parseJsonColor - unknown format for color"));
     return ParsedColor{ .success = false };

+ 25 - 0
test/remote/spec/state_spec.rb

@@ -350,6 +350,31 @@ RSpec.describe 'State' do
       expect(state.select { |x| desired_state.include?(x) } ).to eq(desired_state)
     end
 
+    it 'should support hex colors' do
+      {
+        'FF0000': 0,
+        '00FF00': 120,
+        '0000FF': 240
+      }.each do |hex_color, hue|
+        state = @client.patch_state({status: 'ON', color: "##{hex_color}"}, @id_params)
+        expect(state['hue']).to eq(hue), "Hex color #{hex_color} should map to hue = #{hue}, but was #{state['hue'].inspect}"
+      end
+    end
+
+    it 'should support getting color in hex format' do
+      fields = @client.get('/settings')['group_state_fields']
+      @client.patch_settings({group_state_fields: fields + ['hex_color']})
+      state = @client.patch_state({status: 'ON', color: '#FF0000'}, @id_params)
+      expect(state['color']).to eq('#FF0000')
+    end
+
+    it 'should support getting color in comma-separated format' do
+      fields = @client.get('/settings')['group_state_fields']
+      @client.patch_settings({group_state_fields: fields+['oh_color']})
+      state = @client.patch_state({status: 'ON', color: '#FF0000'}, @id_params)
+      expect(state['color']).to eq('255,0,0')
+    end
+
     it 'should support separate brightness fields for different modes' do
       desired_state = {
         'hue' => 0,