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 40 #include "IHttpHandler.h"
41 41 #include "Light.h"
42 42 #include "ResourceList.h"
  43 +#include "Rule.h"
43 44 #include "Scene.h"
44 45 #include "Schedule.h"
45 46 #include "Sensor.h"
... ... @@ -133,6 +134,7 @@ public:
133 134 using GroupList = GroupResourceList<Group, CreateGroup>;
134 135 using ScheduleList = CreateableResourceList<ResourceList<Schedule, int>, CreateSchedule>;
135 136 using SceneList = CreateableResourceList<ResourceList<Scene, std::string>, CreateScene>;
  137 + using RuleList = CreateableResourceList<ResourceList<Rule, int>, CreateRule>;
136 138  
137 139 public:
138 140 //! \brief Constructor of Bridge class
... ... @@ -231,6 +233,11 @@ public:
231 233 //! \note Does not refresh state.
232 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 241 private:
235 242 //! \brief Function that sets the HttpHandler and updates the HueCommandAPI.
236 243 //! \param handler a HttpHandler of type \ref IHttpHandler
... ... @@ -256,6 +263,7 @@ private:
256 263 detail::MakeCopyable<ScheduleList> scheduleList;
257 264 detail::MakeCopyable<SceneList> sceneList;
258 265 detail::MakeCopyable<SensorList> sensorList;
  266 + detail::MakeCopyable<RuleList> ruleList;
259 267 detail::MakeCopyable<BridgeConfig> bridgeConfig;
260 268 };
261 269 } // namespace hueplusplus
... ...
include/hueplusplus/Condition.h
1 1 /**
2   - \file ConditionHelper.h
  2 + \file Condition.h
3 3 Copyright Notice\n
4 4 Copyright (C) 2020 Jan Rogall - developer\n
5 5  
... ... @@ -28,7 +28,12 @@
28 28  
29 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 37 class Condition
33 38 {
34 39 public:
... ... @@ -48,14 +53,26 @@ public:
48 53  
49 54 public:
50 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 59 Condition(const std::string& address, Operator op, const std::string& value);
52 60  
  61 + //! \brief Get address on the bridge
53 62 std::string getAddress() const;
  63 + //! \brief Get used operator
54 64 Operator getOperator() const;
  65 + //! \brief Get value the attribute is checked against
55 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 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 76 static Condition parse(const nlohmann::json& json);
60 77  
61 78 private:
... ... @@ -66,6 +83,9 @@ private:
66 83  
67 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 89 template <typename T>
70 90 class ConditionHelper
71 91 { };
... ... @@ -137,6 +157,7 @@ struct make_void
137 157 {
138 158 typedef void type;
139 159 };
  160 +//! c++17 void_t
140 161 template <typename... Ts>
141 162 using void_t = typename make_void<Ts...>::type;
142 163  
... ...
include/hueplusplus/Rule.h
... ... @@ -33,6 +33,12 @@
33 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 42 class Rule
37 43 {
38 44 public:
... ... @@ -55,8 +61,19 @@ public:
55 61 int getId() const;
56 62  
57 63 //! \brief Get rule name
  64 + //!
  65 + //! The rule name is always unique for the bridge.
58 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 77 //! \brief Get created time
61 78 time::AbsoluteTime getCreated() const;
62 79  
... ... @@ -68,18 +85,85 @@ public:
68 85  
69 86 //! \brief Get whether rule is enabled or disabled
70 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 96 //! \brief Get user that created or last changed the rule.
73 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 103 std::vector<Condition> getConditions() const;
  104 + //! \brief Get the actions that are executed
  105 + //!
  106 + //! At least one action must exist.
76 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 133 private:
79 134 int id;
80 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 167 } // namespace hueplusplus
84 168  
85 169 #endif
... ...
include/hueplusplus/Schedule.h
... ... @@ -106,7 +106,7 @@ public:
106 106 //! \throws nlohmann::json::parse_error when response could not be parsed
107 107 void setTime(const time::TimePattern& timePattern);
108 108 //! \brief Enable or disable schedule
109   - //! \param status Enabled or disabled
  109 + //! \param enabled true to enable, false to disable.
110 110 //!
111 111 //! Can be used to reset a timer by setting to disabled and enabled again.
112 112 //! \throws std::system_error when system or socket operations fail
... ...
src/Bridge.cpp
... ... @@ -31,12 +31,11 @@
31 31 #include <stdexcept>
32 32 #include <thread>
33 33  
34   -#include "hueplusplus/LibConfig.h"
35 34 #include "hueplusplus/HueExceptionMacro.h"
  35 +#include "hueplusplus/LibConfig.h"
36 36 #include "hueplusplus/UPnP.h"
37 37 #include "hueplusplus/Utils.h"
38 38  
39   -
40 39 namespace hueplusplus
41 40 {
42 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 55 size_t start = p.first.find("//") + 2;
57 56 size_t length = p.first.find(":", start) - start;
58 57 bridge.ip = p.first.substr(start, length);
59   - try {
  58 + try
  59 + {
60 60 std::string desc
61 61 = http_handler->GETString("/description.xml", "application/xml", "", bridge.ip, bridge.port);
62 62 std::string mac = ParseDescription(desc);
... ... @@ -66,7 +66,8 @@ std::vector&lt;BridgeFinder::BridgeIdentification&gt; BridgeFinder::FindBridges() cons
66 66 foundBridges.push_back(std::move(bridge));
67 67 }
68 68 }
69   - catch (const HueException&) {
  69 + catch (const HueException&)
  70 + {
70 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 153 scheduleList(stateCache, "schedules", refreshDuration),
153 154 sceneList(stateCache, "scenes", refreshDuration),
154 155 sensorList(stateCache, "sensors", refreshDuration),
  156 + ruleList(stateCache, "rules", refreshDuration),
155 157 bridgeConfig(stateCache, refreshDuration)
156 158 { }
157 159  
... ... @@ -288,6 +290,16 @@ const hueplusplus::SensorList&amp; Bridge::sensors() const
288 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 303 void Bridge::setHttpHandler(std::shared_ptr<const IHttpHandler> handler)
292 304 {
293 305 http_handler = handler;
... ...
src/Rule.cpp
... ... @@ -122,7 +122,7 @@ Condition Condition::parse(const nlohmann::json&amp; json)
122 122 }
123 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 128 return Condition(address, op, value);
... ... @@ -154,6 +154,13 @@ std::string Rule::getName() const
154 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 164 time::AbsoluteTime Rule::getCreated() const
158 165 {
159 166 return time::AbsoluteTime::parseUTC(state.getValue().at("creationtime").get<std::string>());
... ... @@ -174,6 +181,11 @@ bool Rule::isEnabled() const
174 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 189 std::string Rule::getOwner() const
178 190 {
179 191 return state.getValue().at("owner").get<std::string>();
... ... @@ -201,4 +213,70 @@ std::vector&lt;Action&gt; Rule::getActions() const
201 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 282 } // namespace hueplusplus
205 283 \ No newline at end of file
... ...