From f0166f40dba9980d1151210187cd8255a6a1a732 Mon Sep 17 00:00:00 2001 From: Jojo-1000 <33495614+Jojo-1000@users.noreply.github.com> Date: Sat, 27 Jun 2020 21:05:05 +0200 Subject: [PATCH] Rename classes to simplify names. --- README.md | 20 ++++++++++---------- doc/markdown/Mainpage.md | 26 +++++++++++++------------- include/hueplusplus/APIConfig.h | 75 --------------------------------------------------------------------------- include/hueplusplus/Bridge.h | 263 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ include/hueplusplus/BrightnessStrategy.h | 8 ++++---- include/hueplusplus/ColorHueStrategy.h | 24 ++++++++++++------------ include/hueplusplus/ColorTemperatureStrategy.h | 10 +++++----- include/hueplusplus/ExtendedColorHueStrategy.h | 10 +++++----- include/hueplusplus/ExtendedColorTemperatureStrategy.h | 6 +++--- include/hueplusplus/Hue.h | 262 ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- include/hueplusplus/HueDeviceTypes.h | 14 +++++++------- include/hueplusplus/HueLight.h | 666 ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ include/hueplusplus/HueSensor.h | 97 ------------------------------------------------------------------------------------------------- include/hueplusplus/LibConfig.h | 75 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ include/hueplusplus/Light.h | 625 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ include/hueplusplus/Sensor.h | 97 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ include/hueplusplus/SimpleBrightnessStrategy.h | 8 ++++---- include/hueplusplus/SimpleColorHueStrategy.h | 26 +++++++++++++------------- include/hueplusplus/SimpleColorTemperatureStrategy.h | 12 ++++++------ include/hueplusplus/StateTransaction.h | 2 +- src/Bridge.cpp | 299 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/CMakeLists.txt | 6 +++--- src/ExtendedColorHueStrategy.cpp | 8 ++++---- src/ExtendedColorTemperatureStrategy.cpp | 6 +++--- src/Hue.cpp | 299 ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- src/HueCommandAPI.cpp | 2 +- src/HueDeviceTypes.cpp | 24 ++++++++++++------------ src/HueLight.cpp | 127 ------------------------------------------------------------------------------------------------------------------------------- src/HueSensor.cpp | 77 ----------------------------------------------------------------------------- src/Light.cpp | 127 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/Sensor.cpp | 77 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/SimpleBrightnessStrategy.cpp | 6 +++--- src/SimpleColorHueStrategy.cpp | 28 ++++++++++++++-------------- src/SimpleColorTemperatureStrategy.cpp | 10 +++++----- src/UPnP.cpp | 2 +- test/CMakeLists.txt | 6 +++--- test/mocks/mock_HueLight.h | 130 ---------------------------------------------------------------------------------------------------------------------------------- test/mocks/mock_Light.h | 130 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ test/test_Bridge.cpp | 549 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ test/test_ExtendedColorHueStrategy.cpp | 6 +++--- test/test_ExtendedColorTemperatureStrategy.cpp | 4 ++-- test/test_Hue.cpp | 549 --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- test/test_HueCommandAPI.cpp | 2 +- test/test_HueLight.cpp | 834 ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ test/test_HueLightFactory.cpp | 243 --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- test/test_Light.cpp | 834 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ test/test_LightFactory.cpp | 243 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ test/test_Main.cpp | 2 +- test/test_SimpleBrightnessStrategy.cpp | 8 ++++---- test/test_SimpleColorHueStrategy.cpp | 22 +++++++++++----------- test/test_SimpleColorTemperatureStrategy.cpp | 10 +++++----- test/test_UPnP.cpp | 2 +- 52 files changed, 3479 insertions(+), 3519 deletions(-) delete mode 100644 include/hueplusplus/APIConfig.h create mode 100644 include/hueplusplus/Bridge.h delete mode 100644 include/hueplusplus/Hue.h delete mode 100644 include/hueplusplus/HueLight.h delete mode 100644 include/hueplusplus/HueSensor.h create mode 100644 include/hueplusplus/LibConfig.h create mode 100644 include/hueplusplus/Light.h create mode 100644 include/hueplusplus/Sensor.h create mode 100644 src/Bridge.cpp delete mode 100644 src/Hue.cpp delete mode 100644 src/HueLight.cpp delete mode 100644 src/HueSensor.cpp create mode 100644 src/Light.cpp create mode 100644 src/Sensor.cpp delete mode 100644 test/mocks/mock_HueLight.h create mode 100644 test/mocks/mock_Light.h create mode 100644 test/test_Bridge.cpp delete mode 100644 test/test_Hue.cpp delete mode 100644 test/test_HueLight.cpp delete mode 100644 test/test_HueLightFactory.cpp create mode 100644 test/test_Light.cpp create mode 100644 test/test_LightFactory.cpp diff --git a/README.md b/README.md index 2b430e9..b7cd74b 100755 --- a/README.md +++ b/README.md @@ -23,14 +23,14 @@ A simple and easy to use library for Philips Hue Lights ## How to use ### Searching for Bridges To start searching for a Hue Bridge you will need to choose an IHttpHandler and create one. The options are a "WinHttpHandler" (for windows) or a "LinHttpHandler" (for linux). -Then create a HueFinder object with the handler. +Then create a BridgeFinder object with the handler. The handler is needed, because it tells the finder which functions to use to communicate with a bridge or your local network. After that you can call FindBridges(), which will return a vector containing the ip and mac address of all found Bridges. ```C++ // For windows use std::make_shared(); handler = std::make_shared(); -hueplusplus::HueFinder finder(handler); -std::vector bridges = finder.FindBridges(); +hueplusplus::BridgeFinder finder(handler); +std::vector bridges = finder.FindBridges(); if (bridges.empty()) { std::cerr << "No bridges found\n"; @@ -44,32 +44,32 @@ If you have found the Bridge you were looking for, you can then move on with the To get a new username from the Bridge (for now) you simply call GetBridge(bridges[\]), where index is your preferred Bridge from the part [Searching for Bridges](#searchingBridges). ```C++ -hueplusplus::Hue bridge = finder.GetBridge(bridges[0]); +hueplusplus::Bridge bridge = finder.GetBridge(bridges[0]); ``` If you on the other hand already have a username you can add your bridge like so ```C++ finder.AddUsername(bridges[0].mac, ""); -hueplusplus::Hue bridge = finder.GetBridge(bridges[0]); +hueplusplus::Bridge bridge = finder.GetBridge(bridges[0]); ``` -If you do not want to use the HueFinder or you already know the ip and username of your bridge you have the option to create your own Hue object. +If you do not want to use the BridgeFinder or you already know the ip and username of your bridge you have the option to create your own Bridge object. Here you will need to provide the ip address, the port number, a username and an HttpHandler ```C++ // For windows use std::make_shared(); handler = std::make_shared(); -hueplusplus::Hue bridge("192.168.2.102", 80, "", handler); +hueplusplus::Bridge bridge("192.168.2.102", 80, "", handler); ``` ### Controlling lights If you have your Bridge all set up, you can now control its lights. -For that create a new HueLight object and call lights().get(\) on your bridge object to get a reference to a specific light, where id +For that create a new Light object and call lights().get(\) on your bridge object to get a reference to a specific light, where id is the id of the light set internally by the Hue Bridge. ```C++ -hueplusplus::HueLight light1 = bridge.lights().get(1); +hueplusplus::Light light1 = bridge.lights().get(1); ``` If you don't know the id of a specific light or want to get an overview over all lights that are controlled by your bridge, you can get a vector containing them by calling getAll(). If no lights are found the vector will be empty. ```C++ -std::vector> lights = bridge.lights().getAll(); +std::vector> lights = bridge.lights().getAll(); ``` If you now want to control a light, call a specific function of it. ```C++ diff --git a/doc/markdown/Mainpage.md b/doc/markdown/Mainpage.md index 3907bb3..78d7aab 100644 --- a/doc/markdown/Mainpage.md +++ b/doc/markdown/Mainpage.md @@ -19,14 +19,14 @@ A simple and easy to use library for Philips Hue Lights. ### Searching for Bridges To start searching for a Hue Bridge you will need to choose an IHttpHandler and create one. The options are a [WinHttpHandler](@ref hueplusplus::WinHttpHandler) (for windows) or a [LinHttpHandler](@ref hueplusplus::LinHttpHandler) (for linux or linux-like). -Then create a [HueFinder](@ref hueplusplus::HueFinder) object with the handler. +Then create a [BridgeFinder](@ref hueplusplus::BridgeFinder) object with the handler. The handler is needed, because it tells the finder which functions to use to communicate with a bridge or your local network. -After that you can call [FindBridges()](@ref hueplusplus::HueFinder::FindBridges), which will return a vector containing the ip and mac address of all found Bridges. +After that you can call [FindBridges()](@ref hueplusplus::BridgeFinder::FindBridges), which will return a vector containing the ip and mac address of all found Bridges. ```{.cpp} // For windows use std::make_shared(); handler = std::make_shared(); -hueplusplus::HueFinder finder(handler); -std::vector bridges = finder.FindBridges(); +hueplusplus::BridgeFinder finder(handler); +std::vector bridges = finder.FindBridges(); if (bridges.empty()) { std::cerr << "No bridges found\n"; @@ -37,35 +37,35 @@ if (bridges.empty()) ### Authenticate Bridges If you have found the Bridge you were looking for, you can then move on with the authentication process. -To get a new username from the Bridge (for now) you simply call [GetBridge(bridges[\])](@ref hueplusplus::HueFinder::GetBridge), +To get a new username from the Bridge (for now) you simply call [GetBridge(bridges[\])](@ref hueplusplus::BridgeFinder::GetBridge), where index is your preferred Bridge from the part [Searching for Bridges](#searchingBridges). This requires the user to press the link button. ```{.cpp} -hueplusplus::Hue bridge = finder.GetBridge(bridges[0]); +hueplusplus::Bridge bridge = finder.GetBridge(bridges[0]); ``` If you on the other hand already have a username you can add your bridge like so ```{.cpp} finder.AddUsername(bridges[0].mac, ""); -hueplusplus::Hue bridge = finder.GetBridge(bridges[0]); +hueplusplus::Bridge bridge = finder.GetBridge(bridges[0]); ``` -If you do not want to use the HueFinder or you already know the ip and username of your bridge you have the option to create your own Hue object. +If you do not want to use the BridgeFinder or you already know the ip and username of your bridge you have the option to create your own Bridge object. Here you will need to provide the ip address, the port number, a username and an HttpHandler ```{.cpp} // For windows use std::make_shared(); handler = std::make_shared(); -hueplusplus::Hue bridge("192.168.2.102", 80, "", handler); +hueplusplus::Bridge bridge("192.168.2.102", 80, "", handler); ``` ### Controlling lights If you have your Bridge all set up, you can now control its lights. -For that create a new HueLight object and call [lights().get(\)](@ref hueplusplus::ResourceList::get) on your bridge object to get a reference to a specific light, where id +For that create a new Light object and call [lights().get(\)](@ref hueplusplus::ResourceList::get) on your bridge object to get a reference to a specific light, where id is the id of the light set internally by the Hue Bridge. ```{.cpp} -hueplusplus::HueLight light1 = bridge.lights().get(1); +hueplusplus::Light light1 = bridge.lights().get(1); ``` If you don't know the id of a specific light or want to get an overview over all lights that are controlled by your bridge, you can get a vector containing them by calling [getAll()](@ref hueplusplus::ResourceList::getAll) on your bridge object. If no lights are found the vector will be empty. ```{.cpp} -std::vector> lights = bridge.lights().getAll(); +std::vector> lights = bridge.lights().getAll(); ``` If you now want to control a light, call a specific function of it. ```{.cpp} @@ -79,7 +79,7 @@ lights.at(1).setColorHue(4562); ``` But keep in mind that some light types do not have all functions available. So you might call a specific function, but nothing will happen. For that you might want to check what type -of a light you are controlling. For that you can call the function [getColorType()](@ref hueplusplus::HueLight::getColorType()), which will return +of a light you are controlling. For that you can call the function [getColorType()](@ref hueplusplus::Light::getColorType()), which will return a ColorType. ```{.cpp} hueplusplus::ColorType type1 = light1.getColorType(); diff --git a/include/hueplusplus/APIConfig.h b/include/hueplusplus/APIConfig.h deleted file mode 100644 index 3b1b4fa..0000000 --- a/include/hueplusplus/APIConfig.h +++ /dev/null @@ -1,75 +0,0 @@ -/** - \file APIConfig.h - Copyright Notice\n - Copyright (C) 2017 Jan Rogall - developer\n - Copyright (C) 2017 Moritz Wirger - developer\n - - This file is part of hueplusplus. - - hueplusplus is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - hueplusplus is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with hueplusplus. If not, see . -**/ - -#ifndef INCLUDE_HUEPLUSPLUS_HUE_CONFIG_H -#define INCLUDE_HUEPLUSPLUS_HUE_CONFIG_H - -#include - -namespace hueplusplus -{ -//! \brief Configurable delays -//! -//! Used to set all delays to zero when running tests. -class Config -{ -private: - using duration = std::chrono::steady_clock::duration; - -public: - //! \brief Delay for advanced alerts before the actual alert - duration getPreAlertDelay() const { return preAlertDelay; } - //! \brief Delay for advanced alerts after the actual alert - duration getPostAlertDelay() const { return postAlertDelay; } - - //! \brief Timeout for UPnP multicast request - duration getUPnPTimeout() const { return upnpTimeout; } - - //! \brief Delay between bridge requests - duration getBridgeRequestDelay() const { return bridgeRequestDelay; } - - //! \brief Timeout for Hue::requestUsername, waits until link button was pressed - duration getRequestUsernameTimeout() const { return requestUsernameDelay; } - - //! \brief Interval in which username requests are attempted - duration getRequestUsernameAttemptInterval() const { return requestUsernameAttemptInterval; } - - //! \brief Get config instance - static Config& instance() - { - static Config c; - return c; - } -protected: - Config() = default; - -protected: - duration preAlertDelay = std::chrono::milliseconds(120); - duration postAlertDelay = std::chrono::milliseconds(1600); - duration upnpTimeout = std::chrono::seconds(5); - duration bridgeRequestDelay = std::chrono::milliseconds(100); - duration requestUsernameDelay = std::chrono::seconds(35); - duration requestUsernameAttemptInterval = std::chrono::seconds(1); -}; -} // namespace hueplusplus - -#endif diff --git a/include/hueplusplus/Bridge.h b/include/hueplusplus/Bridge.h new file mode 100644 index 0000000..24deaa1 --- /dev/null +++ b/include/hueplusplus/Bridge.h @@ -0,0 +1,263 @@ +/** + \file Bridge.h + Copyright Notice\n + Copyright (C) 2017 Jan Rogall - developer\n + Copyright (C) 2017 Moritz Wirger - developer\n + + This file is part of hueplusplus. + + hueplusplus is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + hueplusplus is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with hueplusplus. If not, see . +**/ + +#ifndef INCLUDE_HUEPLUSPLUS_HUE_H +#define INCLUDE_HUEPLUSPLUS_HUE_H + +#include +#include +#include +#include +#include + +#include "APICache.h" +#include "BridgeConfig.h" +#include "BrightnessStrategy.h" +#include "ColorHueStrategy.h" +#include "ColorTemperatureStrategy.h" +#include "Group.h" +#include "HueCommandAPI.h" +#include "HueDeviceTypes.h" +#include "IHttpHandler.h" +#include "Light.h" +#include "ResourceList.h" +#include "Scene.h" +#include "Schedule.h" +#include "Sensor.h" +#include "Utils.h" + +#include "json/json.hpp" + +//! \brief Namespace for the hueplusplus library +namespace hueplusplus +{ +// forward declarations +class Bridge; + +//! +//! Class to find all Hue bridges on the network and create usernames for them. +//! +class BridgeFinder +{ +public: + struct BridgeIdentification + { + std::string ip; + int port = 80; + std::string mac; + }; + +public: + //! \brief Constructor of BridgeFinder class + //! + //! \param handler HttpHandler of type \ref IHttpHandler for communication with the bridge + BridgeFinder(std::shared_ptr handler); + + //! \brief Finds all bridges in the network and returns them. + //! + //! The user should be given the opportunity to select the correct one based on the mac address. + //! \return vector containing ip and mac of all found bridges + //! \throws std::system_error when system or socket operations fail + //! \throws HueException when response contained no body + std::vector FindBridges() const; + + //! \brief Gets a Hue bridge based on its identification + //! + //! \param identification \ref BridgeIdentification that specifies a bridge + //! \return \ref Bridge class object + //! \throws std::system_error when system or socket operations fail + //! \throws HueException when response contained no body or username could not be requested + //! \throws HueAPIResponseException when response contains an error + //! \throws nlohmann::json::parse_error when response could not be parsed + Bridge GetBridge(const BridgeIdentification& identification); + + //! \brief Function that adds a username to the usernames map + //! + //! \param mac MAC address of Hue bridge + //! \param username Username that is used to control the Hue bridge + void AddUsername(const std::string& mac, const std::string& username); + + //! \brief Function that returns a map of mac addresses and usernames. + //! + //! Note these should be saved at the end and re-loaded with \ref AddUsername + //! next time, so only one username is generated per bridge. \returns A map + //! mapping mac address to username for every bridge + const std::map& GetAllUsernames() const; + + //! \brief Normalizes mac address to plain hex number. + //! \returns \p input without separators and whitespace, in lower case. + static std::string NormalizeMac(std::string input); + +private: + //! \brief Parses mac address from description.xml + //! + //! \param description Content of description.xml file as returned by GET request. + //! \returns Content of xml element \c serialNumber if description matches a Hue bridge, otherwise an empty + //! string. + static std::string ParseDescription(const std::string& description); + + std::map usernames; //!< Maps all macs to usernames added by \ref + //!< BridgeFinder::AddUsername + std::shared_ptr http_handler; +}; + +//! \brief Bridge class for a bridge. +//! +//! This is the main class used to interact with the Hue bridge. +class Bridge +{ + friend class BridgeFinder; + +public: + using LightList = ResourceList; + using GroupList = GroupResourceList; + using ScheduleList = CreateableResourceList; + using SceneList = CreateableResourceList; + using SensorList = ResourceList; + +public: + //! \brief Constructor of Bridge class + //! + //! \param ip IP address in dotted decimal notation like "192.168.2.1" + //! \param port Port of the hue bridge + //! \param username String that specifies the username that is used to control + //! the bridge. Can be left empty and acquired in \ref requestUsername. + //! \param handler HttpHandler for communication with the bridge + //! \param refreshDuration Time between refreshing the cached state. + Bridge(const std::string& ip, const int port, const std::string& username, + std::shared_ptr handler, + std::chrono::steady_clock::duration refreshDuration = std::chrono::seconds(10)); + + //! \brief Refreshes the bridge state. + //! + //! Should only be called rarely, as a full refresh is costly and usually not necessary. + //! Instead refresh only the parts you are interested in or rely on periodic refreshes + //! that happen automatically when calling non-const methods. + //! \throws std::system_error when system or socket operations fail + //! \throws HueException when response contained no body + //! \throws HueAPIResponseException when response contains an error + //! \throws nlohmann::json::parse_error when response could not be parsed + void refresh(); + + //! \brief Function to get the ip address of the hue bridge + //! + //! \return string containing ip + std::string getBridgeIP() const; + + //! \brief Function to get the port of the hue bridge + //! + //! \return integer containing port + int getBridgePort() const; + + //! \brief Send a username request to the Hue bridge. + //! + //! Blocks for about 30 seconds and 5 seconds to prepare. + //! It automatically sets the username variable according to the username received and returns the username + //! received. This function should only be called once to acquire a username to control the bridge and the + //! username should be saved for future use. \return username for API usage \throws std::system_error when + //! system or socket operations fail \throws HueException when response contained no body \throws + //! HueAPIResponseException when response contains an error except link button not pressed. \throws + //! nlohmann::json::parse_error when response could not be parsed + std::string requestUsername(); + + //! \brief Function that returns the username + //! + //! \return The username used for API access + std::string getUsername() const; + + //! \brief Function to set the ip address of this class representing a bridge + //! + //! \param ip String that specifies the ip in dotted decimal notation like "192.168.2.1" + void setIP(const std::string& ip); + + //! \brief Function to set the port of this class representing a bridge + //! + //! \param port Integer that specifies the port of an address like + //! "192.168.2.1:8080" + void setPort(const int port); + + //! \brief Provides access to the configuration of the bridge. + BridgeConfig& config(); + //! \brief Provides access to the configuration of the bridge. + //! \note Does not refresh state. + const BridgeConfig& config() const; + + //! \brief Provides access to the Light%s on the bridge. + LightList& lights(); + //! \brief Provides access to the Light%s on the bridge. + //! \note Does not refresh state. + const LightList& lights() const; + + //! \brief Provides access to the Group%s on the bridge. + GroupList& groups(); + //! \brief Provides access to the Group%s on the bridge. + //! \note Does not refresh state. + const GroupList& groups() const; + + //! \brief Provides access to the Schedule%s on the bridge. + ScheduleList& schedules(); + //! \brief Provides access to the Schedule%s on the bridge. + //! \note Does not refresh state. + const ScheduleList& schedules() const; + + //! \brief Provides access to the Scene%s on the bridge. + SceneList& scenes(); + //! \brief Provides access to the Scene%s on the bridge. + //! \note Does not refresh state. + const SceneList& scenes() const; + + //! \brief Provides access to the Sensor%s on the bridge. + SensorList& sensors(); + //! \brief Provides access to the Sensor%s on the bridge. + //! \note Does not refresh state. + const SensorList& sensors() const; + +private: + //! \brief Function that sets the HttpHandler and updates the HueCommandAPI. + //! \param handler a HttpHandler of type \ref IHttpHandler + //! + //! The HttpHandler and HueCommandAPI are used for bridge communication. + //! Resetting the HttpHandler should only be done when the username is first set, + //! before Bridge is used. + //! Resets all caches and resource lists. + void setHttpHandler(std::shared_ptr handler); + +private: + std::string ip; //!< IP-Address of the hue bridge in dotted decimal notation + //!< like "192.168.2.1" + std::string username; //!< Username that is ussed to access the hue bridge + int port; + + std::shared_ptr http_handler; //!< A IHttpHandler that is used to communicate with the + //!< bridge + std::chrono::steady_clock::duration refreshDuration; + std::shared_ptr stateCache; + detail::MakeCopyable lightList; + detail::MakeCopyable groupList; + detail::MakeCopyable scheduleList; + detail::MakeCopyable sceneList; + detail::MakeCopyable sensorList; + detail::MakeCopyable bridgeConfig; +}; +} // namespace hueplusplus + +#endif diff --git a/include/hueplusplus/BrightnessStrategy.h b/include/hueplusplus/BrightnessStrategy.h index 9e935ed..65ee233 100644 --- a/include/hueplusplus/BrightnessStrategy.h +++ b/include/hueplusplus/BrightnessStrategy.h @@ -27,7 +27,7 @@ namespace hueplusplus { -class HueLight; +class Light; //! Virtual base class for all BrightnessStrategies class BrightnessStrategy @@ -40,19 +40,19 @@ public: //! \param transition The time it takes to fade to the new brightness in //! multiples of 100ms, 4 = 400ms and should be seen as the default \param //! light A reference of the light - virtual bool setBrightness(unsigned int bri, uint8_t transition, HueLight& light) const = 0; + virtual bool setBrightness(unsigned int bri, uint8_t transition, Light& light) const = 0; //! \brief Virtual function that returns the current brightnessof the light //! //! Should update the lights state by calling refreshState() //! \param light A reference of the light //! \return Unsigned int representing the brightness - virtual unsigned int getBrightness(HueLight& light) const = 0; + virtual unsigned int getBrightness(Light& light) const = 0; //! \brief Virtual function that returns the current brightness of the light //! //! \note This should not update the lights state //! \param light A const reference of the light //! \return Unsigned int representing the brightness - virtual unsigned int getBrightness(const HueLight& light) const = 0; + virtual unsigned int getBrightness(const Light& light) const = 0; //! \brief Virtual dtor virtual ~BrightnessStrategy() = default; }; diff --git a/include/hueplusplus/ColorHueStrategy.h b/include/hueplusplus/ColorHueStrategy.h index 9eee184..30304fb 100644 --- a/include/hueplusplus/ColorHueStrategy.h +++ b/include/hueplusplus/ColorHueStrategy.h @@ -30,7 +30,7 @@ namespace hueplusplus { -class HueLight; +class Light; //! Virtual base class for all ColorHueStrategies class ColorHueStrategy @@ -44,7 +44,7 @@ public: //! The time it takes to fade to the new color in multiples of 100ms, 4 = //! 400ms and should be seen as the default \param light A reference of the //! light - virtual bool setColorHue(uint16_t hue, uint8_t transition, HueLight& light) const = 0; + virtual bool setColorHue(uint16_t hue, uint8_t transition, Light& light) const = 0; //! \brief Virtual function for changing a lights color in saturation with a //! specified transition. //! @@ -53,7 +53,7 @@ public: //! color \param transition The time it takes to fade to the new color in //! multiples of 100ms, 4 = 400ms and should be seen as the default \param //! light A reference of the light - virtual bool setColorSaturation(uint8_t sat, uint8_t transition, HueLight& light) const = 0; + virtual bool setColorSaturation(uint8_t sat, uint8_t transition, Light& light) const = 0; //! \brief Virtual function for changing a lights color in hue and saturation //! format with a specified transition. //! @@ -61,7 +61,7 @@ public: //! \param transition The time it takes to fade to the new color in multiples of //! 100ms, 4 = 400ms and should be seen as the default //! \param light A reference of the light - virtual bool setColorHueSaturation(const HueSaturation& hueSat, uint8_t transition, HueLight& light) const = 0; + virtual bool setColorHueSaturation(const HueSaturation& hueSat, uint8_t transition, Light& light) const = 0; //! \brief Virtual function for changing a lights color in CIE format with a //! specified transition. //! @@ -69,7 +69,7 @@ public: //! \param transition The time it takes to fade to the new color in multiples //! of 100ms, 4 = 400ms and should be seen as the default \param light A //! reference of the light - virtual bool setColorXY(const XYBrightness& xy, uint8_t transition, HueLight& light) const = 0; + virtual bool setColorXY(const XYBrightness& xy, uint8_t transition, Light& light) const = 0; //! \brief Virtual function for turning on/off the color loop feature of a //! light. @@ -83,43 +83,43 @@ public: //! alternatively call Off() and then use any of the setter functions. \param //! on Boolean to turn this feature on or off, true/1 for on and false/0 for //! off \param light A reference of the light - virtual bool setColorLoop(bool on, HueLight& light) const = 0; + virtual bool setColorLoop(bool on, Light& light) const = 0; //! \brief Virtual function that lets the light perform one breath cycle in //! the specified color. //! //! \param hueSat The color in hue and saturation //! \param light A reference of the light - virtual bool alertHueSaturation(const HueSaturation& hueSat, HueLight& light) const = 0; + virtual bool alertHueSaturation(const HueSaturation& hueSat, Light& light) const = 0; //! \brief Virtual function that lets the light perform one breath cycle in //! the specified color. //! //! \param xy The color in XY and brightness //! \param light A reference of the light - virtual bool alertXY(const XYBrightness& xy, HueLight& light) const = 0; + virtual bool alertXY(const XYBrightness& xy, Light& light) const = 0; //! \brief Virtual function that returns the current color of the light as hue //! and saturation //! //! Should update the lights state by calling refreshState() //! \param light A reference of the light - virtual HueSaturation getColorHueSaturation(HueLight& light) const = 0; + virtual HueSaturation getColorHueSaturation(Light& light) const = 0; //! \brief Virtual function that returns the current color of the light as hue //! and saturation //! //! \note This should not update the lights state //! \param light A const reference of the light - virtual HueSaturation getColorHueSaturation(const HueLight& light) const = 0; + virtual HueSaturation getColorHueSaturation(const Light& light) const = 0; //! \brief Virtual function that returns the current color of the light as xy //! //! Should update the lights state by calling refreshState() //! \param light A reference of the light //! \return XY and brightness of current color - virtual XYBrightness getColorXY(HueLight& light) const = 0; + virtual XYBrightness getColorXY(Light& light) const = 0; //! \brief Virtual function that returns the current color of the light as xy //! //! \note This should not update the lights state //! \param light A const reference of the light //! \return XY and brightness of current color - virtual XYBrightness getColorXY(const HueLight& light) const = 0; + virtual XYBrightness getColorXY(const Light& light) const = 0; //! \brief Virtual dtor virtual ~ColorHueStrategy() = default; }; diff --git a/include/hueplusplus/ColorTemperatureStrategy.h b/include/hueplusplus/ColorTemperatureStrategy.h index 1a18662..0950447 100644 --- a/include/hueplusplus/ColorTemperatureStrategy.h +++ b/include/hueplusplus/ColorTemperatureStrategy.h @@ -27,7 +27,7 @@ namespace hueplusplus { -class HueLight; +class Light; //! Virtual base class for all ColorTemperatureStrategies class ColorTemperatureStrategy @@ -41,14 +41,14 @@ public: //! transition The time it takes to fade to the new color in multiples of //! 100ms, 4 = 400ms and should be seen as the default \param light A //! reference of the light - virtual bool setColorTemperature(unsigned int mired, uint8_t transition, HueLight& light) const = 0; + virtual bool setColorTemperature(unsigned int mired, uint8_t transition, Light& light) const = 0; //! \brief Virtual function that lets the light perform one breath cycle in //! the specified color. //! //! The color temperature in mired ranges from 153 to 500 whereas 153 is cold //! and 500 is warm. \param mired The color temperature in mired \param light //! A reference of the light - virtual bool alertTemperature(unsigned int mired, HueLight& light) const = 0; + virtual bool alertTemperature(unsigned int mired, Light& light) const = 0; //! \brief Virtual function that returns the current color temperature of the //! light //! @@ -56,7 +56,7 @@ public: //! The color temperature in mired ranges from 153 to 500 whereas 153 is cold //! and 500 is warm. \param light A reference of the light \return Unsigned //! int representing the color temperature in mired - virtual unsigned int getColorTemperature(HueLight& light) const = 0; + virtual unsigned int getColorTemperature(Light& light) const = 0; //! \brief Virtual function that returns the current color temperature of the //! light //! @@ -64,7 +64,7 @@ public: //! and 500 is warm. \note This should not update the lights state \param //! light A const reference of the light \return Unsigned int representing the //! color temperature in mired - virtual unsigned int getColorTemperature(const HueLight& light) const = 0; + virtual unsigned int getColorTemperature(const Light& light) const = 0; //! \brief Virtual dtor virtual ~ColorTemperatureStrategy() = default; }; diff --git a/include/hueplusplus/ExtendedColorHueStrategy.h b/include/hueplusplus/ExtendedColorHueStrategy.h index afcdc87..33ffae1 100644 --- a/include/hueplusplus/ExtendedColorHueStrategy.h +++ b/include/hueplusplus/ExtendedColorHueStrategy.h @@ -23,7 +23,7 @@ #ifndef INCLUDE_HUEPLUSPLUS_EXTENDED_COLOR_HUE_STRATEGY_H #define INCLUDE_HUEPLUSPLUS_EXTENDED_COLOR_HUE_STRATEGY_H -#include "HueLight.h" +#include "Light.h" #include "SimpleColorHueStrategy.h" namespace hueplusplus @@ -39,15 +39,15 @@ public: //! \param hueSat The color in hue and saturation //! \param light A reference of the light //! - //! Blocks for the time a \ref HueLight::alert() needs - bool alertHueSaturation(const HueSaturation& hueSat, HueLight& light) const override; + //! Blocks for the time a \ref Light::alert() needs + bool alertHueSaturation(const HueSaturation& hueSat, Light& light) const override; //! \brief Function that lets the light perform one breath cycle in the //! specified color. //! \param xy The color in XY and brightness //! \param light A reference of the light //! - //! Blocks for the time a \ref HueLight::alert() needs - bool alertXY(const XYBrightness& xy, HueLight& light) const override; + //! Blocks for the time a \ref Light::alert() needs + bool alertXY(const XYBrightness& xy, Light& light) const override; }; } // namespace hueplusplus diff --git a/include/hueplusplus/ExtendedColorTemperatureStrategy.h b/include/hueplusplus/ExtendedColorTemperatureStrategy.h index bce31ae..5b07202 100644 --- a/include/hueplusplus/ExtendedColorTemperatureStrategy.h +++ b/include/hueplusplus/ExtendedColorTemperatureStrategy.h @@ -23,7 +23,7 @@ #ifndef INCLUDE_HUEPLUSPLUS_EXTENDED_COLOR_TEMPERATURE_STRATEGY_H #define INCLUDE_HUEPLUSPLUS_EXTENDED_COLOR_TEMPERATURE_STRATEGY_H -#include "HueLight.h" +#include "Light.h" #include "SimpleColorTemperatureStrategy.h" namespace hueplusplus @@ -36,10 +36,10 @@ public: //! specified color. //! //! It uses this_thread::sleep_for to accomodate for the time an \ref - //! HueLight::alert() needs The color temperature in mired ranges from 153 to + //! Light::alert() needs The color temperature in mired ranges from 153 to //! 500 whereas 153 is cold and 500 is warm. \param mired The color //! temperature in mired \param light A reference of the light - bool alertTemperature(unsigned int mired, HueLight& light) const override; + bool alertTemperature(unsigned int mired, Light& light) const override; }; } // namespace hueplusplus diff --git a/include/hueplusplus/Hue.h b/include/hueplusplus/Hue.h deleted file mode 100644 index d4ea928..0000000 --- a/include/hueplusplus/Hue.h +++ /dev/null @@ -1,262 +0,0 @@ -/** - \file Hue.h - Copyright Notice\n - Copyright (C) 2017 Jan Rogall - developer\n - Copyright (C) 2017 Moritz Wirger - developer\n - - This file is part of hueplusplus. - - hueplusplus is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - hueplusplus is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with hueplusplus. If not, see . -**/ - -#ifndef INCLUDE_HUEPLUSPLUS_HUE_H -#define INCLUDE_HUEPLUSPLUS_HUE_H - -#include -#include -#include -#include -#include - -#include "APICache.h" -#include "BridgeConfig.h" -#include "BrightnessStrategy.h" -#include "ColorHueStrategy.h" -#include "ColorTemperatureStrategy.h" -#include "Group.h" -#include "HueCommandAPI.h" -#include "HueDeviceTypes.h" -#include "HueLight.h" -#include "HueSensor.h" -#include "IHttpHandler.h" -#include "ResourceList.h" -#include "Scene.h" -#include "Schedule.h" -#include "Utils.h" - -#include "json/json.hpp" - -//! \brief Namespace for the hueplusplus library -namespace hueplusplus -{ -// forward declarations -class Hue; - -//! -//! Class to find all Hue bridges on the network and create usernames for them. -//! -class HueFinder -{ -public: - struct HueIdentification - { - std::string ip; - int port = 80; - std::string mac; - }; - -public: - //! \brief Constructor of HueFinder class - //! - //! \param handler HttpHandler of type \ref IHttpHandler for communication with the bridge - HueFinder(std::shared_ptr handler); - - //! \brief Finds all bridges in the network and returns them. - //! - //! The user should be given the opportunity to select the correct one based on the mac address. - //! \return vector containing ip and mac of all found bridges - //! \throws std::system_error when system or socket operations fail - //! \throws HueException when response contained no body - std::vector FindBridges() const; - - //! \brief Gets a \ref Hue bridge based on its identification - //! - //! \param identification \ref HueIdentification that specifies a bridge - //! \return \ref Hue class object - //! \throws std::system_error when system or socket operations fail - //! \throws HueException when response contained no body or username could not be requested - //! \throws HueAPIResponseException when response contains an error - //! \throws nlohmann::json::parse_error when response could not be parsed - Hue GetBridge(const HueIdentification& identification); - - //! \brief Function that adds a username to the usernames map - //! - //! \param mac MAC address of Hue bridge - //! \param username Username that is used to control the Hue bridge - void AddUsername(const std::string& mac, const std::string& username); - - //! \brief Function that returns a map of mac addresses and usernames. - //! - //! Note these should be saved at the end and re-loaded with \ref AddUsername - //! next time, so only one username is generated per bridge. \returns A map - //! mapping mac address to username for every bridge - const std::map& GetAllUsernames() const; - - //! \brief Normalizes mac address to plain hex number. - //! \returns \p input without separators and whitespace, in lower case. - static std::string NormalizeMac(std::string input); - -private: - //! \brief Parses mac address from description.xml - //! - //! \param description Content of description.xml file as returned by GET request. - //! \returns Content of xml element \c serialNumber if description matches a Hue bridge, otherwise an empty - //! string. - static std::string ParseDescription(const std::string& description); - - std::map usernames; //!< Maps all macs to usernames added by \ref - //!< HueFinder::AddUsername - std::shared_ptr http_handler; -}; - -//! \brief Hue class for a bridge. -//! -//! This is the main class used to interact with the Hue bridge. -class Hue -{ - friend class HueFinder; - -public: - using LightList = ResourceList; - using GroupList = GroupResourceList; - using ScheduleList = CreateableResourceList; - using SceneList = CreateableResourceList; - using SensorList = ResourceList; - -public: - //! \brief Constructor of Hue class - //! - //! \param ip IP address in dotted decimal notation like "192.168.2.1" - //! \param port Port of the hue bridge - //! \param username String that specifies the username that is used to control - //! the bridge. Can be left empty and acquired in \ref requestUsername. - //! \param handler HttpHandler for communication with the bridge - //! \param refreshDuration Time between refreshing the cached state. - Hue(const std::string& ip, const int port, const std::string& username, std::shared_ptr handler, - std::chrono::steady_clock::duration refreshDuration = std::chrono::seconds(10)); - - //! \brief Refreshes the bridge state. - //! - //! Should only be called rarely, as a full refresh is costly and usually not necessary. - //! Instead refresh only the parts you are interested in or rely on periodic refreshes - //! that happen automatically when calling non-const methods. - //! \throws std::system_error when system or socket operations fail - //! \throws HueException when response contained no body - //! \throws HueAPIResponseException when response contains an error - //! \throws nlohmann::json::parse_error when response could not be parsed - void refresh(); - - //! \brief Function to get the ip address of the hue bridge - //! - //! \return string containing ip - std::string getBridgeIP() const; - - //! \brief Function to get the port of the hue bridge - //! - //! \return integer containing port - int getBridgePort() const; - - //! \brief Send a username request to the Hue bridge. - //! - //! Blocks for about 30 seconds and 5 seconds to prepare. - //! It automatically sets the username variable according to the username received and returns the username - //! received. This function should only be called once to acquire a username to control the bridge and the - //! username should be saved for future use. \return username for API usage \throws std::system_error when - //! system or socket operations fail \throws HueException when response contained no body \throws - //! HueAPIResponseException when response contains an error except link button not pressed. \throws - //! nlohmann::json::parse_error when response could not be parsed - std::string requestUsername(); - - //! \brief Function that returns the username - //! - //! \return The username used for API access - std::string getUsername() const; - - //! \brief Function to set the ip address of this class representing a bridge - //! - //! \param ip String that specifies the ip in dotted decimal notation like "192.168.2.1" - void setIP(const std::string& ip); - - //! \brief Function to set the port of this class representing a bridge - //! - //! \param port Integer that specifies the port of an address like - //! "192.168.2.1:8080" - void setPort(const int port); - - //! \brief Provides access to the configuration of the bridge. - BridgeConfig& config(); - //! \brief Provides access to the configuration of the bridge. - //! \note Does not refresh state. - const BridgeConfig& config() const; - - //! \brief Provides access to the HueLight%s on the bridge. - LightList& lights(); - //! \brief Provides access to the HueLight%s on the bridge. - //! \note Does not refresh state. - const LightList& lights() const; - - //! \brief Provides access to the Group%s on the bridge. - GroupList& groups(); - //! \brief Provides access to the Group%s on the bridge. - //! \note Does not refresh state. - const GroupList& groups() const; - - //! \brief Provides access to the Schedule%s on the bridge. - ScheduleList& schedules(); - //! \brief Provides access to the Schedule%s on the bridge. - //! \note Does not refresh state. - const ScheduleList& schedules() const; - - //! \brief Provides access to the Scene%s on the bridge. - SceneList& scenes(); - //! \brief Provides access to the Scene%s on the bridge. - //! \note Does not refresh state. - const SceneList& scenes() const; - - //! \brief Provides access to the HueSensor%s on the bridge. - SensorList& sensors(); - //! \brief Provides access to the HueSensor%s on the bridge. - //! \note Does not refresh state. - const SensorList& sensors() const; - -private: - //! \brief Function that sets the HttpHandler and updates the HueCommandAPI. - //! \param handler a HttpHandler of type \ref IHttpHandler - //! - //! The HttpHandler and HueCommandAPI are used for bridge communication. - //! Resetting the HttpHandler should only be done when the username is first set, - //! before Hue is used. - //! Resets all caches and resource lists. - void setHttpHandler(std::shared_ptr handler); - -private: - std::string ip; //!< IP-Address of the hue bridge in dotted decimal notation - //!< like "192.168.2.1" - std::string username; //!< Username that is ussed to access the hue bridge - int port; - - std::shared_ptr http_handler; //!< A IHttpHandler that is used to communicate with the - //!< bridge - std::chrono::steady_clock::duration refreshDuration; - std::shared_ptr stateCache; - detail::MakeCopyable lightList; - detail::MakeCopyable groupList; - detail::MakeCopyable scheduleList; - detail::MakeCopyable sceneList; - detail::MakeCopyable sensorList; - detail::MakeCopyable bridgeConfig; -}; -} // namespace hueplusplus - -#endif diff --git a/include/hueplusplus/HueDeviceTypes.h b/include/hueplusplus/HueDeviceTypes.h index a1ae481..b44bb6f 100644 --- a/include/hueplusplus/HueDeviceTypes.h +++ b/include/hueplusplus/HueDeviceTypes.h @@ -26,27 +26,27 @@ #include #include -#include "HueLight.h" +#include "Light.h" namespace hueplusplus { -class HueLightFactory +class LightFactory { public: - //! \brief Create a factory for HueLight%s + //! \brief Create a factory for Light%s //! \param commands HueCommandAPI for communication with the bridge //! \param refreshDuration Time between refreshing the cached light state. - HueLightFactory(const HueCommandAPI& commands, std::chrono::steady_clock::duration refreshDuration); + LightFactory(const HueCommandAPI& commands, std::chrono::steady_clock::duration refreshDuration); - //! \brief Create a HueLight with the correct type from the JSON state. + //! \brief Create a Light with the correct type from the JSON state. //! \param lightState Light JSON as returned from the bridge (not only the "state" part of it). //! \param id Light id. - //! \returns HueLight with matching id, strategies and \ref ColorType. + //! \returns Light with matching id, strategies and \ref ColorType. //! \throws std::system_error when system or socket operations fail //! \throws HueException when light type is unknown //! \throws HueAPIResponseException when response contains an error //! \throws nlohmann::json::parse_error when response could not be parsed - HueLight createLight(const nlohmann::json& lightState, int id); + Light createLight(const nlohmann::json& lightState, int id); private: //! \brief Get color type from light JSON. diff --git a/include/hueplusplus/HueLight.h b/include/hueplusplus/HueLight.h deleted file mode 100644 index a8d116d..0000000 --- a/include/hueplusplus/HueLight.h +++ /dev/null @@ -1,666 +0,0 @@ -/** - \file HueLight.h - Copyright Notice\n - Copyright (C) 2017 Jan Rogall - developer\n - Copyright (C) 2017 Moritz Wirger - developer\n - - This file is part of hueplusplus. - - hueplusplus is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - hueplusplus is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with hueplusplus. If not, see . -**/ - -#ifndef INCLUDE_HUEPLUSPLUS_HUE_LIGHT_H -#define INCLUDE_HUEPLUSPLUS_HUE_LIGHT_H - -#include - -#include "APICache.h" -#include "BaseDevice.h" -#include "BrightnessStrategy.h" -#include "ColorHueStrategy.h" -#include "ColorTemperatureStrategy.h" -#include "HueCommandAPI.h" -#include "StateTransaction.h" - -#include "json/json.hpp" - -namespace hueplusplus -{ -/*enum ModelType -{ -UNDEFINED, // undefined model -LCT001, // Hue bulb A19, Color Gamut B, ECL -LCT007, // Hue bulb A19, Color Gamut B, ECL -LCT010, // Hue bulb A19, Color Gamut C, ECL -LCT014, // Hue bulb A19, Color Gamut C, ECL - -LCT002, // Hue Spot BR30, Color Gamut B, ECL -LCT003, // Hue Spot GU10, Color Gamut B, ECL - -LCT011, // Hue BR30, Color Gamut C, ECL - -LST001, // Hue LightStrips, Color Gamut A, CL -LST002, // Hue LightStrips Plus, Color Gamut C, ECL - -LLC010, // Hue Living Colors Iris, Color Gamut A, CL -LLC011, // Hue Living Colors Bloom, Color Gamut A, CL -LLC012, // Hue Living Colors Bloom, Color Gamut A, CL -LLC006, // Living Colors Gen3 Iris, Color Gamut A, CL, NO HUE FRIEND -LLC007, // Living Colors Gen3 Bloom, Aura, Color Gamut A, CL, NO HUE FRIEND -LLC013, // Disney Living Colors, Color Gamut A, CL - -LWB004, // Hue A19 Lux, Color Gamut -, DL -LWB006, // Hue A19 Lux, Color Gamut -, DL -LWB007, // Hue A19 Lux, Color Gamut -, DL -LWB010, // Hue A19 Lux, Color Gamut -, DL -LWB014, // Hue A19 Lux, Color Gamut -, DL - -LLM001, // Color Light Module, Color Gamut B, ECL -LLM010, // Color Temperature Module, Color Gamut 2200K-6500K, CTL -LLM011, // Color Temperature Module, Color Gamut 2200K-6500K, CTL -LLM012, // Color Temperature Module, Color Gamut 2200K-6500K, CTL - -LTW001, // Hue Spot BR30, Color Gamut 2200K-6500K, CTL -LTW004, // Hue Spot BR30, Color Gamut 2200K-6500K, CTL -LTW013, // Hue Spot BR30, Color Gamut 2200K-6500K, CTL -LTW014, // Hue Spot BR30, Color Gamut 2200K-6500K, CTL - -LLC020 // Hue Go, Color Gamut C, ECL -};*/ - -//! enum that specifies the color type of all HueLights -enum class ColorType -{ - UNDEFINED, //!< ColorType for this light is unknown or undefined - NONE, //!< light has no specific ColorType - GAMUT_A, //!< light uses Gamut A - GAMUT_B, //!< light uses Gamut B - GAMUT_C, //!< light uses Gamut C - TEMPERATURE, //!< light has color temperature control - GAMUT_A_TEMPERATURE, //!< light uses Gamut A and has color temperature control - GAMUT_B_TEMPERATURE, //!< light uses Gamut B and has color temperature control - GAMUT_C_TEMPERATURE //!< light uses Gamut C and has color temperature control -}; - -//! \brief Class for Hue Light fixtures -//! -//! Provides methods to query and control lights. -class HueLight : public BaseDevice -{ - friend class HueLightFactory; - friend class SimpleBrightnessStrategy; - friend class SimpleColorHueStrategy; - friend class ExtendedColorHueStrategy; - friend class SimpleColorTemperatureStrategy; - friend class ExtendedColorTemperatureStrategy; - -public: - //! \name General information - ///@{ - - //! \brief Const function that returns the luminaireuniqueid of the light - //! - //! \note Only working on bridges with versions starting at 1.9 - //! \return String containing the luminaireuniqueid or an empty string when the function is not supported - virtual std::string getLuminaireUId() const; - - //! \brief Const function that returns the color type of the light. - //! - //! \return ColorType containig the color type of the light - virtual ColorType getColorType() const; - - //! \brief Get gamut space of possible light colors - //! \returns Used gamut, or \ref gamut::maxGamut when unknown. - ColorGamut getColorGamut() const; - - ///@} - //! \name Light state - ///@{ - - //! \brief Function that turns the light on. - //! - //! \param transition Optional parameter to set the transition from current state to new, standard is 4 = 400ms - //! \return true on success - //! \throws std::system_error when system or socket operations fail - //! \throws HueException when response contained no body - //! \throws HueAPIResponseException when response contains an error - //! \throws nlohmann::json::parse_error when response could not be parsed - virtual bool On(uint8_t transition = 4); - - //! \brief Function that turns the light off. - //! - //! \param transition Optional parameter to set the transition from current state to new, standard is 4 = 400ms - //! \return Bool that is true on success - //! \throws std::system_error when system or socket operations fail - //! \throws HueException when response contained no body - //! \throws HueAPIResponseException when response contains an error - //! \throws nlohmann::json::parse_error when response could not be parsed - virtual bool Off(uint8_t transition = 4); - - //! \brief Function to check whether a light is on or off - //! - //! \return Bool that is true, when the light is on and false, when off - //! \throws std::system_error when system or socket operations fail - //! \throws HueException when response contained no body - //! \throws HueAPIResponseException when response contains an error - //! \throws nlohmann::json::parse_error when response could not be parsed - virtual bool isOn(); - - //! \brief Const function to check whether a light is on or off - //! - //! \note This will not refresh the light state - //! \return Bool that is true, when the light is on and false, when off - virtual bool isOn() const; - - //! \brief Const function to check whether this light has brightness control - //! - //! \return Bool that is true when the light has specified abilities and false - //! when not - virtual bool hasBrightnessControl() const { return brightnessStrategy != nullptr; }; - - //! \brief Const function to check whether this light has color temperature - //! control - //! - //! \return Bool that is true when the light has specified abilities and false - //! when not - virtual bool hasTemperatureControl() const { return colorTemperatureStrategy != nullptr; }; - - //! \brief Connst function to check whether this light has full color control - //! - //! \return Bool that is true when the light has specified abilities and false - //! when not - virtual bool hasColorControl() const { return colorHueStrategy != nullptr; }; - - //! \brief Const function that converts Kelvin to Mired. - //! - //! \param kelvin Unsigned integer value in Kelvin - //! \return Unsigned integer value in Mired - unsigned int KelvinToMired(unsigned int kelvin) const; - - //! \brief Const function that converts Mired to Kelvin. - //! - //! \param mired Unsigned integer value in Mired - //! \return Unsigned integer value in Kelvin - unsigned int MiredToKelvin(unsigned int mired) const; - - //! \brief Function that sets the brightness of this light. - //! - //! \note The brightness will only be set if the light has a reference to a - //! specific \ref BrightnessStrategy. The brightness can range from 0 = off to - //! 254 = fully lit. - //! \param bri Unsigned int that specifies the brightness - //! \param transition Optional parameter to set the transition from current state to new, standard is 4 = 400ms - //! \return Bool that is true on success - //! \throws std::system_error when system or socket operations fail - //! \throws HueException when response contained no body - //! \throws HueAPIResponseException when response contains an error - //! \throws nlohmann::json::parse_error when response could not be parsed - virtual bool setBrightness(unsigned int bri, uint8_t transition = 4) - { - if (brightnessStrategy) - { - return brightnessStrategy->setBrightness(bri, transition, *this); - } - return false; - }; - - //! \brief Const function that returns the brightness of this light. - //! - //! \note The brightness will only be returned if the light has a reference to - //! a specific \ref BrightnessStrategy. \note This will not refresh the light - //! state The brightness can range from 0 = off to 254 = fully lit. \return - //! Unsigned int that is 0 when function failed - virtual unsigned int getBrightness() const - { - if (brightnessStrategy) - { - return brightnessStrategy->getBrightness(*this); - } - return 0; - }; - - //! \brief Function that returns the brightness of this light. - //! - //! \note The brightness will only be returned if the light has a reference to - //! a specific \ref BrightnessStrategy. The brightness can range from 0 = off - //! to 254 = fully lit. - //! \return Unsigned int that is 0 when function failed - //! \throws std::system_error when system or socket operations fail - //! \throws HueException when response contained no body - //! \throws HueAPIResponseException when response contains an error - //! \throws nlohmann::json::parse_error when response could not be parsed - virtual unsigned int getBrightness() - { - if (brightnessStrategy) - { - return brightnessStrategy->getBrightness(*this); - } - return 0; - }; - - //! \brief Function that sets the color temperature of this light in mired. - //! - //! \note The color temperature will only be set if the light has a reference - //! to a specific \ref ColorTemperatureStrategy. The color temperature can - //! range from 153 to 500. - //! \param mired Unsigned int that specifies the color temperature in Mired - //! \param transition Optional parameter to set the transition from current state to new, standard is 4 = 400ms - //! \return Bool that is true on success - //! \throws std::system_error when system or socket operations fail - //! \throws HueException when response contained no body - //! \throws HueAPIResponseException when response contains an error - //! \throws nlohmann::json::parse_error when response could not be parsed - virtual bool setColorTemperature(unsigned int mired, uint8_t transition = 4) - { - if (colorTemperatureStrategy) - { - return colorTemperatureStrategy->setColorTemperature(mired, transition, *this); - } - return false; - }; - - //! \brief Const function that returns the current color temperature of the - //! light - //! - //! \note The color temperature will only be returned when the light has a - //! reference to a specific \ref ColorTemperatureStrategy. - //! \note This will not refresh the light state - //! The color temperature in mired ranges from 153 to 500 whereas 153 is cold - //! and 500 is warm. - //! \return Unsigned int representing the color temperature in mired or 0 when failed - virtual unsigned int getColorTemperature() const - { - if (colorTemperatureStrategy) - { - return colorTemperatureStrategy->getColorTemperature(*this); - } - return 0; - }; - - //! \brief Function that returns the current color temperature of the light - //! - //! \note The color temperature will only be returned when the light has a - //! reference to a specific \ref ColorTemperatureStrategy. - //! Updates the lights state by calling refreshState() - //! The color temperature in mired ranges from 153 to 500 whereas 153 is cold - //! and 500 is warm. - //! \return Unsigned int representing the color temperature in mired or 0 when failed - //! \throws std::system_error when system or socket operations fail - //! \throws HueException when response contained no body - //! \throws HueAPIResponseException when response contains an error - //! \throws nlohmann::json::parse_error when response could not be parsed - virtual unsigned int getColorTemperature() - { - if (colorTemperatureStrategy) - { - return colorTemperatureStrategy->getColorTemperature(*this); - } - return 0; - }; - - //! \brief Function to set the color of this light with specified hue. - //! - //! \note The color will only be set if the light has a reference to a - //! specific \ref ColorHueStrategy. The hue can range from 0 to 65535, whereas - //! 65535 and 0 are red, 25500 is green and 46920 is blue. - //! \param hue uint16_t that specifies the hue - //! \param transition Optional parameter to set the transition from current state to new, standard is 4 = 400ms - //! \return Bool that is true on success - //! \throws std::system_error when system or socket operations fail - //! \throws HueException when response contained no body - //! \throws HueAPIResponseException when response contains an error - //! \throws nlohmann::json::parse_error when response could not be parsed - virtual bool setColorHue(uint16_t hue, uint8_t transition = 4) - { - if (colorHueStrategy) - { - return colorHueStrategy->setColorHue(hue, transition, *this); - } - return false; - }; - - //! \brief Function to set the color of this light with specified saturation. - //! - //! \note The color will only be set if the light has a reference to a - //! specific \ref ColorHueStrategy. The saturation can range from 0 to 254, - //! whereas 0 is least saturated (white) and 254 is most saturated. - //! \param sat uint8_t that specifies the saturation - //! \param transition Optional parameter to set the transition from current state to new, standard is 4 = 400ms - //! \return Bool that is true on success - //! \throws std::system_error when system or socket operations fail - //! \throws HueException when response contained no body - //! \throws HueAPIResponseException when response contains an error - //! \throws nlohmann::json::parse_error when response could not be parsed - virtual bool setColorSaturation(uint8_t sat, uint8_t transition = 4) - { - if (colorHueStrategy) - { - return colorHueStrategy->setColorSaturation(sat, transition, *this); - } - return false; - }; - - //! \brief Function to set the color of this light with specified hue and - //! saturation. - //! - //! \note The color will only be set if the light has a reference to a - //! specific \ref ColorHueStrategy. - //! \param hueSat Color in hue and satuation. - //! \param transition Optional parameter to set the transition from current state to new, standard is 4 = 400ms. - //! \return Bool that is true on success - //! \throws std::system_error when system or socket operations fail - //! \throws HueException when response contained no body - //! \throws HueAPIResponseException when response contains an error - //! \throws nlohmann::json::parse_error when response could not be parsed - virtual bool setColorHueSaturation(const HueSaturation& hueSat, uint8_t transition = 4) - { - if (colorHueStrategy) - { - return colorHueStrategy->setColorHueSaturation(hueSat, transition, *this); - } - return false; - }; - - //! \brief Const function that returns the current color of the light as hue - //! and saturation - //! - //! \note The color hue and saturation will only be returned when the light - //! has a reference to a specific \ref ColorHueStrategy. - //! \note This will not refresh the light state - //! \return Current hue and saturation or {0,0} when failed - //! \throws std::system_error when system or socket operations fail - //! \throws HueException when response contained no body - //! \throws HueAPIResponseException when response contains an error - //! \throws nlohmann::json::parse_error when response could not be parsed - virtual HueSaturation getColorHueSaturation() const - { - if (colorHueStrategy) - { - return colorHueStrategy->getColorHueSaturation(*this); - } - return {}; - }; - - //! \brief Function that returns the current color of the light as hue and - //! saturation - //! - //! \note The color hue and saturation will only be returned when the light - //! has a reference to a specific \ref ColorHueStrategy. Updates the lights - //! state by calling refreshState() - //! \return Current hue and saturation or {0,0} when failed - //! \throws std::system_error when system or socket operations fail - //! \throws HueException when response contained no body - //! \throws HueAPIResponseException when response contains an error - //! \throws nlohmann::json::parse_error when response could not be parsed - virtual HueSaturation getColorHueSaturation() - { - if (colorHueStrategy) - { - return colorHueStrategy->getColorHueSaturation(*this); - } - return {}; - }; - - //! \brief Function to set the color of this light in CIE with specified x y. - //! - //! \note The color will only be set if the light has a reference to a - //! specific \ref ColorHueStrategy. The values of x and y are ranging from 0 to 1. - //! \param xy The color in XY and brightness - //! \param transition Optional parameter to set the transition from current state to new, standard is 4 = 400ms - //! \return Bool that is true on success - //! \throws std::system_error when system or socket operations fail - //! \throws HueException when response contained no body - //! \throws HueAPIResponseException when response contains an error - //! \throws nlohmann::json::parse_error when response could not be parsed - virtual bool setColorXY(const XYBrightness& xy, uint8_t transition = 4) - { - if (colorHueStrategy) - { - return colorHueStrategy->setColorXY(xy, transition, *this); - } - return false; - }; - - //! \brief Const function that returns the current color of the light as xy - //! - //! \note The color x and y will only be returned when the light has a - //! reference to a specific \ref ColorHueStrategy. - //! \note This does not update the lights state - //! \return XYBrightness with x, y and brightness or an empty one (all 0) when failed - virtual XYBrightness getColorXY() const - { - if (colorHueStrategy) - { - return colorHueStrategy->getColorXY(*this); - } - return {}; - }; - - //! \brief Function that returns the current color of the light as xy - //! - //! \note The color x and y will only be returned when the light has a - //! reference to a specific \ref ColorHueStrategy. - //! Updates the lights state by calling refreshState() - //! \return XYBrightness with x, y and brightness or an empty one (all 0) when failed - //! \throws std::system_error when system or socket operations fail - //! \throws HueException when response contained no body - //! \throws HueAPIResponseException when response contains an error - //! \throws nlohmann::json::parse_error when response could not be parsed - virtual XYBrightness getColorXY() - { - if (colorHueStrategy) - { - return colorHueStrategy->getColorXY(*this); - } - return {}; - } - - //! \brief Function to set the color of this light with red green and blue - //! values. - //! - //! \note The color will only be set if the light has a reference to a - //! specific \ref ColorHueStrategy. The values of red, green and blue are - //! ranging from 0 to 255. - //! \param rgb RGB color that will be mapped to the available color space - //! \param transition Optional parameter to set the transition from current state to new, standard is 4 = 400ms - //! \return Bool that is true on success - //! \throws std::system_error when system or socket operations fail - //! \throws HueException when response contained no body - //! \throws HueAPIResponseException when response contains an error - //! \throws nlohmann::json::parse_error when response could not be parsed - virtual bool setColorRGB(const RGB& rgb, uint8_t transition = 4) - { - if (colorHueStrategy) - { - return colorHueStrategy->setColorXY(rgb.toXY(getColorGamut()), transition, *this); - } - return false; - } - - //! \brief Function that lets the light perform one breath cycle. - //! - //! Can be used for locating a light. - //! \return bool that is true on success - //! \throws std::system_error when system or socket operations fail - //! \throws HueException when response contained no body - //! \throws HueAPIResponseException when response contains an error - //! \throws nlohmann::json::parse_error when response could not be parsed - virtual bool alert(); - - //! \brief Function that lets the light perform one breath cycle in specified - //! color temperature. - //! - //! \note The breath cylce will only be performed if the light has a reference - //! to a specific \ref ColorTemperatureStrategy. - //! \param mired Color temperature in mired - //! \return Bool that is true on success - //! \throws std::system_error when system or socket operations fail - //! \throws HueException when response contained no body - //! \throws HueAPIResponseException when response contains an error - //! \throws nlohmann::json::parse_error when response could not be parsed - virtual bool alertTemperature(unsigned int mired) - { - if (colorTemperatureStrategy) - { - return colorTemperatureStrategy->alertTemperature(mired, *this); - } - return false; - } - - //! \brief Function that lets the light perform one breath cycle in specified - //! color. - //! - //! \note The breath cylce will only be performed if the light has a reference - //! to a specific \ref ColorHueStrategy. - //! \param hueSat Color in hue and saturation - //! \return Bool that is true on success - //! \throws std::system_error when system or socket operations fail - //! \throws HueException when response contained no body - //! \throws HueAPIResponseException when response contains an error - //! \throws nlohmann::json::parse_error when response could not be parsed - virtual bool alertHueSaturation(const HueSaturation& hueSat) - { - if (colorHueStrategy) - { - return colorHueStrategy->alertHueSaturation(hueSat, *this); - } - return false; - } - - //! \brief Function that lets the light perform one breath cycle in specified - //! color. - //! - //! \note The breath cylce will only be performed if the light has a reference - //! to a specific \ref ColorHueStrategy. - //! \param xy The x,y coordinates in CIE and brightness - //! \return Bool that is true on success - //! \throws std::system_error when system or socket operations fail - //! \throws HueException when response contained no body - //! \throws HueAPIResponseException when response contains an error - //! \throws nlohmann::json::parse_error when response could not be parsed - virtual bool alertXY(const XYBrightness& xy) - { - if (colorHueStrategy) - { - return colorHueStrategy->alertXY(xy, *this); - } - return false; - } - - //! \brief Function to turn colorloop effect on/off. - //! - //! Notice this function will only be performed light has a reference to a - //! specific \ref ColorHueStrategy. The colorloop effect will loop through all - //! colors on current hue and saturation levels. Notice that none of the - //! setter functions check whether this feature is enabled and the colorloop - //! can only be disabled with this function or by simply calling - //! Off()/OffNoRefresh() and then On()/OnNoRefresh(), so you could - //! alternatively call Off() and then use any of the setter functions. - //! \param on bool that enables this feature when true and disables it when false - //! \return Bool that is true on success - //! \throws std::system_error when system or socket operations fail - //! \throws HueException when response contained no body - //! \throws HueAPIResponseException when response contains an error - //! \throws nlohmann::json::parse_error when response could not be parsed - virtual bool setColorLoop(bool on) - { - if (colorHueStrategy) - { - return colorHueStrategy->setColorLoop(on, *this); - } - return false; - } - - //! \brief Create a transaction for this light. - //! - //! The transaction can be used to change more than one value in one request. - //! Only use the functions supported by the current light type. - //! - //! Example usage: \code - //! light.transaction().setBrightness(240).setColorHue(5000).commit(); - //! \endcode - virtual StateTransaction transaction(); - - ///@} - -protected: - //! \brief Protected ctor that is used by \ref Hue class. - //! - //! \param id Integer that specifies the id of this light - //! \param commands HueCommandAPI for communication with the bridge - //! - //! leaves strategies unset - HueLight(int id, const HueCommandAPI& commands); - - //! \brief Protected ctor that is used by \ref Hue class, also sets - //! strategies. - //! - //! \param id Integer that specifies the id of this light - //! \param commands HueCommandAPI for communication with the bridge - //! \param brightnessStrategy Strategy for brightness. May be nullptr. - //! \param colorTempStrategy Strategy for color temperature. May be nullptr. - //! \param colorHueStrategy Strategy for color hue/saturation. May be nullptr. - //! \param refreshDuration Time between refreshing the cached state. - //! Can be 0 to always refresh, or steady_clock::duration::max() to never refresh. - //! \throws std::system_error when system or socket operations fail - //! \throws HueException when response contained no body - //! \throws HueAPIResponseException when response contains an error - //! \throws nlohmann::json::parse_error when response could not be parsed - HueLight(int id, const HueCommandAPI& commands, std::shared_ptr brightnessStrategy, - std::shared_ptr colorTempStrategy, - std::shared_ptr colorHueStrategy, - std::chrono::steady_clock::duration refreshDuration = std::chrono::seconds(10)); - - //! \brief Protected function that sets the brightness strategy. - //! - //! The strategy defines how specific commands that deal with brightness - //! control are executed \param strat a strategy of type \ref - //! BrightnessStrategy - virtual void setBrightnessStrategy(std::shared_ptr strat) - { - brightnessStrategy = std::move(strat); - }; - - //! \brief Protected function that sets the colorTemperature strategy. - //! - //! The strategy defines how specific commands that deal with colortemperature - //! control are executed \param strat a strategy of type \ref - //! ColorTemperatureStrategy - virtual void setColorTemperatureStrategy(std::shared_ptr strat) - { - colorTemperatureStrategy = std::move(strat); - }; - - //! \brief Protected function that sets the colorHue strategy. - //! - //! The strategy defines how specific commands that deal with color control - //! are executed \param strat a strategy of type \ref ColorHueStrategy - virtual void setColorHueStrategy(std::shared_ptr strat) - { - colorHueStrategy = std::move(strat); - }; - -protected: - ColorType colorType; //!< holds the \ref ColorType of the light - - std::shared_ptr - brightnessStrategy; //!< holds a reference to the strategy that handles brightness commands - std::shared_ptr - colorTemperatureStrategy; //!< holds a reference to the strategy that handles colortemperature commands - std::shared_ptr - colorHueStrategy; //!< holds a reference to the strategy that handles all color commands -}; -} // namespace hueplusplus - -#endif diff --git a/include/hueplusplus/HueSensor.h b/include/hueplusplus/HueSensor.h deleted file mode 100644 index ba646e6..0000000 --- a/include/hueplusplus/HueSensor.h +++ /dev/null @@ -1,97 +0,0 @@ -/** - \file HueSensor.h - Copyright Notice\n - Copyright (C) 2020 Stefan Herbrechtsmeier - developer\n - - This file is part of hueplusplus. - - hueplusplus is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - hueplusplus is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with hueplusplus. If not, see . -**/ - -#ifndef INCLUDE_HUEPLUSPLUS_HUE_SENSOR_H -#define INCLUDE_HUEPLUSPLUS_HUE_SENSOR_H - -#include - -#include "BaseDevice.h" -#include "HueCommandAPI.h" - -#include "json/json.hpp" - -namespace hueplusplus -{ -//! -//! Class for Hue Sensor fixtures -//! -class HueSensor : public BaseDevice -{ - friend class Hue; - -public: - //! \brief std dtor - ~HueSensor() = default; - - //! \brief Function to get button event - //! - //! \return integer representing the button event - //! \throws std::system_error when system or socket operations fail - //! \throws HueException when response contained no body - //! \throws HueAPIResponseException when response contains an error - //! \throws nlohmann::json::parse_error when response could not be parsed - virtual int getButtonEvent(); - - //! \brief Const function to get button event - //! - //! \note This will not refresh the sensor state - //! \return integer representing the button event - virtual int getButtonEvent() const; - - //! \brief Function to get sensor status - //! - //! \return integer representing the status - //! \throws std::system_error when system or socket operations fail - //! \throws HueException when response contained no body - //! \throws HueAPIResponseException when response contains an error - //! \throws nlohmann::json::parse_error when response could not be parsed - virtual int getStatus(); - - //! \brief Const function to get sensor status - //! - //! \note This will not refresh the sensor state - //! \return integer representing the button event - virtual int getStatus() const; - - //! \brief Const function to check whether this sensor has a button event - //! - //! \return Bool that is true when the sensor has specified abilities and false - //! when not - virtual bool hasButtonEvent() const; - - //! \brief Const function to check whether this sensor has a status - //! - //! \return Bool that is true when the sensor has specified abilities and false - //! when not - virtual bool hasStatus() const; - -protected: - //! \brief Protected ctor that is used by \ref Hue class. - //! - //! \param id Integer that specifies the id of this sensor - //! \param commands HueCommandAPI for communication with the bridge - //! \param refreshDuration Time between refreshing the cached state. - HueSensor(int id, const HueCommandAPI& commands, std::chrono::steady_clock::duration refreshDuration); -}; -} // namespace hueplusplus - -#endif diff --git a/include/hueplusplus/LibConfig.h b/include/hueplusplus/LibConfig.h new file mode 100644 index 0000000..88924b2 --- /dev/null +++ b/include/hueplusplus/LibConfig.h @@ -0,0 +1,75 @@ +/** + \file LibConfig.h + Copyright Notice\n + Copyright (C) 2017 Jan Rogall - developer\n + Copyright (C) 2017 Moritz Wirger - developer\n + + This file is part of hueplusplus. + + hueplusplus is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + hueplusplus is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with hueplusplus. If not, see . +**/ + +#ifndef INCLUDE_HUEPLUSPLUS_HUE_CONFIG_H +#define INCLUDE_HUEPLUSPLUS_HUE_CONFIG_H + +#include + +namespace hueplusplus +{ +//! \brief Configurable delays +//! +//! Used to set all delays to zero when running tests. +class Config +{ +private: + using duration = std::chrono::steady_clock::duration; + +public: + //! \brief Delay for advanced alerts before the actual alert + duration getPreAlertDelay() const { return preAlertDelay; } + //! \brief Delay for advanced alerts after the actual alert + duration getPostAlertDelay() const { return postAlertDelay; } + + //! \brief Timeout for UPnP multicast request + duration getUPnPTimeout() const { return upnpTimeout; } + + //! \brief Delay between bridge requests + duration getBridgeRequestDelay() const { return bridgeRequestDelay; } + + //! \brief Timeout for Bridge::requestUsername, waits until link button was pressed + duration getRequestUsernameTimeout() const { return requestUsernameDelay; } + + //! \brief Interval in which username requests are attempted + duration getRequestUsernameAttemptInterval() const { return requestUsernameAttemptInterval; } + + //! \brief Get config instance + static Config& instance() + { + static Config c; + return c; + } +protected: + Config() = default; + +protected: + duration preAlertDelay = std::chrono::milliseconds(120); + duration postAlertDelay = std::chrono::milliseconds(1600); + duration upnpTimeout = std::chrono::seconds(5); + duration bridgeRequestDelay = std::chrono::milliseconds(100); + duration requestUsernameDelay = std::chrono::seconds(35); + duration requestUsernameAttemptInterval = std::chrono::seconds(1); +}; +} // namespace hueplusplus + +#endif diff --git a/include/hueplusplus/Light.h b/include/hueplusplus/Light.h new file mode 100644 index 0000000..45a8210 --- /dev/null +++ b/include/hueplusplus/Light.h @@ -0,0 +1,625 @@ +/** + \file Light.h + Copyright Notice\n + Copyright (C) 2017 Jan Rogall - developer\n + Copyright (C) 2017 Moritz Wirger - developer\n + + This file is part of hueplusplus. + + hueplusplus is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + hueplusplus is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with hueplusplus. If not, see . +**/ + +#ifndef INCLUDE_HUEPLUSPLUS_HUE_LIGHT_H +#define INCLUDE_HUEPLUSPLUS_HUE_LIGHT_H + +#include + +#include "APICache.h" +#include "BaseDevice.h" +#include "BrightnessStrategy.h" +#include "ColorHueStrategy.h" +#include "ColorTemperatureStrategy.h" +#include "HueCommandAPI.h" +#include "StateTransaction.h" + +#include "json/json.hpp" + +namespace hueplusplus +{ + +//! enum that specifies the color type of all HueLights +enum class ColorType +{ + UNDEFINED, //!< ColorType for this light is unknown or undefined + NONE, //!< light has no specific ColorType + GAMUT_A, //!< light uses Gamut A + GAMUT_B, //!< light uses Gamut B + GAMUT_C, //!< light uses Gamut C + TEMPERATURE, //!< light has color temperature control + GAMUT_A_TEMPERATURE, //!< light uses Gamut A and has color temperature control + GAMUT_B_TEMPERATURE, //!< light uses Gamut B and has color temperature control + GAMUT_C_TEMPERATURE //!< light uses Gamut C and has color temperature control +}; + +//! \brief Class for Hue Light fixtures +//! +//! Provides methods to query and control lights. +class Light : public BaseDevice +{ + friend class LightFactory; + friend class SimpleBrightnessStrategy; + friend class SimpleColorHueStrategy; + friend class ExtendedColorHueStrategy; + friend class SimpleColorTemperatureStrategy; + friend class ExtendedColorTemperatureStrategy; + +public: + //! \name General information + ///@{ + + //! \brief Const function that returns the luminaireuniqueid of the light + //! + //! \note Only working on bridges with versions starting at 1.9 + //! \return String containing the luminaireuniqueid or an empty string when the function is not supported + virtual std::string getLuminaireUId() const; + + //! \brief Const function that returns the color type of the light. + //! + //! \return ColorType containig the color type of the light + virtual ColorType getColorType() const; + + //! \brief Get gamut space of possible light colors + //! \returns Used gamut, or \ref gamut::maxGamut when unknown. + ColorGamut getColorGamut() const; + + ///@} + //! \name Light state + ///@{ + + //! \brief Function that turns the light on. + //! + //! \param transition Optional parameter to set the transition from current state to new, standard is 4 = 400ms + //! \return true on success + //! \throws std::system_error when system or socket operations fail + //! \throws HueException when response contained no body + //! \throws HueAPIResponseException when response contains an error + //! \throws nlohmann::json::parse_error when response could not be parsed + virtual bool On(uint8_t transition = 4); + + //! \brief Function that turns the light off. + //! + //! \param transition Optional parameter to set the transition from current state to new, standard is 4 = 400ms + //! \return Bool that is true on success + //! \throws std::system_error when system or socket operations fail + //! \throws HueException when response contained no body + //! \throws HueAPIResponseException when response contains an error + //! \throws nlohmann::json::parse_error when response could not be parsed + virtual bool Off(uint8_t transition = 4); + + //! \brief Function to check whether a light is on or off + //! + //! \return Bool that is true, when the light is on and false, when off + //! \throws std::system_error when system or socket operations fail + //! \throws HueException when response contained no body + //! \throws HueAPIResponseException when response contains an error + //! \throws nlohmann::json::parse_error when response could not be parsed + virtual bool isOn(); + + //! \brief Const function to check whether a light is on or off + //! + //! \note This will not refresh the light state + //! \return Bool that is true, when the light is on and false, when off + virtual bool isOn() const; + + //! \brief Const function to check whether this light has brightness control + //! + //! \return Bool that is true when the light has specified abilities and false + //! when not + virtual bool hasBrightnessControl() const { return brightnessStrategy != nullptr; }; + + //! \brief Const function to check whether this light has color temperature + //! control + //! + //! \return Bool that is true when the light has specified abilities and false + //! when not + virtual bool hasTemperatureControl() const { return colorTemperatureStrategy != nullptr; }; + + //! \brief Connst function to check whether this light has full color control + //! + //! \return Bool that is true when the light has specified abilities and false + //! when not + virtual bool hasColorControl() const { return colorHueStrategy != nullptr; }; + + //! \brief Const function that converts Kelvin to Mired. + //! + //! \param kelvin Unsigned integer value in Kelvin + //! \return Unsigned integer value in Mired + unsigned int KelvinToMired(unsigned int kelvin) const; + + //! \brief Const function that converts Mired to Kelvin. + //! + //! \param mired Unsigned integer value in Mired + //! \return Unsigned integer value in Kelvin + unsigned int MiredToKelvin(unsigned int mired) const; + + //! \brief Function that sets the brightness of this light. + //! + //! \note The brightness will only be set if the light has a reference to a + //! specific \ref BrightnessStrategy. The brightness can range from 0 = off to + //! 254 = fully lit. + //! \param bri Unsigned int that specifies the brightness + //! \param transition Optional parameter to set the transition from current state to new, standard is 4 = 400ms + //! \return Bool that is true on success + //! \throws std::system_error when system or socket operations fail + //! \throws HueException when response contained no body + //! \throws HueAPIResponseException when response contains an error + //! \throws nlohmann::json::parse_error when response could not be parsed + virtual bool setBrightness(unsigned int bri, uint8_t transition = 4) + { + if (brightnessStrategy) + { + return brightnessStrategy->setBrightness(bri, transition, *this); + } + return false; + }; + + //! \brief Const function that returns the brightness of this light. + //! + //! \note The brightness will only be returned if the light has a reference to + //! a specific \ref BrightnessStrategy. \note This will not refresh the light + //! state The brightness can range from 0 = off to 254 = fully lit. \return + //! Unsigned int that is 0 when function failed + virtual unsigned int getBrightness() const + { + if (brightnessStrategy) + { + return brightnessStrategy->getBrightness(*this); + } + return 0; + }; + + //! \brief Function that returns the brightness of this light. + //! + //! \note The brightness will only be returned if the light has a reference to + //! a specific \ref BrightnessStrategy. The brightness can range from 0 = off + //! to 254 = fully lit. + //! \return Unsigned int that is 0 when function failed + //! \throws std::system_error when system or socket operations fail + //! \throws HueException when response contained no body + //! \throws HueAPIResponseException when response contains an error + //! \throws nlohmann::json::parse_error when response could not be parsed + virtual unsigned int getBrightness() + { + if (brightnessStrategy) + { + return brightnessStrategy->getBrightness(*this); + } + return 0; + }; + + //! \brief Function that sets the color temperature of this light in mired. + //! + //! \note The color temperature will only be set if the light has a reference + //! to a specific \ref ColorTemperatureStrategy. The color temperature can + //! range from 153 to 500. + //! \param mired Unsigned int that specifies the color temperature in Mired + //! \param transition Optional parameter to set the transition from current state to new, standard is 4 = 400ms + //! \return Bool that is true on success + //! \throws std::system_error when system or socket operations fail + //! \throws HueException when response contained no body + //! \throws HueAPIResponseException when response contains an error + //! \throws nlohmann::json::parse_error when response could not be parsed + virtual bool setColorTemperature(unsigned int mired, uint8_t transition = 4) + { + if (colorTemperatureStrategy) + { + return colorTemperatureStrategy->setColorTemperature(mired, transition, *this); + } + return false; + }; + + //! \brief Const function that returns the current color temperature of the + //! light + //! + //! \note The color temperature will only be returned when the light has a + //! reference to a specific \ref ColorTemperatureStrategy. + //! \note This will not refresh the light state + //! The color temperature in mired ranges from 153 to 500 whereas 153 is cold + //! and 500 is warm. + //! \return Unsigned int representing the color temperature in mired or 0 when failed + virtual unsigned int getColorTemperature() const + { + if (colorTemperatureStrategy) + { + return colorTemperatureStrategy->getColorTemperature(*this); + } + return 0; + }; + + //! \brief Function that returns the current color temperature of the light + //! + //! \note The color temperature will only be returned when the light has a + //! reference to a specific \ref ColorTemperatureStrategy. + //! Updates the lights state by calling refreshState() + //! The color temperature in mired ranges from 153 to 500 whereas 153 is cold + //! and 500 is warm. + //! \return Unsigned int representing the color temperature in mired or 0 when failed + //! \throws std::system_error when system or socket operations fail + //! \throws HueException when response contained no body + //! \throws HueAPIResponseException when response contains an error + //! \throws nlohmann::json::parse_error when response could not be parsed + virtual unsigned int getColorTemperature() + { + if (colorTemperatureStrategy) + { + return colorTemperatureStrategy->getColorTemperature(*this); + } + return 0; + }; + + //! \brief Function to set the color of this light with specified hue. + //! + //! \note The color will only be set if the light has a reference to a + //! specific \ref ColorHueStrategy. The hue can range from 0 to 65535, whereas + //! 65535 and 0 are red, 25500 is green and 46920 is blue. + //! \param hue uint16_t that specifies the hue + //! \param transition Optional parameter to set the transition from current state to new, standard is 4 = 400ms + //! \return Bool that is true on success + //! \throws std::system_error when system or socket operations fail + //! \throws HueException when response contained no body + //! \throws HueAPIResponseException when response contains an error + //! \throws nlohmann::json::parse_error when response could not be parsed + virtual bool setColorHue(uint16_t hue, uint8_t transition = 4) + { + if (colorHueStrategy) + { + return colorHueStrategy->setColorHue(hue, transition, *this); + } + return false; + }; + + //! \brief Function to set the color of this light with specified saturation. + //! + //! \note The color will only be set if the light has a reference to a + //! specific \ref ColorHueStrategy. The saturation can range from 0 to 254, + //! whereas 0 is least saturated (white) and 254 is most saturated. + //! \param sat uint8_t that specifies the saturation + //! \param transition Optional parameter to set the transition from current state to new, standard is 4 = 400ms + //! \return Bool that is true on success + //! \throws std::system_error when system or socket operations fail + //! \throws HueException when response contained no body + //! \throws HueAPIResponseException when response contains an error + //! \throws nlohmann::json::parse_error when response could not be parsed + virtual bool setColorSaturation(uint8_t sat, uint8_t transition = 4) + { + if (colorHueStrategy) + { + return colorHueStrategy->setColorSaturation(sat, transition, *this); + } + return false; + }; + + //! \brief Function to set the color of this light with specified hue and + //! saturation. + //! + //! \note The color will only be set if the light has a reference to a + //! specific \ref ColorHueStrategy. + //! \param hueSat Color in hue and satuation. + //! \param transition Optional parameter to set the transition from current state to new, standard is 4 = 400ms. + //! \return Bool that is true on success + //! \throws std::system_error when system or socket operations fail + //! \throws HueException when response contained no body + //! \throws HueAPIResponseException when response contains an error + //! \throws nlohmann::json::parse_error when response could not be parsed + virtual bool setColorHueSaturation(const HueSaturation& hueSat, uint8_t transition = 4) + { + if (colorHueStrategy) + { + return colorHueStrategy->setColorHueSaturation(hueSat, transition, *this); + } + return false; + }; + + //! \brief Const function that returns the current color of the light as hue + //! and saturation + //! + //! \note The color hue and saturation will only be returned when the light + //! has a reference to a specific \ref ColorHueStrategy. + //! \note This will not refresh the light state + //! \return Current hue and saturation or {0,0} when failed + //! \throws std::system_error when system or socket operations fail + //! \throws HueException when response contained no body + //! \throws HueAPIResponseException when response contains an error + //! \throws nlohmann::json::parse_error when response could not be parsed + virtual HueSaturation getColorHueSaturation() const + { + if (colorHueStrategy) + { + return colorHueStrategy->getColorHueSaturation(*this); + } + return {}; + }; + + //! \brief Function that returns the current color of the light as hue and + //! saturation + //! + //! \note The color hue and saturation will only be returned when the light + //! has a reference to a specific \ref ColorHueStrategy. Updates the lights + //! state by calling refreshState() + //! \return Current hue and saturation or {0,0} when failed + //! \throws std::system_error when system or socket operations fail + //! \throws HueException when response contained no body + //! \throws HueAPIResponseException when response contains an error + //! \throws nlohmann::json::parse_error when response could not be parsed + virtual HueSaturation getColorHueSaturation() + { + if (colorHueStrategy) + { + return colorHueStrategy->getColorHueSaturation(*this); + } + return {}; + }; + + //! \brief Function to set the color of this light in CIE with specified x y. + //! + //! \note The color will only be set if the light has a reference to a + //! specific \ref ColorHueStrategy. The values of x and y are ranging from 0 to 1. + //! \param xy The color in XY and brightness + //! \param transition Optional parameter to set the transition from current state to new, standard is 4 = 400ms + //! \return Bool that is true on success + //! \throws std::system_error when system or socket operations fail + //! \throws HueException when response contained no body + //! \throws HueAPIResponseException when response contains an error + //! \throws nlohmann::json::parse_error when response could not be parsed + virtual bool setColorXY(const XYBrightness& xy, uint8_t transition = 4) + { + if (colorHueStrategy) + { + return colorHueStrategy->setColorXY(xy, transition, *this); + } + return false; + }; + + //! \brief Const function that returns the current color of the light as xy + //! + //! \note The color x and y will only be returned when the light has a + //! reference to a specific \ref ColorHueStrategy. + //! \note This does not update the lights state + //! \return XYBrightness with x, y and brightness or an empty one (all 0) when failed + virtual XYBrightness getColorXY() const + { + if (colorHueStrategy) + { + return colorHueStrategy->getColorXY(*this); + } + return {}; + }; + + //! \brief Function that returns the current color of the light as xy + //! + //! \note The color x and y will only be returned when the light has a + //! reference to a specific \ref ColorHueStrategy. + //! Updates the lights state by calling refreshState() + //! \return XYBrightness with x, y and brightness or an empty one (all 0) when failed + //! \throws std::system_error when system or socket operations fail + //! \throws HueException when response contained no body + //! \throws HueAPIResponseException when response contains an error + //! \throws nlohmann::json::parse_error when response could not be parsed + virtual XYBrightness getColorXY() + { + if (colorHueStrategy) + { + return colorHueStrategy->getColorXY(*this); + } + return {}; + } + + //! \brief Function to set the color of this light with red green and blue + //! values. + //! + //! \note The color will only be set if the light has a reference to a + //! specific \ref ColorHueStrategy. The values of red, green and blue are + //! ranging from 0 to 255. + //! \param rgb RGB color that will be mapped to the available color space + //! \param transition Optional parameter to set the transition from current state to new, standard is 4 = 400ms + //! \return Bool that is true on success + //! \throws std::system_error when system or socket operations fail + //! \throws HueException when response contained no body + //! \throws HueAPIResponseException when response contains an error + //! \throws nlohmann::json::parse_error when response could not be parsed + virtual bool setColorRGB(const RGB& rgb, uint8_t transition = 4) + { + if (colorHueStrategy) + { + return colorHueStrategy->setColorXY(rgb.toXY(getColorGamut()), transition, *this); + } + return false; + } + + //! \brief Function that lets the light perform one breath cycle. + //! + //! Can be used for locating a light. + //! \return bool that is true on success + //! \throws std::system_error when system or socket operations fail + //! \throws HueException when response contained no body + //! \throws HueAPIResponseException when response contains an error + //! \throws nlohmann::json::parse_error when response could not be parsed + virtual bool alert(); + + //! \brief Function that lets the light perform one breath cycle in specified + //! color temperature. + //! + //! \note The breath cylce will only be performed if the light has a reference + //! to a specific \ref ColorTemperatureStrategy. + //! \param mired Color temperature in mired + //! \return Bool that is true on success + //! \throws std::system_error when system or socket operations fail + //! \throws HueException when response contained no body + //! \throws HueAPIResponseException when response contains an error + //! \throws nlohmann::json::parse_error when response could not be parsed + virtual bool alertTemperature(unsigned int mired) + { + if (colorTemperatureStrategy) + { + return colorTemperatureStrategy->alertTemperature(mired, *this); + } + return false; + } + + //! \brief Function that lets the light perform one breath cycle in specified + //! color. + //! + //! \note The breath cylce will only be performed if the light has a reference + //! to a specific \ref ColorHueStrategy. + //! \param hueSat Color in hue and saturation + //! \return Bool that is true on success + //! \throws std::system_error when system or socket operations fail + //! \throws HueException when response contained no body + //! \throws HueAPIResponseException when response contains an error + //! \throws nlohmann::json::parse_error when response could not be parsed + virtual bool alertHueSaturation(const HueSaturation& hueSat) + { + if (colorHueStrategy) + { + return colorHueStrategy->alertHueSaturation(hueSat, *this); + } + return false; + } + + //! \brief Function that lets the light perform one breath cycle in specified + //! color. + //! + //! \note The breath cylce will only be performed if the light has a reference + //! to a specific \ref ColorHueStrategy. + //! \param xy The x,y coordinates in CIE and brightness + //! \return Bool that is true on success + //! \throws std::system_error when system or socket operations fail + //! \throws HueException when response contained no body + //! \throws HueAPIResponseException when response contains an error + //! \throws nlohmann::json::parse_error when response could not be parsed + virtual bool alertXY(const XYBrightness& xy) + { + if (colorHueStrategy) + { + return colorHueStrategy->alertXY(xy, *this); + } + return false; + } + + //! \brief Function to turn colorloop effect on/off. + //! + //! Notice this function will only be performed light has a reference to a + //! specific \ref ColorHueStrategy. The colorloop effect will loop through all + //! colors on current hue and saturation levels. Notice that none of the + //! setter functions check whether this feature is enabled and the colorloop + //! can only be disabled with this function or by simply calling + //! Off()/OffNoRefresh() and then On()/OnNoRefresh(), so you could + //! alternatively call Off() and then use any of the setter functions. + //! \param on bool that enables this feature when true and disables it when false + //! \return Bool that is true on success + //! \throws std::system_error when system or socket operations fail + //! \throws HueException when response contained no body + //! \throws HueAPIResponseException when response contains an error + //! \throws nlohmann::json::parse_error when response could not be parsed + virtual bool setColorLoop(bool on) + { + if (colorHueStrategy) + { + return colorHueStrategy->setColorLoop(on, *this); + } + return false; + } + + //! \brief Create a transaction for this light. + //! + //! The transaction can be used to change more than one value in one request. + //! Only use the functions supported by the current light type. + //! + //! Example usage: \code + //! light.transaction().setBrightness(240).setColorHue(5000).commit(); + //! \endcode + virtual StateTransaction transaction(); + + ///@} + +protected: + //! \brief Protected ctor that is used by \ref Bridge class. + //! + //! \param id Integer that specifies the id of this light + //! \param commands HueCommandAPI for communication with the bridge + //! + //! leaves strategies unset + Light(int id, const HueCommandAPI& commands); + + //! \brief Protected ctor that is used by \ref Bridge class, also sets + //! strategies. + //! + //! \param id Integer that specifies the id of this light + //! \param commands HueCommandAPI for communication with the bridge + //! \param brightnessStrategy Strategy for brightness. May be nullptr. + //! \param colorTempStrategy Strategy for color temperature. May be nullptr. + //! \param colorHueStrategy Strategy for color hue/saturation. May be nullptr. + //! \param refreshDuration Time between refreshing the cached state. + //! Can be 0 to always refresh, or steady_clock::duration::max() to never refresh. + //! \throws std::system_error when system or socket operations fail + //! \throws HueException when response contained no body + //! \throws HueAPIResponseException when response contains an error + //! \throws nlohmann::json::parse_error when response could not be parsed + Light(int id, const HueCommandAPI& commands, std::shared_ptr brightnessStrategy, + std::shared_ptr colorTempStrategy, + std::shared_ptr colorHueStrategy, + std::chrono::steady_clock::duration refreshDuration = std::chrono::seconds(10)); + + //! \brief Protected function that sets the brightness strategy. + //! + //! The strategy defines how specific commands that deal with brightness + //! control are executed \param strat a strategy of type \ref + //! BrightnessStrategy + virtual void setBrightnessStrategy(std::shared_ptr strat) + { + brightnessStrategy = std::move(strat); + }; + + //! \brief Protected function that sets the colorTemperature strategy. + //! + //! The strategy defines how specific commands that deal with colortemperature + //! control are executed \param strat a strategy of type \ref + //! ColorTemperatureStrategy + virtual void setColorTemperatureStrategy(std::shared_ptr strat) + { + colorTemperatureStrategy = std::move(strat); + }; + + //! \brief Protected function that sets the colorHue strategy. + //! + //! The strategy defines how specific commands that deal with color control + //! are executed \param strat a strategy of type \ref ColorHueStrategy + virtual void setColorHueStrategy(std::shared_ptr strat) + { + colorHueStrategy = std::move(strat); + }; + +protected: + ColorType colorType; //!< holds the \ref ColorType of the light + + std::shared_ptr + brightnessStrategy; //!< holds a reference to the strategy that handles brightness commands + std::shared_ptr + colorTemperatureStrategy; //!< holds a reference to the strategy that handles colortemperature commands + std::shared_ptr + colorHueStrategy; //!< holds a reference to the strategy that handles all color commands +}; +} // namespace hueplusplus + +#endif diff --git a/include/hueplusplus/Sensor.h b/include/hueplusplus/Sensor.h new file mode 100644 index 0000000..5e75bf5 --- /dev/null +++ b/include/hueplusplus/Sensor.h @@ -0,0 +1,97 @@ +/** + \file Sensor.h + Copyright Notice\n + Copyright (C) 2020 Stefan Herbrechtsmeier - developer\n + + This file is part of hueplusplus. + + hueplusplus is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + hueplusplus is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with hueplusplus. If not, see . +**/ + +#ifndef INCLUDE_HUEPLUSPLUS_HUE_SENSOR_H +#define INCLUDE_HUEPLUSPLUS_HUE_SENSOR_H + +#include + +#include "BaseDevice.h" +#include "HueCommandAPI.h" + +#include "json/json.hpp" + +namespace hueplusplus +{ +//! +//! Class for Hue sensors +//! +class Sensor : public BaseDevice +{ + friend class Bridge; + +public: + //! \brief std dtor + ~Sensor() = default; + + //! \brief Function to get button event + //! + //! \return integer representing the button event + //! \throws std::system_error when system or socket operations fail + //! \throws HueException when response contained no body + //! \throws HueAPIResponseException when response contains an error + //! \throws nlohmann::json::parse_error when response could not be parsed + virtual int getButtonEvent(); + + //! \brief Const function to get button event + //! + //! \note This will not refresh the sensor state + //! \return integer representing the button event + virtual int getButtonEvent() const; + + //! \brief Function to get sensor status + //! + //! \return integer representing the status + //! \throws std::system_error when system or socket operations fail + //! \throws HueException when response contained no body + //! \throws HueAPIResponseException when response contains an error + //! \throws nlohmann::json::parse_error when response could not be parsed + virtual int getStatus(); + + //! \brief Const function to get sensor status + //! + //! \note This will not refresh the sensor state + //! \return integer representing the button event + virtual int getStatus() const; + + //! \brief Const function to check whether this sensor has a button event + //! + //! \return Bool that is true when the sensor has specified abilities and false + //! when not + virtual bool hasButtonEvent() const; + + //! \brief Const function to check whether this sensor has a status + //! + //! \return Bool that is true when the sensor has specified abilities and false + //! when not + virtual bool hasStatus() const; + +protected: + //! \brief Protected ctor that is used by \ref Bridge class. + //! + //! \param id Integer that specifies the id of this sensor + //! \param commands HueCommandAPI for communication with the bridge + //! \param refreshDuration Time between refreshing the cached state. + Sensor(int id, const HueCommandAPI& commands, std::chrono::steady_clock::duration refreshDuration); +}; +} // namespace hueplusplus + +#endif diff --git a/include/hueplusplus/SimpleBrightnessStrategy.h b/include/hueplusplus/SimpleBrightnessStrategy.h index d2e2c1b..367a5ee 100644 --- a/include/hueplusplus/SimpleBrightnessStrategy.h +++ b/include/hueplusplus/SimpleBrightnessStrategy.h @@ -24,7 +24,7 @@ #define INCLUDE_HUEPLUSPLUS_SIMPLE_BRIGHTNESS_STRATEGY_H #include "BrightnessStrategy.h" -#include "HueLight.h" +#include "Light.h" namespace hueplusplus { @@ -39,19 +39,19 @@ public: //! \param transition The time it takes to fade to the new brightness in //! multiples of 100ms, 4 = 400ms and should be seen as the default \param //! light A reference of the light - bool setBrightness(unsigned int bri, uint8_t transition, HueLight& light) const override; + bool setBrightness(unsigned int bri, uint8_t transition, Light& light) const override; //! \brief Function that returns the current brightness of the light //! //! Updates the lights state by calling refreshState() //! \param light A reference of the light //! \return Unsigned int representing the brightness - unsigned int getBrightness(HueLight& light) const override; + unsigned int getBrightness(Light& light) const override; //! \brief Function that returns the current brightness of the light //! //! \note This does not update the lights state //! \param light A const reference of the light //! \return Unsigned int representing the brightness - unsigned int getBrightness(const HueLight& light) const override; + unsigned int getBrightness(const Light& light) const override; }; } // namespace hueplusplus diff --git a/include/hueplusplus/SimpleColorHueStrategy.h b/include/hueplusplus/SimpleColorHueStrategy.h index 5ee82e0..2117979 100644 --- a/include/hueplusplus/SimpleColorHueStrategy.h +++ b/include/hueplusplus/SimpleColorHueStrategy.h @@ -24,7 +24,7 @@ #define INCLUDE_HUEPLUSPLUS_SIMPLE_COLOR_HUE_STRATEGY_H #include "ColorHueStrategy.h" -#include "HueLight.h" +#include "Light.h" namespace hueplusplus { @@ -42,7 +42,7 @@ public: //! The time it takes to fade to the new color in multiples of 100ms, 4 = //! 400ms and should be seen as the default \param light A reference of the //! light - bool setColorHue(uint16_t hue, uint8_t transition, HueLight& light) const override; + bool setColorHue(uint16_t hue, uint8_t transition, Light& light) const override; //! \brief Function for changing a lights color in saturation with a specified //! transition. //! @@ -51,7 +51,7 @@ public: //! color \param transition The time it takes to fade to the new color in //! multiples of 100ms, 4 = 400ms and should be seen as the default \param //! light A reference of the light - bool setColorSaturation(uint8_t sat, uint8_t transition, HueLight& light) const override; + bool setColorSaturation(uint8_t sat, uint8_t transition, Light& light) const override; //! \brief Function for changing a lights color in hue and saturation format //! with a specified transition. //! @@ -59,7 +59,7 @@ public: //! \param transition The time it takes to fade to the new color in multiples of //! 100ms, 4 = 400ms and should be seen as the default //! \param light A reference of the light - bool setColorHueSaturation(const HueSaturation& hueSat, uint8_t transition, HueLight& light) const override; + bool setColorHueSaturation(const HueSaturation& hueSat, uint8_t transition, Light& light) const override; //! \brief Function for changing a lights color in CIE format with a specified //! transition. //! @@ -67,7 +67,7 @@ public: //! \param transition The time it takes to fade to the new color in multiples //! of 100ms, 4 = 400ms and should be seen as the default \param light A //! reference of the light - bool setColorXY(const XYBrightness& xy, uint8_t transition, HueLight& light) const override; + bool setColorXY(const XYBrightness& xy, uint8_t transition, Light& light) const override; //! \brief Function for turning on/off the color loop feature of a light. //! @@ -80,19 +80,19 @@ public: //! alternatively call Off() and then use any of the setter functions. //! \param on Boolean to turn this feature on or off, true/1 for on and //! false/0 for off \param light A reference of the light - bool setColorLoop(bool on, HueLight& light) const override; + bool setColorLoop(bool on, Light& light) const override; //! \brief Function that lets the light perform one breath cycle in the //! specified color. //! \param hueSat The color in hue and saturation //! \param light A reference of the light //! - //! Blocks for the time a \ref HueLight::alert() needs - bool alertHueSaturation(const HueSaturation& hueSat, HueLight& light) const override; + //! Blocks for the time a \ref Light::alert() needs + bool alertHueSaturation(const HueSaturation& hueSat, Light& light) const override; //! \brief Function that lets the light perform one breath cycle in the //! specified color. //! \param xy The color in XY and brightness //! \param light A reference of the light - bool alertXY(const XYBrightness& xy, HueLight& light) const override; + bool alertXY(const XYBrightness& xy, Light& light) const override; //! \brief Function that returns the current color of the light as hue and //! saturation //! @@ -100,7 +100,7 @@ public: //! \param light A reference of the light //! \return Pair containing the hue as first value and saturation as second //! value - HueSaturation getColorHueSaturation(HueLight& light) const override; + HueSaturation getColorHueSaturation(Light& light) const override; //! \brief Function that returns the current color of the light as hue and //! saturation //! @@ -108,19 +108,19 @@ public: //! \param light A const reference of the light //! \return Pair containing the hue as first value and saturation as second //! value - HueSaturation getColorHueSaturation(const HueLight& light) const override; + HueSaturation getColorHueSaturation(const Light& light) const override; //! \brief Function that returns the current color of the light as xy //! //! Updates the lights state by calling refreshState() //! \param light A reference of the light //! \return XY and brightness of current color - XYBrightness getColorXY(HueLight& light) const override; + XYBrightness getColorXY(Light& light) const override; //! \brief Function that returns the current color of the light as xy //! //! \note This does not update the lights state //! \param light A const reference of the light //! \return XY and brightness of current color - XYBrightness getColorXY(const HueLight& light) const override; + XYBrightness getColorXY(const Light& light) const override; }; } // namespace hueplusplus diff --git a/include/hueplusplus/SimpleColorTemperatureStrategy.h b/include/hueplusplus/SimpleColorTemperatureStrategy.h index 17a8148..469d995 100644 --- a/include/hueplusplus/SimpleColorTemperatureStrategy.h +++ b/include/hueplusplus/SimpleColorTemperatureStrategy.h @@ -24,7 +24,7 @@ #define INCLUDE_HUEPLUSPLUS_SIMPLE_COLOR_TEMPERATURE_STRATEGY_H #include "ColorTemperatureStrategy.h" -#include "HueLight.h" +#include "Light.h" namespace hueplusplus { @@ -40,29 +40,29 @@ public: //! transition The time it takes to fade to the new color in multiples of //! 100ms, 4 = 400ms and should be seen as the default \param light A //! reference of the light - bool setColorTemperature(unsigned int mired, uint8_t transition, HueLight& light) const override; + bool setColorTemperature(unsigned int mired, uint8_t transition, Light& light) const override; //! \brief Function that lets the light perform one breath cycle in the //! specified color. //! //! It uses this_thread::sleep_for to accomodate for the time an \ref - //! HueLight::alert() needs The color temperature in mired ranges from 153 to + //! Light::alert() needs The color temperature in mired ranges from 153 to //! 500 whereas 153 is cold and 500 is warm. \param mired The color //! temperature in mired \param light A reference of the light - bool alertTemperature(unsigned int mired, HueLight& light) const override; + bool alertTemperature(unsigned int mired, Light& light) const override; //! \brief Function that returns the current color temperature of the light //! //! Updates the lights state by calling refreshState() //! The color temperature in mired ranges from 153 to 500 whereas 153 is cold //! and 500 is warm. \param light A reference of the light \return Unsigned //! int representing the color temperature in mired - unsigned int getColorTemperature(HueLight& light) const override; + unsigned int getColorTemperature(Light& light) const override; //! \brief Function that returns the current color temperature of the light //! //! The color temperature in mired ranges from 153 to 500 whereas 153 is cold //! and 500 is warm. \note This does not update the lights state \param light //! A const reference of the light \return Unsigned int representing the color //! temperature in mired - unsigned int getColorTemperature(const HueLight& light) const override; + unsigned int getColorTemperature(const Light& light) const override; }; } // namespace hueplusplus diff --git a/include/hueplusplus/StateTransaction.h b/include/hueplusplus/StateTransaction.h index a265154..3e3502e 100644 --- a/include/hueplusplus/StateTransaction.h +++ b/include/hueplusplus/StateTransaction.h @@ -77,7 +77,7 @@ public: //! the current state are removed. This reduces load on the bridge. On the other hand, an outdated //! state might cause requests to be dropped unexpectedly. Has no effect on groups. //! \returns true on success or when no change was requested. - //! \note After changing the state of a HueLight or Group, + //! \note After changing the state of a Light or Group, //! refresh() must be called if the updated values are needed immediately. //! \throws std::system_error when system or socket operations fail //! \throws HueException when response contains no body diff --git a/src/Bridge.cpp b/src/Bridge.cpp new file mode 100644 index 0000000..d47ec8d --- /dev/null +++ b/src/Bridge.cpp @@ -0,0 +1,299 @@ +/** + \file Bridge.cpp + Copyright Notice\n + Copyright (C) 2017 Jan Rogall - developer\n + Copyright (C) 2017 Moritz Wirger - developer\n + + This file is part of hueplusplus. + + hueplusplus is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + hueplusplus is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with hueplusplus. If not, see . +**/ + +#include "hueplusplus/Bridge.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hueplusplus/LibConfig.h" +#include "hueplusplus/HueExceptionMacro.h" +#include "hueplusplus/UPnP.h" +#include "hueplusplus/Utils.h" + +namespace hueplusplus +{ +BridgeFinder::BridgeFinder(std::shared_ptr handler) : http_handler(std::move(handler)) { } + +std::vector BridgeFinder::FindBridges() const +{ + UPnP uplug; + std::vector> foundDevices = uplug.getDevices(http_handler); + + std::vector foundBridges; + for (const std::pair& p : foundDevices) + { + size_t found = p.second.find("IpBridge"); + if (found != std::string::npos) + { + BridgeIdentification bridge; + size_t start = p.first.find("//") + 2; + size_t length = p.first.find(":", start) - start; + bridge.ip = p.first.substr(start, length); + std::string desc + = http_handler->GETString("/description.xml", "application/xml", "", bridge.ip, bridge.port); + std::string mac = ParseDescription(desc); + if (!mac.empty()) + { + bridge.mac = NormalizeMac(mac); + foundBridges.push_back(std::move(bridge)); + } + } + } + return foundBridges; +} + +Bridge BridgeFinder::GetBridge(const BridgeIdentification& identification) +{ + std::string normalizedMac = NormalizeMac(identification.mac); + auto pos = usernames.find(normalizedMac); + if (pos != usernames.end()) + { + return Bridge(identification.ip, identification.port, pos->second, http_handler); + } + Bridge bridge(identification.ip, identification.port, "", http_handler); + bridge.requestUsername(); + if (bridge.getUsername().empty()) + { + std::cerr << "Failed to request username for ip " << identification.ip << std::endl; + throw HueException(CURRENT_FILE_INFO, "Failed to request username!"); + } + AddUsername(normalizedMac, bridge.getUsername()); + + return bridge; +} + +void BridgeFinder::AddUsername(const std::string& mac, const std::string& username) +{ + usernames[NormalizeMac(mac)] = username; +} + +const std::map& BridgeFinder::GetAllUsernames() const +{ + return usernames; +} + +std::string BridgeFinder::NormalizeMac(std::string input) +{ + // Remove any non alphanumeric characters (e.g. ':' and whitespace) + input.erase(std::remove_if(input.begin(), input.end(), [](char c) { return !std::isalnum(c, std::locale()); }), + input.end()); + // Convert to lower case + std::transform(input.begin(), input.end(), input.begin(), [](char c) { return std::tolower(c, std::locale()); }); + return input; +} + +std::string BridgeFinder::ParseDescription(const std::string& description) +{ + const char* model = "Philips hue bridge"; + const char* serialBegin = ""; + const char* serialEnd = ""; + if (description.find(model) != std::string::npos) + { + std::size_t begin = description.find(serialBegin); + std::size_t end = description.find(serialEnd, begin); + if (begin != std::string::npos && end != std::string::npos) + { + begin += std::strlen(serialBegin); + if (begin < description.size()) + { + std::string result = description.substr(begin, end - begin); + return result; + } + } + } + return std::string(); +} + +Bridge::Bridge(const std::string& ip, const int port, const std::string& username, + std::shared_ptr handler, std::chrono::steady_clock::duration refreshDuration) + : ip(ip), + username(username), + port(port), + http_handler(std::move(handler)), + refreshDuration(refreshDuration), + stateCache(std::make_shared( + "", HueCommandAPI(ip, port, username, http_handler), std::chrono::steady_clock::duration::max())), + lightList(stateCache, "lights", refreshDuration, + [factory = LightFactory(stateCache->getCommandAPI(), refreshDuration)]( + int id, const nlohmann::json& state) mutable { return factory.createLight(state, id); }), + groupList(stateCache, "groups", refreshDuration), + scheduleList(stateCache, "schedules", refreshDuration), + sceneList(stateCache, "scenes", refreshDuration), + sensorList(stateCache, "sensors", refreshDuration), + bridgeConfig(stateCache, refreshDuration) +{ } + +void Bridge::refresh() +{ + stateCache->refresh(); +} + +std::string Bridge::getBridgeIP() const +{ + return ip; +} + +int Bridge::getBridgePort() const +{ + return port; +} + +std::string Bridge::requestUsername() +{ + std::chrono::steady_clock::duration timeout = Config::instance().getRequestUsernameTimeout(); + std::chrono::steady_clock::duration checkInterval = Config::instance().getRequestUsernameAttemptInterval(); + std::cout << "Please press the link Button! You've got " + << std::chrono::duration_cast(timeout).count() << " secs!\n"; + + // when the link button was pressed we got 30 seconds to get our username for control + nlohmann::json request; + request["devicetype"] = "HuePlusPlus#User"; + + nlohmann::json answer; + std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now(); + // do-while loop to check at least once when timeout is 0 + do + { + std::this_thread::sleep_for(checkInterval); + answer = http_handler->POSTJson("/api", request, ip, port); + nlohmann::json jsonUser = utils::safeGetMember(answer, 0, "success", "username"); + if (jsonUser != nullptr) + { + // [{"success":{"username": ""}}] + username = jsonUser.get(); + // Update commands with new username and ip + setHttpHandler(http_handler); + std::cout << "Success! Link button was pressed!\n"; + std::cout << "Username is \"" << username << "\"\n"; + break; + } + else if (answer.size() > 0 && answer[0].count("error")) + { + HueAPIResponseException exception = HueAPIResponseException::Create(CURRENT_FILE_INFO, answer[0]); + // All errors except 101: Link button not pressed + if (exception.GetErrorNumber() != 101) + { + throw exception; + } + } + } while (std::chrono::steady_clock::now() - start < timeout); + + return username; +} + +std::string Bridge::getUsername() const +{ + return username; +} + +void Bridge::setIP(const std::string& ip) +{ + this->ip = ip; +} + +void Bridge::setPort(const int port) +{ + this->port = port; +} + +BridgeConfig& Bridge::config() +{ + return bridgeConfig; +} + +const BridgeConfig& Bridge::config() const +{ + return bridgeConfig; +} + +Bridge::LightList& Bridge::lights() +{ + return lightList; +} + +const Bridge::LightList& Bridge::lights() const +{ + return lightList; +} + +Bridge::GroupList& Bridge::groups() +{ + return groupList; +} + +const Bridge::GroupList& Bridge::groups() const +{ + return groupList; +} + +Bridge::ScheduleList& Bridge::schedules() +{ + return scheduleList; +} + +const Bridge::ScheduleList& Bridge::schedules() const +{ + return scheduleList; +} + +Bridge::SceneList& Bridge::scenes() +{ + return sceneList; +} + +const Bridge::SceneList& Bridge::scenes() const +{ + return sceneList; +} + +Bridge::SensorList& Bridge::sensors() +{ + return sensorList; +} + +const Bridge::SensorList& Bridge::sensors() const +{ + return sensorList; +} + +void Bridge::setHttpHandler(std::shared_ptr handler) +{ + http_handler = handler; + stateCache = std::make_shared("", HueCommandAPI(ip, port, username, handler), refreshDuration); + lightList = ResourceList(stateCache, "lights", refreshDuration, + [factory = LightFactory(stateCache->getCommandAPI(), refreshDuration)]( + int id, const nlohmann::json& state) mutable { return factory.createLight(state, id); }); + groupList = GroupList(stateCache, "groups", refreshDuration); + scheduleList = ScheduleList(stateCache, "schedules", refreshDuration); + sceneList = SceneList(stateCache, "scenes", refreshDuration); + sensorList = SensorList(stateCache, "sensors", refreshDuration); + bridgeConfig = BridgeConfig(stateCache, refreshDuration); + stateCache->refresh(); +} +} // namespace hueplusplus diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e31568a..89bef61 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -2,20 +2,20 @@ set(hueplusplus_SOURCES APICache.cpp BaseDevice.cpp BaseHttpHandler.cpp + Bridge.cpp BridgeConfig.cpp ColorUnits.cpp ExtendedColorHueStrategy.cpp ExtendedColorTemperatureStrategy.cpp Group.cpp - Hue.cpp HueCommandAPI.cpp HueDeviceTypes.cpp HueException.cpp - HueLight.cpp - HueSensor.cpp + Light.cpp ModelPictures.cpp Scene.cpp Schedule.cpp + Sensor.cpp SimpleBrightnessStrategy.cpp SimpleColorHueStrategy.cpp SimpleColorTemperatureStrategy.cpp diff --git a/src/ExtendedColorHueStrategy.cpp b/src/ExtendedColorHueStrategy.cpp index f16036d..8b666fe 100644 --- a/src/ExtendedColorHueStrategy.cpp +++ b/src/ExtendedColorHueStrategy.cpp @@ -26,11 +26,11 @@ #include #include -#include "hueplusplus/APIConfig.h" +#include "hueplusplus/LibConfig.h" namespace hueplusplus { -bool ExtendedColorHueStrategy::alertHueSaturation(const HueSaturation& hueSat, HueLight& light) const +bool ExtendedColorHueStrategy::alertHueSaturation(const HueSaturation& hueSat, Light& light) const { // Careful, only use state until any light function might refresh the value and invalidate the reference const nlohmann::json& state = light.state.getValue()["state"]; @@ -57,14 +57,14 @@ bool ExtendedColorHueStrategy::alertHueSaturation(const HueSaturation& hueSat, H } } -bool ExtendedColorHueStrategy::alertXY(const XYBrightness& xy, HueLight& light) const +bool ExtendedColorHueStrategy::alertXY(const XYBrightness& xy, Light& light) const { // Careful, only use state until any light function might refresh the value and invalidate the reference const nlohmann::json& state = light.state.getValue()["state"]; std::string cType = state["colormode"].get(); bool on = state["on"].get(); // const reference to prevent refreshes - const HueLight& cLight = light; + const Light& cLight = light; if (cType != "ct") { return SimpleColorHueStrategy::alertXY(xy, light); diff --git a/src/ExtendedColorTemperatureStrategy.cpp b/src/ExtendedColorTemperatureStrategy.cpp index d179ce1..947f68a 100644 --- a/src/ExtendedColorTemperatureStrategy.cpp +++ b/src/ExtendedColorTemperatureStrategy.cpp @@ -26,19 +26,19 @@ #include #include -#include "hueplusplus/APIConfig.h" +#include "hueplusplus/LibConfig.h" #include "hueplusplus/HueExceptionMacro.h" #include "hueplusplus/Utils.h" namespace hueplusplus { -bool ExtendedColorTemperatureStrategy::alertTemperature(unsigned int mired, HueLight& light) const +bool ExtendedColorTemperatureStrategy::alertTemperature(unsigned int mired, Light& light) const { // Careful, only use state until any light function might refresh the value and invalidate the reference const nlohmann::json& state = light.state.getValue()["state"]; std::string cType = state["colormode"].get(); bool on = state["on"].get(); - const HueLight& cLight = light; + const Light& cLight = light; if (cType == "ct") { return SimpleColorTemperatureStrategy::alertTemperature(mired, light); diff --git a/src/Hue.cpp b/src/Hue.cpp deleted file mode 100644 index 82e7301..0000000 --- a/src/Hue.cpp +++ /dev/null @@ -1,299 +0,0 @@ -/** - \file Hue.cpp - Copyright Notice\n - Copyright (C) 2017 Jan Rogall - developer\n - Copyright (C) 2017 Moritz Wirger - developer\n - - This file is part of hueplusplus. - - hueplusplus is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - hueplusplus is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with hueplusplus. If not, see . -**/ - -#include "hueplusplus/Hue.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "hueplusplus/APIConfig.h" -#include "hueplusplus/HueExceptionMacro.h" -#include "hueplusplus/UPnP.h" -#include "hueplusplus/Utils.h" - -namespace hueplusplus -{ -HueFinder::HueFinder(std::shared_ptr handler) : http_handler(std::move(handler)) { } - -std::vector HueFinder::FindBridges() const -{ - UPnP uplug; - std::vector> foundDevices = uplug.getDevices(http_handler); - - std::vector foundBridges; - for (const std::pair& p : foundDevices) - { - size_t found = p.second.find("IpBridge"); - if (found != std::string::npos) - { - HueIdentification bridge; - size_t start = p.first.find("//") + 2; - size_t length = p.first.find(":", start) - start; - bridge.ip = p.first.substr(start, length); - std::string desc - = http_handler->GETString("/description.xml", "application/xml", "", bridge.ip, bridge.port); - std::string mac = ParseDescription(desc); - if (!mac.empty()) - { - bridge.mac = NormalizeMac(mac); - foundBridges.push_back(std::move(bridge)); - } - } - } - return foundBridges; -} - -Hue HueFinder::GetBridge(const HueIdentification& identification) -{ - std::string normalizedMac = NormalizeMac(identification.mac); - auto pos = usernames.find(normalizedMac); - if (pos != usernames.end()) - { - return Hue(identification.ip, identification.port, pos->second, http_handler); - } - Hue bridge(identification.ip, identification.port, "", http_handler); - bridge.requestUsername(); - if (bridge.getUsername().empty()) - { - std::cerr << "Failed to request username for ip " << identification.ip << std::endl; - throw HueException(CURRENT_FILE_INFO, "Failed to request username!"); - } - AddUsername(normalizedMac, bridge.getUsername()); - - return bridge; -} - -void HueFinder::AddUsername(const std::string& mac, const std::string& username) -{ - usernames[NormalizeMac(mac)] = username; -} - -const std::map& HueFinder::GetAllUsernames() const -{ - return usernames; -} - -std::string HueFinder::NormalizeMac(std::string input) -{ - // Remove any non alphanumeric characters (e.g. ':' and whitespace) - input.erase(std::remove_if(input.begin(), input.end(), [](char c) { return !std::isalnum(c, std::locale()); }), - input.end()); - // Convert to lower case - std::transform(input.begin(), input.end(), input.begin(), [](char c) { return std::tolower(c, std::locale()); }); - return input; -} - -std::string HueFinder::ParseDescription(const std::string& description) -{ - const char* model = "Philips hue bridge"; - const char* serialBegin = ""; - const char* serialEnd = ""; - if (description.find(model) != std::string::npos) - { - std::size_t begin = description.find(serialBegin); - std::size_t end = description.find(serialEnd, begin); - if (begin != std::string::npos && end != std::string::npos) - { - begin += std::strlen(serialBegin); - if (begin < description.size()) - { - std::string result = description.substr(begin, end - begin); - return result; - } - } - } - return std::string(); -} - -Hue::Hue(const std::string& ip, const int port, const std::string& username, - std::shared_ptr handler, std::chrono::steady_clock::duration refreshDuration) - : ip(ip), - username(username), - port(port), - http_handler(std::move(handler)), - refreshDuration(refreshDuration), - stateCache(std::make_shared( - "", HueCommandAPI(ip, port, username, http_handler), std::chrono::steady_clock::duration::max())), - lightList(stateCache, "lights", refreshDuration, - [factory = HueLightFactory(stateCache->getCommandAPI(), refreshDuration)]( - int id, const nlohmann::json& state) mutable { return factory.createLight(state, id); }), - groupList(stateCache, "groups", refreshDuration), - scheduleList(stateCache, "schedules", refreshDuration), - sceneList(stateCache, "scenes", refreshDuration), - sensorList(stateCache, "sensors", refreshDuration), - bridgeConfig(stateCache, refreshDuration) -{ } - -void Hue::refresh() -{ - stateCache->refresh(); -} - -std::string Hue::getBridgeIP() const -{ - return ip; -} - -int Hue::getBridgePort() const -{ - return port; -} - -std::string Hue::requestUsername() -{ - std::chrono::steady_clock::duration timeout = Config::instance().getRequestUsernameTimeout(); - std::chrono::steady_clock::duration checkInterval = Config::instance().getRequestUsernameAttemptInterval(); - std::cout << "Please press the link Button! You've got " - << std::chrono::duration_cast(timeout).count() << " secs!\n"; - - // when the link button was pressed we got 30 seconds to get our username for control - nlohmann::json request; - request["devicetype"] = "HuePlusPlus#User"; - - nlohmann::json answer; - std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now(); - // do-while loop to check at least once when timeout is 0 - do - { - std::this_thread::sleep_for(checkInterval); - answer = http_handler->POSTJson("/api", request, ip, port); - nlohmann::json jsonUser = utils::safeGetMember(answer, 0, "success", "username"); - if (jsonUser != nullptr) - { - // [{"success":{"username": ""}}] - username = jsonUser.get(); - // Update commands with new username and ip - setHttpHandler(http_handler); - std::cout << "Success! Link button was pressed!\n"; - std::cout << "Username is \"" << username << "\"\n"; - break; - } - else if (answer.size() > 0 && answer[0].count("error")) - { - HueAPIResponseException exception = HueAPIResponseException::Create(CURRENT_FILE_INFO, answer[0]); - // All errors except 101: Link button not pressed - if (exception.GetErrorNumber() != 101) - { - throw exception; - } - } - } while (std::chrono::steady_clock::now() - start < timeout); - - return username; -} - -std::string Hue::getUsername() const -{ - return username; -} - -void Hue::setIP(const std::string& ip) -{ - this->ip = ip; -} - -void Hue::setPort(const int port) -{ - this->port = port; -} - -BridgeConfig& Hue::config() -{ - return bridgeConfig; -} - -const BridgeConfig& Hue::config() const -{ - return bridgeConfig; -} - -Hue::LightList& Hue::lights() -{ - return lightList; -} - -const Hue::LightList& Hue::lights() const -{ - return lightList; -} - -Hue::GroupList& Hue::groups() -{ - return groupList; -} - -const Hue::GroupList& Hue::groups() const -{ - return groupList; -} - -Hue::ScheduleList& Hue::schedules() -{ - return scheduleList; -} - -const Hue::ScheduleList& Hue::schedules() const -{ - return scheduleList; -} - -Hue::SceneList& Hue::scenes() -{ - return sceneList; -} - -const Hue::SceneList& Hue::scenes() const -{ - return sceneList; -} - -Hue::SensorList& Hue::sensors() -{ - return sensorList; -} - -const Hue::SensorList& Hue::sensors() const -{ - return sensorList; -} - -void Hue::setHttpHandler(std::shared_ptr handler) -{ - http_handler = handler; - stateCache = std::make_shared("", HueCommandAPI(ip, port, username, handler), refreshDuration); - lightList = ResourceList(stateCache, "lights", refreshDuration, - [factory = HueLightFactory(stateCache->getCommandAPI(), refreshDuration)]( - int id, const nlohmann::json& state) mutable { return factory.createLight(state, id); }); - groupList = GroupList(stateCache, "groups", refreshDuration); - scheduleList = ScheduleList(stateCache, "schedules", refreshDuration); - sceneList = SceneList(stateCache, "scenes", refreshDuration); - sensorList = SensorList(stateCache, "sensors", refreshDuration); - bridgeConfig = BridgeConfig(stateCache, refreshDuration); - stateCache->refresh(); -} -} // namespace hueplusplus diff --git a/src/HueCommandAPI.cpp b/src/HueCommandAPI.cpp index 13e0727..868db5c 100644 --- a/src/HueCommandAPI.cpp +++ b/src/HueCommandAPI.cpp @@ -24,7 +24,7 @@ #include -#include "hueplusplus/APIConfig.h" +#include "hueplusplus/LibConfig.h" #include "hueplusplus/HueExceptionMacro.h" namespace hueplusplus diff --git a/src/HueDeviceTypes.cpp b/src/HueDeviceTypes.cpp index 206707e..05a5925 100644 --- a/src/HueDeviceTypes.cpp +++ b/src/HueDeviceTypes.cpp @@ -59,7 +59,7 @@ const std::set& getGamutATypes() } } // namespace -HueLightFactory::HueLightFactory(const HueCommandAPI& commands, std::chrono::steady_clock::duration refreshDuration) +LightFactory::LightFactory(const HueCommandAPI& commands, std::chrono::steady_clock::duration refreshDuration) : commands(commands), refreshDuration(refreshDuration), simpleBrightness(std::make_shared()), @@ -69,7 +69,7 @@ HueLightFactory::HueLightFactory(const HueCommandAPI& commands, std::chrono::ste extendedColorHue(std::make_shared()) {} -HueLight HueLightFactory::createLight(const nlohmann::json& lightState, int id) +Light LightFactory::createLight(const nlohmann::json& lightState, int id) { std::string type = lightState.value("type", ""); // Ignore case @@ -77,39 +77,39 @@ HueLight HueLightFactory::createLight(const nlohmann::json& lightState, int id) if (type == "on/off light" || type == "on/off plug-in unit") { - HueLight light(id, commands, nullptr, nullptr, nullptr, refreshDuration); + Light light(id, commands, nullptr, nullptr, nullptr, refreshDuration); light.colorType = ColorType::NONE; return light; } else if (type == "dimmable light" || type =="dimmable plug-in unit") { - HueLight light(id, commands, simpleBrightness, nullptr, nullptr, refreshDuration); + Light light(id, commands, simpleBrightness, nullptr, nullptr, refreshDuration); light.colorType = ColorType::NONE; return light; } else if (type == "color temperature light") { - HueLight light(id, commands, simpleBrightness, simpleColorTemperature, nullptr, refreshDuration); + Light light(id, commands, simpleBrightness, simpleColorTemperature, nullptr, refreshDuration); light.colorType = ColorType::TEMPERATURE; return light; } else if (type == "color light") { - HueLight light(id, commands, simpleBrightness, nullptr, simpleColorHue, refreshDuration); + Light light(id, commands, simpleBrightness, nullptr, simpleColorHue, refreshDuration); light.colorType = getColorType(lightState, false); return light; } else if (type == "extended color light") { - HueLight light(id, commands, simpleBrightness, extendedColorTemperature, extendedColorHue, refreshDuration); + Light light(id, commands, simpleBrightness, extendedColorTemperature, extendedColorHue, refreshDuration); light.colorType = getColorType(lightState, true); return light; } - std::cerr << "Could not determine HueLight type:" << type << "!\n"; - throw HueException(CURRENT_FILE_INFO, "Could not determine HueLight type!"); + std::cerr << "Could not determine Light type:" << type << "!\n"; + throw HueException(CURRENT_FILE_INFO, "Could not determine Light type!"); } -ColorType HueLightFactory::getColorType(const nlohmann::json& lightState, bool hasCt) const +ColorType LightFactory::getColorType(const nlohmann::json& lightState, bool hasCt) const { // Try to get color type via capabilities const nlohmann::json& gamuttype = utils::safeGetMember(lightState, "capabilities", "control", "colorgamuttype"); @@ -150,8 +150,8 @@ ColorType HueLightFactory::getColorType(const nlohmann::json& lightState, bool h { return hasCt ? ColorType::GAMUT_C_TEMPERATURE : ColorType::GAMUT_C; } - std::cerr << "Could not determine HueLight color type:" << modelid << "!\n"; - throw HueException(CURRENT_FILE_INFO, "Could not determine HueLight color type!"); + std::cerr << "Could not determine Light color type:" << modelid << "!\n"; + throw HueException(CURRENT_FILE_INFO, "Could not determine Light color type!"); } } } // namespace hueplusplus diff --git a/src/HueLight.cpp b/src/HueLight.cpp deleted file mode 100644 index 3193cf0..0000000 --- a/src/HueLight.cpp +++ /dev/null @@ -1,127 +0,0 @@ -/** - \file HueLight.cpp - Copyright Notice\n - Copyright (C) 2017 Jan Rogall - developer\n - Copyright (C) 2017 Moritz Wirger - developer\n - - This file is part of hueplusplus. - - hueplusplus is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - hueplusplus is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with hueplusplus. If not, see . -**/ - -#include "hueplusplus/HueLight.h" - -#include -#include -#include - -#include "hueplusplus/HueExceptionMacro.h" -#include "hueplusplus/Utils.h" -#include "json/json.hpp" - -namespace hueplusplus -{ -bool HueLight::On(uint8_t transition) -{ - return transaction().setOn(true).setTransition(transition).commit(); -} - -bool HueLight::Off(uint8_t transition) -{ - return transaction().setOn(false).setTransition(transition).commit(); -} - -bool HueLight::isOn() -{ - return state.getValue().at("state").at("on").get(); -} - -bool HueLight::isOn() const -{ - return state.getValue().at("state").at("on").get(); -} - -std::string HueLight::getLuminaireUId() const -{ - return state.getValue().value("luminaireuniqueid", std::string()); -} - -ColorType HueLight::getColorType() const -{ - return colorType; -} - -ColorGamut HueLight::getColorGamut() const -{ - switch (colorType) - { - case ColorType::GAMUT_A: - case ColorType::GAMUT_A_TEMPERATURE: - return gamut::gamutA; - case ColorType::GAMUT_B: - case ColorType::GAMUT_B_TEMPERATURE: - return gamut::gamutB; - case ColorType::GAMUT_C: - case ColorType::GAMUT_C_TEMPERATURE: - return gamut::gamutC; - default: { - const nlohmann::json& capabilitiesGamut - = utils::safeGetMember(state.getValue(), "capabilities", "control", "colorgamut"); - if (capabilitiesGamut.is_array() && capabilitiesGamut.size() == 3) - { - // Other gamut - return ColorGamut {{capabilitiesGamut[0].at(0), capabilitiesGamut[0].at(1)}, - {capabilitiesGamut[1].at(0), capabilitiesGamut[1].at(1)}, - {capabilitiesGamut[2].at(0), capabilitiesGamut[2].at(1)}}; - } - // Unknown or no color light - return gamut::maxGamut; - } - } -} - -unsigned int HueLight::KelvinToMired(unsigned int kelvin) const -{ - return int(0.5f + (1000000 / kelvin)); -} - -unsigned int HueLight::MiredToKelvin(unsigned int mired) const -{ - return int(0.5f + (1000000 / mired)); -} - -bool HueLight::alert() -{ - return transaction().alert().commit(); -} - -StateTransaction HueLight::transaction() -{ - return StateTransaction( - state.getCommandAPI(), "/lights/" + std::to_string(id) + "/state", &state.getValue().at("state")); -} - -HueLight::HueLight(int id, const HueCommandAPI& commands) : HueLight(id, commands, nullptr, nullptr, nullptr) { } - -HueLight::HueLight(int id, const HueCommandAPI& commands, std::shared_ptr brightnessStrategy, - std::shared_ptr colorTempStrategy, - std::shared_ptr colorHueStrategy, std::chrono::steady_clock::duration refreshDuration) - : BaseDevice(id, commands, "/lights/", refreshDuration), - colorType(ColorType::NONE), - brightnessStrategy(std::move(brightnessStrategy)), - colorTemperatureStrategy(std::move(colorTempStrategy)), - colorHueStrategy(std::move(colorHueStrategy)) -{ -} -} // namespace hueplusplus diff --git a/src/HueSensor.cpp b/src/HueSensor.cpp deleted file mode 100644 index 567ac71..0000000 --- a/src/HueSensor.cpp +++ /dev/null @@ -1,77 +0,0 @@ -/** - \file HueSensor.cpp - Copyright Notice\n - Copyright (C) 2020 Stefan Herbrechtsmeier - developer\n - - This file is part of hueplusplus. - - hueplusplus is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - hueplusplus is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with hueplusplus. If not, see . -**/ - -#include "hueplusplus/HueSensor.h" - -#include "json/json.hpp" - -namespace hueplusplus -{ -int HueSensor::getButtonEvent() -{ - if (hasButtonEvent()) - { - return state.getValue().at("state").at("buttonevent"); - } - return 0; -} - -int HueSensor::getButtonEvent() const -{ - if (hasButtonEvent()) - { - return state.getValue().at("state").at("buttonevent"); - } - return 0; -} - -int HueSensor::getStatus() -{ - if (hasStatus()) - { - return state.getValue().at("state").at("status"); - } - return 0; -} - -int HueSensor::getStatus() const -{ - if (hasStatus()) - { - return state.getValue().at("state").at("status"); - } - return 0; -} - -bool HueSensor::hasButtonEvent() const -{ - return state.getValue().at("state").count("buttonevent") != 0; -} - -bool HueSensor::hasStatus() const -{ - return state.getValue().at("state").count("status") != 0; -} - -HueSensor::HueSensor(int id, const HueCommandAPI& commands, std::chrono::steady_clock::duration refreshDuration) - : BaseDevice(id, commands, "/sensors/", refreshDuration) -{ } -} // namespace hueplusplus diff --git a/src/Light.cpp b/src/Light.cpp new file mode 100644 index 0000000..28ecbd5 --- /dev/null +++ b/src/Light.cpp @@ -0,0 +1,127 @@ +/** + \file Light.cpp + Copyright Notice\n + Copyright (C) 2017 Jan Rogall - developer\n + Copyright (C) 2017 Moritz Wirger - developer\n + + This file is part of hueplusplus. + + hueplusplus is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + hueplusplus is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with hueplusplus. If not, see . +**/ + +#include "hueplusplus/Light.h" + +#include +#include +#include + +#include "hueplusplus/HueExceptionMacro.h" +#include "hueplusplus/Utils.h" +#include "json/json.hpp" + +namespace hueplusplus +{ +bool Light::On(uint8_t transition) +{ + return transaction().setOn(true).setTransition(transition).commit(); +} + +bool Light::Off(uint8_t transition) +{ + return transaction().setOn(false).setTransition(transition).commit(); +} + +bool Light::isOn() +{ + return state.getValue().at("state").at("on").get(); +} + +bool Light::isOn() const +{ + return state.getValue().at("state").at("on").get(); +} + +std::string Light::getLuminaireUId() const +{ + return state.getValue().value("luminaireuniqueid", std::string()); +} + +ColorType Light::getColorType() const +{ + return colorType; +} + +ColorGamut Light::getColorGamut() const +{ + switch (colorType) + { + case ColorType::GAMUT_A: + case ColorType::GAMUT_A_TEMPERATURE: + return gamut::gamutA; + case ColorType::GAMUT_B: + case ColorType::GAMUT_B_TEMPERATURE: + return gamut::gamutB; + case ColorType::GAMUT_C: + case ColorType::GAMUT_C_TEMPERATURE: + return gamut::gamutC; + default: { + const nlohmann::json& capabilitiesGamut + = utils::safeGetMember(state.getValue(), "capabilities", "control", "colorgamut"); + if (capabilitiesGamut.is_array() && capabilitiesGamut.size() == 3) + { + // Other gamut + return ColorGamut {{capabilitiesGamut[0].at(0), capabilitiesGamut[0].at(1)}, + {capabilitiesGamut[1].at(0), capabilitiesGamut[1].at(1)}, + {capabilitiesGamut[2].at(0), capabilitiesGamut[2].at(1)}}; + } + // Unknown or no color light + return gamut::maxGamut; + } + } +} + +unsigned int Light::KelvinToMired(unsigned int kelvin) const +{ + return int(0.5f + (1000000 / kelvin)); +} + +unsigned int Light::MiredToKelvin(unsigned int mired) const +{ + return int(0.5f + (1000000 / mired)); +} + +bool Light::alert() +{ + return transaction().alert().commit(); +} + +StateTransaction Light::transaction() +{ + return StateTransaction( + state.getCommandAPI(), "/lights/" + std::to_string(id) + "/state", &state.getValue().at("state")); +} + +Light::Light(int id, const HueCommandAPI& commands) : Light(id, commands, nullptr, nullptr, nullptr) { } + +Light::Light(int id, const HueCommandAPI& commands, std::shared_ptr brightnessStrategy, + std::shared_ptr colorTempStrategy, + std::shared_ptr colorHueStrategy, std::chrono::steady_clock::duration refreshDuration) + : BaseDevice(id, commands, "/lights/", refreshDuration), + colorType(ColorType::NONE), + brightnessStrategy(std::move(brightnessStrategy)), + colorTemperatureStrategy(std::move(colorTempStrategy)), + colorHueStrategy(std::move(colorHueStrategy)) +{ +} +} // namespace hueplusplus diff --git a/src/Sensor.cpp b/src/Sensor.cpp new file mode 100644 index 0000000..7f18e5e --- /dev/null +++ b/src/Sensor.cpp @@ -0,0 +1,77 @@ +/** + \file Sensor.cpp + Copyright Notice\n + Copyright (C) 2020 Stefan Herbrechtsmeier - developer\n + + This file is part of hueplusplus. + + hueplusplus is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + hueplusplus is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with hueplusplus. If not, see . +**/ + +#include "hueplusplus/Sensor.h" + +#include "json/json.hpp" + +namespace hueplusplus +{ +int Sensor::getButtonEvent() +{ + if (hasButtonEvent()) + { + return state.getValue().at("state").at("buttonevent"); + } + return 0; +} + +int Sensor::getButtonEvent() const +{ + if (hasButtonEvent()) + { + return state.getValue().at("state").at("buttonevent"); + } + return 0; +} + +int Sensor::getStatus() +{ + if (hasStatus()) + { + return state.getValue().at("state").at("status"); + } + return 0; +} + +int Sensor::getStatus() const +{ + if (hasStatus()) + { + return state.getValue().at("state").at("status"); + } + return 0; +} + +bool Sensor::hasButtonEvent() const +{ + return state.getValue().at("state").count("buttonevent") != 0; +} + +bool Sensor::hasStatus() const +{ + return state.getValue().at("state").count("status") != 0; +} + +Sensor::Sensor(int id, const HueCommandAPI& commands, std::chrono::steady_clock::duration refreshDuration) + : BaseDevice(id, commands, "/sensors/", refreshDuration) +{ } +} // namespace hueplusplus diff --git a/src/SimpleBrightnessStrategy.cpp b/src/SimpleBrightnessStrategy.cpp index 457c732..fab3b1f 100644 --- a/src/SimpleBrightnessStrategy.cpp +++ b/src/SimpleBrightnessStrategy.cpp @@ -31,18 +31,18 @@ namespace hueplusplus { -bool SimpleBrightnessStrategy::setBrightness(unsigned int bri, uint8_t transition, HueLight& light) const +bool SimpleBrightnessStrategy::setBrightness(unsigned int bri, uint8_t transition, Light& light) const { // Careful, only use state until any light function might refresh the value and invalidate the reference return light.transaction().setBrightness(bri).setTransition(transition).commit(); } -unsigned int SimpleBrightnessStrategy::getBrightness(HueLight& light) const +unsigned int SimpleBrightnessStrategy::getBrightness(Light& light) const { return light.state.getValue()["state"]["bri"].get(); } -unsigned int SimpleBrightnessStrategy::getBrightness(const HueLight& light) const +unsigned int SimpleBrightnessStrategy::getBrightness(const Light& light) const { return light.state.getValue()["state"]["bri"].get(); } diff --git a/src/SimpleColorHueStrategy.cpp b/src/SimpleColorHueStrategy.cpp index a9c3114..25294f9 100644 --- a/src/SimpleColorHueStrategy.cpp +++ b/src/SimpleColorHueStrategy.cpp @@ -26,45 +26,45 @@ #include #include -#include "hueplusplus/APIConfig.h" +#include "hueplusplus/LibConfig.h" #include "hueplusplus/HueExceptionMacro.h" #include "hueplusplus/Utils.h" namespace hueplusplus { -bool SimpleColorHueStrategy::setColorHue(uint16_t hue, uint8_t transition, HueLight& light) const +bool SimpleColorHueStrategy::setColorHue(uint16_t hue, uint8_t transition, Light& light) const { return light.transaction().setColorHue(hue).setTransition(transition).commit(); } -bool SimpleColorHueStrategy::setColorSaturation(uint8_t sat, uint8_t transition, HueLight& light) const +bool SimpleColorHueStrategy::setColorSaturation(uint8_t sat, uint8_t transition, Light& light) const { return light.transaction().setColorSaturation(sat).setTransition(transition).commit(); } bool SimpleColorHueStrategy::setColorHueSaturation( - const HueSaturation& hueSat, uint8_t transition, HueLight& light) const + const HueSaturation& hueSat, uint8_t transition, Light& light) const { return light.transaction().setColor(hueSat).setTransition(transition).commit(); } -bool SimpleColorHueStrategy::setColorXY(const XYBrightness& xy, uint8_t transition, HueLight& light) const +bool SimpleColorHueStrategy::setColorXY(const XYBrightness& xy, uint8_t transition, Light& light) const { return light.transaction().setColor(xy).setTransition(transition).commit(); } -bool SimpleColorHueStrategy::setColorLoop(bool on, HueLight& light) const +bool SimpleColorHueStrategy::setColorLoop(bool on, Light& light) const { return light.transaction().setColorLoop(true).commit(); } -bool SimpleColorHueStrategy::alertHueSaturation(const HueSaturation& hueSat, HueLight& light) const +bool SimpleColorHueStrategy::alertHueSaturation(const HueSaturation& hueSat, Light& light) const { // Careful, only use state until any light function might refresh the value and invalidate the reference const nlohmann::json& state = light.state.getValue()["state"]; std::string cType = state["colormode"].get(); bool on = state["on"].get(); - const HueLight& cLight = light; + const Light& cLight = light; if (cType == "hs") { HueSaturation oldHueSat = cLight.getColorHueSaturation(); @@ -101,14 +101,14 @@ bool SimpleColorHueStrategy::alertHueSaturation(const HueSaturation& hueSat, Hue } } -bool SimpleColorHueStrategy::alertXY(const XYBrightness& xy, HueLight& light) const +bool SimpleColorHueStrategy::alertXY(const XYBrightness& xy, Light& light) const { // Careful, only use state until any light function might refresh the value and invalidate the reference const nlohmann::json& state = light.state.getValue()["state"]; std::string cType = state["colormode"].get(); bool on = state["on"].get(); // const reference to prevent refreshes - const HueLight& cLight = light; + const Light& cLight = light; if (cType == "hs") { HueSaturation oldHueSat = cLight.getColorHueSaturation(); @@ -146,27 +146,27 @@ bool SimpleColorHueStrategy::alertXY(const XYBrightness& xy, HueLight& light) co } } -HueSaturation SimpleColorHueStrategy::getColorHueSaturation(HueLight& light) const +HueSaturation SimpleColorHueStrategy::getColorHueSaturation(Light& light) const { // Save value, so there are no inconsistent results if it is refreshed between two calls const nlohmann::json& state = light.state.getValue()["state"]; return HueSaturation {state["hue"].get(), state["sat"].get()}; } -HueSaturation SimpleColorHueStrategy::getColorHueSaturation(const HueLight& light) const +HueSaturation SimpleColorHueStrategy::getColorHueSaturation(const Light& light) const { return HueSaturation { light.state.getValue()["state"]["hue"].get(), light.state.getValue()["state"]["sat"].get()}; } -XYBrightness SimpleColorHueStrategy::getColorXY(HueLight& light) const +XYBrightness SimpleColorHueStrategy::getColorXY(Light& light) const { // Save value, so there are no inconsistent results if it is refreshed between two calls const nlohmann::json& state = light.state.getValue()["state"]; return XYBrightness {{state["xy"][0].get(), state["xy"][1].get()}, state["bri"].get() / 254.f}; } -XYBrightness SimpleColorHueStrategy::getColorXY(const HueLight& light) const +XYBrightness SimpleColorHueStrategy::getColorXY(const Light& light) const { const nlohmann::json& state = light.state.getValue()["state"]; return XYBrightness {{state["xy"][0].get(), state["xy"][1].get()}, state["bri"].get() / 254.f}; diff --git a/src/SimpleColorTemperatureStrategy.cpp b/src/SimpleColorTemperatureStrategy.cpp index fa51c7e..3da6a16 100644 --- a/src/SimpleColorTemperatureStrategy.cpp +++ b/src/SimpleColorTemperatureStrategy.cpp @@ -26,18 +26,18 @@ #include #include -#include "hueplusplus/APIConfig.h" +#include "hueplusplus/LibConfig.h" #include "hueplusplus/HueExceptionMacro.h" #include "hueplusplus/Utils.h" namespace hueplusplus { -bool SimpleColorTemperatureStrategy::setColorTemperature(unsigned int mired, uint8_t transition, HueLight& light) const +bool SimpleColorTemperatureStrategy::setColorTemperature(unsigned int mired, uint8_t transition, Light& light) const { return light.transaction().setColorTemperature(mired).setTransition(transition).commit(); } -bool SimpleColorTemperatureStrategy::alertTemperature(unsigned int mired, HueLight& light) const +bool SimpleColorTemperatureStrategy::alertTemperature(unsigned int mired, Light& light) const { // Careful, only use state until any light function might refresh the value and invalidate the reference const nlohmann::json& state = light.state.getValue()["state"]; @@ -64,12 +64,12 @@ bool SimpleColorTemperatureStrategy::alertTemperature(unsigned int mired, HueLig } } -unsigned int SimpleColorTemperatureStrategy::getColorTemperature(HueLight& light) const +unsigned int SimpleColorTemperatureStrategy::getColorTemperature(Light& light) const { return light.state.getValue()["state"]["ct"].get(); } -unsigned int SimpleColorTemperatureStrategy::getColorTemperature(const HueLight& light) const +unsigned int SimpleColorTemperatureStrategy::getColorTemperature(const Light& light) const { return light.state.getValue()["state"]["ct"].get(); } diff --git a/src/UPnP.cpp b/src/UPnP.cpp index 056b5ad..024c881 100644 --- a/src/UPnP.cpp +++ b/src/UPnP.cpp @@ -25,7 +25,7 @@ #include #include -#include "hueplusplus/APIConfig.h" +#include "hueplusplus/LibConfig.h" namespace hueplusplus { diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index cbfe729..f0317a3 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -32,15 +32,15 @@ target_compile_features(gtest PUBLIC cxx_std_14) set(TEST_SOURCES test_APICache.cpp test_BaseHttpHandler.cpp + test_Bridge.cpp test_BridgeConfig.cpp test_ColorUnits.cpp test_ExtendedColorHueStrategy.cpp test_ExtendedColorTemperatureStrategy.cpp test_Group.cpp - test_Hue.cpp - test_HueLight.cpp - test_HueLightFactory.cpp test_HueCommandAPI.cpp + test_Light.cpp + test_LightFactory.cpp test_Main.cpp test_UPnP.cpp test_ResourceList.cpp diff --git a/test/mocks/mock_HueLight.h b/test/mocks/mock_HueLight.h deleted file mode 100644 index d42c2b6..0000000 --- a/test/mocks/mock_HueLight.h +++ /dev/null @@ -1,130 +0,0 @@ -/** - \file mock_HueLight.h - Copyright Notice\n - Copyright (C) 2017 Jan Rogall - developer\n - Copyright (C) 2017 Moritz Wirger - developer\n - - This file is part of hueplusplus. - - hueplusplus is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - hueplusplus is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with hueplusplus. If not, see . -**/ - -#ifndef _MOCK_HUE_LIGHT_H -#define _MOCK_HUE_LIGHT_H - -#include -#include - -#include - -#include "../testhelper.h" -#include "hueplusplus/HueLight.h" -#include "json/json.hpp" - -//! Mock Class -class MockHueLight : public hueplusplus::HueLight -{ -public: - MockHueLight(std::shared_ptr handler) - : HueLight(1, hueplusplus::HueCommandAPI(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler), nullptr, - nullptr, nullptr, std::chrono::steady_clock::duration::max()) - { - // Set refresh duration to max, so random refreshes do not hinder the test setups - } - - nlohmann::json& getState() { return state.getValue(); } - - MOCK_METHOD1(On, bool(uint8_t transition)); - - MOCK_METHOD1(Off, bool(uint8_t transition)); - - MOCK_METHOD0(isOn, bool()); - - MOCK_CONST_METHOD0(isOn, bool()); - - MOCK_CONST_METHOD0(getId, int()); - - MOCK_CONST_METHOD0(getType, std::string()); - - MOCK_METHOD0(getName, std::string()); - - MOCK_CONST_METHOD0(getName, std::string()); - - MOCK_CONST_METHOD0(getModelId, std::string()); - - MOCK_CONST_METHOD0(getUId, std::string()); - - MOCK_CONST_METHOD0(getManufacturername, std::string()); - - MOCK_CONST_METHOD0(getLuminaireUId, std::string()); - - MOCK_METHOD0(getSwVersion, std::string()); - - MOCK_CONST_METHOD0(getSwVersion, std::string()); - - MOCK_METHOD1(setName, bool(std::string& name)); - - MOCK_CONST_METHOD0(getColorType, hueplusplus::ColorType()); - - MOCK_CONST_METHOD0(hasBrightnessControl, bool()); - - MOCK_CONST_METHOD0(hasTemperatureControl, bool()); - - MOCK_CONST_METHOD0(hasColorControl, bool()); - - MOCK_METHOD2(setBrightness, bool(unsigned int bri, uint8_t transition)); - - MOCK_CONST_METHOD0(getBrightness, unsigned int()); - - MOCK_METHOD0(getBrightness, unsigned int()); - - MOCK_METHOD2(setColorTemperature, bool(unsigned int mired, uint8_t transition)); - - MOCK_CONST_METHOD0(getColorTemperature, unsigned int()); - - MOCK_METHOD0(getColorTemperature, unsigned int()); - - MOCK_METHOD2(setColorHue, bool(uint16_t hue, uint8_t transition)); - - MOCK_METHOD2(setColorSaturation, bool(uint8_t sat, uint8_t transition)); - - MOCK_METHOD2(setColorHueSaturation, bool(const hueplusplus::HueSaturation& hueSat, uint8_t transition)); - - MOCK_CONST_METHOD0(getColorHueSaturation, hueplusplus::HueSaturation()); - - MOCK_METHOD0(getColorHueSaturation, hueplusplus::HueSaturation()); - - MOCK_METHOD2(setColorXY, bool(const hueplusplus::XYBrightness& xy, uint8_t transition)); - - MOCK_CONST_METHOD0(getColorXY, hueplusplus::XYBrightness()); - - MOCK_METHOD0(getColorXY, hueplusplus::XYBrightness()); - - MOCK_METHOD2(setColorRGB, bool(const hueplusplus::RGB& rgb, uint8_t transition)); - - MOCK_METHOD0(alert, bool()); - - MOCK_METHOD1(alertTemperature, bool(unsigned int mired)); - - MOCK_METHOD1(alertHueSaturation, bool(const hueplusplus::HueSaturation& hueSat)); - - MOCK_METHOD1(alertXY, bool(const hueplusplus::XYBrightness& xy)); - - MOCK_METHOD1(setColorLoop, bool(bool on)); - - MOCK_METHOD3(sendPutRequest, - nlohmann::json(const nlohmann::json& request, const std::string& subPath, hueplusplus::FileInfo fileInfo)); -}; - -#endif diff --git a/test/mocks/mock_Light.h b/test/mocks/mock_Light.h new file mode 100644 index 0000000..24112bb --- /dev/null +++ b/test/mocks/mock_Light.h @@ -0,0 +1,130 @@ +/** + \file mock_Light.h + Copyright Notice\n + Copyright (C) 2017 Jan Rogall - developer\n + Copyright (C) 2017 Moritz Wirger - developer\n + + This file is part of hueplusplus. + + hueplusplus is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + hueplusplus is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with hueplusplus. If not, see . +**/ + +#ifndef _MOCK_HUE_LIGHT_H +#define _MOCK_HUE_LIGHT_H + +#include +#include + +#include + +#include "../testhelper.h" +#include "hueplusplus/Light.h" +#include "json/json.hpp" + +//! Mock Class +class MockLight : public hueplusplus::Light +{ +public: + MockLight(std::shared_ptr handler) + : Light(1, hueplusplus::HueCommandAPI(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler), nullptr, + nullptr, nullptr, std::chrono::steady_clock::duration::max()) + { + // Set refresh duration to max, so random refreshes do not hinder the test setups + } + + nlohmann::json& getState() { return state.getValue(); } + + MOCK_METHOD1(On, bool(uint8_t transition)); + + MOCK_METHOD1(Off, bool(uint8_t transition)); + + MOCK_METHOD0(isOn, bool()); + + MOCK_CONST_METHOD0(isOn, bool()); + + MOCK_CONST_METHOD0(getId, int()); + + MOCK_CONST_METHOD0(getType, std::string()); + + MOCK_METHOD0(getName, std::string()); + + MOCK_CONST_METHOD0(getName, std::string()); + + MOCK_CONST_METHOD0(getModelId, std::string()); + + MOCK_CONST_METHOD0(getUId, std::string()); + + MOCK_CONST_METHOD0(getManufacturername, std::string()); + + MOCK_CONST_METHOD0(getLuminaireUId, std::string()); + + MOCK_METHOD0(getSwVersion, std::string()); + + MOCK_CONST_METHOD0(getSwVersion, std::string()); + + MOCK_METHOD1(setName, bool(std::string& name)); + + MOCK_CONST_METHOD0(getColorType, hueplusplus::ColorType()); + + MOCK_CONST_METHOD0(hasBrightnessControl, bool()); + + MOCK_CONST_METHOD0(hasTemperatureControl, bool()); + + MOCK_CONST_METHOD0(hasColorControl, bool()); + + MOCK_METHOD2(setBrightness, bool(unsigned int bri, uint8_t transition)); + + MOCK_CONST_METHOD0(getBrightness, unsigned int()); + + MOCK_METHOD0(getBrightness, unsigned int()); + + MOCK_METHOD2(setColorTemperature, bool(unsigned int mired, uint8_t transition)); + + MOCK_CONST_METHOD0(getColorTemperature, unsigned int()); + + MOCK_METHOD0(getColorTemperature, unsigned int()); + + MOCK_METHOD2(setColorHue, bool(uint16_t hue, uint8_t transition)); + + MOCK_METHOD2(setColorSaturation, bool(uint8_t sat, uint8_t transition)); + + MOCK_METHOD2(setColorHueSaturation, bool(const hueplusplus::HueSaturation& hueSat, uint8_t transition)); + + MOCK_CONST_METHOD0(getColorHueSaturation, hueplusplus::HueSaturation()); + + MOCK_METHOD0(getColorHueSaturation, hueplusplus::HueSaturation()); + + MOCK_METHOD2(setColorXY, bool(const hueplusplus::XYBrightness& xy, uint8_t transition)); + + MOCK_CONST_METHOD0(getColorXY, hueplusplus::XYBrightness()); + + MOCK_METHOD0(getColorXY, hueplusplus::XYBrightness()); + + MOCK_METHOD2(setColorRGB, bool(const hueplusplus::RGB& rgb, uint8_t transition)); + + MOCK_METHOD0(alert, bool()); + + MOCK_METHOD1(alertTemperature, bool(unsigned int mired)); + + MOCK_METHOD1(alertHueSaturation, bool(const hueplusplus::HueSaturation& hueSat)); + + MOCK_METHOD1(alertXY, bool(const hueplusplus::XYBrightness& xy)); + + MOCK_METHOD1(setColorLoop, bool(bool on)); + + MOCK_METHOD3(sendPutRequest, + nlohmann::json(const nlohmann::json& request, const std::string& subPath, hueplusplus::FileInfo fileInfo)); +}; + +#endif diff --git a/test/test_Bridge.cpp b/test/test_Bridge.cpp new file mode 100644 index 0000000..c214b8c --- /dev/null +++ b/test/test_Bridge.cpp @@ -0,0 +1,549 @@ +/** + \file test_Bridge.cpp + Copyright Notice\n + Copyright (C) 2017 Jan Rogall - developer\n + Copyright (C) 2017 Moritz Wirger - developer\n + + This file is part of hueplusplus. + + hueplusplus is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + hueplusplus is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with hueplusplus. If not, see . +**/ + +#include +#include + +#include +#include + +#include "testhelper.h" + +#include "hueplusplus/Bridge.h" +#include "hueplusplus/LibConfig.h" +#include "json/json.hpp" +#include "mocks/mock_HttpHandler.h" + +using namespace hueplusplus; + +class HueFinderTest : public ::testing::Test +{ +protected: + std::shared_ptr handler; + +protected: + HueFinderTest() : handler(std::make_shared()) + { + using namespace ::testing; + + EXPECT_CALL(*handler, + sendMulticast("M-SEARCH * HTTP/1.1\r\nHOST: 239.255.255.250:1900\r\nMAN: " + "\"ssdp:discover\"\r\nMX: 5\r\nST: ssdp:all\r\n\r\n", + "239.255.255.250", 1900, Config::instance().getUPnPTimeout())) + .Times(AtLeast(1)) + .WillRepeatedly(Return(getMulticastReply())); + + EXPECT_CALL(*handler, GETString("/description.xml", "application/xml", "", "192.168.2.1", getBridgePort())) + .Times(0); + + EXPECT_CALL(*handler, GETString("/description.xml", "application/xml", "", getBridgeIp(), getBridgePort())) + .Times(AtLeast(1)) + .WillRepeatedly(Return(getBridgeXml())); + } + ~HueFinderTest() {}; +}; + +TEST_F(HueFinderTest, FindBridges) +{ + BridgeFinder finder(handler); + std::vector bridges = finder.FindBridges(); + + BridgeFinder::BridgeIdentification bridge_to_comp; + bridge_to_comp.ip = getBridgeIp(); + bridge_to_comp.port = getBridgePort(); + bridge_to_comp.mac = getBridgeMac(); + + EXPECT_EQ(bridges.size(), 1) << "BridgeFinder found more than one Bridge"; + EXPECT_EQ(bridges[0].ip, bridge_to_comp.ip) << "BridgeIdentification ip does not match"; + EXPECT_EQ(bridges[0].port, bridge_to_comp.port) << "BridgeIdentification port does not match"; + EXPECT_EQ(bridges[0].mac, bridge_to_comp.mac) << "BridgeIdentification mac does not match"; + + // Test invalid description + EXPECT_CALL(*handler, GETString("/description.xml", "application/xml", "", getBridgeIp(), getBridgePort())) + .Times(1) + .WillOnce(::testing::Return("invalid stuff")); + bridges = finder.FindBridges(); + EXPECT_TRUE(bridges.empty()); +} + +TEST_F(HueFinderTest, GetBridge) +{ + using namespace ::testing; + nlohmann::json request {{"devicetype", "HuePlusPlus#User"}}; + + nlohmann::json errorResponse + = {{{"error", {{"type", 101}, {"address", ""}, {"description", "link button not pressed"}}}}}; + + EXPECT_CALL(*handler, POSTJson("/api", request, getBridgeIp(), getBridgePort())) + .Times(AtLeast(1)) + .WillRepeatedly(Return(errorResponse)); + + BridgeFinder finder(handler); + std::vector bridges = finder.FindBridges(); + + ASSERT_THROW(finder.GetBridge(bridges[0]), HueException); + + nlohmann::json successResponse = {{{"success", {{"username", getBridgeUsername()}}}}}; + + EXPECT_CALL(*handler, POSTJson("/api", request, getBridgeIp(), getBridgePort())) + .Times(1) + .WillOnce(Return(successResponse)); + + finder = BridgeFinder(handler); + bridges = finder.FindBridges(); + + Bridge test_bridge = finder.GetBridge(bridges[0]); + + EXPECT_EQ(test_bridge.getBridgeIP(), getBridgeIp()) << "Bridge IP not matching"; + EXPECT_EQ(test_bridge.getBridgePort(), getBridgePort()) << "Bridge Port not matching"; + EXPECT_EQ(test_bridge.getUsername(), getBridgeUsername()) << "Bridge username not matching"; + + Mock::VerifyAndClearExpectations(handler.get()); +} + +TEST_F(HueFinderTest, AddUsername) +{ + BridgeFinder finder(handler); + std::vector bridges = finder.FindBridges(); + + finder.AddUsername(bridges[0].mac, getBridgeUsername()); + Bridge test_bridge = finder.GetBridge(bridges[0]); + + EXPECT_EQ(test_bridge.getBridgeIP(), getBridgeIp()) << "Bridge IP not matching"; + EXPECT_EQ(test_bridge.getBridgePort(), getBridgePort()) << "Bridge Port not matching"; + EXPECT_EQ(test_bridge.getUsername(), getBridgeUsername()) << "Bridge username not matching"; +} + +TEST_F(HueFinderTest, GetAllUsernames) +{ + BridgeFinder finder(handler); + std::vector bridges = finder.FindBridges(); + + finder.AddUsername(bridges[0].mac, getBridgeUsername()); + + std::map users = finder.GetAllUsernames(); + EXPECT_EQ(users[getBridgeMac()], getBridgeUsername()) << "Username of MAC:" << getBridgeMac() << "not matching"; +} + +TEST(Bridge, Constructor) +{ + std::shared_ptr handler = std::make_shared(); + Bridge test_bridge(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler); + + EXPECT_EQ(test_bridge.getBridgeIP(), getBridgeIp()) << "Bridge IP not matching"; + EXPECT_EQ(test_bridge.getBridgePort(), getBridgePort()) << "Bridge Port not matching"; + EXPECT_EQ(test_bridge.getUsername(), getBridgeUsername()) << "Bridge username not matching"; +} + +TEST(Bridge, requestUsername) +{ + using namespace ::testing; + std::shared_ptr handler = std::make_shared(); + nlohmann::json request {{"devicetype", "HuePlusPlus#User"}}; + + { + nlohmann::json errorResponse + = {{{"error", {{"type", 101}, {"address", ""}, {"description", "link button not pressed"}}}}}; + + EXPECT_CALL(*handler, POSTJson("/api", request, getBridgeIp(), getBridgePort())) + .Times(AtLeast(1)) + .WillRepeatedly(Return(errorResponse)); + + Bridge test_bridge(getBridgeIp(), getBridgePort(), "", handler); + + std::string username = test_bridge.requestUsername(); + EXPECT_EQ(username, "") << "Returned username not matching"; + EXPECT_EQ(test_bridge.getUsername(), "") << "Bridge username not matching"; + } + + { + // Other error code causes exception + int otherError = 1; + nlohmann::json exceptionResponse + = {{{"error", {{"type", otherError}, {"address", ""}, {"description", "some error"}}}}}; + Bridge testBridge(getBridgeIp(), getBridgePort(), "", handler); + + EXPECT_CALL(*handler, POSTJson("/api", request, getBridgeIp(), getBridgePort())) + .WillOnce(Return(exceptionResponse)); + + try + { + testBridge.requestUsername(); + FAIL() << "requestUsername did not throw"; + } + catch (const HueAPIResponseException& e) + { + EXPECT_EQ(e.GetErrorNumber(), otherError); + } + catch (const std::exception& e) + { + FAIL() << "wrong exception: " << e.what(); + } + } + + { + nlohmann::json successResponse = {{{"success", {{"username", getBridgeUsername()}}}}}; + EXPECT_CALL(*handler, POSTJson("/api", request, getBridgeIp(), getBridgePort())) + .Times(1) + .WillRepeatedly(Return(successResponse)); + + Bridge test_bridge(getBridgeIp(), getBridgePort(), "", handler); + + nlohmann::json hue_bridge_state {{"lights", {}}}; + EXPECT_CALL( + *handler, GETJson("/api/" + getBridgeUsername(), nlohmann::json::object(), getBridgeIp(), getBridgePort())) + .Times(1) + .WillOnce(Return(hue_bridge_state)); + std::string username = test_bridge.requestUsername(); + + EXPECT_EQ(username, test_bridge.getUsername()) << "Returned username not matching"; + EXPECT_EQ(test_bridge.getBridgeIP(), getBridgeIp()) << "Bridge IP not matching"; + EXPECT_EQ(test_bridge.getUsername(), getBridgeUsername()) << "Bridge username not matching"; + } +} + +TEST(Bridge, setIP) +{ + std::shared_ptr handler = std::make_shared(); + Bridge test_bridge(getBridgeIp(), getBridgePort(), "", handler); + EXPECT_EQ(test_bridge.getBridgeIP(), getBridgeIp()) << "Bridge IP not matching after initialization"; + test_bridge.setIP("192.168.2.112"); + EXPECT_EQ(test_bridge.getBridgeIP(), "192.168.2.112") << "Bridge IP not matching after setting it"; +} + +TEST(Bridge, setPort) +{ + std::shared_ptr handler = std::make_shared(); + Bridge test_bridge = Bridge(getBridgeIp(), getBridgePort(), "", handler); + EXPECT_EQ(test_bridge.getBridgePort(), getBridgePort()) << "Bridge Port not matching after initialization"; + test_bridge.setPort(81); + EXPECT_EQ(test_bridge.getBridgePort(), 81) << "Bridge Port not matching after setting it"; +} + +TEST(Bridge, getLight) +{ + using namespace ::testing; + std::shared_ptr handler = std::make_shared(); + EXPECT_CALL( + *handler, GETJson("/api/" + getBridgeUsername(), nlohmann::json::object(), getBridgeIp(), getBridgePort())) + .Times(1); + + Bridge test_bridge(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler); + + // Test exception + ASSERT_THROW(test_bridge.lights().get(1), HueException); + + nlohmann::json hue_bridge_state {{"lights", + {{"1", + {{"state", + {{"on", true}, {"bri", 254}, {"ct", 366}, {"alert", "none"}, {"colormode", "ct"}, + {"reachable", true}}}, + {"swupdate", {{"state", "noupdates"}, {"lastinstall", nullptr}}}, {"type", "Color temperature light"}, + {"name", "Hue ambiance lamp 1"}, {"modelid", "LTW001"}, {"manufacturername", "Philips"}, + {"uniqueid", "00:00:00:00:00:00:00:00-00"}, {"swversion", "5.50.1.19085"}}}}}}; + + EXPECT_CALL( + *handler, GETJson("/api/" + getBridgeUsername(), nlohmann::json::object(), getBridgeIp(), getBridgePort())) + .Times(1) + .WillOnce(Return(hue_bridge_state)); + + EXPECT_CALL(*handler, + GETJson("/api/" + getBridgeUsername() + "/lights/1", nlohmann::json::object(), getBridgeIp(), getBridgePort())) + .Times(AtLeast(1)) + .WillRepeatedly(Return(hue_bridge_state["lights"]["1"])); + + // Refresh cache + test_bridge = Bridge(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler); + + // Test when correct data is sent + Light test_light_1 = test_bridge.lights().get(1); + EXPECT_EQ(test_light_1.getName(), "Hue ambiance lamp 1"); + EXPECT_EQ(test_light_1.getColorType(), ColorType::TEMPERATURE); + + // Test again to check whether light is returned directly -> interesting for + // code coverage test + test_light_1 = test_bridge.lights().get(1); + EXPECT_EQ(test_light_1.getName(), "Hue ambiance lamp 1"); + EXPECT_EQ(test_light_1.getColorType(), ColorType::TEMPERATURE); +} + +TEST(Bridge, removeLight) +{ + using namespace ::testing; + std::shared_ptr handler = std::make_shared(); + nlohmann::json hue_bridge_state {{"lights", + {{"1", + {{"state", + {{"on", true}, {"bri", 254}, {"ct", 366}, {"alert", "none"}, {"colormode", "ct"}, + {"reachable", true}}}, + {"swupdate", {{"state", "noupdates"}, {"lastinstall", nullptr}}}, {"type", "Color temperature light"}, + {"name", "Hue ambiance lamp 1"}, {"modelid", "LTW001"}, {"manufacturername", "Philips"}, + {"uniqueid", "00:00:00:00:00:00:00:00-00"}, {"swversion", "5.50.1.19085"}}}}}}; + EXPECT_CALL( + *handler, GETJson("/api/" + getBridgeUsername(), nlohmann::json::object(), getBridgeIp(), getBridgePort())) + .Times(1) + .WillOnce(Return(hue_bridge_state)); + + Bridge test_bridge(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler); + + EXPECT_CALL(*handler, + GETJson("/api/" + getBridgeUsername() + "/lights/1", nlohmann::json::object(), getBridgeIp(), getBridgePort())) + .Times(1) + .WillRepeatedly(Return(hue_bridge_state["lights"]["1"])); + + nlohmann::json return_answer; + return_answer = nlohmann::json::array(); + return_answer[0] = nlohmann::json::object(); + return_answer[0]["success"] = "/lights/1 deleted"; + EXPECT_CALL(*handler, + DELETEJson( + "/api/" + getBridgeUsername() + "/lights/1", nlohmann::json::object(), getBridgeIp(), getBridgePort())) + .Times(2) + .WillOnce(Return(return_answer)) + .WillOnce(Return(nlohmann::json())); + + // Test when correct data is sent + Light test_light_1 = test_bridge.lights().get(1); + + EXPECT_EQ(test_bridge.lights().remove(1), true); + + EXPECT_EQ(test_bridge.lights().remove(1), false); +} + +TEST(Bridge, getAllLights) +{ + using namespace ::testing; + std::shared_ptr handler = std::make_shared(); + nlohmann::json hue_bridge_state {{"lights", + {{"1", + {{"state", + {{"on", true}, {"bri", 254}, {"ct", 366}, {"alert", "none"}, {"colormode", "ct"}, + {"reachable", true}}}, + {"swupdate", {{"state", "noupdates"}, {"lastinstall", nullptr}}}, {"type", "Color temperature light"}, + {"name", "Hue ambiance lamp 1"}, {"modelid", "LTW001"}, {"manufacturername", "Philips"}, + {"uniqueid", "00:00:00:00:00:00:00:00-00"}, {"swversion", "5.50.1.19085"}}}}}}; + + EXPECT_CALL( + *handler, GETJson("/api/" + getBridgeUsername(), nlohmann::json::object(), getBridgeIp(), getBridgePort())) + .Times(AtLeast(1)) + .WillRepeatedly(Return(hue_bridge_state)); + + EXPECT_CALL(*handler, + GETJson("/api/" + getBridgeUsername() + "/lights/1", nlohmann::json::object(), getBridgeIp(), getBridgePort())) + .Times(AtLeast(1)) + .WillRepeatedly(Return(hue_bridge_state["lights"]["1"])); + + Bridge test_bridge(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler); + + std::vector> test_lights = test_bridge.lights().getAll(); + ASSERT_EQ(1, test_lights.size()); + EXPECT_EQ(test_lights[0].get().getName(), "Hue ambiance lamp 1"); + EXPECT_EQ(test_lights[0].get().getColorType(), ColorType::TEMPERATURE); +} + +TEST(Bridge, lightExists) +{ + using namespace ::testing; + std::shared_ptr handler = std::make_shared(); + nlohmann::json hue_bridge_state {{"lights", + {{"1", + {{"state", + {{"on", true}, {"bri", 254}, {"ct", 366}, {"alert", "none"}, {"colormode", "ct"}, + {"reachable", true}}}, + {"swupdate", {{"state", "noupdates"}, {"lastinstall", nullptr}}}, {"type", "Color temperature light"}, + {"name", "Hue ambiance lamp 1"}, {"modelid", "LTW001"}, {"manufacturername", "Philips"}, + {"uniqueid", "00:00:00:00:00:00:00:00-00"}, {"swversion", "5.50.1.19085"}}}}}}; + EXPECT_CALL( + *handler, GETJson("/api/" + getBridgeUsername(), nlohmann::json::object(), getBridgeIp(), getBridgePort())) + .Times(AtLeast(1)) + .WillRepeatedly(Return(hue_bridge_state)); + + Bridge test_bridge(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler); + + test_bridge.refresh(); + + EXPECT_TRUE(Const(test_bridge).lights().exists(1)); + EXPECT_FALSE(Const(test_bridge).lights().exists(2)); +} + +TEST(Bridge, getGroup) +{ + using namespace ::testing; + std::shared_ptr handler = std::make_shared(); + EXPECT_CALL( + *handler, GETJson("/api/" + getBridgeUsername(), nlohmann::json::object(), getBridgeIp(), getBridgePort())) + .Times(1); + + Bridge test_bridge(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler); + + // Test exception + ASSERT_THROW(test_bridge.groups().get(1), HueException); + + nlohmann::json hue_bridge_state {{"groups", + {{"1", + {{"name", "Group 1"}, {"type", "LightGroup"}, {"lights", {"1", "2", "3"}}, + {"action", + {{"on", true}, {"bri", 254}, {"ct", 366}, {"alert", "none"}, {"colormode", "ct"}, {"hue", 200}, + {"sat", 254}, {"effect", "none"}, {"xy", {0.f, 0.f}}}}, + {"state", {{"any_on", true}, {"all_on", true}}}}}}}}; + + EXPECT_CALL( + *handler, GETJson("/api/" + getBridgeUsername(), nlohmann::json::object(), getBridgeIp(), getBridgePort())) + .Times(1) + .WillOnce(Return(hue_bridge_state)); + + EXPECT_CALL(*handler, + GETJson("/api/" + getBridgeUsername() + "/groups/1", nlohmann::json::object(), getBridgeIp(), getBridgePort())) + .Times(AtLeast(1)) + .WillRepeatedly(Return(hue_bridge_state["groups"]["1"])); + + // Refresh cache + test_bridge = Bridge(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler); + + // Test when correct data is sent + Group test_group_1 = test_bridge.groups().get(1); + EXPECT_EQ(test_group_1.getName(), "Group 1"); + EXPECT_EQ(test_group_1.getType(), "LightGroup"); + + // Test again to check whether group is returned directly + test_group_1 = test_bridge.groups().get(1); + EXPECT_EQ(test_group_1.getName(), "Group 1"); + EXPECT_EQ(test_group_1.getType(), "LightGroup"); +} + +TEST(Bridge, removeGroup) +{ + using namespace ::testing; + std::shared_ptr handler = std::make_shared(); + nlohmann::json hue_bridge_state {{"groups", + {{"1", + {{"name", "Group 1"}, {"type", "LightGroup"}, {"lights", {"1", "2", "3"}}, + {"action", + {{"on", true}, {"bri", 254}, {"ct", 366}, {"alert", "none"}, {"colormode", "ct"}, {"hue", 200}, + {"sat", 254}, {"effect", "none"}, {"xy", {0.f, 0.f}}}}, + {"state", {{"any_on", true}, {"all_on", true}}}}}}}}; + EXPECT_CALL( + *handler, GETJson("/api/" + getBridgeUsername(), nlohmann::json::object(), getBridgeIp(), getBridgePort())) + .Times(1) + .WillOnce(Return(hue_bridge_state)); + + Bridge test_bridge(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler); + + EXPECT_CALL(*handler, + GETJson("/api/" + getBridgeUsername() + "/groups/1", nlohmann::json::object(), getBridgeIp(), getBridgePort())) + .Times(1) + .WillRepeatedly(Return(hue_bridge_state["groups"]["1"])); + + nlohmann::json return_answer; + return_answer = nlohmann::json::array(); + return_answer[0] = nlohmann::json::object(); + return_answer[0]["success"] = "/groups/1 deleted"; + EXPECT_CALL(*handler, + DELETEJson( + "/api/" + getBridgeUsername() + "/groups/1", nlohmann::json::object(), getBridgeIp(), getBridgePort())) + .Times(2) + .WillOnce(Return(return_answer)) + .WillOnce(Return(nlohmann::json())); + + // Test when correct data is sent + Group test_group_1 = test_bridge.groups().get(1); + + EXPECT_EQ(test_bridge.groups().remove(1), true); + + EXPECT_EQ(test_bridge.groups().remove(1), false); +} + +TEST(Bridge, groupExists) +{ + using namespace ::testing; + std::shared_ptr handler = std::make_shared(); + nlohmann::json hue_bridge_state {{"groups", + {{"1", + {{"name", "Group 1"}, {"type", "LightGroup"}, {"lights", {"1", "2", "3"}}, + {"action", + {{"on", true}, {"bri", 254}, {"ct", 366}, {"alert", "none"}, {"colormode", "ct"}, {"hue", 200}, + {"sat", 254}, {"effect", "none"}, {"xy", {0.f, 0.f}}}}, + {"state", {{"any_on", true}, {"all_on", true}}}}}}}}; + EXPECT_CALL( + *handler, GETJson("/api/" + getBridgeUsername(), nlohmann::json::object(), getBridgeIp(), getBridgePort())) + .Times(AtLeast(1)) + .WillRepeatedly(Return(hue_bridge_state)); + + Bridge test_bridge(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler); + + test_bridge.refresh(); + + EXPECT_EQ(true, Const(test_bridge).groups().exists(1)); + EXPECT_EQ(false, Const(test_bridge).groups().exists(2)); +} + +TEST(Bridge, getAllGroups) +{ + using namespace ::testing; + std::shared_ptr handler = std::make_shared(); + nlohmann::json hue_bridge_state {{"groups", + {{"1", + {{"name", "Group 1"}, {"type", "LightGroup"}, {"lights", {"1", "2", "3"}}, + {"action", + {{"on", true}, {"bri", 254}, {"ct", 366}, {"alert", "none"}, {"colormode", "ct"}, {"hue", 200}, + {"sat", 254}, {"effect", "none"}, {"xy", {0.f, 0.f}}}}, + {"state", {{"any_on", true}, {"all_on", true}}}}}}}}; + + EXPECT_CALL( + *handler, GETJson("/api/" + getBridgeUsername(), nlohmann::json::object(), getBridgeIp(), getBridgePort())) + .Times(AtLeast(1)) + .WillRepeatedly(Return(hue_bridge_state)); + + EXPECT_CALL(*handler, + GETJson("/api/" + getBridgeUsername() + "/groups/1", nlohmann::json::object(), getBridgeIp(), getBridgePort())) + .Times(AtLeast(1)) + .WillRepeatedly(Return(hue_bridge_state["groups"]["1"])); + + Bridge test_bridge(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler); + + std::vector> test_groups = test_bridge.groups().getAll(); + ASSERT_EQ(1, test_groups.size()); + EXPECT_EQ(test_groups[0].get().getName(), "Group 1"); + EXPECT_EQ(test_groups[0].get().getType(), "LightGroup"); +} + +TEST(Bridge, createGroup) +{ + using namespace ::testing; + std::shared_ptr handler = std::make_shared(); + EXPECT_CALL( + *handler, GETJson("/api/" + getBridgeUsername(), nlohmann::json::object(), getBridgeIp(), getBridgePort())) + .Times(AtLeast(1)); + Bridge test_bridge(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler); + CreateGroup create = CreateGroup::Room({2, 3}, "Nice room", "LivingRoom"); + nlohmann::json request = create.getRequest(); + const int id = 4; + nlohmann::json response = {{{"success", {{"id", std::to_string(id)}}}}}; + EXPECT_CALL(*handler, POSTJson("/api/" + getBridgeUsername() + "/groups", request, getBridgeIp(), getBridgePort())) + .WillOnce(Return(response)); + EXPECT_EQ(id, test_bridge.groups().create(create)); + + response = {}; + EXPECT_CALL(*handler, POSTJson("/api/" + getBridgeUsername() + "/groups", request, getBridgeIp(), getBridgePort())) + .WillOnce(Return(response)); + EXPECT_EQ(0, test_bridge.groups().create(create)); +} diff --git a/test/test_ExtendedColorHueStrategy.cpp b/test/test_ExtendedColorHueStrategy.cpp index cc6afad..886e35d 100644 --- a/test/test_ExtendedColorHueStrategy.cpp +++ b/test/test_ExtendedColorHueStrategy.cpp @@ -32,7 +32,7 @@ #include "hueplusplus/ExtendedColorHueStrategy.h" #include "json/json.hpp" #include "mocks/mock_HttpHandler.h" -#include "mocks/mock_HueLight.h" +#include "mocks/mock_Light.h" using namespace hueplusplus; @@ -44,7 +44,7 @@ TEST(ExtendedColorHueStrategy, alertHueSaturation) *handler, GETJson("/api/" + getBridgeUsername() + "/lights/1", nlohmann::json::object(), getBridgeIp(), 80)) .Times(AtLeast(1)) .WillRepeatedly(Return(nlohmann::json::object())); - MockHueLight light(handler); + MockLight light(handler); const HueSaturation hueSat {200, 100}; // Needs to update the state so transactions are correctly trimmed @@ -123,7 +123,7 @@ TEST(ExtendedColorHueStrategy, alertXY) *handler, GETJson("/api/" + getBridgeUsername() + "/lights/1", nlohmann::json::object(), getBridgeIp(), 80)) .Times(AtLeast(1)) .WillRepeatedly(Return(nlohmann::json::object())); - MockHueLight light(handler); + MockLight light(handler); const XYBrightness xy {{0.1f, 0.1f}, 1.f}; // Needs to update the state so transactions are correctly trimmed diff --git a/test/test_ExtendedColorTemperatureStrategy.cpp b/test/test_ExtendedColorTemperatureStrategy.cpp index 65c763a..4ed85d0 100644 --- a/test/test_ExtendedColorTemperatureStrategy.cpp +++ b/test/test_ExtendedColorTemperatureStrategy.cpp @@ -32,7 +32,7 @@ #include "hueplusplus/ExtendedColorTemperatureStrategy.h" #include "json/json.hpp" #include "mocks/mock_HttpHandler.h" -#include "mocks/mock_HueLight.h" +#include "mocks/mock_Light.h" using namespace hueplusplus; @@ -44,7 +44,7 @@ TEST(ExtendedColorTemperatureStrategy, alertTemperature) *handler, GETJson("/api/" + getBridgeUsername() + "/lights/1", nlohmann::json::object(), getBridgeIp(), 80)) .Times(AtLeast(1)) .WillRepeatedly(Return(nlohmann::json::object())); - MockHueLight light(handler); + MockLight light(handler); const auto setCTLambda = [&](unsigned int ct, int transition) { light.getState()["state"]["colormode"] = "ct"; diff --git a/test/test_Hue.cpp b/test/test_Hue.cpp deleted file mode 100644 index 7f5c86a..0000000 --- a/test/test_Hue.cpp +++ /dev/null @@ -1,549 +0,0 @@ -/** - \file test_Hue.cpp - Copyright Notice\n - Copyright (C) 2017 Jan Rogall - developer\n - Copyright (C) 2017 Moritz Wirger - developer\n - - This file is part of hueplusplus. - - hueplusplus is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - hueplusplus is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with hueplusplus. If not, see . -**/ - -#include -#include - -#include -#include - -#include "testhelper.h" - -#include "hueplusplus/APIConfig.h" -#include "hueplusplus/Hue.h" -#include "json/json.hpp" -#include "mocks/mock_HttpHandler.h" - -using namespace hueplusplus; - -class HueFinderTest : public ::testing::Test -{ -protected: - std::shared_ptr handler; - -protected: - HueFinderTest() : handler(std::make_shared()) - { - using namespace ::testing; - - EXPECT_CALL(*handler, - sendMulticast("M-SEARCH * HTTP/1.1\r\nHOST: 239.255.255.250:1900\r\nMAN: " - "\"ssdp:discover\"\r\nMX: 5\r\nST: ssdp:all\r\n\r\n", - "239.255.255.250", 1900, Config::instance().getUPnPTimeout())) - .Times(AtLeast(1)) - .WillRepeatedly(Return(getMulticastReply())); - - EXPECT_CALL(*handler, GETString("/description.xml", "application/xml", "", "192.168.2.1", getBridgePort())) - .Times(0); - - EXPECT_CALL(*handler, GETString("/description.xml", "application/xml", "", getBridgeIp(), getBridgePort())) - .Times(AtLeast(1)) - .WillRepeatedly(Return(getBridgeXml())); - } - ~HueFinderTest() {}; -}; - -TEST_F(HueFinderTest, FindBridges) -{ - HueFinder finder(handler); - std::vector bridges = finder.FindBridges(); - - HueFinder::HueIdentification bridge_to_comp; - bridge_to_comp.ip = getBridgeIp(); - bridge_to_comp.port = getBridgePort(); - bridge_to_comp.mac = getBridgeMac(); - - EXPECT_EQ(bridges.size(), 1) << "HueFinder found more than one Bridge"; - EXPECT_EQ(bridges[0].ip, bridge_to_comp.ip) << "HueIdentification ip does not match"; - EXPECT_EQ(bridges[0].port, bridge_to_comp.port) << "HueIdentification port does not match"; - EXPECT_EQ(bridges[0].mac, bridge_to_comp.mac) << "HueIdentification mac does not match"; - - // Test invalid description - EXPECT_CALL(*handler, GETString("/description.xml", "application/xml", "", getBridgeIp(), getBridgePort())) - .Times(1) - .WillOnce(::testing::Return("invalid stuff")); - bridges = finder.FindBridges(); - EXPECT_TRUE(bridges.empty()); -} - -TEST_F(HueFinderTest, GetBridge) -{ - using namespace ::testing; - nlohmann::json request {{"devicetype", "HuePlusPlus#User"}}; - - nlohmann::json errorResponse - = {{{"error", {{"type", 101}, {"address", ""}, {"description", "link button not pressed"}}}}}; - - EXPECT_CALL(*handler, POSTJson("/api", request, getBridgeIp(), getBridgePort())) - .Times(AtLeast(1)) - .WillRepeatedly(Return(errorResponse)); - - HueFinder finder(handler); - std::vector bridges = finder.FindBridges(); - - ASSERT_THROW(finder.GetBridge(bridges[0]), HueException); - - nlohmann::json successResponse = {{{"success", {{"username", getBridgeUsername()}}}}}; - - EXPECT_CALL(*handler, POSTJson("/api", request, getBridgeIp(), getBridgePort())) - .Times(1) - .WillOnce(Return(successResponse)); - - finder = HueFinder(handler); - bridges = finder.FindBridges(); - - Hue test_bridge = finder.GetBridge(bridges[0]); - - EXPECT_EQ(test_bridge.getBridgeIP(), getBridgeIp()) << "Bridge IP not matching"; - EXPECT_EQ(test_bridge.getBridgePort(), getBridgePort()) << "Bridge Port not matching"; - EXPECT_EQ(test_bridge.getUsername(), getBridgeUsername()) << "Bridge username not matching"; - - Mock::VerifyAndClearExpectations(handler.get()); -} - -TEST_F(HueFinderTest, AddUsername) -{ - HueFinder finder(handler); - std::vector bridges = finder.FindBridges(); - - finder.AddUsername(bridges[0].mac, getBridgeUsername()); - Hue test_bridge = finder.GetBridge(bridges[0]); - - EXPECT_EQ(test_bridge.getBridgeIP(), getBridgeIp()) << "Bridge IP not matching"; - EXPECT_EQ(test_bridge.getBridgePort(), getBridgePort()) << "Bridge Port not matching"; - EXPECT_EQ(test_bridge.getUsername(), getBridgeUsername()) << "Bridge username not matching"; -} - -TEST_F(HueFinderTest, GetAllUsernames) -{ - HueFinder finder(handler); - std::vector bridges = finder.FindBridges(); - - finder.AddUsername(bridges[0].mac, getBridgeUsername()); - - std::map users = finder.GetAllUsernames(); - EXPECT_EQ(users[getBridgeMac()], getBridgeUsername()) << "Username of MAC:" << getBridgeMac() << "not matching"; -} - -TEST(Hue, Constructor) -{ - std::shared_ptr handler = std::make_shared(); - Hue test_bridge(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler); - - EXPECT_EQ(test_bridge.getBridgeIP(), getBridgeIp()) << "Bridge IP not matching"; - EXPECT_EQ(test_bridge.getBridgePort(), getBridgePort()) << "Bridge Port not matching"; - EXPECT_EQ(test_bridge.getUsername(), getBridgeUsername()) << "Bridge username not matching"; -} - -TEST(Hue, requestUsername) -{ - using namespace ::testing; - std::shared_ptr handler = std::make_shared(); - nlohmann::json request {{"devicetype", "HuePlusPlus#User"}}; - - { - nlohmann::json errorResponse - = {{{"error", {{"type", 101}, {"address", ""}, {"description", "link button not pressed"}}}}}; - - EXPECT_CALL(*handler, POSTJson("/api", request, getBridgeIp(), getBridgePort())) - .Times(AtLeast(1)) - .WillRepeatedly(Return(errorResponse)); - - Hue test_bridge(getBridgeIp(), getBridgePort(), "", handler); - - std::string username = test_bridge.requestUsername(); - EXPECT_EQ(username, "") << "Returned username not matching"; - EXPECT_EQ(test_bridge.getUsername(), "") << "Bridge username not matching"; - } - - { - // Other error code causes exception - int otherError = 1; - nlohmann::json exceptionResponse - = {{{"error", {{"type", otherError}, {"address", ""}, {"description", "some error"}}}}}; - Hue testBridge(getBridgeIp(), getBridgePort(), "", handler); - - EXPECT_CALL(*handler, POSTJson("/api", request, getBridgeIp(), getBridgePort())) - .WillOnce(Return(exceptionResponse)); - - try - { - testBridge.requestUsername(); - FAIL() << "requestUsername did not throw"; - } - catch (const HueAPIResponseException& e) - { - EXPECT_EQ(e.GetErrorNumber(), otherError); - } - catch (const std::exception& e) - { - FAIL() << "wrong exception: " << e.what(); - } - } - - { - nlohmann::json successResponse = {{{"success", {{"username", getBridgeUsername()}}}}}; - EXPECT_CALL(*handler, POSTJson("/api", request, getBridgeIp(), getBridgePort())) - .Times(1) - .WillRepeatedly(Return(successResponse)); - - Hue test_bridge(getBridgeIp(), getBridgePort(), "", handler); - - nlohmann::json hue_bridge_state{ {"lights", {}} }; - EXPECT_CALL( - *handler, GETJson("/api/" + getBridgeUsername(), nlohmann::json::object(), getBridgeIp(), getBridgePort())) - .Times(1) - .WillOnce(Return(hue_bridge_state)); - std::string username = test_bridge.requestUsername(); - - EXPECT_EQ(username, test_bridge.getUsername()) << "Returned username not matching"; - EXPECT_EQ(test_bridge.getBridgeIP(), getBridgeIp()) << "Bridge IP not matching"; - EXPECT_EQ(test_bridge.getUsername(), getBridgeUsername()) << "Bridge username not matching"; - } -} - -TEST(Hue, setIP) -{ - std::shared_ptr handler = std::make_shared(); - Hue test_bridge(getBridgeIp(), getBridgePort(), "", handler); - EXPECT_EQ(test_bridge.getBridgeIP(), getBridgeIp()) << "Bridge IP not matching after initialization"; - test_bridge.setIP("192.168.2.112"); - EXPECT_EQ(test_bridge.getBridgeIP(), "192.168.2.112") << "Bridge IP not matching after setting it"; -} - -TEST(Hue, setPort) -{ - std::shared_ptr handler = std::make_shared(); - Hue test_bridge = Hue(getBridgeIp(), getBridgePort(), "", handler); - EXPECT_EQ(test_bridge.getBridgePort(), getBridgePort()) << "Bridge Port not matching after initialization"; - test_bridge.setPort(81); - EXPECT_EQ(test_bridge.getBridgePort(), 81) << "Bridge Port not matching after setting it"; -} - -TEST(Hue, getLight) -{ - using namespace ::testing; - std::shared_ptr handler = std::make_shared(); - EXPECT_CALL( - *handler, GETJson("/api/" + getBridgeUsername(), nlohmann::json::object(), getBridgeIp(), getBridgePort())) - .Times(1); - - Hue test_bridge(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler); - - // Test exception - ASSERT_THROW(test_bridge.lights().get(1), HueException); - - nlohmann::json hue_bridge_state {{"lights", - {{"1", - {{"state", - {{"on", true}, {"bri", 254}, {"ct", 366}, {"alert", "none"}, {"colormode", "ct"}, - {"reachable", true}}}, - {"swupdate", {{"state", "noupdates"}, {"lastinstall", nullptr}}}, {"type", "Color temperature light"}, - {"name", "Hue ambiance lamp 1"}, {"modelid", "LTW001"}, {"manufacturername", "Philips"}, - {"uniqueid", "00:00:00:00:00:00:00:00-00"}, {"swversion", "5.50.1.19085"}}}}}}; - - EXPECT_CALL( - *handler, GETJson("/api/" + getBridgeUsername(), nlohmann::json::object(), getBridgeIp(), getBridgePort())) - .Times(1) - .WillOnce(Return(hue_bridge_state)); - - EXPECT_CALL(*handler, - GETJson("/api/" + getBridgeUsername() + "/lights/1", nlohmann::json::object(), getBridgeIp(), getBridgePort())) - .Times(AtLeast(1)) - .WillRepeatedly(Return(hue_bridge_state["lights"]["1"])); - - // Refresh cache - test_bridge = Hue(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler); - - // Test when correct data is sent - HueLight test_light_1 = test_bridge.lights().get(1); - EXPECT_EQ(test_light_1.getName(), "Hue ambiance lamp 1"); - EXPECT_EQ(test_light_1.getColorType(), ColorType::TEMPERATURE); - - // Test again to check whether light is returned directly -> interesting for - // code coverage test - test_light_1 = test_bridge.lights().get(1); - EXPECT_EQ(test_light_1.getName(), "Hue ambiance lamp 1"); - EXPECT_EQ(test_light_1.getColorType(), ColorType::TEMPERATURE); -} - -TEST(Hue, removeLight) -{ - using namespace ::testing; - std::shared_ptr handler = std::make_shared(); - nlohmann::json hue_bridge_state {{"lights", - {{"1", - {{"state", - {{"on", true}, {"bri", 254}, {"ct", 366}, {"alert", "none"}, {"colormode", "ct"}, - {"reachable", true}}}, - {"swupdate", {{"state", "noupdates"}, {"lastinstall", nullptr}}}, {"type", "Color temperature light"}, - {"name", "Hue ambiance lamp 1"}, {"modelid", "LTW001"}, {"manufacturername", "Philips"}, - {"uniqueid", "00:00:00:00:00:00:00:00-00"}, {"swversion", "5.50.1.19085"}}}}}}; - EXPECT_CALL( - *handler, GETJson("/api/" + getBridgeUsername(), nlohmann::json::object(), getBridgeIp(), getBridgePort())) - .Times(1) - .WillOnce(Return(hue_bridge_state)); - - Hue test_bridge(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler); - - EXPECT_CALL(*handler, - GETJson("/api/" + getBridgeUsername() + "/lights/1", nlohmann::json::object(), getBridgeIp(), getBridgePort())) - .Times(1) - .WillRepeatedly(Return(hue_bridge_state["lights"]["1"])); - - nlohmann::json return_answer; - return_answer = nlohmann::json::array(); - return_answer[0] = nlohmann::json::object(); - return_answer[0]["success"] = "/lights/1 deleted"; - EXPECT_CALL(*handler, - DELETEJson( - "/api/" + getBridgeUsername() + "/lights/1", nlohmann::json::object(), getBridgeIp(), getBridgePort())) - .Times(2) - .WillOnce(Return(return_answer)) - .WillOnce(Return(nlohmann::json())); - - // Test when correct data is sent - HueLight test_light_1 = test_bridge.lights().get(1); - - EXPECT_EQ(test_bridge.lights().remove(1), true); - - EXPECT_EQ(test_bridge.lights().remove(1), false); -} - -TEST(Hue, getAllLights) -{ - using namespace ::testing; - std::shared_ptr handler = std::make_shared(); - nlohmann::json hue_bridge_state {{"lights", - {{"1", - {{"state", - {{"on", true}, {"bri", 254}, {"ct", 366}, {"alert", "none"}, {"colormode", "ct"}, - {"reachable", true}}}, - {"swupdate", {{"state", "noupdates"}, {"lastinstall", nullptr}}}, {"type", "Color temperature light"}, - {"name", "Hue ambiance lamp 1"}, {"modelid", "LTW001"}, {"manufacturername", "Philips"}, - {"uniqueid", "00:00:00:00:00:00:00:00-00"}, {"swversion", "5.50.1.19085"}}}}}}; - - EXPECT_CALL( - *handler, GETJson("/api/" + getBridgeUsername(), nlohmann::json::object(), getBridgeIp(), getBridgePort())) - .Times(AtLeast(1)) - .WillRepeatedly(Return(hue_bridge_state)); - - EXPECT_CALL(*handler, - GETJson("/api/" + getBridgeUsername() + "/lights/1", nlohmann::json::object(), getBridgeIp(), getBridgePort())) - .Times(AtLeast(1)) - .WillRepeatedly(Return(hue_bridge_state["lights"]["1"])); - - Hue test_bridge(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler); - - std::vector> test_lights = test_bridge.lights().getAll(); - ASSERT_EQ(1, test_lights.size()); - EXPECT_EQ(test_lights[0].get().getName(), "Hue ambiance lamp 1"); - EXPECT_EQ(test_lights[0].get().getColorType(), ColorType::TEMPERATURE); -} - -TEST(Hue, lightExists) -{ - using namespace ::testing; - std::shared_ptr handler = std::make_shared(); - nlohmann::json hue_bridge_state {{"lights", - {{"1", - {{"state", - {{"on", true}, {"bri", 254}, {"ct", 366}, {"alert", "none"}, {"colormode", "ct"}, - {"reachable", true}}}, - {"swupdate", {{"state", "noupdates"}, {"lastinstall", nullptr}}}, {"type", "Color temperature light"}, - {"name", "Hue ambiance lamp 1"}, {"modelid", "LTW001"}, {"manufacturername", "Philips"}, - {"uniqueid", "00:00:00:00:00:00:00:00-00"}, {"swversion", "5.50.1.19085"}}}}}}; - EXPECT_CALL( - *handler, GETJson("/api/" + getBridgeUsername(), nlohmann::json::object(), getBridgeIp(), getBridgePort())) - .Times(AtLeast(1)) - .WillRepeatedly(Return(hue_bridge_state)); - - Hue test_bridge(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler); - - test_bridge.refresh(); - - EXPECT_TRUE(Const(test_bridge).lights().exists(1)); - EXPECT_FALSE(Const(test_bridge).lights().exists(2)); -} - -TEST(Hue, getGroup) -{ - using namespace ::testing; - std::shared_ptr handler = std::make_shared(); - EXPECT_CALL( - *handler, GETJson("/api/" + getBridgeUsername(), nlohmann::json::object(), getBridgeIp(), getBridgePort())) - .Times(1); - - Hue test_bridge(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler); - - // Test exception - ASSERT_THROW(test_bridge.groups().get(1), HueException); - - nlohmann::json hue_bridge_state {{"groups", - {{"1", - {{"name", "Group 1"}, {"type", "LightGroup"}, {"lights", {"1", "2", "3"}}, - {"action", - {{"on", true}, {"bri", 254}, {"ct", 366}, {"alert", "none"}, {"colormode", "ct"}, {"hue", 200}, - {"sat", 254}, {"effect", "none"}, {"xy", {0.f, 0.f}}}}, - {"state", {{"any_on", true}, {"all_on", true}}}}}}}}; - - EXPECT_CALL( - *handler, GETJson("/api/" + getBridgeUsername(), nlohmann::json::object(), getBridgeIp(), getBridgePort())) - .Times(1) - .WillOnce(Return(hue_bridge_state)); - - EXPECT_CALL(*handler, - GETJson("/api/" + getBridgeUsername() + "/groups/1", nlohmann::json::object(), getBridgeIp(), getBridgePort())) - .Times(AtLeast(1)) - .WillRepeatedly(Return(hue_bridge_state["groups"]["1"])); - - // Refresh cache - test_bridge = Hue(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler); - - // Test when correct data is sent - Group test_group_1 = test_bridge.groups().get(1); - EXPECT_EQ(test_group_1.getName(), "Group 1"); - EXPECT_EQ(test_group_1.getType(), "LightGroup"); - - // Test again to check whether group is returned directly - test_group_1 = test_bridge.groups().get(1); - EXPECT_EQ(test_group_1.getName(), "Group 1"); - EXPECT_EQ(test_group_1.getType(), "LightGroup"); -} - -TEST(Hue, removeGroup) -{ - using namespace ::testing; - std::shared_ptr handler = std::make_shared(); - nlohmann::json hue_bridge_state {{"groups", - {{"1", - {{"name", "Group 1"}, {"type", "LightGroup"}, {"lights", {"1", "2", "3"}}, - {"action", - {{"on", true}, {"bri", 254}, {"ct", 366}, {"alert", "none"}, {"colormode", "ct"}, {"hue", 200}, - {"sat", 254}, {"effect", "none"}, {"xy", {0.f, 0.f}}}}, - {"state", {{"any_on", true}, {"all_on", true}}}}}}}}; - EXPECT_CALL( - *handler, GETJson("/api/" + getBridgeUsername(), nlohmann::json::object(), getBridgeIp(), getBridgePort())) - .Times(1) - .WillOnce(Return(hue_bridge_state)); - - Hue test_bridge(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler); - - EXPECT_CALL(*handler, - GETJson("/api/" + getBridgeUsername() + "/groups/1", nlohmann::json::object(), getBridgeIp(), getBridgePort())) - .Times(1) - .WillRepeatedly(Return(hue_bridge_state["groups"]["1"])); - - nlohmann::json return_answer; - return_answer = nlohmann::json::array(); - return_answer[0] = nlohmann::json::object(); - return_answer[0]["success"] = "/groups/1 deleted"; - EXPECT_CALL(*handler, - DELETEJson( - "/api/" + getBridgeUsername() + "/groups/1", nlohmann::json::object(), getBridgeIp(), getBridgePort())) - .Times(2) - .WillOnce(Return(return_answer)) - .WillOnce(Return(nlohmann::json())); - - // Test when correct data is sent - Group test_group_1 = test_bridge.groups().get(1); - - EXPECT_EQ(test_bridge.groups().remove(1), true); - - EXPECT_EQ(test_bridge.groups().remove(1), false); -} - -TEST(Hue, groupExists) -{ - using namespace ::testing; - std::shared_ptr handler = std::make_shared(); - nlohmann::json hue_bridge_state {{"groups", - {{"1", - {{"name", "Group 1"}, {"type", "LightGroup"}, {"lights", {"1", "2", "3"}}, - {"action", - {{"on", true}, {"bri", 254}, {"ct", 366}, {"alert", "none"}, {"colormode", "ct"}, {"hue", 200}, - {"sat", 254}, {"effect", "none"}, {"xy", {0.f, 0.f}}}}, - {"state", {{"any_on", true}, {"all_on", true}}}}}}}}; - EXPECT_CALL( - *handler, GETJson("/api/" + getBridgeUsername(), nlohmann::json::object(), getBridgeIp(), getBridgePort())) - .Times(AtLeast(1)) - .WillRepeatedly(Return(hue_bridge_state)); - - Hue test_bridge(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler); - - test_bridge.refresh(); - - EXPECT_EQ(true, Const(test_bridge).groups().exists(1)); - EXPECT_EQ(false, Const(test_bridge).groups().exists(2)); -} - -TEST(Hue, getAllGroups) -{ - using namespace ::testing; - std::shared_ptr handler = std::make_shared(); - nlohmann::json hue_bridge_state {{"groups", - {{"1", - {{"name", "Group 1"}, {"type", "LightGroup"}, {"lights", {"1", "2", "3"}}, - {"action", - {{"on", true}, {"bri", 254}, {"ct", 366}, {"alert", "none"}, {"colormode", "ct"}, {"hue", 200}, - {"sat", 254}, {"effect", "none"}, {"xy", {0.f, 0.f}}}}, - {"state", {{"any_on", true}, {"all_on", true}}}}}}}}; - - EXPECT_CALL( - *handler, GETJson("/api/" + getBridgeUsername(), nlohmann::json::object(), getBridgeIp(), getBridgePort())) - .Times(AtLeast(1)) - .WillRepeatedly(Return(hue_bridge_state)); - - EXPECT_CALL(*handler, - GETJson("/api/" + getBridgeUsername() + "/groups/1", nlohmann::json::object(), getBridgeIp(), getBridgePort())) - .Times(AtLeast(1)) - .WillRepeatedly(Return(hue_bridge_state["groups"]["1"])); - - Hue test_bridge(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler); - - std::vector> test_groups = test_bridge.groups().getAll(); - ASSERT_EQ(1, test_groups.size()); - EXPECT_EQ(test_groups[0].get().getName(), "Group 1"); - EXPECT_EQ(test_groups[0].get().getType(), "LightGroup"); -} - -TEST(Hue, createGroup) -{ - using namespace ::testing; - std::shared_ptr handler = std::make_shared(); - EXPECT_CALL( - *handler, GETJson("/api/" + getBridgeUsername(), nlohmann::json::object(), getBridgeIp(), getBridgePort())) - .Times(AtLeast(1)); - Hue test_bridge(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler); - CreateGroup create = CreateGroup::Room({2, 3}, "Nice room", "LivingRoom"); - nlohmann::json request = create.getRequest(); - const int id = 4; - nlohmann::json response = {{{"success", {{"id", std::to_string(id)}}}}}; - EXPECT_CALL(*handler, POSTJson("/api/" + getBridgeUsername() + "/groups", request, getBridgeIp(), getBridgePort())) - .WillOnce(Return(response)); - EXPECT_EQ(id, test_bridge.groups().create(create)); - - response = {}; - EXPECT_CALL(*handler, POSTJson("/api/" + getBridgeUsername() + "/groups", request, getBridgeIp(), getBridgePort())) - .WillOnce(Return(response)); - EXPECT_EQ(0, test_bridge.groups().create(create)); -} diff --git a/test/test_HueCommandAPI.cpp b/test/test_HueCommandAPI.cpp index e32efb6..450f527 100644 --- a/test/test_HueCommandAPI.cpp +++ b/test/test_HueCommandAPI.cpp @@ -25,7 +25,7 @@ #include "testhelper.h" -#include "hueplusplus/Hue.h" +#include "hueplusplus/Bridge.h" #include "json/json.hpp" #include "mocks/mock_HttpHandler.h" diff --git a/test/test_HueLight.cpp b/test/test_HueLight.cpp deleted file mode 100644 index 3a61275..0000000 --- a/test/test_HueLight.cpp +++ /dev/null @@ -1,834 +0,0 @@ -/** - \file test_HueLight.cpp - Copyright Notice\n - Copyright (C) 2017 Jan Rogall - developer\n - Copyright (C) 2017 Moritz Wirger - developer\n - - This file is part of hueplusplus. - - hueplusplus is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - hueplusplus is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with hueplusplus. If not, see . -**/ - -#include -#include - -#include "testhelper.h" - -#include "hueplusplus/Hue.h" -#include "hueplusplus/HueLight.h" -#include "json/json.hpp" -#include "mocks/mock_HttpHandler.h" - -using namespace hueplusplus; - -class HueLightTest : public ::testing::Test -{ -protected: - std::shared_ptr handler; - nlohmann::json hue_bridge_state; - Hue test_bridge; - -protected: - HueLightTest() - : handler(std::make_shared()), - hue_bridge_state({{"lights", - {{"1", - {{"state", - {{"on", true}, {"bri", 254}, {"ct", 366}, {"alert", "none"}, {"colormode", "ct"}, - {"reachable", true}, {"effect", "none"}}}, - {"swupdate", {{"state", "noupdates"}, {"lastinstall", nullptr}}}, {"type", "Dimmable light"}, - {"name", "Hue lamp 1"}, {"modelid", "LWB004"}, {"manufacturername", "Philips"}, - {"productname", "Hue bloom"}, {"uniqueid", "00:00:00:00:00:00:00:00-00"}, - {"swversion", "5.50.1.19085"}, {"luminaireuniqueid", "0000000"}}}, - {"2", - {{"state", - {{"on", false}, {"bri", 0}, {"ct", 366}, {"hue", 12345}, {"sat", 123}, - {"xy", {0.102, 0.102}}, {"alert", "none"}, {"colormode", "ct"}, {"reachable", true}, - {"effect", "none"}}}, - {"swupdate", {{"state", "noupdates"}, {"lastinstall", nullptr}}}, {"type", "Color light"}, - {"name", "Hue lamp 2"}, {"modelid", "LST001"}, {"uniqueid", "11:11:11:11:11:11:11:11-11"}, - {"swversion", "5.50.1.19085"}}}, - {"3", - {{"state", - {{"on", false}, {"bri", 254}, {"ct", 366}, {"hue", 12345}, {"sat", 123}, - {"xy", {0.102, 0.102}}, {"alert", "none"}, {"colormode", "ct"}, {"reachable", true}, - {"effect", "none"}}}, - {"swupdate", {{"state", "noupdates"}, {"lastinstall", nullptr}}}, - {"type", "Extended color light"}, {"name", "Hue lamp 3"}, {"modelid", "LCT010"}, - {"manufacturername", "Philips"}, {"productname", "Hue bloom"}, - {"swversion", "5.50.1.19085"}}}}}}), - test_bridge(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler) - { - using namespace ::testing; - - EXPECT_CALL(*handler, GETJson("/api/" + getBridgeUsername(), nlohmann::json::object(), getBridgeIp(), 80)) - .Times(AtLeast(1)) - .WillRepeatedly(Return(hue_bridge_state)); - EXPECT_CALL( - *handler, GETJson("/api/" + getBridgeUsername() + "/lights/1", nlohmann::json::object(), getBridgeIp(), 80)) - .Times(AtLeast(1)) - .WillRepeatedly(Return(hue_bridge_state["lights"]["1"])); - EXPECT_CALL( - *handler, GETJson("/api/" + getBridgeUsername() + "/lights/2", nlohmann::json::object(), getBridgeIp(), 80)) - .Times(AtLeast(1)) - .WillRepeatedly(Return(hue_bridge_state["lights"]["2"])); - EXPECT_CALL( - *handler, GETJson("/api/" + getBridgeUsername() + "/lights/3", nlohmann::json::object(), getBridgeIp(), 80)) - .Times(AtLeast(1)) - .WillRepeatedly(Return(hue_bridge_state["lights"]["3"])); - } - ~HueLightTest() {}; -}; - -TEST_F(HueLightTest, Constructor) -{ - const HueLight ctest_light_1 = test_bridge.lights().get(1); - HueLight test_light_1 = test_bridge.lights().get(1); - const HueLight ctest_light_2 = test_bridge.lights().get(2); - HueLight test_light_2 = test_bridge.lights().get(2); - const HueLight ctest_light_3 = test_bridge.lights().get(3); - HueLight test_light_3 = test_bridge.lights().get(3); -} - -TEST_F(HueLightTest, On) -{ - using namespace ::testing; - EXPECT_CALL(*handler, PUTJson("/api/" + getBridgeUsername() + "/lights/2/state", _, getBridgeIp(), 80)) - .Times(1) - .WillOnce(Return(nlohmann::json::array())); - - nlohmann::json prep_ret; - prep_ret = nlohmann::json::array(); - prep_ret[0] = nlohmann::json::object(); - prep_ret[0]["success"] = nlohmann::json::object(); - prep_ret[0]["success"]["/lights/3/state/transitiontime"] = 255; - prep_ret[1] = nlohmann::json::object(); - prep_ret[1]["success"] = nlohmann::json::object(); - prep_ret[1]["success"]["/lights/3/state/on"] = true; - EXPECT_CALL(*handler, PUTJson("/api/" + getBridgeUsername() + "/lights/3/state", _, getBridgeIp(), 80)) - .Times(1) - .WillOnce(Return(prep_ret)); - - HueLight test_light_1 = test_bridge.lights().get(1); - HueLight test_light_2 = test_bridge.lights().get(2); - HueLight test_light_3 = test_bridge.lights().get(3); - - EXPECT_EQ(true, test_light_1.On(33)); - EXPECT_EQ(false, test_light_2.On()); - EXPECT_EQ(true, test_light_3.On(255)); -} - -TEST_F(HueLightTest, Off) -{ - using namespace ::testing; - nlohmann::json prep_ret; - prep_ret = nlohmann::json::array(); - prep_ret[0] = nlohmann::json::object(); - prep_ret[0]["success"] = nlohmann::json::object(); - prep_ret[0]["success"]["/lights/1/state/transitiontime"] = 33; - prep_ret[1] = nlohmann::json::object(); - prep_ret[1]["success"] = nlohmann::json::object(); - prep_ret[1]["success"]["/lights/1/state/on"] = false; - EXPECT_CALL(*handler, PUTJson("/api/" + getBridgeUsername() + "/lights/1/state", _, getBridgeIp(), 80)) - .Times(1) - .WillOnce(Return(prep_ret)); - - HueLight test_light_1 = test_bridge.lights().get(1); - HueLight test_light_2 = test_bridge.lights().get(2); - HueLight test_light_3 = test_bridge.lights().get(3); - - EXPECT_EQ(true, test_light_1.Off(33)); - EXPECT_EQ(true, test_light_2.Off()); - EXPECT_EQ(true, test_light_3.Off(255)); -} - -TEST_F(HueLightTest, isOn) -{ - const HueLight ctest_light_1 = test_bridge.lights().get(1); - const HueLight ctest_light_2 = test_bridge.lights().get(2); - const HueLight ctest_light_3 = test_bridge.lights().get(3); - HueLight test_light_1 = test_bridge.lights().get(1); - HueLight test_light_2 = test_bridge.lights().get(2); - HueLight test_light_3 = test_bridge.lights().get(3); - - EXPECT_EQ(true, ctest_light_1.isOn()); - EXPECT_EQ(false, ctest_light_2.isOn()); - EXPECT_EQ(false, ctest_light_3.isOn()); - EXPECT_EQ(true, test_light_1.isOn()); - EXPECT_EQ(false, test_light_2.isOn()); - EXPECT_EQ(false, test_light_3.isOn()); -} - -TEST_F(HueLightTest, getId) -{ - const HueLight ctest_light_1 = test_bridge.lights().get(1); - const HueLight ctest_light_2 = test_bridge.lights().get(2); - const HueLight ctest_light_3 = test_bridge.lights().get(3); - HueLight test_light_1 = test_bridge.lights().get(1); - HueLight test_light_2 = test_bridge.lights().get(2); - HueLight test_light_3 = test_bridge.lights().get(3); - - EXPECT_EQ(1, ctest_light_1.getId()); - EXPECT_EQ(2, ctest_light_2.getId()); - EXPECT_EQ(3, ctest_light_3.getId()); - EXPECT_EQ(1, test_light_1.getId()); - EXPECT_EQ(2, test_light_2.getId()); - EXPECT_EQ(3, test_light_3.getId()); -} - -TEST_F(HueLightTest, getType) -{ - const HueLight ctest_light_1 = test_bridge.lights().get(1); - const HueLight ctest_light_2 = test_bridge.lights().get(2); - const HueLight ctest_light_3 = test_bridge.lights().get(3); - HueLight test_light_1 = test_bridge.lights().get(1); - HueLight test_light_2 = test_bridge.lights().get(2); - HueLight test_light_3 = test_bridge.lights().get(3); - - EXPECT_EQ("Dimmable light", ctest_light_1.getType()); - EXPECT_EQ("Color light", ctest_light_2.getType()); - EXPECT_EQ("Extended color light", ctest_light_3.getType()); - EXPECT_EQ("Dimmable light", test_light_1.getType()); - EXPECT_EQ("Color light", test_light_2.getType()); - EXPECT_EQ("Extended color light", test_light_3.getType()); -} - -TEST_F(HueLightTest, getName) -{ - const HueLight ctest_light_1 = test_bridge.lights().get(1); - const HueLight ctest_light_2 = test_bridge.lights().get(2); - const HueLight ctest_light_3 = test_bridge.lights().get(3); - HueLight test_light_1 = test_bridge.lights().get(1); - HueLight test_light_2 = test_bridge.lights().get(2); - HueLight test_light_3 = test_bridge.lights().get(3); - - EXPECT_EQ("Hue lamp 1", ctest_light_1.getName()); - EXPECT_EQ("Hue lamp 2", ctest_light_2.getName()); - EXPECT_EQ("Hue lamp 3", ctest_light_3.getName()); - EXPECT_EQ("Hue lamp 1", test_light_1.getName()); - EXPECT_EQ("Hue lamp 2", test_light_2.getName()); - EXPECT_EQ("Hue lamp 3", test_light_3.getName()); -} - -TEST_F(HueLightTest, getModelId) -{ - const HueLight ctest_light_1 = test_bridge.lights().get(1); - const HueLight ctest_light_2 = test_bridge.lights().get(2); - const HueLight ctest_light_3 = test_bridge.lights().get(3); - HueLight test_light_1 = test_bridge.lights().get(1); - HueLight test_light_2 = test_bridge.lights().get(2); - HueLight test_light_3 = test_bridge.lights().get(3); - - EXPECT_EQ("LWB004", ctest_light_1.getModelId()); - EXPECT_EQ("LST001", ctest_light_2.getModelId()); - EXPECT_EQ("LCT010", ctest_light_3.getModelId()); - EXPECT_EQ("LWB004", test_light_1.getModelId()); - EXPECT_EQ("LST001", test_light_2.getModelId()); - EXPECT_EQ("LCT010", test_light_3.getModelId()); -} - -TEST_F(HueLightTest, getUId) -{ - const HueLight ctest_light_1 = test_bridge.lights().get(1); - const HueLight ctest_light_2 = test_bridge.lights().get(2); - const HueLight ctest_light_3 = test_bridge.lights().get(3); - HueLight test_light_1 = test_bridge.lights().get(1); - HueLight test_light_2 = test_bridge.lights().get(2); - HueLight test_light_3 = test_bridge.lights().get(3); - - EXPECT_EQ("00:00:00:00:00:00:00:00-00", ctest_light_1.getUId()); - EXPECT_EQ("11:11:11:11:11:11:11:11-11", ctest_light_2.getUId()); - EXPECT_EQ("", ctest_light_3.getUId()); - EXPECT_EQ("00:00:00:00:00:00:00:00-00", test_light_1.getUId()); - EXPECT_EQ("11:11:11:11:11:11:11:11-11", test_light_2.getUId()); - EXPECT_EQ("", test_light_3.getUId()); -} - -TEST_F(HueLightTest, getManufacturername) -{ - const HueLight ctest_light_1 = test_bridge.lights().get(1); - const HueLight ctest_light_2 = test_bridge.lights().get(2); - const HueLight ctest_light_3 = test_bridge.lights().get(3); - HueLight test_light_1 = test_bridge.lights().get(1); - HueLight test_light_2 = test_bridge.lights().get(2); - HueLight test_light_3 = test_bridge.lights().get(3); - - EXPECT_EQ("Philips", ctest_light_1.getManufacturername()); - EXPECT_EQ("", ctest_light_2.getManufacturername()); - EXPECT_EQ("Philips", ctest_light_3.getManufacturername()); - EXPECT_EQ("Philips", test_light_1.getManufacturername()); - EXPECT_EQ("", test_light_2.getManufacturername()); - EXPECT_EQ("Philips", test_light_3.getManufacturername()); -} - -TEST_F(HueLightTest, getProductname) -{ - const HueLight ctest_light_1 = test_bridge.lights().get(1); - const HueLight ctest_light_2 = test_bridge.lights().get(2); - const HueLight ctest_light_3 = test_bridge.lights().get(3); - HueLight test_light_1 = test_bridge.lights().get(1); - HueLight test_light_2 = test_bridge.lights().get(2); - HueLight test_light_3 = test_bridge.lights().get(3); - - EXPECT_EQ("Hue bloom", ctest_light_1.getProductname()); - EXPECT_EQ("", ctest_light_2.getProductname()); - EXPECT_EQ("Hue bloom", ctest_light_3.getProductname()); - EXPECT_EQ("Hue bloom", test_light_1.getProductname()); - EXPECT_EQ("", test_light_2.getProductname()); - EXPECT_EQ("Hue bloom", test_light_3.getProductname()); -} - -TEST_F(HueLightTest, getLuminaireUId) -{ - const HueLight ctest_light_1 = test_bridge.lights().get(1); - const HueLight ctest_light_2 = test_bridge.lights().get(2); - const HueLight ctest_light_3 = test_bridge.lights().get(3); - HueLight test_light_1 = test_bridge.lights().get(1); - HueLight test_light_2 = test_bridge.lights().get(2); - HueLight test_light_3 = test_bridge.lights().get(3); - - EXPECT_EQ("0000000", ctest_light_1.getLuminaireUId()); - EXPECT_EQ("", ctest_light_2.getLuminaireUId()); - EXPECT_EQ("", ctest_light_3.getLuminaireUId()); - EXPECT_EQ("0000000", test_light_1.getLuminaireUId()); - EXPECT_EQ("", test_light_2.getLuminaireUId()); - EXPECT_EQ("", test_light_3.getLuminaireUId()); -} - -TEST_F(HueLightTest, getSwVersion) -{ - const HueLight ctest_light_1 = test_bridge.lights().get(1); - const HueLight ctest_light_2 = test_bridge.lights().get(2); - const HueLight ctest_light_3 = test_bridge.lights().get(3); - HueLight test_light_1 = test_bridge.lights().get(1); - HueLight test_light_2 = test_bridge.lights().get(2); - HueLight test_light_3 = test_bridge.lights().get(3); - - EXPECT_EQ("5.50.1.19085", ctest_light_1.getSwVersion()); - EXPECT_EQ("5.50.1.19085", ctest_light_2.getSwVersion()); - EXPECT_EQ("5.50.1.19085", ctest_light_3.getSwVersion()); - EXPECT_EQ("5.50.1.19085", test_light_1.getSwVersion()); - EXPECT_EQ("5.50.1.19085", test_light_2.getSwVersion()); - EXPECT_EQ("5.50.1.19085", test_light_3.getSwVersion()); -} - -TEST_F(HueLightTest, setName) -{ - using namespace ::testing; - nlohmann::json expected_request({}); - expected_request["name"] = "Baskj189"; - nlohmann::json prep_ret; - prep_ret = nlohmann::json::array(); - prep_ret[0] = nlohmann::json::object(); - prep_ret[0]["success"] = nlohmann::json::object(); - prep_ret[0]["success"]["/lights/1/name"] = expected_request["name"]; - EXPECT_CALL( - *handler, PUTJson("/api/" + getBridgeUsername() + "/lights/1/name", expected_request, getBridgeIp(), 80)) - .Times(1) - .WillOnce(Return(prep_ret)); - EXPECT_CALL( - *handler, PUTJson("/api/" + getBridgeUsername() + "/lights/2/name", expected_request, getBridgeIp(), 80)) - .Times(1) - .WillOnce(Return(nlohmann::json::array())); - prep_ret[0]["success"] = nlohmann::json::object(); - prep_ret[0]["success"]["/lights/3/name"] = expected_request["name"]; - EXPECT_CALL( - *handler, PUTJson("/api/" + getBridgeUsername() + "/lights/3/name", expected_request, getBridgeIp(), 80)) - .Times(1) - .WillOnce(Return(prep_ret)); - - HueLight test_light_1 = test_bridge.lights().get(1); - HueLight test_light_2 = test_bridge.lights().get(2); - HueLight test_light_3 = test_bridge.lights().get(3); - - EXPECT_EQ(true, test_light_1.setName(expected_request["name"].get())); - EXPECT_EQ(false, test_light_2.setName(expected_request["name"].get())); - EXPECT_EQ(true, test_light_3.setName(expected_request["name"].get())); -} - -TEST_F(HueLightTest, getColorType) -{ - const HueLight ctest_light_1 = test_bridge.lights().get(1); - const HueLight ctest_light_2 = test_bridge.lights().get(2); - const HueLight ctest_light_3 = test_bridge.lights().get(3); - HueLight test_light_1 = test_bridge.lights().get(1); - HueLight test_light_2 = test_bridge.lights().get(2); - HueLight test_light_3 = test_bridge.lights().get(3); - - EXPECT_EQ(ColorType::NONE, ctest_light_1.getColorType()); - EXPECT_EQ(ColorType::GAMUT_A, ctest_light_2.getColorType()); - EXPECT_EQ(ColorType::GAMUT_C_TEMPERATURE, ctest_light_3.getColorType()); - EXPECT_EQ(ColorType::NONE, test_light_1.getColorType()); - EXPECT_EQ(ColorType::GAMUT_A, test_light_2.getColorType()); - EXPECT_EQ(ColorType::GAMUT_C_TEMPERATURE, test_light_3.getColorType()); -} - -TEST_F(HueLightTest, KelvinToMired) -{ - const HueLight ctest_light_1 = test_bridge.lights().get(1); - const HueLight ctest_light_2 = test_bridge.lights().get(2); - const HueLight ctest_light_3 = test_bridge.lights().get(3); - HueLight test_light_1 = test_bridge.lights().get(1); - HueLight test_light_2 = test_bridge.lights().get(2); - HueLight test_light_3 = test_bridge.lights().get(3); - - EXPECT_EQ(10000, ctest_light_1.KelvinToMired(100)); - EXPECT_EQ(500, ctest_light_2.KelvinToMired(2000)); - EXPECT_EQ(303, ctest_light_3.KelvinToMired(3300)); - EXPECT_EQ(250, test_light_1.KelvinToMired(4000)); - EXPECT_EQ(200, test_light_2.KelvinToMired(5000)); - EXPECT_EQ(166, test_light_3.KelvinToMired(6000)); -} - -TEST_F(HueLightTest, MiredToKelvin) -{ - const HueLight ctest_light_1 = test_bridge.lights().get(1); - const HueLight ctest_light_2 = test_bridge.lights().get(2); - const HueLight ctest_light_3 = test_bridge.lights().get(3); - HueLight test_light_1 = test_bridge.lights().get(1); - HueLight test_light_2 = test_bridge.lights().get(2); - HueLight test_light_3 = test_bridge.lights().get(3); - - EXPECT_EQ(100, ctest_light_1.MiredToKelvin(10000)); - EXPECT_EQ(2000, ctest_light_2.MiredToKelvin(500)); - EXPECT_EQ(3300, ctest_light_3.MiredToKelvin(303)); - EXPECT_EQ(4000, test_light_1.MiredToKelvin(250)); - EXPECT_EQ(5000, test_light_2.MiredToKelvin(200)); - EXPECT_EQ(6024, test_light_3.MiredToKelvin(166)); // 6000 kelvin should be 166 mired, but keep in - // mind flops are not exact -} - -TEST_F(HueLightTest, hasBrightnessControl) -{ - const HueLight ctest_light_1 = test_bridge.lights().get(1); - const HueLight ctest_light_2 = test_bridge.lights().get(2); - const HueLight ctest_light_3 = test_bridge.lights().get(3); - HueLight test_light_1 = test_bridge.lights().get(1); - HueLight test_light_2 = test_bridge.lights().get(2); - HueLight test_light_3 = test_bridge.lights().get(3); - - EXPECT_EQ(true, ctest_light_1.hasBrightnessControl()); - EXPECT_EQ(true, ctest_light_2.hasBrightnessControl()); - EXPECT_EQ(true, ctest_light_3.hasBrightnessControl()); - EXPECT_EQ(true, test_light_1.hasBrightnessControl()); - EXPECT_EQ(true, test_light_2.hasBrightnessControl()); - EXPECT_EQ(true, test_light_3.hasBrightnessControl()); -} - -TEST_F(HueLightTest, hasTemperatureControl) -{ - const HueLight ctest_light_1 = test_bridge.lights().get(1); - const HueLight ctest_light_2 = test_bridge.lights().get(2); - const HueLight ctest_light_3 = test_bridge.lights().get(3); - HueLight test_light_1 = test_bridge.lights().get(1); - HueLight test_light_2 = test_bridge.lights().get(2); - HueLight test_light_3 = test_bridge.lights().get(3); - - EXPECT_EQ(false, ctest_light_1.hasTemperatureControl()); - EXPECT_EQ(false, ctest_light_2.hasTemperatureControl()); - EXPECT_EQ(true, ctest_light_3.hasTemperatureControl()); - EXPECT_EQ(false, test_light_1.hasTemperatureControl()); - EXPECT_EQ(false, test_light_2.hasTemperatureControl()); - EXPECT_EQ(true, test_light_3.hasTemperatureControl()); -} - -TEST_F(HueLightTest, hasColorControl) -{ - const HueLight ctest_light_1 = test_bridge.lights().get(1); - const HueLight ctest_light_2 = test_bridge.lights().get(2); - const HueLight ctest_light_3 = test_bridge.lights().get(3); - HueLight test_light_1 = test_bridge.lights().get(1); - HueLight test_light_2 = test_bridge.lights().get(2); - HueLight test_light_3 = test_bridge.lights().get(3); - - EXPECT_EQ(false, ctest_light_1.hasColorControl()); - EXPECT_EQ(true, ctest_light_2.hasColorControl()); - EXPECT_EQ(true, ctest_light_3.hasColorControl()); - EXPECT_EQ(false, test_light_1.hasColorControl()); - EXPECT_EQ(true, test_light_2.hasColorControl()); - EXPECT_EQ(true, test_light_3.hasColorControl()); -} - -TEST_F(HueLightTest, setBrightness) -{ - using namespace ::testing; - EXPECT_CALL(*handler, PUTJson("/api/" + getBridgeUsername() + "/lights/1/state", _, getBridgeIp(), 80)) - .Times(1) - .WillOnce(Return(nlohmann::json::array())); - nlohmann::json prep_ret; - prep_ret = nlohmann::json::array(); - prep_ret[0] = nlohmann::json::object(); - prep_ret[0]["success"] = nlohmann::json::object(); - prep_ret[0]["success"]["/lights/3/state/transitiontime"] = 0; - prep_ret[1] = nlohmann::json::object(); - prep_ret[1]["success"] = nlohmann::json::object(); - prep_ret[1]["success"]["/lights/3/state/on"] = true; - prep_ret[2] = nlohmann::json::object(); - prep_ret[2]["success"] = nlohmann::json::object(); - prep_ret[2]["success"]["/lights/3/state/bri"] = 253; - EXPECT_CALL(*handler, PUTJson("/api/" + getBridgeUsername() + "/lights/3/state", _, getBridgeIp(), 80)) - .Times(1) - .WillOnce(Return(prep_ret)); - - HueLight test_light_1 = test_bridge.lights().get(1); - HueLight test_light_2 = test_bridge.lights().get(2); - HueLight test_light_3 = test_bridge.lights().get(3); - - EXPECT_EQ(false, test_light_1.setBrightness(200)); - EXPECT_EQ(true, test_light_2.setBrightness(0, 2)); - EXPECT_EQ(true, test_light_3.setBrightness(253, 0)); -} - -TEST_F(HueLightTest, getBrightness) -{ - const HueLight ctest_light_1 = test_bridge.lights().get(1); - const HueLight ctest_light_2 = test_bridge.lights().get(2); - const HueLight ctest_light_3 = test_bridge.lights().get(3); - HueLight test_light_1 = test_bridge.lights().get(1); - HueLight test_light_2 = test_bridge.lights().get(2); - HueLight test_light_3 = test_bridge.lights().get(3); - - EXPECT_EQ(254, ctest_light_1.getBrightness()); - EXPECT_EQ(0, ctest_light_2.getBrightness()); - EXPECT_EQ(254, ctest_light_3.getBrightness()); - EXPECT_EQ(254, test_light_1.getBrightness()); - EXPECT_EQ(0, test_light_2.getBrightness()); - EXPECT_EQ(254, test_light_3.getBrightness()); -} - -TEST_F(HueLightTest, setColorTemperature) -{ - using namespace ::testing; - nlohmann::json prep_ret; - prep_ret = nlohmann::json::array(); - prep_ret[2] = nlohmann::json::object(); - prep_ret[2]["success"] = nlohmann::json::object(); - prep_ret[2]["success"]["/lights/3/state/ct"] = 153; - prep_ret[1] = nlohmann::json::object(); - prep_ret[1]["success"] = nlohmann::json::object(); - prep_ret[1]["success"]["/lights/3/state/on"] = true; - prep_ret[0] = nlohmann::json::object(); - prep_ret[0]["success"] = nlohmann::json::object(); - prep_ret[0]["success"]["/lights/3/state/transitiontime"] = 0; - EXPECT_CALL(*handler, PUTJson("/api/" + getBridgeUsername() + "/lights/3/state", _, getBridgeIp(), 80)) - .Times(1) - .WillOnce(Return(prep_ret)); - - HueLight test_light_1 = test_bridge.lights().get(1); - HueLight test_light_2 = test_bridge.lights().get(2); - HueLight test_light_3 = test_bridge.lights().get(3); - - EXPECT_EQ(false, test_light_1.setColorTemperature(153)); - EXPECT_EQ(false, test_light_2.setColorTemperature(400, 2)); - EXPECT_EQ(true, test_light_3.setColorTemperature(100, 0)); -} - -TEST_F(HueLightTest, getColorTemperature) -{ - const HueLight ctest_light_1 = test_bridge.lights().get(1); - const HueLight ctest_light_2 = test_bridge.lights().get(2); - const HueLight ctest_light_3 = test_bridge.lights().get(3); - HueLight test_light_1 = test_bridge.lights().get(1); - HueLight test_light_2 = test_bridge.lights().get(2); - HueLight test_light_3 = test_bridge.lights().get(3); - - EXPECT_EQ(0, ctest_light_1.getColorTemperature()); - EXPECT_EQ(0, ctest_light_2.getColorTemperature()); - EXPECT_EQ(366, ctest_light_3.getColorTemperature()); - EXPECT_EQ(0, test_light_1.getColorTemperature()); - EXPECT_EQ(0, test_light_2.getColorTemperature()); - EXPECT_EQ(366, test_light_3.getColorTemperature()); -} - -TEST_F(HueLightTest, setColorHue) -{ - using namespace ::testing; - EXPECT_CALL(*handler, PUTJson("/api/" + getBridgeUsername() + "/lights/2/state", _, getBridgeIp(), 80)) - .Times(1) - .WillOnce(Return(nlohmann::json::array())); - nlohmann::json prep_ret; - prep_ret = nlohmann::json::array(); - prep_ret[2] = nlohmann::json::object(); - prep_ret[2]["success"] = nlohmann::json::object(); - prep_ret[2]["success"]["/lights/3/state/hue"] = 65500; - prep_ret[1] = nlohmann::json::object(); - prep_ret[1]["success"] = nlohmann::json::object(); - prep_ret[1]["success"]["/lights/3/state/on"] = true; - prep_ret[0] = nlohmann::json::object(); - prep_ret[0]["success"] = nlohmann::json::object(); - prep_ret[0]["success"]["/lights/3/state/transitiontime"] = 0; - EXPECT_CALL(*handler, PUTJson("/api/" + getBridgeUsername() + "/lights/3/state", _, getBridgeIp(), 80)) - .Times(1) - .WillOnce(Return(prep_ret)); - - HueLight test_light_1 = test_bridge.lights().get(1); - HueLight test_light_2 = test_bridge.lights().get(2); - HueLight test_light_3 = test_bridge.lights().get(3); - - EXPECT_EQ(false, test_light_1.setColorHue(153)); - EXPECT_EQ(false, test_light_2.setColorHue(30000, 2)); - EXPECT_EQ(true, test_light_3.setColorHue(65500, 0)); -} - -TEST_F(HueLightTest, setColorSaturation) -{ - using namespace ::testing; - EXPECT_CALL(*handler, PUTJson("/api/" + getBridgeUsername() + "/lights/2/state", _, getBridgeIp(), 80)) - .Times(1) - .WillOnce(Return(nlohmann::json::array())); - nlohmann::json prep_ret; - prep_ret = nlohmann::json::array(); - prep_ret[2] = nlohmann::json::object(); - prep_ret[2]["success"] = nlohmann::json::object(); - prep_ret[2]["success"]["/lights/3/state/sat"] = 250; - prep_ret[1] = nlohmann::json::object(); - prep_ret[1]["success"] = nlohmann::json::object(); - prep_ret[1]["success"]["/lights/3/state/on"] = true; - prep_ret[0] = nlohmann::json::object(); - prep_ret[0]["success"] = nlohmann::json::object(); - prep_ret[0]["success"]["/lights/3/state/transitiontime"] = 0; - EXPECT_CALL(*handler, PUTJson("/api/" + getBridgeUsername() + "/lights/3/state", _, getBridgeIp(), 80)) - .Times(1) - .WillOnce(Return(prep_ret)); - - HueLight test_light_1 = test_bridge.lights().get(1); - HueLight test_light_2 = test_bridge.lights().get(2); - HueLight test_light_3 = test_bridge.lights().get(3); - - EXPECT_EQ(false, test_light_1.setColorSaturation(0)); - EXPECT_EQ(false, test_light_2.setColorSaturation(140, 2)); - EXPECT_EQ(true, test_light_3.setColorSaturation(250, 0)); -} - -TEST_F(HueLightTest, setColorHueSaturation) -{ - using namespace ::testing; - EXPECT_CALL(*handler, PUTJson("/api/" + getBridgeUsername() + "/lights/2/state", _, getBridgeIp(), 80)) - .Times(1) - .WillOnce(Return(nlohmann::json::array())); - nlohmann::json prep_ret; - prep_ret = nlohmann::json::array(); - prep_ret[0] = nlohmann::json::object(); - prep_ret[0]["success"] = nlohmann::json::object(); - prep_ret[0]["success"]["/lights/3/state/transitiontime"] = 0; - prep_ret[1] = nlohmann::json::object(); - prep_ret[1]["success"] = nlohmann::json::object(); - prep_ret[1]["success"]["/lights/3/state/on"] = true; - prep_ret[2] = nlohmann::json::object(); - prep_ret[2]["success"] = nlohmann::json::object(); - prep_ret[2]["success"]["/lights/3/state/hue"] = 65500; - prep_ret[3] = nlohmann::json::object(); - prep_ret[3]["success"] = nlohmann::json::object(); - prep_ret[3]["success"]["/lights/3/state/sat"] = 250; - EXPECT_CALL(*handler, PUTJson("/api/" + getBridgeUsername() + "/lights/3/state", _, getBridgeIp(), 80)) - .Times(1) - .WillOnce(Return(prep_ret)); - - HueLight test_light_1 = test_bridge.lights().get(1); - HueLight test_light_2 = test_bridge.lights().get(2); - HueLight test_light_3 = test_bridge.lights().get(3); - - EXPECT_EQ(false, test_light_1.setColorHueSaturation({153, 0})); - EXPECT_EQ(false, test_light_2.setColorHueSaturation({30000, 140}, 2)); - EXPECT_EQ(true, test_light_3.setColorHueSaturation({65500, 250}, 0)); -} - -TEST_F(HueLightTest, getColorHueSaturation) -{ - const HueLight ctest_light_1 = test_bridge.lights().get(1); - const HueLight ctest_light_2 = test_bridge.lights().get(2); - const HueLight ctest_light_3 = test_bridge.lights().get(3); - HueLight test_light_1 = test_bridge.lights().get(1); - HueLight test_light_2 = test_bridge.lights().get(2); - HueLight test_light_3 = test_bridge.lights().get(3); - - EXPECT_EQ((HueSaturation {0, 0}), ctest_light_1.getColorHueSaturation()); - EXPECT_EQ((HueSaturation {12345, 123}), ctest_light_2.getColorHueSaturation()); - EXPECT_EQ((HueSaturation {12345, 123}), ctest_light_3.getColorHueSaturation()); - EXPECT_EQ((HueSaturation {0, 0}), test_light_1.getColorHueSaturation()); - EXPECT_EQ((HueSaturation {12345, 123}), test_light_2.getColorHueSaturation()); - EXPECT_EQ((HueSaturation {12345, 123}), test_light_3.getColorHueSaturation()); -} - -TEST_F(HueLightTest, setColorXY) -{ - using namespace ::testing; - EXPECT_CALL(*handler, PUTJson("/api/" + getBridgeUsername() + "/lights/2/state", _, getBridgeIp(), 80)) - .Times(1) - .WillOnce(Return(nlohmann::json::array())); - nlohmann::json prep_ret; - prep_ret = nlohmann::json::array(); - prep_ret[2] = nlohmann::json::object(); - prep_ret[2]["success"] = nlohmann::json::object(); - prep_ret[2]["success"]["/lights/3/state/xy"][0] = 0.4232; - prep_ret[2]["success"]["/lights/3/state/xy"][1] = 0.1231; - prep_ret[1] = nlohmann::json::object(); - prep_ret[1]["success"] = nlohmann::json::object(); - prep_ret[1]["success"]["/lights/3/state/on"] = true; - prep_ret[0] = nlohmann::json::object(); - prep_ret[0]["success"] = nlohmann::json::object(); - prep_ret[0]["success"]["/lights/3/state/transitiontime"] = 0; - EXPECT_CALL(*handler, PUTJson("/api/" + getBridgeUsername() + "/lights/3/state", _, getBridgeIp(), 80)) - .Times(1) - .WillOnce(Return(prep_ret)); - - HueLight test_light_1 = test_bridge.lights().get(1); - HueLight test_light_2 = test_bridge.lights().get(2); - HueLight test_light_3 = test_bridge.lights().get(3); - - EXPECT_EQ(false, test_light_1.setColorXY({{0.01f, 0.f}, 1.f})); - EXPECT_EQ(false, test_light_2.setColorXY({{0.123f, 1.f}, 1.f}, 2)); - EXPECT_EQ(true, test_light_3.setColorXY({{0.4232f, 0.1231f}, 1.f}, 0)); -} - -TEST_F(HueLightTest, getColorXY) -{ - const HueLight ctest_light_1 = test_bridge.lights().get(1); - const HueLight ctest_light_2 = test_bridge.lights().get(2); - const HueLight ctest_light_3 = test_bridge.lights().get(3); - HueLight test_light_1 = test_bridge.lights().get(1); - HueLight test_light_2 = test_bridge.lights().get(2); - HueLight test_light_3 = test_bridge.lights().get(3); - EXPECT_EQ((XYBrightness {{0.f, 0.f}, 0.f}), ctest_light_1.getColorXY()); - EXPECT_EQ((XYBrightness {{0.102f, 0.102f}, 0.f}), ctest_light_2.getColorXY()); - EXPECT_EQ((XYBrightness {{0.102f, 0.102f}, 1.f}), ctest_light_3.getColorXY()); - EXPECT_EQ((XYBrightness {{0.f, 0.f}, 0.f}), test_light_1.getColorXY()); - EXPECT_EQ((XYBrightness {{0.102f, 0.102f}, 0.f}), test_light_2.getColorXY()); - EXPECT_EQ((XYBrightness {{0.102f, 0.102f}, 1.f}), test_light_3.getColorXY()); -} - -TEST_F(HueLightTest, setColorRGB) -{ - using namespace ::testing; - EXPECT_CALL(*handler, PUTJson("/api/" + getBridgeUsername() + "/lights/2/state", _, getBridgeIp(), 80)) - .Times(1) - .WillOnce(Return(nlohmann::json::array())); - nlohmann::json prep_ret; - prep_ret = nlohmann::json::array(); - prep_ret[2] = nlohmann::json::object(); - prep_ret[2]["success"] = nlohmann::json::object(); - prep_ret[2]["success"]["/lights/3/state/xy"][0] = 0.1596; - prep_ret[2]["success"]["/lights/3/state/xy"][1] = 0.1437; - prep_ret[1] = nlohmann::json::object(); - prep_ret[1]["success"] = nlohmann::json::object(); - prep_ret[1]["success"]["/lights/3/state/on"] = true; - prep_ret[0] = nlohmann::json::object(); - prep_ret[0]["success"] = nlohmann::json::object(); - prep_ret[0]["success"]["/lights/3/state/transitiontime"] = 0; - EXPECT_CALL(*handler, PUTJson("/api/" + getBridgeUsername() + "/lights/3/state", _, getBridgeIp(), 80)) - .Times(1) - .WillOnce(Return(prep_ret)); - - HueLight test_light_1 = test_bridge.lights().get(1); - HueLight test_light_2 = test_bridge.lights().get(2); - HueLight test_light_3 = test_bridge.lights().get(3); - - EXPECT_EQ(false, test_light_1.setColorRGB({0, 0, 0}, 0)); - EXPECT_EQ(false, test_light_2.setColorRGB({32, 64, 128}, 2)); - EXPECT_EQ(true, test_light_3.setColorRGB({64, 128, 255}, 0)); -} - -TEST_F(HueLightTest, alert) -{ - using namespace ::testing; - EXPECT_CALL(*handler, PUTJson("/api/" + getBridgeUsername() + "/lights/1/state", _, getBridgeIp(), 80)) - .Times(1) - .WillOnce(Return(nlohmann::json::array())); - EXPECT_CALL(*handler, PUTJson("/api/" + getBridgeUsername() + "/lights/2/state", _, getBridgeIp(), 80)) - .Times(1) - .WillOnce(Return(nlohmann::json::array())); - nlohmann::json prep_ret; - prep_ret = nlohmann::json::array(); - prep_ret[0] = nlohmann::json::object(); - prep_ret[0]["success"] = nlohmann::json::object(); - prep_ret[0]["success"]["/lights/3/state/alert"] = "select"; - EXPECT_CALL(*handler, PUTJson("/api/" + getBridgeUsername() + "/lights/3/state", _, getBridgeIp(), 80)) - .Times(1) - .WillOnce(Return(prep_ret)); - - HueLight test_light_1 = test_bridge.lights().get(1); - HueLight test_light_2 = test_bridge.lights().get(2); - HueLight test_light_3 = test_bridge.lights().get(3); - - EXPECT_EQ(false, test_light_1.alert()); - EXPECT_EQ(false, test_light_2.alert()); - EXPECT_EQ(true, test_light_3.alert()); -} - -TEST_F(HueLightTest, alertTemperature) -{ - using namespace ::testing; - EXPECT_CALL(*handler, PUTJson("/api/" + getBridgeUsername() + "/lights/3/state", _, getBridgeIp(), 80)) - .Times(1) - .WillOnce(Return(nlohmann::json::array())); - - HueLight test_light_1 = test_bridge.lights().get(1); - HueLight test_light_2 = test_bridge.lights().get(2); - HueLight test_light_3 = test_bridge.lights().get(3); - - EXPECT_EQ(false, test_light_1.alertTemperature(400)); - EXPECT_EQ(false, test_light_2.alertTemperature(100)); - EXPECT_EQ(false, test_light_3.alertTemperature(0)); -} - -TEST_F(HueLightTest, alertHueSaturation) -{ - using namespace ::testing; - EXPECT_CALL(*handler, PUTJson("/api/" + getBridgeUsername() + "/lights/3/state", _, getBridgeIp(), 80)) - .Times(1) - .WillOnce(Return(nlohmann::json::array())); - - HueLight test_light_1 = test_bridge.lights().get(1); - HueLight test_light_2 = test_bridge.lights().get(2); - HueLight test_light_3 = test_bridge.lights().get(3); - - EXPECT_EQ(false, test_light_1.alertHueSaturation({0, 255})); - EXPECT_EQ(false, test_light_2.alertHueSaturation({3000, 100})); - EXPECT_EQ(false, test_light_3.alertHueSaturation({50000, 0})); -} - -TEST_F(HueLightTest, alertXY) -{ - using namespace ::testing; - EXPECT_CALL(*handler, PUTJson("/api/" + getBridgeUsername() + "/lights/3/state", _, getBridgeIp(), 80)) - .Times(1) - .WillOnce(Return(nlohmann::json::array())); - - HueLight test_light_1 = test_bridge.lights().get(1); - HueLight test_light_2 = test_bridge.lights().get(2); - HueLight test_light_3 = test_bridge.lights().get(3); - - EXPECT_EQ(false, test_light_1.alertXY({{0.1f, 0.1f}, 1.f})); - EXPECT_EQ(false, test_light_2.alertXY({{0.2434f, 0.2344f}, 1.f})); - EXPECT_EQ(false, test_light_3.alertXY({{0.1234f, 0.1234f}, 1.f})); -} - -TEST_F(HueLightTest, setColorLoop) -{ - using namespace ::testing; - EXPECT_CALL(*handler, PUTJson("/api/" + getBridgeUsername() + "/lights/2/state", _, getBridgeIp(), 80)) - .Times(1) - .WillOnce(Return(nlohmann::json::array())); - EXPECT_CALL(*handler, PUTJson("/api/" + getBridgeUsername() + "/lights/3/state", _, getBridgeIp(), 80)) - .Times(1) - .WillOnce(Return(nlohmann::json::array())); - - HueLight test_light_1 = test_bridge.lights().get(1); - HueLight test_light_2 = test_bridge.lights().get(2); - HueLight test_light_3 = test_bridge.lights().get(3); - - EXPECT_EQ(false, test_light_1.setColorLoop(true)); - EXPECT_EQ(false, test_light_2.setColorLoop(false)); - EXPECT_EQ(false, test_light_3.setColorLoop(true)); -} diff --git a/test/test_HueLightFactory.cpp b/test/test_HueLightFactory.cpp deleted file mode 100644 index 96ad74e..0000000 --- a/test/test_HueLightFactory.cpp +++ /dev/null @@ -1,243 +0,0 @@ -/** - \file test_HueLightFactory.cpp - Copyright Notice\n - Copyright (C) 2020 Jan Rogall - developer\n - Copyright (C) 2020 Moritz Wirger - developer\n - - This file is part of hueplusplus. - - hueplusplus is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - hueplusplus is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with hueplusplus. If not, see . -**/ - -#include -#include - -#include "testhelper.h" - -#include "mocks/mock_HttpHandler.h" - -using namespace hueplusplus; - -TEST(HueLightFactory, createLight_noGamut) -{ - using namespace ::testing; - std::shared_ptr handler = std::make_shared(); - - HueLightFactory factory(HueCommandAPI(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler), - std::chrono::steady_clock::duration::max()); - - nlohmann::json lightState - = {{"state", - {{"on", true}, {"bri", 254}, {"ct", 366}, {"alert", "none"}, {"colormode", "ct"}, {"reachable", true}}}, - {"swupdate", {{"state", "noupdates"}, {"lastinstall", nullptr}}}, {"type", "Color temperature light"}, - {"name", "Hue ambiance lamp 1"}, {"modelid", "LTW001"}, {"manufacturername", "Philips"}, - {"uniqueid", "00:00:00:00:00:00:00:00-00"}, {"swversion", "5.50.1.19085"}}; - - EXPECT_CALL(*handler, - GETJson("/api/" + getBridgeUsername() + "/lights/1", nlohmann::json::object(), getBridgeIp(), getBridgePort())) - .Times(AtLeast(1)) - .WillRepeatedly(Return(lightState)); - - HueLight test_light_1 = factory.createLight(lightState, 1); - EXPECT_EQ(test_light_1.getColorType(), ColorType::TEMPERATURE); - - lightState["type"] = "Dimmable light"; - - EXPECT_CALL(*handler, - GETJson("/api/" + getBridgeUsername() + "/lights/1", nlohmann::json::object(), getBridgeIp(), getBridgePort())) - .Times(AtLeast(1)) - .WillRepeatedly(Return(lightState)); - - test_light_1 = factory.createLight(lightState, 1); - EXPECT_EQ(test_light_1.getColorType(), ColorType::NONE); - - lightState["type"] = "On/Off light"; - - EXPECT_CALL(*handler, - GETJson("/api/" + getBridgeUsername() + "/lights/1", nlohmann::json::object(), getBridgeIp(), getBridgePort())) - .Times(AtLeast(1)) - .WillRepeatedly(Return(lightState)); - - test_light_1 = factory.createLight(lightState, 1); - EXPECT_EQ(test_light_1.getColorType(), ColorType::NONE); - - lightState["type"] = "unknown light type"; - ASSERT_THROW(factory.createLight(lightState, 1), HueException); -} - -TEST(HueLightFactory, createLight_gamutCapabilities) -{ - using namespace ::testing; - std::shared_ptr handler = std::make_shared(); - - HueLightFactory factory(HueCommandAPI(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler), - std::chrono::steady_clock::duration::max()); - - nlohmann::json lightState - = { {"state", - {{"on", true}, {"bri", 254}, {"ct", 366}, {"alert", "none"}, {"colormode", "ct"}, {"reachable", true}}}, - {"swupdate", {{"state", "noupdates"}, {"lastinstall", nullptr}}}, {"type", "Color light"}, - {"name", "Hue ambiance lamp 1"}, {"modelid", "LTW001"}, {"manufacturername", "Philips"}, - {"uniqueid", "00:00:00:00:00:00:00:00-00"}, {"swversion", "5.50.1.19085"}, - {"capabilities", {{"control", {{"colorgamuttype", "A"}}}}} }; - - EXPECT_CALL(*handler, - GETJson("/api/" + getBridgeUsername() + "/lights/1", nlohmann::json::object(), getBridgeIp(), getBridgePort())) - .Times(AtLeast(1)) - .WillRepeatedly(Return(lightState)); - - HueLight test_light_1 = factory.createLight(lightState, 1); - EXPECT_EQ(test_light_1.getColorType(), ColorType::GAMUT_A); - - lightState["capabilities"]["control"]["colorgamuttype"] = "B"; - - EXPECT_CALL(*handler, - GETJson("/api/" + getBridgeUsername() + "/lights/1", nlohmann::json::object(), getBridgeIp(), getBridgePort())) - .Times(AtLeast(1)) - .WillRepeatedly(Return(lightState)); - - test_light_1 = factory.createLight(lightState, 1); - EXPECT_EQ(test_light_1.getColorType(), ColorType::GAMUT_B); - - lightState["capabilities"]["control"]["colorgamuttype"] = "C"; - - EXPECT_CALL(*handler, - GETJson("/api/" + getBridgeUsername() + "/lights/1", nlohmann::json::object(), getBridgeIp(), getBridgePort())) - .Times(AtLeast(1)) - .WillRepeatedly(Return(lightState)); - - test_light_1 = factory.createLight(lightState, 1); - EXPECT_EQ(test_light_1.getColorType(), ColorType::GAMUT_C); - - lightState["capabilities"]["control"]["colorgamuttype"] = "Other"; - - EXPECT_CALL(*handler, - GETJson("/api/" + getBridgeUsername() + "/lights/1", nlohmann::json::object(), getBridgeIp(), getBridgePort())) - .Times(AtLeast(1)) - .WillRepeatedly(Return(lightState)); - - test_light_1 = factory.createLight(lightState, 1); - EXPECT_EQ(test_light_1.getColorType(), ColorType::UNDEFINED); - - // With color temperature - lightState["type"] = "Extended color light"; - lightState["capabilities"]["control"]["colorgamuttype"] = "A"; - EXPECT_CALL(*handler, - GETJson("/api/" + getBridgeUsername() + "/lights/1", nlohmann::json::object(), getBridgeIp(), getBridgePort())) - .Times(AtLeast(1)) - .WillRepeatedly(Return(lightState)); - - test_light_1 = factory.createLight(lightState, 1); - EXPECT_EQ(test_light_1.getColorType(), ColorType::GAMUT_A_TEMPERATURE); - - lightState["capabilities"]["control"]["colorgamuttype"] = "B"; - EXPECT_CALL(*handler, - GETJson("/api/" + getBridgeUsername() + "/lights/1", nlohmann::json::object(), getBridgeIp(), getBridgePort())) - .Times(AtLeast(1)) - .WillRepeatedly(Return(lightState)); - - test_light_1 = factory.createLight(lightState, 1); - EXPECT_EQ(test_light_1.getColorType(), ColorType::GAMUT_B_TEMPERATURE); - - lightState["capabilities"]["control"]["colorgamuttype"] = "C"; - EXPECT_CALL(*handler, - GETJson("/api/" + getBridgeUsername() + "/lights/1", nlohmann::json::object(), getBridgeIp(), getBridgePort())) - .Times(AtLeast(1)) - .WillRepeatedly(Return(lightState)); - - test_light_1 = factory.createLight(lightState, 1); - EXPECT_EQ(test_light_1.getColorType(), ColorType::GAMUT_C_TEMPERATURE); -} - -TEST(HueLightFactory, createLight_gamutModelid) -{ - using namespace ::testing; - std::shared_ptr handler = std::make_shared(); - - HueLightFactory factory(HueCommandAPI(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler), - std::chrono::steady_clock::duration::max()); - - const std::string gamutAModel = "LST001"; - const std::string gamutBModel = "LCT001"; - const std::string gamutCModel = "LCT010"; - - nlohmann::json lightState - = {{"state", - {{"on", true}, {"bri", 254}, {"ct", 366}, {"alert", "none"}, {"colormode", "ct"}, {"reachable", true}}}, - {"swupdate", {{"state", "noupdates"}, {"lastinstall", nullptr}}}, {"type", "Color light"}, - {"name", "Hue ambiance lamp 1"}, {"modelid", gamutAModel}, {"manufacturername", "Philips"}, - {"uniqueid", "00:00:00:00:00:00:00:00-00"}, {"swversion", "5.50.1.19085"}}; - - EXPECT_CALL(*handler, - GETJson("/api/" + getBridgeUsername() + "/lights/1", nlohmann::json::object(), getBridgeIp(), getBridgePort())) - .Times(AtLeast(1)) - .WillRepeatedly(Return(lightState)); - - HueLight test_light_1 = factory.createLight(lightState, 1); - EXPECT_EQ(test_light_1.getColorType(), ColorType::GAMUT_A); - - lightState["modelid"] = gamutBModel; - EXPECT_CALL(*handler, - GETJson("/api/" + getBridgeUsername() + "/lights/1", nlohmann::json::object(), getBridgeIp(), getBridgePort())) - .Times(AtLeast(1)) - .WillRepeatedly(Return(lightState)); - - test_light_1 = factory.createLight(lightState, 1); - EXPECT_EQ(test_light_1.getColorType(), ColorType::GAMUT_B); - - lightState["modelid"] = gamutCModel; - - EXPECT_CALL(*handler, - GETJson("/api/" + getBridgeUsername() + "/lights/1", nlohmann::json::object(), getBridgeIp(), getBridgePort())) - .Times(AtLeast(1)) - .WillRepeatedly(Return(lightState)); - - test_light_1 = factory.createLight(lightState, 1); - EXPECT_EQ(test_light_1.getColorType(), ColorType::GAMUT_C); - - // With color temperature - lightState["type"] = "Extended color light"; - lightState["modelid"] = gamutAModel; - - EXPECT_CALL(*handler, - GETJson("/api/" + getBridgeUsername() + "/lights/1", nlohmann::json::object(), getBridgeIp(), getBridgePort())) - .Times(AtLeast(1)) - .WillRepeatedly(Return(lightState)); - - test_light_1 = factory.createLight(lightState, 1); - EXPECT_EQ(test_light_1.getColorType(), ColorType::GAMUT_A_TEMPERATURE); - - lightState["modelid"] = gamutBModel; - EXPECT_CALL(*handler, - GETJson("/api/" + getBridgeUsername() + "/lights/1", nlohmann::json::object(), getBridgeIp(), getBridgePort())) - .Times(AtLeast(1)) - .WillRepeatedly(Return(lightState)); - - test_light_1 = factory.createLight(lightState, 1); - EXPECT_EQ(test_light_1.getColorType(), ColorType::GAMUT_B_TEMPERATURE); - - lightState["modelid"] = gamutCModel; - - EXPECT_CALL(*handler, - GETJson("/api/" + getBridgeUsername() + "/lights/1", nlohmann::json::object(), getBridgeIp(), getBridgePort())) - .Times(AtLeast(1)) - .WillRepeatedly(Return(lightState)); - - test_light_1 = factory.createLight(lightState, 1); - EXPECT_EQ(test_light_1.getColorType(), ColorType::GAMUT_C_TEMPERATURE); - - // Unknown model - lightState["modelid"] = "Unknown model"; - EXPECT_THROW(factory.createLight(lightState, 1), HueException); -} diff --git a/test/test_Light.cpp b/test/test_Light.cpp new file mode 100644 index 0000000..f344969 --- /dev/null +++ b/test/test_Light.cpp @@ -0,0 +1,834 @@ +/** + \file test_HueLight.cpp + Copyright Notice\n + Copyright (C) 2017 Jan Rogall - developer\n + Copyright (C) 2017 Moritz Wirger - developer\n + + This file is part of hueplusplus. + + hueplusplus is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + hueplusplus is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with hueplusplus. If not, see . +**/ + +#include +#include + +#include "testhelper.h" + +#include "hueplusplus/Bridge.h" +#include "hueplusplus/Light.h" +#include "json/json.hpp" +#include "mocks/mock_HttpHandler.h" + +using namespace hueplusplus; + +class HueLightTest : public ::testing::Test +{ +protected: + std::shared_ptr handler; + nlohmann::json hue_bridge_state; + Bridge test_bridge; + +protected: + HueLightTest() + : handler(std::make_shared()), + hue_bridge_state({{"lights", + {{"1", + {{"state", + {{"on", true}, {"bri", 254}, {"ct", 366}, {"alert", "none"}, {"colormode", "ct"}, + {"reachable", true}, {"effect", "none"}}}, + {"swupdate", {{"state", "noupdates"}, {"lastinstall", nullptr}}}, {"type", "Dimmable light"}, + {"name", "Hue lamp 1"}, {"modelid", "LWB004"}, {"manufacturername", "Philips"}, + {"productname", "Hue bloom"}, {"uniqueid", "00:00:00:00:00:00:00:00-00"}, + {"swversion", "5.50.1.19085"}, {"luminaireuniqueid", "0000000"}}}, + {"2", + {{"state", + {{"on", false}, {"bri", 0}, {"ct", 366}, {"hue", 12345}, {"sat", 123}, + {"xy", {0.102, 0.102}}, {"alert", "none"}, {"colormode", "ct"}, {"reachable", true}, + {"effect", "none"}}}, + {"swupdate", {{"state", "noupdates"}, {"lastinstall", nullptr}}}, {"type", "Color light"}, + {"name", "Hue lamp 2"}, {"modelid", "LST001"}, {"uniqueid", "11:11:11:11:11:11:11:11-11"}, + {"swversion", "5.50.1.19085"}}}, + {"3", + {{"state", + {{"on", false}, {"bri", 254}, {"ct", 366}, {"hue", 12345}, {"sat", 123}, + {"xy", {0.102, 0.102}}, {"alert", "none"}, {"colormode", "ct"}, {"reachable", true}, + {"effect", "none"}}}, + {"swupdate", {{"state", "noupdates"}, {"lastinstall", nullptr}}}, + {"type", "Extended color light"}, {"name", "Hue lamp 3"}, {"modelid", "LCT010"}, + {"manufacturername", "Philips"}, {"productname", "Hue bloom"}, + {"swversion", "5.50.1.19085"}}}}}}), + test_bridge(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler) + { + using namespace ::testing; + + EXPECT_CALL(*handler, GETJson("/api/" + getBridgeUsername(), nlohmann::json::object(), getBridgeIp(), 80)) + .Times(AtLeast(1)) + .WillRepeatedly(Return(hue_bridge_state)); + EXPECT_CALL( + *handler, GETJson("/api/" + getBridgeUsername() + "/lights/1", nlohmann::json::object(), getBridgeIp(), 80)) + .Times(AtLeast(1)) + .WillRepeatedly(Return(hue_bridge_state["lights"]["1"])); + EXPECT_CALL( + *handler, GETJson("/api/" + getBridgeUsername() + "/lights/2", nlohmann::json::object(), getBridgeIp(), 80)) + .Times(AtLeast(1)) + .WillRepeatedly(Return(hue_bridge_state["lights"]["2"])); + EXPECT_CALL( + *handler, GETJson("/api/" + getBridgeUsername() + "/lights/3", nlohmann::json::object(), getBridgeIp(), 80)) + .Times(AtLeast(1)) + .WillRepeatedly(Return(hue_bridge_state["lights"]["3"])); + } + ~HueLightTest() {}; +}; + +TEST_F(HueLightTest, Constructor) +{ + const Light ctest_light_1 = test_bridge.lights().get(1); + Light test_light_1 = test_bridge.lights().get(1); + const Light ctest_light_2 = test_bridge.lights().get(2); + Light test_light_2 = test_bridge.lights().get(2); + const Light ctest_light_3 = test_bridge.lights().get(3); + Light test_light_3 = test_bridge.lights().get(3); +} + +TEST_F(HueLightTest, On) +{ + using namespace ::testing; + EXPECT_CALL(*handler, PUTJson("/api/" + getBridgeUsername() + "/lights/2/state", _, getBridgeIp(), 80)) + .Times(1) + .WillOnce(Return(nlohmann::json::array())); + + nlohmann::json prep_ret; + prep_ret = nlohmann::json::array(); + prep_ret[0] = nlohmann::json::object(); + prep_ret[0]["success"] = nlohmann::json::object(); + prep_ret[0]["success"]["/lights/3/state/transitiontime"] = 255; + prep_ret[1] = nlohmann::json::object(); + prep_ret[1]["success"] = nlohmann::json::object(); + prep_ret[1]["success"]["/lights/3/state/on"] = true; + EXPECT_CALL(*handler, PUTJson("/api/" + getBridgeUsername() + "/lights/3/state", _, getBridgeIp(), 80)) + .Times(1) + .WillOnce(Return(prep_ret)); + + Light test_light_1 = test_bridge.lights().get(1); + Light test_light_2 = test_bridge.lights().get(2); + Light test_light_3 = test_bridge.lights().get(3); + + EXPECT_EQ(true, test_light_1.On(33)); + EXPECT_EQ(false, test_light_2.On()); + EXPECT_EQ(true, test_light_3.On(255)); +} + +TEST_F(HueLightTest, Off) +{ + using namespace ::testing; + nlohmann::json prep_ret; + prep_ret = nlohmann::json::array(); + prep_ret[0] = nlohmann::json::object(); + prep_ret[0]["success"] = nlohmann::json::object(); + prep_ret[0]["success"]["/lights/1/state/transitiontime"] = 33; + prep_ret[1] = nlohmann::json::object(); + prep_ret[1]["success"] = nlohmann::json::object(); + prep_ret[1]["success"]["/lights/1/state/on"] = false; + EXPECT_CALL(*handler, PUTJson("/api/" + getBridgeUsername() + "/lights/1/state", _, getBridgeIp(), 80)) + .Times(1) + .WillOnce(Return(prep_ret)); + + Light test_light_1 = test_bridge.lights().get(1); + Light test_light_2 = test_bridge.lights().get(2); + Light test_light_3 = test_bridge.lights().get(3); + + EXPECT_EQ(true, test_light_1.Off(33)); + EXPECT_EQ(true, test_light_2.Off()); + EXPECT_EQ(true, test_light_3.Off(255)); +} + +TEST_F(HueLightTest, isOn) +{ + const Light ctest_light_1 = test_bridge.lights().get(1); + const Light ctest_light_2 = test_bridge.lights().get(2); + const Light ctest_light_3 = test_bridge.lights().get(3); + Light test_light_1 = test_bridge.lights().get(1); + Light test_light_2 = test_bridge.lights().get(2); + Light test_light_3 = test_bridge.lights().get(3); + + EXPECT_EQ(true, ctest_light_1.isOn()); + EXPECT_EQ(false, ctest_light_2.isOn()); + EXPECT_EQ(false, ctest_light_3.isOn()); + EXPECT_EQ(true, test_light_1.isOn()); + EXPECT_EQ(false, test_light_2.isOn()); + EXPECT_EQ(false, test_light_3.isOn()); +} + +TEST_F(HueLightTest, getId) +{ + const Light ctest_light_1 = test_bridge.lights().get(1); + const Light ctest_light_2 = test_bridge.lights().get(2); + const Light ctest_light_3 = test_bridge.lights().get(3); + Light test_light_1 = test_bridge.lights().get(1); + Light test_light_2 = test_bridge.lights().get(2); + Light test_light_3 = test_bridge.lights().get(3); + + EXPECT_EQ(1, ctest_light_1.getId()); + EXPECT_EQ(2, ctest_light_2.getId()); + EXPECT_EQ(3, ctest_light_3.getId()); + EXPECT_EQ(1, test_light_1.getId()); + EXPECT_EQ(2, test_light_2.getId()); + EXPECT_EQ(3, test_light_3.getId()); +} + +TEST_F(HueLightTest, getType) +{ + const Light ctest_light_1 = test_bridge.lights().get(1); + const Light ctest_light_2 = test_bridge.lights().get(2); + const Light ctest_light_3 = test_bridge.lights().get(3); + Light test_light_1 = test_bridge.lights().get(1); + Light test_light_2 = test_bridge.lights().get(2); + Light test_light_3 = test_bridge.lights().get(3); + + EXPECT_EQ("Dimmable light", ctest_light_1.getType()); + EXPECT_EQ("Color light", ctest_light_2.getType()); + EXPECT_EQ("Extended color light", ctest_light_3.getType()); + EXPECT_EQ("Dimmable light", test_light_1.getType()); + EXPECT_EQ("Color light", test_light_2.getType()); + EXPECT_EQ("Extended color light", test_light_3.getType()); +} + +TEST_F(HueLightTest, getName) +{ + const Light ctest_light_1 = test_bridge.lights().get(1); + const Light ctest_light_2 = test_bridge.lights().get(2); + const Light ctest_light_3 = test_bridge.lights().get(3); + Light test_light_1 = test_bridge.lights().get(1); + Light test_light_2 = test_bridge.lights().get(2); + Light test_light_3 = test_bridge.lights().get(3); + + EXPECT_EQ("Hue lamp 1", ctest_light_1.getName()); + EXPECT_EQ("Hue lamp 2", ctest_light_2.getName()); + EXPECT_EQ("Hue lamp 3", ctest_light_3.getName()); + EXPECT_EQ("Hue lamp 1", test_light_1.getName()); + EXPECT_EQ("Hue lamp 2", test_light_2.getName()); + EXPECT_EQ("Hue lamp 3", test_light_3.getName()); +} + +TEST_F(HueLightTest, getModelId) +{ + const Light ctest_light_1 = test_bridge.lights().get(1); + const Light ctest_light_2 = test_bridge.lights().get(2); + const Light ctest_light_3 = test_bridge.lights().get(3); + Light test_light_1 = test_bridge.lights().get(1); + Light test_light_2 = test_bridge.lights().get(2); + Light test_light_3 = test_bridge.lights().get(3); + + EXPECT_EQ("LWB004", ctest_light_1.getModelId()); + EXPECT_EQ("LST001", ctest_light_2.getModelId()); + EXPECT_EQ("LCT010", ctest_light_3.getModelId()); + EXPECT_EQ("LWB004", test_light_1.getModelId()); + EXPECT_EQ("LST001", test_light_2.getModelId()); + EXPECT_EQ("LCT010", test_light_3.getModelId()); +} + +TEST_F(HueLightTest, getUId) +{ + const Light ctest_light_1 = test_bridge.lights().get(1); + const Light ctest_light_2 = test_bridge.lights().get(2); + const Light ctest_light_3 = test_bridge.lights().get(3); + Light test_light_1 = test_bridge.lights().get(1); + Light test_light_2 = test_bridge.lights().get(2); + Light test_light_3 = test_bridge.lights().get(3); + + EXPECT_EQ("00:00:00:00:00:00:00:00-00", ctest_light_1.getUId()); + EXPECT_EQ("11:11:11:11:11:11:11:11-11", ctest_light_2.getUId()); + EXPECT_EQ("", ctest_light_3.getUId()); + EXPECT_EQ("00:00:00:00:00:00:00:00-00", test_light_1.getUId()); + EXPECT_EQ("11:11:11:11:11:11:11:11-11", test_light_2.getUId()); + EXPECT_EQ("", test_light_3.getUId()); +} + +TEST_F(HueLightTest, getManufacturername) +{ + const Light ctest_light_1 = test_bridge.lights().get(1); + const Light ctest_light_2 = test_bridge.lights().get(2); + const Light ctest_light_3 = test_bridge.lights().get(3); + Light test_light_1 = test_bridge.lights().get(1); + Light test_light_2 = test_bridge.lights().get(2); + Light test_light_3 = test_bridge.lights().get(3); + + EXPECT_EQ("Philips", ctest_light_1.getManufacturername()); + EXPECT_EQ("", ctest_light_2.getManufacturername()); + EXPECT_EQ("Philips", ctest_light_3.getManufacturername()); + EXPECT_EQ("Philips", test_light_1.getManufacturername()); + EXPECT_EQ("", test_light_2.getManufacturername()); + EXPECT_EQ("Philips", test_light_3.getManufacturername()); +} + +TEST_F(HueLightTest, getProductname) +{ + const Light ctest_light_1 = test_bridge.lights().get(1); + const Light ctest_light_2 = test_bridge.lights().get(2); + const Light ctest_light_3 = test_bridge.lights().get(3); + Light test_light_1 = test_bridge.lights().get(1); + Light test_light_2 = test_bridge.lights().get(2); + Light test_light_3 = test_bridge.lights().get(3); + + EXPECT_EQ("Hue bloom", ctest_light_1.getProductname()); + EXPECT_EQ("", ctest_light_2.getProductname()); + EXPECT_EQ("Hue bloom", ctest_light_3.getProductname()); + EXPECT_EQ("Hue bloom", test_light_1.getProductname()); + EXPECT_EQ("", test_light_2.getProductname()); + EXPECT_EQ("Hue bloom", test_light_3.getProductname()); +} + +TEST_F(HueLightTest, getLuminaireUId) +{ + const Light ctest_light_1 = test_bridge.lights().get(1); + const Light ctest_light_2 = test_bridge.lights().get(2); + const Light ctest_light_3 = test_bridge.lights().get(3); + Light test_light_1 = test_bridge.lights().get(1); + Light test_light_2 = test_bridge.lights().get(2); + Light test_light_3 = test_bridge.lights().get(3); + + EXPECT_EQ("0000000", ctest_light_1.getLuminaireUId()); + EXPECT_EQ("", ctest_light_2.getLuminaireUId()); + EXPECT_EQ("", ctest_light_3.getLuminaireUId()); + EXPECT_EQ("0000000", test_light_1.getLuminaireUId()); + EXPECT_EQ("", test_light_2.getLuminaireUId()); + EXPECT_EQ("", test_light_3.getLuminaireUId()); +} + +TEST_F(HueLightTest, getSwVersion) +{ + const Light ctest_light_1 = test_bridge.lights().get(1); + const Light ctest_light_2 = test_bridge.lights().get(2); + const Light ctest_light_3 = test_bridge.lights().get(3); + Light test_light_1 = test_bridge.lights().get(1); + Light test_light_2 = test_bridge.lights().get(2); + Light test_light_3 = test_bridge.lights().get(3); + + EXPECT_EQ("5.50.1.19085", ctest_light_1.getSwVersion()); + EXPECT_EQ("5.50.1.19085", ctest_light_2.getSwVersion()); + EXPECT_EQ("5.50.1.19085", ctest_light_3.getSwVersion()); + EXPECT_EQ("5.50.1.19085", test_light_1.getSwVersion()); + EXPECT_EQ("5.50.1.19085", test_light_2.getSwVersion()); + EXPECT_EQ("5.50.1.19085", test_light_3.getSwVersion()); +} + +TEST_F(HueLightTest, setName) +{ + using namespace ::testing; + nlohmann::json expected_request({}); + expected_request["name"] = "Baskj189"; + nlohmann::json prep_ret; + prep_ret = nlohmann::json::array(); + prep_ret[0] = nlohmann::json::object(); + prep_ret[0]["success"] = nlohmann::json::object(); + prep_ret[0]["success"]["/lights/1/name"] = expected_request["name"]; + EXPECT_CALL( + *handler, PUTJson("/api/" + getBridgeUsername() + "/lights/1/name", expected_request, getBridgeIp(), 80)) + .Times(1) + .WillOnce(Return(prep_ret)); + EXPECT_CALL( + *handler, PUTJson("/api/" + getBridgeUsername() + "/lights/2/name", expected_request, getBridgeIp(), 80)) + .Times(1) + .WillOnce(Return(nlohmann::json::array())); + prep_ret[0]["success"] = nlohmann::json::object(); + prep_ret[0]["success"]["/lights/3/name"] = expected_request["name"]; + EXPECT_CALL( + *handler, PUTJson("/api/" + getBridgeUsername() + "/lights/3/name", expected_request, getBridgeIp(), 80)) + .Times(1) + .WillOnce(Return(prep_ret)); + + Light test_light_1 = test_bridge.lights().get(1); + Light test_light_2 = test_bridge.lights().get(2); + Light test_light_3 = test_bridge.lights().get(3); + + EXPECT_EQ(true, test_light_1.setName(expected_request["name"].get())); + EXPECT_EQ(false, test_light_2.setName(expected_request["name"].get())); + EXPECT_EQ(true, test_light_3.setName(expected_request["name"].get())); +} + +TEST_F(HueLightTest, getColorType) +{ + const Light ctest_light_1 = test_bridge.lights().get(1); + const Light ctest_light_2 = test_bridge.lights().get(2); + const Light ctest_light_3 = test_bridge.lights().get(3); + Light test_light_1 = test_bridge.lights().get(1); + Light test_light_2 = test_bridge.lights().get(2); + Light test_light_3 = test_bridge.lights().get(3); + + EXPECT_EQ(ColorType::NONE, ctest_light_1.getColorType()); + EXPECT_EQ(ColorType::GAMUT_A, ctest_light_2.getColorType()); + EXPECT_EQ(ColorType::GAMUT_C_TEMPERATURE, ctest_light_3.getColorType()); + EXPECT_EQ(ColorType::NONE, test_light_1.getColorType()); + EXPECT_EQ(ColorType::GAMUT_A, test_light_2.getColorType()); + EXPECT_EQ(ColorType::GAMUT_C_TEMPERATURE, test_light_3.getColorType()); +} + +TEST_F(HueLightTest, KelvinToMired) +{ + const Light ctest_light_1 = test_bridge.lights().get(1); + const Light ctest_light_2 = test_bridge.lights().get(2); + const Light ctest_light_3 = test_bridge.lights().get(3); + Light test_light_1 = test_bridge.lights().get(1); + Light test_light_2 = test_bridge.lights().get(2); + Light test_light_3 = test_bridge.lights().get(3); + + EXPECT_EQ(10000, ctest_light_1.KelvinToMired(100)); + EXPECT_EQ(500, ctest_light_2.KelvinToMired(2000)); + EXPECT_EQ(303, ctest_light_3.KelvinToMired(3300)); + EXPECT_EQ(250, test_light_1.KelvinToMired(4000)); + EXPECT_EQ(200, test_light_2.KelvinToMired(5000)); + EXPECT_EQ(166, test_light_3.KelvinToMired(6000)); +} + +TEST_F(HueLightTest, MiredToKelvin) +{ + const Light ctest_light_1 = test_bridge.lights().get(1); + const Light ctest_light_2 = test_bridge.lights().get(2); + const Light ctest_light_3 = test_bridge.lights().get(3); + Light test_light_1 = test_bridge.lights().get(1); + Light test_light_2 = test_bridge.lights().get(2); + Light test_light_3 = test_bridge.lights().get(3); + + EXPECT_EQ(100, ctest_light_1.MiredToKelvin(10000)); + EXPECT_EQ(2000, ctest_light_2.MiredToKelvin(500)); + EXPECT_EQ(3300, ctest_light_3.MiredToKelvin(303)); + EXPECT_EQ(4000, test_light_1.MiredToKelvin(250)); + EXPECT_EQ(5000, test_light_2.MiredToKelvin(200)); + EXPECT_EQ(6024, test_light_3.MiredToKelvin(166)); // 6000 kelvin should be 166 mired, but keep in + // mind flops are not exact +} + +TEST_F(HueLightTest, hasBrightnessControl) +{ + const Light ctest_light_1 = test_bridge.lights().get(1); + const Light ctest_light_2 = test_bridge.lights().get(2); + const Light ctest_light_3 = test_bridge.lights().get(3); + Light test_light_1 = test_bridge.lights().get(1); + Light test_light_2 = test_bridge.lights().get(2); + Light test_light_3 = test_bridge.lights().get(3); + + EXPECT_EQ(true, ctest_light_1.hasBrightnessControl()); + EXPECT_EQ(true, ctest_light_2.hasBrightnessControl()); + EXPECT_EQ(true, ctest_light_3.hasBrightnessControl()); + EXPECT_EQ(true, test_light_1.hasBrightnessControl()); + EXPECT_EQ(true, test_light_2.hasBrightnessControl()); + EXPECT_EQ(true, test_light_3.hasBrightnessControl()); +} + +TEST_F(HueLightTest, hasTemperatureControl) +{ + const Light ctest_light_1 = test_bridge.lights().get(1); + const Light ctest_light_2 = test_bridge.lights().get(2); + const Light ctest_light_3 = test_bridge.lights().get(3); + Light test_light_1 = test_bridge.lights().get(1); + Light test_light_2 = test_bridge.lights().get(2); + Light test_light_3 = test_bridge.lights().get(3); + + EXPECT_EQ(false, ctest_light_1.hasTemperatureControl()); + EXPECT_EQ(false, ctest_light_2.hasTemperatureControl()); + EXPECT_EQ(true, ctest_light_3.hasTemperatureControl()); + EXPECT_EQ(false, test_light_1.hasTemperatureControl()); + EXPECT_EQ(false, test_light_2.hasTemperatureControl()); + EXPECT_EQ(true, test_light_3.hasTemperatureControl()); +} + +TEST_F(HueLightTest, hasColorControl) +{ + const Light ctest_light_1 = test_bridge.lights().get(1); + const Light ctest_light_2 = test_bridge.lights().get(2); + const Light ctest_light_3 = test_bridge.lights().get(3); + Light test_light_1 = test_bridge.lights().get(1); + Light test_light_2 = test_bridge.lights().get(2); + Light test_light_3 = test_bridge.lights().get(3); + + EXPECT_EQ(false, ctest_light_1.hasColorControl()); + EXPECT_EQ(true, ctest_light_2.hasColorControl()); + EXPECT_EQ(true, ctest_light_3.hasColorControl()); + EXPECT_EQ(false, test_light_1.hasColorControl()); + EXPECT_EQ(true, test_light_2.hasColorControl()); + EXPECT_EQ(true, test_light_3.hasColorControl()); +} + +TEST_F(HueLightTest, setBrightness) +{ + using namespace ::testing; + EXPECT_CALL(*handler, PUTJson("/api/" + getBridgeUsername() + "/lights/1/state", _, getBridgeIp(), 80)) + .Times(1) + .WillOnce(Return(nlohmann::json::array())); + nlohmann::json prep_ret; + prep_ret = nlohmann::json::array(); + prep_ret[0] = nlohmann::json::object(); + prep_ret[0]["success"] = nlohmann::json::object(); + prep_ret[0]["success"]["/lights/3/state/transitiontime"] = 0; + prep_ret[1] = nlohmann::json::object(); + prep_ret[1]["success"] = nlohmann::json::object(); + prep_ret[1]["success"]["/lights/3/state/on"] = true; + prep_ret[2] = nlohmann::json::object(); + prep_ret[2]["success"] = nlohmann::json::object(); + prep_ret[2]["success"]["/lights/3/state/bri"] = 253; + EXPECT_CALL(*handler, PUTJson("/api/" + getBridgeUsername() + "/lights/3/state", _, getBridgeIp(), 80)) + .Times(1) + .WillOnce(Return(prep_ret)); + + Light test_light_1 = test_bridge.lights().get(1); + Light test_light_2 = test_bridge.lights().get(2); + Light test_light_3 = test_bridge.lights().get(3); + + EXPECT_EQ(false, test_light_1.setBrightness(200)); + EXPECT_EQ(true, test_light_2.setBrightness(0, 2)); + EXPECT_EQ(true, test_light_3.setBrightness(253, 0)); +} + +TEST_F(HueLightTest, getBrightness) +{ + const Light ctest_light_1 = test_bridge.lights().get(1); + const Light ctest_light_2 = test_bridge.lights().get(2); + const Light ctest_light_3 = test_bridge.lights().get(3); + Light test_light_1 = test_bridge.lights().get(1); + Light test_light_2 = test_bridge.lights().get(2); + Light test_light_3 = test_bridge.lights().get(3); + + EXPECT_EQ(254, ctest_light_1.getBrightness()); + EXPECT_EQ(0, ctest_light_2.getBrightness()); + EXPECT_EQ(254, ctest_light_3.getBrightness()); + EXPECT_EQ(254, test_light_1.getBrightness()); + EXPECT_EQ(0, test_light_2.getBrightness()); + EXPECT_EQ(254, test_light_3.getBrightness()); +} + +TEST_F(HueLightTest, setColorTemperature) +{ + using namespace ::testing; + nlohmann::json prep_ret; + prep_ret = nlohmann::json::array(); + prep_ret[2] = nlohmann::json::object(); + prep_ret[2]["success"] = nlohmann::json::object(); + prep_ret[2]["success"]["/lights/3/state/ct"] = 153; + prep_ret[1] = nlohmann::json::object(); + prep_ret[1]["success"] = nlohmann::json::object(); + prep_ret[1]["success"]["/lights/3/state/on"] = true; + prep_ret[0] = nlohmann::json::object(); + prep_ret[0]["success"] = nlohmann::json::object(); + prep_ret[0]["success"]["/lights/3/state/transitiontime"] = 0; + EXPECT_CALL(*handler, PUTJson("/api/" + getBridgeUsername() + "/lights/3/state", _, getBridgeIp(), 80)) + .Times(1) + .WillOnce(Return(prep_ret)); + + Light test_light_1 = test_bridge.lights().get(1); + Light test_light_2 = test_bridge.lights().get(2); + Light test_light_3 = test_bridge.lights().get(3); + + EXPECT_EQ(false, test_light_1.setColorTemperature(153)); + EXPECT_EQ(false, test_light_2.setColorTemperature(400, 2)); + EXPECT_EQ(true, test_light_3.setColorTemperature(100, 0)); +} + +TEST_F(HueLightTest, getColorTemperature) +{ + const Light ctest_light_1 = test_bridge.lights().get(1); + const Light ctest_light_2 = test_bridge.lights().get(2); + const Light ctest_light_3 = test_bridge.lights().get(3); + Light test_light_1 = test_bridge.lights().get(1); + Light test_light_2 = test_bridge.lights().get(2); + Light test_light_3 = test_bridge.lights().get(3); + + EXPECT_EQ(0, ctest_light_1.getColorTemperature()); + EXPECT_EQ(0, ctest_light_2.getColorTemperature()); + EXPECT_EQ(366, ctest_light_3.getColorTemperature()); + EXPECT_EQ(0, test_light_1.getColorTemperature()); + EXPECT_EQ(0, test_light_2.getColorTemperature()); + EXPECT_EQ(366, test_light_3.getColorTemperature()); +} + +TEST_F(HueLightTest, setColorHue) +{ + using namespace ::testing; + EXPECT_CALL(*handler, PUTJson("/api/" + getBridgeUsername() + "/lights/2/state", _, getBridgeIp(), 80)) + .Times(1) + .WillOnce(Return(nlohmann::json::array())); + nlohmann::json prep_ret; + prep_ret = nlohmann::json::array(); + prep_ret[2] = nlohmann::json::object(); + prep_ret[2]["success"] = nlohmann::json::object(); + prep_ret[2]["success"]["/lights/3/state/hue"] = 65500; + prep_ret[1] = nlohmann::json::object(); + prep_ret[1]["success"] = nlohmann::json::object(); + prep_ret[1]["success"]["/lights/3/state/on"] = true; + prep_ret[0] = nlohmann::json::object(); + prep_ret[0]["success"] = nlohmann::json::object(); + prep_ret[0]["success"]["/lights/3/state/transitiontime"] = 0; + EXPECT_CALL(*handler, PUTJson("/api/" + getBridgeUsername() + "/lights/3/state", _, getBridgeIp(), 80)) + .Times(1) + .WillOnce(Return(prep_ret)); + + Light test_light_1 = test_bridge.lights().get(1); + Light test_light_2 = test_bridge.lights().get(2); + Light test_light_3 = test_bridge.lights().get(3); + + EXPECT_EQ(false, test_light_1.setColorHue(153)); + EXPECT_EQ(false, test_light_2.setColorHue(30000, 2)); + EXPECT_EQ(true, test_light_3.setColorHue(65500, 0)); +} + +TEST_F(HueLightTest, setColorSaturation) +{ + using namespace ::testing; + EXPECT_CALL(*handler, PUTJson("/api/" + getBridgeUsername() + "/lights/2/state", _, getBridgeIp(), 80)) + .Times(1) + .WillOnce(Return(nlohmann::json::array())); + nlohmann::json prep_ret; + prep_ret = nlohmann::json::array(); + prep_ret[2] = nlohmann::json::object(); + prep_ret[2]["success"] = nlohmann::json::object(); + prep_ret[2]["success"]["/lights/3/state/sat"] = 250; + prep_ret[1] = nlohmann::json::object(); + prep_ret[1]["success"] = nlohmann::json::object(); + prep_ret[1]["success"]["/lights/3/state/on"] = true; + prep_ret[0] = nlohmann::json::object(); + prep_ret[0]["success"] = nlohmann::json::object(); + prep_ret[0]["success"]["/lights/3/state/transitiontime"] = 0; + EXPECT_CALL(*handler, PUTJson("/api/" + getBridgeUsername() + "/lights/3/state", _, getBridgeIp(), 80)) + .Times(1) + .WillOnce(Return(prep_ret)); + + Light test_light_1 = test_bridge.lights().get(1); + Light test_light_2 = test_bridge.lights().get(2); + Light test_light_3 = test_bridge.lights().get(3); + + EXPECT_EQ(false, test_light_1.setColorSaturation(0)); + EXPECT_EQ(false, test_light_2.setColorSaturation(140, 2)); + EXPECT_EQ(true, test_light_3.setColorSaturation(250, 0)); +} + +TEST_F(HueLightTest, setColorHueSaturation) +{ + using namespace ::testing; + EXPECT_CALL(*handler, PUTJson("/api/" + getBridgeUsername() + "/lights/2/state", _, getBridgeIp(), 80)) + .Times(1) + .WillOnce(Return(nlohmann::json::array())); + nlohmann::json prep_ret; + prep_ret = nlohmann::json::array(); + prep_ret[0] = nlohmann::json::object(); + prep_ret[0]["success"] = nlohmann::json::object(); + prep_ret[0]["success"]["/lights/3/state/transitiontime"] = 0; + prep_ret[1] = nlohmann::json::object(); + prep_ret[1]["success"] = nlohmann::json::object(); + prep_ret[1]["success"]["/lights/3/state/on"] = true; + prep_ret[2] = nlohmann::json::object(); + prep_ret[2]["success"] = nlohmann::json::object(); + prep_ret[2]["success"]["/lights/3/state/hue"] = 65500; + prep_ret[3] = nlohmann::json::object(); + prep_ret[3]["success"] = nlohmann::json::object(); + prep_ret[3]["success"]["/lights/3/state/sat"] = 250; + EXPECT_CALL(*handler, PUTJson("/api/" + getBridgeUsername() + "/lights/3/state", _, getBridgeIp(), 80)) + .Times(1) + .WillOnce(Return(prep_ret)); + + Light test_light_1 = test_bridge.lights().get(1); + Light test_light_2 = test_bridge.lights().get(2); + Light test_light_3 = test_bridge.lights().get(3); + + EXPECT_EQ(false, test_light_1.setColorHueSaturation({153, 0})); + EXPECT_EQ(false, test_light_2.setColorHueSaturation({30000, 140}, 2)); + EXPECT_EQ(true, test_light_3.setColorHueSaturation({65500, 250}, 0)); +} + +TEST_F(HueLightTest, getColorHueSaturation) +{ + const Light ctest_light_1 = test_bridge.lights().get(1); + const Light ctest_light_2 = test_bridge.lights().get(2); + const Light ctest_light_3 = test_bridge.lights().get(3); + Light test_light_1 = test_bridge.lights().get(1); + Light test_light_2 = test_bridge.lights().get(2); + Light test_light_3 = test_bridge.lights().get(3); + + EXPECT_EQ((HueSaturation {0, 0}), ctest_light_1.getColorHueSaturation()); + EXPECT_EQ((HueSaturation {12345, 123}), ctest_light_2.getColorHueSaturation()); + EXPECT_EQ((HueSaturation {12345, 123}), ctest_light_3.getColorHueSaturation()); + EXPECT_EQ((HueSaturation {0, 0}), test_light_1.getColorHueSaturation()); + EXPECT_EQ((HueSaturation {12345, 123}), test_light_2.getColorHueSaturation()); + EXPECT_EQ((HueSaturation {12345, 123}), test_light_3.getColorHueSaturation()); +} + +TEST_F(HueLightTest, setColorXY) +{ + using namespace ::testing; + EXPECT_CALL(*handler, PUTJson("/api/" + getBridgeUsername() + "/lights/2/state", _, getBridgeIp(), 80)) + .Times(1) + .WillOnce(Return(nlohmann::json::array())); + nlohmann::json prep_ret; + prep_ret = nlohmann::json::array(); + prep_ret[2] = nlohmann::json::object(); + prep_ret[2]["success"] = nlohmann::json::object(); + prep_ret[2]["success"]["/lights/3/state/xy"][0] = 0.4232; + prep_ret[2]["success"]["/lights/3/state/xy"][1] = 0.1231; + prep_ret[1] = nlohmann::json::object(); + prep_ret[1]["success"] = nlohmann::json::object(); + prep_ret[1]["success"]["/lights/3/state/on"] = true; + prep_ret[0] = nlohmann::json::object(); + prep_ret[0]["success"] = nlohmann::json::object(); + prep_ret[0]["success"]["/lights/3/state/transitiontime"] = 0; + EXPECT_CALL(*handler, PUTJson("/api/" + getBridgeUsername() + "/lights/3/state", _, getBridgeIp(), 80)) + .Times(1) + .WillOnce(Return(prep_ret)); + + Light test_light_1 = test_bridge.lights().get(1); + Light test_light_2 = test_bridge.lights().get(2); + Light test_light_3 = test_bridge.lights().get(3); + + EXPECT_EQ(false, test_light_1.setColorXY({{0.01f, 0.f}, 1.f})); + EXPECT_EQ(false, test_light_2.setColorXY({{0.123f, 1.f}, 1.f}, 2)); + EXPECT_EQ(true, test_light_3.setColorXY({{0.4232f, 0.1231f}, 1.f}, 0)); +} + +TEST_F(HueLightTest, getColorXY) +{ + const Light ctest_light_1 = test_bridge.lights().get(1); + const Light ctest_light_2 = test_bridge.lights().get(2); + const Light ctest_light_3 = test_bridge.lights().get(3); + Light test_light_1 = test_bridge.lights().get(1); + Light test_light_2 = test_bridge.lights().get(2); + Light test_light_3 = test_bridge.lights().get(3); + EXPECT_EQ((XYBrightness {{0.f, 0.f}, 0.f}), ctest_light_1.getColorXY()); + EXPECT_EQ((XYBrightness {{0.102f, 0.102f}, 0.f}), ctest_light_2.getColorXY()); + EXPECT_EQ((XYBrightness {{0.102f, 0.102f}, 1.f}), ctest_light_3.getColorXY()); + EXPECT_EQ((XYBrightness {{0.f, 0.f}, 0.f}), test_light_1.getColorXY()); + EXPECT_EQ((XYBrightness {{0.102f, 0.102f}, 0.f}), test_light_2.getColorXY()); + EXPECT_EQ((XYBrightness {{0.102f, 0.102f}, 1.f}), test_light_3.getColorXY()); +} + +TEST_F(HueLightTest, setColorRGB) +{ + using namespace ::testing; + EXPECT_CALL(*handler, PUTJson("/api/" + getBridgeUsername() + "/lights/2/state", _, getBridgeIp(), 80)) + .Times(1) + .WillOnce(Return(nlohmann::json::array())); + nlohmann::json prep_ret; + prep_ret = nlohmann::json::array(); + prep_ret[2] = nlohmann::json::object(); + prep_ret[2]["success"] = nlohmann::json::object(); + prep_ret[2]["success"]["/lights/3/state/xy"][0] = 0.1596; + prep_ret[2]["success"]["/lights/3/state/xy"][1] = 0.1437; + prep_ret[1] = nlohmann::json::object(); + prep_ret[1]["success"] = nlohmann::json::object(); + prep_ret[1]["success"]["/lights/3/state/on"] = true; + prep_ret[0] = nlohmann::json::object(); + prep_ret[0]["success"] = nlohmann::json::object(); + prep_ret[0]["success"]["/lights/3/state/transitiontime"] = 0; + EXPECT_CALL(*handler, PUTJson("/api/" + getBridgeUsername() + "/lights/3/state", _, getBridgeIp(), 80)) + .Times(1) + .WillOnce(Return(prep_ret)); + + Light test_light_1 = test_bridge.lights().get(1); + Light test_light_2 = test_bridge.lights().get(2); + Light test_light_3 = test_bridge.lights().get(3); + + EXPECT_EQ(false, test_light_1.setColorRGB({0, 0, 0}, 0)); + EXPECT_EQ(false, test_light_2.setColorRGB({32, 64, 128}, 2)); + EXPECT_EQ(true, test_light_3.setColorRGB({64, 128, 255}, 0)); +} + +TEST_F(HueLightTest, alert) +{ + using namespace ::testing; + EXPECT_CALL(*handler, PUTJson("/api/" + getBridgeUsername() + "/lights/1/state", _, getBridgeIp(), 80)) + .Times(1) + .WillOnce(Return(nlohmann::json::array())); + EXPECT_CALL(*handler, PUTJson("/api/" + getBridgeUsername() + "/lights/2/state", _, getBridgeIp(), 80)) + .Times(1) + .WillOnce(Return(nlohmann::json::array())); + nlohmann::json prep_ret; + prep_ret = nlohmann::json::array(); + prep_ret[0] = nlohmann::json::object(); + prep_ret[0]["success"] = nlohmann::json::object(); + prep_ret[0]["success"]["/lights/3/state/alert"] = "select"; + EXPECT_CALL(*handler, PUTJson("/api/" + getBridgeUsername() + "/lights/3/state", _, getBridgeIp(), 80)) + .Times(1) + .WillOnce(Return(prep_ret)); + + Light test_light_1 = test_bridge.lights().get(1); + Light test_light_2 = test_bridge.lights().get(2); + Light test_light_3 = test_bridge.lights().get(3); + + EXPECT_EQ(false, test_light_1.alert()); + EXPECT_EQ(false, test_light_2.alert()); + EXPECT_EQ(true, test_light_3.alert()); +} + +TEST_F(HueLightTest, alertTemperature) +{ + using namespace ::testing; + EXPECT_CALL(*handler, PUTJson("/api/" + getBridgeUsername() + "/lights/3/state", _, getBridgeIp(), 80)) + .Times(1) + .WillOnce(Return(nlohmann::json::array())); + + Light test_light_1 = test_bridge.lights().get(1); + Light test_light_2 = test_bridge.lights().get(2); + Light test_light_3 = test_bridge.lights().get(3); + + EXPECT_EQ(false, test_light_1.alertTemperature(400)); + EXPECT_EQ(false, test_light_2.alertTemperature(100)); + EXPECT_EQ(false, test_light_3.alertTemperature(0)); +} + +TEST_F(HueLightTest, alertHueSaturation) +{ + using namespace ::testing; + EXPECT_CALL(*handler, PUTJson("/api/" + getBridgeUsername() + "/lights/3/state", _, getBridgeIp(), 80)) + .Times(1) + .WillOnce(Return(nlohmann::json::array())); + + Light test_light_1 = test_bridge.lights().get(1); + Light test_light_2 = test_bridge.lights().get(2); + Light test_light_3 = test_bridge.lights().get(3); + + EXPECT_EQ(false, test_light_1.alertHueSaturation({0, 255})); + EXPECT_EQ(false, test_light_2.alertHueSaturation({3000, 100})); + EXPECT_EQ(false, test_light_3.alertHueSaturation({50000, 0})); +} + +TEST_F(HueLightTest, alertXY) +{ + using namespace ::testing; + EXPECT_CALL(*handler, PUTJson("/api/" + getBridgeUsername() + "/lights/3/state", _, getBridgeIp(), 80)) + .Times(1) + .WillOnce(Return(nlohmann::json::array())); + + Light test_light_1 = test_bridge.lights().get(1); + Light test_light_2 = test_bridge.lights().get(2); + Light test_light_3 = test_bridge.lights().get(3); + + EXPECT_EQ(false, test_light_1.alertXY({{0.1f, 0.1f}, 1.f})); + EXPECT_EQ(false, test_light_2.alertXY({{0.2434f, 0.2344f}, 1.f})); + EXPECT_EQ(false, test_light_3.alertXY({{0.1234f, 0.1234f}, 1.f})); +} + +TEST_F(HueLightTest, setColorLoop) +{ + using namespace ::testing; + EXPECT_CALL(*handler, PUTJson("/api/" + getBridgeUsername() + "/lights/2/state", _, getBridgeIp(), 80)) + .Times(1) + .WillOnce(Return(nlohmann::json::array())); + EXPECT_CALL(*handler, PUTJson("/api/" + getBridgeUsername() + "/lights/3/state", _, getBridgeIp(), 80)) + .Times(1) + .WillOnce(Return(nlohmann::json::array())); + + Light test_light_1 = test_bridge.lights().get(1); + Light test_light_2 = test_bridge.lights().get(2); + Light test_light_3 = test_bridge.lights().get(3); + + EXPECT_EQ(false, test_light_1.setColorLoop(true)); + EXPECT_EQ(false, test_light_2.setColorLoop(false)); + EXPECT_EQ(false, test_light_3.setColorLoop(true)); +} diff --git a/test/test_LightFactory.cpp b/test/test_LightFactory.cpp new file mode 100644 index 0000000..22fc00f --- /dev/null +++ b/test/test_LightFactory.cpp @@ -0,0 +1,243 @@ +/** + \file test_HueLightFactory.cpp + Copyright Notice\n + Copyright (C) 2020 Jan Rogall - developer\n + Copyright (C) 2020 Moritz Wirger - developer\n + + This file is part of hueplusplus. + + hueplusplus is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + hueplusplus is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with hueplusplus. If not, see . +**/ + +#include +#include + +#include "testhelper.h" + +#include "mocks/mock_HttpHandler.h" + +using namespace hueplusplus; + +TEST(LightFactory, createLight_noGamut) +{ + using namespace ::testing; + std::shared_ptr handler = std::make_shared(); + + LightFactory factory(HueCommandAPI(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler), + std::chrono::steady_clock::duration::max()); + + nlohmann::json lightState + = {{"state", + {{"on", true}, {"bri", 254}, {"ct", 366}, {"alert", "none"}, {"colormode", "ct"}, {"reachable", true}}}, + {"swupdate", {{"state", "noupdates"}, {"lastinstall", nullptr}}}, {"type", "Color temperature light"}, + {"name", "Hue ambiance lamp 1"}, {"modelid", "LTW001"}, {"manufacturername", "Philips"}, + {"uniqueid", "00:00:00:00:00:00:00:00-00"}, {"swversion", "5.50.1.19085"}}; + + EXPECT_CALL(*handler, + GETJson("/api/" + getBridgeUsername() + "/lights/1", nlohmann::json::object(), getBridgeIp(), getBridgePort())) + .Times(AtLeast(1)) + .WillRepeatedly(Return(lightState)); + + Light test_light_1 = factory.createLight(lightState, 1); + EXPECT_EQ(test_light_1.getColorType(), ColorType::TEMPERATURE); + + lightState["type"] = "Dimmable light"; + + EXPECT_CALL(*handler, + GETJson("/api/" + getBridgeUsername() + "/lights/1", nlohmann::json::object(), getBridgeIp(), getBridgePort())) + .Times(AtLeast(1)) + .WillRepeatedly(Return(lightState)); + + test_light_1 = factory.createLight(lightState, 1); + EXPECT_EQ(test_light_1.getColorType(), ColorType::NONE); + + lightState["type"] = "On/Off light"; + + EXPECT_CALL(*handler, + GETJson("/api/" + getBridgeUsername() + "/lights/1", nlohmann::json::object(), getBridgeIp(), getBridgePort())) + .Times(AtLeast(1)) + .WillRepeatedly(Return(lightState)); + + test_light_1 = factory.createLight(lightState, 1); + EXPECT_EQ(test_light_1.getColorType(), ColorType::NONE); + + lightState["type"] = "unknown light type"; + ASSERT_THROW(factory.createLight(lightState, 1), HueException); +} + +TEST(LightFactory, createLight_gamutCapabilities) +{ + using namespace ::testing; + std::shared_ptr handler = std::make_shared(); + + LightFactory factory(HueCommandAPI(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler), + std::chrono::steady_clock::duration::max()); + + nlohmann::json lightState + = { {"state", + {{"on", true}, {"bri", 254}, {"ct", 366}, {"alert", "none"}, {"colormode", "ct"}, {"reachable", true}}}, + {"swupdate", {{"state", "noupdates"}, {"lastinstall", nullptr}}}, {"type", "Color light"}, + {"name", "Hue ambiance lamp 1"}, {"modelid", "LTW001"}, {"manufacturername", "Philips"}, + {"uniqueid", "00:00:00:00:00:00:00:00-00"}, {"swversion", "5.50.1.19085"}, + {"capabilities", {{"control", {{"colorgamuttype", "A"}}}}} }; + + EXPECT_CALL(*handler, + GETJson("/api/" + getBridgeUsername() + "/lights/1", nlohmann::json::object(), getBridgeIp(), getBridgePort())) + .Times(AtLeast(1)) + .WillRepeatedly(Return(lightState)); + + Light test_light_1 = factory.createLight(lightState, 1); + EXPECT_EQ(test_light_1.getColorType(), ColorType::GAMUT_A); + + lightState["capabilities"]["control"]["colorgamuttype"] = "B"; + + EXPECT_CALL(*handler, + GETJson("/api/" + getBridgeUsername() + "/lights/1", nlohmann::json::object(), getBridgeIp(), getBridgePort())) + .Times(AtLeast(1)) + .WillRepeatedly(Return(lightState)); + + test_light_1 = factory.createLight(lightState, 1); + EXPECT_EQ(test_light_1.getColorType(), ColorType::GAMUT_B); + + lightState["capabilities"]["control"]["colorgamuttype"] = "C"; + + EXPECT_CALL(*handler, + GETJson("/api/" + getBridgeUsername() + "/lights/1", nlohmann::json::object(), getBridgeIp(), getBridgePort())) + .Times(AtLeast(1)) + .WillRepeatedly(Return(lightState)); + + test_light_1 = factory.createLight(lightState, 1); + EXPECT_EQ(test_light_1.getColorType(), ColorType::GAMUT_C); + + lightState["capabilities"]["control"]["colorgamuttype"] = "Other"; + + EXPECT_CALL(*handler, + GETJson("/api/" + getBridgeUsername() + "/lights/1", nlohmann::json::object(), getBridgeIp(), getBridgePort())) + .Times(AtLeast(1)) + .WillRepeatedly(Return(lightState)); + + test_light_1 = factory.createLight(lightState, 1); + EXPECT_EQ(test_light_1.getColorType(), ColorType::UNDEFINED); + + // With color temperature + lightState["type"] = "Extended color light"; + lightState["capabilities"]["control"]["colorgamuttype"] = "A"; + EXPECT_CALL(*handler, + GETJson("/api/" + getBridgeUsername() + "/lights/1", nlohmann::json::object(), getBridgeIp(), getBridgePort())) + .Times(AtLeast(1)) + .WillRepeatedly(Return(lightState)); + + test_light_1 = factory.createLight(lightState, 1); + EXPECT_EQ(test_light_1.getColorType(), ColorType::GAMUT_A_TEMPERATURE); + + lightState["capabilities"]["control"]["colorgamuttype"] = "B"; + EXPECT_CALL(*handler, + GETJson("/api/" + getBridgeUsername() + "/lights/1", nlohmann::json::object(), getBridgeIp(), getBridgePort())) + .Times(AtLeast(1)) + .WillRepeatedly(Return(lightState)); + + test_light_1 = factory.createLight(lightState, 1); + EXPECT_EQ(test_light_1.getColorType(), ColorType::GAMUT_B_TEMPERATURE); + + lightState["capabilities"]["control"]["colorgamuttype"] = "C"; + EXPECT_CALL(*handler, + GETJson("/api/" + getBridgeUsername() + "/lights/1", nlohmann::json::object(), getBridgeIp(), getBridgePort())) + .Times(AtLeast(1)) + .WillRepeatedly(Return(lightState)); + + test_light_1 = factory.createLight(lightState, 1); + EXPECT_EQ(test_light_1.getColorType(), ColorType::GAMUT_C_TEMPERATURE); +} + +TEST(LightFactory, createLight_gamutModelid) +{ + using namespace ::testing; + std::shared_ptr handler = std::make_shared(); + + LightFactory factory(HueCommandAPI(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler), + std::chrono::steady_clock::duration::max()); + + const std::string gamutAModel = "LST001"; + const std::string gamutBModel = "LCT001"; + const std::string gamutCModel = "LCT010"; + + nlohmann::json lightState + = {{"state", + {{"on", true}, {"bri", 254}, {"ct", 366}, {"alert", "none"}, {"colormode", "ct"}, {"reachable", true}}}, + {"swupdate", {{"state", "noupdates"}, {"lastinstall", nullptr}}}, {"type", "Color light"}, + {"name", "Hue ambiance lamp 1"}, {"modelid", gamutAModel}, {"manufacturername", "Philips"}, + {"uniqueid", "00:00:00:00:00:00:00:00-00"}, {"swversion", "5.50.1.19085"}}; + + EXPECT_CALL(*handler, + GETJson("/api/" + getBridgeUsername() + "/lights/1", nlohmann::json::object(), getBridgeIp(), getBridgePort())) + .Times(AtLeast(1)) + .WillRepeatedly(Return(lightState)); + + Light test_light_1 = factory.createLight(lightState, 1); + EXPECT_EQ(test_light_1.getColorType(), ColorType::GAMUT_A); + + lightState["modelid"] = gamutBModel; + EXPECT_CALL(*handler, + GETJson("/api/" + getBridgeUsername() + "/lights/1", nlohmann::json::object(), getBridgeIp(), getBridgePort())) + .Times(AtLeast(1)) + .WillRepeatedly(Return(lightState)); + + test_light_1 = factory.createLight(lightState, 1); + EXPECT_EQ(test_light_1.getColorType(), ColorType::GAMUT_B); + + lightState["modelid"] = gamutCModel; + + EXPECT_CALL(*handler, + GETJson("/api/" + getBridgeUsername() + "/lights/1", nlohmann::json::object(), getBridgeIp(), getBridgePort())) + .Times(AtLeast(1)) + .WillRepeatedly(Return(lightState)); + + test_light_1 = factory.createLight(lightState, 1); + EXPECT_EQ(test_light_1.getColorType(), ColorType::GAMUT_C); + + // With color temperature + lightState["type"] = "Extended color light"; + lightState["modelid"] = gamutAModel; + + EXPECT_CALL(*handler, + GETJson("/api/" + getBridgeUsername() + "/lights/1", nlohmann::json::object(), getBridgeIp(), getBridgePort())) + .Times(AtLeast(1)) + .WillRepeatedly(Return(lightState)); + + test_light_1 = factory.createLight(lightState, 1); + EXPECT_EQ(test_light_1.getColorType(), ColorType::GAMUT_A_TEMPERATURE); + + lightState["modelid"] = gamutBModel; + EXPECT_CALL(*handler, + GETJson("/api/" + getBridgeUsername() + "/lights/1", nlohmann::json::object(), getBridgeIp(), getBridgePort())) + .Times(AtLeast(1)) + .WillRepeatedly(Return(lightState)); + + test_light_1 = factory.createLight(lightState, 1); + EXPECT_EQ(test_light_1.getColorType(), ColorType::GAMUT_B_TEMPERATURE); + + lightState["modelid"] = gamutCModel; + + EXPECT_CALL(*handler, + GETJson("/api/" + getBridgeUsername() + "/lights/1", nlohmann::json::object(), getBridgeIp(), getBridgePort())) + .Times(AtLeast(1)) + .WillRepeatedly(Return(lightState)); + + test_light_1 = factory.createLight(lightState, 1); + EXPECT_EQ(test_light_1.getColorType(), ColorType::GAMUT_C_TEMPERATURE); + + // Unknown model + lightState["modelid"] = "Unknown model"; + EXPECT_THROW(factory.createLight(lightState, 1), HueException); +} diff --git a/test/test_Main.cpp b/test/test_Main.cpp index 896d1d2..1acba9b 100644 --- a/test/test_Main.cpp +++ b/test/test_Main.cpp @@ -21,7 +21,7 @@ **/ #include -#include +#include namespace { diff --git a/test/test_SimpleBrightnessStrategy.cpp b/test/test_SimpleBrightnessStrategy.cpp index cfe01b4..ad29b7a 100644 --- a/test/test_SimpleBrightnessStrategy.cpp +++ b/test/test_SimpleBrightnessStrategy.cpp @@ -32,7 +32,7 @@ #include "hueplusplus/SimpleBrightnessStrategy.h" #include "json/json.hpp" #include "mocks/mock_HttpHandler.h" -#include "mocks/mock_HueLight.h" +#include "mocks/mock_Light.h" using namespace hueplusplus; @@ -44,7 +44,7 @@ TEST(SimpleBrightnessStrategy, setBrightness) *handler, GETJson("/api/" + getBridgeUsername() + "/lights/1", nlohmann::json::object(), getBridgeIp(), 80)) .Times(AtLeast(1)) .WillRepeatedly(Return(nlohmann::json::object())); - MockHueLight test_light(handler); + MockLight test_light(handler); const std::string statePath = "/api/" + getBridgeUsername() + "/lights/1/state"; @@ -84,10 +84,10 @@ TEST(SimpleBrightnessStrategy, getBrightness) *handler, GETJson("/api/" + getBridgeUsername() + "/lights/1", nlohmann::json::object(), getBridgeIp(), 80)) .Times(AtLeast(1)) .WillRepeatedly(Return(nlohmann::json::object())); - MockHueLight test_light(handler); + MockLight test_light(handler); test_light.getState()["state"]["bri"] = 200; EXPECT_EQ(200, SimpleBrightnessStrategy().getBrightness(test_light)); test_light.getState()["state"]["bri"] = 0; - EXPECT_EQ(0, SimpleBrightnessStrategy().getBrightness(static_cast(test_light))); + EXPECT_EQ(0, SimpleBrightnessStrategy().getBrightness(static_cast(test_light))); } diff --git a/test/test_SimpleColorHueStrategy.cpp b/test/test_SimpleColorHueStrategy.cpp index a7de057..0b4f0d6 100644 --- a/test/test_SimpleColorHueStrategy.cpp +++ b/test/test_SimpleColorHueStrategy.cpp @@ -32,7 +32,7 @@ #include "hueplusplus/SimpleColorHueStrategy.h" #include "json/json.hpp" #include "mocks/mock_HttpHandler.h" -#include "mocks/mock_HueLight.h" +#include "mocks/mock_Light.h" using namespace hueplusplus; @@ -44,7 +44,7 @@ TEST(SimpleColorHueStrategy, setColorHue) *handler, GETJson("/api/" + getBridgeUsername() + "/lights/1", nlohmann::json::object(), getBridgeIp(), 80)) .Times(AtLeast(1)) .WillRepeatedly(Return(nlohmann::json::object())); - MockHueLight test_light(handler); + MockLight test_light(handler); const std::string statePath = "/api/" + getBridgeUsername() + "/lights/1/state"; @@ -78,7 +78,7 @@ TEST(SimpleColorHueStrategy, setColorSaturation) *handler, GETJson("/api/" + getBridgeUsername() + "/lights/1", nlohmann::json::object(), getBridgeIp(), 80)) .Times(AtLeast(1)) .WillRepeatedly(Return(nlohmann::json::object())); - MockHueLight test_light(handler); + MockLight test_light(handler); const std::string statePath = "/api/" + getBridgeUsername() + "/lights/1/state"; @@ -112,7 +112,7 @@ TEST(SimpleColorHueStrategy, setColorHueSaturation) *handler, GETJson("/api/" + getBridgeUsername() + "/lights/1", nlohmann::json::object(), getBridgeIp(), 80)) .Times(AtLeast(1)) .WillRepeatedly(Return(nlohmann::json::object())); - MockHueLight test_light(handler); + MockLight test_light(handler); const std::string statePath = "/api/" + getBridgeUsername() + "/lights/1/state"; @@ -150,7 +150,7 @@ TEST(SimpleColorHueStrategy, setColorXY) *handler, GETJson("/api/" + getBridgeUsername() + "/lights/1", nlohmann::json::object(), getBridgeIp(), 80)) .Times(AtLeast(1)) .WillRepeatedly(Return(nlohmann::json::object())); - MockHueLight test_light(handler); + MockLight test_light(handler); const std::string statePath = "/api/" + getBridgeUsername() + "/lights/1/state"; @@ -179,7 +179,7 @@ TEST(SimpleColorHueStrategy, setColorLoop) *handler, GETJson("/api/" + getBridgeUsername() + "/lights/1", nlohmann::json::object(), getBridgeIp(), 80)) .Times(AtLeast(1)) .WillRepeatedly(Return(nlohmann::json::object())); - MockHueLight test_light(handler); + MockLight test_light(handler); const std::string statePath = "/api/" + getBridgeUsername() + "/lights/1/state"; @@ -210,7 +210,7 @@ TEST(SimpleColorHueStrategy, alertHueSaturation) *handler, GETJson("/api/" + getBridgeUsername() + "/lights/1", nlohmann::json::object(), getBridgeIp(), 80)) .Times(AtLeast(1)) .WillRepeatedly(Return(nlohmann::json::object())); - MockHueLight light(handler); + MockLight light(handler); // Invalid colormode { @@ -335,7 +335,7 @@ TEST(SimpleColorHueStrategy, alertXY) *handler, GETJson("/api/" + getBridgeUsername() + "/lights/1", nlohmann::json::object(), getBridgeIp(), 80)) .Times(AtLeast(1)) .WillRepeatedly(Return(nlohmann::json::object())); - MockHueLight light(handler); + MockLight light(handler); // Invalid colormode { @@ -455,7 +455,7 @@ TEST(SimpleColorHueStrategy, getColorHueSaturation) *handler, GETJson("/api/" + getBridgeUsername() + "/lights/1", nlohmann::json::object(), getBridgeIp(), 80)) .Times(AtLeast(1)) .WillRepeatedly(Return(nlohmann::json::object())); - MockHueLight test_light(handler); + MockLight test_light(handler); test_light.getState()["state"]["hue"] = 5000; test_light.getState()["state"]["sat"] = 128; @@ -463,7 +463,7 @@ TEST(SimpleColorHueStrategy, getColorHueSaturation) test_light.getState()["state"]["hue"] = 50000; test_light.getState()["state"]["sat"] = 158; EXPECT_EQ((HueSaturation {50000, 158}), - SimpleColorHueStrategy().getColorHueSaturation(static_cast(test_light))); + SimpleColorHueStrategy().getColorHueSaturation(static_cast(test_light))); } TEST(SimpleColorHueStrategy, getColorXY) @@ -474,7 +474,7 @@ TEST(SimpleColorHueStrategy, getColorXY) *handler, GETJson("/api/" + getBridgeUsername() + "/lights/1", nlohmann::json::object(), getBridgeIp(), 80)) .Times(AtLeast(1)) .WillRepeatedly(Return(nlohmann::json::object())); - MockHueLight test_light(handler); + MockLight test_light(handler); test_light.getState()["state"]["xy"][0] = 0.1234; test_light.getState()["state"]["xy"][1] = 0.1234; diff --git a/test/test_SimpleColorTemperatureStrategy.cpp b/test/test_SimpleColorTemperatureStrategy.cpp index bf909c4..770dd28 100644 --- a/test/test_SimpleColorTemperatureStrategy.cpp +++ b/test/test_SimpleColorTemperatureStrategy.cpp @@ -33,7 +33,7 @@ #include "hueplusplus/SimpleColorTemperatureStrategy.h" #include "json/json.hpp" #include "mocks/mock_HttpHandler.h" -#include "mocks/mock_HueLight.h" +#include "mocks/mock_Light.h" using namespace hueplusplus; @@ -45,7 +45,7 @@ TEST(SimpleColorTemperatureStrategy, setColorTemperature) *handler, GETJson("/api/" + getBridgeUsername() + "/lights/1", nlohmann::json::object(), getBridgeIp(), 80)) .Times(AtLeast(1)) .WillRepeatedly(Return(nlohmann::json::object())); - MockHueLight test_light(handler); + MockLight test_light(handler); const std::string statePath = "/api/" + getBridgeUsername() + "/lights/1/state"; @@ -87,7 +87,7 @@ TEST(SimpleColorTemperatureStrategy, alertTemperature) *handler, GETJson("/api/" + getBridgeUsername() + "/lights/1", nlohmann::json::object(), getBridgeIp(), 80)) .Times(AtLeast(1)) .WillRepeatedly(Return(nlohmann::json::object())); - MockHueLight light(handler); + MockLight light(handler); const auto setCTLambda = [&](unsigned int ct, int transition) { light.getState()["state"]["colormode"] = "ct"; @@ -146,10 +146,10 @@ TEST(SimpleColorTemperatureStrategy, getColorTemperature) *handler, GETJson("/api/" + getBridgeUsername() + "/lights/1", nlohmann::json::object(), getBridgeIp(), 80)) .Times(AtLeast(1)) .WillRepeatedly(Return(nlohmann::json::object())); - MockHueLight test_light(handler); + MockLight test_light(handler); test_light.getState()["state"]["ct"] = 200; EXPECT_EQ(200, SimpleColorTemperatureStrategy().getColorTemperature(test_light)); test_light.getState()["state"]["ct"] = 500; - EXPECT_EQ(500, SimpleColorTemperatureStrategy().getColorTemperature(static_cast(test_light))); + EXPECT_EQ(500, SimpleColorTemperatureStrategy().getColorTemperature(static_cast(test_light))); } diff --git a/test/test_UPnP.cpp b/test/test_UPnP.cpp index 5faba5a..f17be6c 100644 --- a/test/test_UPnP.cpp +++ b/test/test_UPnP.cpp @@ -26,7 +26,7 @@ #include "iostream" #include "testhelper.h" -#include "hueplusplus/APIConfig.h" +#include "hueplusplus/LibConfig.h" #include "hueplusplus/UPnP.h" #include "json/json.hpp" #include "mocks/mock_HttpHandler.h" -- libgit2 0.21.4