Commit aa828bf83b4034ed875af93d5ca9a338bc4ef13a
Committed by
Moritz Wirger
1 parent
710ee59b
Implement a shared-state cache model.
All resources on the bridge internally use the same json state. Advantages: Different resources are always consistent. Disadvantages: Different objects are no longer thread safe, changes are not transparent.
Showing
15 changed files
with
110 additions
and
54 deletions
include/hueplusplus/BaseDevice.h
| @@ -120,6 +120,7 @@ public: | @@ -120,6 +120,7 @@ public: | ||
| 120 | virtual void refresh(bool force = false); | 120 | virtual void refresh(bool force = false); |
| 121 | 121 | ||
| 122 | protected: | 122 | protected: |
| 123 | + BaseDevice(int id, const std::shared_ptr<APICache>& baseCache); | ||
| 123 | //! \brief Protected ctor that is used by subclasses. | 124 | //! \brief Protected ctor that is used by subclasses. |
| 124 | //! | 125 | //! |
| 125 | //! \param id Integer that specifies the id of this device | 126 | //! \param id Integer that specifies the id of this device |
include/hueplusplus/Bridge.h
| @@ -145,9 +145,11 @@ public: | @@ -145,9 +145,11 @@ public: | ||
| 145 | //! the bridge. Can be left empty and acquired in \ref requestUsername. | 145 | //! the bridge. Can be left empty and acquired in \ref requestUsername. |
| 146 | //! \param handler HttpHandler for communication with the bridge | 146 | //! \param handler HttpHandler for communication with the bridge |
| 147 | //! \param refreshDuration Time between refreshing the cached state. | 147 | //! \param refreshDuration Time between refreshing the cached state. |
| 148 | + //! \param sharedState Uses a single, shared cache for all objects on the bridge. | ||
| 148 | Bridge(const std::string& ip, const int port, const std::string& username, | 149 | Bridge(const std::string& ip, const int port, const std::string& username, |
| 149 | std::shared_ptr<const IHttpHandler> handler, | 150 | std::shared_ptr<const IHttpHandler> handler, |
| 150 | - std::chrono::steady_clock::duration refreshDuration = std::chrono::seconds(10)); | 151 | + std::chrono::steady_clock::duration refreshDuration = std::chrono::seconds(10), |
| 152 | + bool sharedState = false); | ||
| 151 | 153 | ||
| 152 | //! \brief Refreshes the bridge state. | 154 | //! \brief Refreshes the bridge state. |
| 153 | //! | 155 | //! |
| @@ -265,6 +267,7 @@ private: | @@ -265,6 +267,7 @@ private: | ||
| 265 | detail::MakeCopyable<SensorList> sensorList; | 267 | detail::MakeCopyable<SensorList> sensorList; |
| 266 | detail::MakeCopyable<RuleList> ruleList; | 268 | detail::MakeCopyable<RuleList> ruleList; |
| 267 | detail::MakeCopyable<BridgeConfig> bridgeConfig; | 269 | detail::MakeCopyable<BridgeConfig> bridgeConfig; |
| 270 | + bool sharedState; | ||
| 268 | }; | 271 | }; |
| 269 | } // namespace hueplusplus | 272 | } // namespace hueplusplus |
| 270 | 273 |
include/hueplusplus/Group.h
| @@ -41,6 +41,7 @@ namespace hueplusplus | @@ -41,6 +41,7 @@ namespace hueplusplus | ||
| 41 | class Group | 41 | class Group |
| 42 | { | 42 | { |
| 43 | public: | 43 | public: |
| 44 | + Group(int id, const std::shared_ptr<APICache>& baseCache); | ||
| 44 | //! \brief Creates group with id | 45 | //! \brief Creates group with id |
| 45 | //! \param id Group id in the bridge | 46 | //! \param id Group id in the bridge |
| 46 | //! \param commands HueCommandAPI for requests | 47 | //! \param commands HueCommandAPI for requests |
include/hueplusplus/HueDeviceTypes.h
| @@ -46,7 +46,7 @@ public: | @@ -46,7 +46,7 @@ public: | ||
| 46 | //! \throws HueException when light type is unknown | 46 | //! \throws HueException when light type is unknown |
| 47 | //! \throws HueAPIResponseException when response contains an error | 47 | //! \throws HueAPIResponseException when response contains an error |
| 48 | //! \throws nlohmann::json::parse_error when response could not be parsed | 48 | //! \throws nlohmann::json::parse_error when response could not be parsed |
| 49 | - Light createLight(const nlohmann::json& lightState, int id); | 49 | + Light createLight(const nlohmann::json& lightState, int id, const std::shared_ptr<APICache>& baseCache = {}); |
| 50 | 50 | ||
| 51 | private: | 51 | private: |
| 52 | //! \brief Get color type from light JSON. | 52 | //! \brief Get color type from light JSON. |
include/hueplusplus/Light.h
| @@ -554,6 +554,7 @@ public: | @@ -554,6 +554,7 @@ public: | ||
| 554 | ///@} | 554 | ///@} |
| 555 | 555 | ||
| 556 | protected: | 556 | protected: |
| 557 | + | ||
| 557 | //! \brief Protected ctor that is used by \ref Bridge class. | 558 | //! \brief Protected ctor that is used by \ref Bridge class. |
| 558 | //! | 559 | //! |
| 559 | //! \param id Integer that specifies the id of this light | 560 | //! \param id Integer that specifies the id of this light |
| @@ -562,6 +563,9 @@ protected: | @@ -562,6 +563,9 @@ protected: | ||
| 562 | //! leaves strategies unset | 563 | //! leaves strategies unset |
| 563 | Light(int id, const HueCommandAPI& commands); | 564 | Light(int id, const HueCommandAPI& commands); |
| 564 | 565 | ||
| 566 | + | ||
| 567 | + Light(int id, const std::shared_ptr<APICache>& baseCache); | ||
| 568 | + | ||
| 565 | //! \brief Protected ctor that is used by \ref Bridge class, also sets | 569 | //! \brief Protected ctor that is used by \ref Bridge class, also sets |
| 566 | //! strategies. | 570 | //! strategies. |
| 567 | //! | 571 | //! |
include/hueplusplus/ResourceList.h
| @@ -45,6 +45,9 @@ template <typename Resource, typename IdT> | @@ -45,6 +45,9 @@ template <typename Resource, typename IdT> | ||
| 45 | class ResourceList | 45 | class ResourceList |
| 46 | { | 46 | { |
| 47 | public: | 47 | public: |
| 48 | + struct SharedStateTag | ||
| 49 | + { }; | ||
| 50 | + | ||
| 48 | using ResourceType = Resource; | 51 | using ResourceType = Resource; |
| 49 | using IdType = IdT; | 52 | using IdType = IdT; |
| 50 | static_assert(std::is_integral<IdType>::value || std::is_same<std::string, IdType>::value, | 53 | static_assert(std::is_integral<IdType>::value || std::is_same<std::string, IdType>::value, |
| @@ -57,9 +60,13 @@ public: | @@ -57,9 +60,13 @@ public: | ||
| 57 | //! \param factory Optional factory function to create Resources. | 60 | //! \param factory Optional factory function to create Resources. |
| 58 | //! Necessary if Resource is not constructible as described above. | 61 | //! Necessary if Resource is not constructible as described above. |
| 59 | ResourceList(std::shared_ptr<APICache> baseCache, const std::string& cacheEntry, | 62 | ResourceList(std::shared_ptr<APICache> baseCache, const std::string& cacheEntry, |
| 60 | - std::chrono::steady_clock::duration refreshDuration, | ||
| 61 | - const std::function<Resource(IdType, const nlohmann::json&)>& factory = nullptr) | ||
| 62 | - : stateCache(baseCache, cacheEntry, refreshDuration), factory(factory), path(stateCache.getRequestPath() + '/') | 63 | + std::chrono::steady_clock::duration refreshDuration, bool sharedState = false, |
| 64 | + const std::function<Resource(IdType, const nlohmann::json&, const std::shared_ptr<APICache>&)>& factory | ||
| 65 | + = nullptr) | ||
| 66 | + : stateCache(std::make_shared<APICache>(baseCache, cacheEntry, refreshDuration)), | ||
| 67 | + factory(factory), | ||
| 68 | + path(stateCache->getRequestPath() + '/'), | ||
| 69 | + sharedState(sharedState) | ||
| 63 | { } | 70 | { } |
| 64 | //! \brief Construct ResourceList with a separate cache and optional factory function | 71 | //! \brief Construct ResourceList with a separate cache and optional factory function |
| 65 | //! \param commands HueCommandAPI for requests | 72 | //! \param commands HueCommandAPI for requests |
| @@ -69,8 +76,12 @@ public: | @@ -69,8 +76,12 @@ public: | ||
| 69 | //! Necessary if Resource is not constructible as described above. | 76 | //! Necessary if Resource is not constructible as described above. |
| 70 | ResourceList(const HueCommandAPI& commands, const std::string& path, | 77 | ResourceList(const HueCommandAPI& commands, const std::string& path, |
| 71 | std::chrono::steady_clock::duration refreshDuration, | 78 | std::chrono::steady_clock::duration refreshDuration, |
| 72 | - const std::function<Resource(IdType, const nlohmann::json&)>& factory = nullptr) | ||
| 73 | - : stateCache(path, commands, refreshDuration), factory(factory), path(path + '/') | 79 | + const std::function<Resource(IdType, const nlohmann::json&, const std::shared_ptr<APICache>&)>& factory |
| 80 | + = nullptr) | ||
| 81 | + : stateCache(std::make_shared<APICache>(path, commands, refreshDuration)), | ||
| 82 | + factory(factory), | ||
| 83 | + path(path + '/'), | ||
| 84 | + sharedState(false) | ||
| 74 | { } | 85 | { } |
| 75 | 86 | ||
| 76 | //! \brief Deleted copy constructor | 87 | //! \brief Deleted copy constructor |
| @@ -79,7 +90,7 @@ public: | @@ -79,7 +90,7 @@ public: | ||
| 79 | ResourceList& operator=(const ResourceList&) = delete; | 90 | ResourceList& operator=(const ResourceList&) = delete; |
| 80 | 91 | ||
| 81 | //! \brief Refreshes internal state now | 92 | //! \brief Refreshes internal state now |
| 82 | - void refresh() { stateCache.refresh(); } | 93 | + void refresh() { stateCache->refresh(); } |
| 83 | 94 | ||
| 84 | //! \brief Get all resources that exist | 95 | //! \brief Get all resources that exist |
| 85 | //! \returns A vector of references to every Resource | 96 | //! \returns A vector of references to every Resource |
| @@ -89,7 +100,7 @@ public: | @@ -89,7 +100,7 @@ public: | ||
| 89 | //! \throws nlohmann::json::parse_error when response could not be parsed | 100 | //! \throws nlohmann::json::parse_error when response could not be parsed |
| 90 | std::vector<std::reference_wrapper<Resource>> getAll() | 101 | std::vector<std::reference_wrapper<Resource>> getAll() |
| 91 | { | 102 | { |
| 92 | - nlohmann::json state = stateCache.getValue(); | 103 | + nlohmann::json state = stateCache->getValue(); |
| 93 | for (auto it = state.begin(); it != state.end(); ++it) | 104 | for (auto it = state.begin(); it != state.end(); ++it) |
| 94 | { | 105 | { |
| 95 | get(maybeStoi(it.key())); | 106 | get(maybeStoi(it.key())); |
| @@ -118,7 +129,7 @@ public: | @@ -118,7 +129,7 @@ public: | ||
| 118 | pos->second.refresh(true); | 129 | pos->second.refresh(true); |
| 119 | return pos->second; | 130 | return pos->second; |
| 120 | } | 131 | } |
| 121 | - const nlohmann::json& state = stateCache.getValue(); | 132 | + const nlohmann::json& state = stateCache->getValue(); |
| 122 | std::string key = maybeToString(id); | 133 | std::string key = maybeToString(id); |
| 123 | if (!state.count(key)) | 134 | if (!state.count(key)) |
| 124 | { | 135 | { |
| @@ -141,7 +152,7 @@ public: | @@ -141,7 +152,7 @@ public: | ||
| 141 | { | 152 | { |
| 142 | return true; | 153 | return true; |
| 143 | } | 154 | } |
| 144 | - return stateCache.getValue().count(maybeToString(id)) != 0; | 155 | + return stateCache->getValue().count(maybeToString(id)) != 0; |
| 145 | } | 156 | } |
| 146 | 157 | ||
| 147 | //! \brief Checks whether resource with id exists | 158 | //! \brief Checks whether resource with id exists |
| @@ -156,7 +167,7 @@ public: | @@ -156,7 +167,7 @@ public: | ||
| 156 | { | 167 | { |
| 157 | return true; | 168 | return true; |
| 158 | } | 169 | } |
| 159 | - return stateCache.getValue().count(maybeToString(id)) != 0; | 170 | + return stateCache->getValue().count(maybeToString(id)) != 0; |
| 160 | } | 171 | } |
| 161 | 172 | ||
| 162 | //! \brief Removes the resource | 173 | //! \brief Removes the resource |
| @@ -171,7 +182,7 @@ public: | @@ -171,7 +182,7 @@ public: | ||
| 171 | bool remove(const IdType& id) | 182 | bool remove(const IdType& id) |
| 172 | { | 183 | { |
| 173 | std::string requestPath = path + maybeToString(id); | 184 | std::string requestPath = path + maybeToString(id); |
| 174 | - nlohmann::json result = stateCache.getCommandAPI().DELETERequest( | 185 | + nlohmann::json result = stateCache->getCommandAPI().DELETERequest( |
| 175 | requestPath, nlohmann::json::object(), FileInfo {__FILE__, __LINE__, __func__}); | 186 | requestPath, nlohmann::json::object(), FileInfo {__FILE__, __LINE__, __func__}); |
| 176 | bool success = utils::safeGetMember(result, 0, "success") == requestPath + " deleted"; | 187 | bool success = utils::safeGetMember(result, 0, "success") == requestPath + " deleted"; |
| 177 | auto it = resources.find(id); | 188 | auto it = resources.find(id); |
| @@ -208,11 +219,18 @@ private: | @@ -208,11 +219,18 @@ private: | ||
| 208 | { | 219 | { |
| 209 | if (factory) | 220 | if (factory) |
| 210 | { | 221 | { |
| 211 | - return factory(id, state); | 222 | + return factory(id, state, sharedState ? stateCache : std::shared_ptr<APICache>()); |
| 212 | } | 223 | } |
| 213 | else | 224 | else |
| 214 | { | 225 | { |
| 215 | - return Resource(id, stateCache.getCommandAPI(), stateCache.getRefreshDuration()); | 226 | + if (sharedState) |
| 227 | + { | ||
| 228 | + return Resource(id, stateCache); | ||
| 229 | + } | ||
| 230 | + else | ||
| 231 | + { | ||
| 232 | + return Resource(id, stateCache->getCommandAPI(), stateCache->getRefreshDuration()); | ||
| 233 | + } | ||
| 216 | } | 234 | } |
| 217 | } | 235 | } |
| 218 | // Resource is not constructable | 236 | // Resource is not constructable |
| @@ -223,7 +241,7 @@ private: | @@ -223,7 +241,7 @@ private: | ||
| 223 | throw HueException(FileInfo {__FILE__, __LINE__, __func__}, | 241 | throw HueException(FileInfo {__FILE__, __LINE__, __func__}, |
| 224 | "Resource is not constructable with default parameters, but no factory given"); | 242 | "Resource is not constructable with default parameters, but no factory given"); |
| 225 | } | 243 | } |
| 226 | - return factory(id, state); | 244 | + return factory(id, state, sharedState ? stateCache : std::shared_ptr<APICache>()); |
| 227 | } | 245 | } |
| 228 | 246 | ||
| 229 | private: | 247 | private: |
| @@ -233,10 +251,11 @@ private: | @@ -233,10 +251,11 @@ private: | ||
| 233 | static std::string maybeToString(const IdType& id, std::false_type) { return id; } | 251 | static std::string maybeToString(const IdType& id, std::false_type) { return id; } |
| 234 | 252 | ||
| 235 | protected: | 253 | protected: |
| 236 | - APICache stateCache; | ||
| 237 | - std::function<Resource(IdType, const nlohmann::json&)> factory; | 254 | + std::shared_ptr<APICache> stateCache; |
| 255 | + std::function<Resource(IdType, const nlohmann::json&, const std::shared_ptr<APICache>&)> factory; | ||
| 238 | std::string path; | 256 | std::string path; |
| 239 | std::map<IdType, Resource> resources; | 257 | std::map<IdType, Resource> resources; |
| 258 | + bool sharedState; | ||
| 240 | }; | 259 | }; |
| 241 | 260 | ||
| 242 | //! \brief Handles a ResourceList of physical devices which can be searched for | 261 | //! \brief Handles a ResourceList of physical devices which can be searched for |
| @@ -258,12 +277,12 @@ public: | @@ -258,12 +277,12 @@ public: | ||
| 258 | requestPath.pop_back(); | 277 | requestPath.pop_back(); |
| 259 | if (deviceIds.empty()) | 278 | if (deviceIds.empty()) |
| 260 | { | 279 | { |
| 261 | - this->stateCache.getCommandAPI().POSTRequest( | 280 | + this->stateCache->getCommandAPI().POSTRequest( |
| 262 | requestPath, nlohmann::json::object(), FileInfo {__FILE__, __LINE__, __func__}); | 281 | requestPath, nlohmann::json::object(), FileInfo {__FILE__, __LINE__, __func__}); |
| 263 | } | 282 | } |
| 264 | else | 283 | else |
| 265 | { | 284 | { |
| 266 | - this->stateCache.getCommandAPI().POSTRequest( | 285 | + this->stateCache->getCommandAPI().POSTRequest( |
| 267 | requestPath, nlohmann::json {{"deviceid", deviceIds}}, FileInfo {__FILE__, __LINE__, __func__}); | 286 | requestPath, nlohmann::json {{"deviceid", deviceIds}}, FileInfo {__FILE__, __LINE__, __func__}); |
| 268 | } | 287 | } |
| 269 | } | 288 | } |
| @@ -271,7 +290,7 @@ public: | @@ -271,7 +290,7 @@ public: | ||
| 271 | //! \brief Get devices found in last search | 290 | //! \brief Get devices found in last search |
| 272 | NewDeviceList getNewDevices() const | 291 | NewDeviceList getNewDevices() const |
| 273 | { | 292 | { |
| 274 | - nlohmann::json response = this->stateCache.getCommandAPI().GETRequest( | 293 | + nlohmann::json response = this->stateCache->getCommandAPI().GETRequest( |
| 275 | this->path + "new", nlohmann::json::object(), FileInfo {__FILE__, __LINE__, __func__}); | 294 | this->path + "new", nlohmann::json::object(), FileInfo {__FILE__, __LINE__, __func__}); |
| 276 | return NewDeviceList::parse(response); | 295 | return NewDeviceList::parse(response); |
| 277 | } | 296 | } |
| @@ -306,7 +325,7 @@ public: | @@ -306,7 +325,7 @@ public: | ||
| 306 | std::string requestPath = this->path; | 325 | std::string requestPath = this->path; |
| 307 | // Remove slash | 326 | // Remove slash |
| 308 | requestPath.pop_back(); | 327 | requestPath.pop_back(); |
| 309 | - nlohmann::json response = this->stateCache.getCommandAPI().POSTRequest( | 328 | + nlohmann::json response = this->stateCache->getCommandAPI().POSTRequest( |
| 310 | requestPath, params.getRequest(), FileInfo {__FILE__, __LINE__, __func__}); | 329 | requestPath, params.getRequest(), FileInfo {__FILE__, __LINE__, __func__}); |
| 311 | nlohmann::json id = utils::safeGetMember(response, 0, "success", "id"); | 330 | nlohmann::json id = utils::safeGetMember(response, 0, "success", "id"); |
| 312 | if (id.is_string()) | 331 | if (id.is_string()) |
| @@ -316,7 +335,7 @@ public: | @@ -316,7 +335,7 @@ public: | ||
| 316 | { | 335 | { |
| 317 | idStr.erase(0, this->path.size()); | 336 | idStr.erase(0, this->path.size()); |
| 318 | } | 337 | } |
| 319 | - this->stateCache.refresh(); | 338 | + this->stateCache->refresh(); |
| 320 | return this->maybeStoi(idStr); | 339 | return this->maybeStoi(idStr); |
| 321 | } | 340 | } |
| 322 | return typename BaseResourceList::IdType {}; | 341 | return typename BaseResourceList::IdType {}; |
| @@ -350,7 +369,7 @@ public: | @@ -350,7 +369,7 @@ public: | ||
| 350 | pos->second.refresh(); | 369 | pos->second.refresh(); |
| 351 | return pos->second; | 370 | return pos->second; |
| 352 | } | 371 | } |
| 353 | - const nlohmann::json& state = this->stateCache.getValue(); | 372 | + const nlohmann::json& state = this->stateCache->getValue(); |
| 354 | std::string key = this->maybeToString(id); | 373 | std::string key = this->maybeToString(id); |
| 355 | if (!state.count(key) && id != 0) | 374 | if (!state.count(key) && id != 0) |
| 356 | { | 375 | { |
include/hueplusplus/Sensor.h
| @@ -59,6 +59,8 @@ Alert alertFromString(const std::string& s); | @@ -59,6 +59,8 @@ Alert alertFromString(const std::string& s); | ||
| 59 | class Sensor : public BaseDevice | 59 | class Sensor : public BaseDevice |
| 60 | { | 60 | { |
| 61 | public: | 61 | public: |
| 62 | + Sensor(int id, const std::shared_ptr<APICache>& baseCache); | ||
| 63 | + | ||
| 62 | //! \brief Construct Sensor. | 64 | //! \brief Construct Sensor. |
| 63 | //! \param id Integer that specifies the id of this sensor | 65 | //! \param id Integer that specifies the id of this sensor |
| 64 | //! \param commands HueCommandAPI for communication with the bridge | 66 | //! \param commands HueCommandAPI for communication with the bridge |
include/hueplusplus/SensorList.h
| @@ -58,7 +58,7 @@ public: | @@ -58,7 +58,7 @@ public: | ||
| 58 | template <typename T> | 58 | template <typename T> |
| 59 | std::vector<T> getAllByType() | 59 | std::vector<T> getAllByType() |
| 60 | { | 60 | { |
| 61 | - nlohmann::json state = this->stateCache.getValue(); | 61 | + nlohmann::json state = this->stateCache->getValue(); |
| 62 | std::vector<T> result; | 62 | std::vector<T> result; |
| 63 | for (auto it = state.begin(); it != state.end(); ++it) | 63 | for (auto it = state.begin(); it != state.end(); ++it) |
| 64 | { | 64 | { |
src/BaseDevice.cpp
| @@ -91,6 +91,10 @@ bool BaseDevice::setName(const std::string& name) | @@ -91,6 +91,10 @@ bool BaseDevice::setName(const std::string& name) | ||
| 91 | return utils::safeGetMember(reply, 0, "success", "/lights/" + std::to_string(id) + "/name").is_string(); | 91 | return utils::safeGetMember(reply, 0, "success", "/lights/" + std::to_string(id) + "/name").is_string(); |
| 92 | } | 92 | } |
| 93 | 93 | ||
| 94 | +BaseDevice::BaseDevice(int id, const std::shared_ptr<APICache>& baseCache) | ||
| 95 | + : id(id), state(baseCache, std::to_string(id), baseCache->getRefreshDuration()) | ||
| 96 | +{ } | ||
| 97 | + | ||
| 94 | BaseDevice::BaseDevice( | 98 | BaseDevice::BaseDevice( |
| 95 | int id, const HueCommandAPI& commands, const std::string& path, std::chrono::steady_clock::duration refreshDuration) | 99 | int id, const HueCommandAPI& commands, const std::string& path, std::chrono::steady_clock::duration refreshDuration) |
| 96 | : id(id), path(path), state(path + std::to_string(id), commands, refreshDuration) | 100 | : id(id), path(path), state(path + std::to_string(id), commands, refreshDuration) |
src/Bridge.cpp
| @@ -138,7 +138,7 @@ std::string BridgeFinder::ParseDescription(const std::string& description) | @@ -138,7 +138,7 @@ std::string BridgeFinder::ParseDescription(const std::string& description) | ||
| 138 | } | 138 | } |
| 139 | 139 | ||
| 140 | Bridge::Bridge(const std::string& ip, const int port, const std::string& username, | 140 | Bridge::Bridge(const std::string& ip, const int port, const std::string& username, |
| 141 | - std::shared_ptr<const IHttpHandler> handler, std::chrono::steady_clock::duration refreshDuration) | 141 | + std::shared_ptr<const IHttpHandler> handler, std::chrono::steady_clock::duration refreshDuration, bool sharedState) |
| 142 | : ip(ip), | 142 | : ip(ip), |
| 143 | username(username), | 143 | username(username), |
| 144 | port(port), | 144 | port(port), |
| @@ -146,15 +146,18 @@ Bridge::Bridge(const std::string& ip, const int port, const std::string& usernam | @@ -146,15 +146,18 @@ Bridge::Bridge(const std::string& ip, const int port, const std::string& usernam | ||
| 146 | refreshDuration(refreshDuration), | 146 | refreshDuration(refreshDuration), |
| 147 | stateCache(std::make_shared<APICache>( | 147 | stateCache(std::make_shared<APICache>( |
| 148 | "", HueCommandAPI(ip, port, username, http_handler), std::chrono::steady_clock::duration::max())), | 148 | "", HueCommandAPI(ip, port, username, http_handler), std::chrono::steady_clock::duration::max())), |
| 149 | - lightList(stateCache, "lights", refreshDuration, | 149 | + lightList(stateCache, "lights", refreshDuration, sharedState, |
| 150 | [factory = LightFactory(stateCache->getCommandAPI(), refreshDuration)]( | 150 | [factory = LightFactory(stateCache->getCommandAPI(), refreshDuration)]( |
| 151 | - int id, const nlohmann::json& state) mutable { return factory.createLight(state, id); }), | ||
| 152 | - groupList(stateCache, "groups", refreshDuration), | ||
| 153 | - scheduleList(stateCache, "schedules", refreshDuration), | ||
| 154 | - sceneList(stateCache, "scenes", refreshDuration), | ||
| 155 | - sensorList(stateCache, "sensors", refreshDuration), | ||
| 156 | - ruleList(stateCache, "rules", refreshDuration), | ||
| 157 | - bridgeConfig(stateCache, refreshDuration) | 151 | + int id, const nlohmann::json& state, const std::shared_ptr<APICache>& baseCache) mutable { |
| 152 | + return factory.createLight(state, id, baseCache); | ||
| 153 | + }), | ||
| 154 | + groupList(stateCache, "groups", refreshDuration, sharedState), | ||
| 155 | + scheduleList(stateCache, "schedules", refreshDuration, sharedState), | ||
| 156 | + sceneList(stateCache, "scenes", refreshDuration, sharedState), | ||
| 157 | + sensorList(stateCache, "sensors", refreshDuration, sharedState), | ||
| 158 | + ruleList(stateCache, "rules", refreshDuration, sharedState), | ||
| 159 | + bridgeConfig(stateCache, refreshDuration), | ||
| 160 | + sharedState(sharedState) | ||
| 158 | { } | 161 | { } |
| 159 | 162 | ||
| 160 | void Bridge::refresh() | 163 | void Bridge::refresh() |
| @@ -304,13 +307,14 @@ void Bridge::setHttpHandler(std::shared_ptr<const IHttpHandler> handler) | @@ -304,13 +307,14 @@ void Bridge::setHttpHandler(std::shared_ptr<const IHttpHandler> handler) | ||
| 304 | { | 307 | { |
| 305 | http_handler = handler; | 308 | http_handler = handler; |
| 306 | stateCache = std::make_shared<APICache>("", HueCommandAPI(ip, port, username, handler), refreshDuration); | 309 | stateCache = std::make_shared<APICache>("", HueCommandAPI(ip, port, username, handler), refreshDuration); |
| 307 | - lightList = LightList(stateCache, "lights", refreshDuration, | ||
| 308 | - [factory = LightFactory(stateCache->getCommandAPI(), refreshDuration)]( | ||
| 309 | - int id, const nlohmann::json& state) mutable { return factory.createLight(state, id); }); | ||
| 310 | - groupList = GroupList(stateCache, "groups", refreshDuration); | ||
| 311 | - scheduleList = ScheduleList(stateCache, "schedules", refreshDuration); | ||
| 312 | - sceneList = SceneList(stateCache, "scenes", refreshDuration); | ||
| 313 | - sensorList = SensorList(stateCache, "sensors", refreshDuration); | 310 | + lightList = LightList(stateCache, "lights", refreshDuration,sharedState, |
| 311 | + [factory = LightFactory(stateCache->getCommandAPI(), refreshDuration)](int id, const nlohmann::json& state, | ||
| 312 | + const std::shared_ptr<APICache>& baseCache) mutable { return factory.createLight(state, id, baseCache); }); | ||
| 313 | + groupList = GroupList(stateCache, "groups", refreshDuration, sharedState); | ||
| 314 | + scheduleList = ScheduleList(stateCache, "schedules", refreshDuration, sharedState); | ||
| 315 | + sceneList = SceneList(stateCache, "scenes", refreshDuration, sharedState); | ||
| 316 | + sensorList = SensorList(stateCache, "sensors", refreshDuration, sharedState); | ||
| 317 | + ruleList = RuleList(stateCache, "rules", refreshDuration, sharedState); | ||
| 314 | bridgeConfig = BridgeConfig(stateCache, refreshDuration); | 318 | bridgeConfig = BridgeConfig(stateCache, refreshDuration); |
| 315 | stateCache->refresh(); | 319 | stateCache->refresh(); |
| 316 | } | 320 | } |
src/Group.cpp
| @@ -4,6 +4,10 @@ | @@ -4,6 +4,10 @@ | ||
| 4 | 4 | ||
| 5 | namespace hueplusplus | 5 | namespace hueplusplus |
| 6 | { | 6 | { |
| 7 | +Group::Group(int id, const std::shared_ptr<APICache>& baseCache) | ||
| 8 | + : id(id), state(baseCache, std::to_string(id), baseCache->getRefreshDuration()) | ||
| 9 | +{ } | ||
| 10 | + | ||
| 7 | Group::Group(int id, const HueCommandAPI& commands, std::chrono::steady_clock::duration refreshDuration) | 11 | Group::Group(int id, const HueCommandAPI& commands, std::chrono::steady_clock::duration refreshDuration) |
| 8 | : id(id), state("/groups/" + std::to_string(id), commands, refreshDuration) | 12 | : id(id), state("/groups/" + std::to_string(id), commands, refreshDuration) |
| 9 | { | 13 | { |
src/HueDeviceTypes.cpp
| @@ -67,41 +67,46 @@ LightFactory::LightFactory(const HueCommandAPI& commands, std::chrono::steady_cl | @@ -67,41 +67,46 @@ LightFactory::LightFactory(const HueCommandAPI& commands, std::chrono::steady_cl | ||
| 67 | extendedColorTemperature(std::make_shared<ExtendedColorTemperatureStrategy>()), | 67 | extendedColorTemperature(std::make_shared<ExtendedColorTemperatureStrategy>()), |
| 68 | simpleColorHue(std::make_shared<SimpleColorHueStrategy>()), | 68 | simpleColorHue(std::make_shared<SimpleColorHueStrategy>()), |
| 69 | extendedColorHue(std::make_shared<ExtendedColorHueStrategy>()) | 69 | extendedColorHue(std::make_shared<ExtendedColorHueStrategy>()) |
| 70 | -{} | 70 | +{ } |
| 71 | 71 | ||
| 72 | -Light LightFactory::createLight(const nlohmann::json& lightState, int id) | 72 | +Light LightFactory::createLight(const nlohmann::json& lightState, int id, const std::shared_ptr<APICache>& baseCache) |
| 73 | { | 73 | { |
| 74 | std::string type = lightState.value("type", ""); | 74 | std::string type = lightState.value("type", ""); |
| 75 | // Ignore case | 75 | // Ignore case |
| 76 | std::transform(type.begin(), type.end(), type.begin(), [](char c) { return std::tolower(c); }); | 76 | std::transform(type.begin(), type.end(), type.begin(), [](char c) { return std::tolower(c); }); |
| 77 | 77 | ||
| 78 | + Light light = baseCache ? Light(id, baseCache) : Light(id, commands, nullptr, nullptr, nullptr, refreshDuration); | ||
| 79 | + | ||
| 78 | if (type == "on/off light" || type == "on/off plug-in unit") | 80 | if (type == "on/off light" || type == "on/off plug-in unit") |
| 79 | { | 81 | { |
| 80 | - Light light(id, commands, nullptr, nullptr, nullptr, refreshDuration); | ||
| 81 | light.colorType = ColorType::NONE; | 82 | light.colorType = ColorType::NONE; |
| 82 | return light; | 83 | return light; |
| 83 | } | 84 | } |
| 84 | - else if (type == "dimmable light" || type =="dimmable plug-in unit") | 85 | + else if (type == "dimmable light" || type == "dimmable plug-in unit") |
| 85 | { | 86 | { |
| 86 | - Light light(id, commands, simpleBrightness, nullptr, nullptr, refreshDuration); | 87 | + light.setBrightnessStrategy(simpleBrightness); |
| 87 | light.colorType = ColorType::NONE; | 88 | light.colorType = ColorType::NONE; |
| 88 | return light; | 89 | return light; |
| 89 | } | 90 | } |
| 90 | else if (type == "color temperature light") | 91 | else if (type == "color temperature light") |
| 91 | { | 92 | { |
| 92 | - Light light(id, commands, simpleBrightness, simpleColorTemperature, nullptr, refreshDuration); | 93 | + light.setBrightnessStrategy(simpleBrightness); |
| 94 | + light.setColorTemperatureStrategy(simpleColorTemperature); | ||
| 93 | light.colorType = ColorType::TEMPERATURE; | 95 | light.colorType = ColorType::TEMPERATURE; |
| 94 | return light; | 96 | return light; |
| 95 | } | 97 | } |
| 96 | else if (type == "color light") | 98 | else if (type == "color light") |
| 97 | { | 99 | { |
| 98 | - Light light(id, commands, simpleBrightness, nullptr, simpleColorHue, refreshDuration); | 100 | + light.setBrightnessStrategy(simpleBrightness); |
| 101 | + light.setColorHueStrategy(simpleColorHue); | ||
| 99 | light.colorType = getColorType(lightState, false); | 102 | light.colorType = getColorType(lightState, false); |
| 100 | return light; | 103 | return light; |
| 101 | } | 104 | } |
| 102 | else if (type == "extended color light") | 105 | else if (type == "extended color light") |
| 103 | { | 106 | { |
| 104 | - Light light(id, commands, simpleBrightness, extendedColorTemperature, extendedColorHue, refreshDuration); | 107 | + light.setBrightnessStrategy(simpleBrightness); |
| 108 | + light.setColorTemperatureStrategy(extendedColorTemperature); | ||
| 109 | + light.setColorHueStrategy(extendedColorHue); | ||
| 105 | light.colorType = getColorType(lightState, true); | 110 | light.colorType = getColorType(lightState, true); |
| 106 | return light; | 111 | return light; |
| 107 | } | 112 | } |
src/Light.cpp
| @@ -114,6 +114,9 @@ StateTransaction Light::transaction() | @@ -114,6 +114,9 @@ StateTransaction Light::transaction() | ||
| 114 | 114 | ||
| 115 | Light::Light(int id, const HueCommandAPI& commands) : Light(id, commands, nullptr, nullptr, nullptr) { } | 115 | Light::Light(int id, const HueCommandAPI& commands) : Light(id, commands, nullptr, nullptr, nullptr) { } |
| 116 | 116 | ||
| 117 | +Light::Light(int id, const std::shared_ptr<APICache>& baseCache) : BaseDevice(id, baseCache), colorType(ColorType::NONE) | ||
| 118 | +{ } | ||
| 119 | + | ||
| 117 | Light::Light(int id, const HueCommandAPI& commands, std::shared_ptr<const BrightnessStrategy> brightnessStrategy, | 120 | Light::Light(int id, const HueCommandAPI& commands, std::shared_ptr<const BrightnessStrategy> brightnessStrategy, |
| 118 | std::shared_ptr<const ColorTemperatureStrategy> colorTempStrategy, | 121 | std::shared_ptr<const ColorTemperatureStrategy> colorTempStrategy, |
| 119 | std::shared_ptr<const ColorHueStrategy> colorHueStrategy, std::chrono::steady_clock::duration refreshDuration) | 122 | std::shared_ptr<const ColorHueStrategy> colorHueStrategy, std::chrono::steady_clock::duration refreshDuration) |
| @@ -122,6 +125,5 @@ Light::Light(int id, const HueCommandAPI& commands, std::shared_ptr<const Bright | @@ -122,6 +125,5 @@ Light::Light(int id, const HueCommandAPI& commands, std::shared_ptr<const Bright | ||
| 122 | brightnessStrategy(std::move(brightnessStrategy)), | 125 | brightnessStrategy(std::move(brightnessStrategy)), |
| 123 | colorTemperatureStrategy(std::move(colorTempStrategy)), | 126 | colorTemperatureStrategy(std::move(colorTempStrategy)), |
| 124 | colorHueStrategy(std::move(colorHueStrategy)) | 127 | colorHueStrategy(std::move(colorHueStrategy)) |
| 125 | -{ | ||
| 126 | -} | 128 | +{ } |
| 127 | } // namespace hueplusplus | 129 | } // namespace hueplusplus |
src/Sensor.cpp
| @@ -230,6 +230,11 @@ bool Sensor::isPrimary() const | @@ -230,6 +230,11 @@ bool Sensor::isPrimary() const | ||
| 230 | return primary.is_boolean() && primary.get<bool>(); | 230 | return primary.is_boolean() && primary.get<bool>(); |
| 231 | } | 231 | } |
| 232 | 232 | ||
| 233 | +Sensor::Sensor(int id, const std::shared_ptr<APICache>& baseCache) | ||
| 234 | + : BaseDevice(id, baseCache) | ||
| 235 | +{ } | ||
| 236 | + | ||
| 237 | + | ||
| 233 | Sensor::Sensor(int id, const HueCommandAPI& commands, std::chrono::steady_clock::duration refreshDuration) | 238 | Sensor::Sensor(int id, const HueCommandAPI& commands, std::chrono::steady_clock::duration refreshDuration) |
| 234 | : BaseDevice(id, commands, "/sensors/", refreshDuration) | 239 | : BaseDevice(id, commands, "/sensors/", refreshDuration) |
| 235 | { } | 240 | { } |
test/test_ResourceList.cpp
| @@ -32,6 +32,7 @@ using namespace testing; | @@ -32,6 +32,7 @@ using namespace testing; | ||
| 32 | class TestResource | 32 | class TestResource |
| 33 | { | 33 | { |
| 34 | public: | 34 | public: |
| 35 | + TestResource(int id, std::shared_ptr<APICache> baseCache) {} | ||
| 35 | TestResource(int id, HueCommandAPI api, std::chrono::steady_clock::duration refreshDuration) : id(id) { } | 36 | TestResource(int id, HueCommandAPI api, std::chrono::steady_clock::duration refreshDuration) : id(id) { } |
| 36 | 37 | ||
| 37 | void refresh(bool force = false) { } | 38 | void refresh(bool force = false) { } |
| @@ -47,6 +48,7 @@ public: | @@ -47,6 +48,7 @@ public: | ||
| 47 | class TestStringResource | 48 | class TestStringResource |
| 48 | { | 49 | { |
| 49 | public: | 50 | public: |
| 51 | + TestStringResource(const std::string& id, std::shared_ptr<APICache> baseCache) {} | ||
| 50 | TestStringResource(const std::string& id, HueCommandAPI api, std::chrono::steady_clock::duration refreshDuration) | 52 | TestStringResource(const std::string& id, HueCommandAPI api, std::chrono::steady_clock::duration refreshDuration) |
| 51 | : id(id) | 53 | : id(id) |
| 52 | { } | 54 | { } |
| @@ -122,8 +124,8 @@ TEST(ResourceList, get) | @@ -122,8 +124,8 @@ TEST(ResourceList, get) | ||
| 122 | const nlohmann::json state = {{"resource", "state"}}; | 124 | const nlohmann::json state = {{"resource", "state"}}; |
| 123 | const nlohmann::json response = {{std::to_string(id), state}}; | 125 | const nlohmann::json response = {{std::to_string(id), state}}; |
| 124 | 126 | ||
| 125 | - MockFunction<TestResource(int, const nlohmann::json&)> factory; | ||
| 126 | - EXPECT_CALL(factory, Call(id, state)) | 127 | + MockFunction<TestResource(int, const nlohmann::json&, const std::shared_ptr<APICache>&)> factory; |
| 128 | + EXPECT_CALL(factory, Call(id, state, std::shared_ptr<APICache>())) | ||
| 127 | .WillOnce(Return(TestResource(id, commands, std::chrono::steady_clock::duration::max()))); | 129 | .WillOnce(Return(TestResource(id, commands, std::chrono::steady_clock::duration::max()))); |
| 128 | 130 | ||
| 129 | ResourceList<TestResource, int> list( | 131 | ResourceList<TestResource, int> list( |
| @@ -162,7 +164,7 @@ TEST(ResourceList, get) | @@ -162,7 +164,7 @@ TEST(ResourceList, get) | ||
| 162 | } | 164 | } |
| 163 | { | 165 | { |
| 164 | ResourceList<TestResourceFactory, int> list(commands, path, std::chrono::steady_clock::duration::max(), | 166 | ResourceList<TestResourceFactory, int> list(commands, path, std::chrono::steady_clock::duration::max(), |
| 165 | - [](int, const nlohmann::json&) { return TestResourceFactory(); }); | 167 | + [](int, const nlohmann::json&, const std::shared_ptr<APICache>&) { return TestResourceFactory(); }); |
| 166 | 168 | ||
| 167 | const int id = 2; | 169 | const int id = 2; |
| 168 | const nlohmann::json response = {{std::to_string(id), {{"resource", "state"}}}}; | 170 | const nlohmann::json response = {{std::to_string(id), {{"resource", "state"}}}}; |