You need to sign in before continuing.

Commit a8eb8aa7228cbac17d1c91cc90e4160bb30bc2dc

Authored by Jojo-1000
Committed by Moritz Wirger
1 parent 8cf83417

Add Rule class.

include/hueplusplus/Rule.h 0 → 100644
  1 +/**
  2 + \file Rule.h
  3 + Copyright Notice\n
  4 + Copyright (C) 2020 Jan Rogall - developer\n
  5 +
  6 + This file is part of hueplusplus.
  7 +
  8 + hueplusplus is free software: you can redistribute it and/or modify
  9 + it under the terms of the GNU Lesser General Public License as published by
  10 + the Free Software Foundation, either version 3 of the License, or
  11 + (at your option) any later version.
  12 +
  13 + hueplusplus is distributed in the hope that it will be useful,
  14 + but WITHOUT ANY WARRANTY; without even the implied warranty of
  15 + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16 + GNU Lesser General Public License for more details.
  17 +
  18 + You should have received a copy of the GNU Lesser General Public License
  19 + along with hueplusplus. If not, see <http://www.gnu.org/licenses/>.
  20 +**/
  21 +
  22 +#ifndef INCLUDE_HUEPLUSPLUS_RULE_H
  23 +#define INCLUDE_HUEPLUSPLUS_RULE_H
  24 +
  25 +#include <string>
  26 +
  27 +#include "APICache.h"
  28 +#include "HueCommandAPI.h"
  29 +#include "Schedule.h"
  30 +#include "TimePattern.h"
  31 +
  32 +namespace hueplusplus
  33 +{
  34 +
  35 +class Condition
  36 +{
  37 +public:
  38 + //! \brief Specifies which operation is used to check the condition
  39 + enum class Operator
  40 + {
  41 + eq, //!< Attribute is equal to specified value (for bool and int)
  42 + gt, //!< Attribute is greater than specified value (for int)
  43 + lt, //!< Attribute is less than specified value (for int)
  44 + dx, //!< Attribute has changed (no value given)
  45 + ddx, //!< Delayed attribute has changed (no value given)
  46 + stable, //!< Stable for a given time
  47 + not_stable, //!< Not stable for a given time
  48 + in, //!< Time is in the given interval (triggered on start time)
  49 + not_in //!< Time is not in the interval (triggered on end time)
  50 + };
  51 +
  52 +public:
  53 + Condition(const std::string& address, Operator op, const std::string& value);
  54 +
  55 + std::string getAddress() const;
  56 + Operator getOperator() const;
  57 + std::string getValue() const;
  58 +
  59 + nlohmann::json toJson() const;
  60 +
  61 + static Condition parse(const nlohmann::json& json);
  62 +private:
  63 + std::string address;
  64 + Operator op;
  65 + std::string value;
  66 +};
  67 +
  68 +class Rule
  69 +{
  70 +public:
  71 + //! \brief Creates rule with id
  72 + //! \param id Rule id in the bridge
  73 + //! \param commands HueCommandAPI for requests
  74 + //! \param refreshDuration Time between refreshing the cached state.
  75 + Rule(int id, const HueCommandAPI& commands, std::chrono::steady_clock::duration refreshDuration);
  76 +
  77 + //! \brief Refreshes internal cached state.
  78 + //! \param force \c true forces a refresh, regardless of how long the last refresh was ago.
  79 + //! \c false to only refresh when enough time has passed (needed e.g. when calling only const methods).
  80 + //! \throws std::system_error when system or socket operations fail
  81 + //! \throws HueException when response contained no body
  82 + //! \throws HueAPIResponseException when response contains an error
  83 + //! \throws nlohmann::json::parse_error when response could not be parsed
  84 + void refresh(bool force = false);
  85 +
  86 + //! \brief Get rule identifier
  87 + int getId() const;
  88 +
  89 + //! \brief Get rule name
  90 + std::string getName() const;
  91 +
  92 + //! \brief Get created time
  93 + time::AbsoluteTime getCreated() const;
  94 +
  95 + //! \brief Get time the rule was last triggered
  96 + time::AbsoluteTime getLastTriggered() const;
  97 +
  98 + //! \brief Get the number of times the rule was triggered
  99 + int getTimesTriggered() const;
  100 +
  101 + //! \brief Get whether rule is enabled or disabled
  102 + bool isEnabled() const;
  103 +
  104 + //! \brief Get user that created or last changed the rule.
  105 + std::string getOwner() const;
  106 +
  107 + std::vector<Condition> getConditions() const;
  108 + std::vector<ScheduleCommand> getActions() const;
  109 +private:
  110 + int id;
  111 + APICache state;
  112 +};
  113 +
  114 +} // namespace hueplusplus
  115 +
  116 +#endif
... ...
include/hueplusplus/Schedule.h
... ... @@ -31,6 +31,8 @@ namespace hueplusplus
31 31 //!
32 32 //! The command makes either a POST, PUT or DELETE request with a given body
33 33 //! to an address on the bridge.
  34 +//!
  35 +//! A ScheduleCommand can also be created by StateTransaction::toScheduleCommand().
34 36 class ScheduleCommand
35 37 {
36 38 public:
... ... @@ -74,14 +76,6 @@ private:
74 76 class Schedule
75 77 {
76 78 public:
77   - //! \brief Enabled status of the Schedule
78   - enum class Status
79   - {
80   - disabled, //!< Schedule is disabled
81   - enabled //!< Schedule is enabled
82   - };
83   -
84   -public:
85 79 //! \brief Construct Schedule that exists in the bridge
86 80 //! \param id Schedule ID
87 81 //! \param commands HueCommandAPI for requests
... ... @@ -109,8 +103,8 @@ public:
109 103 //! \brief Get time when the event(s) will occur
110 104 //! \returns TimePattern in local timezone
111 105 time::TimePattern getTime() const;
112   - //! \brief Get schedule enabled/disabled status
113   - Status getStatus() const;
  106 + //! \brief Get whether schedule is enabled or disabled
  107 + bool isEnabled() const;
114 108 //! \brief Get autodelete
115 109 //!
116 110 //! When autodelete is set to true, the schedule is removed after it expires.
... ... @@ -161,7 +155,7 @@ public:
161 155 //! \throws HueException when response contained no body
162 156 //! \throws HueAPIResponseException when response contains an error
163 157 //! \throws nlohmann::json::parse_error when response could not be parsed
164   - void setStatus(Status status);
  158 + void setEnabled(bool enabled);
165 159 //! \brief Set autodelete
166 160 //! \param autodelete Whether to delete the schedule after it expires
167 161 //! \throws std::system_error when system or socket operations fail
... ... @@ -202,8 +196,8 @@ public:
202 196 //! \see Schedule::setTime
203 197 CreateSchedule& setTime(const time::TimePattern& time);
204 198 //! \brief Set status
205   - //! \see Schedule::setStatus
206   - CreateSchedule& setStatus(Schedule::Status status);
  199 + //! \see Schedule::setEnabled
  200 + CreateSchedule& setStatus(bool enabled);
207 201 //! \brief Set autodelete
208 202 //! \see Schedule::setAutodelete
209 203 CreateSchedule& setAutodelete(bool autodelete);
... ... @@ -213,7 +207,7 @@ public:
213 207 CreateSchedule& setRecycle(bool recycle);
214 208  
215 209 //! \brief Get request to create the schedule.
216   -//! \returns JSON request for a POST to create the new schedule
  210 + //! \returns JSON request for a POST to create the new schedule
217 211 nlohmann::json getRequest() const;
218 212  
219 213 private:
... ...
src/Rule.cpp 0 → 100644
  1 +/**
  2 + \file Rule.cpp
  3 + Copyright Notice\n
  4 + Copyright (C) 2020 Jan Rogall - developer\n
  5 +
  6 + This file is part of hueplusplus.
  7 +
  8 + hueplusplus is free software: you can redistribute it and/or modify
  9 + it under the terms of the GNU Lesser General Public License as published by
  10 + the Free Software Foundation, either version 3 of the License, or
  11 + (at your option) any later version.
  12 +
  13 + hueplusplus is distributed in the hope that it will be useful,
  14 + but WITHOUT ANY WARRANTY; without even the implied warranty of
  15 + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16 + GNU Lesser General Public License for more details.
  17 +
  18 + You should have received a copy of the GNU Lesser General Public License
  19 + along with hueplusplus. If not, see <http://www.gnu.org/licenses/>.
  20 +**/
  21 +
  22 +#include <hueplusplus/Rule.h>
  23 +
  24 +namespace hueplusplus
  25 +{
  26 +Condition::Condition(const std::string& address, Operator op, const std::string& value)
  27 + : address(address), op(op), value(value)
  28 +{ }
  29 +std::string Condition::getAddress() const
  30 +{
  31 + return address;
  32 +}
  33 +Condition::Operator Condition::getOperator() const
  34 +{
  35 + return op;
  36 +}
  37 +std::string Condition::getValue() const
  38 +{
  39 + return value;
  40 +}
  41 +
  42 +nlohmann::json Condition::toJson() const
  43 +{
  44 + std::string opStr;
  45 + switch (op)
  46 + {
  47 + case Operator::eq:
  48 + opStr = "eq";
  49 + break;
  50 + case Operator::gt:
  51 + opStr = "gt";
  52 + break;
  53 + case Operator::lt:
  54 + opStr = "lt";
  55 + break;
  56 + case Operator::dx:
  57 + opStr = "dx";
  58 + break;
  59 + case Operator::ddx:
  60 + opStr = "ddx";
  61 + break;
  62 + case Operator::stable:
  63 + opStr = "stable";
  64 + break;
  65 + case Operator::not_stable:
  66 + opStr = "eq";
  67 + break;
  68 + case Operator::in:
  69 + opStr = "eq";
  70 + break;
  71 + case Operator::not_in:
  72 + opStr = "eq";
  73 + break;
  74 + }
  75 +
  76 + nlohmann::json result = {{"address", address}, {"operator", opStr}};
  77 + if (!value.empty())
  78 + {
  79 + result["value"] = value;
  80 + }
  81 + return result;
  82 +}
  83 +
  84 +Condition Condition::parse(const nlohmann::json& json)
  85 +{
  86 + std::string address = json.at("address").get<std::string>();
  87 + std::string value = json.value("value", "");
  88 + std::string opStr = json.at("operator").get<std::string>();
  89 + Operator op = Operator::eq;
  90 + if (opStr == "gt")
  91 + {
  92 + op = Operator::gt;
  93 + }
  94 + else if (opStr == "lt")
  95 + {
  96 + op = Operator::lt;
  97 + }
  98 + else if (opStr == "dx")
  99 + {
  100 + op = Operator::dx;
  101 + }
  102 + else if (opStr == "ddx")
  103 + {
  104 + op = Operator::ddx;
  105 + }
  106 + else if (opStr == "stable")
  107 + {
  108 + op = Operator::stable;
  109 + }
  110 + else if (opStr == "not_stable")
  111 + {
  112 + op = Operator::not_stable;
  113 + }
  114 + else if (opStr == "in")
  115 + {
  116 + op = Operator::in;
  117 + }
  118 + else if (opStr == "not_in")
  119 + {
  120 + op = Operator::not_in;
  121 + }
  122 +
  123 + return Condition(address, op, value);
  124 +}
  125 +
  126 +Rule::Rule(int id, const HueCommandAPI& commands, std::chrono::steady_clock::duration refreshDuration)
  127 + : id(id), state("/rules/" + id, commands, refreshDuration)
  128 +{ }
  129 +
  130 +void Rule::refresh(bool force)
  131 +{
  132 + if (force)
  133 + {
  134 + state.refresh();
  135 + }
  136 + else
  137 + {
  138 + state.getValue();
  139 + }
  140 +}
  141 +
  142 +int Rule::getId() const
  143 +{
  144 + return id;
  145 +}
  146 +
  147 +std::string Rule::getName() const
  148 +{
  149 + return state.getValue().at("name").get<std::string>();
  150 +}
  151 +
  152 +time::AbsoluteTime Rule::getCreated() const
  153 +{
  154 + return time::AbsoluteTime::parseUTC(state.getValue().at("creationtime").get<std::string>());
  155 +}
  156 +
  157 +time::AbsoluteTime Rule::getLastTriggered() const
  158 +{
  159 + return time::AbsoluteTime::parseUTC(state.getValue().at("lasttriggered").get<std::string>());
  160 +}
  161 +
  162 +int Rule::getTimesTriggered() const
  163 +{
  164 + return state.getValue().at("timestriggered").get<int>();
  165 +}
  166 +
  167 +bool Rule::isEnabled() const
  168 +{
  169 + return state.getValue().at("status").get<std::string>() == "enabled";
  170 +}
  171 +
  172 +std::string Rule::getOwner() const
  173 +{
  174 + return state.getValue().at("owner").get<std::string>();
  175 +}
  176 +
  177 +std::vector<Condition> Rule::getConditions() const
  178 +{
  179 + std::vector<Condition> result;
  180 + const nlohmann::json& conditions = state.getValue().at("conditions");
  181 + for (const nlohmann::json& c : conditions)
  182 + {
  183 + result.emplace_back(Condition::parse(c));
  184 + }
  185 + return result;
  186 +}
  187 +
  188 +std::vector<ScheduleCommand> Rule::getActions() const
  189 +{
  190 + std::vector<ScheduleCommand> result;
  191 + const nlohmann::json& actions = state.getValue().at("actions");
  192 + for (const nlohmann::json& a : actions)
  193 + {
  194 + result.emplace_back(a);
  195 + }
  196 + return result;
  197 +}
  198 +
  199 +} // namespace hueplusplus
0 200 \ No newline at end of file
... ...
src/Schedule.cpp
... ... @@ -118,13 +118,13 @@ time::TimePattern Schedule::getTime() const
118 118 // return time::TimePattern::parse(state.getValue().at("time").get<std::string>());
119 119 }
120 120  
121   -Schedule::Status Schedule::getStatus() const
  121 +bool Schedule::isEnabled() const
122 122 {
123 123 if (state.getValue().at("status").get<std::string>() == "enabled")
124 124 {
125   - return Status::enabled;
  125 + return true;
126 126 }
127   - return Status::disabled;
  127 + return false;
128 128 }
129 129  
130 130 bool Schedule::getAutodelete() const
... ... @@ -174,9 +174,9 @@ void Schedule::setTime(const time::TimePattern&amp; timePattern)
174 174 refresh();
175 175 }
176 176  
177   -void Schedule::setStatus(Status status)
  177 +void Schedule::setEnabled(bool enabled)
178 178 {
179   - sendPutRequest({{"status", status == Status::enabled ? "enabled" : "disabled"}}, CURRENT_FILE_INFO);
  179 + sendPutRequest({{"status", enabled ? "enabled" : "disabled"}}, CURRENT_FILE_INFO);
180 180 refresh();
181 181 }
182 182  
... ... @@ -215,9 +215,9 @@ CreateSchedule&amp; CreateSchedule::setTime(const time::TimePattern&amp; time)
215 215 return *this;
216 216 }
217 217  
218   -CreateSchedule& CreateSchedule::setStatus(Schedule::Status status)
  218 +CreateSchedule& CreateSchedule::setStatus(bool enabled)
219 219 {
220   - request["status"] = (status == Schedule::Status::enabled) ? "enabled" : "disabled";
  220 + request["status"] = enabled ? "enabled" : "disabled";
221 221 return *this;
222 222 }
223 223  
... ...
test/test_Schedule.cpp
... ... @@ -149,13 +149,13 @@ TEST_F(ScheduleTest, getStatus)
149 149 scheduleState["status"] = "enabled";
150 150 expectGetState(id);
151 151 const Schedule schedule(id, commands, std::chrono::seconds(0));
152   - EXPECT_EQ(Schedule::Status::enabled, schedule.getStatus());
  152 + EXPECT_EQ(true, schedule.isEnabled());
153 153 }
154 154 {
155 155 scheduleState["status"] = "disabled";
156 156 expectGetState(id);
157 157 const Schedule schedule(id, commands, std::chrono::seconds(0));
158   - EXPECT_EQ(Schedule::Status::disabled, schedule.getStatus());
  158 + EXPECT_EQ(false, schedule.isEnabled());
159 159 }
160 160 }
161 161  
... ... @@ -261,7 +261,7 @@ TEST_F(ScheduleTest, setStatus)
261 261 *handler, PUTJson("/api/" + getBridgeUsername() + "/schedules/1", request, getBridgeIp(), getBridgePort()))
262 262 .WillOnce(Return(response));
263 263 expectGetState(id);
264   - schedule.setStatus(Schedule::Status::enabled);
  264 + schedule.setEnabled(true);
265 265 }
266 266 {
267 267 nlohmann::json request = {{"status", "disabled"}};
... ... @@ -270,7 +270,7 @@ TEST_F(ScheduleTest, setStatus)
270 270 *handler, PUTJson("/api/" + getBridgeUsername() + "/schedules/1", request, getBridgeIp(), getBridgePort()))
271 271 .WillOnce(Return(response));
272 272 expectGetState(id);
273   - schedule.setStatus(Schedule::Status::disabled);
  273 + schedule.setEnabled(false);
274 274 }
275 275 }
276 276  
... ... @@ -329,11 +329,11 @@ TEST(CreateSchedule, setStatus)
329 329 {
330 330 {
331 331 const nlohmann::json request = {{"status", "enabled"}};
332   - EXPECT_EQ(request, CreateSchedule().setStatus(Schedule::Status::enabled).getRequest());
  332 + EXPECT_EQ(request, CreateSchedule().setStatus(true).getRequest());
333 333 }
334 334 {
335 335 const nlohmann::json request = {{"status", "disabled"}};
336   - EXPECT_EQ(request, CreateSchedule().setStatus(Schedule::Status::disabled).getRequest());
  336 + EXPECT_EQ(request, CreateSchedule().setStatus(false).getRequest());
337 337 }
338 338 }
339 339  
... ...