diff --git a/.gitignore b/.gitignore index 2a4e13b..70c4447 100755 --- a/.gitignore +++ b/.gitignore @@ -30,6 +30,7 @@ # build directory /build* +/out # Generated documentation /doc/html diff --git a/include/hueplusplus/Action.h b/include/hueplusplus/Action.h new file mode 100644 index 0000000..744bf06 --- /dev/null +++ b/include/hueplusplus/Action.h @@ -0,0 +1,73 @@ +/** + \file Action.h + Copyright Notice\n + Copyright (C) 2020 Jan Rogall - 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_ACTION_H +#define INCLUDE_HUEPLUSPLUS_ACTION_H + +#include "json/json.hpp" + +namespace hueplusplus +{ +//! \brief Action executed by the bridge, e.g. as a Schedule command +//! +//! The action makes either a POST, PUT or DELETE request with a given body +//! to an address on the bridge. +//! +//! The Action can also be created by StateTransaction::toAction(). +class Action +{ +public: + //! \brief Create Action from json + //! \param json JSON object with address, method and body + explicit Action(const nlohmann::json& json); + + //! \brief Method used for the command + enum class Method + { + post, //!< POST request + put, //!< PUT request + deleteMethod //!< DELETE request + }; + + //! \brief Get address the request is made to + std::string getAddress() const; + //! \brief Get request method + Method getMethod() const; + //! \brief Get request body + const nlohmann::json& getBody() const; + + //! \brief Get json object of command + const nlohmann::json& toJson() const; + +public: + //! \brief Parse Method from string + //! \param s \c POST, \c PUT or \c DELETE + static Method parseMethod(const std::string& s); + //! \brief Get string from Method + //! \returns \c POST, \c PUT or \c DELETE + static std::string methodToString(Method m); + +private: + nlohmann::json json; +}; +} // namespace hueplusplus + +#endif diff --git a/include/hueplusplus/Group.h b/include/hueplusplus/Group.h index 7e60ec3..2f440d2 100644 --- a/include/hueplusplus/Group.h +++ b/include/hueplusplus/Group.h @@ -27,6 +27,7 @@ #include #include "APICache.h" +#include "Action.h" #include "HueCommandAPI.h" #include "StateTransaction.h" @@ -265,12 +266,12 @@ public: //! \param scene Scene name. void setScene(const std::string& scene); - //! \brief Get ScheduleCommand to set scene + //! \brief Get Action to set scene //! \param scene Scene name - //! \returns A ScheduleCommand that can be used to set the scene on a Schedule + //! \returns A Action that can be used to set the scene on a Schedule //! //! To set other light properties in a scene, use transaction(). - ScheduleCommand scheduleScene(const std::string& scene) const; + Action createSceneAction(const std::string& scene) const; ///@} diff --git a/include/hueplusplus/Rule.h b/include/hueplusplus/Rule.h index af57728..e033773 100644 --- a/include/hueplusplus/Rule.h +++ b/include/hueplusplus/Rule.h @@ -25,8 +25,8 @@ #include #include "APICache.h" +#include "Action.h" #include "HueCommandAPI.h" -#include "Schedule.h" #include "TimePattern.h" namespace hueplusplus @@ -59,6 +59,7 @@ public: nlohmann::json toJson() const; static Condition parse(const nlohmann::json& json); + private: std::string address; Operator op; @@ -105,7 +106,8 @@ public: std::string getOwner() const; std::vector getConditions() const; - std::vector getActions() const; + std::vector getActions() const; + private: int id; APICache state; diff --git a/include/hueplusplus/Schedule.h b/include/hueplusplus/Schedule.h index 49c1f56..324c610 100644 --- a/include/hueplusplus/Schedule.h +++ b/include/hueplusplus/Schedule.h @@ -23,53 +23,11 @@ #define INCLUDE_HUEPLUSPLUS_SCHEDULE_H #include "APICache.h" +#include "Action.h" #include "TimePattern.h" namespace hueplusplus { -//! \brief Command executed on a Schedule -//! -//! The command makes either a POST, PUT or DELETE request with a given body -//! to an address on the bridge. -//! -//! A ScheduleCommand can also be created by StateTransaction::toScheduleCommand(). -class ScheduleCommand -{ -public: - //! \brief Create ScheduleCommand from json - //! \param json JSON object with address, method and body - explicit ScheduleCommand(const nlohmann::json& json); - - //! \brief Method used for the command - enum class Method - { - post, //!< POST request - put, //!< PUT request - deleteMethod //!< DELETE request - }; - - //! \brief Get address the request is made to - std::string getAddress() const; - //! \brief Get request method - Method getMethod() const; - //! \brief Get request body - const nlohmann::json& getBody() const; - - //! \brief Get json object of command - const nlohmann::json& toJson() const; - -private: - //! \brief Parse Method from string - //! \param s \c POST, \c PUT or \c DELETE - static Method parseMethod(const std::string& s); - //! \brief Get string from Method - //! \returns \c POST, \c PUT or \c DELETE - static std::string methodToString(Method m); - -private: - nlohmann::json json; -}; - //! \brief Schedule stored in the bridge //! //! A schedule can be created by the user to trigger actions at specific times. @@ -99,7 +57,7 @@ public: //! \brief Get schedule description std::string getDescription() const; //! \brief Get schedule command - ScheduleCommand getCommand() const; + Action getCommand() const; //! \brief Get time when the event(s) will occur //! \returns TimePattern in local timezone time::TimePattern getTime() const; @@ -134,12 +92,12 @@ public: //! \throws nlohmann::json::parse_error when response could not be parsed void setDescription(const std::string& description); //! \brief Set schedule command - //! \param command New command that is executed when the time event occurs. + //! \param command New action that is executed when the time event occurs. //! \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 setCommand(const ScheduleCommand& command); + void setCommand(const Action& command); //! \brief Set new time when the event will occur //! \param timePattern Any possible value of TimePattern //! \throws std::system_error when system or socket operations fail @@ -191,7 +149,7 @@ public: CreateSchedule& setDescription(const std::string& description); //! \brief Set command //! \see Schedule::setCommand - CreateSchedule& setCommand(const ScheduleCommand& command); + CreateSchedule& setCommand(const Action& command); //! \brief Set time //! \see Schedule::setTime CreateSchedule& setTime(const time::TimePattern& time); diff --git a/include/hueplusplus/StateTransaction.h b/include/hueplusplus/StateTransaction.h index 3e3502e..19a20c5 100644 --- a/include/hueplusplus/StateTransaction.h +++ b/include/hueplusplus/StateTransaction.h @@ -25,9 +25,9 @@ #include +#include "Action.h" #include "ColorUnits.h" #include "HueCommandAPI.h" -#include "Schedule.h" #include "json/json.hpp" @@ -54,7 +54,7 @@ namespace hueplusplus //! In this case, it is especially important that the light and the state of the light MUST NOT invalidate. //! That means //! \li the light variable has to live longer than the transaction -//! \li especially no non-const method calls on the light while the transaction is open, +//! \li especially no non-const method calls on the light while the transaction is open, //! or committing other transactions //! //! In general, this method is easier to screw up and should only be used when really necessary. @@ -85,9 +85,9 @@ public: //! \throws nlohmann::json::parse_error when response could not be parsed bool commit(bool trimRequest = true); - //! \brief Create a ScheduleCommand from the transaction - //! \returns A ScheduleCommand that can be used to execute this transaction on a Schedule. - ScheduleCommand toScheduleCommand(); + //! \brief Create an Action from the transaction + //! \returns An Action that can be used to execute this transaction on a Schedule or Rule. + Action toAction(); //! \brief Turn light on or off. //! \param on true for on, false for off diff --git a/src/Action.cpp b/src/Action.cpp new file mode 100644 index 0000000..34936fb --- /dev/null +++ b/src/Action.cpp @@ -0,0 +1,83 @@ +/** + \file Action.cpp + Copyright Notice\n + Copyright (C) 2020 Jan Rogall - 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 + +namespace hueplusplus +{ + +Action::Action(const nlohmann::json& json) : json(json) { } + +std::string Action::getAddress() const +{ + return json.at("address").get(); +} + +Action::Method Action::getMethod() const +{ + return parseMethod(json.at("method").get()); +} + +const nlohmann::json& Action::getBody() const +{ + return json.at("body"); +} + +const nlohmann::json& Action::toJson() const +{ + return json; +} + +Action::Method Action::parseMethod(const std::string& s) +{ + if (s == "POST") + { + return Method::post; + } + else if (s == "PUT") + { + return Method::put; + } + else if (s == "DELETE") + { + return Method::deleteMethod; + } + throw HueException(CURRENT_FILE_INFO, "Unknown ScheduleCommand method: " + s); +} + +std::string Action::methodToString(Method m) +{ + switch (m) + { + case Method::post: + return "POST"; + case Method::put: + return "PUT"; + case Method::deleteMethod: + return "DELETE"; + default: + throw HueException( + CURRENT_FILE_INFO, "Unknown ScheduleCommand method enum: " + std::to_string(static_cast(m))); + } +} + +} // namespace hueplusplus \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b32b4de..8894f80 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -22,7 +22,7 @@ set(hueplusplus_SOURCES StateTransaction.cpp TimePattern.cpp UPnP.cpp - Utils.cpp "ZLLSensors.cpp" "CLIPSensors.cpp" "NewDeviceList.cpp") + Utils.cpp "ZLLSensors.cpp" "CLIPSensors.cpp" "NewDeviceList.cpp" "Action.cpp") # on windows we want to compile the WinHttpHandler if(WIN32) diff --git a/src/Group.cpp b/src/Group.cpp index dad2142..95fba87 100644 --- a/src/Group.cpp +++ b/src/Group.cpp @@ -190,12 +190,12 @@ void Group::setScene(const std::string& scene) sendPutRequest("/action", {{"scene", scene}}, CURRENT_FILE_INFO); } -ScheduleCommand Group::scheduleScene(const std::string& scene) const +Action Group::createSceneAction(const std::string& scene) const { const nlohmann::json command {{"method", "PUT"}, {"address", state.getCommandAPI().combinedPath("/groups/" + std::to_string(id) + "/action")}, {"body", {{"scene", scene}}}}; - return ScheduleCommand(command); + return Action(command); } nlohmann::json Group::sendPutRequest(const std::string& subPath, const nlohmann::json& request, FileInfo fileInfo) diff --git a/src/Schedule.cpp b/src/Schedule.cpp index e26dcff..2e25518 100644 --- a/src/Schedule.cpp +++ b/src/Schedule.cpp @@ -24,62 +24,6 @@ namespace hueplusplus { - -ScheduleCommand::ScheduleCommand(const nlohmann::json& json) : json(json) {} - -std::string ScheduleCommand::getAddress() const -{ - return json.at("address").get(); -} - -ScheduleCommand::Method ScheduleCommand::getMethod() const -{ - return parseMethod(json.at("method").get()); -} - -const nlohmann::json& ScheduleCommand::getBody() const -{ - return json.at("body"); -} - -const nlohmann::json& ScheduleCommand::toJson() const -{ - return json; -} - -ScheduleCommand::Method ScheduleCommand::parseMethod(const std::string& s) -{ - if (s == "POST") - { - return Method::post; - } - else if (s == "PUT") - { - return Method::put; - } - else if (s == "DELETE") - { - return Method::deleteMethod; - } - throw HueException(CURRENT_FILE_INFO, "Unknown ScheduleCommand method: " + s); -} - -std::string ScheduleCommand::methodToString(Method m) -{ - switch (m) - { - case Method::post: - return "POST"; - case Method::put: - return "PUT"; - case Method::deleteMethod: - return "DELETE"; - default: - throw HueException( - CURRENT_FILE_INFO, "Unknown ScheduleCommand method enum: " + std::to_string(static_cast(m))); - } -} - Schedule::Schedule(int id, const HueCommandAPI& commands, std::chrono::steady_clock::duration refreshDuration) : id(id), state("/schedules/" + std::to_string(id), commands, refreshDuration) { @@ -106,9 +50,9 @@ std::string Schedule::getDescription() const return state.getValue().at("description").get(); } -ScheduleCommand Schedule::getCommand() const +Action Schedule::getCommand() const { - return ScheduleCommand(state.getValue().at("command")); + return Action(state.getValue().at("command")); } time::TimePattern Schedule::getTime() const @@ -154,7 +98,7 @@ void Schedule::setDescription(const std::string& description) refresh(); } -void Schedule::setCommand(const ScheduleCommand& command) +void Schedule::setCommand(const Action& command) { sendPutRequest({{"command", command.toJson()}}, CURRENT_FILE_INFO); refresh(); @@ -203,7 +147,7 @@ CreateSchedule& CreateSchedule::setDescription(const std::string& description) return *this; } -CreateSchedule& CreateSchedule::setCommand(const ScheduleCommand& command) +CreateSchedule& CreateSchedule::setCommand(const Action& command) { request["command"] = command.toJson(); return *this; diff --git a/src/StateTransaction.cpp b/src/StateTransaction.cpp index e106a8d..981552c 100644 --- a/src/StateTransaction.cpp +++ b/src/StateTransaction.cpp @@ -83,10 +83,10 @@ bool StateTransaction::commit(bool trimRequest) return true; } -ScheduleCommand StateTransaction::toScheduleCommand() +Action StateTransaction::toAction() { nlohmann::json command {{"method", "PUT"}, {"address", commands.combinedPath(path)}, {"body", request}}; - return ScheduleCommand(command); + return Action(command); } StateTransaction& StateTransaction::setOn(bool on) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 52d7cd8..3a6776f 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -54,7 +54,7 @@ set(TEST_SOURCES test_SimpleColorHueStrategy.cpp test_SimpleColorTemperatureStrategy.cpp test_StateTransaction.cpp - test_TimePattern.cpp "test_NewDeviceList.cpp") + test_TimePattern.cpp "test_NewDeviceList.cpp" "test_Action.cpp") set(HuePlusPlus_INCLUDE_DIR "${PROJECT_SOURCE_DIR}/include") diff --git a/test/test_Action.cpp b/test/test_Action.cpp new file mode 100644 index 0000000..6381b80 --- /dev/null +++ b/test/test_Action.cpp @@ -0,0 +1,70 @@ +/** + \file test_Action.cpp + Copyright Notice\n + Copyright (C) 2020 Jan Rogall - 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 + +using namespace testing; +using hueplusplus::Action; + +TEST(Action, Constructor) +{ + const std::string address = "/api/abcd/test"; + const nlohmann::json body = {{"test", "value"}}; + const nlohmann::json json = {{"address", address}, {"method", "PUT"}, {"body", body}}; + Action command(json); + + EXPECT_EQ(address, command.getAddress()); + EXPECT_EQ(Action::Method::put, command.getMethod()); + EXPECT_EQ(body, command.getBody()); + EXPECT_EQ(json, command.toJson()); +} + +TEST(Action, getMethod) +{ + nlohmann::json json = {{"address", "/test"}, {"method", "PUT"}, {"body", {}}}; + EXPECT_EQ(Action::Method::put, Action(json).getMethod()); + json["method"] = "POST"; + EXPECT_EQ(Action::Method::post, Action(json).getMethod()); + json["method"] = "DELETE"; + EXPECT_EQ(Action::Method::deleteMethod, Action(json).getMethod()); + json["method"] = "unknown"; + EXPECT_THROW(Action(json).getMethod(), hueplusplus::HueException); +} + +TEST(Action, parseMethod) +{ + using M = Action::Method; + EXPECT_EQ(M::put, Action::parseMethod("PUT")); + EXPECT_EQ(M::post, Action::parseMethod("POST")); + EXPECT_EQ(M::deleteMethod, Action::parseMethod("DELETE")); + EXPECT_THROW(Action::parseMethod("unknown"), hueplusplus::HueException); +} + +TEST(Action, methodToString) +{ + using M = Action::Method; + EXPECT_EQ("POST", Action::methodToString(M::post)); + EXPECT_EQ("PUT", Action::methodToString(M::put)); + EXPECT_EQ("DELETE", Action::methodToString(M::deleteMethod)); +} diff --git a/test/test_Group.cpp b/test/test_Group.cpp index e9b1e2e..5d69bab 100644 --- a/test/test_Group.cpp +++ b/test/test_Group.cpp @@ -253,15 +253,15 @@ TEST_F(GroupTest, setScene) group.setScene(scene); } -TEST_F(GroupTest, scheduleScene) +TEST_F(GroupTest, createSceneAction) { const int id = 1; expectGetState(id); const Group group(id, commands, std::chrono::steady_clock::duration::max()); const std::string scene = "testScene"; nlohmann::json request = { {"scene", scene} }; - ScheduleCommand command = group.scheduleScene(scene); - EXPECT_EQ(ScheduleCommand::Method::put, command.getMethod()); + hueplusplus::Action command = group.createSceneAction(scene); + EXPECT_EQ(hueplusplus::Action::Method::put, command.getMethod()); EXPECT_EQ("/api/" + getBridgeUsername() + "/groups/1/action", command.getAddress()); EXPECT_EQ(request, command.getBody()); } diff --git a/test/test_Schedule.cpp b/test/test_Schedule.cpp index a40021c..c6cb7b6 100644 --- a/test/test_Schedule.cpp +++ b/test/test_Schedule.cpp @@ -29,31 +29,6 @@ using namespace hueplusplus; using namespace testing; -TEST(ScheduleCommand, Constructor) -{ - const std::string address = "/api/abcd/test"; - const nlohmann::json body = {{"test", "value"}}; - const nlohmann::json json = {{"address", address}, {"method", "PUT"}, {"body", body}}; - ScheduleCommand command(json); - - EXPECT_EQ(address, command.getAddress()); - EXPECT_EQ(ScheduleCommand::Method::put, command.getMethod()); - EXPECT_EQ(body, command.getBody()); - EXPECT_EQ(json, command.toJson()); -} - -TEST(ScheduleCommand, getMethod) -{ - nlohmann::json json = {{"address", "/test"}, {"method", "PUT"}, {"body", {}}}; - EXPECT_EQ(ScheduleCommand::Method::put, ScheduleCommand(json).getMethod()); - json["method"] = "POST"; - EXPECT_EQ(ScheduleCommand::Method::post, ScheduleCommand(json).getMethod()); - json["method"] = "DELETE"; - EXPECT_EQ(ScheduleCommand::Method::deleteMethod, ScheduleCommand(json).getMethod()); - json["method"] = "unknown"; - EXPECT_THROW(ScheduleCommand(json).getMethod(), HueException); -} - class ScheduleTest : public Test { protected: @@ -125,10 +100,10 @@ TEST_F(ScheduleTest, getCommand) scheduleState["command"] = {{"address", addr}, {"body", body}, {"method", "PUT"}}; expectGetState(id); const Schedule schedule(id, commands, std::chrono::seconds(0)); - ScheduleCommand command = schedule.getCommand(); + hueplusplus::Action command = schedule.getCommand(); EXPECT_EQ(addr, command.getAddress()); EXPECT_EQ(body, command.getBody()); - EXPECT_EQ(ScheduleCommand::Method::put, command.getMethod()); + EXPECT_EQ(hueplusplus::Action::Method::put, command.getMethod()); } TEST_F(ScheduleTest, getTime) @@ -224,7 +199,7 @@ TEST_F(ScheduleTest, setCommand) const int id = 1; expectGetState(id); Schedule schedule(id, commands, std::chrono::steady_clock::duration::max()); - const ScheduleCommand command({{"address", "abcd"}, {"body", {}}, {"method", "PUT"}}); + const hueplusplus::Action command({{"address", "abcd"}, {"body", {}}, {"method", "PUT"}}); nlohmann::json request = {{"command", command.toJson()}}; nlohmann::json response = {{"success", {"/schedules/1/command", command.toJson()}}}; EXPECT_CALL( @@ -313,7 +288,7 @@ TEST(CreateSchedule, setDescription) TEST(CreateSchedule, setCommand) { const nlohmann::json commandJson = {{"address", "/api/asdf"}, {"method", "PUT"}, {"body", {}}}; - ScheduleCommand command {commandJson}; + hueplusplus::Action command {commandJson}; const nlohmann::json request = {{"command", commandJson}}; EXPECT_EQ(request, CreateSchedule().setCommand(command).getRequest()); } diff --git a/test/test_StateTransaction.cpp b/test/test_StateTransaction.cpp index d79035b..6069ac2 100644 --- a/test/test_StateTransaction.cpp +++ b/test/test_StateTransaction.cpp @@ -60,17 +60,17 @@ TEST(StateTransaction, commit) } } -TEST(StateTransaction, toScheduleCommand) +TEST(StateTransaction, toAction) { auto handler = std::make_shared(); HueCommandAPI commands(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler); const std::string requestPath = "/api/" + getBridgeUsername() + "/path"; nlohmann::json request = {{"on", false}, {"bri", 100}}; - ScheduleCommand command - = StateTransaction(commands, "/path", nullptr).setOn(false).setBrightness(100).toScheduleCommand(); + hueplusplus::Action command + = StateTransaction(commands, "/path", nullptr).setOn(false).setBrightness(100).toAction(); Mock::VerifyAndClearExpectations(handler.get()); - EXPECT_EQ(ScheduleCommand::Method::put, command.getMethod()); + EXPECT_EQ(hueplusplus::Action::Method::put, command.getMethod()); EXPECT_EQ(request, command.getBody()); EXPECT_EQ(requestPath, command.getAddress()); }