diff --git a/include/hueplusplus/Hue.h b/include/hueplusplus/Hue.h
index e14efea..22d9f58 100644
--- a/include/hueplusplus/Hue.h
+++ b/include/hueplusplus/Hue.h
@@ -41,6 +41,7 @@
#include "json/json.hpp"
+//! \brief Namespace for the hueplusplus library
namespace hueplusplus
{
// forward declarations
diff --git a/include/hueplusplus/TimePattern.h b/include/hueplusplus/TimePattern.h
index 61b1cc8..e0aba17 100644
--- a/include/hueplusplus/TimePattern.h
+++ b/include/hueplusplus/TimePattern.h
@@ -27,26 +27,74 @@
namespace hueplusplus
{
+//! \brief Namespace for time/date related classes and functions
namespace time
{
+//! \brief Converts a time_point to a timestamp string
+//! \param time Time to convert
+//! \returns Date and time in the format
+//! YYYY-MM-DDThh:mm:ss.
+//!
+//! Returns the time in the local time zone.
+//! \throws HueException when time could not be converted
std::string timepointToTimestamp(std::chrono::system_clock::time_point time);
+
+//! \brief Converts a timestamp to a time_point
+//! \param timestamp Timestamp from the local time zone in the format
+//! YYYY-MM-DDThh:mm:ss
+//! \returns time_point of the local system clock
+//! \throws std::invalid_argument when integer conversion fails
+//! \throws HueException when time cannot be represented as time_point
std::chrono::system_clock::time_point parseTimestamp(const std::string& timestamp);
+//! \brief Converts duration to a time string
+//! \param duration Duration or time of day to format. Must be less than 24 hours
+//! \returns Duration string in the format hh:mm:ss
+//! \throws HueException when \c duration longer than 24 hours.
std::string durationTo_hh_mm_ss(std::chrono::system_clock::duration duration);
+
+//! \brief Converts time string to a duration
+//! \param hourMinSec Time/duration in the format hh:mm:ss
+//! \returns Duration (hours, minutes and seconds) from the string
+//! \throws std::invalid_argument when integer conversion fails
std::chrono::system_clock::duration parseDuration(const std::string& hourMinSec);
+//! \brief One-time, absolute time point with possible random variation
+//!
+//! Can be either used to represent a specific date and time,
+//! or a date and time with a random variation.
class AbsoluteTime
{
using clock = std::chrono::system_clock;
public:
+ //! \brief Create absolute time point
+ //! \param baseTime Absolute time point
+ //! \param variation Random variation, optional. When not zero, the time is randomly chosen between
+ //! baseTime - variation and baseTime + variation
explicit AbsoluteTime(clock::time_point baseTime, clock::duration variation = std::chrono::seconds(0));
+ //! \brief Get base time point
+ //!
+ //! Can be used for calculation with other system_clock time_points
clock::time_point getBaseTime() const;
+
+ //! \brief Get random variation or zero
+ //!
+ //! The time can vary up to this amount in both directions.
clock::duration getRandomVariation() const;
+ //! \brief Get formatted string as expected by Hue API
+ //! \returns when variation is 0: Timestamp in the format
+ //! YYYY-MM-DDThh:mm:ss
+ //! \returns when there is variation: Timestamp in the format
+ //! YYYY-MM-DDThh:mm:ssAhh:mm:ss
+ //! with base time first, variation second
std::string toString() const;
+ //! \brief Parse AbsoluteTime from formatted string
+ //! \param s Timestamp in the same format as returned by \ref toString()
+ //! \returns AbsoluteTime with base time and variation from \c s
static AbsoluteTime parse(const std::string& s);
private:
@@ -54,57 +102,113 @@ private:
clock::duration variation;
};
+//! \brief Any number of days of the week
+//!
+//! Can be used to represent weekly repetitions only on certain days.
class Weekdays
{
public:
+ //! \brief Create with no days
Weekdays() : bitmask(0) {}
+ //! \brief Create with the day \c num
+ //! \param num Day of the week, from monday (0) to sunday (6)
explicit Weekdays(int num) : bitmask(1 << num) {}
+ //! \brief Check if no days are set
bool isNone() const;
+ //! \brief Check if all days are set
bool isAll() const;
+ //! \brief Check if Monday is contained
bool isMonday() const;
+ //! \brief Check if Tuesday is contained
bool isTuesday() const;
+ //! \brief Check if Wednesday is contained
bool isWednesday() const;
+ //! \brief Check if Thursday is contained
bool isThursday() const;
+ //! \brief Check if Friday is contained
bool isFriday() const;
+ //! \brief Check if Saturday is contained
bool isSaturday() const;
+ //! \brief Check if Sunday is contained
bool isSunday() const;
+ //! \brief Create set union with other Weekdays
+ //! \param other Second set of days to combine with
+ //! \returns A set of days containing all days of either \c this or \c other
Weekdays unionWith(Weekdays other) const;
+ //! \brief Create set union with other Weekdays
+ //! \see unionWith
Weekdays operator|(Weekdays other) const { return unionWith(other); }
+ //! \brief Create a formatted, numeric string
+ //! \returns A three digit code for the days as a bitmask
std::string toString() const;
+ //! \brief Creates an empty Weekdays
static Weekdays none();
+ //! \brief Creates set of all days
static Weekdays all();
+ //! \brief Creates Monday
static Weekdays monday();
+ //! \brief Creates Tuesday
static Weekdays tuesday();
+ //! \brief Creates Wednesday
static Weekdays wednesday();
+ //! \brief Creates Thursday
static Weekdays thursday();
+ //! \brief Creates Friday
static Weekdays friday();
+ //! \brief Creates Saturday
static Weekdays saturday();
+ //! \brief Creates Sunday
static Weekdays sunday();
+ //! \brief Parse from three digit code
+ //! \param s Bitmask of days as a string
+ //! \returns Parsed set of weekdays
static Weekdays parse(const std::string& s);
+ //! \brief Check whether all days are equal
bool operator==(const Weekdays& other) const { return bitmask == other.bitmask; }
+ //! \brief Check whether not all days are equal
bool operator!=(const Weekdays& other) const { return bitmask != other.bitmask; }
private:
int bitmask;
};
+//! \brief Time repeated weekly to daily, with possible random variation.
+//!
+//! Can be used to represent a time on one or multiple days per week.
+//! It can also have a random variation of up to 12 hours.
class RecurringTime
{
using clock = std::chrono::system_clock;
public:
+ //! \brief Create recurring time
+ //! \param daytime Time of day, duration from the start of the day.
+ //! \param days Days to repeat on, should not be Weekdays::none()
+ //! \param variation Random variation, optional. Must be less than 12 hours. When not zero, the time is randomly
+ //! chosen between daytime - variation and daytime + variation
explicit RecurringTime(clock::duration daytime, Weekdays days, clock::duration variation = std::chrono::seconds(0));
+ //! \brief Get time of day
clock::duration getDaytime() const;
+ //! \brief Get random variation
+ //!
+ //! The time can vary up to this amount in both directions.
clock::duration getRandomVariation() const;
+ //! \brief Get days on which the repetition will happen
Weekdays getWeekdays() const;
+ //! \brief Get formatted string as expected by Hue API
+ //! \returns with no variation:
+ //! Wbbb/Thh:mm:ss
+ //! \returns with variation:
+ //! Wbbb/Thh:mm:ssAhh:mm:ss,
+ //! where daytime is first and variation is second.
std::string toString() const;
private:
@@ -113,17 +217,34 @@ private:
Weekdays days;
};
+//! \brief Time interval repeated daily to weekly.
+//!
+//! Can be used to represent an interval of time on one or multiple days per week.
+//! The maximum interval length is 23 hours.
class TimeInterval
{
using clock = std::chrono::system_clock;
public:
+ //! \brief Create time interval
+ //! \param start Start time, duration from the start of the day
+ //! \param end End time, duration from the start of the day
+ //! \param days Active days, optional. Defaults to daily repetition.
TimeInterval(clock::duration start, clock::duration end, Weekdays days = Weekdays::all());
+ //! \brief Get start time of the interval
clock::duration getStartTime() const;
+ //! \brief Get end time of the interval
clock::duration getEndTime() const;
+ //! \brief Get active days
Weekdays getWeekdays() const;
+ //! \brief Get formatted string as expected by Hue API
+ //! \returns with daily repetition:
+ //! Thh:mm:ss/Thh:mm:ss,
+ //! with start time first and end time second.
+ //! \returns with repetition that is not daily:
+ //! Wbbb/Thh:mm:ss/Thh:mm:ss
std::string toString() const;
private:
@@ -132,19 +253,53 @@ private:
Weekdays days;
};
+//! \brief Timer that is started and triggers after specified delay
+//!
+//! The timer can have a random variation in the expiry time.
+//! It can be one-off, repeated a set number of times or repeated indefinitely.
class Timer
{
using clock = std::chrono::system_clock;
public:
+ // \brief Used to represent infinite repetitions
+ static constexpr int infiniteExecutions = 0;
+
+ //! \brief Create one-off timer
+ //! \param duration Expiry time of the timer, max 24 hours.
+ //! \param variation Random variation of expiry time, optional.
Timer(clock::duration duration, clock::duration variation = std::chrono::seconds(0));
+ //! \brief Create a repeated timer.
+ //! \param duration Expiry time of the timer, max 24 hours.
+ //! \param numExecutions Number of executions, 1 or higher, or \ref infiniteExecutions to always repeat.
+ //! \param variation Random variation of expiry time, optional.
Timer(clock::duration duration, int numExecutions, clock::duration variation = std::chrono::seconds(0));
+ //! \brief Returns true when the timer is executed more than once
bool isRecurring() const;
+
+ //! \brief Get number of executions
+ //! \returns Number of executions, or \ref infiniteExecutions
int getNumberOfExecutions() const;
+ //! \brief Get expiry time
clock::duration getExpiryTime() const;
+ //! \brief Get random variation of expiry time
+ //!
+ //! The expiry time can vary up to this value in both directions.
clock::duration getRandomVariation() const;
+ //! \brief Get formatted string as expected by Hue API
+ //! \returns one-off timer: PThh:mm:ss
+ //! \returns one-off timer with variation:
+ //! PThh:mm:ssAhh:mm:ss,
+ //! with expiry time first and variation second.
+ //! \returns recurring timer: R/PThh:mm:ss
+ //! \returns recurring timer with n repetitions:
+ //! Rnn/PThh:mm:ss
+ //! \returns recurring timer with random variation:
+ //! Rnn/PThh:mm:ssAhh:mm:ss
+ //! \returns infinite recurring timer with random variation:
+ //! R/PThh:mm:ssAhh:mm:ss
std::string toString() const;
private:
@@ -153,41 +308,75 @@ private:
int numExecutions;
};
+//! \brief Holds different time representations
+//!
+//! Holds either AbsoluteTime, RecurringTime, TimeInterval, Timer or an undefined state.
+//! TimePattern is used to specify the occurrance of Schedule%s.
class TimePattern
{
public:
+ //! \brief Currently active type
enum class Type
{
- undefined,
- absolute,
- recurring,
- interval,
- timer
+ undefined, //!< \brief No active type
+ absolute, //!< \brief Active type is AbsoluteTime
+ recurring, //!< \brief Active type is RecurringTime
+ interval, //!< \brief Active type is TimeInterval
+ timer //!< \brief Active type is Timer
};
+ //! \brief Create empty TimePattern
TimePattern();
+ //! \brief Destructor for union.
~TimePattern();
+ //! \brief Create TimePattern from AbsoluteTime
explicit TimePattern(const AbsoluteTime& absolute);
+ //! \brief Create TimePattern from RecurringTime
explicit TimePattern(const RecurringTime& recurring);
+ //! \brief Create TimePattern from TimeInterval
explicit TimePattern(const TimeInterval& interval);
+ //! \brief Create TimePattern from Timer
explicit TimePattern(const Timer& timer);
+ //! \brief Copy constructor for union
TimePattern(const TimePattern& other);
+ //! \brief Copy assignment for union
TimePattern& operator=(const TimePattern& other);
+ //! \brief Get currently active type
+ //! \note Only the currently active type may be accessed,
+ //! anything else is undefined behavior.
Type getType() const;
+ //! \brief Get contained absolute time
+ //! \pre getType() == Type::absolute
AbsoluteTime asAbsolute() const;
+ //! \brief Get contained recurring time
+ //! \pre getType() == Type::recurring
RecurringTime asRecurring() const;
+ //! \brief Get contained time interval
+ //! \pre getType() == Type::interval
TimeInterval asInterval() const;
+ //! \brief Get contained timer
+ //! \pre getType() == Type::timer
Timer asTimer() const;
+ //! \brief Get formatted string of the contained value as expected by Hue API
+ //! \returns Empty string when type is undefined, otherwise toString() of the active type.
+ //! \see AbsoluteTime::toString, RecurringTime::toString, TimeInterval::toString, Timer::toString
std::string toString() const;
+ //! \brief Parses TimePattern from formatted string as returned by Hue API
+ //! \param s Empty string, "none", or in one of the formats the contained types
+ //! return in their toString() method.
+ //! \returns TimePattern with the matching type that is given in \c s
+ //! \see AbsoluteTime::toString, RecurringTime::toString, TimeInterval::toString, Timer::toString
+ //! \throws HueException when the format does not match or a parsing error occurs
+ //! \throws std::invalid_argument when an integer conversion fails
static TimePattern parse(const std::string& s);
private:
diff --git a/include/hueplusplus/Utils.h b/include/hueplusplus/Utils.h
index 4c547d1..ed51703 100644
--- a/include/hueplusplus/Utils.h
+++ b/include/hueplusplus/Utils.h
@@ -27,6 +27,7 @@
namespace hueplusplus
{
+//! \brief Utility functions used in multiple places.
namespace utils
{
namespace detail
diff --git a/src/TimePattern.cpp b/src/TimePattern.cpp
index c324651..ebd8d33 100644
--- a/src/TimePattern.cpp
+++ b/src/TimePattern.cpp
@@ -28,14 +28,19 @@ namespace hueplusplus
{
namespace time
{
-
-using clock = std::chrono::system_clock;
-std::string timepointToTimestamp(clock::time_point time)
+using std::chrono::system_clock;
+// Full name needed for doxygen
+std::string timepointToTimestamp(std::chrono::system_clock::time_point time)
{
using namespace std::chrono;
- std::time_t ctime = clock::to_time_t(time);
+ std::time_t ctime = system_clock::to_time_t(time);
- std::tm localtime = *std::localtime(&ctime);
+ std::tm* pLocaltime = std::localtime(&ctime);
+ if (pLocaltime == nullptr)
+ {
+ throw HueException(CURRENT_FILE_INFO, "localtime failed");
+ }
+ std::tm localtime = *pLocaltime;
char buf[32];
std::size_t result = std::strftime(buf, sizeof(buf), "%FT%T", &localtime);
@@ -46,7 +51,7 @@ std::string timepointToTimestamp(clock::time_point time)
return std::string(buf);
}
-clock::time_point parseTimestamp(const std::string& timestamp)
+system_clock::time_point parseTimestamp(const std::string& timestamp)
{
std::tm tm {};
tm.tm_year = std::stoi(timestamp.substr(0, 4)) - 1900;
@@ -58,10 +63,15 @@ clock::time_point parseTimestamp(const std::string& timestamp)
// Auto detect daylight savings time
tm.tm_isdst = -1;
std::time_t ctime = std::mktime(&tm);
- return clock::from_time_t(ctime);
+ if (ctime == -1)
+ {
+ throw HueException(CURRENT_FILE_INFO, "mktime failed");
+ }
+ return system_clock::from_time_t(ctime);
}
-std::string durationTo_hh_mm_ss(clock::duration duration)
+// Full name needed for doxygen
+std::string durationTo_hh_mm_ss(std::chrono::system_clock::duration duration)
{
using namespace std::chrono;
if (duration > hours(24))
@@ -79,7 +89,7 @@ std::string durationTo_hh_mm_ss(clock::duration duration)
return std::string(result);
}
-clock::duration parseDuration(const std::string& s)
+system_clock::duration parseDuration(const std::string& s)
{
using namespace std::chrono;
const hours hour(std::stoi(s.substr(0, 2)));
@@ -91,11 +101,11 @@ clock::duration parseDuration(const std::string& s)
AbsoluteTime::AbsoluteTime(clock::time_point baseTime, clock::duration variation) : base(baseTime), variation(variation)
{}
-clock::time_point AbsoluteTime::getBaseTime() const
+system_clock::time_point AbsoluteTime::getBaseTime() const
{
return base;
}
-clock::duration AbsoluteTime::getRandomVariation() const
+system_clock::duration AbsoluteTime::getRandomVariation() const
{
return variation;
}
@@ -243,12 +253,12 @@ RecurringTime::RecurringTime(clock::duration daytime, Weekdays days, clock::dura
: time(daytime), days(days), variation(variation)
{}
-clock::duration RecurringTime::getDaytime() const
+system_clock::duration RecurringTime::getDaytime() const
{
return time;
}
-clock::duration RecurringTime::getRandomVariation() const
+system_clock::duration RecurringTime::getRandomVariation() const
{
return variation;
}
@@ -276,12 +286,12 @@ TimeInterval::TimeInterval(clock::duration start, clock::duration end, Weekdays
: start(start), end(end), days(days)
{}
-clock::duration TimeInterval::getStartTime() const
+system_clock::duration TimeInterval::getStartTime() const
{
return start;
}
-clock::duration TimeInterval::getEndTime() const
+system_clock::duration TimeInterval::getEndTime() const
{
return end;
}
@@ -326,12 +336,12 @@ int Timer::getNumberOfExecutions() const
return numExecutions;
}
-clock::duration Timer::getExpiryTime() const
+system_clock::duration Timer::getExpiryTime() const
{
return expires;
}
-clock::duration Timer::getRandomVariation() const
+system_clock::duration Timer::getRandomVariation() const
{
return variation;
}
@@ -342,7 +352,7 @@ std::string Timer::toString() const
if (numExecutions != 1)
{
result.push_back('R');
- if (numExecutions != 0)
+ if (numExecutions != infiniteExecutions)
{
std::string s = std::to_string(numExecutions);
// Pad to two digits
@@ -495,8 +505,8 @@ TimePattern TimePattern::parse(const std::string& s)
}
std::size_t start = s.find('T') + 1;
std::size_t randomStart = s.find('A');
- clock::duration expires = parseDuration(s.substr(start, randomStart - start));
- clock::duration variance = std::chrono::seconds(0);
+ system_clock::duration expires = parseDuration(s.substr(start, randomStart - start));
+ system_clock::duration variance = std::chrono::seconds(0);
if (randomStart != std::string::npos)
{
variance = parseDuration(s.substr(randomStart + 1));
@@ -507,8 +517,8 @@ TimePattern TimePattern::parse(const std::string& s)
{
// Recurring time
Weekdays days = Weekdays::parse(s.substr(1, 3));
- clock::duration time = parseDuration(s.substr(6));
- clock::duration variation {0};
+ system_clock::duration time = parseDuration(s.substr(6));
+ system_clock::duration variation {0};
if (s.size() > 14)
{
variation = parseDuration(s.substr(15));
@@ -526,8 +536,8 @@ TimePattern TimePattern::parse(const std::string& s)
// Time interval
std::size_t start = s.find('T') + 1;
std::size_t end = s.find('/', start);
- clock::duration startTime = parseDuration(s.substr(start, end - start));
- clock::duration endTime = parseDuration(s.substr(end + 2));
+ system_clock::duration startTime = parseDuration(s.substr(start, end - start));
+ system_clock::duration endTime = parseDuration(s.substr(end + 2));
return TimePattern(TimeInterval(startTime, endTime, days));
}
throw HueException(CURRENT_FILE_INFO, "Unable to parse time string: " + s);
diff --git a/test/test_TimePattern.cpp b/test/test_TimePattern.cpp
index 9b50dcf..d60e113 100644
--- a/test/test_TimePattern.cpp
+++ b/test/test_TimePattern.cpp
@@ -299,7 +299,7 @@ TEST(Timer, toString)
EXPECT_EQ("PT00:01:20A01:00:00", timer.toString());
}
{
- const Timer timer(1min + 20s, 0);
+ const Timer timer(1min + 20s, Timer::infiniteExecutions);
EXPECT_EQ("R/PT00:01:20", timer.toString());
}
{
@@ -315,7 +315,7 @@ TEST(Timer, toString)
EXPECT_EQ("R05/PT00:01:20A01:00:00", timer.toString());
}
{
- const Timer timer(1min + 20s, 0, 1h);
+ const Timer timer(1min + 20s, Timer::infiniteExecutions, 1h);
EXPECT_EQ("R/PT00:01:20A01:00:00", timer.toString());
}
}
@@ -449,7 +449,7 @@ TEST(TimePattern, Timer)
EXPECT_EQ(expected.getNumberOfExecutions(), pattern.asTimer().getNumberOfExecutions());
}
{
- const Timer expected(1h + 30min + 20s, 0);
+ const Timer expected(1h + 30min + 20s, Timer::infiniteExecutions);
const TimePattern pattern = TimePattern::parse("R/PT01:30:20");
ASSERT_EQ(TimePattern::Type::timer, pattern.getType());
EXPECT_EQ(expected.getExpiryTime(), pattern.asTimer().getExpiryTime());
@@ -457,7 +457,7 @@ TEST(TimePattern, Timer)
EXPECT_EQ(expected.getNumberOfExecutions(), pattern.asTimer().getNumberOfExecutions());
}
{
- const Timer expected(1h + 30min + 20s, 0, 20s);
+ const Timer expected(1h + 30min + 20s, Timer::infiniteExecutions, 20s);
const TimePattern pattern = TimePattern::parse("R/PT01:30:20A00:00:20");
ASSERT_EQ(TimePattern::Type::timer, pattern.getType());
EXPECT_EQ(expected.getExpiryTime(), pattern.asTimer().getExpiryTime());