Commit 505a0e055d123c38d56a8384db854e593fc1a2fc

Authored by Jojo-1000
Committed by Moritz Wirger
1 parent eee2f0ea

Make request trimming on StateTransaction optional.

Values already set in the state can still be outdated, so there is a manual override for keeping them.
include/hueplusplus/StateTransaction.h
... ... @@ -52,14 +52,17 @@ public:
52 52 StateTransaction(StateTransaction&&) = default;
53 53  
54 54 //! \brief Commit transaction and make request.
  55 + //! \param trimRequest Optional. When true, request parameters that are unneccessary based on
  56 + //! the current state are removed. This reduces load on the bridge. On the other hand, an outdated
  57 + //! state might cause requests to be dropped unexpectedly. Has no effect on groups.
55 58 //! \returns true on success or when no change was requested.
56   - //! \note After changing the state of a HueLight or Group,
  59 + //! \note After changing the state of a HueLight or Group,
57 60 //! refresh() must be called if the updated values are needed immediately.
58 61 //! \throws std::system_error when system or socket operations fail
59 62 //! \throws HueException when response contains no body
60 63 //! \throws HueAPIResponseException when response contains an error
61 64 //! \throws nlohmann::json::parse_error when response could not be parsed
62   - bool commit() &&;
  65 + bool commit(bool trimRequest = true) &&;
63 66  
64 67 //! \brief Turn light on or off.
65 68 //! \param on true for on, false for off
... ... @@ -132,7 +135,7 @@ public:
132 135 //! \brief Set transition time for the request.
133 136 //! \param transition Transition time in 100ms, default for any request is 400ms.
134 137 //! \returns This transaction for chaining calls
135   - //! \note The transition only applies to the current request.
  138 + //! \note The transition only applies to the current request.
136 139 //! A request without any changes only containing a transition is pointless and is not sent.
137 140 StateTransaction&& setTransition(uint16_t transition) &&;
138 141 //! \brief Trigger an alert.
... ... @@ -147,7 +150,11 @@ public:
147 150 //! \returns This transaction for chaining calls
148 151 StateTransaction&& stopAlert() &&;
149 152  
150   -protected:
  153 +private:
  154 + //! \brief Remove parts from request that are already set in state
  155 + void trimRequest();
  156 +
  157 +private:
151 158 const HueCommandAPI& commands;
152 159 std::string path;
153 160 nlohmann::json state;
... ...
src/StateTransaction.cpp
... ... @@ -22,6 +22,8 @@
22 22  
23 23 #include "hueplusplus/StateTransaction.h"
24 24  
  25 +#include <set>
  26 +
25 27 #include "hueplusplus/HueExceptionMacro.h"
26 28 #include "hueplusplus/StateTransaction.h"
27 29 #include "hueplusplus/Utils.h"
... ... @@ -35,8 +37,12 @@ StateTransaction::StateTransaction(
35 37 assert(currentState.is_object());
36 38 }
37 39  
38   -bool StateTransaction::commit() &&
  40 +bool StateTransaction::commit(bool trimRequest) &&
39 41 {
  42 + if (trimRequest)
  43 + {
  44 + this->trimRequest();
  45 + }
40 46 // Empty request or request with only transition makes no sense
41 47 if (!request.empty() && !(request.size() == 1 && request.count("transitiontime")))
42 48 {
... ... @@ -49,7 +55,7 @@ bool StateTransaction::commit() &amp;&amp;
49 55 // Turn on if it was turned off
50 56 request["on"] = true;
51 57 }
52   - else if(request.value("bri", 254) == 0 && state.value("on", true))
  58 + else if (request.value("bri", 254) == 0 && state.value("on", true))
53 59 {
54 60 // Turn off if brightness is 0
55 61 request["on"] = false;
... ... @@ -64,39 +70,27 @@ bool StateTransaction::commit() &amp;&amp;
64 70  
65 71 StateTransaction&& StateTransaction::setOn(bool on) &&
66 72 {
67   - if (!state.count("on") || state["on"] != on)
68   - {
69   - request["on"] = on;
70   - }
  73 + request["on"] = on;
71 74 return std::move(*this);
72 75 }
73 76  
74 77 StateTransaction&& StateTransaction::setBrightness(uint8_t brightness) &&
75 78 {
76 79 uint8_t clamped = std::min<uint8_t>(brightness, 254);
77   - if (!state.count("bri") || state["bri"].get<unsigned int>() != clamped)
78   - {
79   - request["bri"] = clamped;
80   - }
  80 + request["bri"] = clamped;
81 81 return std::move(*this);
82 82 }
83 83  
84 84 StateTransaction&& StateTransaction::setColorSaturation(uint8_t saturation) &&
85 85 {
86 86 uint8_t clamped = std::min<uint8_t>(saturation, 254);
87   - if (!state.count("sat") || state["sat"].get<unsigned int>() != clamped || state.value("colormode", "") != "hs")
88   - {
89   - request["sat"] = clamped;
90   - }
  87 + request["sat"] = clamped;
91 88 return std::move(*this);
92 89 }
93 90  
94 91 StateTransaction&& StateTransaction::setColorHue(uint16_t hue) &&
95 92 {
96   - if (!state.count("hue") || state["hue"].get<int>() != hue || state.value("colormode", "") != "hs")
97   - {
98   - request["hue"] = hue;
99   - }
  93 + request["hue"] = hue;
100 94 return std::move(*this);
101 95 }
102 96  
... ... @@ -104,32 +98,20 @@ StateTransaction&amp;&amp; StateTransaction::setColorXY(float x, float y) &amp;&amp;
104 98 {
105 99 float clampedX = std::max(0.f, std::min(x, 1.f));
106 100 float clampedY = std::max(0.f, std::min(y, 1.f));
107   - if (!state.count("xy") || !state.count("colormode") || !state["xy"].is_array()
108   - || !utils::floatEquals(state["xy"][0].get<float>(), clampedX)
109   - || !utils::floatEquals(state["xy"][1].get<float>(), clampedY) || state["colormode"] != "xy")
110   - {
111   - request["xy"] = {clampedX, clampedY};
112   - }
  101 + request["xy"] = {clampedX, clampedY};
113 102 return std::move(*this);
114 103 }
115 104  
116 105 StateTransaction&& StateTransaction::setColorTemperature(unsigned int mired) &&
117 106 {
118 107 unsigned int clamped = std::max(153u, std::min(mired, 500u));
119   - if (state.value("ct", 0u) != clamped || state.value("colormode", "") != "ct")
120   - {
121   - request["ct"] = clamped;
122   - }
  108 + request["ct"] = clamped;
123 109 return std::move(*this);
124 110 }
125 111  
126 112 StateTransaction&& StateTransaction::setColorLoop(bool on) &&
127 113 {
128   - std::string effect = on ? "colorloop" : "none";
129   - if (state.value("effect", "") != effect)
130   - {
131   - request["effect"] = effect;
132   - }
  114 + request["effect"] = on ? "colorloop" : "none";
133 115 return std::move(*this);
134 116 }
135 117  
... ... @@ -186,4 +168,46 @@ StateTransaction&amp;&amp; StateTransaction::stopAlert() &amp;&amp;
186 168 request["alert"] = "none";
187 169 return std::move(*this);
188 170 }
  171 +
  172 +void StateTransaction::trimRequest()
  173 +{
  174 + static const std::map<std::string, std::string> colormodes
  175 + = {{"sat", "hs"}, {"hue", "hs"}, {"xy", "xy"}, {"ct", "ct"}};
  176 + static const std::set<std::string> otherRemove = {"on", "bri", "effect"};
  177 + // Skip when there is no state provided (e.g. for groups)
  178 + if (state.empty())
  179 + {
  180 + return;
  181 + }
  182 + for (auto it = request.begin(); it != request.end();)
  183 + {
  184 + auto colormodeIt = colormodes.find(it.key());
  185 + if (colormodeIt != colormodes.end())
  186 + {
  187 + // Only erase color commands if colormode and value matches
  188 + auto stateIt = state.find(it.key());
  189 + if (stateIt != state.end() && state.value("colormode", "") == colormodeIt->second)
  190 + {
  191 + // Compare xy using float comparison
  192 + if ((!it->is_array() && *stateIt == *it)
  193 + || (stateIt->is_array() && utils::floatEquals((*stateIt)[0].get<float>(), (*it)[0].get<float>())
  194 + && utils::floatEquals((*stateIt)[1].get<float>(), (*it)[1].get<float>())))
  195 + {
  196 + it = request.erase(it);
  197 + continue;
  198 + }
  199 + }
  200 + }
  201 + else if (otherRemove.count(it.key()))
  202 + {
  203 + if (state.count(it.key()) && state[it.key()] == *it)
  204 + {
  205 + it = request.erase(it);
  206 + continue;
  207 + }
  208 + }
  209 + ++it;
  210 + }
  211 +}
  212 +
189 213 } // namespace hueplusplus
190 214 \ No newline at end of file
... ...