Commit aa828bf83b4034ed875af93d5ca9a338bc4ef13a

Authored by Jojo-1000
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.
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 &lt;typename Resource, typename IdT&gt; @@ -45,6 +45,9 @@ template &lt;typename Resource, typename IdT&gt;
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&amp; s); @@ -59,6 +59,8 @@ Alert alertFromString(const std::string&amp; 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&amp; name) @@ -91,6 +91,10 @@ bool BaseDevice::setName(const std::string&amp; 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&amp; description) @@ -138,7 +138,7 @@ std::string BridgeFinder::ParseDescription(const std::string&amp; 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&amp; ip, const int port, const std::string&amp; usernam @@ -146,15 +146,18 @@ Bridge::Bridge(const std::string&amp; ip, const int port, const std::string&amp; 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&lt;const IHttpHandler&gt; handler) @@ -304,13 +307,14 @@ void Bridge::setHttpHandler(std::shared_ptr&lt;const IHttpHandler&gt; 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&amp; commands, std::chrono::steady_cl @@ -67,41 +67,46 @@ LightFactory::LightFactory(const HueCommandAPI&amp; 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&amp; commands, std::shared_ptr&lt;const Bright @@ -122,6 +125,5 @@ Light::Light(int id, const HueCommandAPI&amp; commands, std::shared_ptr&lt;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"}}}};