Commit bc52722a6d2a0b1ec82ebdfb67e91790c6138ecd
Committed by
Moritz Wirger
1 parent
ec625114
Add documentation and tests for BridgeConfig.
Showing
6 changed files
with
238 additions
and
10 deletions
include/hueplusplus/BridgeConfig.h
| @@ -27,6 +27,7 @@ | @@ -27,6 +27,7 @@ | ||
| 27 | 27 | ||
| 28 | namespace hueplusplus | 28 | namespace hueplusplus |
| 29 | { | 29 | { |
| 30 | +//! \brief API version consisting of major, minor and patch version | ||
| 30 | struct Version | 31 | struct Version |
| 31 | { | 32 | { |
| 32 | int major; | 33 | int major; |
| @@ -34,31 +35,62 @@ struct Version | @@ -34,31 +35,62 @@ struct Version | ||
| 34 | int patch; | 35 | int patch; |
| 35 | }; | 36 | }; |
| 36 | 37 | ||
| 38 | +//! \brief User that is whitelisted for Hue API usage | ||
| 37 | struct WhitelistedUser | 39 | struct WhitelistedUser |
| 38 | { | 40 | { |
| 41 | + //! \brief API username of the user | ||
| 39 | std::string key; | 42 | std::string key; |
| 43 | + //! \brief Name provided on user creation | ||
| 40 | std::string name; | 44 | std::string name; |
| 45 | + //! \brief Last time the user was used | ||
| 41 | time::AbsoluteTime lastUsed; | 46 | time::AbsoluteTime lastUsed; |
| 47 | + //! \brief Time the user was created | ||
| 42 | time::AbsoluteTime created; | 48 | time::AbsoluteTime created; |
| 43 | }; | 49 | }; |
| 44 | 50 | ||
| 51 | +//! \brief General bridge configuration properties. | ||
| 45 | class BridgeConfig | 52 | class BridgeConfig |
| 46 | { | 53 | { |
| 47 | public: | 54 | public: |
| 55 | + //! \brief Construct BridgeConfig | ||
| 48 | BridgeConfig(std::shared_ptr<APICache> baseCache, std::chrono::steady_clock::duration refreshDuration); | 56 | BridgeConfig(std::shared_ptr<APICache> baseCache, std::chrono::steady_clock::duration refreshDuration); |
| 49 | 57 | ||
| 58 | + | ||
| 59 | + //! \brief Refreshes internal cached state. | ||
| 60 | + //! \throws std::system_error when system or socket operations fail | ||
| 61 | + //! \throws HueException when response contained no body | ||
| 62 | + //! \throws HueAPIResponseException when response contains an error | ||
| 63 | + //! \throws nlohmann::json::parse_error when response could not be parsed | ||
| 50 | void refresh(); | 64 | void refresh(); |
| 51 | 65 | ||
| 66 | + //! \brief Get the list of whitelisted users | ||
| 67 | + //! \returns All users authorized for API access | ||
| 52 | std::vector<WhitelistedUser> getWhitelistedUsers() const; | 68 | std::vector<WhitelistedUser> getWhitelistedUsers() const; |
| 69 | + //! \brief Remove user from the whitelist | ||
| 70 | + //! \param userKey The API username of the user to remove | ||
| 71 | + //! \throws std::system_error when system or socket operations fail | ||
| 72 | + //! \throws HueException when response contained no body | ||
| 73 | + //! \throws HueAPIResponseException when response contains an error | ||
| 74 | + //! \throws nlohmann::json::parse_error when response could not be parsed | ||
| 53 | void removeUser(const std::string& userKey); | 75 | void removeUser(const std::string& userKey); |
| 54 | 76 | ||
| 77 | + //! \brief Get link button state | ||
| 78 | + //! \returns true when link button was pressed in the last 30 seconds. | ||
| 79 | + //! | ||
| 80 | + //! Indicates whether new users can be added currently. | ||
| 55 | bool getLinkButton() const; | 81 | bool getLinkButton() const; |
| 82 | + //! \brief Set the link button state to pressed | ||
| 56 | void pressLinkButton(); | 83 | void pressLinkButton(); |
| 57 | 84 | ||
| 85 | + //! \brief Add the closest lamp to the network | ||
| 58 | void touchLink(); | 86 | void touchLink(); |
| 59 | 87 | ||
| 88 | + //! \brief Get bridge MAC address | ||
| 60 | std::string getMACAddress() const; | 89 | std::string getMACAddress() const; |
| 90 | + //! \brief Get current (of last refresh) UTC time of the bridge | ||
| 61 | time::AbsoluteTime getUTCTime() const; | 91 | time::AbsoluteTime getUTCTime() const; |
| 92 | + //! \brief Get configured timezone for the bridge | ||
| 93 | + //! \note For times not in UTC, the timezone of the program and the bridge are assumed to be identical. | ||
| 62 | std::string getTimezone() const; | 94 | std::string getTimezone() const; |
| 63 | 95 | ||
| 64 | protected: | 96 | protected: |
include/hueplusplus/Hue.h
| @@ -156,6 +156,10 @@ public: | @@ -156,6 +156,10 @@ public: | ||
| 156 | //! Should only be called rarely, as a full refresh is costly and usually not necessary. | 156 | //! Should only be called rarely, as a full refresh is costly and usually not necessary. |
| 157 | //! Instead refresh only the parts you are interested in or rely on periodic refreshes | 157 | //! Instead refresh only the parts you are interested in or rely on periodic refreshes |
| 158 | //! that happen automatically when calling non-const methods. | 158 | //! that happen automatically when calling non-const methods. |
| 159 | + //! \throws std::system_error when system or socket operations fail | ||
| 160 | + //! \throws HueException when response contained no body | ||
| 161 | + //! \throws HueAPIResponseException when response contains an error | ||
| 162 | + //! \throws nlohmann::json::parse_error when response could not be parsed | ||
| 159 | void refresh(); | 163 | void refresh(); |
| 160 | 164 | ||
| 161 | //! \brief Function to get the ip address of the hue bridge | 165 | //! \brief Function to get the ip address of the hue bridge |
src/Hue.cpp
| @@ -282,5 +282,6 @@ void Hue::setHttpHandler(std::shared_ptr<const IHttpHandler> handler) | @@ -282,5 +282,6 @@ void Hue::setHttpHandler(std::shared_ptr<const IHttpHandler> handler) | ||
| 282 | scheduleList = CreateableResourceList<Schedule, int, CreateSchedule>(stateCache, "schedules", refreshDuration); | 282 | scheduleList = CreateableResourceList<Schedule, int, CreateSchedule>(stateCache, "schedules", refreshDuration); |
| 283 | sceneList = CreateableResourceList<Scene, std::string, CreateScene>(stateCache, "scenes", refreshDuration); | 283 | sceneList = CreateableResourceList<Scene, std::string, CreateScene>(stateCache, "scenes", refreshDuration); |
| 284 | bridgeConfig = BridgeConfig(stateCache, refreshDuration); | 284 | bridgeConfig = BridgeConfig(stateCache, refreshDuration); |
| 285 | + stateCache->refresh(); | ||
| 285 | } | 286 | } |
| 286 | } // namespace hueplusplus | 287 | } // namespace hueplusplus |
test/CMakeLists.txt
| @@ -48,7 +48,7 @@ set(TEST_SOURCES | @@ -48,7 +48,7 @@ set(TEST_SOURCES | ||
| 48 | test_SimpleColorHueStrategy.cpp | 48 | test_SimpleColorHueStrategy.cpp |
| 49 | test_SimpleColorTemperatureStrategy.cpp | 49 | test_SimpleColorTemperatureStrategy.cpp |
| 50 | test_StateTransaction.cpp | 50 | test_StateTransaction.cpp |
| 51 | - test_TimePattern.cpp "test_ColorUnits.cpp") | 51 | + test_TimePattern.cpp "test_ColorUnits.cpp" "test_BridgeConfig.cpp") |
| 52 | 52 | ||
| 53 | set(HuePlusPlus_INCLUDE_DIR "${PROJECT_SOURCE_DIR}/include") | 53 | set(HuePlusPlus_INCLUDE_DIR "${PROJECT_SOURCE_DIR}/include") |
| 54 | 54 |
test/test_BridgeConfig.cpp
0 โ 100644
| 1 | +/** | ||
| 2 | + \file test_BridgeConfig.cpp | ||
| 3 | + Copyright Notice\n | ||
| 4 | + Copyright (C) 2020 Jan Rogall - developer\n | ||
| 5 | + | ||
| 6 | + This file is part of hueplusplus. | ||
| 7 | + | ||
| 8 | + hueplusplus is free software: you can redistribute it and/or modify | ||
| 9 | + it under the terms of the GNU Lesser General Public License as published by | ||
| 10 | + the Free Software Foundation, either version 3 of the License, or | ||
| 11 | + (at your option) any later version. | ||
| 12 | + | ||
| 13 | + hueplusplus is distributed in the hope that it will be useful, | ||
| 14 | + but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 15 | + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 16 | + GNU Lesser General Public License for more details. | ||
| 17 | + | ||
| 18 | + You should have received a copy of the GNU Lesser General Public License | ||
| 19 | + along with hueplusplus. If not, see <http://www.gnu.org/licenses/>. | ||
| 20 | +**/ | ||
| 21 | + | ||
| 22 | +#include <hueplusplus/BridgeConfig.h> | ||
| 23 | + | ||
| 24 | +#include <gtest/gtest.h> | ||
| 25 | + | ||
| 26 | +#include "testhelper.h" | ||
| 27 | + | ||
| 28 | +#include "mocks/mock_HttpHandler.h" | ||
| 29 | + | ||
| 30 | +using namespace hueplusplus; | ||
| 31 | +using namespace testing; | ||
| 32 | + | ||
| 33 | +TEST(BridgeConfig, refresh) | ||
| 34 | +{ | ||
| 35 | + auto handler = std::make_shared<MockHttpHandler>(); | ||
| 36 | + HueCommandAPI commands(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler); | ||
| 37 | + auto baseCache = std::make_shared<APICache>("", commands, std::chrono::steady_clock::duration::max()); | ||
| 38 | + EXPECT_CALL( | ||
| 39 | + *handler, GETJson("/api/" + getBridgeUsername(), nlohmann::json::object(), getBridgeIp(), getBridgePort())) | ||
| 40 | + .WillOnce(Return(nlohmann::json::object())); | ||
| 41 | + baseCache->refresh(); | ||
| 42 | + BridgeConfig config(baseCache, std::chrono::steady_clock::duration::max()); | ||
| 43 | + | ||
| 44 | + EXPECT_CALL(*handler, | ||
| 45 | + GETJson("/api/" + getBridgeUsername() + "/config", nlohmann::json::object(), getBridgeIp(), getBridgePort())) | ||
| 46 | + .WillOnce(Return(nlohmann::json::object())); | ||
| 47 | + config.refresh(); | ||
| 48 | +} | ||
| 49 | + | ||
| 50 | +TEST(BridgeConfig, getWhitelistedUsers) | ||
| 51 | +{ | ||
| 52 | + const nlohmann::json state {{"config", | ||
| 53 | + {{"whitelist", | ||
| 54 | + {{"abcd", | ||
| 55 | + {{"name", "User A"}, {"last use date", "2020-04-01T10:00:04"}, | ||
| 56 | + {"create date", "2020-01-01T12:00:00"}}}, | ||
| 57 | + {"cdef", | ||
| 58 | + {{"name", "User B"}, {"last use date", "2020-03-05T14:00:00"}, | ||
| 59 | + {"create date", "2020-02-01T02:03:40"}}}}}}}}; | ||
| 60 | + auto handler = std::make_shared<MockHttpHandler>(); | ||
| 61 | + HueCommandAPI commands(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler); | ||
| 62 | + auto baseCache = std::make_shared<APICache>("", commands, std::chrono::steady_clock::duration::max()); | ||
| 63 | + EXPECT_CALL( | ||
| 64 | + *handler, GETJson("/api/" + getBridgeUsername(), nlohmann::json::object(), getBridgeIp(), getBridgePort())) | ||
| 65 | + .WillOnce(Return(state)); | ||
| 66 | + baseCache->refresh(); | ||
| 67 | + BridgeConfig config(baseCache, std::chrono::steady_clock::duration::max()); | ||
| 68 | + | ||
| 69 | + std::vector<WhitelistedUser> users = config.getWhitelistedUsers(); | ||
| 70 | + EXPECT_THAT(users, | ||
| 71 | + UnorderedElementsAre(Truly([](const WhitelistedUser& u) { return u.key == "abcd" && u.name == "User A"; }), | ||
| 72 | + Truly([](const WhitelistedUser& u) { return u.key == "cdef" && u.name == "User B"; }))); | ||
| 73 | +} | ||
| 74 | + | ||
| 75 | +TEST(BridgeConfig, removeUser) | ||
| 76 | +{ | ||
| 77 | + auto handler = std::make_shared<MockHttpHandler>(); | ||
| 78 | + HueCommandAPI commands(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler); | ||
| 79 | + auto baseCache = std::make_shared<APICache>("", commands, std::chrono::steady_clock::duration::max()); | ||
| 80 | + EXPECT_CALL( | ||
| 81 | + *handler, GETJson("/api/" + getBridgeUsername(), nlohmann::json::object(), getBridgeIp(), getBridgePort())) | ||
| 82 | + .WillOnce(Return(nlohmann::json::object())); | ||
| 83 | + baseCache->refresh(); | ||
| 84 | + BridgeConfig config(baseCache, std::chrono::steady_clock::duration::max()); | ||
| 85 | + | ||
| 86 | + const std::string userKey = "abcd"; | ||
| 87 | + EXPECT_CALL(*handler, | ||
| 88 | + DELETEJson("/api/" + getBridgeUsername() + "/config/whitelist/" + userKey, nlohmann::json::object(), | ||
| 89 | + getBridgeIp(), getBridgePort())) | ||
| 90 | + .WillOnce(Return(nlohmann::json {"/config/whitelist/" + userKey + " deleted"})); | ||
| 91 | + config.removeUser(userKey); | ||
| 92 | +} | ||
| 93 | + | ||
| 94 | +TEST(BridgeConfig, getLinkButton) | ||
| 95 | +{ | ||
| 96 | + const nlohmann::json state {{"config", {{"linkbutton", true}}}}; | ||
| 97 | + auto handler = std::make_shared<MockHttpHandler>(); | ||
| 98 | + HueCommandAPI commands(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler); | ||
| 99 | + auto baseCache = std::make_shared<APICache>("", commands, std::chrono::steady_clock::duration::max()); | ||
| 100 | + EXPECT_CALL( | ||
| 101 | + *handler, GETJson("/api/" + getBridgeUsername(), nlohmann::json::object(), getBridgeIp(), getBridgePort())) | ||
| 102 | + .WillOnce(Return(state)); | ||
| 103 | + baseCache->refresh(); | ||
| 104 | + BridgeConfig config(baseCache, std::chrono::steady_clock::duration::max()); | ||
| 105 | + | ||
| 106 | + EXPECT_TRUE(config.getLinkButton()); | ||
| 107 | + EXPECT_CALL(*handler, | ||
| 108 | + GETJson("/api/" + getBridgeUsername() + "/config", nlohmann::json::object(), getBridgeIp(), getBridgePort())) | ||
| 109 | + .WillOnce(Return(nlohmann::json {{"linkbutton", false}})); | ||
| 110 | + config.refresh(); | ||
| 111 | + EXPECT_FALSE(config.getLinkButton()); | ||
| 112 | +} | ||
| 113 | + | ||
| 114 | +TEST(BridgeConfig, pressLinkButton) | ||
| 115 | +{ | ||
| 116 | + auto handler = std::make_shared<MockHttpHandler>(); | ||
| 117 | + HueCommandAPI commands(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler); | ||
| 118 | + auto baseCache = std::make_shared<APICache>("", commands, std::chrono::steady_clock::duration::max()); | ||
| 119 | + EXPECT_CALL( | ||
| 120 | + *handler, GETJson("/api/" + getBridgeUsername(), nlohmann::json::object(), getBridgeIp(), getBridgePort())) | ||
| 121 | + .WillOnce(Return(nlohmann::json::object())); | ||
| 122 | + baseCache->refresh(); | ||
| 123 | + BridgeConfig config(baseCache, std::chrono::steady_clock::duration::max()); | ||
| 124 | + | ||
| 125 | + EXPECT_CALL(*handler, | ||
| 126 | + PUTJson("/api/" + getBridgeUsername() + "/config", nlohmann::json {{"linkbutton", true}}, getBridgeIp(), | ||
| 127 | + getBridgePort())) | ||
| 128 | + .WillOnce(Return(nlohmann::json {{{"success", {{"/config/linkbutton", true}}}}})); | ||
| 129 | + config.pressLinkButton(); | ||
| 130 | +} | ||
| 131 | + | ||
| 132 | +TEST(BridgeConfig, touchLink) | ||
| 133 | +{ | ||
| 134 | + auto handler = std::make_shared<MockHttpHandler>(); | ||
| 135 | + HueCommandAPI commands(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler); | ||
| 136 | + auto baseCache = std::make_shared<APICache>("", commands, std::chrono::steady_clock::duration::max()); | ||
| 137 | + EXPECT_CALL( | ||
| 138 | + *handler, GETJson("/api/" + getBridgeUsername(), nlohmann::json::object(), getBridgeIp(), getBridgePort())) | ||
| 139 | + .WillOnce(Return(nlohmann::json::object())); | ||
| 140 | + baseCache->refresh(); | ||
| 141 | + BridgeConfig config(baseCache, std::chrono::steady_clock::duration::max()); | ||
| 142 | + | ||
| 143 | + EXPECT_CALL(*handler, | ||
| 144 | + PUTJson("/api/" + getBridgeUsername() + "/config", nlohmann::json {{"touchlink", true}}, getBridgeIp(), | ||
| 145 | + getBridgePort())) | ||
| 146 | + .WillOnce(Return(nlohmann::json {{{"success", {{"/config/touchlink", true}}}}})); | ||
| 147 | + config.touchLink(); | ||
| 148 | +} | ||
| 149 | + | ||
| 150 | +TEST(BridgeConfig, getMACAddress) | ||
| 151 | +{ | ||
| 152 | + const nlohmann::json state {{"config", {{"mac", getBridgeMac()}}}}; | ||
| 153 | + auto handler = std::make_shared<MockHttpHandler>(); | ||
| 154 | + HueCommandAPI commands(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler); | ||
| 155 | + auto baseCache = std::make_shared<APICache>("", commands, std::chrono::steady_clock::duration::max()); | ||
| 156 | + EXPECT_CALL( | ||
| 157 | + *handler, GETJson("/api/" + getBridgeUsername(), nlohmann::json::object(), getBridgeIp(), getBridgePort())) | ||
| 158 | + .WillOnce(Return(state)); | ||
| 159 | + baseCache->refresh(); | ||
| 160 | + BridgeConfig config(baseCache, std::chrono::steady_clock::duration::max()); | ||
| 161 | + | ||
| 162 | + EXPECT_EQ(getBridgeMac(), config.getMACAddress()); | ||
| 163 | +} | ||
| 164 | + | ||
| 165 | +TEST(BridgeConfig, getUTCTime) | ||
| 166 | +{ | ||
| 167 | + const std::string utc = "2020-06-01T10:00:00"; | ||
| 168 | + const nlohmann::json state {{"config", {{"UTC", utc}}}}; | ||
| 169 | + auto handler = std::make_shared<MockHttpHandler>(); | ||
| 170 | + HueCommandAPI commands(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler); | ||
| 171 | + auto baseCache = std::make_shared<APICache>("", commands, std::chrono::steady_clock::duration::max()); | ||
| 172 | + EXPECT_CALL( | ||
| 173 | + *handler, GETJson("/api/" + getBridgeUsername(), nlohmann::json::object(), getBridgeIp(), getBridgePort())) | ||
| 174 | + .WillOnce(Return(state)); | ||
| 175 | + baseCache->refresh(); | ||
| 176 | + BridgeConfig config(baseCache, std::chrono::steady_clock::duration::max()); | ||
| 177 | + | ||
| 178 | + EXPECT_EQ(time::AbsoluteTime::parseUTC(utc).getBaseTime(), config.getUTCTime().getBaseTime()); | ||
| 179 | +} | ||
| 180 | + | ||
| 181 | +TEST(BridgeConfig, getTimezone) | ||
| 182 | +{ | ||
| 183 | + const std::string timezone = "ab"; | ||
| 184 | + const nlohmann::json state {{"config", {{"timezone", timezone}}}}; | ||
| 185 | + auto handler = std::make_shared<MockHttpHandler>(); | ||
| 186 | + HueCommandAPI commands(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler); | ||
| 187 | + auto baseCache = std::make_shared<APICache>("", commands, std::chrono::steady_clock::duration::max()); | ||
| 188 | + EXPECT_CALL( | ||
| 189 | + *handler, GETJson("/api/" + getBridgeUsername(), nlohmann::json::object(), getBridgeIp(), getBridgePort())) | ||
| 190 | + .WillOnce(Return(state)); | ||
| 191 | + baseCache->refresh(); | ||
| 192 | + BridgeConfig config(baseCache, std::chrono::steady_clock::duration::max()); | ||
| 193 | + | ||
| 194 | + EXPECT_EQ(timezone, config.getTimezone()); | ||
| 195 | +} |
test/test_Hue.cpp
| @@ -208,20 +208,16 @@ TEST(Hue, requestUsername) | @@ -208,20 +208,16 @@ TEST(Hue, requestUsername) | ||
| 208 | 208 | ||
| 209 | Hue test_bridge(getBridgeIp(), getBridgePort(), "", handler); | 209 | Hue test_bridge(getBridgeIp(), getBridgePort(), "", handler); |
| 210 | 210 | ||
| 211 | - std::string username = test_bridge.requestUsername(); | ||
| 212 | - | ||
| 213 | - EXPECT_EQ(username, test_bridge.getUsername()) << "Returned username not matching"; | ||
| 214 | - EXPECT_EQ(test_bridge.getBridgeIP(), getBridgeIp()) << "Bridge IP not matching"; | ||
| 215 | - EXPECT_EQ(test_bridge.getUsername(), getBridgeUsername()) << "Bridge username not matching"; | ||
| 216 | - | ||
| 217 | - // Verify that username is correctly set in api requests | ||
| 218 | - nlohmann::json hue_bridge_state {{"lights", {}}}; | 211 | + nlohmann::json hue_bridge_state{ {"lights", {}} }; |
| 219 | EXPECT_CALL( | 212 | EXPECT_CALL( |
| 220 | *handler, GETJson("/api/" + getBridgeUsername(), nlohmann::json::object(), getBridgeIp(), getBridgePort())) | 213 | *handler, GETJson("/api/" + getBridgeUsername(), nlohmann::json::object(), getBridgeIp(), getBridgePort())) |
| 221 | .Times(1) | 214 | .Times(1) |
| 222 | .WillOnce(Return(hue_bridge_state)); | 215 | .WillOnce(Return(hue_bridge_state)); |
| 216 | + std::string username = test_bridge.requestUsername(); | ||
| 223 | 217 | ||
| 224 | - test_bridge.lights().getAll(); | 218 | + EXPECT_EQ(username, test_bridge.getUsername()) << "Returned username not matching"; |
| 219 | + EXPECT_EQ(test_bridge.getBridgeIP(), getBridgeIp()) << "Bridge IP not matching"; | ||
| 220 | + EXPECT_EQ(test_bridge.getUsername(), getBridgeUsername()) << "Bridge username not matching"; | ||
| 225 | } | 221 | } |
| 226 | } | 222 | } |
| 227 | 223 |