Commit a8eb8aa7228cbac17d1c91cc90e4160bb30bc2dc
Committed by
Moritz Wirger
1 parent
8cf83417
Add Rule class.
Showing
5 changed files
with
336 additions
and
27 deletions
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& 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& CreateSchedule::setTime(const time::TimePattern& 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 | ... | ... |