瀏覽代碼

Implement state key filter

Christopher Mullins 8 年之前
父節點
當前提交
3a761d1837

+ 1 - 1
lib/MQTT/BulbStateUpdater.cpp

@@ -32,7 +32,7 @@ inline void BulbStateUpdater::flushGroup(BulbId bulbId, GroupState& state) {
   char buffer[200];
   StaticJsonBuffer<200> jsonBuffer;
   JsonObject& message = jsonBuffer.createObject();
-  state.applyState(message);
+  state.applyState(message, settings.groupStateFields, settings.numGroupStateFields);
   message.printTo(buffer);
 
   mqttClient.sendState(

+ 98 - 32
lib/MiLightState/GroupState.cpp

@@ -76,6 +76,31 @@ GroupState::GroupState() {
   state.fields._mqttDirty            = 0;
 }
 
+bool GroupState::isSetField(GroupStateField field) const {
+  switch (field) {
+    case GroupStateField::STATE:
+    case GroupStateField::STATUS:
+      return isSetState();
+    case GroupStateField::BRIGHTNESS:
+    case GroupStateField::LEVEL:
+      return isSetBrightness();
+    case GroupStateField::COLOR:
+    case GroupStateField::HUE:
+      return isSetHue();
+    case GroupStateField::SATURATION:
+      return isSetSaturation();
+    case GroupStateField::MODE:
+      return isSetMode();
+    case GroupStateField::KELVIN:
+    case GroupStateField::COLOR_TEMP:
+      return isSetKelvin();
+    case GroupStateField::BULB_MODE:
+      return isSetBulbMode();
+  }
+
+  return false;
+}
+
 bool GroupState::isSetState() const { return state.fields._isSetState; }
 MiLightStatus GroupState::getState() const { return state.fields._state ? ON : OFF; }
 bool GroupState::setState(const MiLightStatus status) {
@@ -287,38 +312,79 @@ bool GroupState::patch(const JsonObject& state) {
   return changes;
 }
 
-void GroupState::applyState(JsonObject& partialState) {
-  if (isSetState()) {
-    partialState["state"] = getState() == ON ? "ON" : "OFF";
-  }
-  if (isSetBrightness()) {
-    partialState["brightness"] = Units::rescale(getBrightness(), 255, 100);
-  }
-  if (isSetBulbMode()) {
-    partialState["bulb_mode"] = BULB_MODE_NAMES[getBulbMode()];
-
-    if (getBulbMode() == BULB_MODE_COLOR) {
-      if (isSetHue() && isSetSaturation()) {
-        uint8_t rgb[3];
-        RGBConverter converter;
-        converter.hsvToRgb(getHue()/360.0, getSaturation()/100.0, 1, rgb);
-        JsonObject& color = partialState.createNestedObject("color");
-        color["r"] = rgb[0];
-        color["g"] = rgb[1];
-        color["b"] = rgb[2];
-      } else if (isSetHue()) {
-        partialState["hue"] = getHue();
-      } else if (isSetSaturation()) {
-        partialState["saturation"] = getSaturation();
-      }
-    } else if (getBulbMode() == BULB_MODE_SCENE) {
-      if (isSetMode()) {
-        partialState["mode"] = getMode();
-      }
-    } else if (getBulbMode() == BULB_MODE_WHITE) {
-      if (isSetKelvin()) {
-        partialState["color_temp"] = getMireds();
-      }
+void GroupState::applyField(JsonObject& partialState, GroupStateField field) {
+  if (isSetField(field)) {
+    switch (field) {
+      case GroupStateField::STATE:
+      case GroupStateField::STATUS:
+        partialState[GroupStateFieldHelpers::getFieldName(field)] = getState() == ON ? "ON" : "OFF";
+        break;
+
+      case GroupStateField::BRIGHTNESS:
+        partialState["brightness"] = Units::rescale(getBrightness(), 255, 100);
+        break;
+
+      case GroupStateField::LEVEL:
+        partialState["level"] = getBrightness();
+        break;
+
+      case GroupStateField::BULB_MODE:
+        partialState["bulb_mode"] = BULB_MODE_NAMES[getBulbMode()];
+        break;
+
+      case GroupStateField::COLOR:
+        if (getBulbMode() == BULB_MODE_COLOR) {
+          uint8_t rgb[3];
+          RGBConverter converter;
+          converter.hsvToRgb(
+            getHue()/360.0,
+            // Default to fully saturated
+            (isSetSaturation() ? getSaturation() : 100)/100.0,
+            1,
+            rgb
+          );
+          JsonObject& color = partialState.createNestedObject("color");
+          color["r"] = rgb[0];
+          color["g"] = rgb[1];
+          color["b"] = rgb[2];
+        }
+        break;
+
+      case GroupStateField::HUE:
+        if (getBulbMode() == BULB_MODE_COLOR) {
+          partialState["hue"] = getHue();
+        }
+        break;
+
+      case GroupStateField::SATURATION:
+        if (getBulbMode() == BULB_MODE_COLOR) {
+          partialState["saturation"] = getSaturation();
+        }
+        break;
+
+      case GroupStateField::MODE:
+        if (getBulbMode() == BULB_MODE_SCENE) {
+          partialState["mode"] = getMode();
+        }
+        break;
+
+      case GroupStateField::COLOR_TEMP:
+        if (getBulbMode() == BULB_MODE_WHITE) {
+          partialState["color_temp"] = getMireds();
+        }
+        break;
+
+      case GroupStateField::KELVIN:
+        if (getBulbMode() == BULB_MODE_WHITE) {
+          partialState["kelvin"] = getKelvin();
+        }
+        break;
     }
   }
 }
+
+void GroupState::applyState(JsonObject& partialState, GroupStateField* fields, size_t numFields) {
+  for (size_t i = 0; i < numFields; i++) {
+    applyField(partialState, fields[i]);
+  }
+}

+ 7 - 1
lib/MiLightState/GroupState.h

@@ -1,6 +1,8 @@
 #include <stddef.h>
 #include <inttypes.h>
 #include <MiLightConstants.h>
+#include <MiLightRadioConfig.h>
+#include <GroupStateField.h>
 #include <ArduinoJson.h>
 
 #ifndef _GROUP_STATE_H
@@ -33,8 +35,11 @@ static const char* BULB_MODE_NAMES[] = {
 
 class GroupState {
 public:
+
   GroupState();
 
+  bool isSetField(GroupStateField field) const;
+
   // 1 bit
   bool isSetState() const;
   MiLightStatus getState() const;
@@ -81,7 +86,8 @@ public:
   bool clearMqttDirty();
 
   bool patch(const JsonObject& state);
-  void applyState(JsonObject& state);
+  void applyField(JsonObject& state, GroupStateField field);
+  void applyState(JsonObject& state, GroupStateField* fields, size_t numFields);
 
   void load(Stream& stream);
   void dump(Stream& stream) const;

+ 31 - 0
lib/Settings/Settings.cpp

@@ -62,6 +62,24 @@ void Settings::updateGatewayConfigs(JsonArray& arr) {
   }
 }
 
+void Settings::updateGroupStateFields(JsonArray &arr) {
+  if (arr.success()) {
+    if (this->groupStateFields) {
+      delete this->groupStateFields;
+    }
+
+    this->groupStateFields = new GroupStateField[arr.size()];
+    this->numGroupStateFields = arr.size();
+
+    for (size_t i = 0; i < arr.size(); i++) {
+      String name = arr[i];
+      name.toLowerCase();
+
+      this->groupStateFields[i] = GroupStateFieldHelpers::getFieldByName(name.c_str());
+    }
+  }
+}
+
 void Settings::patch(JsonObject& parsedSettings) {
   if (parsedSettings.success()) {
     this->setIfPresent<String>(parsedSettings, "admin_username", adminUsername);
@@ -98,6 +116,10 @@ void Settings::patch(JsonObject& parsedSettings) {
       JsonArray& arr = parsedSettings["gateway_configs"];
       updateGatewayConfigs(arr);
     }
+    if (parsedSettings.containsKey("group_state_fields")) {
+      JsonArray& arr = parsedSettings["group_state_fields"];
+      updateGroupStateFields(arr);
+    }
   }
 }
 
@@ -177,6 +199,15 @@ void Settings::serialize(Stream& stream, const bool prettyPrint) {
     root["gateway_configs"] = arr;
   }
 
+  if (this->groupStateFields) {
+    JsonArray& arr = jsonBuffer.createArray();
+    for (size_t i = 0; i < this->numGroupStateFields; i++) {
+      arr.add(GroupStateFieldHelpers::getFieldName(this->groupStateFields[i]));
+    }
+
+    root["group_state_fields"] = arr;
+  }
+
   if (prettyPrint) {
     root.prettyPrintTo(stream);
   } else {

+ 24 - 2
lib/Settings/Settings.h

@@ -1,6 +1,8 @@
 #include <Arduino.h>
 #include <StringStream.h>
 #include <ArduinoJson.h>
+#include <GroupStateField.h>
+#include <Size.h>
 
 #ifndef _SETTINGS_H_INCLUDED
 #define _SETTINGS_H_INCLUDED
@@ -41,6 +43,15 @@ enum RadioInterfaceType {
   LT8900 = 1,
 };
 
+static const GroupStateField DEFAULT_GROUP_STATE_FIELDS[] = {
+  GroupStateField::STATE,
+  GroupStateField::BRIGHTNESS,
+  GroupStateField::COLOR,
+  GroupStateField::MODE,
+  GroupStateField::COLOR_TEMP,
+  GroupStateField::BULB_MODE
+};
+
 class GatewayConfig {
 public:
   GatewayConfig(uint16_t deviceId, uint16_t port, uint8_t protocolVersion)
@@ -77,8 +88,16 @@ public:
     mqttStateRateLimit(500),
     packetRepeatThrottleThreshold(200),
     packetRepeatThrottleSensitivity(0),
-    packetRepeatMinimum(3)
-  { }
+    packetRepeatMinimum(3),
+    groupStateFields(NULL),
+    numGroupStateFields(0)
+  {
+    if (groupStateFields == NULL) {
+      numGroupStateFields = size(DEFAULT_GROUP_STATE_FIELDS);
+      groupStateFields = new GroupStateField[numGroupStateFields];
+      memcpy(groupStateFields, DEFAULT_GROUP_STATE_FIELDS, numGroupStateFields * sizeof(GroupStateField));
+    }
+  }
 
   ~Settings() {
     if (deviceIds) {
@@ -101,6 +120,7 @@ public:
   void serialize(Stream& stream, const bool prettyPrint = false);
   void updateDeviceIds(JsonArray& arr);
   void updateGatewayConfigs(JsonArray& arr);
+  void updateGroupStateFields(JsonArray& arr);
   void patch(JsonObject& obj);
   String mqttServer();
   uint16_t mqttPort();
@@ -123,6 +143,8 @@ public:
   String mqttTopicPattern;
   String mqttUpdateTopicPattern;
   String mqttStateTopicPattern;
+  GroupStateField *groupStateFields;
+  size_t numGroupStateFields;
   uint16_t discoveryPort;
   uint8_t listenRepeats;
   size_t stateFlushInterval;

+ 20 - 0
lib/Types/GroupStateField.cpp

@@ -0,0 +1,20 @@
+#include <GroupStateField.h>
+#include <Size.h>
+
+GroupStateField GroupStateFieldHelpers::getFieldByName(const char* name) {
+  for (size_t i = 0; i < size(STATE_NAMES); i++) {
+    if (0 == strcmp(name, STATE_NAMES[i])) {
+      return static_cast<GroupStateField>(i);
+    }
+  }
+  return GroupStateField::UNKNOWN;
+}
+
+const char* GroupStateFieldHelpers::getFieldName(GroupStateField field) {
+  for (size_t i = 0; i < size(STATE_NAMES); i++) {
+    if (field == static_cast<GroupStateField>(i)) {
+      return STATE_NAMES[i];
+    }
+  }
+  return STATE_NAMES[0];
+}

+ 40 - 0
lib/Types/GroupStateField.h

@@ -0,0 +1,40 @@
+#ifndef _GROUP_STATE_FIELDS_H
+#define _GROUP_STATE_FIELDS_H
+
+static const char* STATE_NAMES[] = {
+  "unknown",
+  "state",
+  "status",
+  "brightness",
+  "level",
+  "hue",
+  "saturation",
+  "color",
+  "mode",
+  "kelvin",
+  "color_temp",
+  "bulb_mode"
+};
+
+enum class GroupStateField {
+  UNKNOWN,
+  STATE,
+  STATUS,
+  BRIGHTNESS,
+  LEVEL,
+  HUE,
+  SATURATION,
+  COLOR,
+  MODE,
+  KELVIN,
+  COLOR_TEMP,
+  BULB_MODE
+};
+
+class GroupStateFieldHelpers {
+public:
+  static const char* getFieldName(GroupStateField field);
+  static GroupStateField getFieldByName(const char* name);
+};
+
+#endif

+ 18 - 0
lib/Types/MiLightConstants.h

@@ -0,0 +1,18 @@
+#ifndef _MILIGHT_BUTTONS
+#define _MILIGHT_BUTTONS
+
+enum MiLightRemoteType {
+  REMOTE_TYPE_UNKNOWN = 255,
+  REMOTE_TYPE_RGBW    = 0,
+  REMOTE_TYPE_CCT     = 1,
+  REMOTE_TYPE_RGB_CCT = 2,
+  REMOTE_TYPE_RGB     = 3,
+  REMOTE_TYPE_FUT089  = 4
+};
+
+enum MiLightStatus {
+  ON = 0,
+  OFF = 1
+};
+
+#endif

+ 4 - 4
lib/WebServer/MiLightHttpServer.cpp

@@ -307,7 +307,7 @@ void MiLightHttpServer::sendGroupState(GroupState &state) {
   String body;
   StaticJsonBuffer<200> jsonBuffer;
   JsonObject& obj = jsonBuffer.createObject();
-  state.applyState(obj);
+  state.applyState(obj, settings.groupStateFields, settings.numGroupStateFields);
   obj.printTo(body);
 
   server.send(200, APPLICATION_JSON, body);
@@ -326,8 +326,8 @@ void MiLightHttpServer::handleGetGroup(const UrlTokenBindings* urlBindings) {
   }
 
   BulbId bulbId(parseInt<uint16_t>(_deviceId), _groupId, _remoteType->type);
-  GroupState& state = stateStore.get(bulbId);
-  sendGroupState(stateStore.get(bulbId));
+  GroupState& state = stateStore->get(bulbId);
+  sendGroupState(stateStore->get(bulbId));
 }
 
 void MiLightHttpServer::handleUpdateGroup(const UrlTokenBindings* urlBindings) {
@@ -388,7 +388,7 @@ void MiLightHttpServer::handleUpdateGroup(const UrlTokenBindings* urlBindings) {
   }
 
   if (groupCount == 1) {
-    sendGroupState(stateStore.get(foundBulbId));
+    sendGroupState(stateStore->get(foundBulbId));
   } else {
     server.send(200, APPLICATION_JSON, "true");
   }

+ 2 - 2
lib/WebServer/MiLightHttpServer.h

@@ -16,7 +16,7 @@ const char APPLICATION_JSON[] = "application/json";
 
 class MiLightHttpServer {
 public:
-  MiLightHttpServer(Settings& settings, MiLightClient*& milightClient, GroupStateStore& stateStore)
+  MiLightHttpServer(Settings& settings, MiLightClient*& milightClient, GroupStateStore*& stateStore)
     : server(WebServer(80)),
       wsServer(WebSocketsServer(81)),
       numWsClients(0),
@@ -65,7 +65,7 @@ protected:
   WebSocketsServer wsServer;
   Settings& settings;
   MiLightClient*& milightClient;
-  GroupStateStore& stateStore;
+  GroupStateStore*& stateStore;
   SettingsSavedHandler settingsSavedHandler;
   size_t numWsClients;
 

+ 1 - 1
src/main.cpp

@@ -237,7 +237,7 @@ void setup() {
   SSDP.setDeviceType("upnp:rootdevice");
   SSDP.begin();
 
-  httpServer = new MiLightHttpServer(settings, milightClient, *stateStore);
+  httpServer = new MiLightHttpServer(settings, milightClient, stateStore);
   httpServer->onSettingsSaved(applySettings);
   httpServer->on("/description.xml", HTTP_GET, []() { SSDP.schema(httpServer->client()); });
   httpServer->begin();