diff --git a/include/hueplusplus/Group.h b/include/hueplusplus/Group.h index 0cb947b..8e7426e 100644 --- a/include/hueplusplus/Group.h +++ b/include/hueplusplus/Group.h @@ -28,44 +28,12 @@ #include "APICache.h" #include "HueCommandAPI.h" +#include "StateTransaction.h" #include "json/json.hpp" namespace hueplusplus { -class StateTransaction -{ -public: - StateTransaction(const HueCommandAPI& commands, const std::string& path, const nlohmann::json& currentState); - - StateTransaction(const StateTransaction&) = delete; - StateTransaction(StateTransaction&&) = default; - - bool commit() &&; - - StateTransaction&& setOn(bool on) &&; - StateTransaction&& setBrightness(uint8_t brightness) &&; - StateTransaction&& setColorHue(uint16_t hue) &&; - StateTransaction&& setColorSaturation(uint8_t saturation) &&; - StateTransaction&& setColorHueSaturation(uint16_t hue, uint8_t saturation) &&; - StateTransaction&& setColorXY(float x, float y) &&; - StateTransaction&& setColorTemperature(unsigned int mired) &&; - StateTransaction&& setColorLoop(bool on) &&; - StateTransaction&& incrementBrightness(int increment) &&; - StateTransaction&& incrementSaturation(int increment) &&; - StateTransaction&& incrementHue(int increment) &&; - StateTransaction&& incrementColorTemperature(int increment) &&; - StateTransaction&& incrementColorXY(float xInc, float yInc) &&; - StateTransaction&& setScene(const std::string& scene) &&; - StateTransaction&& setTransition(uint16_t transition) &&; - -private: - const HueCommandAPI& commands; - std::string path; - nlohmann::json state; - nlohmann::json request; -}; - class Group { public: diff --git a/include/hueplusplus/HueLight.h b/include/hueplusplus/HueLight.h index 6531241..9f6138f 100644 --- a/include/hueplusplus/HueLight.h +++ b/include/hueplusplus/HueLight.h @@ -30,6 +30,7 @@ #include "ColorHueStrategy.h" #include "ColorTemperatureStrategy.h" #include "HueCommandAPI.h" +#include "StateTransaction.h" #include "json/json.hpp" @@ -78,7 +79,7 @@ LLC020 // Hue Go, Color Gamut C, ECL };*/ //! enum that specifies the color type of all HueLights -enum ColorType +enum class ColorType { UNDEFINED, //!< ColorType for this light is unknown or undefined NONE, //!< light has no specific ColorType @@ -676,6 +677,8 @@ public: return false; }; + virtual StateTransaction transaction(); + protected: //! \brief Protected ctor that is used by \ref Hue class. //! diff --git a/include/hueplusplus/StateTransaction.h b/include/hueplusplus/StateTransaction.h new file mode 100644 index 0000000..178f690 --- /dev/null +++ b/include/hueplusplus/StateTransaction.h @@ -0,0 +1,71 @@ +/** + \file StateTransaction.h + 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 . +**/ + +#ifndef INCLUDE_HUEPLUSPLUS_STATE_TRANSACTION_H +#define INCLUDE_HUEPLUSPLUS_STATE_TRANSACTION_H + +#include + +#include "HueCommandAPI.h" + +#include "json/json.hpp" + +namespace hueplusplus +{ +class StateTransaction +{ +public: + StateTransaction(const HueCommandAPI& commands, const std::string& path, const nlohmann::json& currentState); + + StateTransaction(const StateTransaction&) = delete; + StateTransaction(StateTransaction&&) = default; + + bool commit() &&; + + StateTransaction&& setOn(bool on) &&; + StateTransaction&& setBrightness(uint8_t brightness) &&; + StateTransaction&& setColorHue(uint16_t hue) &&; + StateTransaction&& setColorSaturation(uint8_t saturation) &&; + StateTransaction&& setColorHueSaturation(uint16_t hue, uint8_t saturation) &&; + StateTransaction&& setColorXY(float x, float y) &&; + StateTransaction&& setColorTemperature(unsigned int mired) &&; + StateTransaction&& setColorLoop(bool on) &&; + StateTransaction&& incrementBrightness(int increment) &&; + StateTransaction&& incrementSaturation(int increment) &&; + StateTransaction&& incrementHue(int increment) &&; + StateTransaction&& incrementColorTemperature(int increment) &&; + StateTransaction&& incrementColorXY(float xInc, float yInc) &&; + StateTransaction&& setTransition(uint16_t transition) &&; + StateTransaction&& alert() &&; + StateTransaction&& longAlert() &&; + StateTransaction&& stopAlert() &&; + +protected: + const HueCommandAPI& commands; + std::string path; + nlohmann::json state; + nlohmann::json request; +}; + +} // namespace hueplusplus + +#endif \ No newline at end of file diff --git a/include/hueplusplus/Utils.h b/include/hueplusplus/Utils.h index d34e297..4c547d1 100644 --- a/include/hueplusplus/Utils.h +++ b/include/hueplusplus/Utils.h @@ -66,12 +66,22 @@ nlohmann::json safeGetMemberHelper(const nlohmann::json& json, std::size_t index //! \brief Function for validating that a request was executed correctly //! +//! \param path The path the PUT request was made to //! \param request The request that was sent initially //! \param reply The reply that was received -//! \param lightId The identifier of the light //! \return True if request was executed correctly +bool validatePUTReply(const std::string& path, const nlohmann::json& request, const nlohmann::json& reply); + bool validateReplyForLight(const nlohmann::json& request, const nlohmann::json& reply, int lightId); +//! \brief Checks equality to 4 decimal places +//! +//! Floats in Hue json responses are rounded to 4 decimal places. +inline bool floatEquals(float lhs, float rhs) +{ + return std::abs(lhs - rhs) <= 1E-4f; +} + //! \brief Returns the object/array member or null if it does not exist //! //! \param json The base json value diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index c7d1f74..aa09a70 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -14,7 +14,7 @@ set(hueplusplus_SOURCES SimpleColorTemperatureStrategy.cpp UPnP.cpp Utils.cpp -) + StateTransaction.cpp) # on windows we want to compile the WinHttpHandler if(WIN32) diff --git a/src/ExtendedColorHueStrategy.cpp b/src/ExtendedColorHueStrategy.cpp index 169bf63..2cce861 100644 --- a/src/ExtendedColorHueStrategy.cpp +++ b/src/ExtendedColorHueStrategy.cpp @@ -200,8 +200,8 @@ bool ExtendedColorHueStrategy::alertRGB(uint8_t r, uint8_t g, uint8_t b, HueLigh { // 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"]; - bool on = state["on"]; + std::string cType = state["colormode"].get(); + bool on = state["on"].get(); if (cType == "hs") { uint16_t oldHue = state["hue"].get(); diff --git a/src/Group.cpp b/src/Group.cpp index 293560c..af72776 100644 --- a/src/Group.cpp +++ b/src/Group.cpp @@ -2,159 +2,35 @@ #include "hueplusplus/HueExceptionMacro.h" -hueplusplus::StateTransaction::StateTransaction( - const HueCommandAPI& commands, const std::string& path, const nlohmann::json& currentState) - : commands(commands), path(path), state(state), request(nlohmann::json::object()) -{} - -bool hueplusplus::StateTransaction::commit() && -{ - // Empty request or request with only transition makes no sense - if (!request.empty() || (request.size() == 1 && request.count("transition"))) - { - if (!request.count("on")) - { - if (request.value("bri", 254) == 0) - { - // Turn off if brightness is 0 - request["on"] = false; - } - else if (request.value("bri", 0) != 0 || request.count("colorloop") || request.count("hue") - || request.count("sat") || request.count("xy")) - { - // Turn on if it was turned off - request["on"] = true; - } - } - - commands.PUTRequest(path, request, CURRENT_FILE_INFO); - return true; - } - return false; -} - -hueplusplus::StateTransaction&& hueplusplus::StateTransaction::setOn(bool on) && -{ - request["on"] = on; - return std::move(*this); -} - -hueplusplus::StateTransaction&& hueplusplus::StateTransaction::setBrightness(uint8_t brightness) && -{ - request["bri"] = std::min(brightness, 254); - return std::move(*this); -} - -hueplusplus::StateTransaction&& hueplusplus::StateTransaction::setColorSaturation(uint8_t saturation) && -{ - request["sat"] = std::min(saturation, 254); - return std::move(*this); -} - -hueplusplus::StateTransaction&& hueplusplus::StateTransaction::setColorHue(uint16_t hue) && -{ - request["hue"] = hue; - return std::move(*this); -} - -hueplusplus::StateTransaction&& hueplusplus::StateTransaction::setColorHueSaturation( - uint16_t hue, uint8_t saturation) && -{ - request["hue"] = hue; - request["sat"] = std::min(saturation, 254); - return std::move(*this); -} - -hueplusplus::StateTransaction&& hueplusplus::StateTransaction::setColorXY(float x, float y) && -{ - request["xy"] = {x, y}; - return std::move(*this); -} - -hueplusplus::StateTransaction&& hueplusplus::StateTransaction::setColorTemperature(unsigned int mired) && -{ - request["ct"] = mired; - return std::move(*this); -} - -hueplusplus::StateTransaction&& hueplusplus::StateTransaction::setColorLoop(bool on) && +namespace hueplusplus { - request["effect"] = on ? "colorloop" : "none"; - return std::move(*this); -} - -hueplusplus::StateTransaction&& hueplusplus::StateTransaction::incrementBrightness(int increment) && -{ - request["bri_inc"] = std::max(-254, std::min(increment, 254)); - return std::move(*this); -} - -hueplusplus::StateTransaction&& hueplusplus::StateTransaction::incrementSaturation(int increment) && -{ - request["sat_inc"] = std::max(-254, std::min(increment, 254)); - return std::move(*this); -} - -hueplusplus::StateTransaction&& hueplusplus::StateTransaction::incrementHue(int increment) && -{ - request["hue_inc"] = std::max(-65534, std::min(increment, 65534)); - return std::move(*this); -} - -hueplusplus::StateTransaction&& hueplusplus::StateTransaction::incrementColorTemperature(int increment) && -{ - request["ct_inc"] = std::max(-65534, std::min(increment, 65534)); - return std::move(*this); -} - -hueplusplus::StateTransaction&& hueplusplus::StateTransaction::incrementColorXY(float xInc, float yInc) && -{ - request["xy_inc"] = {std::min(-0.5f, std::max(xInc, 0.5f)), std::min(-0.5f, std::max(yInc, 0.5f))}; - return std::move(*this); -} - -hueplusplus::StateTransaction&& hueplusplus::StateTransaction::setScene(const std::string& scene) && -{ - request["scene"] = scene; - return std::move(*this); -} - -hueplusplus::StateTransaction&& hueplusplus::StateTransaction::setTransition(uint16_t transition) && -{ - if (transition != 4) - { - request["transitiontime"] = transition; - } - return std::move(*this); -} - -hueplusplus::Group::Group(int id, const HueCommandAPI& commands, std::chrono::steady_clock::duration refreshDuration) +Group::Group(int id, const HueCommandAPI& commands, std::chrono::steady_clock::duration refreshDuration) : id(id), state("/groups/" + std::to_string(id), commands, refreshDuration), commands(commands) { state.Refresh(); } -void hueplusplus::Group::Refresh() +void Group::Refresh() { state.Refresh(); } -int hueplusplus::Group::getId() const +int Group::getId() const { return id; } -std::string hueplusplus::Group::getName() const +std::string Group::getName() const { return state.GetValue().at("name").get(); } -std::string hueplusplus::Group::getType() const +std::string Group::getType() const { return state.GetValue().at("type").get(); } -std::vector hueplusplus::Group::getLightIds() const +std::vector Group::getLightIds() const { const nlohmann::json& lights = state.GetValue().at("lights"); std::vector ids; @@ -166,13 +42,13 @@ std::vector hueplusplus::Group::getLightIds() const return ids; } -void hueplusplus::Group::setName(const std::string& name) +void Group::setName(const std::string& name) { nlohmann::json request = {{"name", name}}; SendPutRequest(request, "", CURRENT_FILE_INFO); } -void hueplusplus::Group::setLights(const std::vector& ids) +void Group::setLights(const std::vector& ids) { nlohmann::json lights = nlohmann::json::array(); for (int id : ids) @@ -182,90 +58,91 @@ void hueplusplus::Group::setLights(const std::vector& ids) SendPutRequest({{"lights", lights}}, "", CURRENT_FILE_INFO); } -bool hueplusplus::Group::getAllOn() +bool Group::getAllOn() { return state.GetValue().at("state").at("all_on").get(); } -bool hueplusplus::Group::getAllOn() const +bool Group::getAllOn() const { return state.GetValue().at("state").at("all_on").get(); } -bool hueplusplus::Group::getAnyOn() +bool Group::getAnyOn() { return state.GetValue().at("state").at("any_on").get(); } -bool hueplusplus::Group::getAnyOn() const +bool Group::getAnyOn() const { return state.GetValue().at("state").at("any_on").get(); } -bool hueplusplus::Group::getActionOn() +bool Group::getActionOn() { return state.GetValue().at("action").at("on").get(); } -bool hueplusplus::Group::getActionOn() const +bool Group::getActionOn() const { return state.GetValue().at("action").at("on").get(); } -std::pair hueplusplus::Group::getActionHueSaturation() +std::pair Group::getActionHueSaturation() { const nlohmann::json& action = state.GetValue().at("action"); return std::make_pair(action.at("hue").get(), action.at("sat").get()); } -std::pair hueplusplus::Group::getActionHueSaturation() const +std::pair Group::getActionHueSaturation() const { const nlohmann::json& action = state.GetValue().at("action"); return std::make_pair(action.at("hue").get(), action.at("sat").get()); } -unsigned int hueplusplus::Group::getActionBrightness() +unsigned int Group::getActionBrightness() { return state.GetValue().at("action").at("bri").get(); } -unsigned int hueplusplus::Group::getActionBrightness() const +unsigned int Group::getActionBrightness() const { return state.GetValue().at("action").at("bri").get(); } -unsigned int hueplusplus::Group::getActionColorTemperature() +unsigned int Group::getActionColorTemperature() { return state.GetValue().at("action").at("ct").get(); } -unsigned int hueplusplus::Group::getActionColorTemperature() const +unsigned int Group::getActionColorTemperature() const { return state.GetValue().at("action").at("ct").get(); } -std::pair hueplusplus::Group::getActionColorXY() +std::pair Group::getActionColorXY() { const nlohmann::json& xy = state.GetValue().at("action").at("xy"); return std::pair(xy[0].get(), xy[1].get()); } -std::pair hueplusplus::Group::getActionColorXY() const +std::pair Group::getActionColorXY() const { const nlohmann::json& xy = state.GetValue().at("action").at("xy"); return std::pair(xy[0].get(), xy[1].get()); } -std::string hueplusplus::Group::getActionColorMode() +std::string Group::getActionColorMode() { return state.GetValue().at("action").at("colormode").get(); } -std::string hueplusplus::Group::getActionColorMode() const +std::string Group::getActionColorMode() const { return state.GetValue().at("action").at("colormode").get(); } -hueplusplus::StateTransaction hueplusplus::Group::transaction() +StateTransaction Group::transaction() { - return StateTransaction(commands, "/groups/" + std::to_string(id) + "/action", state.GetValue()); + // Do not pass state, because it is not the state of ALL lights in the group + return StateTransaction(commands, "/groups/" + std::to_string(id) + "/action", nlohmann::json::object()); } -void hueplusplus::Group::setOn(bool on, uint8_t transition) +void Group::setOn(bool on, uint8_t transition) { nlohmann::json request = {{"on", on}}; if (transition != 4) @@ -275,83 +152,83 @@ void hueplusplus::Group::setOn(bool on, uint8_t transition) SendPutRequest(request, "/action", CURRENT_FILE_INFO); } -void hueplusplus::Group::setBrightness(uint8_t brightness, uint8_t transition) +void Group::setBrightness(uint8_t brightness, uint8_t transition) { transaction().setBrightness(brightness).setTransition(transition).commit(); } -void hueplusplus::Group::setColorHueSaturation(uint16_t hue, uint8_t saturation, uint8_t transition) +void Group::setColorHueSaturation(uint16_t hue, uint8_t saturation, uint8_t transition) { transaction().setColorHueSaturation(hue, saturation).setTransition(transition).commit(); } -void hueplusplus::Group::setColorXY(float x, float y, uint8_t transition) +void Group::setColorXY(float x, float y, uint8_t transition) { transaction().setColorXY(x, y).setTransition(transition).commit(); } -void hueplusplus::Group::setColorTemperature(unsigned int mired, uint8_t transition) +void Group::setColorTemperature(unsigned int mired, uint8_t transition) { transaction().setColorTemperature(mired).setTransition(transition).commit(); } -void hueplusplus::Group::setColorLoop(bool on, uint8_t transition) +void Group::setColorLoop(bool on, uint8_t transition) { transaction().setColorLoop(on).setTransition(transition); } -void hueplusplus::Group::incrementBrightness(int increment, uint8_t transition) +void Group::incrementBrightness(int increment, uint8_t transition) { transaction().incrementBrightness(increment).setTransition(transition).commit(); } -void hueplusplus::Group::incrementSaturation(int increment, uint8_t transition) +void Group::incrementSaturation(int increment, uint8_t transition) { transaction().incrementSaturation(increment).setTransition(transition).commit(); } -void hueplusplus::Group::incrementHue(int increment, uint8_t transition) +void Group::incrementHue(int increment, uint8_t transition) { transaction().incrementHue(increment).setTransition(transition).commit(); } -void hueplusplus::Group::incrementColorTemperature(int increment, uint8_t transition) +void Group::incrementColorTemperature(int increment, uint8_t transition) { transaction().incrementColorTemperature(increment).setTransition(transition).commit(); } -void hueplusplus::Group::incrementColorXY(float incX, float incY, uint8_t transition) +void Group::incrementColorXY(float incX, float incY, uint8_t transition) { transaction().incrementColorXY(incX, incY).setTransition(transition).commit(); } -void hueplusplus::Group::setScene(const std::string& scene, uint8_t transition) +void Group::setScene(const std::string& scene, uint8_t transition) { - transaction().setScene(scene).setTransition(transition).commit(); + SendPutRequest({ {"scene", scene} }, "/action", CURRENT_FILE_INFO); } -nlohmann::json hueplusplus::Group::SendPutRequest( - const nlohmann::json& request, const std::string& subPath, FileInfo fileInfo) +nlohmann::json Group::SendPutRequest(const nlohmann::json& request, const std::string& subPath, FileInfo fileInfo) { return commands.PUTRequest("/groups/" + std::to_string(id) + subPath, request, std::move(fileInfo)); } -std::string hueplusplus::Group::getRoomType() const +std::string Group::getRoomType() const { return state.GetValue().at("class").get(); } -void hueplusplus::Group::setRoomType(const std::string& type) +void Group::setRoomType(const std::string& type) { SendPutRequest({{"class", type}}, "", CURRENT_FILE_INFO); } -std::string hueplusplus::Group::getModelId() const +std::string Group::getModelId() const { return state.GetValue().at("modelid").get(); } -std::string hueplusplus::Group::getUniqueId() const +std::string Group::getUniqueId() const { return state.GetValue().at("uniqueid").get(); } +} // namespace hueplusplus diff --git a/src/HueLight.cpp b/src/HueLight.cpp index fafc7a2..3ff0c87 100644 --- a/src/HueLight.cpp +++ b/src/HueLight.cpp @@ -34,60 +34,22 @@ namespace hueplusplus { bool HueLight::On(uint8_t transition) { - nlohmann::json request = nlohmann::json::object(); - if (transition != 4) - { - request["transitiontime"] = transition; - } - if (state.GetValue()["state"]["on"] != true) - { - request["on"] = true; - } - - if (!request.count("on")) - { - // Nothing needs to be changed - return true; - } - - nlohmann::json reply = SendPutRequest(request, "/state", CURRENT_FILE_INFO); - - // Check whether request was successful - return utils::validateReplyForLight(request, reply, id); + return transaction().setOn(true).setTransition(transition).commit(); } bool HueLight::Off(uint8_t transition) { - nlohmann::json request = nlohmann::json::object(); - if (transition != 4) - { - request["transitiontime"] = transition; - } - if (state.GetValue()["state"]["on"] != false) - { - request["on"] = false; - } - - if (!request.count("on")) - { - // Nothing needs to be changed - return true; - } - - nlohmann::json reply = SendPutRequest(request, "/state", CURRENT_FILE_INFO); - - // Check whether request was successful - return utils::validateReplyForLight(request, reply, id); + return transaction().setOn(false).setTransition(transition).commit(); } bool HueLight::isOn() { - return state.GetValue()["state"]["on"].get(); + return state.GetValue().at("state").at("on").get(); } bool HueLight::isOn() const { - return state.GetValue()["state"]["on"].get(); + return state.GetValue().at("state").at("on").get(); } int HueLight::getId() const @@ -173,12 +135,12 @@ unsigned int HueLight::MiredToKelvin(unsigned int mired) const bool HueLight::alert() { - nlohmann::json request; - request["alert"] = "select"; - - nlohmann::json reply = SendPutRequest(request, "/state", CURRENT_FILE_INFO); + return transaction().alert().commit(); +} - return utils::validateReplyForLight(request, reply, id); +StateTransaction HueLight::transaction() +{ + return StateTransaction(commands, "/lights/" + std::to_string(id) + "/state", state.GetValue().at("state")); } HueLight::HueLight(int id, const HueCommandAPI& commands) : HueLight(id, commands, nullptr, nullptr, nullptr) {} @@ -187,12 +149,12 @@ HueLight::HueLight(int id, const HueCommandAPI& commands, std::shared_ptr colorTempStrategy, std::shared_ptr colorHueStrategy, std::chrono::steady_clock::duration refreshDuration) : id(id), + state("/lights/" + std::to_string(id), commands, refreshDuration), + colorType(ColorType::NONE), brightnessStrategy(std::move(brightnessStrategy)), colorTemperatureStrategy(std::move(colorTempStrategy)), colorHueStrategy(std::move(colorHueStrategy)), - commands(commands), - state("/lights/" + std::to_string(id), commands, refreshDuration) - + commands(commands) { state.Refresh(); } diff --git a/src/SimpleBrightnessStrategy.cpp b/src/SimpleBrightnessStrategy.cpp index 185d7ce..3a37a4b 100644 --- a/src/SimpleBrightnessStrategy.cpp +++ b/src/SimpleBrightnessStrategy.cpp @@ -34,49 +34,7 @@ namespace hueplusplus bool SimpleBrightnessStrategy::setBrightness(unsigned int bri, uint8_t transition, HueLight& 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"]; - if (bri == 0) - { - if (state["on"] == true) - { - return light.Off(transition); - } - else - { - return true; - } - } - else - { - nlohmann::json request = nlohmann::json::object(); - if (transition != 4) - { - request["transitiontime"] = transition; - } - if (state["on"] != true) - { - request["on"] = true; - } - if (state["bri"] != bri) - { - if (bri > 254) - { - bri = 254; - } - request["bri"] = bri; - } - - if (!request.count("on") && !request.count("bri")) - { - // Nothing needs to be changed - return true; - } - - nlohmann::json reply = light.SendPutRequest(request, "/state", CURRENT_FILE_INFO); - - // Check whether request was successful - return utils::validateReplyForLight(request, reply, light.id); - } + return light.transaction().setBrightness(bri).setTransition(transition).commit(); } unsigned int SimpleBrightnessStrategy::getBrightness(HueLight& light) const diff --git a/src/SimpleColorHueStrategy.cpp b/src/SimpleColorHueStrategy.cpp index dac0a79..e3dcbcc 100644 --- a/src/SimpleColorHueStrategy.cpp +++ b/src/SimpleColorHueStrategy.cpp @@ -34,140 +34,22 @@ namespace hueplusplus { bool SimpleColorHueStrategy::setColorHue(uint16_t hue, uint8_t transition, HueLight& 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"]; - nlohmann::json request = nlohmann::json::object(); - if (transition != 4) - { - request["transitiontime"] = transition; - } - if (state["on"] != true) - { - request["on"] = true; - } - if (state["hue"] != hue || state["colormode"] != "hs") - { - hue = hue % 65535; - request["hue"] = hue; - } - - if (!request.count("on") && !request.count("hue")) - { - // Nothing needs to be changed - return true; - } - - nlohmann::json reply = light.SendPutRequest(request, "/state", CURRENT_FILE_INFO); - - // Check whether request was successful - return utils::validateReplyForLight(request, reply, light.id); + return light.transaction().setColorHue(hue).setTransition(transition).commit(); } bool SimpleColorHueStrategy::setColorSaturation(uint8_t sat, uint8_t transition, HueLight& 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"]; - nlohmann::json request = nlohmann::json::object(); - if (transition != 4) - { - request["transitiontime"] = transition; - } - if (state["on"] != true) - { - request["on"] = true; - } - if (state["sat"] != sat) - { - if (sat > 254) - { - sat = 254; - } - request["sat"] = sat; - } - - if (!request.count("on") && !request.count("sat")) - { - // Nothing needs to be changed - return true; - } - - nlohmann::json reply = light.SendPutRequest(request, "/state", CURRENT_FILE_INFO); - - // Check whether request was successful - return utils::validateReplyForLight(request, reply, light.id); + return light.transaction().setColorSaturation(sat).setTransition(transition).commit(); } bool SimpleColorHueStrategy::setColorHueSaturation(uint16_t hue, uint8_t sat, uint8_t transition, HueLight& 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"]; - nlohmann::json request = nlohmann::json::object(); - - if (transition != 4) - { - request["transitiontime"] = transition; - } - if (state["on"] != true) - { - request["on"] = true; - } - if (state["hue"] != hue || state["colormode"] != "hs") - { - hue = hue % 65535; - request["hue"] = hue; - } - if (state["sat"] != sat || state["colormode"] != "hs") - { - if (sat > 254) - { - sat = 254; - } - request["sat"] = sat; - } - - if (!request.count("on") && !request.count("hue") && !request.count("sat")) - { - // Nothing needs to be changed - return true; - } - - nlohmann::json reply = light.SendPutRequest(request, "/state", CURRENT_FILE_INFO); - - // Check whether request was successful - return utils::validateReplyForLight(request, reply, light.id); + return light.transaction().setColorHueSaturation(hue, sat).setTransition(transition).commit(); } bool SimpleColorHueStrategy::setColorXY(float x, float y, uint8_t transition, HueLight& 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"]; - nlohmann::json request = nlohmann::json::object(); - - if (transition != 4) - { - request["transitiontime"] = transition; - } - if (state["on"] != true) - { - request["on"] = true; - } - if (std::abs(state["xy"][0].get() - x) > 1E-4f || std::abs(state["xy"][1].get() - y) > 1E-4f - || state["colormode"] != "xy") - { - request["xy"][0] = x; - request["xy"][1] = y; - } - - if (!request.count("on") && !request.count("xy")) - { - // Nothing needs to be changed - return true; - } - - nlohmann::json reply = light.SendPutRequest(request, "/state", CURRENT_FILE_INFO); - - // Check whether request was successful - return utils::validateReplyForLight(request, reply, light.id); + return light.transaction().setColorXY(x, y).setTransition(transition).commit(); } bool SimpleColorHueStrategy::setColorRGB(uint8_t r, uint8_t g, uint8_t b, uint8_t transition, HueLight& light) const @@ -198,30 +80,7 @@ bool SimpleColorHueStrategy::setColorRGB(uint8_t r, uint8_t g, uint8_t b, uint8_ bool SimpleColorHueStrategy::setColorLoop(bool on, HueLight& light) const { - // colorloop - // Careful, only use state until any light function might refresh the value and invalidate the reference - const nlohmann::json& state = light.state.GetValue()["state"]; - nlohmann::json request = nlohmann::json::object(); - - if (state["on"] != true) - { - request["on"] = true; - } - std::string effect; - if ((effect = on ? "colorloop" : "none") != state["effect"]) - { - request["effect"] = effect; - } - if (!request.count("on") && !request.count("effect")) - { - // Nothing needs to be changed - return true; - } - - nlohmann::json reply = light.SendPutRequest(request, "/state", CURRENT_FILE_INFO); - - // Check whether request was successful - return utils::validateReplyForLight(request, reply, light.id); + return light.transaction().setColorLoop(true).commit(); } bool SimpleColorHueStrategy::alertHueSaturation(uint16_t hue, uint8_t sat, HueLight& light) const diff --git a/src/SimpleColorTemperatureStrategy.cpp b/src/SimpleColorTemperatureStrategy.cpp index 02c96b5..9da0e98 100644 --- a/src/SimpleColorTemperatureStrategy.cpp +++ b/src/SimpleColorTemperatureStrategy.cpp @@ -34,40 +34,7 @@ namespace hueplusplus { bool SimpleColorTemperatureStrategy::setColorTemperature(unsigned int mired, uint8_t transition, HueLight& 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"]; - nlohmann::json request = nlohmann::json::object(); - if (transition != 4) - { - request["transitiontime"] = transition; - } - if (state["on"] != true) - { - request["on"] = true; - } - if (state["ct"] != mired) - { - if (mired > 500) - { - mired = 500; - } - if (mired < 153) - { - mired = 153; - } - request["ct"] = mired; - } - - if (!request.count("on") && !request.count("ct")) - { - // Nothing needs to be changed - return true; - } - - nlohmann::json reply = light.SendPutRequest(request, "/state", CURRENT_FILE_INFO); - - // Check whether request was successful - return utils::validateReplyForLight(request, reply, light.id); + return light.transaction().setColorTemperature(mired).setTransition(transition).commit(); } bool SimpleColorTemperatureStrategy::alertTemperature(unsigned int mired, HueLight& light) const diff --git a/src/StateTransaction.cpp b/src/StateTransaction.cpp new file mode 100644 index 0000000..90d2fa9 --- /dev/null +++ b/src/StateTransaction.cpp @@ -0,0 +1,199 @@ +/** + \file StateTransaction.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 "hueplusplus/StateTransaction.h" + +#include "hueplusplus/HueExceptionMacro.h" +#include "hueplusplus/StateTransaction.h" +#include "hueplusplus/Utils.h" + +namespace hueplusplus +{ +StateTransaction::StateTransaction( + const HueCommandAPI& commands, const std::string& path, const nlohmann::json& currentState) + : commands(commands), path(path), state(currentState), request(nlohmann::json::object()) +{} + +bool StateTransaction::commit() && +{ + // Empty request or request with only transition makes no sense + if (!request.empty() && !(request.size() == 1 && request.count("transitiontime"))) + { + if (!request.count("on")) + { + if (request.value("bri", 254) == 0 && state.value("on", true)) + { + // Turn off if brightness is 0 + request["on"] = false; + } + else if (!state.value("on", false) + && (request.value("bri", 0) != 0 || request.count("effect") || request.count("hue") + || request.count("sat") || request.count("xy") || request.count("ct"))) + { + // Turn on if it was turned off + request["on"] = true; + } + } + + nlohmann::json reply = commands.PUTRequest(path, request, CURRENT_FILE_INFO); + return utils::validatePUTReply(path, request, reply); + } + return true; +} + +StateTransaction&& StateTransaction::setOn(bool on) && +{ + if (!state.count("on") || state["on"] != on) + { + request["on"] = on; + } + return std::move(*this); +} + +StateTransaction&& StateTransaction::setBrightness(uint8_t brightness) && +{ + uint8_t clamped = std::min(brightness, 254); + if (!state.count("bri") || state["bri"].get() != clamped) + { + request["bri"] = clamped; + } + return std::move(*this); +} + +StateTransaction&& StateTransaction::setColorSaturation(uint8_t saturation) && +{ + uint8_t clamped = std::min(saturation, 254); + if (!state.count("sat") || state["sat"].get() != clamped || state.value("colormode", "") != "hs") + { + request["sat"] = clamped; + } + return std::move(*this); +} + +StateTransaction&& StateTransaction::setColorHue(uint16_t hue) && +{ + if (!state.count("hue") || state["hue"].get() != hue || state.value("colormode", "") != "hs") + { + request["hue"] = hue; + } + return std::move(*this); +} + +StateTransaction&& StateTransaction::setColorHueSaturation(uint16_t hue, uint8_t saturation) && +{ + if (!state.count("hue") || state["hue"].get() != hue) + { + request["hue"] = hue; + } + uint8_t clamped = std::min(saturation, 254); + if (!state.count("sat") || state["sat"].get() != clamped) + { + request["sat"] = clamped; + } + return std::move(*this); +} + +StateTransaction&& StateTransaction::setColorXY(float x, float y) && +{ + if (!state.count("xy") || !state.count("colormode") || !state["xy"].is_array() + || !utils::floatEquals(state["xy"][0].get(), x) || !utils::floatEquals(state["xy"][1].get(), y) + || state["colormode"] != "xy") + { + request["xy"] = {x, y}; + } + return std::move(*this); +} + +StateTransaction&& StateTransaction::setColorTemperature(unsigned int mired) && +{ + unsigned int clamped = std::max(153u, std::min(mired, 500u)); + if (state.value("ct", 0u) != clamped || state.value("colormode", "") != "ct") + { + request["ct"] = clamped; + } + return std::move(*this); +} + +StateTransaction&& StateTransaction::setColorLoop(bool on) && +{ + std::string effect = on ? "colorloop" : "none"; + if (state.value("effect", "") != effect) + { + request["effect"] = effect; + } + return std::move(*this); +} + +StateTransaction&& StateTransaction::incrementBrightness(int increment) && +{ + request["bri_inc"] = std::max(-254, std::min(increment, 254)); + return std::move(*this); +} + +StateTransaction&& StateTransaction::incrementSaturation(int increment) && +{ + request["sat_inc"] = std::max(-254, std::min(increment, 254)); + return std::move(*this); +} + +StateTransaction&& StateTransaction::incrementHue(int increment) && +{ + request["hue_inc"] = std::max(-65534, std::min(increment, 65534)); + return std::move(*this); +} + +StateTransaction&& StateTransaction::incrementColorTemperature(int increment) && +{ + request["ct_inc"] = std::max(-65534, std::min(increment, 65534)); + return std::move(*this); +} + +StateTransaction&& StateTransaction::incrementColorXY(float xInc, float yInc) && +{ + request["xy_inc"] = {std::min(-0.5f, std::max(xInc, 0.5f)), std::min(-0.5f, std::max(yInc, 0.5f))}; + return std::move(*this); +} + +StateTransaction&& StateTransaction::setTransition(uint16_t transition) && +{ + if (transition != 4) + { + request["transitiontime"] = transition; + } + return std::move(*this); +} +StateTransaction&& StateTransaction::alert() && +{ + request["alert"] = "select"; + return std::move(*this); +} +StateTransaction&& StateTransaction::longAlert() && +{ + request["alert"] = "lselect"; + return std::move(*this); +} +StateTransaction&& StateTransaction::stopAlert() && +{ + request["alert"] = "none"; + return std::move(*this); +} +} // namespace hueplusplus \ No newline at end of file diff --git a/src/Utils.cpp b/src/Utils.cpp index a95e67b..491cfec 100644 --- a/src/Utils.cpp +++ b/src/Utils.cpp @@ -28,10 +28,14 @@ namespace hueplusplus { namespace utils { -bool validateReplyForLight(const nlohmann::json& request, const nlohmann::json& reply, int lightId) +bool validatePUTReply(const std::string& path, const nlohmann::json& request, const nlohmann::json& reply) { + std::string pathAppend = path; + if (pathAppend.back() != '/') + { + pathAppend.push_back('/'); + } bool success = false; - std::string path = "/lights/" + std::to_string(lightId) + "/state/"; for (auto it = reply.begin(); it != reply.end(); ++it) { success = it.value().count("success"); @@ -42,9 +46,9 @@ bool validateReplyForLight(const nlohmann::json& request, const nlohmann::json& for (auto successIt = successObject.begin(); successIt != successObject.end(); ++successIt) { const std::string successPath = successIt.key(); - if (successPath.find(path) == 0) + if (successPath.find(pathAppend) == 0) { - const std::string valueKey = successPath.substr(path.size()); + const std::string valueKey = successPath.substr(pathAppend.size()); auto requestIt = request.find(valueKey); success = requestIt != request.end(); if (success) @@ -58,7 +62,8 @@ bool validateReplyForLight(const nlohmann::json& request, const nlohmann::json& } else { - success = requestIt.value() == successIt.value(); + success = requestIt.value() == successIt.value() + || (successIt.value().is_string() && successIt.value() == "Updated."); } if (!success) { @@ -80,5 +85,10 @@ bool validateReplyForLight(const nlohmann::json& request, const nlohmann::json& } return success; } + +bool validateReplyForLight(const nlohmann::json& request, const nlohmann::json& reply, int lightId) +{ + return validatePUTReply("/lights/" + std::to_string(lightId) + "/state/", request, reply); +} } // namespace utils } // namespace hueplusplus diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index fe61852..51a423c 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -70,6 +70,9 @@ add_custom_target("unittest" find_program( GCOV_PATH gcov ) find_program( LCOV_PATH lcov ) +mark_as_advanced(GCOV_PATH) +mark_as_advanced(LCOV_PATH) + if(LCOV_PATH AND GCOV_PATH) # GCov include(CodeCoverage.cmake) diff --git a/test/test_HueLight.cpp b/test/test_HueLight.cpp index a8b3edd..d38ef48 100644 --- a/test/test_HueLight.cpp +++ b/test/test_HueLight.cpp @@ -69,7 +69,7 @@ protected: hue_bridge_state["lights"]["2"] = nlohmann::json::object(); hue_bridge_state["lights"]["2"]["state"] = nlohmann::json::object(); hue_bridge_state["lights"]["2"]["state"]["on"] = false; - hue_bridge_state["lights"]["2"]["state"]["bri"] = 254; + hue_bridge_state["lights"]["2"]["state"]["bri"] = 0; hue_bridge_state["lights"]["2"]["state"]["ct"] = 366; hue_bridge_state["lights"]["2"]["state"]["hue"] = 123456; hue_bridge_state["lights"]["2"]["state"]["sat"] = 123; @@ -514,7 +514,7 @@ TEST_F(HueLightTest, setBrightness) 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"] = 254; + 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)); @@ -525,7 +525,7 @@ TEST_F(HueLightTest, setBrightness) EXPECT_EQ(false, test_light_1.setBrightness(200)); EXPECT_EQ(true, test_light_2.setBrightness(0, 2)); - EXPECT_EQ(true, test_light_3.setBrightness(255, 0)); + EXPECT_EQ(true, test_light_3.setBrightness(253, 0)); } TEST_F(HueLightTest, getBrightness) @@ -538,10 +538,10 @@ TEST_F(HueLightTest, getBrightness) HueLight test_light_3 = test_bridge.getLight(3); EXPECT_EQ(254, ctest_light_1.getBrightness()); - EXPECT_EQ(254, ctest_light_2.getBrightness()); + EXPECT_EQ(0, ctest_light_2.getBrightness()); EXPECT_EQ(254, ctest_light_3.getBrightness()); EXPECT_EQ(254, test_light_1.getBrightness()); - EXPECT_EQ(254, test_light_2.getBrightness()); + EXPECT_EQ(0, test_light_2.getBrightness()); EXPECT_EQ(254, test_light_3.getBrightness()); } diff --git a/test/test_SimpleBrightnessStrategy.cpp b/test/test_SimpleBrightnessStrategy.cpp index 38179ad..a63421b 100644 --- a/test/test_SimpleBrightnessStrategy.cpp +++ b/test/test_SimpleBrightnessStrategy.cpp @@ -46,33 +46,30 @@ TEST(SimpleBrightnessStrategy, setBrightness) .WillRepeatedly(Return(nlohmann::json::object())); MockHueLight test_light(handler); - EXPECT_CALL(test_light, Off(_)).Times(AtLeast(1)).WillRepeatedly(Return(true)); - 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"] = 6; - prep_ret[1] = nlohmann::json::object(); - prep_ret[1]["success"] = nlohmann::json::object(); - prep_ret[1]["success"]["/lights/1/state/on"] = true; - prep_ret[2] = nlohmann::json::object(); - prep_ret[2]["success"] = nlohmann::json::object(); - prep_ret[2]["success"]["/lights/1/state/bri"] = 50; - EXPECT_CALL(test_light, SendPutRequest(_, "/state", _)).Times(1).WillOnce(Return(prep_ret)); + const std::string statePath = "/api/" + getBridgeUsername() + "/lights/1/state"; + nlohmann::json prep_ret + = {{{"success", {{"/lights/1/state/on", false}}}}, {{"success", {{"/lights/1/state/bri", 0}}}}}; + EXPECT_CALL(*handler, PUTJson(statePath, _, getBridgeIp(), getBridgePort())).Times(1).WillOnce(Return(prep_ret)); test_light.getState()["state"]["on"] = true; EXPECT_EQ(true, SimpleBrightnessStrategy().setBrightness(0, 4, test_light)); + // Only set brightness, already off test_light.getState()["state"]["on"] = false; + prep_ret = {{{"success", {{"/lights/1/state/bri", 0}}}}}; + EXPECT_CALL(*handler, PUTJson(statePath, _, getBridgeIp(), getBridgePort())).Times(1).WillOnce(Return(prep_ret)); EXPECT_EQ(true, SimpleBrightnessStrategy().setBrightness(0, 4, test_light)); + prep_ret = {{{"success", {{"/lights/1/state/on", true}}}}, {{"success", {{"/lights/1/state/bri", 50}}}}}; + EXPECT_CALL(*handler, PUTJson(statePath, _, getBridgeIp(), getBridgePort())).Times(1).WillOnce(Return(prep_ret)); test_light.getState()["state"]["bri"] = 0; EXPECT_EQ(true, SimpleBrightnessStrategy().setBrightness(50, 6, test_light)); test_light.getState()["state"]["on"] = true; test_light.getState()["state"]["bri"] = 50; + // No request because state matches EXPECT_EQ(true, SimpleBrightnessStrategy().setBrightness(50, 6, test_light)); - prep_ret[2]["success"]["/lights/1/state/bri"] = 254; - EXPECT_CALL(test_light, SendPutRequest(_, "/state", _)).Times(1).WillOnce(Return(prep_ret)); + prep_ret[1]["success"]["/lights/1/state/bri"] = 254; + EXPECT_CALL(*handler, PUTJson(statePath, _, getBridgeIp(), getBridgePort())).Times(1).WillOnce(Return(prep_ret)); test_light.getState()["state"]["on"] = false; EXPECT_EQ(true, SimpleBrightnessStrategy().setBrightness(255, 6, test_light)); } diff --git a/test/test_SimpleColorHueStrategy.cpp b/test/test_SimpleColorHueStrategy.cpp index 2216d81..4c432ff 100644 --- a/test/test_SimpleColorHueStrategy.cpp +++ b/test/test_SimpleColorHueStrategy.cpp @@ -45,6 +45,8 @@ TEST(SimpleColorHueStrategy, setColorHue) .WillRepeatedly(Return(nlohmann::json::object())); MockHueLight test_light(handler); + const std::string statePath = "/api/" + getBridgeUsername() + "/lights/1/state"; + nlohmann::json prep_ret; prep_ret = nlohmann::json::array(); prep_ret[0] = nlohmann::json::object(); @@ -56,7 +58,7 @@ TEST(SimpleColorHueStrategy, setColorHue) prep_ret[2] = nlohmann::json::object(); prep_ret[2]["success"] = nlohmann::json::object(); prep_ret[2]["success"]["/lights/1/state/hue"] = 30500; - EXPECT_CALL(test_light, SendPutRequest(_, "/state", _)).Times(1).WillOnce(Return(prep_ret)); + EXPECT_CALL(*handler, PUTJson(statePath, _, getBridgeIp(), getBridgePort())).Times(1).WillOnce(Return(prep_ret)); test_light.getState()["state"]["on"] = true; test_light.getState()["state"]["hue"] = 200; @@ -77,6 +79,8 @@ TEST(SimpleColorHueStrategy, setColorSaturation) .WillRepeatedly(Return(nlohmann::json::object())); MockHueLight test_light(handler); + const std::string statePath = "/api/" + getBridgeUsername() + "/lights/1/state"; + nlohmann::json prep_ret; prep_ret = nlohmann::json::array(); prep_ret[0] = nlohmann::json::object(); @@ -88,7 +92,7 @@ TEST(SimpleColorHueStrategy, setColorSaturation) prep_ret[2] = nlohmann::json::object(); prep_ret[2]["success"] = nlohmann::json::object(); prep_ret[2]["success"]["/lights/1/state/sat"] = 254; - EXPECT_CALL(test_light, SendPutRequest(_, "/state", _)).Times(1).WillOnce(Return(prep_ret)); + EXPECT_CALL(*handler, PUTJson(statePath, _, getBridgeIp(), getBridgePort())).Times(1).WillOnce(Return(prep_ret)); test_light.getState()["state"]["on"] = true; test_light.getState()["state"]["sat"] = 100; @@ -109,6 +113,8 @@ TEST(SimpleColorHueStrategy, setColorHueSaturation) .WillRepeatedly(Return(nlohmann::json::object())); MockHueLight test_light(handler); + const std::string statePath = "/api/" + getBridgeUsername() + "/lights/1/state"; + nlohmann::json prep_ret; prep_ret = nlohmann::json::array(); prep_ret[0] = nlohmann::json::object(); @@ -123,7 +129,7 @@ TEST(SimpleColorHueStrategy, setColorHueSaturation) prep_ret[3] = nlohmann::json::object(); prep_ret[3]["success"] = nlohmann::json::object(); prep_ret[3]["success"]["/lights/1/state/sat"] = 254; - EXPECT_CALL(test_light, SendPutRequest(_, "/state", _)).Times(1).WillOnce(Return(prep_ret)); + EXPECT_CALL(*handler, PUTJson(statePath, _, getBridgeIp(), getBridgePort())).Times(1).WillOnce(Return(prep_ret)); test_light.getState()["state"]["on"] = true; test_light.getState()["state"]["sat"] = 100; @@ -145,6 +151,8 @@ TEST(SimpleColorHueStrategy, setColorXY) .WillRepeatedly(Return(nlohmann::json::object())); MockHueLight test_light(handler); + const std::string statePath = "/api/" + getBridgeUsername() + "/lights/1/state"; + nlohmann::json prep_ret; prep_ret = nlohmann::json::array(); prep_ret[0] = nlohmann::json::object(); @@ -157,7 +165,7 @@ TEST(SimpleColorHueStrategy, setColorXY) prep_ret[2]["success"] = nlohmann::json::object(); prep_ret[2]["success"]["/lights/1/state/xy"][0] = 0.2355; prep_ret[2]["success"]["/lights/1/state/xy"][1] = 0.1234; - EXPECT_CALL(test_light, SendPutRequest(_, "/state", _)).Times(1).WillOnce(Return(prep_ret)); + EXPECT_CALL(*handler, PUTJson(statePath, _, getBridgeIp(), getBridgePort())).Times(1).WillOnce(Return(prep_ret)); test_light.getState()["state"]["on"] = true; test_light.getState()["state"]["xy"][0] = 0.1f; @@ -198,6 +206,8 @@ TEST(SimpleColorHueStrategy, setColorLoop) .WillRepeatedly(Return(nlohmann::json::object())); MockHueLight test_light(handler); + const std::string statePath = "/api/" + getBridgeUsername() + "/lights/1/state"; + nlohmann::json prep_ret; prep_ret = nlohmann::json::array(); prep_ret[0] = nlohmann::json::object(); @@ -206,7 +216,7 @@ TEST(SimpleColorHueStrategy, setColorLoop) prep_ret[1] = nlohmann::json::object(); prep_ret[1]["success"] = nlohmann::json::object(); prep_ret[1]["success"]["/lights/1/state/effect"] = "colorloop"; - EXPECT_CALL(test_light, SendPutRequest(_, "/state", _)).Times(1).WillOnce(Return(prep_ret)); + EXPECT_CALL(*handler, PUTJson(statePath, _, getBridgeIp(), getBridgePort())).Times(1).WillOnce(Return(prep_ret)); test_light.getState()["state"]["on"] = true; test_light.getState()["state"]["effect"] = "colorloop"; diff --git a/test/test_SimpleColorTemperatureStrategy.cpp b/test/test_SimpleColorTemperatureStrategy.cpp index 3385870..8d6c845 100644 --- a/test/test_SimpleColorTemperatureStrategy.cpp +++ b/test/test_SimpleColorTemperatureStrategy.cpp @@ -46,6 +46,8 @@ TEST(SimpleColorTemperatureStrategy, setColorTemperature) .WillRepeatedly(Return(nlohmann::json::object())); MockHueLight test_light(handler); + const std::string statePath = "/api/" + getBridgeUsername() + "/lights/1/state"; + nlohmann::json prep_ret; prep_ret = nlohmann::json::array(); prep_ret[0] = nlohmann::json::object(); @@ -57,21 +59,22 @@ TEST(SimpleColorTemperatureStrategy, setColorTemperature) prep_ret[2] = nlohmann::json::object(); prep_ret[2]["success"] = nlohmann::json::object(); prep_ret[2]["success"]["/lights/1/state/ct"] = 155; - EXPECT_CALL(test_light, SendPutRequest(_, "/state", _)).Times(1).WillOnce(Return(prep_ret)); + EXPECT_CALL(*handler, PUTJson(statePath, _, getBridgeIp(), getBridgePort())).Times(1).WillOnce(Return(prep_ret)); test_light.getState()["state"]["on"] = true; test_light.getState()["state"]["ct"] = 200; + test_light.getState()["state"]["colormode"] = "ct"; EXPECT_EQ(true, SimpleColorTemperatureStrategy().setColorTemperature(200, 4, test_light)); test_light.getState()["state"]["on"] = false; EXPECT_EQ(true, SimpleColorTemperatureStrategy().setColorTemperature(155, 6, test_light)); - prep_ret[2]["success"]["/lights/1/state/ct"] = 153; - EXPECT_CALL(test_light, SendPutRequest(_, "/state", _)).Times(1).WillOnce(Return(prep_ret)); + prep_ret = {{{"success", {{"/lights/1/state/transitiontime", 6}}}}, {{"success", {{"/lights/1/state/ct", 153}}}}}; + EXPECT_CALL(*handler, PUTJson(statePath, _, getBridgeIp(), getBridgePort())).Times(1).WillOnce(Return(prep_ret)); EXPECT_EQ(true, SimpleColorTemperatureStrategy().setColorTemperature(0, 6, test_light)); - prep_ret[2]["success"]["/lights/1/state/ct"] = 500; - EXPECT_CALL(test_light, SendPutRequest(_, "/state", _)).Times(1).WillOnce(Return(prep_ret)); + prep_ret[1]["success"]["/lights/1/state/ct"] = 500; + EXPECT_CALL(*handler, PUTJson(statePath, _, getBridgeIp(), getBridgePort())).Times(1).WillOnce(Return(prep_ret)); EXPECT_EQ(true, SimpleColorTemperatureStrategy().setColorTemperature(600, 6, test_light)); }