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());
}
}