Ver código fonte

merge upstream, resolve conflict

Christopher Mullins 6 anos atrás
pai
commit
43227d674f

Diferenças do arquivo suprimidas por serem muito extensas
+ 2 - 2
dist/index.html.gz.h


+ 86 - 0
lib/MiLight/FUT020PacketFormatter.cpp

@@ -0,0 +1,86 @@
+#include <FUT020PacketFormatter.h>
+#include <Units.h>
+
+void FUT020PacketFormatter::updateColorRaw(uint8_t color) {
+  command(static_cast<uint8_t>(FUT020Command::COLOR), color);
+}
+
+void FUT020PacketFormatter::updateHue(uint16_t hue) {
+  uint16_t remapped = Units::rescale<uint16_t, uint16_t>(hue, 255.0, 360.0);
+  remapped = (remapped + 0xB0) % 0x100;
+
+  updateColorRaw(remapped);
+}
+
+void FUT020PacketFormatter::updateColorWhite() {
+  command(static_cast<uint8_t>(FUT020Command::COLOR_WHITE_TOGGLE), 0);
+}
+
+void FUT020PacketFormatter::nextMode() {
+  command(static_cast<uint8_t>(FUT020Command::MODE_SWITCH), 0);
+}
+
+void FUT020PacketFormatter::updateBrightness(uint8_t value) {
+  const GroupState* state = this->stateStore->get(deviceId, groupId, MiLightRemoteType::REMOTE_TYPE_FUT020);
+  int8_t knownValue = (state != NULL && state->isSetBrightness()) ? state->getBrightness() : -1;
+
+  valueByStepFunction(
+    &PacketFormatter::increaseBrightness,
+    &PacketFormatter::decreaseBrightness,
+    FUT02xPacketFormatter::NUM_BRIGHTNESS_INTERVALS,
+    value / FUT02xPacketFormatter::NUM_BRIGHTNESS_INTERVALS,
+    knownValue / FUT02xPacketFormatter::NUM_BRIGHTNESS_INTERVALS
+  );
+}
+
+void FUT020PacketFormatter::increaseBrightness() {
+  command(static_cast<uint8_t>(FUT020Command::BRIGHTNESS_UP), 0);
+}
+
+void FUT020PacketFormatter::decreaseBrightness() {
+  command(static_cast<uint8_t>(FUT020Command::BRIGHTNESS_DOWN), 0);
+}
+
+void FUT020PacketFormatter::updateStatus(MiLightStatus status, uint8_t groupId) {
+  command(static_cast<uint8_t>(FUT020Command::ON_OFF), 0);
+}
+
+BulbId FUT020PacketFormatter::parsePacket(const uint8_t* packet, JsonObject result) {
+  FUT020Command command = static_cast<FUT020Command>(packet[FUT02xPacketFormatter::FUT02X_COMMAND_INDEX] & 0x0F);
+
+  BulbId bulbId(
+    (packet[1] << 8) | packet[2],
+    0,
+    REMOTE_TYPE_FUT020
+  );
+
+  switch (command) {
+    case FUT020Command::ON_OFF:
+      result[F("state")] = F("ON");
+      break;
+
+    case FUT020Command::BRIGHTNESS_DOWN:
+      result[F("command")] = F("brightness_down");
+      break;
+
+    case FUT020Command::BRIGHTNESS_UP:
+      result[F("command")] = F("brightness_up");
+      break;
+
+    case FUT020Command::MODE_SWITCH:
+      result[F("command")] = F("next_mode");
+      break;
+
+    case FUT020Command::COLOR_WHITE_TOGGLE:
+      result[F("command")] = F("color_white_toggle");
+      break;
+
+    case FUT020Command::COLOR:
+      uint16_t remappedColor = Units::rescale<uint16_t, uint16_t>(packet[FUT02xPacketFormatter::FUT02X_ARGUMENT_INDEX], 360.0, 255.0);
+      remappedColor = (remappedColor + 113) % 360;
+      result[GroupStateFieldNames::HUE] = remappedColor;
+      break;
+  }
+
+  return bulbId;
+}

+ 30 - 0
lib/MiLight/FUT020PacketFormatter.h

@@ -0,0 +1,30 @@
+#include <FUT02xPacketFormatter.h>
+
+#pragma once
+
+enum class FUT020Command {
+  ON_OFF             = 0x04,
+  MODE_SWITCH        = 0x02,
+  COLOR_WHITE_TOGGLE = 0x05,
+  BRIGHTNESS_DOWN    = 0x01,
+  BRIGHTNESS_UP      = 0x03,
+  COLOR              = 0x00
+};
+
+class FUT020PacketFormatter : public FUT02xPacketFormatter {
+public:
+  FUT020PacketFormatter()
+    : FUT02xPacketFormatter(REMOTE_TYPE_FUT020)
+  { }
+
+  virtual void updateStatus(MiLightStatus status, uint8_t groupId);
+  virtual void updateHue(uint16_t value);
+  virtual void updateColorRaw(uint8_t value);
+  virtual void updateColorWhite();
+  virtual void nextMode();
+  virtual void updateBrightness(uint8_t value);
+  virtual void increaseBrightness();
+  virtual void decreaseBrightness();
+
+  virtual BulbId parsePacket(const uint8_t* packet, JsonObject result) override;
+};

+ 50 - 0
lib/MiLight/FUT02xPacketFormatter.cpp

@@ -0,0 +1,50 @@
+#include <FUT02xPacketFormatter.h>
+
+static const uint8_t FUT02X_PACKET_HEADER = 0xA5;
+
+static const uint8_t FUT02X_PAIR_COMMAND = 0x03;
+static const uint8_t FUT02X_UNPAIR_COMMAND = 0x03;
+
+void FUT02xPacketFormatter::initializePacket(uint8_t *packet) {
+  size_t packetPtr = 0;
+
+  packet[packetPtr++] = 0xA5;
+  packet[packetPtr++] = deviceId >> 8;
+  packet[packetPtr++] = deviceId & 0xFF;
+  packet[packetPtr++] = 0; // arg
+  packet[packetPtr++] = 0; // command
+  packet[packetPtr++] = sequenceNum++;
+}
+
+bool FUT02xPacketFormatter::canHandle(const uint8_t* packet, const size_t len) {
+  return len == packetLength && packet[0] == FUT02X_PACKET_HEADER;
+}
+
+void FUT02xPacketFormatter::command(uint8_t command, uint8_t arg) {
+  pushPacket();
+  if (held) {
+    command |= 0x10;
+  }
+  currentPacket[FUT02X_COMMAND_INDEX] = command;
+  currentPacket[FUT02X_ARGUMENT_INDEX] = arg;
+}
+
+void FUT02xPacketFormatter::pair() {
+  for (size_t i = 0; i < 5; i++) {
+    command(FUT02X_PAIR_COMMAND, 0);
+  }
+}
+
+void FUT02xPacketFormatter::unpair() {
+  for (size_t i = 0; i < 5; i++) {
+    command(FUT02X_PAIR_COMMAND, 0);
+  }
+}
+
+void FUT02xPacketFormatter::format(uint8_t const* packet, char* buffer) {
+  buffer += sprintf_P(buffer, PSTR("b0       : %02X\n"), packet[0]);
+  buffer += sprintf_P(buffer, PSTR("ID       : %02X%02X\n"), packet[1], packet[2]);
+  buffer += sprintf_P(buffer, PSTR("Arg      : %02X\n"), packet[3]);
+  buffer += sprintf_P(buffer, PSTR("Command  : %02X\n"), packet[4]);
+  buffer += sprintf_P(buffer, PSTR("Sequence : %02X\n"), packet[5]);
+}

+ 24 - 0
lib/MiLight/FUT02xPacketFormatter.h

@@ -0,0 +1,24 @@
+#include <PacketFormatter.h>
+
+#pragma once
+
+class FUT02xPacketFormatter : public PacketFormatter {
+public:
+  static const uint8_t FUT02X_COMMAND_INDEX = 4;
+  static const uint8_t FUT02X_ARGUMENT_INDEX = 3;
+  static const uint8_t NUM_BRIGHTNESS_INTERVALS = 8;
+
+  FUT02xPacketFormatter(MiLightRemoteType type)
+    : PacketFormatter(type, 6, 10)
+  { }
+
+  virtual bool canHandle(const uint8_t* packet, const size_t len) override;
+
+  virtual void command(uint8_t command, uint8_t arg) override;
+
+  virtual void pair() override;
+  virtual void unpair() override;
+
+  virtual void initializePacket(uint8_t* packet) override;
+  virtual void format(uint8_t const* packet, char* buffer) override;
+};

+ 12 - 1
lib/MiLight/MiLightRemoteConfig.cpp

@@ -10,9 +10,12 @@ const MiLightRemoteConfig* MiLightRemoteConfig::ALL_REMOTES[] = {
   &FUT092Config, // rgb+cct
   &FUT098Config, // rgb
   &FUT089Config, // 8-group rgb+cct (b8, fut089)
-  &FUT091Config
+  &FUT091Config,
+  &FUT020Config
 };
 
+const size_t MiLightRemoteConfig::NUM_REMOTES = size(ALL_REMOTES);
+
 const MiLightRemoteConfig* MiLightRemoteConfig::fromType(const String& type) {
   return fromType(MiLightRemoteTypeHelpers::remoteTypeFromString(type));
 }
@@ -95,3 +98,11 @@ const MiLightRemoteConfig FUT098Config( //rgb
   "rgb",
   0
 );
+
+const MiLightRemoteConfig FUT020Config(
+  new FUT020PacketFormatter(),
+  MiLightRadioConfig::ALL_CONFIGS[4],
+  REMOTE_TYPE_FUT020,
+  "fut020",
+  0
+);

+ 4 - 2
lib/MiLight/MiLightRemoteConfig.h

@@ -7,6 +7,7 @@
 #include <CctPacketFormatter.h>
 #include <FUT089PacketFormatter.h>
 #include <FUT091PacketFormatter.h>
+#include <FUT020PacketFormatter.h>
 #include <PacketFormatter.h>
 
 #ifndef _MILIGHT_REMOTE_CONFIG_H
@@ -37,8 +38,8 @@ public:
   static const MiLightRemoteConfig* fromType(const String& type);
   static const MiLightRemoteConfig* fromReceivedPacket(const MiLightRadioConfig& radioConfig, const uint8_t* packet, const size_t len);
 
-  static const size_t NUM_REMOTES = 6;
-  static const MiLightRemoteConfig* ALL_REMOTES[NUM_REMOTES];
+  static const size_t NUM_REMOTES;
+  static const MiLightRemoteConfig* ALL_REMOTES[];
 };
 
 extern const MiLightRemoteConfig FUT096Config; //rgbw
@@ -47,5 +48,6 @@ extern const MiLightRemoteConfig FUT092Config; //rgb+cct
 extern const MiLightRemoteConfig FUT089Config; //rgb+cct B8 / FUT089
 extern const MiLightRemoteConfig FUT098Config; //rgb
 extern const MiLightRemoteConfig FUT091Config; //v2 cct
+extern const MiLightRemoteConfig FUT020Config;
 
 #endif

+ 20 - 6
lib/MiLightState/GroupState.cpp

@@ -32,16 +32,30 @@ static const GroupStateField ALL_SCRATCH_FIELDS[] = {
 // Number of units each increment command counts for
 static const uint8_t INCREMENT_COMMAND_VALUE = 10;
 
-const GroupState& GroupState::defaultState(MiLightRemoteType remoteType) {
-  static GroupState instances[MiLightRemoteConfig::NUM_REMOTES];
-  GroupState& state = instances[remoteType];
+static const GroupState DEFAULT_STATE = GroupState();
+static const GroupState DEFAULT_RGB_ONLY_STATE = GroupState::initDefaultRgbState();
+static const GroupState DEFAULT_WHITE_ONLY_STATE = GroupState::initDefaultWhiteState();
 
+GroupState GroupState::initDefaultRgbState() {
+  GroupState state;
+  state.setBulbMode(BULB_MODE_COLOR);
+  return state;
+}
+
+GroupState GroupState::initDefaultWhiteState() {
+  GroupState state;
+  state.setBulbMode(BULB_MODE_WHITE);
+  return state;
+}
+
+const GroupState& GroupState::defaultState(MiLightRemoteType remoteType) {
   switch (remoteType) {
     case REMOTE_TYPE_RGB:
-      state.setBulbMode(BULB_MODE_COLOR);
+      return DEFAULT_RGB_ONLY_STATE;
       break;
     case REMOTE_TYPE_CCT:
-      state.setBulbMode(BULB_MODE_WHITE);
+    case REMOTE_TYPE_FUT091:
+      return DEFAULT_WHITE_ONLY_STATE;
       break;
 
     default:
@@ -49,7 +63,7 @@ const GroupState& GroupState::defaultState(MiLightRemoteType remoteType) {
       break;
   }
 
-  return state;
+  return DEFAULT_STATE;
 }
 
 void GroupState::initFields() {

+ 2 - 0
lib/MiLightState/GroupState.h

@@ -154,6 +154,8 @@ public:
   void debugState(char const *debugMessage) const;
 
   static const GroupState& defaultState(MiLightRemoteType remoteType);
+  static GroupState initDefaultRgbState();
+  static GroupState initDefaultWhiteState();
   static bool isPhysicalField(GroupStateField field);
 
 private:

+ 1 - 0
lib/MiLightState/GroupStateStore.cpp

@@ -89,6 +89,7 @@ void GroupStateStore::clear(const BulbId& bulbId) {
 
   if (state != NULL) {
     state->initFields();
+    state->patch(GroupState::defaultState(bulbId.deviceType));
   }
 }
 

+ 2 - 1
lib/Radio/MiLightRadioConfig.cpp

@@ -4,5 +4,6 @@ MiLightRadioConfig MiLightRadioConfig::ALL_CONFIGS[] = {
   MiLightRadioConfig(0x147A, 0x258B, 7, 9, 40, 71, 0xAA, 0x05), // rgbw
   MiLightRadioConfig(0x050A, 0x55AA, 7, 4, 39, 74, 0xAA, 0x05), // cct
   MiLightRadioConfig(0x7236, 0x1809, 9, 8, 39, 70, 0xAA, 0x05), // rgb+cct, fut089
-  MiLightRadioConfig(0x9AAB, 0xBCCD, 6, 3, 38, 73, 0x55, 0x0A)  // rgb
+  MiLightRadioConfig(0x9AAB, 0xBCCD, 6, 3, 38, 73, 0x55, 0x0A), // rgb
+  MiLightRadioConfig(0x50A0, 0xAA55, 6, 6, 41, 76, 0xAA, 0x0A)  // FUT020
 };

+ 1 - 1
lib/Radio/MiLightRadioConfig.h

@@ -76,7 +76,7 @@ public:
 
   const size_t packetLength;
 
-  static const size_t NUM_CONFIGS = 4;
+  static const size_t NUM_CONFIGS = 5;
   static MiLightRadioConfig ALL_CONFIGS[NUM_CONFIGS];
 };
 

+ 0 - 1
lib/Transitions/FieldTransition.cpp

@@ -11,7 +11,6 @@ FieldTransition::Builder::Builder(size_t id, const BulbId& bulbId, TransitionFn
 { }
 
 std::shared_ptr<Transition> FieldTransition::Builder::_build() const {
-  size_t duration = getOrComputeDuration();
   size_t numPeriods = getOrComputeNumPeriods();
   size_t period = getOrComputePeriod();
   int16_t distance = end - start;

+ 7 - 0
lib/Types/MiLightRemoteType.cpp

@@ -7,6 +7,7 @@ static const char* REMOTE_NAME_RGB_CCT = "rgb_cct";
 static const char* REMOTE_NAME_FUT089  = "fut089";
 static const char* REMOTE_NAME_RGB     = "rgb";
 static const char* REMOTE_NAME_FUT091  = "fut091";
+static const char* REMOTE_NAME_FUT020  = "fut020";
 
 const MiLightRemoteType MiLightRemoteTypeHelpers::remoteTypeFromString(const String& type) {
   if (type.equalsIgnoreCase(REMOTE_NAME_RGBW) || type.equalsIgnoreCase("fut096")) {
@@ -33,6 +34,10 @@ const MiLightRemoteType MiLightRemoteTypeHelpers::remoteTypeFromString(const Str
     return REMOTE_TYPE_FUT091;
   }
 
+  if (type.equalsIgnoreCase(REMOTE_NAME_FUT020)) {
+    return REMOTE_TYPE_FUT020;
+  }
+
   Serial.print(F("remoteTypeFromString: ERROR - tried to fetch remote config for type: "));
   Serial.println(type);
 
@@ -53,6 +58,8 @@ const String MiLightRemoteTypeHelpers::remoteTypeToString(const MiLightRemoteTyp
       return REMOTE_NAME_RGB;
     case REMOTE_TYPE_FUT091:
       return REMOTE_NAME_FUT091;
+    case REMOTE_TYPE_FUT020:
+      return REMOTE_NAME_FUT020;
     default:
       Serial.print(F("remoteTypeToString: ERROR - tried to fetch remote config name for unknown type: "));
       Serial.println(type);

+ 2 - 1
lib/Types/MiLightRemoteType.h

@@ -9,7 +9,8 @@ enum MiLightRemoteType {
   REMOTE_TYPE_RGB_CCT = 2,
   REMOTE_TYPE_RGB     = 3,
   REMOTE_TYPE_FUT089  = 4,
-  REMOTE_TYPE_FUT091  = 5
+  REMOTE_TYPE_FUT091  = 5,
+  REMOTE_TYPE_FUT020  = 6
 };
 
 class MiLightRemoteTypeHelpers {

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

@@ -15,6 +15,26 @@ RSpec.describe 'State' do
     @client.delete_state(@id_params)
   end
 
+  context 'initial state' do
+    it 'should assume white mode for device types that are white-only' do
+      %w(cct fut091).each do |type|
+        id = @id_params.merge(type: type)
+        @client.delete_state(id)
+        state = @client.patch_state({status: 'ON'}, id)
+        expect(state['bulb_mode']).to eq('white'), "it should assume white mode for #{type}"
+      end
+    end
+
+    it 'should assume color mode for device types that are rgb-only' do
+      %w(rgb).each do |type|
+        id = @id_params.merge(type: type)
+        @client.delete_state(id)
+        state = @client.patch_state({status: 'ON'}, id)
+        expect(state['bulb_mode']).to eq('color'), "it should assume color mode for #{type}"
+      end
+    end
+  end
+
   context 'toggle command' do
     it 'should toggle ON to OFF' do
       init_state = @client.patch_state({'status' => 'ON'}, @id_params)
@@ -461,4 +481,22 @@ RSpec.describe 'State' do
       end
     end
   end
+
+  context 'fut020' do
+    it 'should support fut020 commands' do
+      id = @id_params.merge(type: 'fut020', group_id: 0)
+      @client.delete_state(id)
+      state = @client.patch_state({status: 'ON'}, id)
+
+      expect(state['status']).to eq('ON')
+    end
+
+    it 'should assume the "off" command sets state to on... commands are the same' do
+      id = @id_params.merge(type: 'fut020', group_id: 0)
+      @client.delete_state(id)
+      state = @client.patch_state({status: 'OFF'}, id)
+
+      expect(state['status']).to eq('ON')
+    end
+  end
 end

+ 6 - 3
web/src/index.html

@@ -149,13 +149,16 @@
             <li data-value="fut091">
               <a href="#">FUT091 / B2</a>
             </li>
+            <li data-value="fut020">
+              <a href="#">FUT020</a>
+            </li>
           </ul>
         </div>
       </div>
     </div>
 
     <div class="row"><div class="col-sm-12">
-    <div class="mode-option" data-for="rgbw,rgb_cct,rgb,fut089">
+    <div class="mode-option" data-for="rgbw,rgb_cct,rgb,fut089,fut020">
       <div class="row">
         <div class="col-sm-12">
           <h5>Hue</h5>
@@ -237,7 +240,7 @@
           <li>
             <input type="checkbox" name="status" class="raw-update" data-toggle="toggle" checked/>
           </li>
-          <div class="mode-option inline" data-for="rgbw,rgb_cct,fut089">
+          <div class="mode-option inline" data-for="rgbw,rgb_cct,fut089,fut020">
             <li>
               <button type="button" class="btn btn-secondary command-btn" data-command="set_white">White</button>
             </li>
@@ -260,7 +263,7 @@
         </ul>
         <p></p>
         <ul class="command-buttons">
-          <div class="mode-option inline" data-for="rgb">
+          <div class="mode-option inline" data-for="rgb,fut020">
             <li>
               <div class="plus-minus-group">
                 <button type="button" class="btn btn-default btn-number command-btn" data-command="previous_mode">