Commit 65ddf85814bc7269767b6371c11d5aeb84937009
Committed by
Moritz Wirger
1 parent
6a9b8f2c
Implement searching for new sensors and lights.
Showing
11 changed files
with
346 additions
and
46 deletions
include/hueplusplus/Bridge.h
| ... | ... | @@ -129,10 +129,10 @@ class Bridge |
| 129 | 129 | friend class BridgeFinder; |
| 130 | 130 | |
| 131 | 131 | public: |
| 132 | - using LightList = ResourceList<Light, int>; | |
| 132 | + using LightList = SearchableResourceList<Light>; | |
| 133 | 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 | 137 | public: |
| 138 | 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 | 29 | |
| 30 | 30 | #include "APICache.h" |
| 31 | 31 | #include "HueException.h" |
| 32 | +#include "NewDeviceList.h" | |
| 32 | 33 | #include "Utils.h" |
| 33 | 34 | |
| 34 | 35 | namespace hueplusplus |
| 35 | 36 | { |
| 36 | 37 | //! \brief Handles a list of a certain API resource |
| 37 | 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 | 41 | //! The resources are assumed to be in an object with ids as keys. |
| 41 | 42 | //! The Resource class needs a constructor that accepts \c id, HueCommandAPI and \c refreshDuration; |
| 42 | 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 | 45 | class ResourceList |
| 45 | 46 | { |
| 46 | 47 | public: |
| 48 | + using ResourceType = Resource; | |
| 49 | + using IdType = IdT; | |
| 47 | 50 | static_assert(std::is_integral<IdType>::value || std::is_same<std::string, IdType>::value, |
| 48 | 51 | "IdType must be integral or string"); |
| 49 | 52 | |
| ... | ... | @@ -236,16 +239,59 @@ protected: |
| 236 | 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 | 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 | 288 | //! \tparam CreateType Type that provides parameters for creation. |
| 243 | 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 | 293 | public: |
| 248 | - using ResourceList<Resource, IdType>::ResourceList; | |
| 294 | + using BaseResourceList::BaseResourceList; | |
| 249 | 295 | |
| 250 | 296 | //! \brief Create a new resource |
| 251 | 297 | //! \param params Parameters for the new resource |
| ... | ... | @@ -255,7 +301,7 @@ public: |
| 255 | 301 | //! \throws HueAPIResponseException when response contains an error |
| 256 | 302 | //! \throws nlohmann::json::parse_error when response could not be parsed |
| 257 | 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 | 306 | std::string requestPath = this->path; |
| 261 | 307 | // Remove slash |
| ... | ... | @@ -273,8 +319,9 @@ public: |
| 273 | 319 | this->stateCache.refresh(); |
| 274 | 320 | return this->maybeStoi(idStr); |
| 275 | 321 | } |
| 276 | - return IdType {}; | |
| 322 | + return BaseResourceList::IdType {}; | |
| 277 | 323 | } |
| 324 | + | |
| 278 | 325 | protected: |
| 279 | 326 | //! \brief Protected defaulted move constructor |
| 280 | 327 | CreateableResourceList(CreateableResourceList&&) = default; |
| ... | ... | @@ -287,10 +334,12 @@ protected: |
| 287 | 334 | //! \tparam CreateType Type that provides parameters for creation. |
| 288 | 335 | //! Must have a const getRequest() function returning the JSON for the POST request. |
| 289 | 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 | 341 | public: |
| 293 | - using CreateableResourceList<Resource, int, CreateType>::CreateableResourceList; | |
| 342 | + using Base::Base; | |
| 294 | 343 | //! \brief Get group, specially handles group 0 |
| 295 | 344 | //! \see ResourceList::get |
| 296 | 345 | Resource& get(const int& id) |
| ... | ... | @@ -311,7 +360,7 @@ public: |
| 311 | 360 | } |
| 312 | 361 | //! \brief Get group, specially handles group 0 |
| 313 | 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 | 365 | protected: |
| 317 | 366 | //! \brief Protected defaulted move constructor | ... | ... |
include/hueplusplus/SensorList.h
| ... | ... | @@ -30,7 +30,7 @@ namespace hueplusplus |
| 30 | 30 | //! \brief Handles a list of Sensor%s with type specific getters |
| 31 | 31 | //! |
| 32 | 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 | 35 | public: |
| 36 | 36 | using CreateableResourceList::CreateableResourceList; | ... | ... |
src/Bridge.cpp
| ... | ... | @@ -292,7 +292,7 @@ void Bridge::setHttpHandler(std::shared_ptr<const IHttpHandler> handler) |
| 292 | 292 | { |
| 293 | 293 | http_handler = handler; |
| 294 | 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 | 296 | [factory = LightFactory(stateCache->getCommandAPI(), refreshDuration)]( |
| 297 | 297 | int id, const nlohmann::json& state) mutable { return factory.createLight(state, id); }); |
| 298 | 298 | groupList = GroupList(stateCache, "groups", refreshDuration); | ... | ... |
src/CMakeLists.txt
| ... | ... | @@ -22,7 +22,7 @@ set(hueplusplus_SOURCES |
| 22 | 22 | StateTransaction.cpp |
| 23 | 23 | TimePattern.cpp |
| 24 | 24 | UPnP.cpp |
| 25 | - Utils.cpp "ZLLSensors.cpp" "CLIPSensors.cpp") | |
| 25 | + Utils.cpp "ZLLSensors.cpp" "CLIPSensors.cpp" "NewDeviceList.cpp") | |
| 26 | 26 | |
| 27 | 27 | # on windows we want to compile the WinHttpHandler |
| 28 | 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 | 64 | \ No newline at end of file | ... | ... |
src/TimePattern.cpp
| ... | ... | @@ -33,16 +33,23 @@ namespace |
| 33 | 33 | { |
| 34 | 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 | 54 | } // namespace |
| 48 | 55 | ... | ... |
test/CMakeLists.txt
| ... | ... | @@ -54,7 +54,7 @@ set(TEST_SOURCES |
| 54 | 54 | test_SimpleColorHueStrategy.cpp |
| 55 | 55 | test_SimpleColorTemperatureStrategy.cpp |
| 56 | 56 | test_StateTransaction.cpp |
| 57 | - test_TimePattern.cpp) | |
| 57 | + test_TimePattern.cpp "test_NewDeviceList.cpp") | |
| 58 | 58 | |
| 59 | 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 | 76 | \ No newline at end of file | ... | ... |
test/test_ResourceList.cpp
| ... | ... | @@ -32,9 +32,9 @@ using namespace testing; |
| 32 | 32 | class TestResource |
| 33 | 33 | { |
| 34 | 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 | 39 | public: |
| 40 | 40 | int id; |
| ... | ... | @@ -42,15 +42,15 @@ public: |
| 42 | 42 | class TestResourceFactory |
| 43 | 43 | { |
| 44 | 44 | public: |
| 45 | - void refresh(bool force = false) {} | |
| 45 | + void refresh(bool force = false) { } | |
| 46 | 46 | }; |
| 47 | 47 | class TestStringResource |
| 48 | 48 | { |
| 49 | 49 | public: |
| 50 | 50 | TestStringResource(const std::string& id, HueCommandAPI api, std::chrono::steady_clock::duration refreshDuration) |
| 51 | 51 | : id(id) |
| 52 | - {} | |
| 53 | - void refresh(bool force = false) {} | |
| 52 | + { } | |
| 53 | + void refresh(bool force = false) { } | |
| 54 | 54 | |
| 55 | 55 | public: |
| 56 | 56 | std::string id; |
| ... | ... | @@ -149,12 +149,12 @@ TEST(ResourceList, get) |
| 149 | 149 | TestStringResource& r2 = list.get(id); |
| 150 | 150 | EXPECT_EQ(id, r2.id); |
| 151 | 151 | } |
| 152 | - | |
| 152 | + | |
| 153 | 153 | { |
| 154 | 154 | ResourceList<TestResourceFactory, int> list(commands, path, std::chrono::steady_clock::duration::max()); |
| 155 | 155 | |
| 156 | 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 | 158 | EXPECT_CALL(*handler, |
| 159 | 159 | GETJson("/api/" + getBridgeUsername() + path, nlohmann::json::object(), getBridgeIp(), getBridgePort())) |
| 160 | 160 | .WillOnce(Return(response)); |
| ... | ... | @@ -162,10 +162,10 @@ TEST(ResourceList, get) |
| 162 | 162 | } |
| 163 | 163 | { |
| 164 | 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 | 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 | 169 | EXPECT_CALL(*handler, |
| 170 | 170 | GETJson("/api/" + getBridgeUsername() + path, nlohmann::json::object(), getBridgeIp(), getBridgePort())) |
| 171 | 171 | .WillOnce(Return(response)); |
| ... | ... | @@ -238,22 +238,59 @@ TEST(ResourceList, remove) |
| 238 | 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 | 278 | TEST(CreateableResourceList, create) |
| 242 | 279 | { |
| 243 | 280 | auto handler = std::make_shared<MockHttpHandler>(); |
| 244 | 281 | HueCommandAPI commands(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler); |
| 245 | 282 | |
| 246 | 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 | 289 | .WillOnce(Return(response)) |
| 253 | 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 | 294 | TestCreateType params; |
| 258 | 295 | EXPECT_CALL(params, getRequest()).Times(2).WillRepeatedly(Return(request)); |
| 259 | 296 | EXPECT_EQ(2, list.create(params)); | ... | ... |