Commit c6e6f5be3293ad55240cb51088c1eaca5493e00d

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

Add helper functions for creating conditions from sensors.

include/hueplusplus/CLIPSensors.h
... ... @@ -123,6 +123,8 @@ public:
123 123 static constexpr const char* typeStr = "CLIPOpenClose";
124 124 };
125 125  
  126 +detail::ConditionHelper<bool> makeCondition(const CLIPOpenClose& sensor);
  127 +
126 128 //! \brief CLIP sensor to detect presence
127 129 class CLIPPresence : public BaseCLIP
128 130 {
... ... @@ -187,6 +189,8 @@ public:
187 189 static constexpr const char* typeStr = "CLIPHumidity";
188 190 };
189 191  
  192 +detail::ConditionHelper<int> makeCondition(const CLIPHumidity& sensor);
  193 +
190 194 //! \brief CLIP sensor for light level
191 195 class CLIPLightLevel : public BaseCLIP
192 196 {
... ... @@ -257,6 +261,8 @@ public:
257 261 static constexpr const char* typeStr = "CLIPGenericFlag";
258 262 };
259 263  
  264 +detail::ConditionHelper<bool> makeCondition(const CLIPGenericFlag& sensor);
  265 +
260 266 //! \brief CLIP sensor for a generic 3rd party status
261 267 //!
262 268 //! Can be created by POST.
... ... @@ -278,6 +284,9 @@ public:
278 284 //! \brief CLIPGenericStatus sensor type name
279 285 static constexpr const char* typeStr = "CLIPGenericStatus";
280 286 };
  287 +
  288 +detail::ConditionHelper<int> makeCondition(const CLIPGenericStatus& sensor);
  289 +
281 290 } // namespace sensors
282 291 } // namespace hueplusplus
283 292  
... ...
include/hueplusplus/Condition.h 0 → 100644
  1 +/**
  2 + \file ConditionHelper.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_CONDITION_H
  23 +#define INCLUDE_HUEPLUSPLUS_CONDITION_H
  24 +
  25 +#include "TimePattern.h"
  26 +
  27 +#include "json/json.hpp"
  28 +
  29 +namespace hueplusplus
  30 +{
  31 +
  32 +class Condition
  33 +{
  34 +public:
  35 + //! \brief Specifies which operation is used to check the condition
  36 + enum class Operator
  37 + {
  38 + eq, //!< Attribute is equal to specified value (for bool and int)
  39 + gt, //!< Attribute is greater than specified value (for int)
  40 + lt, //!< Attribute is less than specified value (for int)
  41 + dx, //!< Attribute has changed (no value given)
  42 + ddx, //!< Delayed attribute has changed (no value given)
  43 + stable, //!< Stable for a given time. Does not trigger a rule change
  44 + notStable, //!< Not stable for a given time. Does not trigger a rule change
  45 + in, //!< Time is in the given interval (triggered on start time, local time)
  46 + notIn //!< Time is not in the interval (triggered on end time, local time)
  47 + };
  48 +
  49 +public:
  50 + //! \brief Create a condition from any address on the bridge
  51 + Condition(const std::string& address, Operator op, const std::string& value);
  52 +
  53 + std::string getAddress() const;
  54 + Operator getOperator() const;
  55 + std::string getValue() const;
  56 +
  57 + nlohmann::json toJson() const;
  58 +
  59 + static Condition parse(const nlohmann::json& json);
  60 +
  61 +private:
  62 + std::string address;
  63 + Operator op;
  64 + std::string value;
  65 +};
  66 +
  67 +namespace detail
  68 +{
  69 +template <typename T>
  70 +class ConditionHelper
  71 +{ };
  72 +
  73 +//! General operators supported by all data types
  74 +class GeneralConditionHelper
  75 +{
  76 +public:
  77 + explicit GeneralConditionHelper(const std::string& address) : address(address) { }
  78 +
  79 + Condition dx() { return Condition(address, Condition::Operator::dx, ""); }
  80 + Condition ddx() { return Condition(address, Condition::Operator::ddx, ""); }
  81 + //! Docs does not say anything about format of stable value
  82 + //! \todo Change to either duration or int for seconds
  83 + Condition stable(const std::string& value) { return Condition(address, Condition::Operator::dx, value); }
  84 +
  85 +protected:
  86 + std::string address;
  87 +};
  88 +
  89 +//! Operators supported by int conditions
  90 +template <>
  91 +class ConditionHelper<int> : public GeneralConditionHelper
  92 +{
  93 +public:
  94 + using GeneralConditionHelper::GeneralConditionHelper;
  95 +
  96 + Condition eq(int value) { return create(Condition::Operator::eq, value); }
  97 + Condition gt(int value) { return create(Condition::Operator::gt, value); }
  98 + Condition lt(int value) { return create(Condition::Operator::eq, value); }
  99 +
  100 + Condition create(Condition::Operator op, int value) { return Condition(address, op, std::to_string(value)); }
  101 +};
  102 +
  103 +//! Operators supported by bool conditions
  104 +template <>
  105 +class ConditionHelper<bool> : public GeneralConditionHelper
  106 +{
  107 +public:
  108 + using GeneralConditionHelper::GeneralConditionHelper;
  109 +
  110 + Condition eq(bool value) { return create(Condition::Operator::eq, value); }
  111 +
  112 + Condition create(Condition::Operator op, bool value) { return Condition(address, op, value ? "true" : "false"); }
  113 +};
  114 +
  115 +//! Operators supported by timestamp conditions
  116 +template <>
  117 +class ConditionHelper<time::AbsoluteTime> : public GeneralConditionHelper
  118 +{
  119 +public:
  120 + using GeneralConditionHelper::GeneralConditionHelper;
  121 +
  122 + Condition in(const time::TimeInterval& interval) { return create(Condition::Operator::in, interval); }
  123 + Condition notIn(const time::TimeInterval& interval) { return create(Condition::Operator::notIn, interval); }
  124 +
  125 + Condition create(Condition::Operator op, const time::AbsoluteTime& value)
  126 + {
  127 + return Condition(address, op, value.toString());
  128 + }
  129 + Condition create(Condition::Operator op, const time::TimeInterval& interval)
  130 + {
  131 + return Condition(address, op, interval.toString());
  132 + }
  133 +};
  134 +
  135 +template <typename... Ts>
  136 +struct make_void
  137 +{
  138 + typedef void type;
  139 +};
  140 +template <typename... Ts>
  141 +using void_t = typename make_void<Ts...>::type;
  142 +
  143 +} // namespace detail
  144 +
  145 +} // namespace hueplusplus
  146 +
  147 +#endif
... ...
include/hueplusplus/Rule.h
... ... @@ -26,46 +26,13 @@
26 26  
27 27 #include "APICache.h"
28 28 #include "Action.h"
  29 +#include "Condition.h"
29 30 #include "HueCommandAPI.h"
30 31 #include "TimePattern.h"
31 32  
32 33 namespace hueplusplus
33 34 {
34 35  
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   -
63   -private:
64   - std::string address;
65   - Operator op;
66   - std::string value;
67   -};
68   -
69 36 class Rule
70 37 {
71 38 public:
... ...
include/hueplusplus/Sensor.h
... ... @@ -26,6 +26,7 @@
26 26 #include <memory>
27 27  
28 28 #include "BaseDevice.h"
  29 +#include "Condition.h"
29 30 #include "HueCommandAPI.h"
30 31 #include "TimePattern.h"
31 32  
... ... @@ -183,6 +184,11 @@ public:
183 184 //! The state can usually only be set on CLIP sensors, not on physical devices.
184 185 void setStateAttribute(const std::string& key, const nlohmann::json& value);
185 186  
  187 + //! \brief Get address of the given state attribute, used for conditions
  188 + //! \param key Key in the state object
  189 + //! \returns \c key prefixed with the path to the sensor state
  190 + std::string getStateAddress(const std::string& key) const;
  191 +
186 192 //! \brief Check if the sensor is Hue certified
187 193 bool isCertified() const;
188 194 //! \brief Check if the sensor is primary sensor of the device
... ... @@ -345,6 +351,36 @@ public:
345 351 static constexpr const char* typeStr = "Daylight";
346 352 };
347 353  
  354 +detail::ConditionHelper<bool> makeCondition(const DaylightSensor& sensor);
  355 +
  356 +template <typename SensorT, detail::void_t<decltype(std::declval<const SensorT>().getLastUpdated())>* = nullptr>
  357 +detail::ConditionHelper<time::AbsoluteTime> makeConditionLastUpdate(const SensorT& sensor)
  358 +{
  359 + return detail::ConditionHelper<time::AbsoluteTime>(
  360 + "/sensors/" + std::to_string(sensor.getId()) + "/state/lastupdated");
  361 +}
  362 +
  363 +template <typename ButtonSensor, detail::void_t<decltype(std::declval<const ButtonSensor>().getButtonEvent())>* = nullptr>
  364 +detail::ConditionHelper<int> makeCondition(const ButtonSensor& sensor)
  365 +{
  366 + return detail::ConditionHelper<int>(
  367 + "/sensors/" + std::to_string(sensor.getId()) + "/state/buttonevent");
  368 +}
  369 +
  370 +template <typename PresenceSensor, detail::void_t<decltype(std::declval<const PresenceSensor>().getPresence())>* = nullptr>
  371 +detail::ConditionHelper<bool> makeCondition(const PresenceSensor& sensor)
  372 +{
  373 + return detail::ConditionHelper<bool>(
  374 + "/sensors/" + std::to_string(sensor.getId()) + "/state/presence");
  375 +}
  376 +
  377 +template <typename TemperatureSensor, detail::void_t<decltype(std::declval<const TemperatureSensor>().getPresence())>* = nullptr>
  378 +detail::ConditionHelper<int> makeCondition(const TemperatureSensor& sensor)
  379 +{
  380 + return detail::ConditionHelper<int>(
  381 + "/sensors/" + std::to_string(sensor.getId()) + "/state/temperature");
  382 +}
  383 +
348 384 } // namespace sensors
349 385  
350 386 } // namespace hueplusplus
... ...
include/hueplusplus/ZLLSensors.h
... ... @@ -319,6 +319,10 @@ public:
319 319 //! \brief ZLLLightLevel sensor type name
320 320 static constexpr const char* typeStr = "ZLLLightLevel";
321 321 };
  322 +
  323 +detail::ConditionHelper<bool> makeConditionDark(const ZLLLightLevel& sensor);
  324 +detail::ConditionHelper<bool> makeConditionDaylight(const ZLLLightLevel& sensor);
  325 +detail::ConditionHelper<int> makeConditionLightLevel(const ZLLLightLevel& sensor);
322 326 } // namespace sensors
323 327 } // namespace hueplusplus
324 328  
... ...
src/CLIPSensors.cpp
... ... @@ -99,6 +99,12 @@ void CLIPOpenClose::setOpen(bool open)
99 99 sendPutRequest("/state", nlohmann::json {{"open", open}}, CURRENT_FILE_INFO);
100 100 }
101 101  
  102 +
  103 +detail::ConditionHelper<bool> makeCondition(const CLIPOpenClose& sensor)
  104 +{
  105 + return detail::ConditionHelper<bool>("/sensors/" + std::to_string(sensor.getId()) + "/state/open");
  106 +}
  107 +
102 108 constexpr const char* CLIPPresence::typeStr;
103 109  
104 110 bool CLIPPresence::getPresence() const
... ... @@ -132,6 +138,11 @@ void CLIPHumidity::setHumidity(int humidity)
132 138 sendPutRequest("/state", nlohmann::json {{"humidity", humidity}}, CURRENT_FILE_INFO);
133 139 }
134 140  
  141 +detail::ConditionHelper<int> makeCondition(const CLIPHumidity& sensor)
  142 +{
  143 + return detail::ConditionHelper<int>("/sensors/" + std::to_string(sensor.getId()) + "/state/humidity");
  144 +}
  145 +
135 146 constexpr const char* CLIPLightLevel::typeStr;
136 147  
137 148 int CLIPLightLevel::getDarkThreshold() const
... ... @@ -184,6 +195,11 @@ void CLIPGenericFlag::setFlag(bool flag)
184 195 sendPutRequest("/state", nlohmann::json {{"flag", flag}}, CURRENT_FILE_INFO);
185 196 }
186 197  
  198 +detail::ConditionHelper<bool> makeCondition(const CLIPGenericFlag& sensor)
  199 +{
  200 + return detail::ConditionHelper<bool>("/sensors/" + std::to_string(sensor.getId()) + "/state/flag");
  201 +}
  202 +
187 203 constexpr const char* CLIPGenericStatus::typeStr;
188 204  
189 205 int CLIPGenericStatus::getStatus() const
... ... @@ -195,5 +211,10 @@ void CLIPGenericStatus::setStatus(int status)
195 211 {
196 212 sendPutRequest("/state", nlohmann::json {{"status", status}}, CURRENT_FILE_INFO);
197 213 }
  214 +
  215 +detail::ConditionHelper<int> makeCondition(const CLIPGenericStatus& sensor)
  216 +{
  217 + return detail::ConditionHelper<int>("/sensors/" + std::to_string(sensor.getId()) + "/state/status");
  218 +}
198 219 } // namespace sensors
199 220 } // namespace hueplusplus
200 221 \ No newline at end of file
... ...
src/Rule.cpp
... ... @@ -19,6 +19,7 @@
19 19 along with hueplusplus. If not, see <http://www.gnu.org/licenses/>.
20 20 **/
21 21  
  22 +#include <hueplusplus/HueExceptionMacro.h>
22 23 #include <hueplusplus/Rule.h>
23 24  
24 25 namespace hueplusplus
... ... @@ -62,14 +63,14 @@ nlohmann::json Condition::toJson() const
62 63 case Operator::stable:
63 64 opStr = "stable";
64 65 break;
65   - case Operator::not_stable:
66   - opStr = "eq";
  66 + case Operator::notStable:
  67 + opStr = "not stable";
67 68 break;
68 69 case Operator::in:
69   - opStr = "eq";
  70 + opStr = "in";
70 71 break;
71   - case Operator::not_in:
72   - opStr = "eq";
  72 + case Operator::notIn:
  73 + opStr = "not in";
73 74 break;
74 75 }
75 76  
... ... @@ -107,17 +108,21 @@ Condition Condition::parse(const nlohmann::json&amp; json)
107 108 {
108 109 op = Operator::stable;
109 110 }
110   - else if (opStr == "not_stable")
  111 + else if (opStr == "not stable")
111 112 {
112   - op = Operator::not_stable;
  113 + op = Operator::notStable;
113 114 }
114 115 else if (opStr == "in")
115 116 {
116 117 op = Operator::in;
117 118 }
118   - else if (opStr == "not_in")
  119 + else if (opStr == "not in")
  120 + {
  121 + op = Operator::notIn;
  122 + }
  123 + else
119 124 {
120   - op = Operator::not_in;
  125 + throw HueException("Unknown condition operator: " + opStr, CURRENT_FILE_INFO);
121 126 }
122 127  
123 128 return Condition(address, op, value);
... ... @@ -185,9 +190,9 @@ std::vector&lt;Condition&gt; Rule::getConditions() const
185 190 return result;
186 191 }
187 192  
188   -std::vector<ScheduleCommand> Rule::getActions() const
  193 +std::vector<Action> Rule::getActions() const
189 194 {
190   - std::vector<ScheduleCommand> result;
  195 + std::vector<Action> result;
191 196 const nlohmann::json& actions = state.getValue().at("actions");
192 197 for (const nlohmann::json& a : actions)
193 198 {
... ...
src/Sensor.cpp
... ... @@ -203,6 +203,11 @@ void Sensor::setStateAttribute(const std::string&amp; key, const nlohmann::json&amp; val
203 203 sendPutRequest("/state", nlohmann::json {{key, value}}, CURRENT_FILE_INFO);
204 204 }
205 205  
  206 +std::string Sensor::getStateAddress(const std::string& key) const
  207 +{
  208 + return path + "/state/" + key;
  209 +}
  210 +
206 211 nlohmann::json Sensor::getConfig() const
207 212 {
208 213 return state.getValue().at("config");
... ... @@ -270,7 +275,7 @@ bool DaylightSensor::isOn() const
270 275  
271 276 void DaylightSensor::setOn(bool on)
272 277 {
273   - sendPutRequest("/config", { {"on", on} }, CURRENT_FILE_INFO);
  278 + sendPutRequest("/config", {{"on", on}}, CURRENT_FILE_INFO);
274 279 }
275 280  
276 281 bool DaylightSensor::hasBatteryState() const
... ... @@ -333,9 +338,15 @@ time::AbsoluteTime DaylightSensor::getLastUpdated() const
333 338 auto it = stateJson.find("lastupdated");
334 339 if (it == stateJson.end() || !it->is_string() || *it == "none")
335 340 {
336   - return time::AbsoluteTime(std::chrono::system_clock::time_point(std::chrono::seconds{ 0 }));
  341 + return time::AbsoluteTime(std::chrono::system_clock::time_point(std::chrono::seconds {0}));
337 342 }
338 343 return time::AbsoluteTime::parseUTC(it->get<std::string>());
339 344 }
  345 +
  346 +detail::ConditionHelper<bool> makeCondition(const DaylightSensor& sensor)
  347 +{
  348 + return detail::ConditionHelper<bool>("/sensors/" + std::to_string(sensor.getId()) + "/state/daylight");
  349 +}
  350 +
340 351 } // namespace sensors
341 352 } // namespace hueplusplus
... ...
src/ZLLSensors.cpp
... ... @@ -293,5 +293,20 @@ time::AbsoluteTime ZLLLightLevel::getLastUpdated() const
293 293 }
294 294 return time::AbsoluteTime::parseUTC(it->get<std::string>());
295 295 }
  296 +
  297 +detail::ConditionHelper<bool> makeConditionDark(const ZLLLightLevel& sensor)
  298 +{
  299 + return detail::ConditionHelper<bool>("/sensors/" + std::to_string(sensor.getId()) + "/state/dark");
  300 +}
  301 +
  302 +detail::ConditionHelper<bool> makeConditionDaylight(const ZLLLightLevel& sensor)
  303 +{
  304 + return detail::ConditionHelper<bool>("/sensors/" + std::to_string(sensor.getId()) + "/state/daylight");
  305 +}
  306 +
  307 +detail::ConditionHelper<int> makeConditionLightLevel(const ZLLLightLevel& sensor)
  308 +{
  309 + return detail::ConditionHelper<int>("/sensors/" + std::to_string(sensor.getId()) + "/state/lightlevel");
  310 +}
296 311 } // namespace sensors
297 312 } // namespace hueplusplus
298 313 \ No newline at end of file
... ...