From a8eb8aa7228cbac17d1c91cc90e4160bb30bc2dc Mon Sep 17 00:00:00 2001 From: Jojo-1000 <33495614+Jojo-1000@users.noreply.github.com> Date: Sun, 30 Aug 2020 01:14:54 +0200 Subject: [PATCH] Add Rule class. --- include/hueplusplus/Rule.h | 116 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ include/hueplusplus/Schedule.h | 22 ++++++++-------------- src/Rule.cpp | 199 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/Schedule.cpp | 14 +++++++------- test/test_Schedule.cpp | 12 ++++++------ 5 files changed, 336 insertions(+), 27 deletions(-) create mode 100644 include/hueplusplus/Rule.h create mode 100644 src/Rule.cpp diff --git a/include/hueplusplus/Rule.h b/include/hueplusplus/Rule.h new file mode 100644 index 0000000..af57728 --- /dev/null +++ b/include/hueplusplus/Rule.h @@ -0,0 +1,116 @@ +/** + \file Rule.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_RULE_H +#define INCLUDE_HUEPLUSPLUS_RULE_H + +#include + +#include "APICache.h" +#include "HueCommandAPI.h" +#include "Schedule.h" +#include "TimePattern.h" + +namespace hueplusplus +{ + +class Condition +{ +public: + //! \brief Specifies which operation is used to check the condition + enum class Operator + { + eq, //!< Attribute is equal to specified value (for bool and int) + gt, //!< Attribute is greater than specified value (for int) + lt, //!< Attribute is less than specified value (for int) + dx, //!< Attribute has changed (no value given) + ddx, //!< Delayed attribute has changed (no value given) + stable, //!< Stable for a given time + not_stable, //!< Not stable for a given time + in, //!< Time is in the given interval (triggered on start time) + not_in //!< Time is not in the interval (triggered on end time) + }; + +public: + Condition(const std::string& address, Operator op, const std::string& value); + + std::string getAddress() const; + Operator getOperator() const; + std::string getValue() const; + + nlohmann::json toJson() const; + + static Condition parse(const nlohmann::json& json); +private: + std::string address; + Operator op; + std::string value; +}; + +class Rule +{ +public: + //! \brief Creates rule with id + //! \param id Rule id in the bridge + //! \param commands HueCommandAPI for requests + //! \param refreshDuration Time between refreshing the cached state. + Rule(int id, const HueCommandAPI& commands, std::chrono::steady_clock::duration refreshDuration); + + //! \brief Refreshes internal cached state. + //! \param force \c true forces a refresh, regardless of how long the last refresh was ago. + //! \c false to only refresh when enough time has passed (needed e.g. when calling only const methods). + //! \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(bool force = false); + + //! \brief Get rule identifier + int getId() const; + + //! \brief Get rule name + std::string getName() const; + + //! \brief Get created time + time::AbsoluteTime getCreated() const; + + //! \brief Get time the rule was last triggered + time::AbsoluteTime getLastTriggered() const; + + //! \brief Get the number of times the rule was triggered + int getTimesTriggered() const; + + //! \brief Get whether rule is enabled or disabled + bool isEnabled() const; + + //! \brief Get user that created or last changed the rule. + std::string getOwner() const; + + std::vector getConditions() const; + std::vector getActions() const; +private: + int id; + APICache state; +}; + +} // namespace hueplusplus + +#endif diff --git a/include/hueplusplus/Schedule.h b/include/hueplusplus/Schedule.h index b422546..49c1f56 100644 --- a/include/hueplusplus/Schedule.h +++ b/include/hueplusplus/Schedule.h @@ -31,6 +31,8 @@ namespace hueplusplus //! //! 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: @@ -74,14 +76,6 @@ private: class Schedule { public: - //! \brief Enabled status of the Schedule - enum class Status - { - 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 @@ -109,8 +103,8 @@ public: //! \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 whether schedule is enabled or disabled + bool isEnabled() const; //! \brief Get autodelete //! //! When autodelete is set to true, the schedule is removed after it expires. @@ -161,7 +155,7 @@ public: //! \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); + void setEnabled(bool enabled); //! \brief Set autodelete //! \param autodelete Whether to delete the schedule after it expires //! \throws std::system_error when system or socket operations fail @@ -202,8 +196,8 @@ public: //! \see Schedule::setTime CreateSchedule& setTime(const time::TimePattern& time); //! \brief Set status - //! \see Schedule::setStatus - CreateSchedule& setStatus(Schedule::Status status); + //! \see Schedule::setEnabled + CreateSchedule& setStatus(bool enabled); //! \brief Set autodelete //! \see Schedule::setAutodelete CreateSchedule& setAutodelete(bool autodelete); @@ -213,7 +207,7 @@ public: CreateSchedule& setRecycle(bool recycle); //! \brief Get request to create the schedule. -//! \returns JSON request for a POST to create the new schedule + //! \returns JSON request for a POST to create the new schedule nlohmann::json getRequest() const; private: diff --git a/src/Rule.cpp b/src/Rule.cpp new file mode 100644 index 0000000..15bccd2 --- /dev/null +++ b/src/Rule.cpp @@ -0,0 +1,199 @@ +/** + \file Rule.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 + +namespace hueplusplus +{ +Condition::Condition(const std::string& address, Operator op, const std::string& value) + : address(address), op(op), value(value) +{ } +std::string Condition::getAddress() const +{ + return address; +} +Condition::Operator Condition::getOperator() const +{ + return op; +} +std::string Condition::getValue() const +{ + return value; +} + +nlohmann::json Condition::toJson() const +{ + std::string opStr; + switch (op) + { + case Operator::eq: + opStr = "eq"; + break; + case Operator::gt: + opStr = "gt"; + break; + case Operator::lt: + opStr = "lt"; + break; + case Operator::dx: + opStr = "dx"; + break; + case Operator::ddx: + opStr = "ddx"; + break; + case Operator::stable: + opStr = "stable"; + break; + case Operator::not_stable: + opStr = "eq"; + break; + case Operator::in: + opStr = "eq"; + break; + case Operator::not_in: + opStr = "eq"; + break; + } + + nlohmann::json result = {{"address", address}, {"operator", opStr}}; + if (!value.empty()) + { + result["value"] = value; + } + return result; +} + +Condition Condition::parse(const nlohmann::json& json) +{ + std::string address = json.at("address").get(); + std::string value = json.value("value", ""); + std::string opStr = json.at("operator").get(); + Operator op = Operator::eq; + if (opStr == "gt") + { + op = Operator::gt; + } + else if (opStr == "lt") + { + op = Operator::lt; + } + else if (opStr == "dx") + { + op = Operator::dx; + } + else if (opStr == "ddx") + { + op = Operator::ddx; + } + else if (opStr == "stable") + { + op = Operator::stable; + } + else if (opStr == "not_stable") + { + op = Operator::not_stable; + } + else if (opStr == "in") + { + op = Operator::in; + } + else if (opStr == "not_in") + { + op = Operator::not_in; + } + + return Condition(address, op, value); +} + +Rule::Rule(int id, const HueCommandAPI& commands, std::chrono::steady_clock::duration refreshDuration) + : id(id), state("/rules/" + id, commands, refreshDuration) +{ } + +void Rule::refresh(bool force) +{ + if (force) + { + state.refresh(); + } + else + { + state.getValue(); + } +} + +int Rule::getId() const +{ + return id; +} + +std::string Rule::getName() const +{ + return state.getValue().at("name").get(); +} + +time::AbsoluteTime Rule::getCreated() const +{ + return time::AbsoluteTime::parseUTC(state.getValue().at("creationtime").get()); +} + +time::AbsoluteTime Rule::getLastTriggered() const +{ + return time::AbsoluteTime::parseUTC(state.getValue().at("lasttriggered").get()); +} + +int Rule::getTimesTriggered() const +{ + return state.getValue().at("timestriggered").get(); +} + +bool Rule::isEnabled() const +{ + return state.getValue().at("status").get() == "enabled"; +} + +std::string Rule::getOwner() const +{ + return state.getValue().at("owner").get(); +} + +std::vector Rule::getConditions() const +{ + std::vector result; + const nlohmann::json& conditions = state.getValue().at("conditions"); + for (const nlohmann::json& c : conditions) + { + result.emplace_back(Condition::parse(c)); + } + return result; +} + +std::vector Rule::getActions() const +{ + std::vector result; + const nlohmann::json& actions = state.getValue().at("actions"); + for (const nlohmann::json& a : actions) + { + result.emplace_back(a); + } + return result; +} + +} // namespace hueplusplus \ No newline at end of file diff --git a/src/Schedule.cpp b/src/Schedule.cpp index 7a30de3..e26dcff 100644 --- a/src/Schedule.cpp +++ b/src/Schedule.cpp @@ -118,13 +118,13 @@ time::TimePattern Schedule::getTime() const // return time::TimePattern::parse(state.getValue().at("time").get()); } -Schedule::Status Schedule::getStatus() const +bool Schedule::isEnabled() const { if (state.getValue().at("status").get() == "enabled") { - return Status::enabled; + return true; } - return Status::disabled; + return false; } bool Schedule::getAutodelete() const @@ -174,9 +174,9 @@ void Schedule::setTime(const time::TimePattern& timePattern) refresh(); } -void Schedule::setStatus(Status status) +void Schedule::setEnabled(bool enabled) { - sendPutRequest({{"status", status == Status::enabled ? "enabled" : "disabled"}}, CURRENT_FILE_INFO); + sendPutRequest({{"status", enabled ? "enabled" : "disabled"}}, CURRENT_FILE_INFO); refresh(); } @@ -215,9 +215,9 @@ CreateSchedule& CreateSchedule::setTime(const time::TimePattern& time) return *this; } -CreateSchedule& CreateSchedule::setStatus(Schedule::Status status) +CreateSchedule& CreateSchedule::setStatus(bool enabled) { - request["status"] = (status == Schedule::Status::enabled) ? "enabled" : "disabled"; + request["status"] = enabled ? "enabled" : "disabled"; return *this; } diff --git a/test/test_Schedule.cpp b/test/test_Schedule.cpp index c10d5ac..a40021c 100644 --- a/test/test_Schedule.cpp +++ b/test/test_Schedule.cpp @@ -149,13 +149,13 @@ TEST_F(ScheduleTest, getStatus) scheduleState["status"] = "enabled"; expectGetState(id); const Schedule schedule(id, commands, std::chrono::seconds(0)); - EXPECT_EQ(Schedule::Status::enabled, schedule.getStatus()); + EXPECT_EQ(true, schedule.isEnabled()); } { scheduleState["status"] = "disabled"; expectGetState(id); const Schedule schedule(id, commands, std::chrono::seconds(0)); - EXPECT_EQ(Schedule::Status::disabled, schedule.getStatus()); + EXPECT_EQ(false, schedule.isEnabled()); } } @@ -261,7 +261,7 @@ TEST_F(ScheduleTest, setStatus) *handler, PUTJson("/api/" + getBridgeUsername() + "/schedules/1", request, getBridgeIp(), getBridgePort())) .WillOnce(Return(response)); expectGetState(id); - schedule.setStatus(Schedule::Status::enabled); + schedule.setEnabled(true); } { nlohmann::json request = {{"status", "disabled"}}; @@ -270,7 +270,7 @@ TEST_F(ScheduleTest, setStatus) *handler, PUTJson("/api/" + getBridgeUsername() + "/schedules/1", request, getBridgeIp(), getBridgePort())) .WillOnce(Return(response)); expectGetState(id); - schedule.setStatus(Schedule::Status::disabled); + schedule.setEnabled(false); } } @@ -329,11 +329,11 @@ TEST(CreateSchedule, setStatus) { { const nlohmann::json request = {{"status", "enabled"}}; - EXPECT_EQ(request, CreateSchedule().setStatus(Schedule::Status::enabled).getRequest()); + EXPECT_EQ(request, CreateSchedule().setStatus(true).getRequest()); } { const nlohmann::json request = {{"status", "disabled"}}; - EXPECT_EQ(request, CreateSchedule().setStatus(Schedule::Status::disabled).getRequest()); + EXPECT_EQ(request, CreateSchedule().setStatus(false).getRequest()); } } -- libgit2 0.21.4