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,14 +52,17 @@ public:
52 StateTransaction(StateTransaction&&) = default; 52 StateTransaction(StateTransaction&&) = default;
53 53
54 //! \brief Commit transaction and make request. 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 //! \returns true on success or when no change was requested. 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 //! refresh() must be called if the updated values are needed immediately. 60 //! refresh() must be called if the updated values are needed immediately.
58 //! \throws std::system_error when system or socket operations fail 61 //! \throws std::system_error when system or socket operations fail
59 //! \throws HueException when response contains no body 62 //! \throws HueException when response contains no body
60 //! \throws HueAPIResponseException when response contains an error 63 //! \throws HueAPIResponseException when response contains an error
61 //! \throws nlohmann::json::parse_error when response could not be parsed 64 //! \throws nlohmann::json::parse_error when response could not be parsed
62 - bool commit() &&; 65 + bool commit(bool trimRequest = true) &&;
63 66
64 //! \brief Turn light on or off. 67 //! \brief Turn light on or off.
65 //! \param on true for on, false for off 68 //! \param on true for on, false for off
@@ -132,7 +135,7 @@ public: @@ -132,7 +135,7 @@ public:
132 //! \brief Set transition time for the request. 135 //! \brief Set transition time for the request.
133 //! \param transition Transition time in 100ms, default for any request is 400ms. 136 //! \param transition Transition time in 100ms, default for any request is 400ms.
134 //! \returns This transaction for chaining calls 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 //! A request without any changes only containing a transition is pointless and is not sent. 139 //! A request without any changes only containing a transition is pointless and is not sent.
137 StateTransaction&& setTransition(uint16_t transition) &&; 140 StateTransaction&& setTransition(uint16_t transition) &&;
138 //! \brief Trigger an alert. 141 //! \brief Trigger an alert.
@@ -147,7 +150,11 @@ public: @@ -147,7 +150,11 @@ public:
147 //! \returns This transaction for chaining calls 150 //! \returns This transaction for chaining calls
148 StateTransaction&& stopAlert() &&; 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 const HueCommandAPI& commands; 158 const HueCommandAPI& commands;
152 std::string path; 159 std::string path;
153 nlohmann::json state; 160 nlohmann::json state;
src/StateTransaction.cpp
@@ -22,6 +22,8 @@ @@ -22,6 +22,8 @@
22 22
23 #include "hueplusplus/StateTransaction.h" 23 #include "hueplusplus/StateTransaction.h"
24 24
  25 +#include <set>
  26 +
25 #include "hueplusplus/HueExceptionMacro.h" 27 #include "hueplusplus/HueExceptionMacro.h"
26 #include "hueplusplus/StateTransaction.h" 28 #include "hueplusplus/StateTransaction.h"
27 #include "hueplusplus/Utils.h" 29 #include "hueplusplus/Utils.h"
@@ -35,8 +37,12 @@ StateTransaction::StateTransaction( @@ -35,8 +37,12 @@ StateTransaction::StateTransaction(
35 assert(currentState.is_object()); 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 // Empty request or request with only transition makes no sense 46 // Empty request or request with only transition makes no sense
41 if (!request.empty() && !(request.size() == 1 && request.count("transitiontime"))) 47 if (!request.empty() && !(request.size() == 1 && request.count("transitiontime")))
42 { 48 {
@@ -49,7 +55,7 @@ bool StateTransaction::commit() &amp;&amp; @@ -49,7 +55,7 @@ bool StateTransaction::commit() &amp;&amp;
49 // Turn on if it was turned off 55 // Turn on if it was turned off
50 request["on"] = true; 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 // Turn off if brightness is 0 60 // Turn off if brightness is 0
55 request["on"] = false; 61 request["on"] = false;
@@ -64,39 +70,27 @@ bool StateTransaction::commit() &amp;&amp; @@ -64,39 +70,27 @@ bool StateTransaction::commit() &amp;&amp;
64 70
65 StateTransaction&& StateTransaction::setOn(bool on) && 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 return std::move(*this); 74 return std::move(*this);
72 } 75 }
73 76
74 StateTransaction&& StateTransaction::setBrightness(uint8_t brightness) && 77 StateTransaction&& StateTransaction::setBrightness(uint8_t brightness) &&
75 { 78 {
76 uint8_t clamped = std::min<uint8_t>(brightness, 254); 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 return std::move(*this); 81 return std::move(*this);
82 } 82 }
83 83
84 StateTransaction&& StateTransaction::setColorSaturation(uint8_t saturation) && 84 StateTransaction&& StateTransaction::setColorSaturation(uint8_t saturation) &&
85 { 85 {
86 uint8_t clamped = std::min<uint8_t>(saturation, 254); 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 return std::move(*this); 88 return std::move(*this);
92 } 89 }
93 90
94 StateTransaction&& StateTransaction::setColorHue(uint16_t hue) && 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 return std::move(*this); 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,32 +98,20 @@ StateTransaction&amp;&amp; StateTransaction::setColorXY(float x, float y) &amp;&amp;
104 { 98 {
105 float clampedX = std::max(0.f, std::min(x, 1.f)); 99 float clampedX = std::max(0.f, std::min(x, 1.f));
106 float clampedY = std::max(0.f, std::min(y, 1.f)); 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 return std::move(*this); 102 return std::move(*this);
114 } 103 }
115 104
116 StateTransaction&& StateTransaction::setColorTemperature(unsigned int mired) && 105 StateTransaction&& StateTransaction::setColorTemperature(unsigned int mired) &&
117 { 106 {
118 unsigned int clamped = std::max(153u, std::min(mired, 500u)); 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 return std::move(*this); 109 return std::move(*this);
124 } 110 }
125 111
126 StateTransaction&& StateTransaction::setColorLoop(bool on) && 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 return std::move(*this); 115 return std::move(*this);
134 } 116 }
135 117
@@ -186,4 +168,46 @@ StateTransaction&amp;&amp; StateTransaction::stopAlert() &amp;&amp; @@ -186,4 +168,46 @@ StateTransaction&amp;&amp; StateTransaction::stopAlert() &amp;&amp;
186 request["alert"] = "none"; 168 request["alert"] = "none";
187 return std::move(*this); 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 } // namespace hueplusplus 213 } // namespace hueplusplus
190 \ No newline at end of file 214 \ No newline at end of file