Commit 48dd0747025cfb98d3aed76b6b2eb4da7d3eca42

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

Add removeGroup and createGroup, add missing tests.

include/hueplusplus/Group.h
@@ -91,7 +91,7 @@ public: @@ -91,7 +91,7 @@ public:
91 void incrementHue(int increment, uint8_t transition = 4); 91 void incrementHue(int increment, uint8_t transition = 4);
92 void incrementColorTemperature(int increment, uint8_t transition = 4); 92 void incrementColorTemperature(int increment, uint8_t transition = 4);
93 void incrementColorXY(float incX, float incY, uint8_t transition = 4); 93 void incrementColorXY(float incX, float incY, uint8_t transition = 4);
94 - void setScene(const std::string& scene, uint8_t transition = 4); 94 + void setScene(const std::string& scene);
95 95
96 protected: 96 protected:
97 nlohmann::json SendPutRequest(const nlohmann::json& request, const std::string& subPath, FileInfo fileInfo); 97 nlohmann::json SendPutRequest(const nlohmann::json& request, const std::string& subPath, FileInfo fileInfo);
@@ -101,6 +101,27 @@ protected: @@ -101,6 +101,27 @@ protected:
101 APICache state; 101 APICache state;
102 HueCommandAPI commands; 102 HueCommandAPI commands;
103 }; 103 };
  104 +
  105 +class CreateGroup
  106 +{
  107 +public:
  108 + static CreateGroup LightGroup(const std::vector<int>& lights, const std::string& name = "");
  109 + static CreateGroup Room(
  110 + const std::vector<int>& lights, const std::string& name = "", const std::string& roomType = "");
  111 + static CreateGroup Entertainment(const std::vector<int>& lights, const std::string& name = "");
  112 +
  113 + nlohmann::json getRequest() const;
  114 +
  115 +protected:
  116 + CreateGroup(
  117 + const std::vector<int>& lights, const std::string& name, const std::string& type, const std::string& roomType);
  118 +
  119 +private:
  120 + std::vector<int> lights;
  121 + std::string name;
  122 + std::string type;
  123 + std::string roomType;
  124 +};
104 } // namespace hueplusplus 125 } // namespace hueplusplus
105 126
106 #endif 127 #endif
107 \ No newline at end of file 128 \ No newline at end of file
include/hueplusplus/Hue.h
@@ -224,9 +224,12 @@ public: @@ -224,9 +224,12 @@ public:
224 std::vector<std::reference_wrapper<Group>> getAllGroups(); 224 std::vector<std::reference_wrapper<Group>> getAllGroups();
225 225
226 Group& getGroup(int id); 226 Group& getGroup(int id);
  227 + bool removeGroup(int id);
227 bool groupExists(int id); 228 bool groupExists(int id);
228 bool groupExists(int id) const; 229 bool groupExists(int id) const;
229 230
  231 + int createGroup(const CreateGroup& params);
  232 +
230 //! \brief Const function that returns the picture name of a given light id 233 //! \brief Const function that returns the picture name of a given light id
231 //! 234 //!
232 //! \note This will not update the local state of the bridge. 235 //! \note This will not update the local state of the bridge.
include/hueplusplus/HueCommandAPI.h
@@ -96,6 +96,18 @@ public: @@ -96,6 +96,18 @@ public:
96 nlohmann::json DELETERequest(const std::string& path, const nlohmann::json& request) const; 96 nlohmann::json DELETERequest(const std::string& path, const nlohmann::json& request) const;
97 nlohmann::json DELETERequest(const std::string& path, const nlohmann::json& request, FileInfo fileInfo) const; 97 nlohmann::json DELETERequest(const std::string& path, const nlohmann::json& request, FileInfo fileInfo) const;
98 98
  99 + //! \brief Sends a HTTP POST request to the bridge and returns the response
  100 + //!
  101 + //! This function will block until at least \ref minDelay has passed to any previous request
  102 + //! \param path API request path (appended after /api/{username})
  103 + //! \param request Request to the api, may be empty
  104 + //! \returns The return value of the underlying \ref IHttpHandler::POSTJson call
  105 + //! \throws std::system_error when system or socket operations fail
  106 + //! \throws HueException when response contains no body
  107 + //! \throws HueAPIResponseException when response contains an error
  108 + nlohmann::json POSTRequest(const std::string& path, const nlohmann::json& request) const;
  109 + nlohmann::json POSTRequest(const std::string& path, const nlohmann::json& request, FileInfo fileInfo) const;
  110 +
99 private: 111 private:
100 struct TimeoutData 112 struct TimeoutData
101 { 113 {
src/Group.cpp
@@ -46,6 +46,7 @@ void Group::setName(const std::string&amp; name) @@ -46,6 +46,7 @@ void Group::setName(const std::string&amp; name)
46 { 46 {
47 nlohmann::json request = {{"name", name}}; 47 nlohmann::json request = {{"name", name}};
48 SendPutRequest(request, "", CURRENT_FILE_INFO); 48 SendPutRequest(request, "", CURRENT_FILE_INFO);
  49 + Refresh();
49 } 50 }
50 51
51 void Group::setLights(const std::vector<int>& ids) 52 void Group::setLights(const std::vector<int>& ids)
@@ -56,6 +57,7 @@ void Group::setLights(const std::vector&lt;int&gt;&amp; ids) @@ -56,6 +57,7 @@ void Group::setLights(const std::vector&lt;int&gt;&amp; ids)
56 lights.push_back(std::to_string(id)); 57 lights.push_back(std::to_string(id));
57 } 58 }
58 SendPutRequest({{"lights", lights}}, "", CURRENT_FILE_INFO); 59 SendPutRequest({{"lights", lights}}, "", CURRENT_FILE_INFO);
  60 + Refresh();
59 } 61 }
60 62
61 bool Group::getAllOn() 63 bool Group::getAllOn()
@@ -144,12 +146,7 @@ StateTransaction Group::transaction() @@ -144,12 +146,7 @@ StateTransaction Group::transaction()
144 146
145 void Group::setOn(bool on, uint8_t transition) 147 void Group::setOn(bool on, uint8_t transition)
146 { 148 {
147 - nlohmann::json request = {{"on", on}};  
148 - if (transition != 4)  
149 - {  
150 - request["transition"] = transition;  
151 - }  
152 - SendPutRequest(request, "/action", CURRENT_FILE_INFO); 149 + transaction().setOn(on).setTransition(transition).commit();
153 } 150 }
154 151
155 void Group::setBrightness(uint8_t brightness, uint8_t transition) 152 void Group::setBrightness(uint8_t brightness, uint8_t transition)
@@ -202,7 +199,7 @@ void Group::incrementColorXY(float incX, float incY, uint8_t transition) @@ -202,7 +199,7 @@ void Group::incrementColorXY(float incX, float incY, uint8_t transition)
202 transaction().incrementColorXY(incX, incY).setTransition(transition).commit(); 199 transaction().incrementColorXY(incX, incY).setTransition(transition).commit();
203 } 200 }
204 201
205 -void Group::setScene(const std::string& scene, uint8_t transition) 202 +void Group::setScene(const std::string& scene)
206 { 203 {
207 SendPutRequest({{"scene", scene}}, "/action", CURRENT_FILE_INFO); 204 SendPutRequest({{"scene", scene}}, "/action", CURRENT_FILE_INFO);
208 } 205 }
@@ -220,6 +217,7 @@ std::string Group::getRoomType() const @@ -220,6 +217,7 @@ std::string Group::getRoomType() const
220 void Group::setRoomType(const std::string& type) 217 void Group::setRoomType(const std::string& type)
221 { 218 {
222 SendPutRequest({{"class", type}}, "", CURRENT_FILE_INFO); 219 SendPutRequest({{"class", type}}, "", CURRENT_FILE_INFO);
  220 + Refresh();
223 } 221 }
224 222
225 std::string Group::getModelId() const 223 std::string Group::getModelId() const
@@ -231,4 +229,44 @@ std::string Group::getUniqueId() const @@ -231,4 +229,44 @@ std::string Group::getUniqueId() const
231 { 229 {
232 return state.GetValue().at("uniqueid").get<std::string>(); 230 return state.GetValue().at("uniqueid").get<std::string>();
233 } 231 }
  232 +
  233 +CreateGroup CreateGroup::LightGroup(const std::vector<int>& lights, const std::string& name)
  234 +{
  235 + return CreateGroup(lights, name, "LightGroup", "");
  236 +}
  237 +
  238 +CreateGroup CreateGroup::Room(const std::vector<int>& lights, const std::string& name, const std::string& roomType)
  239 +{
  240 + return CreateGroup(lights, name, "Room", roomType);
  241 +}
  242 +
  243 +CreateGroup CreateGroup::Entertainment(const std::vector<int>& lights, const std::string& name)
  244 +{
  245 + return CreateGroup(lights, name, "Entertainment", "");
  246 +}
  247 +
  248 +nlohmann::json CreateGroup::getRequest() const
  249 +{
  250 + nlohmann::json lightStrings = nlohmann::json::array();
  251 + for (int light : lights)
  252 + {
  253 + lightStrings.push_back(std::to_string(light));
  254 + }
  255 + nlohmann::json result = {{"lights", lightStrings}, {"type", type}};
  256 + if (!name.empty())
  257 + {
  258 + result["name"] = name;
  259 + }
  260 + if (!roomType.empty())
  261 + {
  262 + result["class"] = roomType;
  263 + }
  264 + return result;
  265 +}
  266 +
  267 +CreateGroup::CreateGroup(
  268 + const std::vector<int>& lights, const std::string& name, const std::string& type, const std::string& roomType)
  269 + : lights(lights), name(name), type(type), roomType(roomType)
  270 +{}
  271 +
234 } // namespace hueplusplus 272 } // namespace hueplusplus
src/Hue.cpp
@@ -307,6 +307,18 @@ Group&amp; Hue::getGroup(int id) @@ -307,6 +307,18 @@ Group&amp; Hue::getGroup(int id)
307 return groups.emplace(id, Group(id, commands, stateCache.GetRefreshDuration())).first->second; 307 return groups.emplace(id, Group(id, commands, stateCache.GetRefreshDuration())).first->second;
308 } 308 }
309 309
  310 +bool Hue::removeGroup(int id)
  311 +{
  312 + nlohmann::json result
  313 + = commands.DELETERequest("/groups/" + std::to_string(id), nlohmann::json::object(), CURRENT_FILE_INFO);
  314 + bool success = utils::safeGetMember(result, 0, "success") == "/groups/" + std::to_string(id) + " deleted";
  315 + if (success && groups.count(id) != 0)
  316 + {
  317 + groups.erase(id);
  318 + }
  319 + return success;
  320 +}
  321 +
310 bool Hue::groupExists(int id) 322 bool Hue::groupExists(int id)
311 { 323 {
312 auto pos = lights.find(id); 324 auto pos = lights.find(id);
@@ -335,6 +347,17 @@ bool Hue::groupExists(int id) const @@ -335,6 +347,17 @@ bool Hue::groupExists(int id) const
335 return false; 347 return false;
336 } 348 }
337 349
  350 +int Hue::createGroup(const CreateGroup& params)
  351 +{
  352 + nlohmann::json response = commands.POSTRequest("/groups", params.getRequest(), CURRENT_FILE_INFO);
  353 + nlohmann::json id = utils::safeGetMember(response, 0, "success", "id");
  354 + if (id.is_string())
  355 + {
  356 + return std::stoi(id.get<std::string>());
  357 + }
  358 + return 0;
  359 +}
  360 +
338 bool Hue::lightExists(int id) 361 bool Hue::lightExists(int id)
339 { 362 {
340 auto pos = lights.find(id); 363 auto pos = lights.find(id);
src/HueCommandAPI.cpp
@@ -70,7 +70,7 @@ HueCommandAPI::HueCommandAPI( @@ -70,7 +70,7 @@ HueCommandAPI::HueCommandAPI(
70 port(port), 70 port(port),
71 username(username), 71 username(username),
72 httpHandler(std::move(httpHandler)), 72 httpHandler(std::move(httpHandler)),
73 - timeout(new TimeoutData{std::chrono::steady_clock::now(), {}}) 73 + timeout(new TimeoutData {std::chrono::steady_clock::now(), {}})
74 {} 74 {}
75 75
76 nlohmann::json HueCommandAPI::PUTRequest(const std::string& path, const nlohmann::json& request) const 76 nlohmann::json HueCommandAPI::PUTRequest(const std::string& path, const nlohmann::json& request) const
@@ -81,8 +81,9 @@ nlohmann::json HueCommandAPI::PUTRequest(const std::string&amp; path, const nlohmann @@ -81,8 +81,9 @@ nlohmann::json HueCommandAPI::PUTRequest(const std::string&amp; path, const nlohmann
81 nlohmann::json HueCommandAPI::PUTRequest( 81 nlohmann::json HueCommandAPI::PUTRequest(
82 const std::string& path, const nlohmann::json& request, FileInfo fileInfo) const 82 const std::string& path, const nlohmann::json& request, FileInfo fileInfo) const
83 { 83 {
84 - return HandleError(std::move(fileInfo),  
85 - RunWithTimeout(timeout, minDelay, [&]() { return httpHandler->PUTJson(CombinedPath(path), request, ip); })); 84 + return HandleError(std::move(fileInfo), RunWithTimeout(timeout, minDelay, [&]() {
  85 + return httpHandler->PUTJson(CombinedPath(path), request, ip, port);
  86 + }));
86 } 87 }
87 88
88 nlohmann::json HueCommandAPI::GETRequest(const std::string& path, const nlohmann::json& request) const 89 nlohmann::json HueCommandAPI::GETRequest(const std::string& path, const nlohmann::json& request) const
@@ -93,8 +94,9 @@ nlohmann::json HueCommandAPI::GETRequest(const std::string&amp; path, const nlohmann @@ -93,8 +94,9 @@ nlohmann::json HueCommandAPI::GETRequest(const std::string&amp; path, const nlohmann
93 nlohmann::json HueCommandAPI::GETRequest( 94 nlohmann::json HueCommandAPI::GETRequest(
94 const std::string& path, const nlohmann::json& request, FileInfo fileInfo) const 95 const std::string& path, const nlohmann::json& request, FileInfo fileInfo) const
95 { 96 {
96 - return HandleError(std::move(fileInfo),  
97 - RunWithTimeout(timeout, minDelay, [&]() { return httpHandler->GETJson(CombinedPath(path), request, ip); })); 97 + return HandleError(std::move(fileInfo), RunWithTimeout(timeout, minDelay, [&]() {
  98 + return httpHandler->GETJson(CombinedPath(path), request, ip, port);
  99 + }));
98 } 100 }
99 101
100 nlohmann::json HueCommandAPI::DELETERequest(const std::string& path, const nlohmann::json& request) const 102 nlohmann::json HueCommandAPI::DELETERequest(const std::string& path, const nlohmann::json& request) const
@@ -105,8 +107,22 @@ nlohmann::json HueCommandAPI::DELETERequest(const std::string&amp; path, const nlohm @@ -105,8 +107,22 @@ nlohmann::json HueCommandAPI::DELETERequest(const std::string&amp; path, const nlohm
105 nlohmann::json HueCommandAPI::DELETERequest( 107 nlohmann::json HueCommandAPI::DELETERequest(
106 const std::string& path, const nlohmann::json& request, FileInfo fileInfo) const 108 const std::string& path, const nlohmann::json& request, FileInfo fileInfo) const
107 { 109 {
108 - return HandleError(std::move(fileInfo),  
109 - RunWithTimeout(timeout, minDelay, [&]() { return httpHandler->DELETEJson(CombinedPath(path), request, ip); })); 110 + return HandleError(std::move(fileInfo), RunWithTimeout(timeout, minDelay, [&]() {
  111 + return httpHandler->DELETEJson(CombinedPath(path), request, ip, port);
  112 + }));
  113 +}
  114 +
  115 +nlohmann::json HueCommandAPI::POSTRequest(const std::string& path, const nlohmann::json& request) const
  116 +{
  117 + return POSTRequest(path, request, CURRENT_FILE_INFO);
  118 +}
  119 +
  120 +nlohmann::json HueCommandAPI::POSTRequest(
  121 + const std::string& path, const nlohmann::json& request, FileInfo fileInfo) const
  122 +{
  123 + return HandleError(std::move(fileInfo), RunWithTimeout(timeout, minDelay, [&]() {
  124 + return httpHandler->POSTJson(CombinedPath(path), request, ip, port);
  125 + }));
110 } 126 }
111 127
112 nlohmann::json HueCommandAPI::HandleError(FileInfo fileInfo, const nlohmann::json& response) const 128 nlohmann::json HueCommandAPI::HandleError(FileInfo fileInfo, const nlohmann::json& response) const
src/HueLight.cpp
@@ -112,6 +112,7 @@ bool HueLight::setName(const std::string&amp; name) @@ -112,6 +112,7 @@ bool HueLight::setName(const std::string&amp; name)
112 nlohmann::json request = nlohmann::json::object(); 112 nlohmann::json request = nlohmann::json::object();
113 request["name"] = name; 113 request["name"] = name;
114 nlohmann::json reply = SendPutRequest(request, "/name", CURRENT_FILE_INFO); 114 nlohmann::json reply = SendPutRequest(request, "/name", CURRENT_FILE_INFO);
  115 + state.Refresh();
115 116
116 // Check whether request was successful (returned name is not necessarily the actually set name) 117 // Check whether request was successful (returned name is not necessarily the actually set name)
117 // If it already exists, a number is added, if it is too long to be returned, "Updated" is returned 118 // If it already exists, a number is added, if it is too long to be returned, "Updated" is returned
test/test_Group.cpp
@@ -155,7 +155,7 @@ TEST_F(GroupTest, getActionHueSaturation) @@ -155,7 +155,7 @@ TEST_F(GroupTest, getActionHueSaturation)
155 const int id = 1; 155 const int id = 1;
156 expectGetState(id); 156 expectGetState(id);
157 Group group(id, commands, std::chrono::steady_clock::duration::max()); 157 Group group(id, commands, std::chrono::steady_clock::duration::max());
158 - std::pair<uint16_t, uint8_t> hueSat{hue, sat}; 158 + std::pair<uint16_t, uint8_t> hueSat {hue, sat};
159 EXPECT_EQ(hueSat, group.getActionHueSaturation()); 159 EXPECT_EQ(hueSat, group.getActionHueSaturation());
160 EXPECT_EQ(hueSat, Const(group).getActionHueSaturation()); 160 EXPECT_EQ(hueSat, Const(group).getActionHueSaturation());
161 } 161 }
@@ -183,7 +183,7 @@ TEST_F(GroupTest, getActionColorXY) @@ -183,7 +183,7 @@ TEST_F(GroupTest, getActionColorXY)
183 const int id = 1; 183 const int id = 1;
184 expectGetState(id); 184 expectGetState(id);
185 Group group(id, commands, std::chrono::steady_clock::duration::max()); 185 Group group(id, commands, std::chrono::steady_clock::duration::max());
186 - std::pair<float, float> xy{x, y}; 186 + std::pair<float, float> xy {x, y};
187 EXPECT_EQ(xy, group.getActionColorXY()); 187 EXPECT_EQ(xy, group.getActionColorXY());
188 EXPECT_EQ(xy, Const(group).getActionColorXY()); 188 EXPECT_EQ(xy, Const(group).getActionColorXY());
189 } 189 }
@@ -196,3 +196,86 @@ TEST_F(GroupTest, getActionColorMode) @@ -196,3 +196,86 @@ TEST_F(GroupTest, getActionColorMode)
196 EXPECT_EQ(colormode, group.getActionColorMode()); 196 EXPECT_EQ(colormode, group.getActionColorMode());
197 EXPECT_EQ(colormode, Const(group).getActionColorMode()); 197 EXPECT_EQ(colormode, Const(group).getActionColorMode());
198 } 198 }
  199 +
  200 +TEST_F(GroupTest, setName)
  201 +{
  202 + const int id = 1;
  203 + expectGetState(id);
  204 + Group group(id, commands, std::chrono::steady_clock::duration::max());
  205 + const std::string name = "Test group";
  206 + nlohmann::json request = {{"name", name}};
  207 + nlohmann::json response = {{"success", {"/groups/1/name", name}}};
  208 + EXPECT_CALL(*handler, PUTJson("/api/" + getBridgeUsername() + "/groups/1", request, getBridgeIp(), getBridgePort()))
  209 + .WillOnce(Return(response));
  210 + expectGetState(id);
  211 + group.setName(name);
  212 +}
  213 +
  214 +TEST_F(GroupTest, setLights)
  215 +{
  216 + const int id = 1;
  217 + expectGetState(id);
  218 + Group group(id, commands, std::chrono::steady_clock::duration::max());
  219 + const nlohmann::json lights = {"2", "4", "5"};
  220 + nlohmann::json request = {{"lights", lights}};
  221 + nlohmann::json response = {{"success", {"/groups/1/lights", lights}}};
  222 + EXPECT_CALL(*handler, PUTJson("/api/" + getBridgeUsername() + "/groups/1", request, getBridgeIp(), getBridgePort()))
  223 + .WillOnce(Return(response));
  224 + expectGetState(id);
  225 + group.setLights(std::vector<int> {2, 4, 5});
  226 +}
  227 +
  228 +TEST_F(GroupTest, setRoomType)
  229 +{
  230 + const int id = 1;
  231 + expectGetState(id);
  232 + Group group(id, commands, std::chrono::steady_clock::duration::max());
  233 + const std::string type = "LivingRoom";
  234 + nlohmann::json request = {{"class", type}};
  235 + nlohmann::json response = {{"success", {"/groups/1/class", type}}};
  236 + EXPECT_CALL(*handler, PUTJson("/api/" + getBridgeUsername() + "/groups/1", request, getBridgeIp(), getBridgePort()))
  237 + .WillOnce(Return(response));
  238 + expectGetState(id);
  239 + group.setRoomType(type);
  240 +}
  241 +
  242 +TEST_F(GroupTest, setScene)
  243 +{
  244 + const int id = 1;
  245 + expectGetState(id);
  246 + Group group(id, commands, std::chrono::steady_clock::duration::max());
  247 + const std::string scene = "testScene";
  248 + nlohmann::json request = {{"scene", scene}};
  249 + nlohmann::json response = {{"success", {"/groups/1/action/scene", scene}}};
  250 + EXPECT_CALL(
  251 + *handler, PUTJson("/api/" + getBridgeUsername() + "/groups/1/action", request, getBridgeIp(), getBridgePort()))
  252 + .WillOnce(Return(response));
  253 + (id);
  254 + group.setScene(scene);
  255 +}
  256 +
  257 +TEST(CreateGroup, LightGroup)
  258 +{
  259 + EXPECT_EQ(nlohmann::json({{"lights", {"1"}}, {"type", "LightGroup"}, {"name", "Name"}}),
  260 + CreateGroup::LightGroup({1}, "Name").getRequest());
  261 + EXPECT_EQ(
  262 + nlohmann::json({{"lights", {"2", "4"}}, {"type", "LightGroup"}}), CreateGroup::LightGroup({2, 4}).getRequest());
  263 +}
  264 +
  265 +TEST(CreateGroup, Entertainment)
  266 +{
  267 + EXPECT_EQ(nlohmann::json({{"lights", {"1"}}, {"type", "Entertainment"}, {"name", "Name"}}),
  268 + CreateGroup::Entertainment({1}, "Name").getRequest());
  269 + EXPECT_EQ(nlohmann::json({{"lights", {"2", "4"}}, {"type", "Entertainment"}}),
  270 + CreateGroup::Entertainment({2, 4}).getRequest());
  271 +}
  272 +
  273 +TEST(CreateGroup, Room)
  274 +{
  275 + EXPECT_EQ(nlohmann::json({{"lights", {"1"}}, {"type", "Room"}, {"name", "Name"}, {"class", "Bedroom"}}),
  276 + CreateGroup::Room({1}, "Name", "Bedroom").getRequest());
  277 + EXPECT_EQ(nlohmann::json({{"lights", {"1"}}, {"type", "Room"}, {"name", "Name"}}),
  278 + CreateGroup::Room({1}, "Name").getRequest());
  279 + EXPECT_EQ(
  280 + nlohmann::json({{"lights", {"2", "4"}}, {"type", "Room"}}), CreateGroup::Room({2, 4}).getRequest());
  281 +}
199 \ No newline at end of file 282 \ No newline at end of file
test/test_Hue.cpp
@@ -58,7 +58,7 @@ protected: @@ -58,7 +58,7 @@ protected:
58 .Times(AtLeast(1)) 58 .Times(AtLeast(1))
59 .WillRepeatedly(Return(getBridgeXml())); 59 .WillRepeatedly(Return(getBridgeXml()));
60 } 60 }
61 - ~HueFinderTest(){}; 61 + ~HueFinderTest() {};
62 }; 62 };
63 63
64 TEST_F(HueFinderTest, FindBridges) 64 TEST_F(HueFinderTest, FindBridges)
@@ -87,7 +87,7 @@ TEST_F(HueFinderTest, FindBridges) @@ -87,7 +87,7 @@ TEST_F(HueFinderTest, FindBridges)
87 TEST_F(HueFinderTest, GetBridge) 87 TEST_F(HueFinderTest, GetBridge)
88 { 88 {
89 using namespace ::testing; 89 using namespace ::testing;
90 - nlohmann::json request{{"devicetype", "HuePlusPlus#User"}}; 90 + nlohmann::json request {{"devicetype", "HuePlusPlus#User"}};
91 91
92 nlohmann::json errorResponse 92 nlohmann::json errorResponse
93 = {{{"error", {{"type", 101}, {"address", ""}, {"description", "link button not pressed"}}}}}; 93 = {{{"error", {{"type", 101}, {"address", ""}, {"description", "link button not pressed"}}}}};
@@ -117,7 +117,7 @@ TEST_F(HueFinderTest, GetBridge) @@ -117,7 +117,7 @@ TEST_F(HueFinderTest, GetBridge)
117 EXPECT_EQ(test_bridge.getUsername(), getBridgeUsername()) << "Bridge username not matching"; 117 EXPECT_EQ(test_bridge.getUsername(), getBridgeUsername()) << "Bridge username not matching";
118 118
119 // Verify that username is correctly set in api requests 119 // Verify that username is correctly set in api requests
120 - nlohmann::json hue_bridge_state{{"lights", {}}}; 120 + nlohmann::json hue_bridge_state {{"lights", {}}};
121 EXPECT_CALL( 121 EXPECT_CALL(
122 *handler, GETJson("/api/" + getBridgeUsername(), nlohmann::json::object(), getBridgeIp(), getBridgePort())) 122 *handler, GETJson("/api/" + getBridgeUsername(), nlohmann::json::object(), getBridgeIp(), getBridgePort()))
123 .Times(1) 123 .Times(1)
@@ -166,7 +166,7 @@ TEST(Hue, requestUsername) @@ -166,7 +166,7 @@ TEST(Hue, requestUsername)
166 { 166 {
167 using namespace ::testing; 167 using namespace ::testing;
168 std::shared_ptr<MockHttpHandler> handler = std::make_shared<MockHttpHandler>(); 168 std::shared_ptr<MockHttpHandler> handler = std::make_shared<MockHttpHandler>();
169 - nlohmann::json request{{"devicetype", "HuePlusPlus#User"}}; 169 + nlohmann::json request {{"devicetype", "HuePlusPlus#User"}};
170 170
171 { 171 {
172 nlohmann::json errorResponse 172 nlohmann::json errorResponse
@@ -223,7 +223,7 @@ TEST(Hue, requestUsername) @@ -223,7 +223,7 @@ TEST(Hue, requestUsername)
223 EXPECT_EQ(test_bridge.getUsername(), getBridgeUsername()) << "Bridge username not matching"; 223 EXPECT_EQ(test_bridge.getUsername(), getBridgeUsername()) << "Bridge username not matching";
224 224
225 // Verify that username is correctly set in api requests 225 // Verify that username is correctly set in api requests
226 - nlohmann::json hue_bridge_state{{"lights", {}}}; 226 + nlohmann::json hue_bridge_state {{"lights", {}}};
227 EXPECT_CALL( 227 EXPECT_CALL(
228 *handler, GETJson("/api/" + getBridgeUsername(), nlohmann::json::object(), getBridgeIp(), getBridgePort())) 228 *handler, GETJson("/api/" + getBridgeUsername(), nlohmann::json::object(), getBridgeIp(), getBridgePort()))
229 .Times(1) 229 .Times(1)
@@ -264,7 +264,7 @@ TEST(Hue, getLight) @@ -264,7 +264,7 @@ TEST(Hue, getLight)
264 // Test exception 264 // Test exception
265 ASSERT_THROW(test_bridge.getLight(1), HueException); 265 ASSERT_THROW(test_bridge.getLight(1), HueException);
266 266
267 - nlohmann::json hue_bridge_state{{"lights", 267 + nlohmann::json hue_bridge_state {{"lights",
268 {{"1", 268 {{"1",
269 {{"state", 269 {{"state",
270 {{"on", true}, {"bri", 254}, {"ct", 366}, {"alert", "none"}, {"colormode", "ct"}, 270 {{"on", true}, {"bri", 254}, {"ct", 366}, {"alert", "none"}, {"colormode", "ct"},
@@ -396,7 +396,7 @@ TEST(Hue, removeLight) @@ -396,7 +396,7 @@ TEST(Hue, removeLight)
396 { 396 {
397 using namespace ::testing; 397 using namespace ::testing;
398 std::shared_ptr<MockHttpHandler> handler = std::make_shared<MockHttpHandler>(); 398 std::shared_ptr<MockHttpHandler> handler = std::make_shared<MockHttpHandler>();
399 - nlohmann::json hue_bridge_state{{"lights", 399 + nlohmann::json hue_bridge_state {{"lights",
400 {{"1", 400 {{"1",
401 {{"state", 401 {{"state",
402 {{"on", true}, {"bri", 254}, {"ct", 366}, {"alert", "none"}, {"colormode", "ct"}, 402 {{"on", true}, {"bri", 254}, {"ct", 366}, {"alert", "none"}, {"colormode", "ct"},
@@ -439,7 +439,7 @@ TEST(Hue, getAllLights) @@ -439,7 +439,7 @@ TEST(Hue, getAllLights)
439 { 439 {
440 using namespace ::testing; 440 using namespace ::testing;
441 std::shared_ptr<MockHttpHandler> handler = std::make_shared<MockHttpHandler>(); 441 std::shared_ptr<MockHttpHandler> handler = std::make_shared<MockHttpHandler>();
442 - nlohmann::json hue_bridge_state{{"lights", 442 + nlohmann::json hue_bridge_state {{"lights",
443 {{"1", 443 {{"1",
444 {{"state", 444 {{"state",
445 {{"on", true}, {"bri", 254}, {"ct", 366}, {"alert", "none"}, {"colormode", "ct"}, 445 {{"on", true}, {"bri", 254}, {"ct", 366}, {"alert", "none"}, {"colormode", "ct"},
@@ -470,7 +470,7 @@ TEST(Hue, lightExists) @@ -470,7 +470,7 @@ TEST(Hue, lightExists)
470 { 470 {
471 using namespace ::testing; 471 using namespace ::testing;
472 std::shared_ptr<MockHttpHandler> handler = std::make_shared<MockHttpHandler>(); 472 std::shared_ptr<MockHttpHandler> handler = std::make_shared<MockHttpHandler>();
473 - nlohmann::json hue_bridge_state{{"lights", 473 + nlohmann::json hue_bridge_state {{"lights",
474 {{"1", 474 {{"1",
475 {{"state", 475 {{"state",
476 {{"on", true}, {"bri", 254}, {"ct", 366}, {"alert", "none"}, {"colormode", "ct"}, 476 {{"on", true}, {"bri", 254}, {"ct", 366}, {"alert", "none"}, {"colormode", "ct"},
@@ -502,11 +502,182 @@ TEST(Hue, lightExists) @@ -502,11 +502,182 @@ TEST(Hue, lightExists)
502 EXPECT_EQ(true, const_test_bridge2.lightExists(1)); 502 EXPECT_EQ(true, const_test_bridge2.lightExists(1));
503 } 503 }
504 504
  505 +TEST(Hue, getGroup)
  506 +{
  507 + using namespace ::testing;
  508 + std::shared_ptr<MockHttpHandler> handler = std::make_shared<MockHttpHandler>();
  509 + EXPECT_CALL(
  510 + *handler, GETJson("/api/" + getBridgeUsername(), nlohmann::json::object(), getBridgeIp(), getBridgePort()))
  511 + .Times(1);
  512 +
  513 + Hue test_bridge(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler);
  514 +
  515 + // Test exception
  516 + ASSERT_THROW(test_bridge.getGroup(1), HueException);
  517 +
  518 + nlohmann::json hue_bridge_state {{"groups",
  519 + {{"1",
  520 + {{"name", "Group 1"}, {"type", "LightGroup"}, {"lights", {"1", "2", "3"}},
  521 + {"action",
  522 + {{"on", true}, {"bri", 254}, {"ct", 366}, {"alert", "none"}, {"colormode", "ct"}, {"hue", 200},
  523 + {"sat", 254}, {"effect", "none"}, {"xy", {0.f, 0.f}}}},
  524 + {"state", {{"any_on", true}, {"all_on", true}}}}}}}};
  525 +
  526 + EXPECT_CALL(
  527 + *handler, GETJson("/api/" + getBridgeUsername(), nlohmann::json::object(), getBridgeIp(), getBridgePort()))
  528 + .Times(1)
  529 + .WillOnce(Return(hue_bridge_state));
  530 +
  531 + EXPECT_CALL(*handler,
  532 + GETJson("/api/" + getBridgeUsername() + "/groups/1", nlohmann::json::object(), getBridgeIp(), getBridgePort()))
  533 + .Times(AtLeast(1))
  534 + .WillRepeatedly(Return(hue_bridge_state["groups"]["1"]));
  535 +
  536 + // Refresh cache
  537 + test_bridge = Hue(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler);
  538 +
  539 + // Test when correct data is sent
  540 + Group test_group_1 = test_bridge.getGroup(1);
  541 + EXPECT_EQ(test_group_1.getName(), "Group 1");
  542 + EXPECT_EQ(test_group_1.getType(), "LightGroup");
  543 +
  544 + // Test again to check whether group is returned directly
  545 + test_group_1 = test_bridge.getGroup(1);
  546 + EXPECT_EQ(test_group_1.getName(), "Group 1");
  547 + EXPECT_EQ(test_group_1.getType(), "LightGroup");
  548 +}
  549 +
  550 +TEST(Hue, removeGroup)
  551 +{
  552 + using namespace ::testing;
  553 + std::shared_ptr<MockHttpHandler> handler = std::make_shared<MockHttpHandler>();
  554 + nlohmann::json hue_bridge_state {{"groups",
  555 + {{"1",
  556 + {{"name", "Group 1"}, {"type", "LightGroup"}, {"lights", {"1", "2", "3"}},
  557 + {"action",
  558 + {{"on", true}, {"bri", 254}, {"ct", 366}, {"alert", "none"}, {"colormode", "ct"}, {"hue", 200},
  559 + {"sat", 254}, {"effect", "none"}, {"xy", {0.f, 0.f}}}},
  560 + {"state", {{"any_on", true}, {"all_on", true}}}}}}}};
  561 + EXPECT_CALL(
  562 + *handler, GETJson("/api/" + getBridgeUsername(), nlohmann::json::object(), getBridgeIp(), getBridgePort()))
  563 + .Times(1)
  564 + .WillOnce(Return(hue_bridge_state));
  565 +
  566 + Hue test_bridge(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler);
  567 +
  568 + EXPECT_CALL(*handler,
  569 + GETJson("/api/" + getBridgeUsername() + "/groups/1", nlohmann::json::object(), getBridgeIp(), getBridgePort()))
  570 + .Times(1)
  571 + .WillRepeatedly(Return(hue_bridge_state["groups"]["1"]));
  572 +
  573 + nlohmann::json return_answer;
  574 + return_answer = nlohmann::json::array();
  575 + return_answer[0] = nlohmann::json::object();
  576 + return_answer[0]["success"] = "/groups/1 deleted";
  577 + EXPECT_CALL(*handler,
  578 + DELETEJson(
  579 + "/api/" + getBridgeUsername() + "/groups/1", nlohmann::json::object(), getBridgeIp(), getBridgePort()))
  580 + .Times(2)
  581 + .WillOnce(Return(return_answer))
  582 + .WillOnce(Return(nlohmann::json()));
  583 +
  584 + // Test when correct data is sent
  585 + Group test_group_1 = test_bridge.getGroup(1);
  586 +
  587 + EXPECT_EQ(test_bridge.removeGroup(1), true);
  588 +
  589 + EXPECT_EQ(test_bridge.removeGroup(1), false);
  590 +}
  591 +
  592 +TEST(Hue, groupExists)
  593 +{
  594 + using namespace ::testing;
  595 + std::shared_ptr<MockHttpHandler> handler = std::make_shared<MockHttpHandler>();
  596 + nlohmann::json hue_bridge_state {{"groups",
  597 + {{"1",
  598 + {{"name", "Group 1"}, {"type", "LightGroup"}, {"lights", {"1", "2", "3"}},
  599 + {"action",
  600 + {{"on", true}, {"bri", 254}, {"ct", 366}, {"alert", "none"}, {"colormode", "ct"}, {"hue", 200},
  601 + {"sat", 254}, {"effect", "none"}, {"xy", {0.f, 0.f}}}},
  602 + {"state", {{"any_on", true}, {"all_on", true}}}}}}}};
  603 + EXPECT_CALL(
  604 + *handler, GETJson("/api/" + getBridgeUsername(), nlohmann::json::object(), getBridgeIp(), getBridgePort()))
  605 + .Times(AtLeast(1))
  606 + .WillRepeatedly(Return(hue_bridge_state));
  607 + EXPECT_CALL(*handler,
  608 + GETJson("/api/" + getBridgeUsername() + "/groups/1", nlohmann::json::object(), getBridgeIp(), getBridgePort()))
  609 + .Times(AtLeast(1))
  610 + .WillRepeatedly(Return(hue_bridge_state["groups"]["1"]));
  611 +
  612 + Hue test_bridge(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler);
  613 +
  614 + EXPECT_EQ(true, test_bridge.groupExists(1));
  615 + EXPECT_EQ(false, test_bridge.groupExists(2));
  616 +
  617 + const Hue const_test_bridge1 = test_bridge;
  618 + EXPECT_EQ(true, const_test_bridge1.groupExists(1));
  619 + EXPECT_EQ(false, const_test_bridge1.groupExists(2));
  620 +
  621 + test_bridge.getGroup(1);
  622 + const Hue const_test_bridge2 = test_bridge;
  623 + EXPECT_EQ(true, test_bridge.groupExists(1));
  624 + EXPECT_EQ(true, const_test_bridge2.groupExists(1));
  625 +}
  626 +
  627 +TEST(Hue, getAllGroups)
  628 +{
  629 + using namespace ::testing;
  630 + std::shared_ptr<MockHttpHandler> handler = std::make_shared<MockHttpHandler>();
  631 + nlohmann::json hue_bridge_state {{"groups",
  632 + {{"1",
  633 + {{"name", "Group 1"}, {"type", "LightGroup"}, {"lights", {"1", "2", "3"}},
  634 + {"action",
  635 + {{"on", true}, {"bri", 254}, {"ct", 366}, {"alert", "none"}, {"colormode", "ct"}, {"hue", 200},
  636 + {"sat", 254}, {"effect", "none"}, {"xy", {0.f, 0.f}}}},
  637 + {"state", {{"any_on", true}, {"all_on", true}}}}}}}};
  638 +
  639 + EXPECT_CALL(
  640 + *handler, GETJson("/api/" + getBridgeUsername(), nlohmann::json::object(), getBridgeIp(), getBridgePort()))
  641 + .Times(AtLeast(1))
  642 + .WillRepeatedly(Return(hue_bridge_state));
  643 +
  644 + EXPECT_CALL(*handler,
  645 + GETJson("/api/" + getBridgeUsername() + "/groups/1", nlohmann::json::object(), getBridgeIp(), getBridgePort()))
  646 + .Times(AtLeast(1))
  647 + .WillRepeatedly(Return(hue_bridge_state["groups"]["1"]));
  648 +
  649 + Hue test_bridge(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler);
  650 +
  651 + std::vector<std::reference_wrapper<Group>> test_groups = test_bridge.getAllGroups();
  652 + ASSERT_EQ(1, test_groups.size());
  653 + EXPECT_EQ(test_groups[0].get().getName(), "Group 1");
  654 + EXPECT_EQ(test_groups[0].get().getType(), "LightGroup");
  655 +}
  656 +
  657 +TEST(Hue, createGroup)
  658 +{
  659 + using namespace ::testing;
  660 + std::shared_ptr<MockHttpHandler> handler = std::make_shared<MockHttpHandler>();
  661 + Hue test_bridge(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler);
  662 + CreateGroup create = CreateGroup::Room({2, 3}, "Nice room", "LivingRoom");
  663 + nlohmann::json request = create.getRequest();
  664 + const int id = 4;
  665 + nlohmann::json response = {{{"success", {{"id", std::to_string(id)}}}}};
  666 + EXPECT_CALL(*handler, POSTJson("/api/" + getBridgeUsername() + "/groups", request, getBridgeIp(), getBridgePort()))
  667 + .WillOnce(Return(response));
  668 + EXPECT_EQ(id, test_bridge.createGroup(create));
  669 +
  670 + response = {};
  671 + EXPECT_CALL(*handler, POSTJson("/api/" + getBridgeUsername() + "/groups", request, getBridgeIp(), getBridgePort()))
  672 + .WillOnce(Return(response));
  673 + EXPECT_EQ(0, test_bridge.createGroup(create));
  674 +}
  675 +
505 TEST(Hue, getPictureOfLight) 676 TEST(Hue, getPictureOfLight)
506 { 677 {
507 using namespace ::testing; 678 using namespace ::testing;
508 std::shared_ptr<MockHttpHandler> handler = std::make_shared<MockHttpHandler>(); 679 std::shared_ptr<MockHttpHandler> handler = std::make_shared<MockHttpHandler>();
509 - nlohmann::json hue_bridge_state{{"lights", 680 + nlohmann::json hue_bridge_state {{"lights",
510 {{"1", 681 {{"1",
511 {{"state", 682 {{"state",
512 {{"on", true}, {"bri", 254}, {"ct", 366}, {"alert", "none"}, {"colormode", "ct"}, 683 {{"on", true}, {"bri", 254}, {"ct", 366}, {"alert", "none"}, {"colormode", "ct"},