From dfbcb0aebedc3a7a79c2e3d3b43a1014bfb1b93a Mon Sep 17 00:00:00 2001 From: Jojo-1000 <33495614+Jojo-1000@users.noreply.github.com> Date: Thu, 30 Apr 2020 18:55:11 +0200 Subject: [PATCH] Add schedule methods to Hue, add docs to Schedule. --- include/hueplusplus/Hue.h | 42 ++++++++++++++++++++++++++++++++++++++++-- include/hueplusplus/Schedule.h | 136 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- include/hueplusplus/TimePattern.h | 1 + src/Hue.cpp | 65 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/Schedule.cpp | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 286 insertions(+), 5 deletions(-) diff --git a/include/hueplusplus/Hue.h b/include/hueplusplus/Hue.h index 22d9f58..5107080 100644 --- a/include/hueplusplus/Hue.h +++ b/include/hueplusplus/Hue.h @@ -38,6 +38,7 @@ #include "HueDeviceTypes.h" #include "HueLight.h" #include "IHttpHandler.h" +#include "Schedule.h" #include "json/json.hpp" @@ -292,6 +293,42 @@ public: int createGroup(const CreateGroup& params); ///@} + //! \name Schedule + ///@{ + + //! \brief Get all schedules on this bridge. + //! \return A vector of references to every Schedule. + //! \throws std::system_error when system or socket operations fail + //! \throws HueException when response contains no body + //! \throws HueAPIResponseException when response contains an error + //! \throws nlohmann::json::parse_error when response could not be parsed + std::vector> getAllSchedules(); + + //! \brief Get schedule specified by id. + //! \param id ID of the schedule. + //! \returns Schedule that can be modified. + //! \throws std::system_error when system or socket operations fail + //! \throws HueException when id does not exist + //! \throws HueAPIResponseException when response contains an error + //! \throws nlohmann::json::parse_error when response could not be parsed + Schedule& getSchedule(int id); + + //! \brief Checks whether a schedule exists. + //! \param id ID of the schedule. + //! \returns true when the schedule exists. + //! \note Does not refresh the cached state. + bool scheduleExists(int id) const; + + //! \brief Create a new schedule. + //! \param params CreateSchedule parameters for the new group. + //! \returns The new schedule id or 0 if failed. + //! \throws std::system_error when system or socket operations fail + //! \throws HueException when response contains no body + //! \throws HueAPIResponseException when response contains an error + //! \throws nlohmann::json::parse_error when response could not be parsed + int createSchedule(const CreateSchedule& params); + + ///@} //! \brief Const function that returns the picture name of a given light id //! @@ -317,8 +354,9 @@ private: //!< like "192.168.2.1" std::string username; //!< Username that is ussed to access the hue bridge int port; - std::map lights; //!< Maps ids to HueLights that are controlled by this bridge - std::map groups; //!< Maps ids to Groups + std::map lights; //!< Maps ids to HueLights that are controlled by this bridge + std::map groups; //!< Maps ids to Groups + std::map schedules; //!< Maps ids to Schedules std::shared_ptr http_handler; //!< A IHttpHandler that is used to communicate with the //!< bridge diff --git a/include/hueplusplus/Schedule.h b/include/hueplusplus/Schedule.h index 8b53ff0..134af1c 100644 --- a/include/hueplusplus/Schedule.h +++ b/include/hueplusplus/Schedule.h @@ -27,61 +27,155 @@ 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. 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, put, deleteMethod + 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. class Schedule { public: + //! \brief Enabled status of the Schedule enum class Status { - disabled, - enabled + disabled, //!< Schedule is disabled + enabled //!< Schedule is enabled }; public: + //! \brief Construct Schedule that exists in the bridge + //! \param id Schedule ID + //! \param commands HueCommandAPI for requests + //! \param refreshDuration Time between refreshing the cached state Schedule(int id, const HueCommandAPI& commands, std::chrono::steady_clock::duration refreshDuration); + //! \brief Refreshes internal cached state + //! \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 Get schedule id int getId() const; + //! \brief Get schedule name + //! + //! The schedule name is always unique for the bridge. std::string getName() const; + //! \brief Get schedule description std::string getDescription() const; + //! \brief Get schedule command ScheduleCommand getCommand() const; + //! \brief Get time when the event(s) will occur + //! \returns TimePattern in local timezone time::TimePattern getTime() const; + //! \brief Get schedule enabled/disabled status Status getStatus() const; + //! \brief Get autodelete + //! + //! When autodelete is set to true, the schedule is removed after it expires. + //! Only for non-recurring schedules. bool getAutodelete() const; + //! \brief Get created time + //! \returns AbsoluteTime without variation time::AbsoluteTime getCreated() const; + //! \brief Get start time for timers + //! \returns AbsoluteTime without variation when the timer was started. + //! \throws nlohmann::json::out_of_range when the schedule does not have a start time time::AbsoluteTime getStartTime() const; + //! \brief Set schedule name + //! \param name New name for the schedule. Max size is 32. + //! Must be unique for all schedules, otherwise a number is added. + //! \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 setName(const std::string& name); + //! \brief Set schedule description + //! \param description New description, may be empty. Max size is 64. + //! \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 setDescription(const std::string& description); + //! \brief Set schedule command + //! \param command New command 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); + //! \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 + //! \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 setTime(const time::TimePattern& timePattern); + //! \brief Enable or disable schedule + //! \param status Enabled or disabled + //! + //! Can be used to reset a timer by setting to disabled and enabled again. + //! \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 setStatus(Status status); + //! \brief Set autodelete + //! \param autodelete Whether to delete the schedule after it expires + //! \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 setAutodelete(bool autodelete); private: + //! \brief Utility function to send put request to the schedule. + //! \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 sendPutRequest(const nlohmann::json& request, FileInfo fileInfo); private: @@ -89,6 +183,42 @@ private: APICache state; HueCommandAPI commands; }; + +//! \brief Parameters for creating a new Schedule. +//! +//! Can be used like a builder object with chained calls. +class CreateSchedule +{ +public: + //! \brief Set name + //! \see Schedule::setName + CreateSchedule& setName(const std::string& name); + //! \brief Set description + //! \see Schedule::setDescription + CreateSchedule& setDescription(const std::string& description); + //! \brief Set command + //! \see Schedule::setCommand + CreateSchedule& setCommand(const ScheduleCommand& command); + //! \brief Set time + //! \see Schedule::setTime + CreateSchedule& setTime(const time::TimePattern& time); + //! \brief Set status + //! \see Schedule::setStatus + CreateSchedule& setStatus(Schedule::Status status); + //! \brief Set autodelete + //! \see Schedule::setAutodelete + CreateSchedule& setAutodelete(bool autodelete); + //! \brief Set recycle + //! + //! When recycle is true, it is deleted when no resourcelinks refer to it. + CreateSchedule& setRecycle(bool recycle); + + nlohmann::json getRequest() const; + +private: + nlohmann::json request; +}; + } // namespace hueplusplus #endif diff --git a/include/hueplusplus/TimePattern.h b/include/hueplusplus/TimePattern.h index e0aba17..0033a61 100644 --- a/include/hueplusplus/TimePattern.h +++ b/include/hueplusplus/TimePattern.h @@ -380,6 +380,7 @@ public: static TimePattern parse(const std::string& s); private: + //! \brief Calls destructor of active union member void destroy(); private: diff --git a/src/Hue.cpp b/src/Hue.cpp index ee5e9c9..e071565 100644 --- a/src/Hue.cpp +++ b/src/Hue.cpp @@ -381,6 +381,71 @@ bool Hue::lightExists(int id) const return false; } +std::vector> Hue::getAllSchedules() +{ + nlohmann::json schedulesState = stateCache.getValue().at("schedules"); + for (auto it = schedulesState.begin(); it != schedulesState.end(); ++it) + { + getSchedule(std::stoi(it.key())); + } + std::vector> result; + result.reserve(result.size()); + for (auto& entry : schedules) + { + result.emplace_back(entry.second); + } + return result; +} + +Schedule& Hue::getSchedule(int id) +{ + auto pos = schedules.find(id); + if (pos != schedules.end()) + { + pos->second.refresh(); + return pos->second; + } + const nlohmann::json& schedulesCache = stateCache.getValue()["schedules"]; + if (!schedulesCache.count(std::to_string(id))) + { + std::cerr << "Error in Hue getSchedule(): schedule with id " << id << " is not valid\n"; + throw HueException(CURRENT_FILE_INFO, "Schedule id is not valid"); + } + return schedules.emplace(id, Schedule(id, commands, stateCache.getRefreshDuration())).first->second; +} + +bool Hue::scheduleExists(int id) const +{ + auto pos = schedules.find(id); + if (pos != schedules.end()) + { + return true; + } + if (stateCache.getValue()["schedules"].count(std::to_string(id))) + { + return true; + } + return false; +} + +int Hue::createSchedule(const CreateSchedule& params) +{ + nlohmann::json response = commands.POSTRequest("/schedules", params.getRequest(), CURRENT_FILE_INFO); + nlohmann::json id = utils::safeGetMember(response, 0, "success", "id"); + if (id.is_string()) + { + std::string idStr = id.get(); + // Sometimes the response can be /groups/? + if (idStr.find("/schedules/") == 0) + { + idStr.erase(0, 11); + } + stateCache.refresh(); + return std::stoi(idStr); + } + return 0; +} + std::string Hue::getPictureOfLight(int id) const { std::string ret = ""; diff --git a/src/Schedule.cpp b/src/Schedule.cpp index 064e20e..eafcfec 100644 --- a/src/Schedule.cpp +++ b/src/Schedule.cpp @@ -190,4 +190,51 @@ void Schedule::sendPutRequest(const nlohmann::json& request, FileInfo fileInfo) { commands.PUTRequest("/schedules/" + std::to_string(id), request, std::move(fileInfo)); } + +CreateSchedule& CreateSchedule::setName(const std::string& name) +{ + request["name"] = name; + return *this; +} + +CreateSchedule& CreateSchedule::setDescription(const std::string& description) +{ + request["descripton"] = description; + return *this; +} + +CreateSchedule& CreateSchedule::setCommand(const ScheduleCommand& command) +{ + request["command"] = command.toJson(); + return *this; +} + +CreateSchedule& CreateSchedule::setTime(const time::TimePattern& time) +{ + request["localtime"] = time.toString(); + return *this; +} + +CreateSchedule& CreateSchedule::setStatus(Schedule::Status status) +{ + request["status"] = (status == Schedule::Status::enabled) ? "enabled" : "disabled"; + return *this; +} + +CreateSchedule& CreateSchedule::setAutodelete(bool autodelete) +{ + request["autodelete"] = autodelete; + return *this; +} + +CreateSchedule& CreateSchedule::setRecycle(bool recycle) +{ + request["recycle"] = recycle; + return *this; +} + +nlohmann::json CreateSchedule::getRequest() const +{ + return request; +} } // namespace hueplusplus \ No newline at end of file -- libgit2 0.21.4