Commit dfbcb0aebedc3a7a79c2e3d3b43a1014bfb1b93a

Authored by Jojo-1000
Committed by Moritz Wirger
1 parent 3be79a14

Add schedule methods to Hue, add docs to Schedule.

include/hueplusplus/Hue.h
@@ -38,6 +38,7 @@ @@ -38,6 +38,7 @@
38 #include "HueDeviceTypes.h" 38 #include "HueDeviceTypes.h"
39 #include "HueLight.h" 39 #include "HueLight.h"
40 #include "IHttpHandler.h" 40 #include "IHttpHandler.h"
  41 +#include "Schedule.h"
41 42
42 #include "json/json.hpp" 43 #include "json/json.hpp"
43 44
@@ -292,6 +293,42 @@ public: @@ -292,6 +293,42 @@ public:
292 int createGroup(const CreateGroup& params); 293 int createGroup(const CreateGroup& params);
293 294
294 ///@} 295 ///@}
  296 + //! \name Schedule
  297 + ///@{
  298 +
  299 + //! \brief Get all schedules on this bridge.
  300 + //! \return A vector of references to every Schedule.
  301 + //! \throws std::system_error when system or socket operations fail
  302 + //! \throws HueException when response contains no body
  303 + //! \throws HueAPIResponseException when response contains an error
  304 + //! \throws nlohmann::json::parse_error when response could not be parsed
  305 + std::vector<std::reference_wrapper<Schedule>> getAllSchedules();
  306 +
  307 + //! \brief Get schedule specified by id.
  308 + //! \param id ID of the schedule.
  309 + //! \returns Schedule that can be modified.
  310 + //! \throws std::system_error when system or socket operations fail
  311 + //! \throws HueException when id does not exist
  312 + //! \throws HueAPIResponseException when response contains an error
  313 + //! \throws nlohmann::json::parse_error when response could not be parsed
  314 + Schedule& getSchedule(int id);
  315 +
  316 + //! \brief Checks whether a schedule exists.
  317 + //! \param id ID of the schedule.
  318 + //! \returns true when the schedule exists.
  319 + //! \note Does not refresh the cached state.
  320 + bool scheduleExists(int id) const;
  321 +
  322 + //! \brief Create a new schedule.
  323 + //! \param params CreateSchedule parameters for the new group.
  324 + //! \returns The new schedule id or 0 if failed.
  325 + //! \throws std::system_error when system or socket operations fail
  326 + //! \throws HueException when response contains no body
  327 + //! \throws HueAPIResponseException when response contains an error
  328 + //! \throws nlohmann::json::parse_error when response could not be parsed
  329 + int createSchedule(const CreateSchedule& params);
  330 +
  331 + ///@}
295 332
296 //! \brief Const function that returns the picture name of a given light id 333 //! \brief Const function that returns the picture name of a given light id
297 //! 334 //!
@@ -317,8 +354,9 @@ private: @@ -317,8 +354,9 @@ private:
317 //!< like "192.168.2.1" 354 //!< like "192.168.2.1"
318 std::string username; //!< Username that is ussed to access the hue bridge 355 std::string username; //!< Username that is ussed to access the hue bridge
319 int port; 356 int port;
320 - std::map<uint8_t, HueLight> lights; //!< Maps ids to HueLights that are controlled by this bridge  
321 - std::map<uint8_t, Group> groups; //!< Maps ids to Groups 357 + std::map<int, HueLight> lights; //!< Maps ids to HueLights that are controlled by this bridge
  358 + std::map<int, Group> groups; //!< Maps ids to Groups
  359 + std::map<int, Schedule> schedules; //!< Maps ids to Schedules
322 360
323 std::shared_ptr<const IHttpHandler> http_handler; //!< A IHttpHandler that is used to communicate with the 361 std::shared_ptr<const IHttpHandler> http_handler; //!< A IHttpHandler that is used to communicate with the
324 //!< bridge 362 //!< bridge
include/hueplusplus/Schedule.h
@@ -27,61 +27,155 @@ @@ -27,61 +27,155 @@
27 27
28 namespace hueplusplus 28 namespace hueplusplus
29 { 29 {
  30 +//! \brief Command executed on a Schedule
  31 +//!
  32 +//! The command makes either a POST, PUT or DELETE request with a given body
  33 +//! to an address on the bridge.
30 class ScheduleCommand 34 class ScheduleCommand
31 { 35 {
32 public: 36 public:
  37 + //! \brief Create ScheduleCommand from json
  38 + //! \param json JSON object with address, method and body
33 explicit ScheduleCommand(const nlohmann::json& json); 39 explicit ScheduleCommand(const nlohmann::json& json);
34 40
  41 + //! \brief Method used for the command
35 enum class Method 42 enum class Method
36 { 43 {
37 - post, put, deleteMethod 44 + post, //!< POST request
  45 + put, //!< PUT request
  46 + deleteMethod //!< DELETE request
38 }; 47 };
39 48
  49 + //! \brief Get address the request is made to
40 std::string getAddress() const; 50 std::string getAddress() const;
  51 + //! \brief Get request method
41 Method getMethod() const; 52 Method getMethod() const;
  53 + //! \brief Get request body
42 const nlohmann::json& getBody() const; 54 const nlohmann::json& getBody() const;
43 55
  56 + //! \brief Get json object of command
44 const nlohmann::json& toJson() const; 57 const nlohmann::json& toJson() const;
  58 +
45 private: 59 private:
  60 + //! \brief Parse Method from string
  61 + //! \param s \c POST, \c PUT or \c DELETE
46 static Method parseMethod(const std::string& s); 62 static Method parseMethod(const std::string& s);
  63 + //! \brief Get string from Method
  64 + //! \returns \c POST, \c PUT or \c DELETE
47 static std::string methodToString(Method m); 65 static std::string methodToString(Method m);
  66 +
48 private: 67 private:
49 nlohmann::json json; 68 nlohmann::json json;
50 }; 69 };
51 70
  71 +//! \brief Schedule stored in the bridge
  72 +//!
  73 +//! A schedule can be created by the user to trigger actions at specific times.
52 class Schedule 74 class Schedule
53 { 75 {
54 public: 76 public:
  77 + //! \brief Enabled status of the Schedule
55 enum class Status 78 enum class Status
56 { 79 {
57 - disabled,  
58 - enabled 80 + disabled, //!< Schedule is disabled
  81 + enabled //!< Schedule is enabled
59 }; 82 };
60 83
61 public: 84 public:
  85 + //! \brief Construct Schedule that exists in the bridge
  86 + //! \param id Schedule ID
  87 + //! \param commands HueCommandAPI for requests
  88 + //! \param refreshDuration Time between refreshing the cached state
62 Schedule(int id, const HueCommandAPI& commands, std::chrono::steady_clock::duration refreshDuration); 89 Schedule(int id, const HueCommandAPI& commands, std::chrono::steady_clock::duration refreshDuration);
63 90
  91 + //! \brief Refreshes internal cached state
  92 + //! \throws std::system_error when system or socket operations fail
  93 + //! \throws HueException when response contained no body
  94 + //! \throws HueAPIResponseException when response contains an error
  95 + //! \throws nlohmann::json::parse_error when response could not be parsed
64 void refresh(); 96 void refresh();
65 97
  98 + //! \brief Get schedule id
66 int getId() const; 99 int getId() const;
67 100
  101 + //! \brief Get schedule name
  102 + //!
  103 + //! The schedule name is always unique for the bridge.
68 std::string getName() const; 104 std::string getName() const;
  105 + //! \brief Get schedule description
69 std::string getDescription() const; 106 std::string getDescription() const;
  107 + //! \brief Get schedule command
70 ScheduleCommand getCommand() const; 108 ScheduleCommand getCommand() const;
  109 + //! \brief Get time when the event(s) will occur
  110 + //! \returns TimePattern in local timezone
71 time::TimePattern getTime() const; 111 time::TimePattern getTime() const;
  112 + //! \brief Get schedule enabled/disabled status
72 Status getStatus() const; 113 Status getStatus() const;
  114 + //! \brief Get autodelete
  115 + //!
  116 + //! When autodelete is set to true, the schedule is removed after it expires.
  117 + //! Only for non-recurring schedules.
73 bool getAutodelete() const; 118 bool getAutodelete() const;
  119 + //! \brief Get created time
  120 + //! \returns AbsoluteTime without variation
74 time::AbsoluteTime getCreated() const; 121 time::AbsoluteTime getCreated() const;
  122 + //! \brief Get start time for timers
  123 + //! \returns AbsoluteTime without variation when the timer was started.
  124 + //! \throws nlohmann::json::out_of_range when the schedule does not have a start time
75 time::AbsoluteTime getStartTime() const; 125 time::AbsoluteTime getStartTime() const;
76 126
  127 + //! \brief Set schedule name
  128 + //! \param name New name for the schedule. Max size is 32.
  129 + //! Must be unique for all schedules, otherwise a number is added.
  130 + //! \throws std::system_error when system or socket operations fail
  131 + //! \throws HueException when response contained no body
  132 + //! \throws HueAPIResponseException when response contains an error
  133 + //! \throws nlohmann::json::parse_error when response could not be parsed
77 void setName(const std::string& name); 134 void setName(const std::string& name);
  135 + //! \brief Set schedule description
  136 + //! \param description New description, may be empty. Max size is 64.
  137 + //! \throws std::system_error when system or socket operations fail
  138 + //! \throws HueException when response contained no body
  139 + //! \throws HueAPIResponseException when response contains an error
  140 + //! \throws nlohmann::json::parse_error when response could not be parsed
78 void setDescription(const std::string& description); 141 void setDescription(const std::string& description);
  142 + //! \brief Set schedule command
  143 + //! \param command New command that is executed when the time event occurs.
  144 + //! \throws std::system_error when system or socket operations fail
  145 + //! \throws HueException when response contained no body
  146 + //! \throws HueAPIResponseException when response contains an error
  147 + //! \throws nlohmann::json::parse_error when response could not be parsed
79 void setCommand(const ScheduleCommand& command); 148 void setCommand(const ScheduleCommand& command);
  149 + //! \brief Set new time when the event will occur
  150 + //! \param timePattern Any possible value of TimePattern
  151 + //! \throws std::system_error when system or socket operations fail
  152 + //! \throws HueException when response contained no body
  153 + //! \throws HueAPIResponseException when response contains an error
  154 + //! \throws nlohmann::json::parse_error when response could not be parsed
80 void setTime(const time::TimePattern& timePattern); 155 void setTime(const time::TimePattern& timePattern);
  156 + //! \brief Enable or disable schedule
  157 + //! \param status Enabled or disabled
  158 + //!
  159 + //! Can be used to reset a timer by setting to disabled and enabled again.
  160 + //! \throws std::system_error when system or socket operations fail
  161 + //! \throws HueException when response contained no body
  162 + //! \throws HueAPIResponseException when response contains an error
  163 + //! \throws nlohmann::json::parse_error when response could not be parsed
81 void setStatus(Status status); 164 void setStatus(Status status);
  165 + //! \brief Set autodelete
  166 + //! \param autodelete Whether to delete the schedule after it expires
  167 + //! \throws std::system_error when system or socket operations fail
  168 + //! \throws HueException when response contained no body
  169 + //! \throws HueAPIResponseException when response contains an error
  170 + //! \throws nlohmann::json::parse_error when response could not be parsed
82 void setAutodelete(bool autodelete); 171 void setAutodelete(bool autodelete);
83 172
84 private: 173 private:
  174 + //! \brief Utility function to send put request to the schedule.
  175 + //! \throws std::system_error when system or socket operations fail
  176 + //! \throws HueException when response contained no body
  177 + //! \throws HueAPIResponseException when response contains an error
  178 + //! \throws nlohmann::json::parse_error when response could not be parsed
85 void sendPutRequest(const nlohmann::json& request, FileInfo fileInfo); 179 void sendPutRequest(const nlohmann::json& request, FileInfo fileInfo);
86 180
87 private: 181 private:
@@ -89,6 +183,42 @@ private: @@ -89,6 +183,42 @@ private:
89 APICache state; 183 APICache state;
90 HueCommandAPI commands; 184 HueCommandAPI commands;
91 }; 185 };
  186 +
  187 +//! \brief Parameters for creating a new Schedule.
  188 +//!
  189 +//! Can be used like a builder object with chained calls.
  190 +class CreateSchedule
  191 +{
  192 +public:
  193 + //! \brief Set name
  194 + //! \see Schedule::setName
  195 + CreateSchedule& setName(const std::string& name);
  196 + //! \brief Set description
  197 + //! \see Schedule::setDescription
  198 + CreateSchedule& setDescription(const std::string& description);
  199 + //! \brief Set command
  200 + //! \see Schedule::setCommand
  201 + CreateSchedule& setCommand(const ScheduleCommand& command);
  202 + //! \brief Set time
  203 + //! \see Schedule::setTime
  204 + CreateSchedule& setTime(const time::TimePattern& time);
  205 + //! \brief Set status
  206 + //! \see Schedule::setStatus
  207 + CreateSchedule& setStatus(Schedule::Status status);
  208 + //! \brief Set autodelete
  209 + //! \see Schedule::setAutodelete
  210 + CreateSchedule& setAutodelete(bool autodelete);
  211 + //! \brief Set recycle
  212 + //!
  213 + //! When recycle is true, it is deleted when no resourcelinks refer to it.
  214 + CreateSchedule& setRecycle(bool recycle);
  215 +
  216 + nlohmann::json getRequest() const;
  217 +
  218 +private:
  219 + nlohmann::json request;
  220 +};
  221 +
92 } // namespace hueplusplus 222 } // namespace hueplusplus
93 223
94 #endif 224 #endif
include/hueplusplus/TimePattern.h
@@ -380,6 +380,7 @@ public: @@ -380,6 +380,7 @@ public:
380 static TimePattern parse(const std::string& s); 380 static TimePattern parse(const std::string& s);
381 381
382 private: 382 private:
  383 + //! \brief Calls destructor of active union member
383 void destroy(); 384 void destroy();
384 385
385 private: 386 private:
src/Hue.cpp
@@ -381,6 +381,71 @@ bool Hue::lightExists(int id) const @@ -381,6 +381,71 @@ bool Hue::lightExists(int id) const
381 return false; 381 return false;
382 } 382 }
383 383
  384 +std::vector<std::reference_wrapper<Schedule>> Hue::getAllSchedules()
  385 +{
  386 + nlohmann::json schedulesState = stateCache.getValue().at("schedules");
  387 + for (auto it = schedulesState.begin(); it != schedulesState.end(); ++it)
  388 + {
  389 + getSchedule(std::stoi(it.key()));
  390 + }
  391 + std::vector<std::reference_wrapper<Schedule>> result;
  392 + result.reserve(result.size());
  393 + for (auto& entry : schedules)
  394 + {
  395 + result.emplace_back(entry.second);
  396 + }
  397 + return result;
  398 +}
  399 +
  400 +Schedule& Hue::getSchedule(int id)
  401 +{
  402 + auto pos = schedules.find(id);
  403 + if (pos != schedules.end())
  404 + {
  405 + pos->second.refresh();
  406 + return pos->second;
  407 + }
  408 + const nlohmann::json& schedulesCache = stateCache.getValue()["schedules"];
  409 + if (!schedulesCache.count(std::to_string(id)))
  410 + {
  411 + std::cerr << "Error in Hue getSchedule(): schedule with id " << id << " is not valid\n";
  412 + throw HueException(CURRENT_FILE_INFO, "Schedule id is not valid");
  413 + }
  414 + return schedules.emplace(id, Schedule(id, commands, stateCache.getRefreshDuration())).first->second;
  415 +}
  416 +
  417 +bool Hue::scheduleExists(int id) const
  418 +{
  419 + auto pos = schedules.find(id);
  420 + if (pos != schedules.end())
  421 + {
  422 + return true;
  423 + }
  424 + if (stateCache.getValue()["schedules"].count(std::to_string(id)))
  425 + {
  426 + return true;
  427 + }
  428 + return false;
  429 +}
  430 +
  431 +int Hue::createSchedule(const CreateSchedule& params)
  432 +{
  433 + nlohmann::json response = commands.POSTRequest("/schedules", params.getRequest(), CURRENT_FILE_INFO);
  434 + nlohmann::json id = utils::safeGetMember(response, 0, "success", "id");
  435 + if (id.is_string())
  436 + {
  437 + std::string idStr = id.get<std::string>();
  438 + // Sometimes the response can be /groups/<id>?
  439 + if (idStr.find("/schedules/") == 0)
  440 + {
  441 + idStr.erase(0, 11);
  442 + }
  443 + stateCache.refresh();
  444 + return std::stoi(idStr);
  445 + }
  446 + return 0;
  447 +}
  448 +
384 std::string Hue::getPictureOfLight(int id) const 449 std::string Hue::getPictureOfLight(int id) const
385 { 450 {
386 std::string ret = ""; 451 std::string ret = "";
src/Schedule.cpp
@@ -190,4 +190,51 @@ void Schedule::sendPutRequest(const nlohmann::json&amp; request, FileInfo fileInfo) @@ -190,4 +190,51 @@ void Schedule::sendPutRequest(const nlohmann::json&amp; request, FileInfo fileInfo)
190 { 190 {
191 commands.PUTRequest("/schedules/" + std::to_string(id), request, std::move(fileInfo)); 191 commands.PUTRequest("/schedules/" + std::to_string(id), request, std::move(fileInfo));
192 } 192 }
  193 +
  194 +CreateSchedule& CreateSchedule::setName(const std::string& name)
  195 +{
  196 + request["name"] = name;
  197 + return *this;
  198 +}
  199 +
  200 +CreateSchedule& CreateSchedule::setDescription(const std::string& description)
  201 +{
  202 + request["descripton"] = description;
  203 + return *this;
  204 +}
  205 +
  206 +CreateSchedule& CreateSchedule::setCommand(const ScheduleCommand& command)
  207 +{
  208 + request["command"] = command.toJson();
  209 + return *this;
  210 +}
  211 +
  212 +CreateSchedule& CreateSchedule::setTime(const time::TimePattern& time)
  213 +{
  214 + request["localtime"] = time.toString();
  215 + return *this;
  216 +}
  217 +
  218 +CreateSchedule& CreateSchedule::setStatus(Schedule::Status status)
  219 +{
  220 + request["status"] = (status == Schedule::Status::enabled) ? "enabled" : "disabled";
  221 + return *this;
  222 +}
  223 +
  224 +CreateSchedule& CreateSchedule::setAutodelete(bool autodelete)
  225 +{
  226 + request["autodelete"] = autodelete;
  227 + return *this;
  228 +}
  229 +
  230 +CreateSchedule& CreateSchedule::setRecycle(bool recycle)
  231 +{
  232 + request["recycle"] = recycle;
  233 + return *this;
  234 +}
  235 +
  236 +nlohmann::json CreateSchedule::getRequest() const
  237 +{
  238 + return request;
  239 +}
193 } // namespace hueplusplus 240 } // namespace hueplusplus
194 \ No newline at end of file 241 \ No newline at end of file