Commit 765b33155a0aa3fec9cd3256cc2b88da7390135d

Authored by Jojo-1000
Committed by Moritz Wirger
1 parent c6e6f5be

Add rule list to bridge, add documentation.

include/hueplusplus/Bridge.h
@@ -40,6 +40,7 @@ @@ -40,6 +40,7 @@
40 #include "IHttpHandler.h" 40 #include "IHttpHandler.h"
41 #include "Light.h" 41 #include "Light.h"
42 #include "ResourceList.h" 42 #include "ResourceList.h"
  43 +#include "Rule.h"
43 #include "Scene.h" 44 #include "Scene.h"
44 #include "Schedule.h" 45 #include "Schedule.h"
45 #include "Sensor.h" 46 #include "Sensor.h"
@@ -133,6 +134,7 @@ public: @@ -133,6 +134,7 @@ public:
133 using GroupList = GroupResourceList<Group, CreateGroup>; 134 using GroupList = GroupResourceList<Group, CreateGroup>;
134 using ScheduleList = CreateableResourceList<ResourceList<Schedule, int>, CreateSchedule>; 135 using ScheduleList = CreateableResourceList<ResourceList<Schedule, int>, CreateSchedule>;
135 using SceneList = CreateableResourceList<ResourceList<Scene, std::string>, CreateScene>; 136 using SceneList = CreateableResourceList<ResourceList<Scene, std::string>, CreateScene>;
  137 + using RuleList = CreateableResourceList<ResourceList<Rule, int>, CreateRule>;
136 138
137 public: 139 public:
138 //! \brief Constructor of Bridge class 140 //! \brief Constructor of Bridge class
@@ -231,6 +233,11 @@ public: @@ -231,6 +233,11 @@ public:
231 //! \note Does not refresh state. 233 //! \note Does not refresh state.
232 const SensorList& sensors() const; 234 const SensorList& sensors() const;
233 235
  236 + //! \brief Provides access to the Rule%s on the bridge.
  237 + RuleList& rules();
  238 + //! \brief Provides access to the Rule%s on the bridge
  239 + //! \note Does not refresh state.
  240 + const RuleList& rules() const;
234 private: 241 private:
235 //! \brief Function that sets the HttpHandler and updates the HueCommandAPI. 242 //! \brief Function that sets the HttpHandler and updates the HueCommandAPI.
236 //! \param handler a HttpHandler of type \ref IHttpHandler 243 //! \param handler a HttpHandler of type \ref IHttpHandler
@@ -256,6 +263,7 @@ private: @@ -256,6 +263,7 @@ private:
256 detail::MakeCopyable<ScheduleList> scheduleList; 263 detail::MakeCopyable<ScheduleList> scheduleList;
257 detail::MakeCopyable<SceneList> sceneList; 264 detail::MakeCopyable<SceneList> sceneList;
258 detail::MakeCopyable<SensorList> sensorList; 265 detail::MakeCopyable<SensorList> sensorList;
  266 + detail::MakeCopyable<RuleList> ruleList;
259 detail::MakeCopyable<BridgeConfig> bridgeConfig; 267 detail::MakeCopyable<BridgeConfig> bridgeConfig;
260 }; 268 };
261 } // namespace hueplusplus 269 } // namespace hueplusplus
include/hueplusplus/Condition.h
1 /** 1 /**
2 - \file ConditionHelper.h 2 + \file Condition.h
3 Copyright Notice\n 3 Copyright Notice\n
4 Copyright (C) 2020 Jan Rogall - developer\n 4 Copyright (C) 2020 Jan Rogall - developer\n
5 5
@@ -28,7 +28,12 @@ @@ -28,7 +28,12 @@
28 28
29 namespace hueplusplus 29 namespace hueplusplus
30 { 30 {
31 - 31 +//! \brief Condition for a Rule
  32 +//!
  33 +//! The condition checks whether a resource attribute (usually a Sensor value) matches the
  34 +//! specified Operator.
  35 +//!
  36 +//! Conditions from sensors can be created more easily using the makeCondition() helper functions.
32 class Condition 37 class Condition
33 { 38 {
34 public: 39 public:
@@ -48,14 +53,26 @@ public: @@ -48,14 +53,26 @@ public:
48 53
49 public: 54 public:
50 //! \brief Create a condition from any address on the bridge 55 //! \brief Create a condition from any address on the bridge
  56 + //! \param address Path to an attribute of the bridge
  57 + //! \param op Operator used for comparison.
  58 + //! \param value String representation of the value to check against. Empty for some operators.
51 Condition(const std::string& address, Operator op, const std::string& value); 59 Condition(const std::string& address, Operator op, const std::string& value);
52 60
  61 + //! \brief Get address on the bridge
53 std::string getAddress() const; 62 std::string getAddress() const;
  63 + //! \brief Get used operator
54 Operator getOperator() const; 64 Operator getOperator() const;
  65 + //! \brief Get value the attribute is checked against
55 std::string getValue() const; 66 std::string getValue() const;
56 67
  68 + //! \brief Create the json form of the condition
  69 + //! \returns A json object with address, operator and value
57 nlohmann::json toJson() const; 70 nlohmann::json toJson() const;
58 71
  72 + //! \brief Parse condition from json value
  73 + //! \param json Json object with address, operator and value
  74 + //! \returns The parsed condition with the same values
  75 + //! \throws HueException when the operator is unknown.
59 static Condition parse(const nlohmann::json& json); 76 static Condition parse(const nlohmann::json& json);
60 77
61 private: 78 private:
@@ -66,6 +83,9 @@ private: @@ -66,6 +83,9 @@ private:
66 83
67 namespace detail 84 namespace detail
68 { 85 {
  86 +//! Helper class to make creating conditions more convenient.
  87 +//! Specializations for each data type provide methods for the supported operators.
  88 +//! This allows the user to write <code>makeCondition(sensor).eq(value)</code>
69 template <typename T> 89 template <typename T>
70 class ConditionHelper 90 class ConditionHelper
71 { }; 91 { };
@@ -137,6 +157,7 @@ struct make_void @@ -137,6 +157,7 @@ struct make_void
137 { 157 {
138 typedef void type; 158 typedef void type;
139 }; 159 };
  160 +//! c++17 void_t
140 template <typename... Ts> 161 template <typename... Ts>
141 using void_t = typename make_void<Ts...>::type; 162 using void_t = typename make_void<Ts...>::type;
142 163
include/hueplusplus/Rule.h
@@ -33,6 +33,12 @@ @@ -33,6 +33,12 @@
33 namespace hueplusplus 33 namespace hueplusplus
34 { 34 {
35 35
  36 +//! \brief Rule stored in the bridge.
  37 +//!
  38 +//! Rules are used to automatically trigger Action%s when certain events happen.
  39 +//! The bridge can only support a limited number of rules, conditions and actions.
  40 +//!
  41 +//! They are deactivated if any errors occur when they are evaluated.
36 class Rule 42 class Rule
37 { 43 {
38 public: 44 public:
@@ -55,8 +61,19 @@ public: @@ -55,8 +61,19 @@ public:
55 int getId() const; 61 int getId() const;
56 62
57 //! \brief Get rule name 63 //! \brief Get rule name
  64 + //!
  65 + //! The rule name is always unique for the bridge.
58 std::string getName() const; 66 std::string getName() const;
59 67
  68 + //! \brief Set rule name.
  69 + //! \param name New name for the rule.
  70 + //! Must be unique for all rules, otherwise a number is added.
  71 + //! \throws std::system_error when system or socket operations fail
  72 + //! \throws HueException when response contained no body
  73 + //! \throws HueAPIResponseException when response contains an error
  74 + //! \throws nlohmann::json::parse_error when response could not be parsed
  75 + void setName(const std::string& name);
  76 +
60 //! \brief Get created time 77 //! \brief Get created time
61 time::AbsoluteTime getCreated() const; 78 time::AbsoluteTime getCreated() const;
62 79
@@ -68,18 +85,85 @@ public: @@ -68,18 +85,85 @@ public:
68 85
69 //! \brief Get whether rule is enabled or disabled 86 //! \brief Get whether rule is enabled or disabled
70 bool isEnabled() const; 87 bool isEnabled() const;
  88 + //! \brief Enable or disable rule.
  89 + //! \param enabled whether the rule is triggered.
  90 + //! \throws std::system_error when system or socket operations fail
  91 + //! \throws HueException when response contained no body
  92 + //! \throws HueAPIResponseException when response contains an error
  93 + //! \throws nlohmann::json::parse_error when response could not be parsed
  94 + void setEnabled(bool enabled);
71 95
72 //! \brief Get user that created or last changed the rule. 96 //! \brief Get user that created or last changed the rule.
73 std::string getOwner() const; 97 std::string getOwner() const;
74 98
  99 + //! \brief Get the conditions that have to be met
  100 + //!
  101 + //! The rule triggers the actions when all conditions are true.
  102 + //! At least one condition must exist.
75 std::vector<Condition> getConditions() const; 103 std::vector<Condition> getConditions() const;
  104 + //! \brief Get the actions that are executed
  105 + //!
  106 + //! At least one action must exist.
76 std::vector<Action> getActions() const; 107 std::vector<Action> getActions() const;
77 108
  109 + //! \brief Set conditions for the rule
  110 + //! \param conditions All conditions that need to be fulfilled. Must not be empty.
  111 + //! \throws std::system_error when system or socket operations fail
  112 + //! \throws HueException when response contained no body
  113 + //! \throws HueAPIResponseException when response contains an error
  114 + //! \throws nlohmann::json::parse_error when response could not be parsed
  115 + void setConditions(const std::vector<Condition>& conditions);
  116 + //! \brief Set actions for the rule
  117 + //! \param actions The actions that are triggered when the conditions are met.
  118 + //! Must not be empty.
  119 + void setActions(const std::vector<Action>& actions);
  120 +
  121 +private:
  122 + //! \brief Utility function to send a put request to the group.
  123 + //!
  124 + //! \param request The request to send
  125 + //! \param fileInfo FileInfo from calling function for exception details.
  126 + //! \returns The parsed reply
  127 + //! \throws std::system_error when system or socket operations fail
  128 + //! \throws HueException when response contained no body
  129 + //! \throws HueAPIResponseException when response contains an error
  130 + //! \throws nlohmann::json::parse_error when response could not be parsed
  131 + nlohmann::json sendPutRequest(const nlohmann::json& request, FileInfo fileInfo);
  132 +
78 private: 133 private:
79 int id; 134 int id;
80 APICache state; 135 APICache state;
81 }; 136 };
82 137
  138 +//! \brief Parameters for creating a new Rule.
  139 +//!
  140 +//! Can be used like a builder object with chained calls.
  141 +class CreateRule
  142 +{
  143 +public:
  144 + //! \brief Set name
  145 + //! \see Rule::setName
  146 + CreateRule& setName(const std::string& name);
  147 +
  148 + //! \brief Set status
  149 + //! \see Rule::setEnabled
  150 + CreateRule& setStatus(bool enabled);
  151 +
  152 + //! \brief Set conditions
  153 + //! \see Rule::setConditions
  154 + CreateRule& setConditions(const std::vector<Condition>& conditions);
  155 + //! \brief Set actions
  156 + //! \see Rule::setActions
  157 + CreateRule& setActions(const std::vector<Action>& actions);
  158 +
  159 + //! \brief Get request to create the rule.
  160 + //! \returns JSON request for a POST to create the new rule.
  161 + nlohmann::json getRequest() const;
  162 +
  163 +private:
  164 + nlohmann::json request;
  165 +};
  166 +
83 } // namespace hueplusplus 167 } // namespace hueplusplus
84 168
85 #endif 169 #endif
include/hueplusplus/Schedule.h
@@ -106,7 +106,7 @@ public: @@ -106,7 +106,7 @@ public:
106 //! \throws nlohmann::json::parse_error when response could not be parsed 106 //! \throws nlohmann::json::parse_error when response could not be parsed
107 void setTime(const time::TimePattern& timePattern); 107 void setTime(const time::TimePattern& timePattern);
108 //! \brief Enable or disable schedule 108 //! \brief Enable or disable schedule
109 - //! \param status Enabled or disabled 109 + //! \param enabled true to enable, false to disable.
110 //! 110 //!
111 //! Can be used to reset a timer by setting to disabled and enabled again. 111 //! Can be used to reset a timer by setting to disabled and enabled again.
112 //! \throws std::system_error when system or socket operations fail 112 //! \throws std::system_error when system or socket operations fail
src/Bridge.cpp
@@ -31,12 +31,11 @@ @@ -31,12 +31,11 @@
31 #include <stdexcept> 31 #include <stdexcept>
32 #include <thread> 32 #include <thread>
33 33
34 -#include "hueplusplus/LibConfig.h"  
35 #include "hueplusplus/HueExceptionMacro.h" 34 #include "hueplusplus/HueExceptionMacro.h"
  35 +#include "hueplusplus/LibConfig.h"
36 #include "hueplusplus/UPnP.h" 36 #include "hueplusplus/UPnP.h"
37 #include "hueplusplus/Utils.h" 37 #include "hueplusplus/Utils.h"
38 38
39 -  
40 namespace hueplusplus 39 namespace hueplusplus
41 { 40 {
42 BridgeFinder::BridgeFinder(std::shared_ptr<const IHttpHandler> handler) : http_handler(std::move(handler)) { } 41 BridgeFinder::BridgeFinder(std::shared_ptr<const IHttpHandler> handler) : http_handler(std::move(handler)) { }
@@ -56,7 +55,8 @@ std::vector&lt;BridgeFinder::BridgeIdentification&gt; BridgeFinder::FindBridges() cons @@ -56,7 +55,8 @@ std::vector&lt;BridgeFinder::BridgeIdentification&gt; BridgeFinder::FindBridges() cons
56 size_t start = p.first.find("//") + 2; 55 size_t start = p.first.find("//") + 2;
57 size_t length = p.first.find(":", start) - start; 56 size_t length = p.first.find(":", start) - start;
58 bridge.ip = p.first.substr(start, length); 57 bridge.ip = p.first.substr(start, length);
59 - try { 58 + try
  59 + {
60 std::string desc 60 std::string desc
61 = http_handler->GETString("/description.xml", "application/xml", "", bridge.ip, bridge.port); 61 = http_handler->GETString("/description.xml", "application/xml", "", bridge.ip, bridge.port);
62 std::string mac = ParseDescription(desc); 62 std::string mac = ParseDescription(desc);
@@ -66,7 +66,8 @@ std::vector&lt;BridgeFinder::BridgeIdentification&gt; BridgeFinder::FindBridges() cons @@ -66,7 +66,8 @@ std::vector&lt;BridgeFinder::BridgeIdentification&gt; BridgeFinder::FindBridges() cons
66 foundBridges.push_back(std::move(bridge)); 66 foundBridges.push_back(std::move(bridge));
67 } 67 }
68 } 68 }
69 - catch (const HueException&) { 69 + catch (const HueException&)
  70 + {
70 // No body found in response, skip this device 71 // No body found in response, skip this device
71 } 72 }
72 } 73 }
@@ -152,6 +153,7 @@ Bridge::Bridge(const std::string&amp; ip, const int port, const std::string&amp; usernam @@ -152,6 +153,7 @@ Bridge::Bridge(const std::string&amp; ip, const int port, const std::string&amp; usernam
152 scheduleList(stateCache, "schedules", refreshDuration), 153 scheduleList(stateCache, "schedules", refreshDuration),
153 sceneList(stateCache, "scenes", refreshDuration), 154 sceneList(stateCache, "scenes", refreshDuration),
154 sensorList(stateCache, "sensors", refreshDuration), 155 sensorList(stateCache, "sensors", refreshDuration),
  156 + ruleList(stateCache, "rules", refreshDuration),
155 bridgeConfig(stateCache, refreshDuration) 157 bridgeConfig(stateCache, refreshDuration)
156 { } 158 { }
157 159
@@ -288,6 +290,16 @@ const hueplusplus::SensorList&amp; Bridge::sensors() const @@ -288,6 +290,16 @@ const hueplusplus::SensorList&amp; Bridge::sensors() const
288 return sensorList; 290 return sensorList;
289 } 291 }
290 292
  293 +Bridge::RuleList& Bridge::rules()
  294 +{
  295 + return ruleList;
  296 +}
  297 +
  298 +const Bridge::RuleList& Bridge::rules() const
  299 +{
  300 + return ruleList;
  301 +}
  302 +
291 void Bridge::setHttpHandler(std::shared_ptr<const IHttpHandler> handler) 303 void Bridge::setHttpHandler(std::shared_ptr<const IHttpHandler> handler)
292 { 304 {
293 http_handler = handler; 305 http_handler = handler;
src/Rule.cpp
@@ -122,7 +122,7 @@ Condition Condition::parse(const nlohmann::json&amp; json) @@ -122,7 +122,7 @@ Condition Condition::parse(const nlohmann::json&amp; json)
122 } 122 }
123 else 123 else
124 { 124 {
125 - throw HueException("Unknown condition operator: " + opStr, CURRENT_FILE_INFO); 125 + throw HueException(CURRENT_FILE_INFO, "Unknown condition operator: " + opStr);
126 } 126 }
127 127
128 return Condition(address, op, value); 128 return Condition(address, op, value);
@@ -154,6 +154,13 @@ std::string Rule::getName() const @@ -154,6 +154,13 @@ std::string Rule::getName() const
154 return state.getValue().at("name").get<std::string>(); 154 return state.getValue().at("name").get<std::string>();
155 } 155 }
156 156
  157 +void Rule::setName(const std::string& name)
  158 +{
  159 + nlohmann::json request = {{"name", name}};
  160 + sendPutRequest(request, CURRENT_FILE_INFO);
  161 + refresh(true);
  162 +}
  163 +
157 time::AbsoluteTime Rule::getCreated() const 164 time::AbsoluteTime Rule::getCreated() const
158 { 165 {
159 return time::AbsoluteTime::parseUTC(state.getValue().at("creationtime").get<std::string>()); 166 return time::AbsoluteTime::parseUTC(state.getValue().at("creationtime").get<std::string>());
@@ -174,6 +181,11 @@ bool Rule::isEnabled() const @@ -174,6 +181,11 @@ bool Rule::isEnabled() const
174 return state.getValue().at("status").get<std::string>() == "enabled"; 181 return state.getValue().at("status").get<std::string>() == "enabled";
175 } 182 }
176 183
  184 +void Rule::setEnabled(bool enabled)
  185 +{
  186 + sendPutRequest({{"status", enabled ? "enabled" : "disabled"}}, CURRENT_FILE_INFO);
  187 +}
  188 +
177 std::string Rule::getOwner() const 189 std::string Rule::getOwner() const
178 { 190 {
179 return state.getValue().at("owner").get<std::string>(); 191 return state.getValue().at("owner").get<std::string>();
@@ -201,4 +213,70 @@ std::vector&lt;Action&gt; Rule::getActions() const @@ -201,4 +213,70 @@ std::vector&lt;Action&gt; Rule::getActions() const
201 return result; 213 return result;
202 } 214 }
203 215
  216 +void Rule::setConditions(const std::vector<Condition>& conditions)
  217 +{
  218 + nlohmann::json json;
  219 + for (const Condition& c : conditions)
  220 + {
  221 + json.push_back(c.toJson());
  222 + }
  223 +
  224 + sendPutRequest({{"conditions", json}}, CURRENT_FILE_INFO);
  225 +}
  226 +
  227 +void Rule::setActions(const std::vector<Action>& actions)
  228 +{
  229 + nlohmann::json json;
  230 + for (const Action& a : actions)
  231 + {
  232 + json.push_back(a.toJson());
  233 + }
  234 +
  235 + sendPutRequest({{"actions", json}}, CURRENT_FILE_INFO);
  236 +}
  237 +
  238 +nlohmann::json Rule::sendPutRequest(const nlohmann::json& request, FileInfo fileInfo)
  239 +{
  240 + return state.getCommandAPI().PUTRequest("/groups/" + std::to_string(id), request, std::move(fileInfo));
  241 +}
  242 +
  243 +CreateRule& CreateRule::setName(const std::string& name)
  244 +{
  245 + request["name"] = name;
  246 + return *this;
  247 +}
  248 +
  249 +CreateRule& CreateRule::setStatus(bool enabled)
  250 +{
  251 + request["status"] = enabled ? "enabled" : "disabled";
  252 + return *this;
  253 +}
  254 +
  255 +CreateRule& CreateRule::setConditions(const std::vector<Condition>& conditions)
  256 +{
  257 + nlohmann::json conditionsJson;
  258 + for (const Condition& c : conditions)
  259 + {
  260 + conditionsJson.push_back(c.toJson());
  261 + }
  262 + request["conditions"] = conditionsJson;
  263 + return *this;
  264 +}
  265 +
  266 +CreateRule& CreateRule::setActions(const std::vector<Action>& actions)
  267 +{
  268 + nlohmann::json actionsJson;
  269 + for (const Action& a : actions)
  270 + {
  271 + actionsJson.push_back(a.toJson());
  272 + }
  273 + request["actions"] = actionsJson;
  274 + return *this;
  275 +}
  276 +
  277 +nlohmann::json CreateRule::getRequest() const
  278 +{
  279 + return request;
  280 +}
  281 +
204 } // namespace hueplusplus 282 } // namespace hueplusplus
205 \ No newline at end of file 283 \ No newline at end of file