Commit 65ddf85814bc7269767b6371c11d5aeb84937009

Authored by Jojo-1000
Committed by Moritz Wirger
1 parent 6a9b8f2c

Implement searching for new sensors and lights.

include/hueplusplus/Bridge.h
@@ -129,10 +129,10 @@ class Bridge @@ -129,10 +129,10 @@ class Bridge
129 friend class BridgeFinder; 129 friend class BridgeFinder;
130 130
131 public: 131 public:
132 - using LightList = ResourceList<Light, int>; 132 + using LightList = SearchableResourceList<Light>;
133 using GroupList = GroupResourceList<Group, CreateGroup>; 133 using GroupList = GroupResourceList<Group, CreateGroup>;
134 - using ScheduleList = CreateableResourceList<Schedule, int, CreateSchedule>;  
135 - using SceneList = CreateableResourceList<Scene, std::string, CreateScene>; 134 + using ScheduleList = CreateableResourceList<ResourceList<Schedule, int>, CreateSchedule>;
  135 + using SceneList = CreateableResourceList<ResourceList<Scene, std::string>, CreateScene>;
136 136
137 public: 137 public:
138 //! \brief Constructor of Bridge class 138 //! \brief Constructor of Bridge class
include/hueplusplus/NewDeviceList.h 0 → 100644
  1 +/**
  2 + \file NewDeviceList.h
  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 +#ifndef INCLUDE_HUEPLUSPLUS_NEW_DEVICE_LIST_H
  23 +#define INCLUDE_HUEPLUSPLUS_NEW_DEVICE_LIST_H
  24 +
  25 +#include <map>
  26 +#include <string>
  27 +
  28 +#include "TimePattern.h"
  29 +
  30 +#include "json/json.hpp"
  31 +
  32 +namespace hueplusplus
  33 +{
  34 +//! \brief List of new devices found during the last scan
  35 +class NewDeviceList
  36 +{
  37 +public:
  38 + //! \brief Construct from data
  39 + NewDeviceList(const std::string& lastScan, const std::map<int, std::string>& devices);
  40 +
  41 + //! \brief Get a map of id and name of new devices
  42 + const std::map<int, std::string>& getNewDevices() const;
  43 +
  44 + //! \brief Get whether a last scan time is available
  45 + //!
  46 + //! This can be false if there was no scan since the last restart
  47 + //! or if the scan is still running.
  48 + bool hasLastScanTime() const;
  49 + //! \brief Get whether scan is currently active
  50 + //!
  51 + //! When scan is active, no last scan time is available
  52 + bool isScanActive();
  53 + //! \brief Get time when last scan was completed
  54 + //! \throws HueException when no time is available or timestamp is invalid
  55 + //! \note Must only be called when \ref hasLastScanTime() is true.
  56 + time::AbsoluteTime getLastScanTime() const;
  57 +
  58 + //! \brief Parse from json response
  59 + //! \throws std::invalid_argument when json is invalid.
  60 + //! \throws nlohmann::json::exception when json is invalid.
  61 + static NewDeviceList parse(const nlohmann::json& json);
  62 +
  63 +private:
  64 + std::string lastScan;
  65 + std::map<int, std::string> devices;
  66 +};
  67 +} // namespace hueplusplus
  68 +
  69 +#endif
include/hueplusplus/ResourceList.h
@@ -29,21 +29,24 @@ @@ -29,21 +29,24 @@
29 29
30 #include "APICache.h" 30 #include "APICache.h"
31 #include "HueException.h" 31 #include "HueException.h"
  32 +#include "NewDeviceList.h"
32 #include "Utils.h" 33 #include "Utils.h"
33 34
34 namespace hueplusplus 35 namespace hueplusplus
35 { 36 {
36 //! \brief Handles a list of a certain API resource 37 //! \brief Handles a list of a certain API resource
37 //! \tparam Resource Resource type that is in the list 38 //! \tparam Resource Resource type that is in the list
38 -//! \tparam IdType Type of the resource id. int or std::string 39 +//! \tparam IdT Type of the resource id. int or std::string
39 //! 40 //!
40 //! The resources are assumed to be in an object with ids as keys. 41 //! The resources are assumed to be in an object with ids as keys.
41 //! The Resource class needs a constructor that accepts \c id, HueCommandAPI and \c refreshDuration; 42 //! The Resource class needs a constructor that accepts \c id, HueCommandAPI and \c refreshDuration;
42 //! otherwise a factory function needs to be provided that takes \c id and the JSON state. 43 //! otherwise a factory function needs to be provided that takes \c id and the JSON state.
43 -template <typename Resource, typename IdType> 44 +template <typename Resource, typename IdT>
44 class ResourceList 45 class ResourceList
45 { 46 {
46 public: 47 public:
  48 + using ResourceType = Resource;
  49 + using IdType = IdT;
47 static_assert(std::is_integral<IdType>::value || std::is_same<std::string, IdType>::value, 50 static_assert(std::is_integral<IdType>::value || std::is_same<std::string, IdType>::value,
48 "IdType must be integral or string"); 51 "IdType must be integral or string");
49 52
@@ -236,16 +239,59 @@ protected: @@ -236,16 +239,59 @@ protected:
236 std::map<IdType, Resource> resources; 239 std::map<IdType, Resource> resources;
237 }; 240 };
238 241
239 -//! \brief Handles a ResourceList where Resources can be added by the user 242 +//! \brief Handles a ResourceList of physical devices which can be searched for
240 //! \tparam Resource Resource type that is in the list 243 //! \tparam Resource Resource type that is in the list
241 -//! \tparam IdType Type of the resource id. int or std::string 244 +template <typename Resource>
  245 +class SearchableResourceList : public ResourceList<Resource, int>
  246 +{
  247 +public:
  248 + using ResourceList<Resource, int>::ResourceList;
  249 +
  250 + //! \brief Start search for new devices
  251 + //! \param deviceIds Serial numbers of the devices to search for (max. 10)
  252 + //!
  253 + //! Takes more than 40s. If many devices were found a second search command might be necessary.
  254 + void search(const std::vector<std::string>& deviceIds = {})
  255 + {
  256 + std::string requestPath = this->path;
  257 + // Remove trailing slash
  258 + requestPath.pop_back();
  259 + if (deviceIds.empty())
  260 + {
  261 + this->stateCache.getCommandAPI().POSTRequest(
  262 + requestPath, nlohmann::json::object(), FileInfo {__FILE__, __LINE__, __func__});
  263 + }
  264 + else
  265 + {
  266 + this->stateCache.getCommandAPI().POSTRequest(
  267 + requestPath, nlohmann::json {{"deviceid", deviceIds}}, FileInfo {__FILE__, __LINE__, __func__});
  268 + }
  269 + }
  270 +
  271 + //! \brief Get devices found in last search
  272 + NewDeviceList getNewDevices() const
  273 + {
  274 + nlohmann::json response = this->stateCache.getCommandAPI().GETRequest(
  275 + this->path + "new", nlohmann::json::object(), FileInfo {__FILE__, __LINE__, __func__});
  276 + return NewDeviceList::parse(response);
  277 + }
  278 +
  279 +protected:
  280 + //! \brief Protected defaulted move constructor
  281 + SearchableResourceList(SearchableResourceList&&) = default;
  282 + //! \brief Protected defaulted move assignment
  283 + SearchableResourceList& operator=(SearchableResourceList&&) = default;
  284 +};
  285 +
  286 +//! \brief Handles a ResourceList where Resources can be added by the user
  287 +//! \tparam BaseResourceList Base resource list type (ResourceList or SearchableResourceList).
242 //! \tparam CreateType Type that provides parameters for creation. 288 //! \tparam CreateType Type that provides parameters for creation.
243 //! Must have a const getRequest() function returning the JSON for the POST request. 289 //! Must have a const getRequest() function returning the JSON for the POST request.
244 -template <typename Resource, typename IdType, typename CreateType>  
245 -class CreateableResourceList : public ResourceList<Resource, IdType> 290 +template <typename BaseResourceList, typename CreateType>
  291 +class CreateableResourceList : public BaseResourceList
246 { 292 {
247 public: 293 public:
248 - using ResourceList<Resource, IdType>::ResourceList; 294 + using BaseResourceList::BaseResourceList;
249 295
250 //! \brief Create a new resource 296 //! \brief Create a new resource
251 //! \param params Parameters for the new resource 297 //! \param params Parameters for the new resource
@@ -255,7 +301,7 @@ public: @@ -255,7 +301,7 @@ public:
255 //! \throws HueAPIResponseException when response contains an error 301 //! \throws HueAPIResponseException when response contains an error
256 //! \throws nlohmann::json::parse_error when response could not be parsed 302 //! \throws nlohmann::json::parse_error when response could not be parsed
257 //! \throws std::invalid_argument when IdType is int and std::stoi fails 303 //! \throws std::invalid_argument when IdType is int and std::stoi fails
258 - IdType create(const CreateType& params) 304 + typename BaseResourceList::IdType create(const CreateType& params)
259 { 305 {
260 std::string requestPath = this->path; 306 std::string requestPath = this->path;
261 // Remove slash 307 // Remove slash
@@ -273,8 +319,9 @@ public: @@ -273,8 +319,9 @@ public:
273 this->stateCache.refresh(); 319 this->stateCache.refresh();
274 return this->maybeStoi(idStr); 320 return this->maybeStoi(idStr);
275 } 321 }
276 - return IdType {}; 322 + return BaseResourceList::IdType {};
277 } 323 }
  324 +
278 protected: 325 protected:
279 //! \brief Protected defaulted move constructor 326 //! \brief Protected defaulted move constructor
280 CreateableResourceList(CreateableResourceList&&) = default; 327 CreateableResourceList(CreateableResourceList&&) = default;
@@ -287,10 +334,12 @@ protected: @@ -287,10 +334,12 @@ protected:
287 //! \tparam CreateType Type that provides parameters for creation. 334 //! \tparam CreateType Type that provides parameters for creation.
288 //! Must have a const getRequest() function returning the JSON for the POST request. 335 //! Must have a const getRequest() function returning the JSON for the POST request.
289 template <typename Resource, typename CreateType> 336 template <typename Resource, typename CreateType>
290 -class GroupResourceList : public CreateableResourceList<Resource, int, CreateType> 337 +class GroupResourceList : public CreateableResourceList<ResourceList<Resource, int>, CreateType>
291 { 338 {
  339 + using Base = CreateableResourceList<ResourceList<Resource, int>, CreateType>;
  340 +
292 public: 341 public:
293 - using CreateableResourceList<Resource, int, CreateType>::CreateableResourceList; 342 + using Base::Base;
294 //! \brief Get group, specially handles group 0 343 //! \brief Get group, specially handles group 0
295 //! \see ResourceList::get 344 //! \see ResourceList::get
296 Resource& get(const int& id) 345 Resource& get(const int& id)
@@ -311,7 +360,7 @@ public: @@ -311,7 +360,7 @@ public:
311 } 360 }
312 //! \brief Get group, specially handles group 0 361 //! \brief Get group, specially handles group 0
313 //! \see ResourceList::exists 362 //! \see ResourceList::exists
314 - bool exists(int id) const { return id == 0 || CreateableResourceList<Resource, int, CreateType>::exists(id); } 363 + bool exists(int id) const { return id == 0 || Base::exists(id); }
315 364
316 protected: 365 protected:
317 //! \brief Protected defaulted move constructor 366 //! \brief Protected defaulted move constructor
include/hueplusplus/SensorList.h
@@ -30,7 +30,7 @@ namespace hueplusplus @@ -30,7 +30,7 @@ namespace hueplusplus
30 //! \brief Handles a list of Sensor%s with type specific getters 30 //! \brief Handles a list of Sensor%s with type specific getters
31 //! 31 //!
32 //! Allows to directly get the requested sensor type or all sensors of a given type. 32 //! Allows to directly get the requested sensor type or all sensors of a given type.
33 -class SensorList : public CreateableResourceList<Sensor, int, CreateSensor> 33 +class SensorList : public CreateableResourceList<SearchableResourceList<Sensor>, CreateSensor>
34 { 34 {
35 public: 35 public:
36 using CreateableResourceList::CreateableResourceList; 36 using CreateableResourceList::CreateableResourceList;
src/Bridge.cpp
@@ -292,7 +292,7 @@ void Bridge::setHttpHandler(std::shared_ptr&lt;const IHttpHandler&gt; handler) @@ -292,7 +292,7 @@ void Bridge::setHttpHandler(std::shared_ptr&lt;const IHttpHandler&gt; handler)
292 { 292 {
293 http_handler = handler; 293 http_handler = handler;
294 stateCache = std::make_shared<APICache>("", HueCommandAPI(ip, port, username, handler), refreshDuration); 294 stateCache = std::make_shared<APICache>("", HueCommandAPI(ip, port, username, handler), refreshDuration);
295 - lightList = ResourceList<Light, int>(stateCache, "lights", refreshDuration, 295 + lightList = LightList(stateCache, "lights", refreshDuration,
296 [factory = LightFactory(stateCache->getCommandAPI(), refreshDuration)]( 296 [factory = LightFactory(stateCache->getCommandAPI(), refreshDuration)](
297 int id, const nlohmann::json& state) mutable { return factory.createLight(state, id); }); 297 int id, const nlohmann::json& state) mutable { return factory.createLight(state, id); });
298 groupList = GroupList(stateCache, "groups", refreshDuration); 298 groupList = GroupList(stateCache, "groups", refreshDuration);
src/CMakeLists.txt
@@ -22,7 +22,7 @@ set(hueplusplus_SOURCES @@ -22,7 +22,7 @@ set(hueplusplus_SOURCES
22 StateTransaction.cpp 22 StateTransaction.cpp
23 TimePattern.cpp 23 TimePattern.cpp
24 UPnP.cpp 24 UPnP.cpp
25 - Utils.cpp "ZLLSensors.cpp" "CLIPSensors.cpp") 25 + Utils.cpp "ZLLSensors.cpp" "CLIPSensors.cpp" "NewDeviceList.cpp")
26 26
27 # on windows we want to compile the WinHttpHandler 27 # on windows we want to compile the WinHttpHandler
28 if(WIN32) 28 if(WIN32)
src/NewDeviceList.cpp 0 → 100644
  1 +/**
  2 + \file NewDeviceList.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/NewDeviceList.h>
  23 +
  24 +namespace hueplusplus
  25 +{
  26 +NewDeviceList::NewDeviceList(const std::string& lastScan, const std::map<int, std::string>& devices)
  27 + : lastScan(lastScan), devices(devices)
  28 +{ }
  29 +const std::map<int, std::string>& NewDeviceList::getNewDevices() const
  30 +{
  31 + return devices;
  32 +}
  33 +bool NewDeviceList::hasLastScanTime() const
  34 +{
  35 + return !lastScan.empty() && lastScan != "none" && lastScan != "active";
  36 +}
  37 +bool NewDeviceList::isScanActive()
  38 +{
  39 + return lastScan == "active";
  40 +}
  41 +time::AbsoluteTime NewDeviceList::getLastScanTime() const
  42 +{
  43 + return time::AbsoluteTime::parseUTC(lastScan); // UTC? not clear in docs
  44 +}
  45 +NewDeviceList NewDeviceList::parse(const nlohmann::json& json)
  46 +{
  47 + std::map<int, std::string> devices;
  48 + std::string lastScan;
  49 + for (auto it = json.begin(); it != json.end(); ++it)
  50 + {
  51 + if (it.key() == "lastscan")
  52 + {
  53 + lastScan = it.value().get<std::string>();
  54 + }
  55 + else
  56 + {
  57 + int id = std::stoi(it.key());
  58 + devices.emplace(id, it.value().at("name").get<std::string>());
  59 + }
  60 + }
  61 + return NewDeviceList(lastScan, devices);
  62 +}
  63 +} // namespace hueplusplus
0 \ No newline at end of file 64 \ No newline at end of file
src/TimePattern.cpp
@@ -33,16 +33,23 @@ namespace @@ -33,16 +33,23 @@ namespace
33 { 33 {
34 std::tm timestampToTm(const std::string& timestamp) 34 std::tm timestampToTm(const std::string& timestamp)
35 { 35 {
36 - std::tm tm {};  
37 - tm.tm_year = std::stoi(timestamp.substr(0, 4)) - 1900;  
38 - tm.tm_mon = std::stoi(timestamp.substr(5, 2)) - 1;  
39 - tm.tm_mday = std::stoi(timestamp.substr(8, 2));  
40 - tm.tm_hour = std::stoi(timestamp.substr(11, 2));  
41 - tm.tm_min = std::stoi(timestamp.substr(14, 2));  
42 - tm.tm_sec = std::stoi(timestamp.substr(17, 2));  
43 - // Auto detect daylight savings time  
44 - tm.tm_isdst = -1;  
45 - return tm; 36 + try
  37 + {
  38 + std::tm tm {};
  39 + tm.tm_year = std::stoi(timestamp.substr(0, 4)) - 1900;
  40 + tm.tm_mon = std::stoi(timestamp.substr(5, 2)) - 1;
  41 + tm.tm_mday = std::stoi(timestamp.substr(8, 2));
  42 + tm.tm_hour = std::stoi(timestamp.substr(11, 2));
  43 + tm.tm_min = std::stoi(timestamp.substr(14, 2));
  44 + tm.tm_sec = std::stoi(timestamp.substr(17, 2));
  45 + // Auto detect daylight savings time
  46 + tm.tm_isdst = -1;
  47 + return tm;
  48 + }
  49 + catch (const std::invalid_argument& e)
  50 + {
  51 + throw HueException(CURRENT_FILE_INFO, std::string("Invalid argument: ") + e.what());
  52 + }
46 } 53 }
47 } // namespace 54 } // namespace
48 55
test/CMakeLists.txt
@@ -54,7 +54,7 @@ set(TEST_SOURCES @@ -54,7 +54,7 @@ set(TEST_SOURCES
54 test_SimpleColorHueStrategy.cpp 54 test_SimpleColorHueStrategy.cpp
55 test_SimpleColorTemperatureStrategy.cpp 55 test_SimpleColorTemperatureStrategy.cpp
56 test_StateTransaction.cpp 56 test_StateTransaction.cpp
57 - test_TimePattern.cpp) 57 + test_TimePattern.cpp "test_NewDeviceList.cpp")
58 58
59 set(HuePlusPlus_INCLUDE_DIR "${PROJECT_SOURCE_DIR}/include") 59 set(HuePlusPlus_INCLUDE_DIR "${PROJECT_SOURCE_DIR}/include")
60 60
test/test_NewDeviceList.cpp 0 → 100644
  1 +/**
  2 + \file test_NewDeviceList.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/HueException.h>
  23 +#include <hueplusplus/NewDeviceList.h>
  24 +
  25 +#include <gtest/gtest.h>
  26 +
  27 +using namespace hueplusplus;
  28 +using namespace testing;
  29 +
  30 +TEST(NewDeviceList, Constructor)
  31 +{
  32 + {
  33 + NewDeviceList list("none", {});
  34 + EXPECT_TRUE(list.getNewDevices().empty());
  35 + EXPECT_FALSE(list.hasLastScanTime());
  36 + EXPECT_FALSE(list.isScanActive());
  37 + EXPECT_THROW(list.getLastScanTime(), HueException);
  38 + }
  39 + {
  40 + const std::map<int, std::string> devices = {{1, "a"}, {2, "b"}, {3, "c"}};
  41 + NewDeviceList list("active", devices);
  42 + EXPECT_FALSE(list.hasLastScanTime());
  43 + EXPECT_TRUE(list.isScanActive());
  44 + EXPECT_EQ(devices, list.getNewDevices());
  45 + EXPECT_THROW(list.getLastScanTime(), HueException);
  46 + }
  47 + {
  48 + const std::string timestamp = "2020-03-01T00:10:00";
  49 + NewDeviceList list(timestamp, {});
  50 + EXPECT_TRUE(list.hasLastScanTime());
  51 + EXPECT_FALSE(list.isScanActive());
  52 + EXPECT_EQ(time::AbsoluteTime::parseUTC(timestamp).getBaseTime(), list.getLastScanTime().getBaseTime());
  53 + }
  54 +}
  55 +
  56 +TEST(NewDeviceList, parse)
  57 +{
  58 + {
  59 + NewDeviceList list = NewDeviceList::parse({});
  60 + EXPECT_FALSE(list.hasLastScanTime());
  61 + EXPECT_FALSE(list.isScanActive());
  62 + EXPECT_TRUE(list.getNewDevices().empty());
  63 + EXPECT_THROW(list.getLastScanTime(), HueException);
  64 + }
  65 + {
  66 + const std::map<int, std::string> devices = {{1, "a"}, {2, "b"}, {3, "c"}};
  67 + const std::string timestamp = "2020-03-01T00:10:00";
  68 + NewDeviceList list = NewDeviceList::parse(
  69 + {{"1", {{"name", "a"}}}, {"2", {{"name", "b"}}}, {"3", {{"name", "c"}}}, {"lastscan", timestamp}});
  70 + EXPECT_TRUE(list.hasLastScanTime());
  71 + EXPECT_FALSE(list.isScanActive());
  72 + EXPECT_EQ(time::AbsoluteTime::parseUTC(timestamp).getBaseTime(), list.getLastScanTime().getBaseTime());
  73 + EXPECT_EQ(devices, list.getNewDevices());
  74 + }
  75 +}
0 \ No newline at end of file 76 \ No newline at end of file
test/test_ResourceList.cpp
@@ -32,9 +32,9 @@ using namespace testing; @@ -32,9 +32,9 @@ using namespace testing;
32 class TestResource 32 class TestResource
33 { 33 {
34 public: 34 public:
35 - TestResource(int id, HueCommandAPI api, std::chrono::steady_clock::duration refreshDuration) : id(id) {} 35 + TestResource(int id, HueCommandAPI api, std::chrono::steady_clock::duration refreshDuration) : id(id) { }
36 36
37 - void refresh(bool force = false) {} 37 + void refresh(bool force = false) { }
38 38
39 public: 39 public:
40 int id; 40 int id;
@@ -42,15 +42,15 @@ public: @@ -42,15 +42,15 @@ public:
42 class TestResourceFactory 42 class TestResourceFactory
43 { 43 {
44 public: 44 public:
45 - void refresh(bool force = false) {} 45 + void refresh(bool force = false) { }
46 }; 46 };
47 class TestStringResource 47 class TestStringResource
48 { 48 {
49 public: 49 public:
50 TestStringResource(const std::string& id, HueCommandAPI api, std::chrono::steady_clock::duration refreshDuration) 50 TestStringResource(const std::string& id, HueCommandAPI api, std::chrono::steady_clock::duration refreshDuration)
51 : id(id) 51 : id(id)
52 - {}  
53 - void refresh(bool force = false) {} 52 + { }
  53 + void refresh(bool force = false) { }
54 54
55 public: 55 public:
56 std::string id; 56 std::string id;
@@ -149,12 +149,12 @@ TEST(ResourceList, get) @@ -149,12 +149,12 @@ TEST(ResourceList, get)
149 TestStringResource& r2 = list.get(id); 149 TestStringResource& r2 = list.get(id);
150 EXPECT_EQ(id, r2.id); 150 EXPECT_EQ(id, r2.id);
151 } 151 }
152 - 152 +
153 { 153 {
154 ResourceList<TestResourceFactory, int> list(commands, path, std::chrono::steady_clock::duration::max()); 154 ResourceList<TestResourceFactory, int> list(commands, path, std::chrono::steady_clock::duration::max());
155 155
156 const int id = 2; 156 const int id = 2;
157 - const nlohmann::json response = { {std::to_string(id), {{"resource", "state"}}} }; 157 + const nlohmann::json response = {{std::to_string(id), {{"resource", "state"}}}};
158 EXPECT_CALL(*handler, 158 EXPECT_CALL(*handler,
159 GETJson("/api/" + getBridgeUsername() + path, nlohmann::json::object(), getBridgeIp(), getBridgePort())) 159 GETJson("/api/" + getBridgeUsername() + path, nlohmann::json::object(), getBridgeIp(), getBridgePort()))
160 .WillOnce(Return(response)); 160 .WillOnce(Return(response));
@@ -162,10 +162,10 @@ TEST(ResourceList, get) @@ -162,10 +162,10 @@ TEST(ResourceList, get)
162 } 162 }
163 { 163 {
164 ResourceList<TestResourceFactory, int> list(commands, path, std::chrono::steady_clock::duration::max(), 164 ResourceList<TestResourceFactory, int> list(commands, path, std::chrono::steady_clock::duration::max(),
165 - [](int, const nlohmann::json&) {return TestResourceFactory(); }); 165 + [](int, const nlohmann::json&) { return TestResourceFactory(); });
166 166
167 const int id = 2; 167 const int id = 2;
168 - const nlohmann::json response = { {std::to_string(id), {{"resource", "state"}}} }; 168 + const nlohmann::json response = {{std::to_string(id), {{"resource", "state"}}}};
169 EXPECT_CALL(*handler, 169 EXPECT_CALL(*handler,
170 GETJson("/api/" + getBridgeUsername() + path, nlohmann::json::object(), getBridgeIp(), getBridgePort())) 170 GETJson("/api/" + getBridgeUsername() + path, nlohmann::json::object(), getBridgeIp(), getBridgePort()))
171 .WillOnce(Return(response)); 171 .WillOnce(Return(response));
@@ -238,22 +238,59 @@ TEST(ResourceList, remove) @@ -238,22 +238,59 @@ TEST(ResourceList, remove)
238 EXPECT_FALSE(list.remove(id)); 238 EXPECT_FALSE(list.remove(id));
239 } 239 }
240 240
  241 +TEST(SearchableResourceList, search)
  242 +{
  243 + auto handler = std::make_shared<MockHttpHandler>();
  244 + HueCommandAPI commands(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler);
  245 +
  246 + const std::string path = "/resources";
  247 + SearchableResourceList<TestResource> list(commands, path, std::chrono::steady_clock::duration::max());
  248 + const nlohmann::json response = {{{"success", {{path, "Searching for new devices"}}}}};
  249 + EXPECT_CALL(*handler,
  250 + POSTJson("/api/" + getBridgeUsername() + path, nlohmann::json::object(), getBridgeIp(), getBridgePort()))
  251 + .WillOnce(Return(response));
  252 + list.search();
  253 +
  254 + EXPECT_CALL(*handler,
  255 + POSTJson("/api/" + getBridgeUsername() + path, nlohmann::json({{"deviceid", {"abcd", "def", "fgh"}}}),
  256 + getBridgeIp(), getBridgePort()))
  257 + .WillOnce(Return(response));
  258 + list.search({"abcd", "def", "fgh"});
  259 +}
  260 +
  261 +TEST(SearchableResourceList, getNewDevices)
  262 +{
  263 + auto handler = std::make_shared<MockHttpHandler>();
  264 + HueCommandAPI commands(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler);
  265 +
  266 + const std::string path = "/resources";
  267 + SearchableResourceList<TestResource> list(commands, path, std::chrono::steady_clock::duration::max());
  268 + const nlohmann::json response = {{"lastscan", "active"}, {"1", {{"name", "A"}}}};
  269 + EXPECT_CALL(*handler,
  270 + GETJson(
  271 + "/api/" + getBridgeUsername() + path + "/new", nlohmann::json::object(), getBridgeIp(), getBridgePort()))
  272 + .WillOnce(Return(response));
  273 + NewDeviceList newDevices = list.getNewDevices();
  274 + EXPECT_TRUE(newDevices.isScanActive());
  275 + EXPECT_THAT(newDevices.getNewDevices(), ElementsAre(std::make_pair(1, "A")));
  276 +}
  277 +
241 TEST(CreateableResourceList, create) 278 TEST(CreateableResourceList, create)
242 { 279 {
243 auto handler = std::make_shared<MockHttpHandler>(); 280 auto handler = std::make_shared<MockHttpHandler>();
244 HueCommandAPI commands(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler); 281 HueCommandAPI commands(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler);
245 282
246 const std::string path = "/resources"; 283 const std::string path = "/resources";
247 - const nlohmann::json response = { {{"success", {{"id", path + "/2"}}}} };  
248 - const nlohmann::json request = { {"name", "bla"} };  
249 - CreateableResourceList<TestResource, int, TestCreateType> list(commands, path, std::chrono::steady_clock::duration::max());  
250 - EXPECT_CALL(*handler, POSTJson(  
251 - "/api/" + getBridgeUsername() + path, request, getBridgeIp(), getBridgePort())) 284 + const nlohmann::json response = {{{"success", {{"id", path + "/2"}}}}};
  285 + const nlohmann::json request = {{"name", "bla"}};
  286 + CreateableResourceList<ResourceList<TestResource, int>, TestCreateType> list(
  287 + commands, path, std::chrono::steady_clock::duration::max());
  288 + EXPECT_CALL(*handler, POSTJson("/api/" + getBridgeUsername() + path, request, getBridgeIp(), getBridgePort()))
252 .WillOnce(Return(response)) 289 .WillOnce(Return(response))
253 .WillOnce(Return(nlohmann::json())); 290 .WillOnce(Return(nlohmann::json()));
254 - EXPECT_CALL(*handler, GETJson(  
255 - "/api/" + getBridgeUsername() + path, _, getBridgeIp(), getBridgePort()))  
256 - .Times(AnyNumber()).WillRepeatedly(Return(nlohmann::json::object())); 291 + EXPECT_CALL(*handler, GETJson("/api/" + getBridgeUsername() + path, _, getBridgeIp(), getBridgePort()))
  292 + .Times(AnyNumber())
  293 + .WillRepeatedly(Return(nlohmann::json::object()));
257 TestCreateType params; 294 TestCreateType params;
258 EXPECT_CALL(params, getRequest()).Times(2).WillRepeatedly(Return(request)); 295 EXPECT_CALL(params, getRequest()).Times(2).WillRepeatedly(Return(request));
259 EXPECT_EQ(2, list.create(params)); 296 EXPECT_EQ(2, list.create(params));