Publish LumaOps source

This commit is contained in:
LumaOps release export
2026-09-03 01:18:36 +02:00
commit 7f1c0e5f71
2363 changed files with 501543 additions and 0 deletions
+2088
View File
File diff suppressed because it is too large Load Diff
+497
View File
@@ -0,0 +1,497 @@
/*-----------------------------------------------------*\
| ColorWheel.cpp |
| |
| Color wheel selector widget for Qt |
| |
| Original: https://github.com/liuyanghejerry/Qt-Plus |
| |
| Modified by Adam Honse (calcprogrammer1@gmail.com) |
\*-----------------------------------------------------*/
#include "ColorWheel.h"
#include <QPainter>
#include <QResizeEvent>
#include <QStyleOption>
#include <QtCore/qmath.h>
#include <QDebug>
#include <QPainterPath>
ColorWheel::ColorWheel(QWidget *parent) :
QWidget(parent),
initSize(128,128),
mouseDown(false),
margin(0),
wheelWidth(10),
current(Qt::red),
inWheel(false),
inSquare(false)
{
current = current.toHsv();
}
QColor ColorWheel::color()
{
return current;
}
void ColorWheel::setColor(const QColor &color)
{
if(color == current) return;
if(color.hue() != current.hue())
{
hueChanged(color.hue());
}
if((color.saturation() != current.saturation()) || (color.value() != current.value()))
{
svChanged(color);
}
update();
emit colorChanged(color);
}
QColor ColorWheel::posColor(const QPoint &point)
{
/*-----------------------------------------------------*\
| Subtract offsets from point value |
\*-----------------------------------------------------*/
int point_x = point.x() - x_offset;
int point_y = point.y() - y_offset;
/*-----------------------------------------------------*\
| If within wheel region, update hue from point |
| position |
\*-----------------------------------------------------*/
if(inWheel)
{
qreal hue = 0;
int r = qMin(width() - x_offset, height() - y_offset) / 2;
if( point_x > r )
{
if(point_y < r )
{
//1
hue = 90 - (qAtan2( (point_x - r) , (r - point_y) ) / 3.14 / 2 * 360);
}
else
{
//4
hue = 270 + (qAtan2( (point_x - r) , (point_y - r ) ) / 3.14 / 2 * 360);
}
}
else
{
if(point_y < r )
{
//2
hue = 90 + (qAtan2( (r - point_x) , (r - point_y) ) / 3.14 / 2 * 360);
}
else
{
//3
hue = 270 - (qAtan2( (r - point_x) , (point_y - r )) / 3.14 / 2 * 360);
}
}
/*-----------------------------------------------------*\
| Restrict hue to range 0-359 |
\*-----------------------------------------------------*/
hue = (hue > 359) ? 359 : hue;
hue = hue < 0 ? 0 : hue;
return QColor::fromHsv(hue,
current.saturation(),
current.value());
}
/*-----------------------------------------------------*\
| If within square region, update saturation and value |
| from point position |
\*-----------------------------------------------------*/
if(inSquare)
{
// region of the widget
int w = qMin(width() - x_offset, height() - y_offset);
// radius of outer circle
qreal r = w/2 - margin;
// radius of inner circle
qreal ir = r - wheelWidth;
// left corner of square
qreal m = w/2.0 - ir/qSqrt(2);
QPoint p = point - QPoint(x_offset, y_offset) - QPoint(m, m);
qreal SquareWidth = 2*ir/qSqrt(2);
qreal saturation = qBound(0.0, p.x()/SquareWidth, 1.0);
qreal value = qBound(0.0, p.y()/SquareWidth, 1.0);
return QColor::fromHsvF( current.hueF(),
saturation,
value);
}
return QColor();
}
QSize ColorWheel::sizeHint () const
{
return QSize(height(),height());
}
QSize ColorWheel::minimumSizeHint () const
{
return initSize;
}
void ColorWheel::mousePressEvent(QMouseEvent *event)
{
/*-----------------------------------------------------*\
| Update last position |
\*-----------------------------------------------------*/
lastPos = event->pos();
/*-----------------------------------------------------*\
| If mouse is within wheel region, process wheel (hue) |
\*-----------------------------------------------------*/
if(wheelRegion.contains(lastPos))
{
inWheel = true;
inSquare = false;
QColor color = posColor(lastPos);
hueChanged(color.hue());
}
/*-----------------------------------------------------*\
| If mouse is within square region, process square |
| (saturation and value) |
\*-----------------------------------------------------*/
else if(squareRegion.contains(lastPos))
{
inWheel = false;
inSquare = true;
QColor color = posColor(lastPos);
svChanged(color);
}
/*-----------------------------------------------------*\
| Set the mouse down flag if the click started inside a |
| selectable region |
\*-----------------------------------------------------*/
mouseDown = inWheel || inSquare;
}
void ColorWheel::mouseMoveEvent(QMouseEvent *event)
{
/*-----------------------------------------------------*\
| Update last position |
\*-----------------------------------------------------*/
lastPos = event->pos();
/*-----------------------------------------------------*\
| Don't process if mouse button is not down |
\*-----------------------------------------------------*/
if(!mouseDown)
{
return;
}
/*-----------------------------------------------------*\
| If dragging started in the wheel, continue processing |
| hue from the cursor angle even outside the wheel |
\*-----------------------------------------------------*/
if(inWheel)
{
QColor color = posColor(lastPos);
hueChanged(color.hue());
}
/*-----------------------------------------------------*\
| If dragging started in the square, continue processing|
| saturation and value with clamped coordinates |
\*-----------------------------------------------------*/
else if(inSquare)
{
QColor color = posColor(lastPos);
svChanged(color);
}
}
void ColorWheel::mouseReleaseEvent(QMouseEvent *)
{
/*-----------------------------------------------------*\
| Clear mouse down and in-region flags |
\*-----------------------------------------------------*/
mouseDown = false;
inWheel = false;
inSquare = false;
}
void ColorWheel::resizeEvent(QResizeEvent *event)
{
unsigned int size = 0;
if(event->size().width() < event->size().height())
{
size = event->size().width();
}
else
{
size = event->size().height();
}
wheelWidth = 0.1 * size;
wheel = QPixmap(event->size());
wheel.fill(Qt::transparent);
drawWheelImage(event->size());
drawSquareImage(current.hue());
update();
}
void ColorWheel::paintEvent(QPaintEvent *)
{
QPainter painter(this);
QStyleOption opt;
opt.initFrom(this);
composeWheel();
painter.drawPixmap(0,0,wheel);
style()->drawPrimitive(QStyle::PE_Widget, &opt, &painter, this);
}
void ColorWheel::drawWheelImage(const QSize &newSize)
{
/*-----------------------------------------------------*\
| Create image canvas |
\*-----------------------------------------------------*/
wheelImage = QImage(newSize, QImage::Format_ARGB32_Premultiplied);
/*-----------------------------------------------------*\
| Paint the background |
\*-----------------------------------------------------*/
wheelImage.fill(Qt::transparent);
/*-----------------------------------------------------*\
| Create rainbow gradient for wheel |
\*-----------------------------------------------------*/
QConicalGradient conicalGradient(0, 0, 0);
conicalGradient.setColorAt(0.0, Qt::red);
conicalGradient.setColorAt(60.0 / 360.0, Qt::yellow);
conicalGradient.setColorAt(120.0 / 360.0, Qt::green);
conicalGradient.setColorAt(180.0 / 360.0, Qt::cyan);
conicalGradient.setColorAt(240.0 / 360.0, Qt::blue);
conicalGradient.setColorAt(300.0 / 360.0, Qt::magenta);
conicalGradient.setColorAt(1.0, Qt::red);
/*-----------------------------------------------------*\
| Set up painter with antialiasing |
\*-----------------------------------------------------*/
QPainter painter(&wheelImage);
painter.setRenderHint(QPainter::Antialiasing);
/*-----------------------------------------------------*\
| Paint the wheel |
\*-----------------------------------------------------*/
int size = qMin(newSize.width(), newSize.height());
x_offset = (newSize.width() - size) / 2;
y_offset = (newSize.height() - size) / 2;
int r = size;
QPainterPath painterpath;
painterpath.addEllipse(QPoint(0,0),r/2-margin,r/2-margin);
painterpath.addEllipse(QPoint(0,0),r/2-margin-wheelWidth,r/2-margin-wheelWidth);
painter.translate(x_offset + (size / 2), y_offset + (size / 2));
QBrush brush(conicalGradient);
painter.setPen(Qt::NoPen);
painter.setBrush(brush);
painter.drawPath(painterpath);
/*-----------------------------------------------------*\
| Calculate wheel region and subtract out the inner |
| region |
\*-----------------------------------------------------*/
wheelRegion = QRegion(r/2, r/2, r-2*margin, r-2*margin, QRegion::Ellipse);
wheelRegion.translate(x_offset - (r-2*margin)/2, y_offset - (r-2*margin)/2);
int tmp = 2*(margin+wheelWidth);
QRegion subRe( r/2, r/2, r-tmp, r-tmp, QRegion::Ellipse );
subRe.translate( x_offset - (r-tmp)/2, y_offset - (r-tmp)/2);
wheelRegion -= subRe;
CleanWheel = QPixmap().fromImage(wheelImage);
}
void ColorWheel::drawSquareImage(const int &hue)
{
// QPainter painter(&squarePixmap);
// painter.setRenderHint(QPainter::Antialiasing);
/*-----------------------------------------------------*\
| Calculate dimensions |
\*-----------------------------------------------------*/
int w = qMin(width(), height());
// radius of outer circle
qreal r = w/2-margin;
// radius of inner circle
qreal ir = r-wheelWidth;
// left corner of square
qreal m = w/2.0-ir/qSqrt(2);
/*-----------------------------------------------------*\
| Create image canvas |
\*-----------------------------------------------------*/
QImage square(255,255, QImage::Format_ARGB32_Premultiplied);
/*-----------------------------------------------------*\
| Paint the square. X axis is saturation and Y axis is |
| value |
\*-----------------------------------------------------*/
QColor color;
QRgb qrgb;
for(int x = 0; x < 255; x++)
{
for(int y = 0; y < 255; y++)
{
color = QColor::fromHsv(hue, x, y);
qrgb = qRgb(color.red(),color.green(),color.blue());
square.setPixel(x, y, qrgb);
}
}
/*-----------------------------------------------------*\
| Copy the fixed-size square image on to the scaled |
| canvas |
\*-----------------------------------------------------*/
qreal SquareWidth = 2*ir/qSqrt(2);
squareImage = square.scaled(SquareWidth, SquareWidth);
/*-----------------------------------------------------*\
| Calculate square region |
\*-----------------------------------------------------*/
squareRegion = QRegion(x_offset + m, y_offset + m, SquareWidth, SquareWidth);
CleanSquare = squareImage;
}
void ColorWheel::drawIndicator(const int &hue)
{
QPainter painter(&wheel);
painter.setRenderHint(QPainter::Antialiasing);
if(hue > 20 && hue < 200)
{
painter.setPen(Qt::black);
}
else
{
painter.setPen(Qt::white);
}
painter.setBrush(Qt::NoBrush);
QPen pen = painter.pen();
pen.setWidth(3);
painter.setPen(pen);
qreal r = qMin(height(), width()) / 2.0;
painter.translate(x_offset + r, y_offset + r);
painter.rotate( -hue );
r = qMin(height(), width()) / 2.0 - margin - wheelWidth/2;
painter.drawEllipse(QPointF(r,0.0),5,5);
}
void ColorWheel::drawPicker(const QColor &color)
{
QPainter painter(&wheel);
painter.setRenderHint(QPainter::Antialiasing);
QPen pen;
// region of the widget
int w = qMin(width(), height());
// radius of outer circle
qreal r = w/2-margin;
// radius of inner circle
qreal ir = r-wheelWidth;
// left corner of square
qreal m = w/2.0-ir/qSqrt(2);
painter.translate(x_offset + m-5, y_offset + m-5);
qreal SquareWidth = 2*ir/qSqrt(2);
qreal S = color.saturationF()*SquareWidth;
qreal V = color.valueF()*SquareWidth;
if(color.saturation() > 30 ||color.value() < 50)
{
pen.setColor(Qt::white);
}
pen.setWidth(3);
painter.setPen(pen);
painter.drawEllipse(S,V,10,10);
}
void ColorWheel::composeWheel()
{
wheel = CleanWheel;
squareImage = CleanSquare;
QPainter composePainter(&wheel);
composePainter.drawImage(0, 0, wheelImage);
composePainter.drawImage(squareRegion.boundingRect().topLeft(), squareImage);
composePainter.end();
drawIndicator(current.hue());
drawPicker(current);
}
void ColorWheel::hueChanged(const int &hue)
{
if((hue < 0) || (hue > 359))
{
return;
}
int s = current.saturation();
int v = current.value();
current.setHsv(hue, s, v);
drawSquareImage(hue);
if(!isVisible())
{
return;
}
repaint();
emit colorChanged(current);
}
void ColorWheel::svChanged(const QColor &newcolor)
{
int hue = current.hue();
current.setHsv
(
hue,
newcolor.saturation(),
newcolor.value()
);
if(!isVisible())
{
return;
}
repaint();
emit colorChanged(current);
}
+59
View File
@@ -0,0 +1,59 @@
#ifndef COLORWHEEL_H
#define COLORWHEEL_H
#include <QWidget>
class ColorWheel : public QWidget
{
Q_OBJECT
public:
explicit ColorWheel(QWidget *parent = 0);
virtual QSize sizeHint () const;
virtual QSize minimumSizeHint () const;
QColor color();
signals:
void colorChanged(const QColor color);
public slots:
void setColor(const QColor &color);
protected:
void mousePressEvent(QMouseEvent *event);
void mouseMoveEvent(QMouseEvent *event);
void mouseReleaseEvent(QMouseEvent *);
void resizeEvent(QResizeEvent *event);
void paintEvent(QPaintEvent *);
private:
QSize initSize;
QImage wheelImage;
QImage squareImage;
QPixmap wheel;
bool mouseDown;
QPoint lastPos;
int margin;
int wheelWidth;
QRegion wheelRegion;
QRegion squareRegion;
QColor current;
bool inWheel;
bool inSquare;
int x_offset;
int y_offset;
QPixmap CleanWheel;
QImage CleanSquare;
QColor posColor(const QPoint &point);
void drawWheelImage(const QSize &newSize);
void drawIndicator(const int &hue);
void drawPicker(const QColor &color);
void drawSquareImage(const int &hue);
void composeWheel();
private slots:
void hueChanged(const int &hue);
void svChanged(const QColor &newcolor);
};
#endif // COLORWHEEL_H
+612
View File
@@ -0,0 +1,612 @@
#ifdef _WIN32
#define _WIN32_LEAN_AND_MEAN
#include <windows.h>
#elif __linux__
#include <dlfcn.h>
#endif
#include "nvapi.h"
#include <string.h>
typedef void * (*nvapi_QueryInterface_t)(int);
// Constructors for NvAPI structures that just zero the memory and set the right version
NV_DELTA_ENTRY::NV_DELTA_ENTRY()
{
memset((void*)this, 0, sizeof *this);
}
NV_GPU_PSTATES20_V2::NV_GPU_PSTATES20_V2()
{
memset((void*)this, 0, sizeof *this);
version = NV_STRUCT_VERSION(NV_GPU_PSTATES20_V2, 2);
}
NV_CLOCK_FREQUENCIES_V2::NV_CLOCK_FREQUENCIES_V2()
{
memset((void*)this, 0, sizeof *this);
version = NV_STRUCT_VERSION(NV_CLOCK_FREQUENCIES_V2, 2);
}
NV_GPU_PERFORMANCE_TABLE_V1::NV_GPU_PERFORMANCE_TABLE_V1()
{
memset((void*)this, 0, sizeof *this);
version = NV_STRUCT_VERSION(NV_GPU_PERFORMANCE_TABLE_V1, 1);
}
NV_DYNAMIC_PSTATES_V1::NV_DYNAMIC_PSTATES_V1()
{
memset((void*)this, 0, sizeof *this);
version = NV_STRUCT_VERSION(NV_DYNAMIC_PSTATES_V1, 1);
}
NV_GPU_POWER_POLICIES_INFO_V1::NV_GPU_POWER_POLICIES_INFO_V1()
{
memset((void*)this, 0, sizeof *this);
version = NV_STRUCT_VERSION(NV_GPU_POWER_POLICIES_INFO_V1, 1);
}
NV_GPU_POWER_POLICIES_STATUS_V1::NV_GPU_POWER_POLICIES_STATUS_V1()
{
memset((void*)this, 0, sizeof *this);
version = NV_STRUCT_VERSION(NV_GPU_POWER_POLICIES_STATUS_V1, 1);
}
NV_GPU_VOLTAGE_DOMAINS_STATUS_V1::NV_GPU_VOLTAGE_DOMAINS_STATUS_V1()
{
memset((void*)this, 0, sizeof *this);
version = NV_STRUCT_VERSION(NV_GPU_VOLTAGE_DOMAINS_STATUS_V1, 1);
}
NV_GPU_THERMAL_SETTINGS_V2::NV_GPU_THERMAL_SETTINGS_V2()
{
memset((void*)this, 0, sizeof *this);
version = NV_STRUCT_VERSION(NV_GPU_THERMAL_SETTINGS_V2, 2);
}
NV_GPU_THERMAL_POLICIES_INFO_V2::NV_GPU_THERMAL_POLICIES_INFO_V2()
{
memset((void*)this, 0, sizeof *this);
version = NV_STRUCT_VERSION(NV_GPU_THERMAL_POLICIES_INFO_V2, 2);
}
NV_GPU_THERMAL_POLICIES_STATUS_V2::NV_GPU_THERMAL_POLICIES_STATUS_V2()
{
memset((void*)this, 0, sizeof *this);
version = NV_STRUCT_VERSION(NV_GPU_THERMAL_POLICIES_STATUS_V2, 2);
}
NV_GPU_COOLER_SETTINGS_V2::NV_GPU_COOLER_SETTINGS_V2()
{
memset((void*)this, 0, sizeof *this);
version = NV_STRUCT_VERSION(NV_GPU_COOLER_SETTINGS_V2, 2);
}
NV_GPU_COOLER_LEVELS_V1::NV_GPU_COOLER_LEVELS_V1()
{
memset((void*)this, 0, sizeof *this);
version = NV_STRUCT_VERSION(NV_GPU_COOLER_LEVELS_V1, 1);
}
NV_MEMORY_INFO_V2::NV_MEMORY_INFO_V2()
{
memset((void*)this, 0, sizeof *this);
version = NV_STRUCT_VERSION(NV_MEMORY_INFO_V2, 2);
}
NV_DISPLAY_DRIVER_VERSION_V1::NV_DISPLAY_DRIVER_VERSION_V1()
{
memset((void*)this, 0, sizeof *this);
version = NV_STRUCT_VERSION(NV_DISPLAY_DRIVER_VERSION_V1, 1);
}
NV_I2C_INFO_V3::NV_I2C_INFO_V3()
{
memset((void*)this, 0, sizeof * this);
version = NV_STRUCT_VERSION(NV_I2C_INFO_V3, 3);
}
// Interface: 0150E828
static NV_STATUS (*pNvAPI_Initialize)();
// Interface: D22BDD7E
static NV_STATUS (*pNvAPI_Unload)();
// Interface: 9ABDD40D
static NV_STATUS (*pNvAPI_EnumDisplayHandle)(
NV_S32 this_enum,
NV_DISPLAY_HANDLE *display_handle);
// Interface: E5AC921F
static NV_STATUS (*pNvAPI_EnumPhysicalGPUs)(
NV_PHYSICAL_GPU_HANDLE *physical_gpu_handles,
NV_S32 *gpu_count);
// Interface: F951A4D1
static NV_STATUS (*pNvAPI_GetDisplayDriverVersion)(
NV_DISPLAY_HANDLE display_handle,
NV_DISPLAY_DRIVER_VERSION_V1 *display_driver_version);
// Interface: 01053FA5
static NV_STATUS (*pNvAPI_GetInterfaceVersionString)(
NV_SHORT_STRING version);
// Interface: 34EF9506
static NV_STATUS (*pNvAPI_GetPhysicalGPUsFromDisplay)(
NV_DISPLAY_HANDLE display_handle,
NV_PHYSICAL_GPU_HANDLE *gpu_handles,
NV_U32 *gpu_count);
// Interface: 774AA982
static NV_STATUS (*pNvAPI_GetMemoryInfo)(
NV_DISPLAY_HANDLE display_handle,
NV_MEMORY_INFO_V2 *memory_info);
// Interface: 0CEEE8E9F
static NV_STATUS (*pNvAPI_GPU_GetFullName)(
NV_PHYSICAL_GPU_HANDLE physical_gpu_handle,
NV_SHORT_STRING name);
// Interface: 6FF81213
static NV_STATUS (*pNvAPI_GPU_GetPStates20)(
NV_PHYSICAL_GPU_HANDLE physical_gpu_handle,
NV_GPU_PSTATES20_V2 *pstates);
// Interface: 0F4DAE6B
static NV_STATUS (*pNvAPI_GPU_SetPStates20)(
NV_PHYSICAL_GPU_HANDLE physical_gpu_handle,
NV_GPU_PSTATES20_V2 *pstates);
// Interface: DCB616C3
static NV_STATUS (*pNvAPI_GPU_GetAllClockFrequencies)(
NV_PHYSICAL_GPU_HANDLE physical_gpu_handle,
NV_CLOCK_FREQUENCIES_V2 *frequencies);
// Interface: 60DED2ED
static NV_STATUS (*pNvAPI_GPU_GetDynamicPStates)(
NV_PHYSICAL_GPU_HANDLE physical_gpu_handle,
NV_DYNAMIC_PSTATES_V1 *dynamic_pstates);
// Interface: 34206D86
static NV_STATUS (*pNvAPI_GPU_GetPowerPoliciesInfo)(
NV_PHYSICAL_GPU_HANDLE physical_gpu_handle,
NV_GPU_POWER_POLICIES_INFO_V1 *policies_info);
// Interface: 70916171
static NV_STATUS (*pNvAPI_GPU_GetPowerPoliciesStatus)(
NV_PHYSICAL_GPU_HANDLE physical_gpu_handle,
NV_GPU_POWER_POLICIES_STATUS_V1 *policies_status);
// Interface: 0C16C7E2C
static NV_STATUS (*pNvAPI_GPU_GetVoltageDomainStatus)(
NV_PHYSICAL_GPU_HANDLE physical_gpu_handle,
NV_GPU_VOLTAGE_DOMAINS_STATUS_V1 *voltage_domains_status);
// Interface: 0E3640A56
static NV_STATUS (*pNvAPI_GPU_GetThermalSettings)(
NV_PHYSICAL_GPU_HANDLE physical_gpu_handle,
NV_THERMAL_TARGET sensor_index,
NV_GPU_THERMAL_SETTINGS_V2 *thermal_settings);
// Interface: 014B83A5F
static NV_STATUS (*pNvAPI_GPU_GetSerialNumber)(
NV_PHYSICAL_GPU_HANDLE physical_gpu_handle,
NV_SHORT_STRING serial_number);
// Interface: 0AD95F5ED
static NV_STATUS (*pNvAPI_GPU_SetPowerPoliciesStatus)(
NV_PHYSICAL_GPU_HANDLE physical_gpu_handle,
NV_GPU_POWER_POLICIES_STATUS_V1* policies_status);
// Interface: 00D258BB5
static NV_STATUS (*pNvAPI_GPU_GetThermalPoliciesInfo)(
NV_PHYSICAL_GPU_HANDLE physical_gpu_handle,
NV_GPU_THERMAL_POLICIES_INFO_V2* thermal_info);
// Interface: 0E9C425A1
static NV_STATUS (*pNvAPI_GPU_GetThermalPoliciesStatus)(
NV_PHYSICAL_GPU_HANDLE physical_gpu_handle,
NV_GPU_THERMAL_POLICIES_STATUS_V2* thermal_status);
// Interface: 034C0B13D
static NV_STATUS (*pNvAPI_GPU_SetThermalPoliciesStatus)(
NV_PHYSICAL_GPU_HANDLE physical_gpu_handle,
NV_GPU_THERMAL_POLICIES_STATUS_V2* thermal_status);
// Interface: DA141340
static NV_STATUS (*pNvAPI_GPU_GetCoolerSettings)(
NV_PHYSICAL_GPU_HANDLE physical_gpu_handle,
NV_S32 cooler_index,
NV_GPU_COOLER_SETTINGS_V2 *cooler_settings);
// Interface: 891FA0AE
static NV_STATUS (*pNvAPI_GPU_SetCoolerLevels)(
NV_PHYSICAL_GPU_HANDLE physical_gpu_handle,
NV_S32 cooler_index,
NV_GPU_COOLER_LEVELS_V1 *cooler_levels);
// Interface: 2DDFB66E
static NV_STATUS (*pNvAPI_GPU_GetPCIIdentifiers)(
NV_PHYSICAL_GPU_HANDLE physical_gpu_handle,
NV_U32 *device_id,
NV_U32 *sub_system_id,
NV_U32 *revision_id,
NV_U32 *ext_device_id);
// Interface: 283AC65A
static NV_STATUS (*pNvAPI_I2CWriteEx)(
NV_PHYSICAL_GPU_HANDLE physical_gpu_handle,
NV_I2C_INFO_V3* i2c_info,
NV_U32 *unknown);
// Interface: 4D7B0709
static NV_STATUS(*pNvAPI_I2CReadEx)(
NV_PHYSICAL_GPU_HANDLE physical_gpu_handle,
NV_I2C_INFO_V3* i2c_info,
NV_U32 *unknown);
// Interface: 3DBF5764
static NV_STATUS(*pNvAPI_GPU_ClientIllumZonesGetControl)(
NV_PHYSICAL_GPU_HANDLE physical_gpu_handle,
NV_GPU_CLIENT_ILLUM_ZONE_CONTROL_PARAMS* pIllumZonesControl);
// Interface: 197D065E
static NV_STATUS(*pNvAPI_GPU_ClientIllumZonesSetControl)(
NV_PHYSICAL_GPU_HANDLE physical_gpu_handle,
NV_GPU_CLIENT_ILLUM_ZONE_CONTROL_PARAMS* pIllumZonesControl);
static bool QueryInterfaceOpaque(nvapi_QueryInterface_t query_interface, NV_U32 id, void **result)
{
void *address = ((void *(*)(NV_U32))query_interface)(id);
if (address) {
*result = address;
return true;
}
return false;
}
template<typename F>
static void QueryInterfaceCast(nvapi_QueryInterface_t query_interface, NV_U32 id, const char */*function_name*/, F &function_pointer)
{
/*const bool result = */QueryInterfaceOpaque(query_interface, id, (void **)&function_pointer);
////Log::write("%s querying interface '0x%08x' '%s'", result ? "success" : "failure", id, function_name);
}
#define QueryInterface(query_interface, id, function) \
QueryInterfaceCast((query_interface), (id), #function, p ## function)
static void QueryInterfaces(nvapi_QueryInterface_t query_interface)
{
//Log::write("querying interfaces with '0x%p'", query_interface);
QueryInterface(query_interface, 0x0150E828, NvAPI_Initialize);
QueryInterface(query_interface, 0xD22BDD7E, NvAPI_Unload);
QueryInterface(query_interface, 0x9ABDD40D, NvAPI_EnumDisplayHandle);
QueryInterface(query_interface, 0xE5AC921F, NvAPI_EnumPhysicalGPUs);
QueryInterface(query_interface, 0xF951A4D1, NvAPI_GetDisplayDriverVersion);
QueryInterface(query_interface, 0x01053FA5, NvAPI_GetInterfaceVersionString);
QueryInterface(query_interface, 0x34EF9506, NvAPI_GetPhysicalGPUsFromDisplay);
QueryInterface(query_interface, 0x774AA982, NvAPI_GetMemoryInfo);
QueryInterface(query_interface, 0x0CEEE8E9F, NvAPI_GPU_GetFullName);
QueryInterface(query_interface, 0x6FF81213, NvAPI_GPU_GetPStates20);
QueryInterface(query_interface, 0x0F4DAE6B, NvAPI_GPU_SetPStates20);
QueryInterface(query_interface, 0xDCB616C3, NvAPI_GPU_GetAllClockFrequencies);
QueryInterface(query_interface, 0x60DED2ED, NvAPI_GPU_GetDynamicPStates);
QueryInterface(query_interface, 0x34206D86, NvAPI_GPU_GetPowerPoliciesInfo);
QueryInterface(query_interface, 0x70916171, NvAPI_GPU_GetPowerPoliciesStatus);
QueryInterface(query_interface, 0x0C16C7E2C, NvAPI_GPU_GetVoltageDomainStatus);
QueryInterface(query_interface, 0x0E3640A56, NvAPI_GPU_GetThermalSettings);
QueryInterface(query_interface, 0x014B83A5F, NvAPI_GPU_GetSerialNumber);
QueryInterface(query_interface, 0x0AD95F5ED, NvAPI_GPU_SetPowerPoliciesStatus);
QueryInterface(query_interface, 0x00D258BB5, NvAPI_GPU_GetThermalPoliciesInfo);
QueryInterface(query_interface, 0x0E9C425A1, NvAPI_GPU_GetThermalPoliciesStatus);
QueryInterface(query_interface, 0x034C0B13D, NvAPI_GPU_SetThermalPoliciesStatus);
QueryInterface(query_interface, 0xDA141340, NvAPI_GPU_GetCoolerSettings);
QueryInterface(query_interface, 0x891FA0AE, NvAPI_GPU_SetCoolerLevels);
QueryInterface(query_interface, 0x2DDFB66E, NvAPI_GPU_GetPCIIdentifiers);
QueryInterface(query_interface, 0x283AC65A, NvAPI_I2CWriteEx);
QueryInterface(query_interface, 0x4D7B0709, NvAPI_I2CReadEx);
QueryInterface(query_interface, 0x3DBF5764, NvAPI_GPU_ClientIllumZonesGetControl);
QueryInterface(query_interface, 0x197D065E, NvAPI_GPU_ClientIllumZonesSetControl);
}
NV_STATUS NvAPI_Initialize()
{
if (!pNvAPI_Initialize) {
#ifdef _WIN32
const char *name = sizeof(void*) == 4 ? "nvapi.dll" : "nvapi64.dll";
HMODULE nvapi = LoadLibraryA(name);
if (!nvapi) {
//Log::write("failed to load '%s'", name);
return -1;
}
//Log::write("loaded '%s' '0x%p'", name, nvapi);
nvapi_QueryInterface_t query_interface = (nvapi_QueryInterface_t) GetProcAddress(nvapi, "nvapi_QueryInterface");
if (!query_interface) {
//Log::write("failed to find 'nvapi_QueryInterface'");
return -1;
}
#elif __linux__
void* nvapi = nullptr;
if (!nvapi) nvapi = dlopen("libnvidia-api.so.1", RTLD_LAZY);
if (!nvapi) nvapi = dlopen("libnvidia-api.so", RTLD_LAZY);
if (!nvapi) {
// NVIDIA Driver is not installed
//Log::write("failed to load libnvidia-api.so, NVIDIA Driver is not installed");
return -1;
}
nvapi_QueryInterface_t query_interface = (nvapi_QueryInterface_t) dlsym(nvapi, "nvapi_QueryInterface");
if (!query_interface) {
// NVIDIA Driver is probably not up to date, requires at least driver version 525
//Log::write("failed to load QueryInterface from libnvidia-api.so, NVIDIA Driver is not up to date");
return -1;
}
#endif
QueryInterfaces(query_interface);
}
return pNvAPI_Initialize
? (*pNvAPI_Initialize)()
: -1;
}
NV_STATUS NvAPI_Unload()
{
return pNvAPI_Unload
? (*pNvAPI_Unload)()
: -1;
}
NV_STATUS NvAPI_EnumDisplayHandle(
NV_S32 this_enum,
NV_DISPLAY_HANDLE *display_handle)
{
return pNvAPI_EnumDisplayHandle
? (*pNvAPI_EnumDisplayHandle)(this_enum, display_handle)
: -1;
}
NV_STATUS NvAPI_EnumPhysicalGPUs(
NV_PHYSICAL_GPU_HANDLE *physical_gpu_handles,
NV_S32 *gpu_count)
{
return pNvAPI_EnumPhysicalGPUs
? (*pNvAPI_EnumPhysicalGPUs)(physical_gpu_handles, gpu_count)
: -1;
}
NV_STATUS NvAPI_GetDisplayDriverVersion(
NV_DISPLAY_HANDLE display_handle,
NV_DISPLAY_DRIVER_VERSION_V1 *display_driver_version)
{
return pNvAPI_GetDisplayDriverVersion
? (*pNvAPI_GetDisplayDriverVersion)(display_handle, display_driver_version)
: -1;
}
NV_STATUS NvAPI_GetInterfaceVersionString(
NV_SHORT_STRING version)
{
return pNvAPI_GetInterfaceVersionString
? (*pNvAPI_GetInterfaceVersionString)(version)
: -1;
}
NV_STATUS NvAPI_GetPhysicalGPUsFromDisplay(
NV_DISPLAY_HANDLE display_handle,
NV_PHYSICAL_GPU_HANDLE *gpu_handles,
NV_U32 *gpu_count)
{
return pNvAPI_GetPhysicalGPUsFromDisplay
? (*pNvAPI_GetPhysicalGPUsFromDisplay)(display_handle, gpu_handles, gpu_count)
: -1;
}
NV_STATUS NvAPI_GetMemoryInfo(
NV_DISPLAY_HANDLE display_handle,
NV_MEMORY_INFO_V2 *memory_info)
{
return pNvAPI_GetMemoryInfo
? (*pNvAPI_GetMemoryInfo)(display_handle, memory_info)
: -1;
}
NV_STATUS NvAPI_GPU_GetFullName(
NV_PHYSICAL_GPU_HANDLE physical_gpu_handle,
NV_SHORT_STRING name)
{
return pNvAPI_GPU_GetFullName
? (*pNvAPI_GPU_GetFullName)(physical_gpu_handle, name)
: -1;
}
NV_STATUS NvAPI_GPU_GetPStates20(
NV_PHYSICAL_GPU_HANDLE physical_gpu_handle,
NV_GPU_PSTATES20_V2 *pstates)
{
return pNvAPI_GPU_GetPStates20
? (*pNvAPI_GPU_GetPStates20)(physical_gpu_handle, pstates)
: -1;
}
NV_STATUS NvAPI_GPU_SetPStates20(
NV_PHYSICAL_GPU_HANDLE physical_gpu_handle,
NV_GPU_PSTATES20_V2 *pstates)
{
return pNvAPI_GPU_SetPStates20
? (*pNvAPI_GPU_GetPStates20)(physical_gpu_handle, pstates)
: -1;
}
NV_STATUS NvAPI_GPU_GetAllClockFrequencies(
NV_PHYSICAL_GPU_HANDLE physical_gpu_handle,
NV_CLOCK_FREQUENCIES_V2 *frequencies)
{
return pNvAPI_GPU_GetAllClockFrequencies
? (*pNvAPI_GPU_GetAllClockFrequencies)(physical_gpu_handle, frequencies)
: -1;
}
NV_STATUS NvAPI_GPU_GetDynamicPStates(
NV_PHYSICAL_GPU_HANDLE physical_gpu_handle,
NV_DYNAMIC_PSTATES_V1 *dynamic_pstates)
{
return pNvAPI_GPU_GetDynamicPStates
? (*pNvAPI_GPU_GetDynamicPStates)(physical_gpu_handle, dynamic_pstates)
: -1;
}
NV_STATUS NvAPI_GPU_GetPowerPoliciesInfo(
NV_PHYSICAL_GPU_HANDLE physical_gpu_handle,
NV_GPU_POWER_POLICIES_INFO_V1 *policies_info)
{
return pNvAPI_GPU_GetPowerPoliciesInfo
? (*pNvAPI_GPU_GetPowerPoliciesInfo)(physical_gpu_handle, policies_info)
: -1;
}
NV_STATUS NvAPI_GPU_GetPowerPoliciesStatus(
NV_PHYSICAL_GPU_HANDLE physical_gpu_handle,
NV_GPU_POWER_POLICIES_STATUS_V1 *policies_status)
{
return pNvAPI_GPU_GetPowerPoliciesStatus
? (*NvAPI_GPU_GetPowerPoliciesStatus)(physical_gpu_handle, policies_status)
: -1;
}
NV_STATUS NvAPI_GPU_GetVoltageDomainStatus(
NV_PHYSICAL_GPU_HANDLE physical_gpu_handle,
NV_GPU_VOLTAGE_DOMAINS_STATUS_V1 *voltage_domains_status)
{
return pNvAPI_GPU_GetVoltageDomainStatus
? (*pNvAPI_GPU_GetVoltageDomainStatus)(physical_gpu_handle, voltage_domains_status)
: -1;
}
NV_STATUS NvAPI_GPU_GetThermalSettings(
NV_PHYSICAL_GPU_HANDLE physical_gpu_handle,
NV_THERMAL_TARGET sensor_index,
NV_GPU_THERMAL_SETTINGS_V2 *thermal_settings)
{
return pNvAPI_GPU_GetThermalSettings
? (*pNvAPI_GPU_GetThermalSettings)(physical_gpu_handle, sensor_index, thermal_settings)
: -1;
}
NV_STATUS NvAPI_GPU_GetSerialNumber(
NV_PHYSICAL_GPU_HANDLE physical_gpu_handle,
NV_SHORT_STRING serial_number)
{
return pNvAPI_GPU_GetSerialNumber
? (*pNvAPI_GPU_GetSerialNumber)(physical_gpu_handle, serial_number)
: -1;
}
NV_STATUS NvAPI_GPU_SetPowerPoliciesStatus(
NV_PHYSICAL_GPU_HANDLE physical_gpu_handle,
NV_GPU_POWER_POLICIES_STATUS_V1* policies_status)
{
return pNvAPI_GPU_SetPowerPoliciesStatus
? (*pNvAPI_GPU_SetPowerPoliciesStatus)(physical_gpu_handle, policies_status)
: -1;
}
NV_STATUS NvAPI_GPU_GetThermalPoliciesInfo(
NV_PHYSICAL_GPU_HANDLE physical_gpu_handle,
NV_GPU_THERMAL_POLICIES_INFO_V2* thermal_info)
{
return pNvAPI_GPU_GetThermalPoliciesInfo
? (*pNvAPI_GPU_GetThermalPoliciesInfo)(physical_gpu_handle, thermal_info)
: -1;
}
NV_STATUS NvAPI_GPU_GetThermalPoliciesStatus(
NV_PHYSICAL_GPU_HANDLE physical_gpu_handle,
NV_GPU_THERMAL_POLICIES_STATUS_V2* thermal_status)
{
return pNvAPI_GPU_GetThermalPoliciesStatus
? (*pNvAPI_GPU_GetThermalPoliciesStatus)(physical_gpu_handle, thermal_status)
: -1;
}
NV_STATUS NvAPI_GPU_SetThermalPoliciesStatus(
NV_PHYSICAL_GPU_HANDLE physical_gpu_handle,
NV_GPU_THERMAL_POLICIES_STATUS_V2* thermal_status)
{
return pNvAPI_GPU_SetThermalPoliciesStatus
? (*pNvAPI_GPU_SetThermalPoliciesStatus)(physical_gpu_handle, thermal_status)
: -1;
}
NV_STATUS NvAPI_GPU_GetCoolerSettings(
NV_PHYSICAL_GPU_HANDLE physical_gpu_handle,
NV_S32 cooler_index,
NV_GPU_COOLER_SETTINGS_V2 *cooler_settings)
{
return pNvAPI_GPU_GetCoolerSettings
? (*pNvAPI_GPU_GetCoolerSettings)(physical_gpu_handle, cooler_index, cooler_settings)
: -1;
}
NV_STATUS NvAPI_GPU_SetCoolerLevels(
NV_PHYSICAL_GPU_HANDLE physical_gpu_handle,
NV_S32 cooler_index,
NV_GPU_COOLER_LEVELS_V1 *cooler_levels)
{
return pNvAPI_GPU_SetCoolerLevels
? (*pNvAPI_GPU_SetCoolerLevels)(physical_gpu_handle, cooler_index, cooler_levels)
: -1;
}
NV_STATUS NvAPI_GPU_GetPCIIdentifiers(
NV_PHYSICAL_GPU_HANDLE physical_gpu_handle,
NV_U32 *device_id,
NV_U32 *sub_system_id,
NV_U32 *revision_id,
NV_U32 *ext_device_id)
{
return pNvAPI_GPU_GetPCIIdentifiers
? (*pNvAPI_GPU_GetPCIIdentifiers)(physical_gpu_handle, device_id, sub_system_id, revision_id, ext_device_id)
: -1;
}
NV_STATUS NvAPI_I2CWriteEx(
NV_PHYSICAL_GPU_HANDLE physical_gpu_handle,
NV_I2C_INFO_V3* i2c_info,
NV_U32 *unknown)
{
return pNvAPI_I2CWriteEx
? (*pNvAPI_I2CWriteEx)(physical_gpu_handle, i2c_info, unknown)
: -1;
}
NV_STATUS NvAPI_I2CReadEx(
NV_PHYSICAL_GPU_HANDLE physical_gpu_handle,
NV_I2C_INFO_V3* i2c_info,
NV_U32 *unknown)
{
return pNvAPI_I2CReadEx
? (*pNvAPI_I2CReadEx)(physical_gpu_handle, i2c_info, unknown)
: -1;
}
NV_STATUS NvAPI_GPU_ClientIllumZonesGetControl(
NV_PHYSICAL_GPU_HANDLE physical_gpu_handle,
NV_GPU_CLIENT_ILLUM_ZONE_CONTROL_PARAMS* pIllumZonesControl)
{
return pNvAPI_GPU_ClientIllumZonesGetControl
? (*pNvAPI_GPU_ClientIllumZonesGetControl)(physical_gpu_handle, pIllumZonesControl)
: -1;
}
NV_STATUS NvAPI_GPU_ClientIllumZonesSetControl(
NV_PHYSICAL_GPU_HANDLE physical_gpu_handle,
NV_GPU_CLIENT_ILLUM_ZONE_CONTROL_PARAMS* pIllumZonesControl)
{
return pNvAPI_GPU_ClientIllumZonesSetControl
? (*pNvAPI_GPU_ClientIllumZonesSetControl)(physical_gpu_handle, pIllumZonesControl)
: -1;
}
+1035
View File
File diff suppressed because it is too large Load Diff
Binary file not shown.
+75
View File
@@ -0,0 +1,75 @@
// PawnIOLib - Library and tooling source to be used with PawnIO.
// Copyright (C) 2023 namazso <admin@namazso.eu>
//
// This library 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 2.1 of the License, or (at your option) any later version.
//
// This library 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 this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#ifndef PAWNIOLIB_LIBRARY_H
#define PAWNIOLIB_LIBRARY_H
#ifdef PawnIOLib_EXPORTS
#define PAWNIO_EXPORT __declspec(dllexport)
#else
#define PAWNIO_EXPORT __declspec(dllimport)
#endif
#define PAWNIOAPI EXTERN_C PAWNIO_EXPORT HRESULT STDAPICALLTYPE
/// Get PawnIOLib version.
///
/// @p version A pointer to a ULONG which receives the version.
/// @return A HRESULT.
PAWNIOAPI pawnio_version(PULONG version);
/// Open a PawnIO executor.
///
/// @p handle A handle to the executor, or NULL.
/// @return A HRESULT.
PAWNIOAPI pawnio_open(PHANDLE handle);
/// Load a PawnIO blob.
///
/// @p handle Handle from @c pawnio_open.
/// @p blob Blob to load.
/// @p size Size of blob.
/// @return A HRESULT.
PAWNIOAPI pawnio_load(HANDLE handle, const UCHAR* blob, SIZE_T size);
/// Executes a function from the loaded blob.
///
/// @p handle Handle from @c pawnio_open.
/// @p name Function name to execute.
/// @p in Input buffer.
/// @p in_size Input buffer count.
/// @p out Output buffer.
/// @p out_size Output buffer count.
/// @p return_size Entries written in out_size.
/// @return A HRESULT.
PAWNIOAPI pawnio_execute(
HANDLE handle,
PCSTR name,
const ULONG64* in,
SIZE_T in_size,
PULONG64 out,
SIZE_T out_size,
PSIZE_T return_size
);
/// Close a PawnIO executor.
///
/// @p handle Handle from @c pawnio_open.
/// @return A HRESULT.
PAWNIOAPI pawnio_close(HANDLE handle);
#endif //PAWNIOLIB_LIBRARY_H
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large Load Diff
+46
View File
@@ -0,0 +1,46 @@
//
// Copyright (c) 2016 - 2022 Advanced Micro Devices, Inc. All rights reserved.
//
// MIT LICENSE:
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
/// \file adl_sdk.h
/// \brief Contains the definition of the Memory Allocation Callback.\n <b>Included in ADL SDK</b>
///
/// \n\n
/// This file contains the definition of the Memory Allocation Callback.\n
/// It also includes definitions of the respective structures and constants.\n
/// <b> This is the only header file to be included in a C/C++ project using ADL </b>
#ifndef ADL_SDK_H_
#define ADL_SDK_H_
#include "adl_structures.h"
#if defined (LINUX)
#define __stdcall
#endif /* (LINUX) */
/// Memory Allocation Call back
typedef void* ( __stdcall *ADL_MAIN_MALLOC_CALLBACK )( int );
#define ADL_SDK_MAJOR_VERSION 17
#define ADL_SDK_MINOR_VERSION 1
#endif /* ADL_SDK_H_ */
File diff suppressed because it is too large Load Diff
+624
View File
@@ -0,0 +1,624 @@
/*******************************************************
HIDAPI - Multi-Platform library for
communication with HID devices.
Alan Ott
Signal 11 Software
libusb/hidapi Team
Copyright 2023, All Rights Reserved.
At the discretion of the user of this library,
this software may be licensed under the terms of the
GNU General Public License v3, a BSD-Style license, or the
original HIDAPI license as outlined in the LICENSE.txt,
LICENSE-gpl3.txt, LICENSE-bsd.txt, and LICENSE-orig.txt
files located at the root of the source distribution.
These files may also be found in the public source
code repository located at:
https://github.com/libusb/hidapi .
********************************************************/
/** @file
* @defgroup API hidapi API
*/
#ifndef HIDAPI_H__
#define HIDAPI_H__
#include <wchar.h>
/* #480: this is to be refactored properly for v1.0 */
#ifdef _WIN32
#ifndef HID_API_NO_EXPORT_DEFINE
#define HID_API_EXPORT __declspec(dllexport)
#endif
#endif
#ifndef HID_API_EXPORT
#define HID_API_EXPORT /**< API export macro */
#endif
/* To be removed in v1.0 */
#define HID_API_CALL /**< API call macro */
#define HID_API_EXPORT_CALL HID_API_EXPORT HID_API_CALL /**< API export and call macro*/
/** @brief Static/compile-time major version of the library.
@ingroup API
*/
#define HID_API_VERSION_MAJOR 0
/** @brief Static/compile-time minor version of the library.
@ingroup API
*/
#define HID_API_VERSION_MINOR 14
/** @brief Static/compile-time patch version of the library.
@ingroup API
*/
#define HID_API_VERSION_PATCH 0
/* Helper macros */
#define HID_API_AS_STR_IMPL(x) #x
#define HID_API_AS_STR(x) HID_API_AS_STR_IMPL(x)
#define HID_API_TO_VERSION_STR(v1, v2, v3) HID_API_AS_STR(v1.v2.v3)
/** @brief Coverts a version as Major/Minor/Patch into a number:
<8 bit major><16 bit minor><8 bit patch>.
This macro was added in version 0.12.0.
Convenient function to be used for compile-time checks, like:
@code{.c}
#if HID_API_VERSION >= HID_API_MAKE_VERSION(0, 12, 0)
@endcode
@ingroup API
*/
#define HID_API_MAKE_VERSION(mj, mn, p) (((mj) << 24) | ((mn) << 8) | (p))
/** @brief Static/compile-time version of the library.
This macro was added in version 0.12.0.
@see @ref HID_API_MAKE_VERSION.
@ingroup API
*/
#define HID_API_VERSION HID_API_MAKE_VERSION(HID_API_VERSION_MAJOR, HID_API_VERSION_MINOR, HID_API_VERSION_PATCH)
/** @brief Static/compile-time string version of the library.
@ingroup API
*/
#define HID_API_VERSION_STR HID_API_TO_VERSION_STR(HID_API_VERSION_MAJOR, HID_API_VERSION_MINOR, HID_API_VERSION_PATCH)
/** @brief Maximum expected HID Report descriptor size in bytes.
Since version 0.13.0, @ref HID_API_VERSION >= HID_API_MAKE_VERSION(0, 13, 0)
@ingroup API
*/
#define HID_API_MAX_REPORT_DESCRIPTOR_SIZE 4096
#ifdef __cplusplus
extern "C" {
#endif
/** A structure to hold the version numbers. */
struct hid_api_version {
int major; /**< major version number */
int minor; /**< minor version number */
int patch; /**< patch version number */
};
struct hid_device_;
typedef struct hid_device_ hid_device; /**< opaque hidapi structure */
/** @brief HID underlying bus types.
@ingroup API
*/
typedef enum {
/** Unknown bus type */
HID_API_BUS_UNKNOWN = 0x00,
/** USB bus
Specifications:
https://usb.org/hid */
HID_API_BUS_USB = 0x01,
/** Bluetooth or Bluetooth LE bus
Specifications:
https://www.bluetooth.com/specifications/specs/human-interface-device-profile-1-1-1/
https://www.bluetooth.com/specifications/specs/hid-service-1-0/
https://www.bluetooth.com/specifications/specs/hid-over-gatt-profile-1-0/ */
HID_API_BUS_BLUETOOTH = 0x02,
/** I2C bus
Specifications:
https://docs.microsoft.com/previous-versions/windows/hardware/design/dn642101(v=vs.85) */
HID_API_BUS_I2C = 0x03,
/** SPI bus
Specifications:
https://www.microsoft.com/download/details.aspx?id=103325 */
HID_API_BUS_SPI = 0x04,
} hid_bus_type;
/** hidapi info structure */
struct hid_device_info {
/** Platform-specific device path */
char *path;
/** Device Vendor ID */
unsigned short vendor_id;
/** Device Product ID */
unsigned short product_id;
/** Serial Number */
wchar_t *serial_number;
/** Device Release Number in binary-coded decimal,
also known as Device Version Number */
unsigned short release_number;
/** Manufacturer String */
wchar_t *manufacturer_string;
/** Product string */
wchar_t *product_string;
/** Usage Page for this Device/Interface
(Windows/Mac/hidraw only) */
unsigned short usage_page;
/** Usage for this Device/Interface
(Windows/Mac/hidraw only) */
unsigned short usage;
/** The USB interface which this logical device
represents.
Valid only if the device is a USB HID device.
Set to -1 in all other cases.
*/
int interface_number;
/** Pointer to the next device */
struct hid_device_info *next;
/** Underlying bus type
Since version 0.13.0, @ref HID_API_VERSION >= HID_API_MAKE_VERSION(0, 13, 0)
*/
hid_bus_type bus_type;
};
/** @brief Initialize the HIDAPI library.
This function initializes the HIDAPI library. Calling it is not
strictly necessary, as it will be called automatically by
hid_enumerate() and any of the hid_open_*() functions if it is
needed. This function should be called at the beginning of
execution however, if there is a chance of HIDAPI handles
being opened by different threads simultaneously.
@ingroup API
@returns
This function returns 0 on success and -1 on error.
Call hid_error(NULL) to get the failure reason.
*/
int HID_API_EXPORT HID_API_CALL hid_init(void);
/** @brief Finalize the HIDAPI library.
This function frees all of the static data associated with
HIDAPI. It should be called at the end of execution to avoid
memory leaks.
@ingroup API
@returns
This function returns 0 on success and -1 on error.
*/
int HID_API_EXPORT HID_API_CALL hid_exit(void);
/** @brief Enumerate the HID Devices.
This function returns a linked list of all the HID devices
attached to the system which match vendor_id and product_id.
If @p vendor_id is set to 0 then any vendor matches.
If @p product_id is set to 0 then any product matches.
If @p vendor_id and @p product_id are both set to 0, then
all HID devices will be returned.
@ingroup API
@param vendor_id The Vendor ID (VID) of the types of device
to open.
@param product_id The Product ID (PID) of the types of
device to open.
@returns
This function returns a pointer to a linked list of type
struct #hid_device_info, containing information about the HID devices
attached to the system,
or NULL in the case of failure or if no HID devices present in the system.
Call hid_error(NULL) to get the failure reason.
@note The returned value by this function must to be freed by calling hid_free_enumeration(),
when not needed anymore.
*/
struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned short vendor_id, unsigned short product_id);
/** @brief Free an enumeration Linked List
This function frees a linked list created by hid_enumerate().
@ingroup API
@param devs Pointer to a list of struct_device returned from
hid_enumerate().
*/
void HID_API_EXPORT HID_API_CALL hid_free_enumeration(struct hid_device_info *devs);
/** @brief Open a HID device using a Vendor ID (VID), Product ID
(PID) and optionally a serial number.
If @p serial_number is NULL, the first device with the
specified VID and PID is opened.
@ingroup API
@param vendor_id The Vendor ID (VID) of the device to open.
@param product_id The Product ID (PID) of the device to open.
@param serial_number The Serial Number of the device to open
(Optionally NULL).
@returns
This function returns a pointer to a #hid_device object on
success or NULL on failure.
Call hid_error(NULL) to get the failure reason.
@note The returned object must be freed by calling hid_close(),
when not needed anymore.
*/
HID_API_EXPORT hid_device * HID_API_CALL hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number);
/** @brief Open a HID device by its path name.
The path name be determined by calling hid_enumerate(), or a
platform-specific path name can be used (eg: /dev/hidraw0 on
Linux).
@ingroup API
@param path The path name of the device to open
@returns
This function returns a pointer to a #hid_device object on
success or NULL on failure.
Call hid_error(NULL) to get the failure reason.
@note The returned object must be freed by calling hid_close(),
when not needed anymore.
*/
HID_API_EXPORT hid_device * HID_API_CALL hid_open_path(const char *path);
/** @brief Write an Output report to a HID device.
The first byte of @p data[] must contain the Report ID. For
devices which only support a single report, this must be set
to 0x0. The remaining bytes contain the report data. Since
the Report ID is mandatory, calls to hid_write() will always
contain one more byte than the report contains. For example,
if a hid report is 16 bytes long, 17 bytes must be passed to
hid_write(), the Report ID (or 0x0, for devices with a
single report), followed by the report data (16 bytes). In
this example, the length passed in would be 17.
hid_write() will send the data on the first OUT endpoint, if
one exists. If it does not, it will send the data through
the Control Endpoint (Endpoint 0).
@ingroup API
@param dev A device handle returned from hid_open().
@param data The data to send, including the report number as
the first byte.
@param length The length in bytes of the data to send.
@returns
This function returns the actual number of bytes written and
-1 on error.
Call hid_error(dev) to get the failure reason.
*/
int HID_API_EXPORT HID_API_CALL hid_write(hid_device *dev, const unsigned char *data, size_t length);
/** @brief Read an Input report from a HID device with timeout.
Input reports are returned
to the host through the INTERRUPT IN endpoint. The first byte will
contain the Report number if the device uses numbered reports.
@ingroup API
@param dev A device handle returned from hid_open().
@param data A buffer to put the read data into.
@param length The number of bytes to read. For devices with
multiple reports, make sure to read an extra byte for
the report number.
@param milliseconds timeout in milliseconds or -1 for blocking wait.
@returns
This function returns the actual number of bytes read and
-1 on error.
Call hid_error(dev) to get the failure reason.
If no packet was available to be read within
the timeout period, this function returns 0.
*/
int HID_API_EXPORT HID_API_CALL hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds);
/** @brief Read an Input report from a HID device.
Input reports are returned
to the host through the INTERRUPT IN endpoint. The first byte will
contain the Report number if the device uses numbered reports.
@ingroup API
@param dev A device handle returned from hid_open().
@param data A buffer to put the read data into.
@param length The number of bytes to read. For devices with
multiple reports, make sure to read an extra byte for
the report number.
@returns
This function returns the actual number of bytes read and
-1 on error.
Call hid_error(dev) to get the failure reason.
If no packet was available to be read and
the handle is in non-blocking mode, this function returns 0.
*/
int HID_API_EXPORT HID_API_CALL hid_read(hid_device *dev, unsigned char *data, size_t length);
/** @brief Set the device handle to be non-blocking.
In non-blocking mode calls to hid_read() will return
immediately with a value of 0 if there is no data to be
read. In blocking mode, hid_read() will wait (block) until
there is data to read before returning.
Nonblocking can be turned on and off at any time.
@ingroup API
@param dev A device handle returned from hid_open().
@param nonblock enable or not the nonblocking reads
- 1 to enable nonblocking
- 0 to disable nonblocking.
@returns
This function returns 0 on success and -1 on error.
Call hid_error(dev) to get the failure reason.
*/
int HID_API_EXPORT HID_API_CALL hid_set_nonblocking(hid_device *dev, int nonblock);
/** @brief Send a Feature report to the device.
Feature reports are sent over the Control endpoint as a
Set_Report transfer. The first byte of @p data[] must
contain the Report ID. For devices which only support a
single report, this must be set to 0x0. The remaining bytes
contain the report data. Since the Report ID is mandatory,
calls to hid_send_feature_report() will always contain one
more byte than the report contains. For example, if a hid
report is 16 bytes long, 17 bytes must be passed to
hid_send_feature_report(): the Report ID (or 0x0, for
devices which do not use numbered reports), followed by the
report data (16 bytes). In this example, the length passed
in would be 17.
@ingroup API
@param dev A device handle returned from hid_open().
@param data The data to send, including the report number as
the first byte.
@param length The length in bytes of the data to send, including
the report number.
@returns
This function returns the actual number of bytes written and
-1 on error.
Call hid_error(dev) to get the failure reason.
*/
int HID_API_EXPORT HID_API_CALL hid_send_feature_report(hid_device *dev, const unsigned char *data, size_t length);
/** @brief Get a feature report from a HID device.
Set the first byte of @p data[] to the Report ID of the
report to be read. Make sure to allow space for this
extra byte in @p data[]. Upon return, the first byte will
still contain the Report ID, and the report data will
start in data[1].
@ingroup API
@param dev A device handle returned from hid_open().
@param data A buffer to put the read data into, including
the Report ID. Set the first byte of @p data[] to the
Report ID of the report to be read, or set it to zero
if your device does not use numbered reports.
@param length The number of bytes to read, including an
extra byte for the report ID. The buffer can be longer
than the actual report.
@returns
This function returns the number of bytes read plus
one for the report ID (which is still in the first
byte), or -1 on error.
Call hid_error(dev) to get the failure reason.
*/
int HID_API_EXPORT HID_API_CALL hid_get_feature_report(hid_device *dev, unsigned char *data, size_t length);
/** @brief Get a input report from a HID device.
Since version 0.10.0, @ref HID_API_VERSION >= HID_API_MAKE_VERSION(0, 10, 0)
Set the first byte of @p data[] to the Report ID of the
report to be read. Make sure to allow space for this
extra byte in @p data[]. Upon return, the first byte will
still contain the Report ID, and the report data will
start in data[1].
@ingroup API
@param dev A device handle returned from hid_open().
@param data A buffer to put the read data into, including
the Report ID. Set the first byte of @p data[] to the
Report ID of the report to be read, or set it to zero
if your device does not use numbered reports.
@param length The number of bytes to read, including an
extra byte for the report ID. The buffer can be longer
than the actual report.
@returns
This function returns the number of bytes read plus
one for the report ID (which is still in the first
byte), or -1 on error.
Call hid_error(dev) to get the failure reason.
*/
int HID_API_EXPORT HID_API_CALL hid_get_input_report(hid_device *dev, unsigned char *data, size_t length);
/** @brief Close a HID device.
@ingroup API
@param dev A device handle returned from hid_open().
*/
void HID_API_EXPORT HID_API_CALL hid_close(hid_device *dev);
/** @brief Get The Manufacturer String from a HID device.
@ingroup API
@param dev A device handle returned from hid_open().
@param string A wide string buffer to put the data into.
@param maxlen The length of the buffer in multiples of wchar_t.
@returns
This function returns 0 on success and -1 on error.
Call hid_error(dev) to get the failure reason.
*/
int HID_API_EXPORT_CALL hid_get_manufacturer_string(hid_device *dev, wchar_t *string, size_t maxlen);
/** @brief Get The Product String from a HID device.
@ingroup API
@param dev A device handle returned from hid_open().
@param string A wide string buffer to put the data into.
@param maxlen The length of the buffer in multiples of wchar_t.
@returns
This function returns 0 on success and -1 on error.
Call hid_error(dev) to get the failure reason.
*/
int HID_API_EXPORT_CALL hid_get_product_string(hid_device *dev, wchar_t *string, size_t maxlen);
/** @brief Get The Serial Number String from a HID device.
@ingroup API
@param dev A device handle returned from hid_open().
@param string A wide string buffer to put the data into.
@param maxlen The length of the buffer in multiples of wchar_t.
@returns
This function returns 0 on success and -1 on error.
Call hid_error(dev) to get the failure reason.
*/
int HID_API_EXPORT_CALL hid_get_serial_number_string(hid_device *dev, wchar_t *string, size_t maxlen);
/** @brief Get The struct #hid_device_info from a HID device.
Since version 0.13.0, @ref HID_API_VERSION >= HID_API_MAKE_VERSION(0, 13, 0)
@ingroup API
@param dev A device handle returned from hid_open().
@returns
This function returns a pointer to the struct #hid_device_info
for this hid_device, or NULL in the case of failure.
Call hid_error(dev) to get the failure reason.
This struct is valid until the device is closed with hid_close().
@note The returned object is owned by the @p dev, and SHOULD NOT be freed by the user.
*/
struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_get_device_info(hid_device *dev);
/** @brief Get a string from a HID device, based on its string index.
@ingroup API
@param dev A device handle returned from hid_open().
@param string_index The index of the string to get.
@param string A wide string buffer to put the data into.
@param maxlen The length of the buffer in multiples of wchar_t.
@returns
This function returns 0 on success and -1 on error.
Call hid_error(dev) to get the failure reason.
*/
int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *dev, int string_index, wchar_t *string, size_t maxlen);
/** @brief Get a report descriptor from a HID device.
Since version 0.14.0, @ref HID_API_VERSION >= HID_API_MAKE_VERSION(0, 14, 0)
User has to provide a preallocated buffer where descriptor will be copied to.
The recommended size for preallocated buffer is @ref HID_API_MAX_REPORT_DESCRIPTOR_SIZE bytes.
@ingroup API
@param dev A device handle returned from hid_open().
@param buf The buffer to copy descriptor into.
@param buf_size The size of the buffer in bytes.
@returns
This function returns non-negative number of bytes actually copied, or -1 on error.
*/
int HID_API_EXPORT_CALL hid_get_report_descriptor(hid_device *dev, unsigned char *buf, size_t buf_size);
/** @brief Get a string describing the last error which occurred.
This function is intended for logging/debugging purposes.
This function guarantees to never return NULL.
If there was no error in the last function call -
the returned string clearly indicates that.
Any HIDAPI function that can explicitly indicate an execution failure
(e.g. by an error code, or by returning NULL) - may set the error string,
to be returned by this function.
Strings returned from hid_error() must not be freed by the user,
i.e. owned by HIDAPI library.
Device-specific error string may remain allocated at most until hid_close() is called.
Global error string may remain allocated at most until hid_exit() is called.
@ingroup API
@param dev A device handle returned from hid_open(),
or NULL to get the last non-device-specific error
(e.g. for errors in hid_open() or hid_enumerate()).
@returns
A string describing the last error (if any).
*/
HID_API_EXPORT const wchar_t* HID_API_CALL hid_error(hid_device *dev);
/** @brief Get a runtime version of the library.
This function is thread-safe.
@ingroup API
@returns
Pointer to statically allocated struct, that contains version.
*/
HID_API_EXPORT const struct hid_api_version* HID_API_CALL hid_version(void);
/** @brief Get a runtime version string of the library.
This function is thread-safe.
@ingroup API
@returns
Pointer to statically allocated string, that contains version string.
*/
HID_API_EXPORT const char* HID_API_CALL hid_version_str(void);
#ifdef __cplusplus
}
#endif
#endif
+74
View File
@@ -0,0 +1,74 @@
/*******************************************************
HIDAPI - Multi-Platform library for
communication with HID devices.
libusb/hidapi Team
Copyright 2022, All Rights Reserved.
At the discretion of the user of this library,
this software may be licensed under the terms of the
GNU General Public License v3, a BSD-Style license, or the
original HIDAPI license as outlined in the LICENSE.txt,
LICENSE-gpl3.txt, LICENSE-bsd.txt, and LICENSE-orig.txt
files located at the root of the source distribution.
These files may also be found in the public source
code repository located at:
https://github.com/libusb/hidapi .
********************************************************/
/** @file
* @defgroup API hidapi API
*
* Since version 0.12.0, @ref HID_API_VERSION >= HID_API_MAKE_VERSION(0, 12, 0)
*/
#ifndef HIDAPI_WINAPI_H__
#define HIDAPI_WINAPI_H__
#include <stdint.h>
#include <guiddef.h>
#include "hidapi.h"
#ifdef __cplusplus
extern "C" {
#endif
/** @brief Get the container ID for a HID device.
Since version 0.12.0, @ref HID_API_VERSION >= HID_API_MAKE_VERSION(0, 12, 0)
This function returns the `DEVPKEY_Device_ContainerId` property of
the given device. This can be used to correlate different
interfaces/ports on the same hardware device.
@ingroup API
@param dev A device handle returned from hid_open().
@param guid The device's container ID on return.
@returns
This function returns 0 on success and -1 on error.
*/
int HID_API_EXPORT_CALL hid_winapi_get_container_id(hid_device *dev, GUID *container_id);
/**
* @brief Reconstructs a HID Report Descriptor from a Win32 HIDP_PREPARSED_DATA structure.
* This reconstructed report descriptor is logical identical to the real report descriptor,
* but not byte wise identical.
*
* @param[in] hidp_preparsed_data Pointer to the HIDP_PREPARSED_DATA to read, i.e.: the value of PHIDP_PREPARSED_DATA,
* as returned by HidD_GetPreparsedData WinAPI function.
* @param buf Pointer to the buffer where the report descriptor should be stored.
* @param[in] buf_size Size of the buffer. The recommended size for the buffer is @ref HID_API_MAX_REPORT_DESCRIPTOR_SIZE bytes.
*
* @return Returns size of reconstructed report descriptor if successful, -1 for error.
*/
int HID_API_EXPORT_CALL hid_winapi_descriptor_reconstruct_pp_data(void *hidp_preparsed_data, unsigned char *buf, size_t buf_size);
#ifdef __cplusplus
}
#endif
#endif
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+9806
View File
File diff suppressed because it is too large Load Diff
+61
View File
@@ -0,0 +1,61 @@
---
# Based on Webkit style
BasedOnStyle: Webkit
IndentWidth: 4
ColumnLimit: 120
---
Language: Cpp
Standard: Cpp11
# Pointers aligned to the left
DerivePointerAlignment: false
PointerAlignment: Left
AccessModifierOffset: -4
AllowShortFunctionsOnASingleLine: Inline
AlwaysBreakTemplateDeclarations: true
BreakBeforeBraces: Custom
BraceWrapping:
AfterClass: true
AfterControlStatement: true
AfterEnum: true
AfterFunction: true
AfterNamespace: true
AfterStruct: true
AfterUnion: true
AfterExternBlock: true
BeforeCatch: true
BeforeElse: true
SplitEmptyFunction: false
SplitEmptyRecord: false
SplitEmptyNamespace: false
BreakConstructorInitializers: BeforeColon
CompactNamespaces: false
ConstructorInitializerAllOnOneLineOrOnePerLine: true
ConstructorInitializerIndentWidth: 4
Cpp11BracedListStyle: true
FixNamespaceComments: true
IncludeBlocks: Regroup
IncludeCategories:
# C++ standard headers (no .h)
- Regex: '<[[:alnum:]_-]+>'
Priority: 1
# Hueplusplus library
- Regex: '<hueplusplus/[[:alnum:]_./-]+>'
Priority: 2
# Extenal libraries (with .h)
- Regex: '<[[:alnum:]_./-]+>'
Priority: 3
# Headers from same folder
- Regex: '"[[:alnum:]_.-]+"'
Priority: 4
# Headers from other folders
- Regex: '"[[:alnum:]_/.-]+"'
Priority: 5
IndentCaseLabels: false
NamespaceIndentation: None
SortIncludes: true
SortUsingDeclarations: true
SpaceAfterTemplateKeyword: true
SpacesInAngles: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
UseTab: Never
+58
View File
@@ -0,0 +1,58 @@
# Contribution Guide
Help is always welcome. If you want to contribute to hueplusplus, please read these guidelines
## Request feature / Report bug
To request a feature or report a bug, create an issue using the templates.
## Making changes
If you want to add a new feature or fix a bug, first check out the development branch and open and closed issues.
Maybe the feature already exists and you would just do duplicate work.
Also use the development branch as the base for your feature branch, because all pull requests are first rebased into development.
## Pull requests
When creating a pull request, be sure to choose development as the target.
You might need to rebase on development again and merge in new changes.
### Keeping up with changes
While you are working on your pull request or your forked branch it might occur that
someone has force pushed the development branch, from which you started your feature branch.
In that case you will need to rebase onto the force pushed branch. For that you need to follow these steps:
1. Switch to the development branch
```
git checkout development
```
2. Add this repository as your remote upstream
```
git remote add upstream git@github.com:enwi/hueplusplus.git
```
3. Fetch all changes
```
git fetch upstream
```
4. Reset your development branch and replace it with our (force pushed) version
```
git reset --hard upstream/development
```
> If you have for some reason made changes to your development branch do a rebase pull to preserve them
> ```
> git pull --rebase upstream/development
> ```
5. Switch back to your feature branch
```
git checkout name-of-your-feature-branch
```
6. Rebase your changes on to the new development branch
```
git rebase development
```
7. Force push your changes (because you are diverged now)
```
git push --force
```
## Code style
The code is formatted using clang-format. If you do not want to use it yourself, try to keep your style consistent with the other code
so not too many reformats are necessary.
+12
View File
@@ -0,0 +1,12 @@
# These are supported funding model platforms
github: [enwi]
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
@@ -0,0 +1,31 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: 'Status: Available, Type: Bug'
assignees: ''
---
**Describe the bug**:
A clear and concise description of what the bug is.
**To Reproduce**:
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**:
A clear and concise description of what you expected to happen.
**Console log/Error message**:
If applicable, add a console log or error message to help explain your problem.
**Desktop (please complete the following information):**
- OS: [e.g. MacOS, Windows, Linux, ESP32 SDK, Arduino]
- Version [e.g. 22]
**Additional context**
Add any other context about the problem here.
@@ -0,0 +1,20 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: 'Status: Available, Type: Enhancement'
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.
@@ -0,0 +1,72 @@
name: CI
on:
push:
branches:
- master
- development
pull_request:
jobs:
build:
runs-on: ubuntu-24.04
env:
LINUX_DIST: bionic
DEPS_DIR: ${{ github.workspace }}/deps
COMPILER_NAME: gcc
CXX: g++
CC: gcc
RUN_TESTS: true
COVERAGE: false
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
# PATH: ${{ github.workspace }}/deps/cmake/bin:${{ env.PATH }}
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Set up dependencies
run: |
sudo apt-get update -qq
sudo apt-get install -y gcc g++ lcov doxygen graphviz python3-yaml
# - name: Install CodeCov and LCOV
# run: |
# sudo update-alternatives --install /usr/bin/gcov gcov /usr/bin/gcov-7 90
# wget http://ftp.de.debian.org/debian/pool/main/l/lcov/lcov_1.13.orig.tar.gz
# tar xf lcov_1.13.orig.tar.gz
# make -C lcov-1.13 "PREFIX=${HOME}/.local" install
# echo "${HOME}/.local/bin" >> $GITHUB_PATH
- name: Show tool versions
run: |
echo $PATH
echo $CXX
$CXX --version
$CXX -v
cmake --version
lcov --version
gcov --version
- name: Build project
run: |
mkdir -p build
cd build
cmake .. -Dhueplusplus_TESTS=ON -DCMAKE_BUILD_TYPE=Debug -Dhueplusplus_EXAMPLES=ON
make hueplusplus_examples hueplusplus_snippets
make coveragetest
cd ..
doxygen Doxyfile
touch doc/html/.nojekyll
- name: Upload coverage to Codecov
run: |
bash <(curl -s https://codecov.io/bash)
- name: Deploy documentation to GitHub Pages
if: github.ref == 'refs/heads/master'
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./doc/html
+65
View File
@@ -0,0 +1,65 @@
# Compiled Object files
*.slo
*.lo
*.o
*.obj
# Precompiled Headers
*.gch
*.pch
# Compiled Dynamic libraries
*.so
*.dylib
*.dll
# Fortran module files
*.mod
*.smod
# Compiled Static libraries
*.lai
*.la
*.a
*.lib
# Executables
*.exe
*.out
*.app
# build directory
/build*
/out
/bin
# Generated documentation
/doc/html
# General
.DS_Store
.AppleDouble
.LSOverride
.vs
# Icon must end with two \r
Icon
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
@@ -0,0 +1,6 @@
[submodule "lib/mbedtls"]
path = lib/mbedtls
url = https://github.com/ARMmbed/mbedtls.git
[submodule "lib/json"]
path = lib/json
url = https://github.com/nlohmann/json.git
+76
View File
@@ -0,0 +1,76 @@
language: generic
env:
global:
# Ubuntu version
- LINUX_DIST=bionic
- DEPS_DIR=${TRAVIS_BUILD_DIR}/deps
# compiler settings
- COMPILER_NAME=gcc
- CXX=g++
- CC=gcc
# Misc
- RUN_TESTS=true
- COVERAGE=false
- PATH=${DEPS_DIR}/cmake/bin:${PATH}
matrix:
include:
- os: linux
dist: bionic
sudo: true
compiler: gcc
addons:
apt:
packages:
# Misc
- python-yaml
- doxygen
- graphviz
before_install:
# Combine global build options with OS/compiler-dependent options
- export CMAKE_OPTIONS=${CMAKE_OPTIONS}" "${ENV_CMAKE_OPTIONS}
- export CXX_FLAGS=${CXX_FLAGS}" "${ENV_CXX_FLAGS}
# c++14
- sudo apt-get update -qq
install:
# CodeCov
- sudo update-alternatives --install /usr/bin/gcov gcov /usr/bin/gcov-7 90
# we have to build lcov on our own, because it is not possible to install lcov-1.13 with apt
- wget http://ftp.de.debian.org/debian/pool/main/l/lcov/lcov_1.13.orig.tar.gz && tar xf lcov_1.13.orig.tar.gz && make -C lcov-1.13 "PREFIX=${HOME}/.local" install && export PATH="${PATH}:${HOME}/.local/bin";
# show info
- echo ${PATH}
- echo ${CXX}
- ${CXX} --version
- ${CXX} -v
- cmake --version
- lcov --version
script:
############################################################################
# Build main, tests and examples
############################################################################
- mkdir -p build
- cd build
- cmake .. -Dhueplusplus_TESTS=ON -DCMAKE_BUILD_TYPE=Debug -Dhueplusplus_EXAMPLES=ON
- make hueplusplus_examples hueplusplus_snippets
- make coveragetest
- cd ..
- doxygen Doxyfile
# .nojekyll file prevents hiding of files starting with _
- touch doc/html/.nojekyll
after_success:
# upload result to codecov
- bash <(curl -s https://codecov.io/bash)
deploy:
provider: pages
skip_cleanup: true
local_dir: doc/html
github_token: $GH_REPO_TOKEN
on:
branch: master
+128
View File
@@ -0,0 +1,128 @@
cmake_minimum_required(VERSION 3.10.2...3.28)
# Add cmake dir to module path, so Find*.cmake can be found
set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH})
project(hueplusplus VERSION 1.2.0 LANGUAGES CXX)
# check whether hueplusplus is compiled directly or included as a subdirectory
if(NOT DEFINED hueplusplus_master_project)
if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME)
set(hueplusplus_master_project ON)
else()
set(hueplusplus_master_project OFF)
endif()
endif()
# options to set
option(hueplusplus_TESTS "Build tests" OFF)
option(hueplusplus_EXAMPLES "Build examples" OFF)
option(hueplusplus_NO_EXTERNAL_LIBRARIES "Do not try to use external libraries" OFF)
# Try to find installed packages
if(NOT hueplusplus_NO_EXTERNAL_LIBRARIES)
# Suppress warnings if libraries are not found, they will be built from submodules
find_package(MbedTLS QUIET)
find_package(nlohmann_json QUIET)
endif()
set(NEED_SUBMODULES NOT (${MbedTLS_FOUND} AND ${nlohmann_json_FOUND}))
option(CLANG_TIDY_FIX "Perform fixes for Clang-Tidy" OFF)
find_program(CLANG_TIDY_EXE NAMES "clang-tidy" DOC "Path to clang-tidy executable")
if(CLANG_TIDY_EXE)
if(CLANG_TIDY_FIX)
set(DO_CLANG_TIDY "${CLANG_TIDY_EXE}" "-fix")
else()
set(DO_CLANG_TIDY "${CLANG_TIDY_EXE}")
endif()
endif()
# update submodules
find_package(Git QUIET)
if(GIT_FOUND AND EXISTS "${PROJECT_SOURCE_DIR}/.git")
option(GIT_SUBMODULE "Check submodules during build" ON)
if(GIT_SUBMODULE AND NEED_SUBMODULES)
message(STATUS "Submodule update")
execute_process(COMMAND ${GIT_EXECUTABLE} submodule update --init --recursive WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
RESULT_VARIABLE GIT_SUBMOD_RESULT)
if(NOT GIT_SUBMOD_RESULT EQUAL "0")
message(FATAL_ERROR "git submodule update --init failed with ${GIT_SUBMOD_RESULT}, please checkout submodules")
endif()
endif()
endif()
# Set default build type if none was specified
set(default_build_type "Release")
if(hueplusplus_master_project AND (NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES))
message(STATUS "Setting build type to '${default_build_type}' as none was specified")
set(CMAKE_BUILD_TYPE "${default_build_type}" CACHE STRING "Choose the type of build." FORCE)
# Set possible values for cmake-gui
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS
"Debug" "Release" "MinSizeRel" "RelWithDebInfo")
endif()
# get the correct installation directory for add_library() to work
if(WIN32 AND NOT CYGWIN)
set(DEF_INSTALL_CMAKE_DIR cmake)
else()
set(DEF_INSTALL_CMAKE_DIR lib/cmake/hueplusplus)
endif()
set(INSTALL_CMAKE_DIR ${DEF_INSTALL_CMAKE_DIR} CACHE PATH "Installation directory for CMake files")
# target for uninstall
if(NOT TARGET uninstall)
configure_file(
"${PROJECT_SOURCE_DIR}/cmake/cmake_uninstall.cmake.in"
"${PROJECT_BINARY_DIR}/cmake_uninstall.cmake"
IMMEDIATE @ONLY)
add_custom_target(uninstall
COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake)
endif()
# if we are on a apple machine this is needed
if (1 AND APPLE)
set(CMAKE_MACOSX_RPATH 1)
endif()
if(NOT MbedTLS_FOUND)
# Build mbedtls if not installed
message(STATUS "MbedTLS was not found, the submodule is used.")
set(USE_STATIC_MBEDTLS_LIBRARY ON)
set(USE_SHARED_MBEDTLS_LIBRARY OFF)
add_subdirectory("lib/mbedtls" EXCLUDE_FROM_ALL)
# Compile the mbedtls library as a static with position independent code,
# because we need it for both a shared and static library
set_property(TARGET mbedtls PROPERTY POSITION_INDEPENDENT_CODE ON)
set_property(TARGET mbedcrypto PROPERTY POSITION_INDEPENDENT_CODE ON)
set_property(TARGET mbedx509 PROPERTY POSITION_INDEPENDENT_CODE ON)
if(CMAKE_VERSION VERSION_LESS 3.18)
# Aliases for compatibility with find_package, newer cmake versions add these already
add_library(MbedTLS::mbedtls ALIAS mbedtls)
add_library(MbedTLS::mbedcrypto ALIAS mbedcrypto)
add_library(MbedTLS::mbedx509 ALIAS mbedx509)
endif()
endif()
if(NOT nlohmann_json_FOUND)
# Use embedded json
message(STATUS "nlohmann_json was not found, the submodule is used.")
# disable tests for json
set(JSON_BuildTests OFF CACHE INTERNAL "")
add_subdirectory("lib/json" EXCLUDE_FROM_ALL)
endif()
add_subdirectory(src)
# if the user decided to use tests add the subdirectory
if(hueplusplus_TESTS)
add_subdirectory("test")
endif()
if(hueplusplus_EXAMPLES)
add_subdirectory("examples")
endif()
File diff suppressed because it is too large Load Diff
+165
View File
@@ -0,0 +1,165 @@
GNU LESSER GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
This version of the GNU Lesser General Public License incorporates
the terms and conditions of version 3 of the GNU General Public
License, supplemented by the additional permissions listed below.
0. Additional Definitions.
As used herein, "this License" refers to version 3 of the GNU Lesser
General Public License, and the "GNU GPL" refers to version 3 of the GNU
General Public License.
"The Library" refers to a covered work governed by this License,
other than an Application or a Combined Work as defined below.
An "Application" is any work that makes use of an interface provided
by the Library, but which is not otherwise based on the Library.
Defining a subclass of a class defined by the Library is deemed a mode
of using an interface provided by the Library.
A "Combined Work" is a work produced by combining or linking an
Application with the Library. The particular version of the Library
with which the Combined Work was made is also called the "Linked
Version".
The "Minimal Corresponding Source" for a Combined Work means the
Corresponding Source for the Combined Work, excluding any source code
for portions of the Combined Work that, considered in isolation, are
based on the Application, and not on the Linked Version.
The "Corresponding Application Code" for a Combined Work means the
object code and/or source code for the Application, including any data
and utility programs needed for reproducing the Combined Work from the
Application, but excluding the System Libraries of the Combined Work.
1. Exception to Section 3 of the GNU GPL.
You may convey a covered work under sections 3 and 4 of this License
without being bound by section 3 of the GNU GPL.
2. Conveying Modified Versions.
If you modify a copy of the Library, and, in your modifications, a
facility refers to a function or data to be supplied by an Application
that uses the facility (other than as an argument passed when the
facility is invoked), then you may convey a copy of the modified
version:
a) under this License, provided that you make a good faith effort to
ensure that, in the event an Application does not supply the
function or data, the facility still operates, and performs
whatever part of its purpose remains meaningful, or
b) under the GNU GPL, with none of the additional permissions of
this License applicable to that copy.
3. Object Code Incorporating Material from Library Header Files.
The object code form of an Application may incorporate material from
a header file that is part of the Library. You may convey such object
code under terms of your choice, provided that, if the incorporated
material is not limited to numerical parameters, data structure
layouts and accessors, or small macros, inline functions and templates
(ten or fewer lines in length), you do both of the following:
a) Give prominent notice with each copy of the object code that the
Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the object code with a copy of the GNU GPL and this license
document.
4. Combined Works.
You may convey a Combined Work under terms of your choice that,
taken together, effectively do not restrict modification of the
portions of the Library contained in the Combined Work and reverse
engineering for debugging such modifications, if you also do each of
the following:
a) Give prominent notice with each copy of the Combined Work that
the Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the Combined Work with a copy of the GNU GPL and this license
document.
c) For a Combined Work that displays copyright notices during
execution, include the copyright notice for the Library among
these notices, as well as a reference directing the user to the
copies of the GNU GPL and this license document.
d) Do one of the following:
0) Convey the Minimal Corresponding Source under the terms of this
License, and the Corresponding Application Code in a form
suitable for, and under terms that permit, the user to
recombine or relink the Application with a modified version of
the Linked Version to produce a modified Combined Work, in the
manner specified by section 6 of the GNU GPL for conveying
Corresponding Source.
1) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (a) uses at run time
a copy of the Library already present on the user's computer
system, and (b) will operate properly with a modified version
of the Library that is interface-compatible with the Linked
Version.
e) Provide Installation Information, but only if you would otherwise
be required to provide such information under section 6 of the
GNU GPL, and only to the extent that such information is
necessary to install and execute a modified version of the
Combined Work produced by recombining or relinking the
Application with a modified version of the Linked Version. (If
you use option 4d0, the Installation Information must accompany
the Minimal Corresponding Source and Corresponding Application
Code. If you use option 4d1, you must provide the Installation
Information in the manner specified by section 6 of the GNU GPL
for conveying Corresponding Source.)
5. Combined Libraries.
You may place library facilities that are a work based on the
Library side by side in a single library together with other library
facilities that are not Applications and are not covered by this
License, and convey such a combined library under terms of your
choice, if you do both of the following:
a) Accompany the combined library with a copy of the same work based
on the Library, uncombined with any other library facilities,
conveyed under the terms of this License.
b) Give prominent notice with the combined library that part of it
is a work based on the Library, and explaining where to find the
accompanying uncombined form of the same work.
6. Revised Versions of the GNU Lesser General Public License.
The Free Software Foundation may publish revised and/or new versions
of the GNU Lesser General Public License from time to time. Such new
versions will be similar in spirit to the present version, but may
differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the
Library as you received it specifies that a certain numbered version
of the GNU Lesser General Public License "or any later version"
applies to it, you have the option of following the terms and
conditions either of that published version or of any later version
published by the Free Software Foundation. If the Library as you
received it does not specify a version number of the GNU Lesser
General Public License, you may choose any version of the GNU Lesser
General Public License ever published by the Free Software Foundation.
If the Library as you received it specifies that a proxy can decide
whether future versions of the GNU Lesser General Public License shall
apply, that proxy's public statement of acceptance of any version is
permanent authorization for you to choose that version for the
Library.
+165
View File
@@ -0,0 +1,165 @@
# hueplusplus
A simple and easy to use library for Philips Hue Lights
| Branch | CI | Codecov | LGTM
|:-|:-|:-|:-|
| [Master](https://github.com/enwi/hueplusplus/tree/master) | [![CI](https://github.com/enwi/hueplusplus/actions/workflows/build.yml/badge.svg?branch=master)](https://github.com/enwi/hueplusplus/actions/workflows/build.yml)| [![codecov](https://codecov.io/gh/enwi/hueplusplus/branch/master/graph/badge.svg)](https://codecov.io/gh/enwi/hueplusplus) | [![Language grade: C/C++](https://img.shields.io/lgtm/grade/cpp/g/enwi/hueplusplus.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/enwi/hueplusplus/context:cpp) |
| [Development](https://github.com/enwi/hueplusplus/tree/development) | [![CI](https://github.com/enwi/hueplusplus/actions/workflows/build.yml/badge.svg?branch=development)](https://github.com/enwi/hueplusplus/actions/workflows/build.yml) | [![codecov](https://codecov.io/gh/enwi/hueplusplus/branch/development/graph/badge.svg)](https://codecov.io/gh/enwi/hueplusplus) | |
## Features
* find bridges with SSDP or set an ip manually
* all common light functions (brightness, color, temperature)
* extended alert() functions, which alert in a specific color (good for notifications)
* supports sensors, rules, groups, scenes and schedules
* streaming with entertainment mode
* [documented with doxygen](https://enwi.github.io/hueplusplus/)
* tested with google test, google mock and gcov/lcov
## Compatibility
* Linux
* Windows
* MacOS
* Espressif ESP32 SDK & Arduino
## How to use
### <a name="searchingBridges"></a>Searching for Bridges
To start searching for a Hue Bridge you will need to choose an IHttpHandler and create one. The options are a "WinHttpHandler" (for windows) or a "LinHttpHandler" (for linux).
Then create a BridgeFinder object with the handler.
The handler is needed, because it tells the finder which functions to use to communicate with a bridge or your local network.
After that you can call findBridges(), which will return a vector containing the ip and mac address of all found Bridges.
```C++
// For windows use std::make_shared<hueplusplus::WinHttpHandler>();
handler = std::make_shared<hueplusplus::LinHttpHandler>();
hueplusplus::BridgeFinder finder(handler);
std::vector<hueplusplus::BridgeFinder::BridgeIdentification> bridges = finder.findBridges();
if (bridges.empty())
{
std::cerr << "No bridges found\n";
return;
}
```
### Authenticate Bridges
If you have found the Bridge you were looking for, you can then move on with the authentication process.
To get a new username from the Bridge (for now) you simply call getBridge(bridges[\<index\>]),
where index is your preferred Bridge from the part [Searching for Bridges](#searchingBridges).
```C++
hueplusplus::Bridge bridge = finder.getBridge(bridges[0]);
```
If you on the other hand already have a username you can add your bridge like so
```C++
finder.addUsername(bridges[0].mac, "<username>");
hueplusplus::Bridge bridge = finder.getBridge(bridges[0]);
```
If you do not want to use the BridgeFinder or you already know the ip and username of your bridge you have the option to create your own Bridge object.
Here you will need to provide the ip address, the port number, a username and an HttpHandler
```C++
// For windows use std::make_shared<hueplusplus::WinHttpHandler>();
handler = std::make_shared<hueplusplus::LinHttpHandler>();
hueplusplus::Bridge bridge("192.168.2.102", 80, "<username>", handler);
```
### Controlling lights
If you have your Bridge all set up, you can now control its lights.
For that create a new Light object and call lights().get(\<id\>) on your bridge object to get a reference to a specific light, where id
is the id of the light set internally by the Hue Bridge.
```C++
hueplusplus::Light light1 = bridge.lights().get(1);
```
If you don't know the id of a specific light or want to get an overview over all lights that are controlled by your bridge,
you can get a vector containing them by calling getAll(). If no lights are found the vector will be empty.
```C++
std::vector<hueplusplus::Light> lights = bridge.lights().getAll();
```
If you now want to control a light, call a specific function of it.
```C++
light1.on();
light1.setBrightness(120);
light1.alertHueSaturation(25500, 255);
light1.setColorLoop(true);
light1.setColorRGB(255, 128, 0);
lights[1].off();
lights.at(1).setColorHue(4562);
```
But keep in mind that some light types do not have all functions available. So you might call a
specific function, but nothing will happen. For that you might want to check what type
of a light you are controlling. For that you can call the function getColorType(), which will return
a ColorType.
```C++
hueplusplus::ColorType type1 = light1.getColorType();
```
There's also a new way to check whether specific functions of a light are available:
```C++
light1.hasBrightnessControl();
light1.hasTemperatureControl();
light1.hasColorControl();
```
These will either return true(light has specified function) or false(light lacks specified function).
### Further reading
If you want to know more about all functions just look inside the doxygen documentation. It can be found [here](https://enwi.github.io/hueplusplus/)
## Build and install
### Basic installation
If you want to build the library you can use cmake (at least version 3.8). First create a build folder and then execute cmake.
```bash
mkdir build
cd build
cmake ..
```
Then compile the code with make. If you are inpatient use the option -j\<number\>, where number specifies how many files are compiled at the same time. Note this number should not exceed the number of cores*2 of your machine.
```bash
make
```
```bash
make -j4
```
If you want to install the library use
```bash
make install
```
To remove it
```bash
make uninstall
```
### Advanced usage
If you have a project that already uses CMake you probably want to add the hueplusplus library directly in your cmake file.
For that the best way is to use find_package().
When cmake finds the hueplusplus library you can then link against either the shared or static version of the library.
```cmake
find_package(hueplusplus REQUIRED)
target_link_libraries(<executable> PUBLIC hueplusplusstatic)
```
But this will only work if the hueplusplus library is already installed.
To get around this problem there is a pretty awesome way.
If you have the hueplusplus repository included in your project repository (as a submodule) or know where the folder lives you can do the following:
```cmake
find_package(hueplusplus QUIET)
if(NOT hueplusplus_FOUND)
message(STATUS "-- hueplusplus not found, building it")
add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/<path to directory>/hueplusplus" "${CMAKE_CURRENT_BINARY_DIR}/hueplusplus")
endif()
target_link_libraries(<executable> PUBLIC hueplusplusstatic)
```
This will check if the hueplusplus library was found by find_package() and if not it will use the specified path to the library source and compile it during the build process.
### Running tests
If you additionally want to run the tests use cmake with the option -Dhueplusplus_TESTS=ON. Testing is done with Google gtest and gmock. Note that you wont need to install gtest/gmock yourself, because cmake will automatically download them and include them during the build. Since I added a custom target you will only need to call "make unittest" and the tests are compiled and executed.
```bash
mkdir build
cd build
cmake .. -Dhueplusplus_TESTS=ON
make unittest
```
If you also want to execute coverage tests you will need to install gcov and lcov yourself. To run the coverage test use
```bash
make coveragetest
```
## Copyright
Copyright (c) 2017 Jan Rogall & Moritz Wirger. See LICENSE for further details.
@@ -0,0 +1,21 @@
if(NOT EXISTS "@CMAKE_BINARY_DIR@/install_manifest.txt")
message(FATAL_ERROR "Cannot find install manifest: @CMAKE_BINARY_DIR@/install_manifest.txt")
endif(NOT EXISTS "@CMAKE_BINARY_DIR@/install_manifest.txt")
file(READ "@CMAKE_BINARY_DIR@/install_manifest.txt" files)
string(REGEX REPLACE "\n" ";" files "${files}")
foreach(file ${files})
message(STATUS "Uninstalling $ENV{DESTDIR}${file}")
if(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}")
exec_program(
"@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\""
OUTPUT_VARIABLE rm_out
RETURN_VALUE rm_retval
)
if(NOT "${rm_retval}" STREQUAL 0)
message(FATAL_ERROR "Problem when removing $ENV{DESTDIR}${file}")
endif(NOT "${rm_retval}" STREQUAL 0)
else(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}")
message(STATUS "File $ENV{DESTDIR}${file} does not exist.")
endif(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}")
endforeach(file)
@@ -0,0 +1,7 @@
# -config file of the hueplusplus package
# It defines the following
# hueplusplus_FOUND - which indicates that the module was found
# hueplusplus_INCLUDE_DIR
set(hueplusplus_FOUND TRUE)
set(hueplusplus_INCLUDE_DIR "@INSTALL_INCLUDE_DIR@")
+8
View File
@@ -0,0 +1,8 @@
ignore:
- "/usr/**/*" # ingnore all unix system ressources
- "**/json*.*" # ignore all json files
- "**/hueplusplus/include/json/*" # ignore all files in json folder
- "**/hueplusplus/test/*" # ignore all files in test folder
- "**/hueplusplus/build/**/*" # ignore all files in build folder
- "**/test/**/*" # ignore all files in test folders
- "**/v1/**/*" # ignore all macros files
+90
View File
@@ -0,0 +1,90 @@
# Build and install {#build}
[TOC]
# Basic installation {#basic-install}
## Clone from github {#clone}
To get the newest version of the hueplusplus library, clone it directly from [github](https://github.com/enwi/hueplusplus).
The master branch contains the latest tested and stable version, while the development branch is more unstable.
```{.sh}
~ $ git clone https://github.com/enwi/hueplusplus.git
```
This creates a folder hueplusplus with the library sources.
When you want to update the library for a new version, use pull with rebase.
```{.sh}
~/hueplusplus $ git pull --rebase
```
## Build with CMake {#build-cmake}
To build the library, you need to use [CMake](https://cmake.org) version 3.10 or higher.
It is easiest to create a separate build directory where the build files are stored.
```{.sh}
~/hueplusplus $ mkdir build
~/hueplusplus $ cd build
~/hueplusplus/build $ cmake ..
~/hueplusplus/build $ make
```
To install or uninstall the library use the make targets.
```{.sh}
~/hueplusplus/build $ make install
~/hueplusplus/build $ make uninstall
```
## Use in a CMake project {#import-cmake}
If you have a project that already uses CMake you probably want to add the hueplusplus library directly in your cmake file.
For that the best way is to use find_package().
```{.cmake}
find_package(hueplusplus REQUIRED)
```
But this will only work if the hueplusplus library is already installed.
Instead, if you have the hueplusplus repository included in your project repository (as a submodule) or know where the folder lives you can do the following:
```{.cmake}
find_package(hueplusplus QUIET)
if(NOT hueplusplus_FOUND)
message(STATUS "-- hueplusplus not found, building it")
add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/<path to directory>/hueplusplus" "${CMAKE_CURRENT_BINARY_DIR}/hueplusplus")
endif()
```
This will check if the hueplusplus library was found by find_package() and if not it will use the specified path to the library source and compile it during the build process.
The cmake project defines two library targets: `hueplusplusstatic` to link as a static library and `hueplusplusshared` to link as a shared library.
```{.cmake}
target_link_libraries(<executable> PUBLIC hueplusplusstatic)
```
## Use in another project {#import-other}
When you are not using CMake, you have to install hueplusplus and change your build configuration to link to the compiled library.
The header files in the include directory need to be added to the include path.
How you do this depends on the build system.
## Building tests {#build-tests}
If you additionally want to run the tests use cmake with the option -Dhueplusplus_TESTS=ON. Testing is done with Google gtest and gmock. Note that you wont need to install gtest/gmock yourself, because cmake will automatically download them and include them during the build.
The custom target `unittest` compiles and executes all tests.
```bash
mkdir build
cd build
cmake .. -Dhueplusplus_TESTS=ON
make unittest
```
If you also want to execute coverage tests you will need to install gcov and lcov yourself. To run the coverage test use
```bash
make coveragetest
```
## Building examples {#build-examples}
There are some small [example programs](@ref examples) using this library in the examples folder. To build them,
set `hueplusplus_EXAMPLES=ON`. The target `hueplusplus_examples` builds all examples into build/examples.
```{.sh}
mkdir build
cd build
cmake .. -Dhueplusplus_EXAMPLES=ON
make hueplusplus_examples
```
## External libraries
Hueplusplus requires a few external libraries (e.g. Mbed TLS and GTest), which are included automatically. If these are pre-installed on your system, those versions will be used by default. This can potentially cause issues if your installed versions are incompatible.
In this case, set `hueplusplus_NO_EXTERNAL_LIBRARIES=ON` to force using the embedded versions instead of the installed libraries.
@@ -0,0 +1,39 @@
# Getting started {#getting-started}
## Creating the Hue bridge
To start searching for a Hue Bridge you will need to choose an IHttpHandler and create one.
The options are a [WinHttpHandler](@ref hueplusplus::WinHttpHandler) (for windows) or a [LinHttpHandler](@ref hueplusplus::LinHttpHandler) (for linux or linux-like).
Then create a [BridgeFinder](@ref hueplusplus::BridgeFinder) object with the handler.
The handler is needed, because it tells the finder which functions to use to communicate with a bridge or your local network.
After that you can call [findBridges()](@ref hueplusplus::BridgeFinder::findBridges), which will return a vector containing the ip and mac address of all found Bridges.
\snippet Snippets.cpp search-bridge
## Authenticate Bridges
If you have found the Bridge you were looking for, you can then move on with the authentication process.
To get a new username from the Bridge (for now) you simply call [getBridge(bridges[\<index\>])](@ref hueplusplus::BridgeFinder::getBridge),
where index is your preferred Bridge from the part [Searching for Bridges](#searchingBridges). This requires the user to press the link button.
\snippet Snippets.cpp get-bridge-1
If you on the other hand already have a username you can add your bridge like so
\snippet Snippets.cpp get-bridge-2
If you do not want to use the BridgeFinder or you already know the ip and username of your bridge you have the option to create your own Hue object.
Here you will need to provide the ip address, the port number, a username and an HttpHandler
\snippet Snippets.cpp get-bridge-3
At this point you may want to decide whether to use a [shared state](@ref shared-state) cache model or keep the default settings.
### Controlling lights
\snippet Snippets.cpp control-lights
Use [transactions](@ref transactions) to change multiple properties at once.
### Controlling groups
\snippet Snippets.cpp control-groups
## More information
- [Transactions](@ref transactions)
- [Shared state cache](@ref shared-state)
+139
View File
@@ -0,0 +1,139 @@
# Documentation for the hueplusplus library
A simple and easy to use library for Philips Hue Lights.
[TOC]
## Features
* find bridges with SSDP or set an ip manually
* all common light functions (brightness, color, temperature)
* extended alert() functions, which alert in a specific color (good for notifications)
* supports sensors, rules, groups, scenes and schedules
* streaming with entertainment mode
* documented with doxygen
* tested with google test, google mock and gcov/lcov
## Compatibility
* Linux
* Windows
* MacOS
* Espressif ESP32 SDK & Arduino
## How to use
[Getting started](@ref getting-started)
### Searching for Bridges
To start searching for a Hue Bridge you will need to choose an IHttpHandler and create one. The options are a [WinHttpHandler](@ref hueplusplus::WinHttpHandler) (for windows) or a [LinHttpHandler](@ref hueplusplus::LinHttpHandler) (for linux or linux-like).
Then create a [BridgeFinder](@ref hueplusplus::BridgeFinder) object with the handler.
The handler is needed, because it tells the finder which functions to use to communicate with a bridge or your local network.
After that you can call [findBridges()](@ref hueplusplus::BridgeFinder::findBridges), which will return a vector containing the ip and mac address of all found Bridges.
\snippet Snippets.cpp search-bridge
At this point you may want to decide whether to use a [shared state](@ref shared-state) cache model or keep the default settings.
### Authenticate Bridges
If you have found the Bridge you were looking for, you can then move on with the authentication process.
To get a new username from the Bridge (for now) you simply call [getBridge(bridges[\<index\>])](@ref hueplusplus::BridgeFinder::getBridge),
where index is your preferred Bridge from the part [Searching for Bridges](#searchingBridges). This requires the user to press the link button.
\snippet Snippets.cpp get-bridge-1
If you on the other hand already have a username you can add your bridge like so
\snippet Snippets.cpp get-bridge-2
If you do not want to use the BridgeFinder or you already know the ip and username of your bridge you have the option to create your own Bridge object.
Here you will need to provide the ip address, the port number, a username and an HttpHandler
\snippet Snippets.cpp get-bridge-3
### Controlling lights
If you have your Bridge all set up, you can now control its lights.
For that create a new Light object and call [lights().get(\<id\>)](@ref hueplusplus::ResourceList::get) on your bridge object to get a reference to a specific light, where id
is the id of the light set internally by the Hue Bridge.
\snippet Snippets.cpp light-1
If you don't know the id of a specific light or want to get an overview over all lights that are controlled by your bridge,
you can get a vector containing them by calling [getAll()](@ref hueplusplus::ResourceList::getAll) on your bridge object. If no lights are found the vector will be empty.
\snippet Snippets.cpp light-2
If you now want to control a light, call a specific function of it.
\snippet Snippets.cpp light-3
But keep in mind that some light types do not have all functions available. So you might call a
specific function, but nothing will happen. For that you might want to check what type
of a light you are controlling. For that you can call the function [getColorType()](@ref hueplusplus::Light::getColorType()), which will return
a ColorType.
\snippet Snippets.cpp light-4
There's also a new way to check whether specific functions of a light are available:
\snippet Snippets.cpp light-5
These will either return true(light has specified function) or false(light lacks specified function).
## Build and install
[Build and install guide](@ref build)
### Basic installation
If you want to build the library you can use cmake (at least version 3.8). First create a build folder and then execute cmake.
```bash
mkdir build
cd build
cmake ..
```
Then compile the code with make. If you are inpatient use the option -j\<number\>, where number specifies how many files are compiled at the same time. Note this number should not exceed the number of cores*2 of your machine.
```bash
make
```
```bash
make -j4
```
If you want to install the library use
```bash
make install
```
To remove it
```bash
make uninstall
```
### Advanced usage
If you have a project that already uses CMake you probably want to add the hueplusplus library directly in your cmake file.
For that the best way is to use find_package().
When cmake finds the hueplusplus library you can then link against either the shared or static version of the library.
```cmake
find_package(hueplusplus REQUIRED)
target_link_libraries(<executable> PUBLIC hueplusplusstatic)
```
But this will only work if the hueplusplus library is already installed.
To get around this problem there is a pretty awesome way.
If you have the hueplusplus repository included in your project repository (as a submodule) or know where the folder lives you can do the following:
```cmake
find_package(hueplusplus QUIET)
if(NOT hueplusplus_FOUND)
message(STATUS "-- hueplusplus not found, building it")
add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/<path to directory>/hueplusplus" "${CMAKE_CURRENT_BINARY_DIR}/hueplusplus")
endif()
target_link_libraries(<executable> PUBLIC hueplusplusstatic)
```
This will check if the hueplusplus library was found by find_package() and if not it will use the specified path to the library source and compile it during the build process.
### Running tests
If you additionally want to run the tests use cmake with the option -Dhueplusplus_TESTS=ON. Testing is done with Google gtest and gmock. Note that you wont need to install gtest/gmock yourself, because cmake will automatically download them and include them during the build. Since I added a custom target you will only need to call "make unittest" and the tests are compiled and executed.
```bash
mkdir build
cd build
cmake .. -Dhueplusplus_TESTS=ON
make unittest
```
If you also want to execute coverage tests you will need to install gcov and lcov yourself. To run the coverage test use
```bash
make coveragetest
```
## Other pages
- [Getting started](@ref getting-started)
- [Build and install](@ref build)
- [Shared state cache](@ref shared-state)
- [Transactions](@ref transactions)
- [Sensors](@ref sensors)
+47
View File
@@ -0,0 +1,47 @@
# Sensors {#sensors}
[TOC]
## Sensor support
The library supports the sensor types listed on the Hue developer documentation.
Include `hueplusplus/ZLLSensors.h` for ZigBee sensors and `hueplusplus/CLIPSensors.h` for CLIP sensors.
Other sensors can be used with the generic [Sensor](@ref hueplusplus::Sensor) class.
### Working with a known sensor
In most cases, the type of the sensors is known in advance, such as a switch.
The classes in the [sensors](@ref hueplusplus::sensors) namespace provide the documented
functionality. The type can be specified when accessing the sensor. When it does not match,
an exception is thrown.
\snippet Snippets.cpp known-sensor-1
You can also get all sensors of a specified type by using [getAllByType<T>()](@ref hueplusplus::SensorList::getAllByType).
\snippet Snippets.cpp known-sensor-2
### Working with an unknown sensor
When the sensor type is not known, use the generic sensor class. In this case, some attributes might not
exist, so they have to be checked first. This applies to all attributes that have a `hasXXX` method.
\snippet Snippets.cpp generic-sensor-1
It is easiest to compare the sensor type to the existing ones (`typeStr` on the specific sensor classes)
and then convert the sensor to that type.
\snippet Snippets.cpp generic-sensor-2
## ZLL sensors vs. CLIP sensors
ZLL sensors (defined in `ZLLSensors.h`) are physical device sensors which send their data
to the bridge using ZigBee. They are added in the same way as lights are, using [search()](@ref hueplusplus::SearchableResourceList::search).
CLIP sensors (in `CLIPSensors.h`) are added using [create()](@ref hueplusplus::CreateableResourceList::create) with [CreateSensor](@ref hueplusplus::CreateSensor)
for parameters. In general, which config and state attributes exist is specified when the sensor is created.
The values of CLIP sensors can be changed using requests, unlike ZLL sensors. They can also have a URL to query from.
## Creating conditions
The most important use for sensors is in [Rules](@ref hueplusplus::Rule), to trigger changes.
Conditions can be created from the specific sensor types using `makeCondition()`.
These functions return a helper class with methods for the [possible operators](@ref hueplusplus::Condition::Operator) valid for the state.
For some sensors, which have multiple possible states, there exist multiple variations of makeCondition.
\snippet Snippets.cpp sensor-conditions
For generic sensors, the conditions must be created manually using the [Condition](@ref hueplusplus::Condition::Condition)
constructor with a proper address to the sensor state.
@@ -0,0 +1,46 @@
# Shared state cache {#shared-state}
[TOC]
## What shared state means
There are two ways in which the API state (internally JSON) can be handled:
1. Every resource instance holds its own cache of the state (default).
2. All instances share the cache of the entire bridge.
### Advantages of shared state
* Different resources are always consistent on the library level.
If one part of the code uses the light with id 1 and turns it off,
light 1 is also off when using a different variable to access it.
* The number of requests can be reduced, because they can be bundled together on a higher cache level.
### Disadvantages of shared state
* Different objects are no longer thread safe, you cannot use **any** parts of the library
from multiple threads (without locking).
* Changes are not transparent. For example, a `const Light` can suddenly change its name, because the
name was changed somewhere else in the code.
Because of these considerations, shared state is disabled by default.
## Enabling shared state
Shared state can be configured when the bridge is first constructed, either in [getBridge()](@ref hueplusplus::BridgeFinder::getBridge)
or in the [constructor](@ref hueplusplus::Bridge::Bridge). Set `sharedState` to `true` to keep all resources
connected to the bridge cache.
\snippet Snippets.cpp shared-bridge-1
\snippet Snippets.cpp shared-bridge-2
## Shared state and refreshing
When shared cache is used, refreshes use a hierarchichal structure to determine how much should be requested from the bridge.
Every level has its own last update time and refresh duration.
First, it is checked whether the higher level is up to date and refresh everything if not.
Otherwise, only the lowest necessary level is requested from the bridge to be more efficient.
### Example:
\snippet Snippets.cpp refresh-example
[isOn()](@ref hueplusplus::Light::isOn) is a non-const method (in this case). That means it will refresh the
state if it is outdated. The default refresh time is inherited from `bridge.lights()`, so it is 30 seconds.
After 30 seconds, the state of `light` *and* `bridge.lights()` is outdated. Therefore, the entire list of lights is
updated at this point.
After more than one minute, the bridge state is considered outdated. This means that `isOn()` causes an update of
the entire bridge.
@@ -0,0 +1,38 @@
# Transactions {#transactions}
[TOC]
## Using a transaction for lights
Often, you want to change more than one property on a light at the same time,
for example brightness and color. This is done using transactions ([StateTransaction](@ref hueplusplus::StateTransaction)).
\snippet Snippets.cpp transaction-lights
The request is reduced to only the variables that need to be changed based on the current state.
For example, if the light is already on, that part of the transaction is ignored.
__Important:__ The transaction has an internal reference to the light state.
You must not cause a refresh of the state between creating and committing the transaction (e.g. non-const getters/setters),
because that invalidates the reference.
### Advanced usage
Another way to use the transaction is by storing it and building up the calls separately.
\snippet Snippets.cpp transaction-advanced
In this case, it is especially important that the light and the state of the light __MUST NOT__ invalidate. That means
* the light variable has to live longer than the transaction
* especially no non-const method calls on the light while the transaction is open, or committing other transactions
In general, this method is easier to screw up and should only be used when really necessary.
## Using a transaction for groups
The same principles of transactions for lights also apply for groups. The main difference is that
for groups, there are no checks of the current state. Even if all lights in the group are already on,
the request to turn on all lights on the group is still sent.
\snippet Snippets.cpp transaction-groups
## Creating Actions
In a [Schedule](@ref hueplusplus::Schedule) or [Rule](@ref hueplusplus::Rule),
the bridge can set the state of lights and groups. To configure this, a transaction
can be saved for later instead of committing it directly.
\snippet Snippets.cpp transaction-action
+101
View File
@@ -0,0 +1,101 @@
/**
\file BridgeSetup.cpp
Copyright Notice\n
Copyright (C) 2021 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 <http://www.gnu.org/licenses/>.
\example{lineno} BridgeSetup.cpp
This example connects to a bridge with hardcoded mac and username.
**/
#include <algorithm>
#include <iostream>
#include <hueplusplus/Bridge.h>
#ifdef _MSC_VER
#include <hueplusplus/WinHttpHandler.h>
using SystemHttpHandler = hueplusplus::WinHttpHandler;
#else
#include <hueplusplus/LinHttpHandler.h>
using SystemHttpHandler = hueplusplus::LinHttpHandler;
#endif
namespace hue = hueplusplus;
// Configure existing connections here, or leave empty for new connection
const std::string macAddress = "";
const std::string username = "";
// Connects to a bridge and returns it.
hue::Bridge connectToBridge()
{
hue::BridgeFinder finder(std::make_shared<SystemHttpHandler>());
std::vector<hue::BridgeFinder::BridgeIdentification> bridges = finder.findBridges();
for (const auto& bridge : bridges)
{
std::cout << "Bridge: " << bridge.mac << " at " << bridge.ip << '\n';
}
if (bridges.empty())
{
std::cout << "Found no bridges\n";
throw std::runtime_error("no bridges found");
}
if (macAddress.empty())
{
std::cout << "No bridge given, connecting to first one.\n";
return finder.getBridge(bridges.front());
}
if (!username.empty())
{
finder.addUsername(macAddress, username);
}
auto it = std::find_if(
bridges.begin(), bridges.end(), [&](const auto& identification) { return identification.mac == macAddress; });
if (it == bridges.end())
{
std::cout << "Given bridge not found\n";
throw std::runtime_error("bridge not found");
}
return finder.getBridge(*it);
}
int main(int argc, char** argv)
{
try
{
hue::Bridge hue = connectToBridge();
std::cout << "Connected to bridge. IP: " << hue.getBridgeIP() << ", username: " << hue.getUsername() << '\n';
}
catch (...)
{ }
std::cout << "Press enter to exit\n";
std::cin.get();
return 0;
}
+24
View File
@@ -0,0 +1,24 @@
add_executable(bridge_setup BridgeSetup.cpp)
set_property(TARGET bridge_setup PROPERTY CXX_STANDARD 14)
set_property(TARGET bridge_setup PROPERTY CXX_EXTENSIONS OFF)
target_link_libraries(bridge_setup hueplusplusstatic)
add_executable(lights_off LightsOff.cpp)
set_property(TARGET lights_off PROPERTY CXX_STANDARD 14)
set_property(TARGET lights_off PROPERTY CXX_EXTENSIONS OFF)
target_link_libraries(lights_off hueplusplusstatic)
add_executable(username_config UsernameConfig.cpp)
set_property(TARGET lights_off PROPERTY CXX_STANDARD 14)
set_property(TARGET lights_off PROPERTY CXX_EXTENSIONS OFF)
target_link_libraries(username_config hueplusplusstatic)
add_custom_target(hueplusplus_examples)
add_dependencies(hueplusplus_examples bridge_setup lights_off username_config)
# Snippets for documentation, not included with the examples target
add_executable(hueplusplus_snippets Snippets.cpp)
set_property(TARGET hueplusplus_snippets PROPERTY CXX_STANDARD 14)
set_property(TARGET hueplusplus_snippets PROPERTY CXX_EXTENSIONS OFF)
target_link_libraries(hueplusplus_snippets hueplusplusstatic)
+136
View File
@@ -0,0 +1,136 @@
/**
\file LightsOff.cpp
Copyright Notice\n
Copyright (C) 2021 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 <http://www.gnu.org/licenses/>.
\example{lineno} LightsOff.cpp
This example turns off all lights for 20 seconds, then turns them on again.
**/
#include <thread>
#include <hueplusplus/Bridge.h>
#ifdef _MSC_VER
#include <hueplusplus/WinHttpHandler.h>
using SystemHttpHandler = hueplusplus::WinHttpHandler;
#else
#include <hueplusplus/LinHttpHandler.h>
using SystemHttpHandler = hueplusplus::LinHttpHandler;
#endif
namespace hue = hueplusplus;
// Configure existing connections here, or leave empty for new connection
const std::string macAddress = "";
const std::string username = "";
// Connects to a bridge and returns it.
hue::Bridge connectToBridge()
{
hue::BridgeFinder finder(std::make_shared<SystemHttpHandler>());
std::vector<hue::BridgeFinder::BridgeIdentification> bridges = finder.findBridges();
for (const auto& bridge : bridges)
{
std::cout << "Bridge: " << bridge.mac << " at " << bridge.ip << '\n';
}
if (bridges.empty())
{
std::cout << "Found no bridges\n";
throw std::runtime_error("no bridges found");
}
if (macAddress.empty())
{
std::cout << "No bridge given, connecting to first one.\n";
return finder.getBridge(bridges.front());
}
if (!username.empty())
{
finder.addUsername(macAddress, username);
}
auto it = std::find_if(
bridges.begin(), bridges.end(), [&](const auto& identification) { return identification.mac == macAddress; });
if (it == bridges.end())
{
std::cout << "Given bridge not found\n";
throw std::runtime_error("bridge not found");
}
return finder.getBridge(*it);
}
// Turns off the lights on the bridge for 20 seconds.
// Only turns the lights back on that were on before.
void lightsOff(hue::Bridge& hue)
{
std::vector<hue::Light> lights = hue.lights().getAll();
// Save current on state of the lights
std::map<int, bool> onMap;
for (hue::Light& l : lights)
{
onMap.emplace(l.getId(), l.isOn());
l.off();
}
// This would be preferrable, but does not work because it also resets the brightness of all lights
// Group 0 contains all lights, turn all off with a transition of 1 second
// hue.groups().get(0).setOn(false, 10);
// -------------------------------------
std::cout << "Turned off all lights\n";
std::this_thread::sleep_for(std::chrono::seconds(20));
// Restore the original state of the lights
for (hue::Light& l : lights)
{
if (onMap[l.getId()])
{
l.on();
}
}
std::cout << "Turned lights back on\n";
}
int main(int argc, char** argv)
{
try
{
hue::Bridge hue = connectToBridge();
std::cout << "Connected to bridge. IP: " << hue.getBridgeIP() << ", username: " << hue.getUsername() << '\n';
lightsOff(hue);
}
catch (...)
{ }
std::cout << "Press enter to exit\n";
std::cin.get();
return 0;
}
+199
View File
@@ -0,0 +1,199 @@
/**
\file Snippets.cpp
Copyright Notice\n
Copyright (C) 2021 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 <http://www.gnu.org/licenses/>.
\brief Contains code snippets used in the documentation, performs no useful functions.
**/
#include <hueplusplus/Bridge.h>
#include <hueplusplus/CLIPSensors.h>
#include <hueplusplus/ZLLSensors.h>
#ifdef _MSC_VER
#include <hueplusplus/WinHttpHandler.h>
namespace hueplusplus
{
// Dirty hack to make the snippets compile under windows. Dont do this,
// instead use your own alias which is set to either type like in BridgeSetup.cpp!
using LinHttpHandler = WinHttpHandler;
} // namespace hueplusplus
#else
#include <hueplusplus/LinHttpHandler.h>
#endif
void snippet1()
{
// Main page
//! [search-bridge]
// For windows use std::make_shared<hueplusplus::WinHttpHandler>();
auto handler = std::make_shared<hueplusplus::LinHttpHandler>();
hueplusplus::BridgeFinder finder(handler);
std::vector<hueplusplus::BridgeFinder::BridgeIdentification> bridges = finder.findBridges();
if (bridges.empty())
{
std::cerr << "No bridges found\n";
return;
}
//! [search-bridge]
//! [get-bridge-2]
finder.addUsername(bridges[0].mac, "<username>");
//! [get-bridge-1]
hueplusplus::Bridge bridge = finder.getBridge(bridges[0]);
//! [get-bridge-1]
//! [get-bridge-2]
//! [light-1]
hueplusplus::Light light1 = bridge.lights().get(1);
//! [light-1]
//! [light-2]
std::vector<hueplusplus::Light> lights = bridge.lights().getAll();
//! [light-2]
//! [light-3]
light1.on();
light1.setBrightness(120);
light1.alertHueSaturation({25500, 254});
light1.setColorLoop(true);
light1.setColorRGB({255, 128, 0});
lights[1].off();
lights.at(1).setColorHue(4562);
//! [light-3]
//! [light-4]
hueplusplus::ColorType type1 = light1.getColorType();
//! [light-4]
//! [light-5]
light1.hasBrightnessControl();
light1.hasTemperatureControl();
light1.hasColorControl();
//! [light-5]
// Getting started
//! [control-lights]
hueplusplus::Light light = bridge.lights().get(1);
light.on();
light.off();
//! [control-lights]
//! [control-groups]
hueplusplus::Group group = bridge.groups().get(1);
group.setOn(true);
//! [control-groups]
// Sensors
//! [sensor-conditions]
//! [known-sensor-1]
hueplusplus::sensors::ZLLSwitch switchSensor = bridge.sensors().getAsType<hueplusplus::sensors::ZLLSwitch>(2);
//! [known-sensor-1]
// ZLLSwitch conditions operate on `buttonEvent`, use makeConditionLastUpdate()
// to trigger on the last update time.
// Some examples:
hueplusplus::Condition upPressed
= makeCondition(switchSensor).eq(hueplusplus::sensors::ZLLSwitch::c_UP_INITIAL_PRESS);
hueplusplus::Condition buttonChanged = makeCondition(switchSensor).dx();
hueplusplus::time::TimeInterval interval(std::chrono::hours(12), std::chrono::hours(13));
hueplusplus::Condition updatedAtNoon = makeConditionLastUpdate(switchSensor).in(interval);
//! [sensor-conditions]
//! [known-sensor-2]
std::vector<hueplusplus::sensors::ZLLSwitch> allSwitches
= bridge.sensors().getAllByType<hueplusplus::sensors::ZLLSwitch>();
//! [known-sensor-2]
//! [generic-sensor-1]
hueplusplus::Sensor genericSensor = bridge.sensors().get(1);
if (genericSensor.hasOn())
{
// Now can check whether it is on
if (genericSensor.isOn())
{
// ...
}
}
//! [generic-sensor-1]
// Transactions
//! [transaction-lights]
light.transaction().setOn(true).setBrightness(29).setColorHue(3000).setColorSaturation(128).commit();
//! [transaction-lights]
bool shouldTurnOn = true;
//! [transaction-advanced]
hueplusplus::StateTransaction t = light.transaction();
if (shouldTurnOn)
t.setOn(true);
t.commit();
//! [transaction-advanced]
//! [transaction-groups]
group.transaction().setOn(true).setBrightness(64).commit();
//! [transaction-groups]
hueplusplus::Schedule schedule = bridge.schedules().get(1);
//! [transaction-action]
hueplusplus::Action action = light.transaction().setOn(true).setBrightness(254).toAction();
schedule.setCommand(action);
//! [transaction-action]
}
void snippet2()
{
// Main page
//! [get-bridge-3]
// For windows use std::make_shared<hueplusplus::WinHttpHandler>();
auto handler = std::make_shared<hueplusplus::LinHttpHandler>();
hueplusplus::Bridge bridge("192.168.2.102", 80, "<username>", handler);
//! [get-bridge-3]
// Sensors
//! [generic-sensor-2]
hueplusplus::Sensor genericSensor = bridge.sensors().get(1);
if (genericSensor.getType() == hueplusplus::sensors::ZLLSwitch::typeStr)
{
hueplusplus::sensors::ZLLSwitch switchSensor = genericSensor.asSensorType<hueplusplus::sensors::ZLLSwitch>();
// ...
}
//! [generic-sensor-2]
}
void snippet3()
{
// Shared state
auto handler = std::make_shared<hueplusplus::LinHttpHandler>();
hueplusplus::BridgeFinder finder(handler);
std::vector<hueplusplus::BridgeFinder::BridgeIdentification> bridges = finder.findBridges();
//! [shared-bridge-1]
hueplusplus::Bridge bridge = finder.getBridge(bridges[0], true);
//! [shared-bridge-1]
//! [refresh-example]
bridge.setRefreshDuration(std::chrono::minutes(1));
bridge.lights().setRefreshDuration(std::chrono::seconds(30));
hueplusplus::Light light = bridge.lights().get(1);
// ... wait some time
bool on = light.isOn();
//! [refresh-example]
}
void snippet4()
{
// Shared state
auto handler = std::make_shared<hueplusplus::LinHttpHandler>();
//! [shared-bridge-2]
hueplusplus::Bridge bridge("192.168.2.102", 80, "<username>", handler, "", std::chrono::seconds(10), true);
//! [shared-bridge-2]
}
int main(int argc, char** argv)
{
return 0;
}
@@ -0,0 +1,149 @@
/**
\file UsernameConfig.cpp
Copyright Notice\n
Copyright (C) 2021 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 <http://www.gnu.org/licenses/>.
\example{lineno} UsernameConfig.cpp
This example reads the username and mac address from a config file.
**/
#include <algorithm>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <hueplusplus/Bridge.h>
#ifdef _MSC_VER
#include <hueplusplus/WinHttpHandler.h>
using SystemHttpHandler = hueplusplus::WinHttpHandler;
#else
#include <hueplusplus/LinHttpHandler.h>
using SystemHttpHandler = hueplusplus::LinHttpHandler;
#endif
namespace hue = hueplusplus;
// Reads a json config file.
// filename: Path to the config file
// returns parsed json or an empty object if not successful.
nlohmann::json readConfigFile(const std::string& filename)
{
std::ifstream stream(filename);
try
{
nlohmann::json result = nlohmann::json::object();
if (stream)
{
stream >> result;
}
return result;
}
catch (const nlohmann::json::exception&)
{
// Ignore parse errors
return nlohmann::json::object();
}
}
// Saves a json file.
// filename: Path to the config file
// config: Json value to save
void saveConfigFile(const std::string& filename, const nlohmann::json& config)
{
std::ofstream stream(filename);
stream << std::setw(4) << config;
}
// Connects to a bridge and returns it
// username: Already existing username, can be left empty.
// macAddress: MAC address of the bridge, can be left empty.
// throws std::runtime_error when the bridge was not found.
// returns a connected bridge.
hue::Bridge connectToBridge(const std::string& username, const std::string& macAddress)
{
hue::BridgeFinder finder(std::make_shared<SystemHttpHandler>());
std::vector<hue::BridgeFinder::BridgeIdentification> bridges = finder.findBridges();
for (const auto& bridge : bridges)
{
std::cout << "Bridge: " << bridge.mac << " at " << bridge.ip << '\n';
}
if (bridges.empty())
{
std::cout << "Found no bridges\n";
throw std::runtime_error("no bridges found");
}
if (macAddress.empty())
{
std::cout << "No bridge given, connecting to first one.\n";
return finder.getBridge(bridges.front());
}
if (!username.empty())
{
finder.addUsername(macAddress, username);
}
auto it = std::find_if(
bridges.begin(), bridges.end(), [&](const auto& identification) { return identification.mac == macAddress; });
if (it == bridges.end())
{
std::cout << "Given bridge not found\n";
throw std::runtime_error("bridge not found");
}
return finder.getBridge(*it);
}
// Connects to a bridge. The steps are:
// - read "config.json" for an existing config
// - connect to the bridge
// - save the username to the config file for the next run
//
// Also prints out the IP and username.
int main(int argc, char** argv)
{
const std::string filename = "config.json";
try
{
nlohmann::json config = readConfigFile(filename);
const std::string username = config.value("username", "");
const std::string macAddress = config.value("mac", "");
hue::Bridge hue = connectToBridge(username, macAddress);
// Store updated information into file
config["username"] = hue.getUsername();
config["mac"] = hue.config().getMACAddress();
saveConfigFile(filename, config);
std::cout << "Connected to bridge. IP: " << hue.getBridgeIP() << ", username: " << hue.getUsername() << '\n';
}
catch (...)
{ }
std::cout << "Press enter to exit\n";
std::cin.get();
return 0;
}
@@ -0,0 +1,111 @@
/**
\file APICache.h
Copyright Notice\n
Copyright (C) 2020 Jan Rogall - developer\n
Copyright (C) 2020 Moritz Wirger - 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 <http://www.gnu.org/licenses/>.
**/
#ifndef INCLUDE_API_CACHE_H
#define INCLUDE_API_CACHE_H
#include <chrono>
#include <string>
#include "HueCommandAPI.h"
namespace hueplusplus
{
//! \brief Maximum duration, used to indicate that the cache should never be refreshed automatically.
constexpr std::chrono::steady_clock::duration c_refreshNever = std::chrono::steady_clock::duration::max();
//! \brief Caches API GET requests and refreshes regularly.
class APICache
{
public:
//! \brief Constructs APICache which forwards to a base cache
//! \param baseCache Base cache providing a parent state, must not be nullptr
//! \param subEntry Key of the child to use in the base cache
//! \param refresh Interval between cache refreshing. May be 0 to always refresh.
//! This is independent from the base cache refresh rate.
//!
//! Refreshes only part of the base cache.
APICache(
std::shared_ptr<APICache> baseCache, const std::string& subEntry, std::chrono::steady_clock::duration refresh);
//! \brief Constructs APICache with an own internal json cache
//! \param path URL appended after username, may be empty.
//! \param commands HueCommandAPI for making API requests.
//! \param refresh Interval between cache refreshing. May be 0 to always refresh.
//! \param initial Initial value, may be null. If present, assumes the value is up to date.
APICache(const std::string& path, const HueCommandAPI& commands, std::chrono::steady_clock::duration refresh,
const nlohmann::json& initial);
//! \brief Refresh cache now.
//! \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
//!
//! If there is a base cache, refreshes only the used part of that cache.
void refresh();
//! \brief Get cached value, refresh if necessary.
//! \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
nlohmann::json& getValue();
//! \brief Get cached value, does not refresh.
//! \throws HueException when no previous request was cached
const nlohmann::json& getValue() const;
//! \brief Set duration after which the cache is refreshed.
//! \param refreshDuration Interval between cache refreshing.
//! May be 0 to always refresh, or \ref c_refreshNever to never refresh.
//!
//! If the new refresh duration is exceeded, does not refresh immediately.
//! Instead, the next non-const getValue() call will refresh the value.
//! This is to reduce the number of unneccessary requests.
void setRefreshDuration(std::chrono::steady_clock::duration refreshDuration);
//! \brief Get duration between refreshes.
std::chrono::steady_clock::duration getRefreshDuration() const;
//! \brief Get HueCommandAPI used for requests
HueCommandAPI& getCommandAPI();
//! \brief Get HueCommandAPI used for requests
const HueCommandAPI& getCommandAPI() const;
//! \brief Get path the cache is refreshed from
//! \returns Request path as passed to HueCommandAPI::GETRequest
std::string getRequestPath() const;
private:
bool needsRefresh();
private:
std::shared_ptr<APICache> base;
std::string path;
HueCommandAPI commands;
std::chrono::steady_clock::duration refreshDuration;
std::chrono::steady_clock::time_point lastRefresh;
nlohmann::json value;
};
} // namespace hueplusplus
#endif
@@ -0,0 +1,73 @@
/**
\file Action.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 <http://www.gnu.org/licenses/>.
**/
#ifndef INCLUDE_HUEPLUSPLUS_ACTION_H
#define INCLUDE_HUEPLUSPLUS_ACTION_H
#include <nlohmann/json.hpp>
namespace hueplusplus
{
//! \brief Action executed by the bridge, e.g. as a Schedule command
//!
//! The action makes either a POST, PUT or DELETE request with a given body
//! to an address on the bridge.
//!
//! The Action can also be created by StateTransaction::toAction().
class Action
{
public:
//! \brief Create Action from json
//! \param json JSON object with address, method and body
explicit Action(const nlohmann::json& json);
//! \brief Method used for the command
enum class Method
{
post, //!< POST request
put, //!< PUT request
deleteMethod //!< DELETE request
};
//! \brief Get address the request is made to
std::string getAddress() const;
//! \brief Get request method
Method getMethod() const;
//! \brief Get request body
const nlohmann::json& getBody() const;
//! \brief Get json object of command
const nlohmann::json& toJson() const;
public:
//! \brief Parse Method from string
//! \param s \c POST, \c PUT or \c DELETE
static Method parseMethod(const std::string& s);
//! \brief Get string from Method
//! \returns \c POST, \c PUT or \c DELETE
static std::string methodToString(Method m);
private:
nlohmann::json json;
};
} // namespace hueplusplus
#endif
@@ -0,0 +1,159 @@
/**
\file BaseDevice.h
Copyright Notice\n
Copyright (C) 2017 Jan Rogall - developer\n
Copyright (C) 2017 Moritz Wirger - 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 <http://www.gnu.org/licenses/>.
**/
#ifndef INCLUDE_HUEPLUSPLUS_HUE_THING_H
#define INCLUDE_HUEPLUSPLUS_HUE_THING_H
#include <memory>
#include "APICache.h"
#include <nlohmann/json.hpp>
namespace hueplusplus
{
//! \brief Base class for physical devices connected to the bridge (sensor or light).
class BaseDevice
{
public:
//! \brief Virtual destructor
virtual ~BaseDevice() = default;
//! \brief Const function that returns the id of this device
//!
//! \return integer representing the device id
virtual int getId() const;
//! \brief Const function that returns the device type
//!
//! \return String containing the type
virtual std::string getType() const;
//! \brief Function that returns the name of the device.
//!
//! \return String containig the name of the device
//! \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
virtual std::string getName();
//! \brief Const function that returns the name of the device.
//!
//! \note This will not refresh the device state
//! \return String containig the name of the thing
virtual std::string getName() const;
//! \brief Const function that returns the modelid of the device
//!
//! \return String containing the modelid
virtual std::string getModelId() const;
//! \brief Const function that returns the uniqueid of the device
//!
//! \note Only working on bridges with versions starting at 1.4
//! \return String containing the uniqueid or an empty string when the function is not supported
virtual std::string getUId() const;
//! \brief Const function that returns the manufacturername of the device
//!
//! \note Only working on bridges with versions starting at 1.7
//! \return String containing the manufacturername or an empty string when the function is not supported
virtual std::string getManufacturername() const;
//! \brief Const function that returns the productname of the device
//!
//! \note Only working on bridges with versions starting at 1.24
//! \return String containing the productname or an empty string when the function is not supported
virtual std::string getProductname() const;
//! \brief Function that returns the software version of the device
//!
//! \return String containing the software version
//! \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
virtual std::string getSwVersion();
//! \brief Const function that returns the software version of the device
//!
//! \note This will not refresh the device state
//! \return String containing the software version
virtual std::string getSwVersion() const;
//! \brief Function that sets the name of the device
//!
//! \return Bool that is true on success
//! \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
virtual bool setName(const std::string& name);
//! \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
virtual void refresh(bool force = false);
//! \brief Sets custom refresh interval for this device.
//! \param refreshDuration The new minimum duration between refreshes. May be 0 or \ref c_refreshNever.
virtual void setRefreshDuration(std::chrono::steady_clock::duration refreshDuration);
protected:
//! \brief Protected ctor that is used by subclasses, construct with shared cache.
//! \param id Integer that specifies the id of this device
//! \param baseCache Cache of the ResourceList containing this device (must not be null).
BaseDevice(int id, const std::shared_ptr<APICache>& baseCache);
//! \brief Protected ctor that is used by subclasses.
//!
//! \param id Integer that specifies the id of this device
//! \param commands HueCommandAPI for communication with the bridge
//! \param path Base path for the resource type, ending with a '/'. Example: \c "/lights/"
//! \param refreshDuration Time between refreshing the cached state.
//! \param currentState Current state of the device, may be null.
BaseDevice(int id, const HueCommandAPI& commands, const std::string& path,
std::chrono::steady_clock::duration refreshDuration, const nlohmann::json& currentState);
//! \brief Utility function to send a put request to the device.
//!
//! \param subPath A path that is appended to the uri, note it should always start with a slash ("/")
//! \param request A nlohmann::json aka the request to send
//! \param fileInfo FileInfo from calling function for exception details.
//! \return The parsed reply
//! \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
virtual nlohmann::json sendPutRequest(const std::string& subPath, const nlohmann::json& request, FileInfo fileInfo);
protected:
int id; //!< holds the id of the device
APICache state; //!< holds the current state of the device
};
} // namespace hueplusplus
#endif
@@ -0,0 +1,178 @@
/**
\file BaseHttpHandler.h
Copyright Notice\n
Copyright (C) 2020 Jan Rogall - developer\n
Copyright (C) 2020 Moritz Wirger - 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 <http://www.gnu.org/licenses/>.
**/
#ifndef INCLUDE_HUEPLUSPLUS_BASE_HTTP_HANDLER_H
#define INCLUDE_HUEPLUSPLUS_BASE_HTTP_HANDLER_H
#include <iostream>
#include <memory>
#include <string>
#include <vector>
#include "IHttpHandler.h"
#include <nlohmann/json.hpp>
namespace hueplusplus
{
//! Base class for classes that handle http requests and multicast requests
class BaseHttpHandler : public IHttpHandler
{
public:
//! \brief Virtual dtor
virtual ~BaseHttpHandler() = default;
//! \brief Send a message to a specified host and return the body of the response.
//!
//! \param msg The message that should sent to the specified address
//! \param adr Ip or hostname in dotted decimal notation like "192.168.2.1"
//! \param port Optional port the request is sent to, default is 80
//! \return The body of the response of the host as a string
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
std::string sendGetHTTPBody(const std::string& msg, const std::string& adr, int port = 80) const override;
//! \brief Send a HTTP request with the given method to the specified host and return the body of the response.
//!
//! \param method HTTP method type e.g. GET, HEAD, POST, PUT, DELETE, ...
//! \param uri Uniform Resource Identifier in the request
//! \param contentType MIME type of the body data e.g. "text/html", "application/json", ...
//! \param body Request body, may be empty
//! \param adr Ip or hostname in dotted decimal notation like "192.168.2.1"
//! \param port Optional port the request is sent to, default is 80
//! \return Body of the response of the host
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
std::string sendHTTPRequest(const std::string& method, const std::string& uri, const std::string& contentType,
const std::string& body, const std::string& adr, int port = 80) const override;
//! \brief Send a HTTP GET request to the specified host and return the body of the response.
//!
//! \param uri Uniform Resource Identifier in the request
//! \param contentType MIME type of the body data e.g. "text/html", "application/json", ...
//! \param body Request body, may be empty
//! \param adr Ip or hostname in dotted decimal notation like "192.168.2.1"
//! \param port Optional port the request is sent to, default is 80
//! that specifies the port to which the request is sent to. Default is 80
//! \return Body of the response of the host
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
std::string GETString(const std::string& uri, const std::string& contentType, const std::string& body,
const std::string& adr, int port = 80) const override;
//! \brief Send a HTTP POST request to the specified host and return the body of the response.
//!
//! \param uri Uniform Resource Identifier in the request
//! \param contentType MIME type of the body data e.g. "text/html", "application/json", ...
//! \param body Request body, may be empty
//! \param adr Ip or hostname in dotted decimal notation like "192.168.2.1"
//! \param port Optional port the request is sent to, default is 80
//! that specifies the port to which the request is sent to. Default is 80
//! \return Body of the response of the host
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
std::string POSTString(const std::string& uri, const std::string& contentType, const std::string& body,
const std::string& adr, int port = 80) const override;
//! \brief Send a HTTP PUT request to the specified host and return the body of the response.
//!
//! \param uri Uniform Resource Identifier in the request
//! \param contentType MIME type of the body data e.g. "text/html", "application/json", ...
//! \param body Request body, may be empty
//! \param adr Ip or hostname in dotted decimal notation like "192.168.2.1"
//! \param port Optional port the request is sent to, default is 80
//! that specifies the port to which the request is sent to. Default is 80
//! \return Body of the response of the host
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
std::string PUTString(const std::string& uri, const std::string& contentType, const std::string& body,
const std::string& adr, int port = 80) const override;
//! \brief Send a HTTP DELETE request to the specified host and return the body of the response.
//!
//! \param uri Uniform Resource Identifier in the request
//! \param contentType MIME type of the body data e.g. "text/html", "application/json", ...
//! \param body Request body, may be empty
//! \param adr Ip or hostname in dotted decimal notation like "192.168.2.1"
//! \param port Optional port the request is sent to, default is 80
//! that specifies the port to which the request is sent to. Default is 80
//! \return Body of the response of the host
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
std::string DELETEString(const std::string& uri, const std::string& contentType, const std::string& body,
const std::string& adr, int port = 80) const override;
//! \brief Send a HTTP GET request to the specified host and return the body of the response parsed as JSON.
//!
//! \param uri Uniform Resource Identifier in the request
//! \param body Request body, may be empty
//! \param adr Ip or hostname in dotted decimal notation like "192.168.2.1"
//! \param port Optional port the request is sent to, default is 80
//! \return Parsed body of the response of the host
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws nlohmann::json::parse_error when the body could not be parsed
nlohmann::json GETJson(
const std::string& uri, const nlohmann::json& body, const std::string& adr, int port = 80) const override;
//! \brief Send a HTTP POST request to the specified host and return the body of the response parsed as JSON.
//!
//! \param uri Uniform Resource Identifier in the request
//! \param body Request body, may be empty
//! \param adr Ip or hostname in dotted decimal notation like "192.168.2.1"
//! \param port Optional port the request is sent to, default is 80
//! \return Parsed body of the response of the host
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws nlohmann::json::parse_error when the body could not be parsed
nlohmann::json POSTJson(
const std::string& uri, const nlohmann::json& body, const std::string& adr, int port = 80) const override;
//! \brief Send a HTTP PUT request to the specified host and return the body of the response parsed as JSON.
//!
//! \param uri Uniform Resource Identifier in the request
//! \param body Request body, may be empty
//! \param adr Ip or hostname in dotted decimal notation like "192.168.2.1"
//! \param port Optional port the request is sent to, default is 80
//! \return Parsed body of the response of the host
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws nlohmann::json::parse_error when the body could not be parsed
nlohmann::json PUTJson(
const std::string& uri, const nlohmann::json& body, const std::string& adr, int port = 80) const override;
//! \brief Send a HTTP DELETE request to the specified host and return the body of the response parsed as JSON.
//!
//! \param uri Uniform Resource Identifier in the request
//! \param body Request body, may be empty
//! \param adr Ip or hostname in dotted decimal notation like "192.168.2.1"
//! \param port Optional port the request is sent to, default is 80
//! \return Parsed body of the response of the host
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws nlohmann::json::parse_error when the body could not be parsed
nlohmann::json DELETEJson(
const std::string& uri, const nlohmann::json& body, const std::string& adr, int port = 80) const override;
};
} // namespace hueplusplus
#endif
@@ -0,0 +1,308 @@
/**
\file Bridge.h
Copyright Notice\n
Copyright (C) 2017 Jan Rogall - developer\n
Copyright (C) 2017 Moritz Wirger - 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 <http://www.gnu.org/licenses/>.
**/
#ifndef INCLUDE_HUEPLUSPLUS_HUE_H
#define INCLUDE_HUEPLUSPLUS_HUE_H
#include <map>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "APICache.h"
#include "BridgeConfig.h"
#include "BrightnessStrategy.h"
#include "ColorHueStrategy.h"
#include "ColorTemperatureStrategy.h"
#include "Group.h"
#include "HueCommandAPI.h"
#include "HueDeviceTypes.h"
#include "IHttpHandler.h"
#include "Light.h"
#include "ResourceList.h"
#include "Rule.h"
#include "Scene.h"
#include "Schedule.h"
#include "Sensor.h"
#include "SensorList.h"
#include "Utils.h"
#include <nlohmann/json.hpp>
//! \brief Namespace for the hueplusplus library
namespace hueplusplus
{
// forward declarations
class Bridge;
//!
//! Class to find all Hue bridges on the network and create usernames for them.
//!
class BridgeFinder
{
public:
struct BridgeIdentification
{
std::string ip;
int port = 80;
std::string mac;
};
public:
//! \brief Constructor of BridgeFinder class
//!
//! \param handler HttpHandler of type \ref IHttpHandler for communication with the bridge
BridgeFinder(std::shared_ptr<const IHttpHandler> handler);
//! \brief Finds all bridges in the network and returns them.
//!
//! The user should be given the opportunity to select the correct one based on the mac address.
//! \return vector containing ip and mac of all found bridges
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
std::vector<BridgeIdentification> findBridges() const;
//! \brief Gets a Hue bridge based on its identification
//!
//! \param identification \ref BridgeIdentification that specifies a bridge
//! \param sharedState Uses a single, shared cache for all objects on the bridge.
//! \return \ref Bridge class object
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body or username could not be requested
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
Bridge getBridge(const BridgeIdentification& identification, bool sharedState = false);
//! \brief Function that adds a username to the usernames map
//!
//! \param mac MAC address of Hue bridge
//! \param username Username that is used to control the Hue bridge
void addUsername(const std::string& mac, const std::string& username);
//! \brief Function that adds a client key to the clientkeys map
//!
//! The client key is only needed for entertainment mode, otherwise it is optional.
//! \param mac MAC address of Hue bridge
//! \param clientkey Client key that is used to control the Hue bridge in entertainment mode
void addClientKey(const std::string& mac, const std::string& clientkey);
//! \brief Function that returns a map of mac addresses and usernames.
//!
//! Note these should be saved at the end and re-loaded with \ref addUsername
//! next time, so only one username is generated per bridge. \returns A map
//! mapping mac address to username for every bridge
const std::map<std::string, std::string>& getAllUsernames() const;
//! \brief Normalizes mac address to plain hex number.
//! \returns \p input without separators and whitespace, in lower case.
static std::string normalizeMac(std::string input);
private:
//! \brief Parses mac address from description.xml
//!
//! \param description Content of description.xml file as returned by GET request.
//! \returns Content of xml element \c serialNumber if description matches a Hue bridge, otherwise an empty
//! string.
static std::string parseDescription(const std::string& description);
std::map<std::string, std::string> usernames; //!< Maps all macs to usernames added by \ref
//!< BridgeFinder::addUsername
std::map<std::string, std::string> clientkeys; //!< Maps all macs to clientkeys added by \ref
//!< BridgeFinder::addClientKey
std::shared_ptr<const IHttpHandler> http_handler;
};
//! \brief Bridge class for a bridge.
//!
//! This is the main class used to interact with the Hue bridge.
class Bridge
{
friend class BridgeFinder;
public:
using LightList = SearchableResourceList<Light>;
using GroupList = GroupResourceList<Group, CreateGroup>;
using ScheduleList = CreateableResourceList<ResourceList<Schedule, int>, CreateSchedule>;
using SceneList = CreateableResourceList<ResourceList<Scene, std::string>, CreateScene>;
using RuleList = CreateableResourceList<ResourceList<Rule, int>, CreateRule>;
public:
//! \brief Constructor of Bridge class
//!
//! \param ip IP address in dotted decimal notation like "192.168.2.1"
//! \param port Port of the hue bridge
//! \param username String that specifies the username that is used to control
//! the bridge. Can be left empty and acquired in \ref requestUsername.
//! \param handler HttpHandler for communication with the bridge
//! \param clientkey Optional client key for streaming
//! \param refreshDuration Time between refreshing the cached state.
//! \param sharedState Uses a single, shared cache for all objects on the bridge.
Bridge(const std::string& ip, const int port, const std::string& username,
std::shared_ptr<const IHttpHandler> handler, const std::string& clientkey = "",
std::chrono::steady_clock::duration refreshDuration = std::chrono::seconds(10), bool sharedState = false);
//! \brief Refreshes the bridge state.
//!
//! Should only be called rarely, as a full refresh is costly and usually not necessary.
//! Instead refresh only the parts you are interested in or rely on periodic refreshes
//! that happen automatically when calling non-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();
//! \brief Sets refresh interval for the whole bridge state.
//! \param refreshDuration The new minimum duration between refreshes. May be 0 or \ref c_refreshNever.
//!
//! Also sets refresh duration on all resource lists on the bridge, but not on already existing lights.
//! The resource lists (such as lights()) can have their own durations, but those must be set after calling this function.
void setRefreshDuration(std::chrono::steady_clock::duration refreshDuration);
//! \brief Function to get the ip address of the hue bridge
//!
//! \return string containing ip
std::string getBridgeIP() const;
//! \brief Function to set stream mode to active for entertainment mode
//!
//! \return bool - whether stream request was successful
bool startStreaming(std::string group_identifier);
//! \brief Function to set stream mode to active for entertainment mode
//!
//! \return bool - whether stream request was successful
bool stopStreaming(std::string group_identifier);
//! \brief Function to get the port of the hue bridge
//!
//! \return integer containing port
int getBridgePort() const;
//! \brief Send a username request to the Hue bridge.
//!
//! Blocks for about 30 seconds and 5 seconds to prepare.
//! It automatically sets the username variable according to the username received and returns the username
//! received. This function should only be called once to acquire a username to control the bridge and the
//! username should be saved for future use. \return username for API usage \throws std::system_error when
//! system or socket operations fail \throws HueException when response contained no body \throws
//! HueAPIResponseException when response contains an error except link button not pressed. \throws
//! nlohmann::json::parse_error when response could not be parsed
std::string requestUsername();
//! \brief Function that returns the username
//!
//! \return The username used for API access
std::string getUsername() const;
//! \brief Function that returns the client key
//!
//! \return The client key used for Entertainment Mode API access
std::string getClientKey() const;
//! \brief Function to set the ip address of this class representing a bridge
//!
//! \param ip String that specifies the ip in dotted decimal notation like "192.168.2.1"
void setIP(const std::string& ip);
//! \brief Function to set the port of this class representing a bridge
//!
//! \param port Integer that specifies the port of an address like
//! "192.168.2.1:8080"
void setPort(const int port);
//! \brief Provides access to the configuration of the bridge.
BridgeConfig& config();
//! \brief Provides access to the configuration of the bridge.
//! \note Does not refresh state.
const BridgeConfig& config() const;
//! \brief Provides access to the Light%s on the bridge.
LightList& lights();
//! \brief Provides access to the Light%s on the bridge.
//! \note Does not refresh state.
const LightList& lights() const;
//! \brief Provides access to the Group%s on the bridge.
GroupList& groups();
//! \brief Provides access to the Group%s on the bridge.
//! \note Does not refresh state.
const GroupList& groups() const;
//! \brief Provides access to the Schedule%s on the bridge.
ScheduleList& schedules();
//! \brief Provides access to the Schedule%s on the bridge.
//! \note Does not refresh state.
const ScheduleList& schedules() const;
//! \brief Provides access to the Scene%s on the bridge.
SceneList& scenes();
//! \brief Provides access to the Scene%s on the bridge.
//! \note Does not refresh state.
const SceneList& scenes() const;
//! \brief Provides access to the Sensor%s on the bridge.
SensorList& sensors();
//! \brief Provides access to the Sensor%s on the bridge.
//! \note Does not refresh state.
const SensorList& sensors() const;
//! \brief Provides access to the Rule%s on the bridge.
RuleList& rules();
//! \brief Provides access to the Rule%s on the bridge
//! \note Does not refresh state.
const RuleList& rules() const;
private:
//! \brief Function that sets the HttpHandler and updates the HueCommandAPI.
//! \param handler a HttpHandler of type \ref IHttpHandler
//!
//! The HttpHandler and HueCommandAPI are used for bridge communication.
//! Resetting the HttpHandler should only be done when the username is first set,
//! before Bridge is used.
//! Resets all caches and resource lists.
void setHttpHandler(std::shared_ptr<const IHttpHandler> handler);
private:
std::string ip; //!< IP-Address of the hue bridge in dotted decimal notation
//!< like "192.168.2.1"
std::string username; //!< Username that is ussed to access the hue bridge
std::string clientkey; //!< Client key that is used for entertainment mode
int port;
std::shared_ptr<const IHttpHandler> http_handler; //!< A IHttpHandler that is used to communicate with the
//!< bridge
std::chrono::steady_clock::duration refreshDuration;
std::shared_ptr<APICache> stateCache;
detail::MakeCopyable<LightList> lightList;
detail::MakeCopyable<GroupList> groupList;
detail::MakeCopyable<ScheduleList> scheduleList;
detail::MakeCopyable<SceneList> sceneList;
detail::MakeCopyable<SensorList> sensorList;
detail::MakeCopyable<RuleList> ruleList;
detail::MakeCopyable<BridgeConfig> bridgeConfig;
bool sharedState;
};
} // namespace hueplusplus
#endif
@@ -0,0 +1,115 @@
/**
\file BridgeConfig.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 <http://www.gnu.org/licenses/>.
**/
#ifndef INCLUDE_HUEPLUSPLUS_BRIDGE_CONFIG_H
#define INCLUDE_HUEPLUSPLUS_BRIDGE_CONFIG_H
#include <string>
#include <vector>
#include "APICache.h"
#include "TimePattern.h"
namespace hueplusplus
{
//! \brief API version consisting of major, minor and patch version
struct Version
{
int major;
int minor;
int patch;
};
//! \brief User that is whitelisted for Hue API usage
struct WhitelistedUser
{
//! \brief API username of the user
std::string key;
//! \brief Name provided on user creation
std::string name;
//! \brief Last time the user was used
time::AbsoluteTime lastUsed;
//! \brief Time the user was created
time::AbsoluteTime created;
};
//! \brief General bridge configuration properties.
class BridgeConfig
{
public:
//! \brief Construct BridgeConfig
BridgeConfig(std::shared_ptr<APICache> baseCache, 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 Sets custom refresh interval for the config.
//! \param refreshDuration The new minimum duration between refreshes. May be 0 or \ref c_refreshNever.
void setRefreshDuration(std::chrono::steady_clock::duration refreshDuration);
//! \brief Get the list of whitelisted users
//! \returns All users authorized for API access
std::vector<WhitelistedUser> getWhitelistedUsers() const;
//! \brief Remove user from the whitelist
//! \param userKey The API username of the user to remove
//! \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 removeUser(const std::string& userKey);
//! \brief Get link button state
//! \returns true when link button was pressed in the last 30 seconds.
//!
//! Indicates whether new users can be added currently.
bool getLinkButton() const;
//! \brief Set the link button state to pressed
void pressLinkButton();
//! \brief Add the closest lamp to the network
void touchLink();
//! \brief Get bridge MAC address
std::string getMACAddress() const;
//! \brief Get current (of last refresh) UTC time of the bridge
time::AbsoluteTime getUTCTime() const;
//! \brief Get configured timezone for the bridge
//! \note For times not in UTC, the timezone of the program and the bridge are assumed to be identical.
std::string getTimezone() const;
protected:
BridgeConfig(const BridgeConfig&) = default;
BridgeConfig(BridgeConfig&&) = default;
BridgeConfig& operator=(const BridgeConfig&) = default;
BridgeConfig& operator=(BridgeConfig&&) = default;
private:
APICache cache;
};
} // namespace hueplusplus
#endif
@@ -0,0 +1,61 @@
/**
\file BrightnessStrategy.h
Copyright Notice\n
Copyright (C) 2017 Jan Rogall - developer\n
Copyright (C) 2017 Moritz Wirger - 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 <http://www.gnu.org/licenses/>.
**/
#ifndef INCLUDE_HUEPLUSPLUS_BRIGHTNESS_STRATEGY_H
#define INCLUDE_HUEPLUSPLUS_BRIGHTNESS_STRATEGY_H
#include <cstdint>
namespace hueplusplus
{
class Light;
//! Virtual base class for all BrightnessStrategies
class BrightnessStrategy
{
public:
//! \brief Virtual function for changing a lights brightness with a specified
//! transition.
//!
//! \param bri The brightness raning from 0 = off to 255 = fully lit
//! \param transition The time it takes to fade to the new brightness in
//! multiples of 100ms, 4 = 400ms and should be seen as the default \param
//! light A reference of the light
virtual bool setBrightness(unsigned int bri, uint8_t transition, Light& light) const = 0;
//! \brief Virtual function that returns the current brightnessof the light
//!
//! Should update the lights state by calling refreshState()
//! \param light A reference of the light
//! \return Unsigned int representing the brightness
virtual unsigned int getBrightness(Light& light) const = 0;
//! \brief Virtual function that returns the current brightness of the light
//!
//! \note This should not update the lights state
//! \param light A const reference of the light
//! \return Unsigned int representing the brightness
virtual unsigned int getBrightness(const Light& light) const = 0;
//! \brief Virtual dtor
virtual ~BrightnessStrategy() = default;
};
} // namespace hueplusplus
#endif
@@ -0,0 +1,293 @@
/**
\file CLIPSensors.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 <http://www.gnu.org/licenses/>.
*/
#ifndef INCLUDE_HUEPLUSPLUS_CLIP_SENSORS_H
#define INCLUDE_HUEPLUSPLUS_CLIP_SENSORS_H
#include "Sensor.h"
namespace hueplusplus
{
namespace sensors
{
//! \brief Common methods for CLIP sensors
class BaseCLIP : public BaseDevice
{
public:
//! \brief Check if sensor is on
//!
//! Sensors which are off do not change their status
bool isOn() const;
//! \brief Enable or disable sensor
//! \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 setOn(bool on);
//! \brief Check whether the sensor has a battery state
bool hasBatteryState() const;
//! \brief Get battery state
//! \returns Battery state in percent
//! \throws nlohmann::json::out_of_range when sensor has no battery state.
int getBatteryState() const;
//! \brief Set battery state
//! \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 setBatteryState(int percent);
//! \brief Check whether the sensor is reachable
//! \note Reachable verification is not implemented for CLIP sensors yet
bool isReachable() const;
//! \brief Check whether the sensor has a URL
bool hasURL() const;
//! \brief Get sensor URL
std::string getURL() const;
//! \brief Set sensor URL
//! \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 setURL(const std::string& url);
//! \brief Get time of last status update
//! \returns The last update time, or a time with a zero duration from epoch
//! if the last update time is not set.
time::AbsoluteTime getLastUpdated() const;
protected:
//! \brief Protected constructor to be used by subclasses
explicit BaseCLIP(Sensor sensor) : BaseDevice(std::move(sensor)) { }
};
//! \brief CLIP sensor for button presses
class CLIPSwitch : public BaseCLIP
{
public:
//! \brief Construct from generic sensor
explicit CLIPSwitch(Sensor sensor) : BaseCLIP(std::move(sensor)) { }
//! \brief Get the code of the last switch event.
int getButtonEvent() const;
//! \brief Set the button event code
//! \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 setButtonEvent(int code);
//! \brief CLIPSwitch sensor type name
static constexpr const char* typeStr = "CLIPSwitch";
};
//! \brief CLIP sensor detecting whether a contact is open or closed
class CLIPOpenClose : public BaseCLIP
{
public:
//! \brief Construct from generic sensor
explicit CLIPOpenClose(Sensor sensor) : BaseCLIP(std::move(sensor)) { }
//! \brief Check whether the switch is open
bool isOpen() const;
//! \brief Set switch state
//!
//! The sensor needs to stay in a state for at least 1s.
//! \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 setOpen(bool open);
//! \brief CLIPOpenClose sensor type name
static constexpr const char* typeStr = "CLIPOpenClose";
};
detail::ConditionHelper<bool> makeCondition(const CLIPOpenClose& sensor);
//! \brief CLIP sensor to detect presence
class CLIPPresence : public BaseCLIP
{
public:
//! \brief Construct from generic sensor
explicit CLIPPresence(Sensor sensor) : BaseCLIP(std::move(sensor)) { }
//! \brief Check whether presence was detected
bool getPresence() const;
//! \brief Set presence state
//! \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 setPresence(bool presence);
//! \brief CLIPPresence sensor type name
static constexpr const char* typeStr = "CLIPPresence";
};
//! \brief CLIP sensor for temperature
class CLIPTemperature : public BaseCLIP
{
public:
//! \brief Construct from generic sensor
explicit CLIPTemperature(Sensor sensor) : BaseCLIP(std::move(sensor)) { }
//! \brief Get measured temperature
//! \returns Temperature in 0.01 degrees Celsius.
int getTemperature() const;
//! \brief Set temperature
//! \param temperature Temperature in 0.01 degreese Celsius.
//! \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 setTemperature(int temperature);
//! \brief CLIPTemperature sensor type name
static constexpr const char* typeStr = "CLIPTemperature";
};
//! \brief CLIP sensor for humidity
class CLIPHumidity : public BaseCLIP
{
public:
//! \brief Construct from generic sensor
explicit CLIPHumidity(Sensor sensor) : BaseCLIP(std::move(sensor)) { }
//! \brief Get measured humidity
//! \returns Humidity in 0.01% steps
int getHumidity() const;
//! \brief Set humidity
//! \param humidity Humidity in 0.01% steps
//! \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 setHumidity(int humidity);
//! \brief CLIPHumidity sensor type name
static constexpr const char* typeStr = "CLIPHumidity";
};
detail::ConditionHelper<int> makeCondition(const CLIPHumidity& sensor);
//! \brief CLIP sensor for light level
class CLIPLightLevel : public BaseCLIP
{
public:
//! \brief Construct from generic sensor
explicit CLIPLightLevel(Sensor sensor) : BaseCLIP(std::move(sensor)) { }
//! \brief Get threshold to detect darkness
int getDarkThreshold() const;
//! \brief Set threshold to detect darkness
//! \param threshold Light level as reported by \ref getLightLevel
//! \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 setDarkThreshold(int threshold);
//! \brief Get offset over dark threshold to detect daylight
int getThresholdOffset() const;
//! \brief Set offset to detect daylight
//! \param offset Offset to dark threshold to detect daylight. Must be greater than 1.
//! \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 setThresholdOffset(int offset);
//! \brief Get measured light level
//! \returns Light level in <code>10000*log10(lux)+1</code> (logarithmic scale)
int getLightLevel() const;
//! \brief Set measured light level
//! \param level Light level in <code>10000*log10(lux)+1</code>
//! \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 setLightLevel(int level);
//! \brief Check whether light level is below dark threshold
bool isDark() const;
//! \brief Check whether light level is above light threshold
//!
//! Light threshold is dark threshold + offset
bool isDaylight() const;
//! \brief CLIPLightLevel sensor type name
static constexpr const char* typeStr = "CLIPLightLevel";
};
//! \brief CLIP sensor for a generic 3rd party sensor.
//!
//! Can be created by POST.
class CLIPGenericFlag : public BaseCLIP
{
public:
//! \brief Construct from generic sensor
explicit CLIPGenericFlag(Sensor sensor) : BaseCLIP(std::move(sensor)) { }
//! \brief Get boolean flag
bool getFlag() const;
//! \brief Set flag
//! \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 setFlag(bool flag);
//! \brief CLIPGenericFlag sensor type name
static constexpr const char* typeStr = "CLIPGenericFlag";
};
detail::ConditionHelper<bool> makeCondition(const CLIPGenericFlag& sensor);
//! \brief CLIP sensor for a generic 3rd party status
//!
//! Can be created by POST.
class CLIPGenericStatus : public BaseCLIP
{
public:
//! \brief Construct from generic sensor
explicit CLIPGenericStatus(Sensor sensor) : BaseCLIP(std::move(sensor)) { }
//! \brief Get sensor status
int getStatus() const;
//! \brief Set sensor status
//! \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 setStatus(int status);
//! \brief CLIPGenericStatus sensor type name
static constexpr const char* typeStr = "CLIPGenericStatus";
};
detail::ConditionHelper<int> makeCondition(const CLIPGenericStatus& sensor);
} // namespace sensors
} // namespace hueplusplus
#endif
@@ -0,0 +1,128 @@
/**
\file ColorHueStrategy.h
Copyright Notice\n
Copyright (C) 2017 Jan Rogall - developer\n
Copyright (C) 2017 Moritz Wirger - 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 <http://www.gnu.org/licenses/>.
**/
#ifndef _COLOR_HUE_STRATEGY_H
#define _COLOR_HUE_STRATEGY_H
#include <cstdint>
#include <memory>
#include "ColorUnits.h"
namespace hueplusplus
{
class Light;
//! Virtual base class for all ColorHueStrategies
class ColorHueStrategy
{
public:
//! \brief Virtual function for changing a lights color in hue with a
//! specified transition.
//!
//! The hue ranges from 0 to 65535, whereas 65535 and 0 are red, 25500 is
//! green and 46920 is blue. \param hue The hue of the color \param transition
//! The time it takes to fade to the new color in multiples of 100ms, 4 =
//! 400ms and should be seen as the default \param light A reference of the
//! light
virtual bool setColorHue(uint16_t hue, uint8_t transition, Light& light) const = 0;
//! \brief Virtual function for changing a lights color in saturation with a
//! specified transition.
//!
//! The saturation ranges from 0 to 254, whereas 0 is least saturated (white)
//! and 254 is most saturated (vibrant). \param sat The saturation of the
//! color \param transition The time it takes to fade to the new color in
//! multiples of 100ms, 4 = 400ms and should be seen as the default \param
//! light A reference of the light
virtual bool setColorSaturation(uint8_t sat, uint8_t transition, Light& light) const = 0;
//! \brief Virtual function for changing a lights color in hue and saturation
//! format with a specified transition.
//!
//! \param hueSat Color in hue and satuation.
//! \param transition The time it takes to fade to the new color in multiples of
//! 100ms, 4 = 400ms and should be seen as the default
//! \param light A reference of the light
virtual bool setColorHueSaturation(const HueSaturation& hueSat, uint8_t transition, Light& light) const = 0;
//! \brief Virtual function for changing a lights color in CIE format with a
//! specified transition.
//!
//! \param xy The color in XY and brightness
//! \param transition The time it takes to fade to the new color in multiples
//! of 100ms, 4 = 400ms and should be seen as the default \param light A
//! reference of the light
virtual bool setColorXY(const XYBrightness& xy, uint8_t transition, Light& light) const = 0;
//! \brief Virtual function for turning on/off the color loop feature of a
//! light.
//!
//! Can be theoretically set for any light, but it only works for lights that
//! support this feature. When this feature is activated the light will fade
//! through every color on the current hue and saturation settings. Notice
//! that none of the setter functions check whether this feature is enabled
//! and the colorloop can only be disabled with this function or by simply
//! calling off() and then on(), so you could
//! alternatively call off() and then use any of the setter functions. \param
//! on Boolean to turn this feature on or off, true/1 for on and false/0 for
//! off \param light A reference of the light
virtual bool setColorLoop(bool on, Light& light) const = 0;
//! \brief Virtual function that lets the light perform one breath cycle in
//! the specified color.
//!
//! \param hueSat The color in hue and saturation
//! \param light A reference of the light
virtual bool alertHueSaturation(const HueSaturation& hueSat, Light& light) const = 0;
//! \brief Virtual function that lets the light perform one breath cycle in
//! the specified color.
//!
//! \param xy The color in XY and brightness
//! \param light A reference of the light
virtual bool alertXY(const XYBrightness& xy, Light& light) const = 0;
//! \brief Virtual function that returns the current color of the light as hue
//! and saturation
//!
//! Should update the lights state by calling refreshState()
//! \param light A reference of the light
virtual HueSaturation getColorHueSaturation(Light& light) const = 0;
//! \brief Virtual function that returns the current color of the light as hue
//! and saturation
//!
//! \note This should not update the lights state
//! \param light A const reference of the light
virtual HueSaturation getColorHueSaturation(const Light& light) const = 0;
//! \brief Virtual function that returns the current color of the light as xy
//!
//! Should update the lights state by calling refreshState()
//! \param light A reference of the light
//! \return XY and brightness of current color
virtual XYBrightness getColorXY(Light& light) const = 0;
//! \brief Virtual function that returns the current color of the light as xy
//!
//! \note This should not update the lights state
//! \param light A const reference of the light
//! \return XY and brightness of current color
virtual XYBrightness getColorXY(const Light& light) const = 0;
//! \brief Virtual dtor
virtual ~ColorHueStrategy() = default;
};
} // namespace hueplusplus
#endif
@@ -0,0 +1,73 @@
/**
\file ColorTemperatureStrategy.h
Copyright Notice\n
Copyright (C) 2017 Jan Rogall - developer\n
Copyright (C) 2017 Moritz Wirger - 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 <http://www.gnu.org/licenses/>.
**/
#ifndef INCLUDE_HUEPLUSPLUS_COLOR_TEMPERATURE_STRATEGY_H
#define INCLUDE_HUEPLUSPLUS_COLOR_TEMPERATURE_STRATEGY_H
#include <stdint.h>
namespace hueplusplus
{
class Light;
//! Virtual base class for all ColorTemperatureStrategies
class ColorTemperatureStrategy
{
public:
//! \brief Virtual function for changing a lights color temperature in mired
//! with a specified transition.
//!
//! The color temperature in mired ranges from 153 to 500 whereas 153 is cold
//! and 500 is warm. \param mired The color temperature in mired \param
//! transition The time it takes to fade to the new color in multiples of
//! 100ms, 4 = 400ms and should be seen as the default \param light A
//! reference of the light
virtual bool setColorTemperature(unsigned int mired, uint8_t transition, Light& light) const = 0;
//! \brief Virtual function that lets the light perform one breath cycle in
//! the specified color.
//!
//! The color temperature in mired ranges from 153 to 500 whereas 153 is cold
//! and 500 is warm. \param mired The color temperature in mired \param light
//! A reference of the light
virtual bool alertTemperature(unsigned int mired, Light& light) const = 0;
//! \brief Virtual function that returns the current color temperature of the
//! light
//!
//! Should update the lights state by calling refreshState()
//! The color temperature in mired ranges from 153 to 500 whereas 153 is cold
//! and 500 is warm. \param light A reference of the light \return Unsigned
//! int representing the color temperature in mired
virtual unsigned int getColorTemperature(Light& light) const = 0;
//! \brief Virtual function that returns the current color temperature of the
//! light
//!
//! The color temperature in mired ranges from 153 to 500 whereas 153 is cold
//! and 500 is warm. \note This should not update the lights state \param
//! light A const reference of the light \return Unsigned int representing the
//! color temperature in mired
virtual unsigned int getColorTemperature(const Light& light) const = 0;
//! \brief Virtual dtor
virtual ~ColorTemperatureStrategy() = default;
};
} // namespace hueplusplus
#endif
@@ -0,0 +1,164 @@
/**
\file ColorUnits.h
Copyright Notice\n
Copyright (C) 2017 Jan Rogall - developer\n
Copyright (C) 2017 Moritz Wirger - 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 <http://www.gnu.org/licenses/>.
**/
#ifndef INCLUDE_HUEPLUSPLUS_UNITS_H
#define INCLUDE_HUEPLUSPLUS_UNITS_H
#include <cstdint>
namespace hueplusplus
{
//! \brief Color in hue and saturation
struct HueSaturation
{
//! \brief Color hue
//!
//! Ranges from 0 to 65535 (16 bit), where 65535 and 0 are red, 25500 is green and 46920 is blue.
int hue;
//! \brief Color saturation
//!
//! Ranges from 0 to 254 (8 bit), where 0 is least saturated (white) and 254 is most saturated (vibrant).
int saturation;
bool operator==(const HueSaturation& other) const { return hue == other.hue && saturation == other.saturation; }
bool operator!=(const HueSaturation& other) const { return !(*this == other); }
};
//! \brief Color in CIE x and y coordinates
struct XY
{
//! \brief x coordinate in CIE, 0 to 1
float x;
//! \brief y coordinate in CIE, 0 to 1
float y;
bool operator==(const XY& other) const { return x == other.x && y == other.y; }
bool operator!=(const XY& other) const { return !(*this == other); }
};
//! \brief Color and brightness in CIE
//!
//! The brightness is needed to convert back to RGB colors if necessary.
//! \note brightness is not the actual luminance of the color, but instead the brightness the light is set to.
struct XYBrightness
{
//! \brief XY color
XY xy;
//! \brief Brightness from 0 to 1
float brightness;
bool operator==(const XYBrightness& other) const { return xy == other.xy && brightness == other.brightness; }
bool operator!=(const XYBrightness& other) const { return !(*this == other); }
};
//! \brief Triangle of representable colors in CIE
//!
//! \note Red, green and blue corner are oriented counter clockwise.
//! \see https://en.wikipedia.org/wiki/Chromaticity
struct ColorGamut
{
//! \brief Red corner in the color triangle
XY redCorner;
//! \brief Green corner in the color triangle
XY greenCorner;
//! \brief Blue corner in the color triangle
XY blueCorner;
//! \brief Check whether \c xy is representable.
bool contains(const XY& xy) const;
//! \brief Correct \c xy to closest representable color.
//! \returns \c xy if it is in the triangle, otherwise the closest point on the border.
XY corrected(const XY& xy) const;
};
//! \brief Predefined ColorGamut%s for Hue API
namespace gamut
{
//! \brief Gamut A, used by most Color Lights
constexpr ColorGamut gamutA {{0.704f, 0.296f}, {0.2151f, 0.7106f}, {0.138f, 0.08f}};
//! \brief Gamut B, used by older Extended Color Lights
constexpr ColorGamut gamutB {{0.675f, 0.322f}, {0.409f, 0.518f}, {0.167f, 0.04f}};
//! \brief Gamut C, used by newer Extended Color Lights
constexpr ColorGamut gamutC {{0.692f, 0.308f}, {0.17f, 0.7f}, {0.153f, 0.048f}};
//! \brief Maximal gamut to be used when unknown
//!
//! \note Most of this triangle is outside of visible colors.
constexpr ColorGamut maxGamut {{1.f, 0.f}, {0.f, 1.f}, {0.f, 0.f}};
} // namespace gamut
//! \brief Color in RGB
struct RGB
{
//! \brief Red amount from 0 to 255
uint8_t r;
//! \brief Green amount from 0 to 255
uint8_t g;
//! \brief Blue amount from 0 to 255
uint8_t b;
bool operator==(const RGB& other) const { return r == other.r && g == other.g && b == other.b; }
bool operator!=(const RGB& other) const { return !(*this == other); }
//! \brief Convert to XYBrightness without clamping
//!
//! Performs gamma correction so the light color matches the screen color better.
XYBrightness toXY() const;
//! \brief Convert to XYBrightness and clip to \c gamut
//!
//! Performs gamma correction so the light color matches the screen color better.
XYBrightness toXY(const ColorGamut& gamut) const;
//! \brief Convert to HueSaturation
//!
//! To get the correct color, set brightness to max(r,g,b).
HueSaturation toHueSaturation() const;
//! \brief Create from XYBrightness
//!
//! Performs gamma correction so the light color matches the screen color better.
//! \note The conversion formula is not exact, it can be off by up to 9 for each channel.
//! This is because the color luminosity is not saved.
static RGB fromXY(const XYBrightness& xy);
//! \brief Create from XYBrightness and clip to \c gamut
//!
//! A light may have XY set out of its range. Then this function returns the actual color
//! the light shows rather than what it is set to.
//! Performs gamma correction so the light color matches the screen color better.
//! \note The conversion formula is not exact, it can be off by up to 9 for each channel.
//! This is because the color luminosity is not saved.
static RGB fromXY(const XYBrightness& xy, const ColorGamut& gamut);
};
//! \brief Const function that converts Kelvin to Mired.
//!
//! \param kelvin Unsigned integer value in Kelvin
//! \return Unsigned integer value in Mired
unsigned int kelvinToMired(unsigned int kelvin);
//! \brief Const function that converts Mired to Kelvin.
//!
//! \param mired Unsigned integer value in Mired
//! \return Unsigned integer value in Kelvin
unsigned int miredToKelvin(unsigned int mired);
} // namespace hueplusplus
#endif
@@ -0,0 +1,168 @@
/**
\file Condition.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 <http://www.gnu.org/licenses/>.
**/
#ifndef INCLUDE_HUEPLUSPLUS_CONDITION_H
#define INCLUDE_HUEPLUSPLUS_CONDITION_H
#include "TimePattern.h"
#include <nlohmann/json.hpp>
namespace hueplusplus
{
//! \brief Condition for a Rule
//!
//! The condition checks whether a resource attribute (usually a Sensor value) matches the
//! specified Operator.
//!
//! Conditions from sensors can be created more easily using the makeCondition() helper functions.
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. Does not trigger a rule change
notStable, //!< Not stable for a given time. Does not trigger a rule change
in, //!< Time is in the given interval (triggered on start time, local time)
notIn //!< Time is not in the interval (triggered on end time, local time)
};
public:
//! \brief Create a condition from any address on the bridge
//! \param address Path to an attribute of the bridge
//! \param op Operator used for comparison.
//! \param value String representation of the value to check against. Empty for some operators.
Condition(const std::string& address, Operator op, const std::string& value);
//! \brief Get address on the bridge
std::string getAddress() const;
//! \brief Get used operator
Operator getOperator() const;
//! \brief Get value the attribute is checked against
std::string getValue() const;
//! \brief Create the json form of the condition
//! \returns A json object with address, operator and value
nlohmann::json toJson() const;
//! \brief Parse condition from json value
//! \param json Json object with address, operator and value
//! \returns The parsed condition with the same values
//! \throws HueException when the operator is unknown.
static Condition parse(const nlohmann::json& json);
private:
std::string address;
Operator op;
std::string value;
};
namespace detail
{
//! Helper class to make creating conditions more convenient.
//! Specializations for each data type provide methods for the supported operators.
//! This allows the user to write <code>makeCondition(sensor).eq(value)</code>
template <typename T>
class ConditionHelper
{ };
//! General operators supported by all data types
class GeneralConditionHelper
{
public:
explicit GeneralConditionHelper(const std::string& address) : address(address) { }
Condition dx() { return Condition(address, Condition::Operator::dx, ""); }
Condition ddx() { return Condition(address, Condition::Operator::ddx, ""); }
//! Docs does not say anything about format of stable value
//! \todo Change to either duration or int for seconds
Condition stable(const std::string& value) { return Condition(address, Condition::Operator::dx, value); }
protected:
std::string address;
};
//! Operators supported by int conditions
template <>
class ConditionHelper<int> : public GeneralConditionHelper
{
public:
using GeneralConditionHelper::GeneralConditionHelper;
Condition eq(int value) { return create(Condition::Operator::eq, value); }
Condition gt(int value) { return create(Condition::Operator::gt, value); }
Condition lt(int value) { return create(Condition::Operator::eq, value); }
Condition create(Condition::Operator op, int value) { return Condition(address, op, std::to_string(value)); }
};
//! Operators supported by bool conditions
template <>
class ConditionHelper<bool> : public GeneralConditionHelper
{
public:
using GeneralConditionHelper::GeneralConditionHelper;
Condition eq(bool value) { return create(Condition::Operator::eq, value); }
Condition create(Condition::Operator op, bool value) { return Condition(address, op, value ? "true" : "false"); }
};
//! Operators supported by timestamp conditions
template <>
class ConditionHelper<time::AbsoluteTime> : public GeneralConditionHelper
{
public:
using GeneralConditionHelper::GeneralConditionHelper;
Condition in(const time::TimeInterval& interval) { return create(Condition::Operator::in, interval); }
Condition notIn(const time::TimeInterval& interval) { return create(Condition::Operator::notIn, interval); }
Condition create(Condition::Operator op, const time::AbsoluteTime& value)
{
return Condition(address, op, value.toString());
}
Condition create(Condition::Operator op, const time::TimeInterval& interval)
{
return Condition(address, op, interval.toString());
}
};
template <typename... Ts>
struct make_void
{
typedef void type;
};
//! c++17 void_t
template <typename... Ts>
using void_t = typename make_void<Ts...>::type;
} // namespace detail
} // namespace hueplusplus
#endif
@@ -0,0 +1,90 @@
/**
\file EntertainmentMode.h
Copyright Notice\n
Copyright (C) 2020 Adam Honse - developer\n
Copyright (C) 2021 Moritz Wirger - 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 <http://www.gnu.org/licenses/>.
**/
#ifndef INCLUDE_HUEPLUSPLUS_HUE_ENTERTAINMENT_MODE_H
#define INCLUDE_HUEPLUSPLUS_HUE_ENTERTAINMENT_MODE_H
#include "Bridge.h"
#include "Group.h"
namespace hueplusplus
{
struct TLSContext;
//! \brief Class for Hue Entertainment Mode
//!
//! Provides methods to initialize and control Entertainment groups.
class EntertainmentMode
{
public:
//! \brief Constructor
//!
//! \param b Bridge reference
//! \param g Group to control in entertainment mode reference
//!
//! \note References are held to both \c b and \c g.
//! They must stay valid until EntertainmentMode ist destroyed.
EntertainmentMode(Bridge& b, Group& g);
//! \brief Destroy the Entertainment Mode object
~EntertainmentMode();
//! \brief Connect and start streaming
//!
//! \return true If conected and ready to receive commands
//! \return false If an error occured
bool connect();
//! \brief Disconnect and stop streaming
//!
//! \return true If disconnected successfully
//! \return false If an error occurred
bool disconnect();
//! \brief Set the color of the given light in RGB format
//!
//! \param light_index Light index inside the group
//! \param red Red color value (0-255)
//! \param green Green color value (0-255)
//! \param blue Blue color value (0-255)
//! \return true If light_index was valid
//! \return false If light_index was invalid
bool setColorRGB(uint8_t light_index, uint8_t red, uint8_t green, uint8_t blue);
//! \brief Update all set colors by \ref setColorRGB
//!
//! \return true If all color values for all lights have ben written/sent
//! \return false If there was an error while writing
bool update();
protected:
Bridge* bridge; //!< Associated bridge
Group* group; //!< Associated group
std::vector<uint8_t> entertainment_msg; //!< buffer containing the entertainment mode packet data
uint8_t entertainment_num_lights; //!< number of lights in entertainment mode group
std::unique_ptr<TLSContext> tls_context; //!< tls context
};
} // namespace hueplusplus
#endif
@@ -0,0 +1,54 @@
/**
\file ExtendedColorHueStrategy.h
Copyright Notice\n
Copyright (C) 2017 Jan Rogall - developer\n
Copyright (C) 2017 Moritz Wirger - 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 <http://www.gnu.org/licenses/>.
**/
#ifndef INCLUDE_HUEPLUSPLUS_EXTENDED_COLOR_HUE_STRATEGY_H
#define INCLUDE_HUEPLUSPLUS_EXTENDED_COLOR_HUE_STRATEGY_H
#include "Light.h"
#include "SimpleColorHueStrategy.h"
namespace hueplusplus
{
//! Class extending the implementation of SimpleColorHueStrategy
//!
//! To be used for lights that have both color and color temperature.
class ExtendedColorHueStrategy : public SimpleColorHueStrategy
{
public:
//! \brief Function that lets the light perform one breath cycle in the
//! specified color.
//! \param hueSat The color in hue and saturation
//! \param light A reference of the light
//!
//! Blocks for the time a \ref Light::alert() needs
bool alertHueSaturation(const HueSaturation& hueSat, Light& light) const override;
//! \brief Function that lets the light perform one breath cycle in the
//! specified color.
//! \param xy The color in XY and brightness
//! \param light A reference of the light
//!
//! Blocks for the time a \ref Light::alert() needs
bool alertXY(const XYBrightness& xy, Light& light) const override;
};
} // namespace hueplusplus
#endif
@@ -0,0 +1,46 @@
/**
\file ExtendedColorTemperatureStrategy.h
Copyright Notice\n
Copyright (C) 2017 Jan Rogall - developer\n
Copyright (C) 2017 Moritz Wirger - 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 <http://www.gnu.org/licenses/>.
**/
#ifndef INCLUDE_HUEPLUSPLUS_EXTENDED_COLOR_TEMPERATURE_STRATEGY_H
#define INCLUDE_HUEPLUSPLUS_EXTENDED_COLOR_TEMPERATURE_STRATEGY_H
#include "Light.h"
#include "SimpleColorTemperatureStrategy.h"
namespace hueplusplus
{
//! Class implementing the functions of ColorTemperatureStrategy
class ExtendedColorTemperatureStrategy : public SimpleColorTemperatureStrategy
{
public:
//! \brief Function that lets the light perform one breath cycle in the
//! specified color.
//!
//! It uses this_thread::sleep_for to accomodate for the time an \ref
//! Light::alert() needs The color temperature in mired ranges from 153 to
//! 500 whereas 153 is cold and 500 is warm. \param mired The color
//! temperature in mired \param light A reference of the light
bool alertTemperature(unsigned int mired, Light& light) const override;
};
} // namespace hueplusplus
#endif
@@ -0,0 +1,364 @@
/**
\file Group.h
Copyright Notice\n
Copyright (C) 2020 Jan Rogall - developer\n
Copyright (C) 2020 Moritz Wirger - 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 <http://www.gnu.org/licenses/>.
**/
#ifndef INCLUDE_HUEPLUSPLUS_GROUP_H
#define INCLUDE_HUEPLUSPLUS_GROUP_H
#include <string>
#include <vector>
#include "APICache.h"
#include "Action.h"
#include "HueCommandAPI.h"
#include "StateTransaction.h"
#include <nlohmann/json.hpp>
namespace hueplusplus
{
//! \brief Class for Groups of lights.
//!
//! Provides methods to control groups.
class Group
{
public:
//! \brief Creates group with shared cache
//! \param id Group id in the bridge
//! \param baseCache Cache of the group list.
Group(int id, const std::shared_ptr<APICache>& baseCache);
//! \brief Creates group with id
//! \param id Group id in the bridge
//! \param commands HueCommandAPI for requests
//! \param refreshDuration Time between refreshing the cached state.
//! \param currentState The current state, may be null.
Group(int id, const HueCommandAPI& commands, std::chrono::steady_clock::duration refreshDuration, const nlohmann::json& currentState);
//! \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 Sets custom refresh interval for this group.
//! \param refreshDuration The new minimum duration between refreshes. May be 0 or \ref c_refreshNever.
void setRefreshDuration(std::chrono::steady_clock::duration refreshDuration);
//! \name General information
///@{
//! \brief Get the group id.
int getId() const;
//! \brief Get the group name.
std::string getName() const;
//! \brief Get the group type.
//!
//! The type is specified on creation and cannot be changed.
//!
//! Possible types:
//! \li <code>0</code>: Special group containing all lights, cannot be modified.
//! \li <code>Luminaire</code>, <code>Lightsource</code>: Automatically created groups for multisource luminaires.
//! \li <code>LightGroup</code>: Standard, user created group, not empty.
//! \li <code>Room</code>: User created room, has room type.
//! \li <code>Entertainment</code>: User created entertainment setup.
//! \li <code>Zone</code>: User created Zone.
std::string getType() const;
//! \brief Get lights in the group.
//! \returns Ids of the lights in the group.
std::vector<int> getLightIds() const;
//! \brief Set group name.
//! \param name New name for the group.
//! Must be unique for all groups, otherwise a number is added.
void setName(const std::string& name);
//! \brief Set group lights.
//! \param ids New light ids. May or may not be empty depending on type.
//! \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 setLights(const std::vector<int>& ids);
//! \brief Get room type, only for type room.
//! \returns Room type/class of the group.
std::string getRoomType() const;
//! \brief Set room type, only for type room.
//! \param type New room class, case sensitive.
//! Only specific values are allowed.
//! \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 setRoomType(const std::string& type);
//! \brief Get luminaire model id, only for type luminaire.
//! \returns Unique id for the hardware model.
std::string getModelId() const;
//! \brief Get luminaire model id, only for type luminaire or lightsource.
//! \returns Unique id in <code>AA:BB:CC:DD</code> format for luminaire groups
//! or <code>AA:BB:CC:DD-XX</code> for Lightsource groups.
std::string getUniqueId() const;
//! \brief Get whether all lights are on.
//! \returns true when all lights are on.
//! \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
bool getAllOn();
//! \brief Get whether all lights are on.
//! \returns true when all lights are on.
//! \note Does not refresh the state.
bool getAllOn() const;
//! \brief Get whether any light is on.
//! \returns true when any light is on.
//! \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
bool getAnyOn();
//! \brief Get whether any light is on.
//! \returns true when any light is on.
//! \note Does not refresh the state.
bool getAnyOn() const;
///@}
//! \name Query Action
//! The action is the state of one light in the group.
//! It can be accessed using these methods.
///@{
//! \brief Get on state of one light in the group.
//! \returns True if the light is on.
//! \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
bool getActionOn();
//! \brief Get on state of one light in the group.
//! \returns True if the light is on.
//! \note Does not refresh the state.
bool getActionOn() const;
//! \brief Get hue and saturation of one light in the group.
//! \returns Pair of hue, saturation.
//! \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
std::pair<uint16_t, uint8_t> getActionHueSaturation();
//! \brief Get hue and saturation of one light in the group.
//! \returns Pair of hue, saturation.
//! \note Does not refresh the state.
std::pair<uint16_t, uint8_t> getActionHueSaturation() const;
//! \brief Get brightness of one light in the group.
//! \returns Brightness (0-254).
//! \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
unsigned int getActionBrightness();
//! \brief Get brightness of one light in the group.
//! \returns Brightness (0-254).
//! \note Does not refresh the state.
unsigned int getActionBrightness() const;
//! \brief Get color temperature of one light in the group.
//! \returns Color temperature in mired.
//! \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
unsigned int getActionColorTemperature();
//! \brief Get color temperature of one light in the group.
//! \returns Color temperature in mired.
//! \note Does not refresh the state.
unsigned int getActionColorTemperature() const;
//! \brief Get color coordinates of one light in the group.
//! \returns Pair of x and y color coordinates.
//! \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
std::pair<float, float> getActionColorXY();
//! \brief Get color coordinates of one light in the group.
//! \returns Pair of x and y color coordinates.
//! \note Does not refresh the state.
std::pair<float, float> getActionColorXY() const;
//! \brief Get color mode of one light in the group.
//!
//! The color mode is the currently used way to specify the color (hs,ct or xy).
//! \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
std::string getActionColorMode();
//! \brief Get color mode of one light in the group.
//!
//! The color mode is the currently used way to specify the color (hs,ct or xy).
//! \note Does not refresh the state.
std::string getActionColorMode() const;
///@}
//! \name Change lights
///@{
//! \brief Create a transaction for this group.
//!
//! The transaction can be used to change more than one value in one request.
//!
//! Example usage: \code
//! group.transaction().setBrightness(240).setColorHue(5000).commit();
//! \endcode
StateTransaction transaction();
//! \brief Convenience function to turn lights on.
//! \see StateTransaction::setOn
void setOn(bool on, uint8_t transition = 4);
//! \brief Convenience function to set brightness.
//! \see StateTransaction::setBrightness
void setBrightness(uint8_t brightness, uint8_t transition = 4);
//! \brief Convenience function to set hue and saturation.
//! \see StateTransaction::setColor(const HueSaturation&)
void setColor(const HueSaturation& hueSat, uint8_t transition = 4);
//! \brief Convenience function to set color xy.
//! \see StateTransaction::setColor(const XYBrightness&)
void setColor(const XYBrightness& xy, uint8_t transition = 4);
//! \brief Convenience function to set color temperature.
//! \see StateTransaction::setColorTemperature
void setColorTemperature(unsigned int mired, uint8_t transition = 4);
//! \brief Convenience function to set color loop.
//! \see StateTransaction::setColorLoop
void setColorLoop(bool on, uint8_t transition = 4);
//! \brief Recall scene for the group.
//!
//! Scenes are saved configurations for the lights in a group.
//! \param scene Scene name.
void setScene(const std::string& scene);
//! \brief Get Action to set scene
//! \param scene Scene name
//! \returns A Action that can be used to set the scene on a Schedule
//!
//! To set other light properties in a scene, use transaction().
Action createSceneAction(const std::string& scene) const;
///@}
protected:
//! \brief Utility function to send a put request to the group.
//!
//! \param request The request to send
//! \param subPath A path that is appended to the uri, note it should always start with a slash ("/")
//! \param fileInfo FileInfo from calling function for exception details.
//! \returns The parsed reply
//! \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
nlohmann::json sendPutRequest(const std::string& subPath, const nlohmann::json& request, FileInfo fileInfo);
protected:
int id;
APICache state;
};
//! \brief Parameters necessary for creating a new Group.
//!
//! Provides static functions for each group type that can be created by the user.
//! \note These are not all types that Group::getType() can return,
//! because some types cannot be created manually.
class CreateGroup
{
public:
//! \brief Create a LightGroup.
//!
//! LightGroup is the default type for groups. Empty LightGroups will be deleted.
//! \param lights List of light ids, must not be empty.
//! \param name Name of the new group, optional.
static CreateGroup LightGroup(const std::vector<int>& lights, const std::string& name = "");
//! \brief Create a Room group.
//!
//! Rooms can have a room class and can be empty. Every light can only be in one room.
//! \param lights List of light ids, may be empty.
//! \param name Name of the room, optional.
//! \param roomType Class of the room (case sensitive), optional.
//! Refer to Hue developer documentation for a list of possible room classes.
static CreateGroup Room(
const std::vector<int>& lights, const std::string& name = "", const std::string& roomType = "");
//! \brief Create an Entertainment group.
//!
//! The lights are used in an entertainment setup and can have relative positions.
//! The group can be empty.
//! \param lights List of light ids, may be empty.
//! \param name Name of the group, optional.
static CreateGroup Entertainment(const std::vector<int>& lights, const std::string& name = "");
//! \brief Create a Zone.
//!
//! Zones can be empty, a light can be in multiple zones.
//! \param lights List of light ids, may be empty.
//! \param name Name of the Zone, optional.
static CreateGroup Zone(const std::vector<int>& lights, const std::string& name = "");
//! \brief Get request to create the group.
//! \returns JSON request for a POST to create the new group
nlohmann::json getRequest() const;
protected:
//! \brief Protected constructor, should not be called directly.
//! \param lights List of light ids for the group.
//! \param name Name of the group, empty for default name.
//! \param type Type of the group, empty for default type.
//! \param roomType Room class if type is room, empty for default class or if type is not room.
CreateGroup(
const std::vector<int>& lights, const std::string& name, const std::string& type, const std::string& roomType);
private:
std::vector<int> lights;
std::string name;
std::string type;
std::string roomType;
};
} // namespace hueplusplus
#endif
@@ -0,0 +1,146 @@
/**
\file HueCommandAPI.h
Copyright Notice\n
Copyright (C) 2018 Jan Rogall - developer\n
Copyright (C) 2018 Moritz Wirger - 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 <http://www.gnu.org/licenses/>.
**/
#ifndef INCLUDE_HUEPLUSPLUS_HUECOMMANDAPI_H
#define INCLUDE_HUEPLUSPLUS_HUECOMMANDAPI_H
#include <atomic>
#include <chrono>
#include <mutex>
#include "HueException.h"
#include "IHttpHandler.h"
namespace hueplusplus
{
//! Handles communication to the bridge via IHttpHandler and enforces a timeout
//! between each request
class HueCommandAPI
{
public:
//! \brief Construct from ip, username and HttpHandler
//!
//! \param ip ip address of the Hue bridge in dotted decimal notation like "192.168.2.1"
//! \param port of the hue bridge
//! \param username username that is used to control the bridge
//! \param httpHandler HttpHandler for communication with the bridge
HueCommandAPI(
const std::string& ip, int port, const std::string& username, std::shared_ptr<const IHttpHandler> httpHandler);
//! \brief Copy construct from other HueCommandAPI
//! \note All copies refer to the same timeout data, so even calls from different objects will be delayed
HueCommandAPI(const HueCommandAPI&) = default;
//! \brief Move construct from other HueCommandAPI
//! \note All copies refer to the same timeout data, so even calls from different objects will be delayed
HueCommandAPI(HueCommandAPI&&) = default;
//! \brief Copy assign from other HueCommandAPI
//! \note All copies refer to the same timeout data, so even calls from different objects will be delayed
HueCommandAPI& operator=(const HueCommandAPI&) = default;
//! \brief Move assign from other HueCommandAPI
//! \note All copies refer to the same timeout data, so even calls from different objects will be delayed
HueCommandAPI& operator=(HueCommandAPI&&) = default;
//! \brief Sends a HTTP PUT request to the bridge and returns the response
//!
//! This function will block until at least Config::getBridgeRequestDelay() has passed to any previous request
//! \param path API request path (appended after /api/{username})
//! \param request Request to the api, may be empty
//! \param fileInfo File information for thrown exceptions.
//! \returns The return value of the underlying \ref IHttpHandler::PUTJson call
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contains no body
//! \throws HueAPIResponseException when response contains an error
nlohmann::json PUTRequest(const std::string& path, const nlohmann::json& request, FileInfo fileInfo) const;
//! \overload
nlohmann::json PUTRequest(const std::string& path, const nlohmann::json& request) const;
//! \brief Sends a HTTP GET request to the bridge and returns the response
//!
//! This function will block until at least Config::getBridgeRequestDelay() has passed to any previous request
//! \param path API request path (appended after /api/{username})
//! \param request Request to the api, may be empty
//! \param fileInfo File information for thrown exceptions.
//! \returns The return value of the underlying \ref IHttpHandler::GETJson call
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contains no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
nlohmann::json GETRequest(const std::string& path, const nlohmann::json& request, FileInfo fileInfo) const;
//! \overload
nlohmann::json GETRequest(const std::string& path, const nlohmann::json& request) const;
//! \brief Sends a HTTP DELETE request to the bridge and returns the response
//!
//! This function will block until at least Config::getBridgeRequestDelay() has passed to any previous request
//! \param path API request path (appended after /api/{username})
//! \param request Request to the api, may be empty
//! \param fileInfo File information for thrown exceptions.
//! \returns The return value of the underlying \ref IHttpHandler::DELETEJson call
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contains no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
nlohmann::json DELETERequest(const std::string& path, const nlohmann::json& request, FileInfo fileInfo) const;
//! \overload
nlohmann::json DELETERequest(const std::string& path, const nlohmann::json& request) const;
//! \brief Sends a HTTP POST request to the bridge and returns the response
//!
//! This function will block until at least Config::getBridgeRequestDelay() has passed to any previous request
//! \param path API request path (appended after /api/{username})
//! \param request Request to the api, may be empty
//! \param fileInfo File information for thrown exceptions.
//! \returns The return value of the underlying \ref IHttpHandler::POSTJson call
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contains no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
nlohmann::json POSTRequest(const std::string& path, const nlohmann::json& request, FileInfo fileInfo) const;
//! \overload
nlohmann::json POSTRequest(const std::string& path, const nlohmann::json& request) const;
//! \brief Combines path with api prefix and username
//! \returns "/api/<username>/<path>"
std::string combinedPath(const std::string& path) const;
private:
struct TimeoutData
{
std::chrono::steady_clock::time_point timeout;
std::mutex mutex;
};
//! \brief Throws an exception if response contains an error, passes though value
//! \throws HueAPIResponseException when response contains an error
//! \returns \ref response if there is no error
nlohmann::json HandleError(FileInfo fileInfo, const nlohmann::json& response) const;
private:
std::string ip;
int port;
std::string username;
std::shared_ptr<const IHttpHandler> httpHandler;
std::shared_ptr<TimeoutData> timeout;
};
} // namespace hueplusplus
#endif
@@ -0,0 +1,72 @@
/**
\file HueDeviceTypes.h
Copyright Notice\n
Copyright (C) 2020 Jan Rogall - developer\n
Copyright (C) 2020 Moritz Wirger - 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 <http://www.gnu.org/licenses/>.
**/
#ifndef INCLUDE_HUEPLUSPLUS_HUEDEVICETYPES_H
#define INCLUDE_HUEPLUSPLUS_HUEDEVICETYPES_H
#include <memory>
#include <string>
#include "Light.h"
namespace hueplusplus
{
class LightFactory
{
public:
//! \brief Create a factory for Light%s
//! \param commands HueCommandAPI for communication with the bridge
//! \param refreshDuration Time between refreshing the cached light state.
LightFactory(const HueCommandAPI& commands, std::chrono::steady_clock::duration refreshDuration);
//! \brief Create a Light with the correct type from the JSON state.
//! \param lightState Light JSON as returned from the bridge (not only the "state" part of it).
//! \param id Light id.
//! \param baseCache Optional shared cache for the light.
//! \returns Light with matching id, strategies and \ref ColorType.
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when light type is unknown
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
Light createLight(const nlohmann::json& lightState, int id, const std::shared_ptr<APICache>& baseCache = {});
private:
//! \brief Get color type from light JSON.
//! \param lightState Light JSON as returned from the bridge (not only the "state" part of it).
//! \param hasCt Whether the light has color temperature control.
//! \returns The color gamut specified in the light capabilities or,
//! if that does not exist, from a set of known models. Returns GAMUT_X_TEMPERATURE when \ref hasCt is true.
//! \throws HueException when the light has no capabilities and the model is not known.
ColorType getColorType(const nlohmann::json& lightState, bool hasCt) const;
private:
HueCommandAPI commands;
std::chrono::steady_clock::duration refreshDuration;
std::shared_ptr<BrightnessStrategy> simpleBrightness;
std::shared_ptr<ColorTemperatureStrategy> simpleColorTemperature;
std::shared_ptr<ColorTemperatureStrategy> extendedColorTemperature;
std::shared_ptr<ColorHueStrategy> simpleColorHue;
std::shared_ptr<ColorHueStrategy> extendedColorHue;
};
} // namespace hueplusplus
#endif
@@ -0,0 +1,121 @@
/**
\file HueException.h
Copyright Notice\n
Copyright (C) 2020 Jan Rogall - developer\n
Copyright (C) 2020 Moritz Wirger - 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 <http://www.gnu.org/licenses/>.
**/
#ifndef INCLUDE_HUEPLUSPLUS_HUE_EXCEPTION_H
#define INCLUDE_HUEPLUSPLUS_HUE_EXCEPTION_H
#include <exception>
#include <string>
#include <nlohmann/json.hpp>
namespace hueplusplus
{
//! \brief Contains information about error location, use \ref CURRENT_FILE_INFO to create
struct FileInfo
{
//! \brief Current file name from __FILE__. Empty if unknown
std::string filename;
//! \brief Current line number from __LINE__. -1 if unknown
int line = -1;
//! \brief Current function from __func__. Empty if unknown
std::string func;
//! \brief String representation of func, file and line.
//! \returns "<func> in <filename>:<line>" or "Unknown file" if unknown.
std::string ToString() const;
};
//! \brief Exception class with file information. Base class of all custom exception classes
class HueException : public std::exception
{
public:
//! \brief Creates HueException with information about the error and source
//! \param fileInfo Source of the error. Must not always be the throw location,
//! can also be a calling function which matches the cause better.
//! \param message Human readable error message.
HueException(FileInfo fileInfo, const std::string& message);
//! \brief What message of the exception
//! \returns exception name, file info and constructor message as char* into member string
const char* what() const noexcept override;
//! \brief Filename and line where the exception was thrown or caused by
const FileInfo& GetFile() const noexcept;
protected:
//! \brief Creates HueException with child class name
//!
//! Should be used by subclasses which can append additional information to the end of whatMessage.
//! \param exceptionName class name of the subclass
//! \param fileInfo Source of the error. Must not always be the throw location,
//! can also be a calling function which matches the cause better.
//! \param message Human readable error message
HueException(const char* exceptionName, FileInfo fileInfo, const std::string& message);
private:
std::string whatMessage;
FileInfo fileInfo;
};
//! \brief Exception caused by a Hue API "error" response with additional information
//!
//! Refer to Hue developer documentation for more detail on specific error codes.
class HueAPIResponseException : public HueException
{
public:
//! \brief Create exception with info from Hue API error
//! \param fileInfo Source of the error. Must not always be the throw location,
//! can also be a calling function which matches the cause better.
//! \param error Hue API error code from error response.
//! \param address URI the API call referred to from error response.
//! \param description Error description from response.
HueAPIResponseException(FileInfo fileInfo, int error, std::string address, std::string description);
//! \brief Error number from Hue API error response.
//!
//! Refer to Hue developer documentation for meaning of error codes.
int GetErrorNumber() const noexcept;
//! \brief Address the API call tried to access.
const std::string& GetAddress() const noexcept;
//! \brief Error description
const std::string& GetDescription() const noexcept;
//! \brief Creates exception from API response.
//! \param fileInfo Location of the cause
//! \param response Hue API response. Must contain a member "error" with "type", "address" and "description".
//! \returns HueAPIResponseException with info from the response.
//! If response does not contain the required members, they are defaulted to -1 or "".
static HueAPIResponseException Create(FileInfo fileInfo, const nlohmann::json& response);
private:
//! \brief Creates exception message containing the given information
static std::string GetMessage(int error, const std::string& addr, const std::string& description);
private:
int error;
std::string address;
std::string description;
};
} // namespace hueplusplus
#endif
@@ -0,0 +1,29 @@
/**
\file HueException.h
Copyright Notice\n
Copyright (C) 2020 Jan Rogall - developer\n
Copyright (C) 2020 Moritz Wirger - 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 <http://www.gnu.org/licenses/>.
**/
#include "HueException.h"
//! \def CURRENT_FILE_INFO
//! \brief Creates the FileInfo for the current line.
#ifndef CURRENT_FILE_INFO
#define CURRENT_FILE_INFO (::hueplusplus::FileInfo{__FILE__, __LINE__, __func__})
#endif
@@ -0,0 +1,200 @@
/**
\file IHttpHandler.h
Copyright Notice\n
Copyright (C) 2017 Jan Rogall - developer\n
Copyright (C) 2017 Moritz Wirger - 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 <http://www.gnu.org/licenses/>.
**/
#ifndef INCLUDE_HUEPLUSPLUS_IHTTPHANDLER_H
#define INCLUDE_HUEPLUSPLUS_IHTTPHANDLER_H
#include <chrono>
#include <iostream>
#include <memory>
#include <string>
#include <vector>
#include <nlohmann/json.hpp>
namespace hueplusplus
{
//! Abstract class for classes that handle http requests and multicast requests
class IHttpHandler
{
public:
//! \brief Virtual dtor
virtual ~IHttpHandler() = default;
//! \brief Send a message to a specified host and return the response.
//!
//! \param msg The message that should be sent to the specified address
//! \param adr Ip or hostname in dotted decimal notation like "192.168.2.1"
//! \param port Optional port the request is sent to, default is 80
//! \return The response of the host as a string
//! \throws std::system_error when system or socket operations fail
virtual std::string send(const std::string& msg, const std::string& adr, int port = 80) const = 0;
//! \brief Send a message to a specified host and return the body of the response.
//!
//! \param msg The message that should sent to the specified address
//! \param adr Ip or hostname in dotted decimal notation like "192.168.2.1"
//! \param port Optional port the request is sent to, default is 80
//! \return The body of the response of the host as a string
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
virtual std::string sendGetHTTPBody(const std::string& msg, const std::string& adr, int port = 80) const = 0;
//! \brief Send a multicast request with a specified message.
//!
//! \param msg The message that should sent to the specified multicast address
//! \param adr Optional ip or hostname in dotted decimal notation, default is "239.255.255.250"
//! \param port Optional port the request is sent to, default is 1900
//! \param timeout Optional time to wait for responses, default is 5 seconds
//!
//! Blocks for the duration of the timeout.
//!
//! \return vector of strings containing each received answer
//! \throws std::system_error when system or socket operations fail
virtual std::vector<std::string> sendMulticast(const std::string& msg, const std::string& adr = "239.255.255.250",
int port = 1900, std::chrono::steady_clock::duration timeout = std::chrono::seconds(5)) const = 0;
//! \brief Send a HTTP request with the given method to the specified host and return the body of the response.
//!
//! \param method HTTP method type e.g. GET, HEAD, POST, PUT, DELETE, ...
//! \param uri Uniform Resource Identifier in the request
//! \param contentType MIME type of the body data e.g. "text/html", "application/json", ...
//! \param body Request body, may be empty
//! \param adr Ip or hostname in dotted decimal notation like "192.168.2.1"
//! \param port Optional port the request is sent to, default is 80
//! \return Body of the response of the host
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
virtual std::string sendHTTPRequest(const std::string& method, const std::string& uri,
const std::string& contentType, const std::string& body, const std::string& adr, int port = 80) const = 0;
//! \brief Send a HTTP GET request to the specified host and return the body of the response.
//!
//! \param uri Uniform Resource Identifier in the request
//! \param contentType MIME type of the body data e.g. "text/html", "application/json", ...
//! \param body Request body, may be empty
//! \param adr Ip or hostname in dotted decimal notation like "192.168.2.1"
//! \param port Optional port the request is sent to, default is 80
//! that specifies the port to which the request is sent to. Default is 80
//! \return Body of the response of the host
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
virtual std::string GETString(const std::string& uri, const std::string& contentType, const std::string& body,
const std::string& adr, int port = 80) const = 0;
//! \brief Send a HTTP POST request to the specified host and return the body of the response.
//!
//! \param uri Uniform Resource Identifier in the request
//! \param contentType MIME type of the body data e.g. "text/html", "application/json", ...
//! \param body Request body, may be empty
//! \param adr Ip or hostname in dotted decimal notation like "192.168.2.1"
//! \param port Optional port the request is sent to, default is 80
//! that specifies the port to which the request is sent to. Default is 80
//! \return Body of the response of the host
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
virtual std::string POSTString(const std::string& uri, const std::string& contentType, const std::string& body,
const std::string& adr, int port = 80) const = 0;
//! \brief Send a HTTP PUT request to the specified host and return the body of the response.
//!
//! \param uri Uniform Resource Identifier in the request
//! \param contentType MIME type of the body data e.g. "text/html", "application/json", ...
//! \param body Request body, may be empty
//! \param adr Ip or hostname in dotted decimal notation like "192.168.2.1"
//! \param port Optional port the request is sent to, default is 80
//! that specifies the port to which the request is sent to. Default is 80
//! \return Body of the response of the host
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
virtual std::string PUTString(const std::string& uri, const std::string& contentType, const std::string& body,
const std::string& adr, int port = 80) const = 0;
//! \brief Send a HTTP DELETE request to the specified host and return the body of the response.
//!
//! \param uri Uniform Resource Identifier in the request
//! \param contentType MIME type of the body data e.g. "text/html", "application/json", ...
//! \param body Request body, may be empty
//! \param adr Ip or hostname in dotted decimal notation like "192.168.2.1"
//! \param port Optional port the request is sent to, default is 80
//! that specifies the port to which the request is sent to. Default is 80
//! \return Body of the response of the host
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
virtual std::string DELETEString(const std::string& uri, const std::string& contentType, const std::string& body,
const std::string& adr, int port = 80) const = 0;
//! \brief Send a HTTP GET request to the specified host and return the body of the response parsed as JSON.
//!
//! \param uri Uniform Resource Identifier in the request
//! \param body Request body, may be empty
//! \param adr Ip or hostname in dotted decimal notation like "192.168.2.1"
//! \param port Optional port the request is sent to, default is 80
//! \return Parsed body of the response of the host
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws nlohmann::json::parse_error when the body could not be parsed
virtual nlohmann::json GETJson(
const std::string& uri, const nlohmann::json& body, const std::string& adr, int port = 80) const = 0;
//! \brief Send a HTTP POST request to the specified host and return the body of the response parsed as JSON.
//!
//! \param uri Uniform Resource Identifier in the request
//! \param body Request body, may be empty
//! \param adr Ip or hostname in dotted decimal notation like "192.168.2.1"
//! \param port Optional port the request is sent to, default is 80
//! \return Parsed body of the response of the host
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws nlohmann::json::parse_error when the body could not be parsed
virtual nlohmann::json POSTJson(
const std::string& uri, const nlohmann::json& body, const std::string& adr, int port = 80) const = 0;
//! \brief Send a HTTP PUT request to the specified host and return the body of the response parsed as JSON.
//!
//! \param uri Uniform Resource Identifier in the request
//! \param body Request body, may be empty
//! \param adr Ip or hostname in dotted decimal notation like "192.168.2.1"
//! \param port Optional port the request is sent to, default is 80
//! \return Parsed body of the response of the host
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws nlohmann::json::parse_error when the body could not be parsed
virtual nlohmann::json PUTJson(
const std::string& uri, const nlohmann::json& body, const std::string& adr, int port = 80) const = 0;
//! \brief Send a HTTP DELETE request to the specified host and return the body of the response parsed as JSON.
//!
//! \param uri Uniform Resource Identifier in the request
//! \param body Request body, may be empty
//! \param adr Ip or hostname in dotted decimal notation like "192.168.2.1"
//! \param port Optional port the request is sent to, default is 80
//! \return Parsed body of the response of the host
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws nlohmann::json::parse_error when the body could not be parsed
virtual nlohmann::json DELETEJson(
const std::string& uri, const nlohmann::json& body, const std::string& adr, int port = 80) const = 0;
};
} // namespace hueplusplus
#endif
@@ -0,0 +1,75 @@
/**
\file LibConfig.h
Copyright Notice\n
Copyright (C) 2017 Jan Rogall - developer\n
Copyright (C) 2017 Moritz Wirger - 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 <http://www.gnu.org/licenses/>.
**/
#ifndef INCLUDE_HUEPLUSPLUS_HUE_CONFIG_H
#define INCLUDE_HUEPLUSPLUS_HUE_CONFIG_H
#include <chrono>
namespace hueplusplus
{
//! \brief Configurable delays
//!
//! Used to set all delays to zero when running tests.
class Config
{
private:
using duration = std::chrono::steady_clock::duration;
public:
//! \brief Delay for advanced alerts before the actual alert
duration getPreAlertDelay() const { return preAlertDelay; }
//! \brief Delay for advanced alerts after the actual alert
duration getPostAlertDelay() const { return postAlertDelay; }
//! \brief Timeout for UPnP multicast request
duration getUPnPTimeout() const { return upnpTimeout; }
//! \brief Delay between bridge requests
duration getBridgeRequestDelay() const { return bridgeRequestDelay; }
//! \brief Timeout for Bridge::requestUsername, waits until link button was pressed
duration getRequestUsernameTimeout() const { return requestUsernameDelay; }
//! \brief Interval in which username requests are attempted
duration getRequestUsernameAttemptInterval() const { return requestUsernameAttemptInterval; }
//! \brief Get config instance
static Config& instance()
{
static Config c;
return c;
}
protected:
Config() = default;
protected:
duration preAlertDelay = std::chrono::milliseconds(120);
duration postAlertDelay = std::chrono::milliseconds(1600);
duration upnpTimeout = std::chrono::seconds(5);
duration bridgeRequestDelay = std::chrono::milliseconds(100);
duration requestUsernameDelay = std::chrono::seconds(35);
duration requestUsernameAttemptInterval = std::chrono::seconds(1);
};
} // namespace hueplusplus
#endif
@@ -0,0 +1,624 @@
/**
\file Light.h
Copyright Notice\n
Copyright (C) 2017 Jan Rogall - developer\n
Copyright (C) 2017 Moritz Wirger - 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 <http://www.gnu.org/licenses/>.
**/
#ifndef INCLUDE_HUEPLUSPLUS_HUE_LIGHT_H
#define INCLUDE_HUEPLUSPLUS_HUE_LIGHT_H
#include <memory>
#include "APICache.h"
#include "BaseDevice.h"
#include "BrightnessStrategy.h"
#include "ColorHueStrategy.h"
#include "ColorTemperatureStrategy.h"
#include "HueCommandAPI.h"
#include "StateTransaction.h"
#include <nlohmann/json.hpp>
namespace hueplusplus
{
//! enum that specifies the color type of all HueLights
enum class ColorType
{
UNDEFINED, //!< ColorType for this light is unknown or undefined
NONE, //!< light has no specific ColorType
GAMUT_A, //!< light uses Gamut A
GAMUT_B, //!< light uses Gamut B
GAMUT_C, //!< light uses Gamut C
TEMPERATURE, //!< light has color temperature control
GAMUT_A_TEMPERATURE, //!< light uses Gamut A and has color temperature control
GAMUT_B_TEMPERATURE, //!< light uses Gamut B and has color temperature control
GAMUT_C_TEMPERATURE, //!< light uses Gamut C and has color temperature control
GAMUT_OTHER, //!< light uses capabilities to specify a different gamut
GAMUT_OTHER_TEMPERATURE //!< light uses capabilities to specify a different gamut and has color temperature control
};
//! \brief Class for Hue Light fixtures
//!
//! Provides methods to query and control lights.
class Light : public BaseDevice
{
friend class LightFactory;
friend class SimpleBrightnessStrategy;
friend class SimpleColorHueStrategy;
friend class ExtendedColorHueStrategy;
friend class SimpleColorTemperatureStrategy;
friend class ExtendedColorTemperatureStrategy;
public:
//! \name General information
///@{
//! \brief Const function that returns the luminaireuniqueid of the light
//!
//! \note Only working on bridges with versions starting at 1.9
//! \return String containing the luminaireuniqueid or an empty string when the function is not supported
virtual std::string getLuminaireUId() const;
//! \brief Const function that returns the color type of the light.
//!
//! \return ColorType containig the color type of the light
virtual ColorType getColorType() const;
//! \brief Get gamut space of possible light colors
//! \returns Used gamut, or \ref gamut::maxGamut when unknown.
ColorGamut getColorGamut() const;
///@}
//! \name Light state
///@{
//! \brief Function that turns the light on.
//!
//! \param transition Optional parameter to set the transition from current state to new, standard is 4 = 400ms
//! \return true on success
//! \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
virtual bool on(uint8_t transition = 4);
//! \brief Function that turns the light off.
//!
//! \param transition Optional parameter to set the transition from current state to new, standard is 4 = 400ms
//! \return Bool that is true on success
//! \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
virtual bool off(uint8_t transition = 4);
//! \brief Function to check whether a light is on or off
//!
//! \return Bool that is true, when the light is on and false, when off
//! \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
virtual bool isOn();
//! \brief Const function to check whether a light is on or off
//!
//! \note This will not refresh the light state
//! \return Bool that is true, when the light is on and false, when off
virtual bool isOn() const;
//! \brief Const function to check whether this light has brightness control
//!
//! \return Bool that is true when the light has specified abilities and false
//! when not
virtual bool hasBrightnessControl() const { return brightnessStrategy != nullptr; };
//! \brief Const function to check whether this light has color temperature
//! control
//!
//! \return Bool that is true when the light has specified abilities and false
//! when not
virtual bool hasTemperatureControl() const { return colorTemperatureStrategy != nullptr; };
//! \brief Connst function to check whether this light has full color control
//!
//! \return Bool that is true when the light has specified abilities and false
//! when not
virtual bool hasColorControl() const { return colorHueStrategy != nullptr; };
//! \brief Function that sets the brightness of this light.
//!
//! \note The brightness will only be set if the light has a reference to a
//! specific \ref BrightnessStrategy. The brightness can range from 0 = off to
//! 254 = fully lit.
//! \param bri Unsigned int that specifies the brightness
//! \param transition Optional parameter to set the transition from current state to new, standard is 4 = 400ms
//! \return Bool that is true on success
//! \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
virtual bool setBrightness(unsigned int bri, uint8_t transition = 4)
{
if (brightnessStrategy)
{
return brightnessStrategy->setBrightness(bri, transition, *this);
}
return false;
};
//! \brief Const function that returns the brightness of this light.
//!
//! \note The brightness will only be returned if the light has a reference to
//! a specific \ref BrightnessStrategy. \note This will not refresh the light
//! state The brightness can range from 0 = off to 254 = fully lit. \return
//! Unsigned int that is 0 when function failed
virtual unsigned int getBrightness() const
{
if (brightnessStrategy)
{
return brightnessStrategy->getBrightness(*this);
}
return 0;
};
//! \brief Function that returns the brightness of this light.
//!
//! \note The brightness will only be returned if the light has a reference to
//! a specific \ref BrightnessStrategy. The brightness can range from 0 = off
//! to 254 = fully lit.
//! \return Unsigned int that is 0 when function failed
//! \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
virtual unsigned int getBrightness()
{
if (brightnessStrategy)
{
return brightnessStrategy->getBrightness(*this);
}
return 0;
};
//! \brief Function that sets the color temperature of this light in mired.
//!
//! \note The color temperature will only be set if the light has a reference
//! to a specific \ref ColorTemperatureStrategy. The color temperature can
//! range from 153 to 500.
//! \param mired Unsigned int that specifies the color temperature in Mired
//! \param transition Optional parameter to set the transition from current state to new, standard is 4 = 400ms
//! \return Bool that is true on success
//! \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
virtual bool setColorTemperature(unsigned int mired, uint8_t transition = 4)
{
if (colorTemperatureStrategy)
{
return colorTemperatureStrategy->setColorTemperature(mired, transition, *this);
}
return false;
};
//! \brief Const function that returns the current color temperature of the
//! light
//!
//! \note The color temperature will only be returned when the light has a
//! reference to a specific \ref ColorTemperatureStrategy.
//! \note This will not refresh the light state
//! The color temperature in mired ranges from 153 to 500 whereas 153 is cold
//! and 500 is warm.
//! \return Unsigned int representing the color temperature in mired or 0 when failed
virtual unsigned int getColorTemperature() const
{
if (colorTemperatureStrategy)
{
return colorTemperatureStrategy->getColorTemperature(*this);
}
return 0;
};
//! \brief Function that returns the current color temperature of the light
//!
//! \note The color temperature will only be returned when the light has a
//! reference to a specific \ref ColorTemperatureStrategy.
//! Updates the lights state by calling refreshState()
//! The color temperature in mired ranges from 153 to 500 whereas 153 is cold
//! and 500 is warm.
//! \return Unsigned int representing the color temperature in mired or 0 when failed
//! \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
virtual unsigned int getColorTemperature()
{
if (colorTemperatureStrategy)
{
return colorTemperatureStrategy->getColorTemperature(*this);
}
return 0;
};
//! \brief Function to set the color of this light with specified hue.
//!
//! \note The color will only be set if the light has a reference to a
//! specific \ref ColorHueStrategy. The hue can range from 0 to 65535, whereas
//! 65535 and 0 are red, 25500 is green and 46920 is blue.
//! \param hue uint16_t that specifies the hue
//! \param transition Optional parameter to set the transition from current state to new, standard is 4 = 400ms
//! \return Bool that is true on success
//! \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
virtual bool setColorHue(uint16_t hue, uint8_t transition = 4)
{
if (colorHueStrategy)
{
return colorHueStrategy->setColorHue(hue, transition, *this);
}
return false;
};
//! \brief Function to set the color of this light with specified saturation.
//!
//! \note The color will only be set if the light has a reference to a
//! specific \ref ColorHueStrategy. The saturation can range from 0 to 254,
//! whereas 0 is least saturated (white) and 254 is most saturated.
//! \param sat uint8_t that specifies the saturation
//! \param transition Optional parameter to set the transition from current state to new, standard is 4 = 400ms
//! \return Bool that is true on success
//! \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
virtual bool setColorSaturation(uint8_t sat, uint8_t transition = 4)
{
if (colorHueStrategy)
{
return colorHueStrategy->setColorSaturation(sat, transition, *this);
}
return false;
};
//! \brief Function to set the color of this light with specified hue and
//! saturation.
//!
//! \note The color will only be set if the light has a reference to a
//! specific \ref ColorHueStrategy.
//! \param hueSat Color in hue and satuation.
//! \param transition Optional parameter to set the transition from current state to new, standard is 4 = 400ms.
//! \return Bool that is true on success
//! \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
virtual bool setColorHueSaturation(const HueSaturation& hueSat, uint8_t transition = 4)
{
if (colorHueStrategy)
{
return colorHueStrategy->setColorHueSaturation(hueSat, transition, *this);
}
return false;
};
//! \brief Const function that returns the current color of the light as hue
//! and saturation
//!
//! \note The color hue and saturation will only be returned when the light
//! has a reference to a specific \ref ColorHueStrategy.
//! \note This will not refresh the light state
//! \return Current hue and saturation or {0,0} when failed
//! \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
virtual HueSaturation getColorHueSaturation() const
{
if (colorHueStrategy)
{
return colorHueStrategy->getColorHueSaturation(*this);
}
return {};
};
//! \brief Function that returns the current color of the light as hue and
//! saturation
//!
//! \note The color hue and saturation will only be returned when the light
//! has a reference to a specific \ref ColorHueStrategy. Updates the lights
//! state by calling refreshState()
//! \return Current hue and saturation or {0,0} when failed
//! \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
virtual HueSaturation getColorHueSaturation()
{
if (colorHueStrategy)
{
return colorHueStrategy->getColorHueSaturation(*this);
}
return {};
};
//! \brief Function to set the color of this light in CIE with specified x y.
//!
//! \note The color will only be set if the light has a reference to a
//! specific \ref ColorHueStrategy. The values of x and y are ranging from 0 to 1.
//! \param xy The color in XY and brightness
//! \param transition Optional parameter to set the transition from current state to new, standard is 4 = 400ms
//! \return Bool that is true on success
//! \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
virtual bool setColorXY(const XYBrightness& xy, uint8_t transition = 4)
{
if (colorHueStrategy)
{
return colorHueStrategy->setColorXY(xy, transition, *this);
}
return false;
};
//! \brief Const function that returns the current color of the light as xy
//!
//! \note The color x and y will only be returned when the light has a
//! reference to a specific \ref ColorHueStrategy.
//! \note This does not update the lights state
//! \return XYBrightness with x, y and brightness or an empty one (all 0) when failed
virtual XYBrightness getColorXY() const
{
if (colorHueStrategy)
{
return colorHueStrategy->getColorXY(*this);
}
return {};
};
//! \brief Function that returns the current color of the light as xy
//!
//! \note The color x and y will only be returned when the light has a
//! reference to a specific \ref ColorHueStrategy.
//! Updates the lights state by calling refreshState()
//! \return XYBrightness with x, y and brightness or an empty one (all 0) when failed
//! \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
virtual XYBrightness getColorXY()
{
if (colorHueStrategy)
{
return colorHueStrategy->getColorXY(*this);
}
return {};
}
//! \brief Function to set the color of this light with red green and blue
//! values.
//!
//! \note The color will only be set if the light has a reference to a
//! specific \ref ColorHueStrategy. The values of red, green and blue are
//! ranging from 0 to 255.
//! \param rgb RGB color that will be mapped to the available color space
//! \param transition Optional parameter to set the transition from current state to new, standard is 4 = 400ms
//! \return Bool that is true on success
//! \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
virtual bool setColorRGB(const RGB& rgb, uint8_t transition = 4)
{
if (colorHueStrategy)
{
return colorHueStrategy->setColorXY(rgb.toXY(getColorGamut()), transition, *this);
}
return false;
}
//! \brief Function that lets the light perform one breath cycle.
//!
//! Can be used for locating a light.
//! \return bool that is true on success
//! \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
virtual bool alert();
//! \brief Function that lets the light perform one breath cycle in specified
//! color temperature.
//!
//! \note The breath cylce will only be performed if the light has a reference
//! to a specific \ref ColorTemperatureStrategy.
//! \param mired Color temperature in mired
//! \return Bool that is true on success
//! \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
virtual bool alertTemperature(unsigned int mired)
{
if (colorTemperatureStrategy)
{
return colorTemperatureStrategy->alertTemperature(mired, *this);
}
return false;
}
//! \brief Function that lets the light perform one breath cycle in specified
//! color.
//!
//! \note The breath cylce will only be performed if the light has a reference
//! to a specific \ref ColorHueStrategy.
//! \param hueSat Color in hue and saturation
//! \return Bool that is true on success
//! \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
virtual bool alertHueSaturation(const HueSaturation& hueSat)
{
if (colorHueStrategy)
{
return colorHueStrategy->alertHueSaturation(hueSat, *this);
}
return false;
}
//! \brief Function that lets the light perform one breath cycle in specified
//! color.
//!
//! \note The breath cylce will only be performed if the light has a reference
//! to a specific \ref ColorHueStrategy.
//! \param xy The x,y coordinates in CIE and brightness
//! \return Bool that is true on success
//! \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
virtual bool alertXY(const XYBrightness& xy)
{
if (colorHueStrategy)
{
return colorHueStrategy->alertXY(xy, *this);
}
return false;
}
//! \brief Function to turn colorloop effect on/off.
//!
//! Notice this function will only be performed light has a reference to a
//! specific \ref ColorHueStrategy. The colorloop effect will loop through all
//! colors on current hue and saturation levels. Notice that none of the
//! setter functions check whether this feature is enabled and the colorloop
//! can only be disabled with this function or by simply calling
//! off() and then on(), so you could
//! alternatively call off() and then use any of the setter functions.
//! \param on bool that enables this feature when true and disables it when false
//! \return Bool that is true on success
//! \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
virtual bool setColorLoop(bool on)
{
if (colorHueStrategy)
{
return colorHueStrategy->setColorLoop(on, *this);
}
return false;
}
//! \brief Create a transaction for this light.
//!
//! The transaction can be used to change more than one value in one request.
//! Only use the functions supported by the current light type.
//!
//! Example usage: \code
//! light.transaction().setBrightness(240).setColorHue(5000).commit();
//! \endcode
virtual StateTransaction transaction();
///@}
protected:
//! \brief Protected ctor that is used by \ref LightFactory.
//!
//! \param id Integer that specifies the id of this light
//! \param commands HueCommandAPI for communication with the bridge
//!
//! leaves strategies unset
Light(int id, const HueCommandAPI& commands);
//! \brief Protected ctor that is used by \ref LightFactory.
//!
//! \param id Integer that specifies the id of this light
//! \param baseCache Cache of the light list (must not be null).
//!
//! leaves strategies unset
Light(int id, const std::shared_ptr<APICache>& baseCache);
//! \brief Protected ctor that is used by \ref LightFactory, also sets
//! strategies.
//!
//! \param id Integer that specifies the id of this light
//! \param commands HueCommandAPI for communication with the bridge
//! \param brightnessStrategy Strategy for brightness. May be nullptr.
//! \param colorTempStrategy Strategy for color temperature. May be nullptr.
//! \param colorHueStrategy Strategy for color hue/saturation. May be nullptr.
//! \param refreshDuration Time between refreshing the cached state.
//! Can be 0 to always refresh, or steady_clock::duration::max() to never refresh.
//! \param currentState The current light state, may be null.
//! \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
Light(int id, const HueCommandAPI& commands, std::shared_ptr<const BrightnessStrategy> brightnessStrategy,
std::shared_ptr<const ColorTemperatureStrategy> colorTempStrategy,
std::shared_ptr<const ColorHueStrategy> colorHueStrategy, std::chrono::steady_clock::duration refreshDuration,
const nlohmann::json& currentState);
//! \brief Protected function that sets the brightness strategy.
//!
//! The strategy defines how specific commands that deal with brightness
//! control are executed \param strat a strategy of type \ref
//! BrightnessStrategy
virtual void setBrightnessStrategy(std::shared_ptr<const BrightnessStrategy> strat)
{
brightnessStrategy = std::move(strat);
};
//! \brief Protected function that sets the colorTemperature strategy.
//!
//! The strategy defines how specific commands that deal with colortemperature
//! control are executed \param strat a strategy of type \ref
//! ColorTemperatureStrategy
virtual void setColorTemperatureStrategy(std::shared_ptr<const ColorTemperatureStrategy> strat)
{
colorTemperatureStrategy = std::move(strat);
};
//! \brief Protected function that sets the colorHue strategy.
//!
//! The strategy defines how specific commands that deal with color control
//! are executed \param strat a strategy of type \ref ColorHueStrategy
virtual void setColorHueStrategy(std::shared_ptr<const ColorHueStrategy> strat)
{
colorHueStrategy = std::move(strat);
};
protected:
ColorType colorType; //!< holds the \ref ColorType of the light
std::shared_ptr<const BrightnessStrategy>
brightnessStrategy; //!< holds a reference to the strategy that handles brightness commands
std::shared_ptr<const ColorTemperatureStrategy>
colorTemperatureStrategy; //!< holds a reference to the strategy that handles colortemperature commands
std::shared_ptr<const ColorHueStrategy>
colorHueStrategy; //!< holds a reference to the strategy that handles all color commands
};
} // namespace hueplusplus
#endif
@@ -0,0 +1,63 @@
/**
\file LinHttpHandler.h
Copyright Notice\n
Copyright (C) 2017 Jan Rogall - developer\n
Copyright (C) 2017 Moritz Wirger - 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 <http://www.gnu.org/licenses/>.
**/
#ifndef INCLUDE_HUEPLUSPLUS_LINHTTPHANDLER_H
#define INCLUDE_HUEPLUSPLUS_LINHTTPHANDLER_H
#include <string>
#include <vector>
#include "BaseHttpHandler.h"
#include <nlohmann/json.hpp>
namespace hueplusplus
{
//! Class to handle http requests and multicast requests on linux systems
class LinHttpHandler : public BaseHttpHandler
{
public:
//! \brief Function that sends a given message to the specified host and
//! returns the response.
//!
//! \param msg String that contains the message that is sent to the specified
//! address \param adr String that contains an ip or hostname in dotted
//! decimal notation like "192.168.2.1" \param port Optional integer that
//! specifies the port to which the request is sent to. Default is 80 \return
//! String containing the response of the host
virtual std::string send(const std::string& msg, const std::string& adr, int port = 80) const override;
//! \brief Function that sends a multicast request with the specified message.
//!
//! \param msg String that contains the request that is sent to the specified
//! address \param adr Optional String that contains an ip or hostname in
//! dotted decimal notation, default is "239.255.255.250" \param port Optional
//! integer that specifies the port to which the request is sent. Default is
//! 1900 \param timeout Optional The timeout of the
//! request. Default is 5 seconds \return Vector containing strings of each
//! answer received
std::vector<std::string> sendMulticast(const std::string& msg, const std::string& adr = "239.255.255.250",
int port = 1900, std::chrono::steady_clock::duration timeout = std::chrono::seconds(5)) const override;
};
} // namespace hueplusplus
#endif
@@ -0,0 +1,40 @@
/**
\file ModelPictures.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 <http://www.gnu.org/licenses/>.
**/
#ifndef INCLUDE_HUEPLUSPLUS_MODEL_PICTURES_H
#define INCLUDE_HUEPLUSPLUS_MODEL_PICTURES_H
#include <string>
namespace hueplusplus
{
//! \brief Get the picture name of a given model id
//!
//! \note This function will only return the filename without extension,
//! because Philips provides different file types.
//! \param modelId Model Id of a device to get the picture of
//! \returns String that either contains the filename of the picture of the device
//! or an empty string if it was not found.
std::string getPictureOfModel(const std::string& modelId);
}
#endif
@@ -0,0 +1,69 @@
/**
\file NewDeviceList.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 <http://www.gnu.org/licenses/>.
**/
#ifndef INCLUDE_HUEPLUSPLUS_NEW_DEVICE_LIST_H
#define INCLUDE_HUEPLUSPLUS_NEW_DEVICE_LIST_H
#include <map>
#include <string>
#include "TimePattern.h"
#include <nlohmann/json.hpp>
namespace hueplusplus
{
//! \brief List of new devices found during the last scan
class NewDeviceList
{
public:
//! \brief Construct from data
NewDeviceList(const std::string& lastScan, const std::map<int, std::string>& devices);
//! \brief Get a map of id and name of new devices
const std::map<int, std::string>& getNewDevices() const;
//! \brief Get whether a last scan time is available
//!
//! This can be false if there was no scan since the last restart
//! or if the scan is still running.
bool hasLastScanTime() const;
//! \brief Get whether scan is currently active
//!
//! When scan is active, no last scan time is available
bool isScanActive();
//! \brief Get time when last scan was completed
//! \throws HueException when no time is available or timestamp is invalid
//! \note Must only be called when \ref hasLastScanTime() is true.
time::AbsoluteTime getLastScanTime() const;
//! \brief Parse from json response
//! \throws std::invalid_argument when json is invalid.
//! \throws nlohmann::json::exception when json is invalid.
static NewDeviceList parse(const nlohmann::json& json);
private:
std::string lastScan;
std::map<int, std::string> devices;
};
} // namespace hueplusplus
#endif
@@ -0,0 +1,361 @@
/**
\file ResourceList.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 <http://www.gnu.org/licenses/>.
**/
#ifndef INCLUDE_HUEPLUSPLUS_RESOURCE_LIST_H
#define INCLUDE_HUEPLUSPLUS_RESOURCE_LIST_H
#include <functional>
#include <map>
#include <string>
#include <vector>
#include "APICache.h"
#include "HueException.h"
#include "NewDeviceList.h"
#include "Utils.h"
namespace hueplusplus
{
//! \brief Handles a list of a certain API resource
//! \tparam Resource Resource type that is in the list
//! \tparam IdT Type of the resource id. int or std::string
//!
//! The resources are assumed to be in an object with ids as keys.
//! The Resource class needs a constructor that accepts \c id, HueCommandAPI, \c refreshDuration and \c state;
//! otherwise a factory function needs to be provided that takes \c id, \c state
//! and a base cache that is null when shared state is disabled.
template <typename Resource, typename IdT>
class ResourceList
{
public:
using ResourceType = Resource;
using IdType = IdT;
static_assert(std::is_integral<IdType>::value || std::is_same<std::string, IdType>::value,
"IdType must be integral or string");
//! \brief Construct ResourceList using a base cache and optional factory function
//! \param baseCache Base cache which holds the parent state, not nullptr
//! \param cacheEntry Entry name of the list state in the base cache
//! \param refreshDuration Interval between refreshing the cache
//! \param sharedState Whether created resources should share the same base cache.
//! \param factory Optional factory function to create Resources.
//! Necessary if Resource is not constructible as described above.
ResourceList(std::shared_ptr<APICache> baseCache, const std::string& cacheEntry,
std::chrono::steady_clock::duration refreshDuration, bool sharedState = false,
const std::function<Resource(IdType, const nlohmann::json&, const std::shared_ptr<APICache>&)>& factory
= nullptr)
: stateCache(std::make_shared<APICache>(baseCache, cacheEntry, refreshDuration)),
factory(factory),
path(stateCache->getRequestPath() + '/'),
sharedState(sharedState)
{ }
//! \brief Construct ResourceList with a separate cache and optional factory function
//! \param commands HueCommandAPI for requests
//! \param path Path of the resource list
//! \param refreshDuration Interval between refreshing the cache
//! \param factory Optional factory function to create Resources.
//! Necessary if Resource is not constructible as described above.
ResourceList(const HueCommandAPI& commands, const std::string& path,
std::chrono::steady_clock::duration refreshDuration,
const std::function<Resource(IdType, const nlohmann::json&, const std::shared_ptr<APICache>&)>& factory
= nullptr)
: stateCache(std::make_shared<APICache>(path, commands, refreshDuration, nullptr)),
factory(factory),
path(path + '/'),
sharedState(false)
{ }
//! \brief Deleted copy constructor
ResourceList(const ResourceList&) = delete;
//! \brief Deleted copy assignment
ResourceList& operator=(const ResourceList&) = delete;
//! \brief Refreshes internal state now
void refresh() { stateCache->refresh(); }
//! \brief Sets custom refresh interval for this list and all resources created.
//! \param refreshDuration The new minimum duration between refreshes. May be 0 or \ref c_refreshNever.
void setRefreshDuration(std::chrono::steady_clock::duration refreshDuration)
{
stateCache->setRefreshDuration(refreshDuration);
}
//! \brief Get all resources that exist
//! \returns A vector of references to every Resource
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contains no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
std::vector<Resource> getAll()
{
nlohmann::json& state = stateCache->getValue();
std::vector<Resource> result;
result.reserve(state.size());
for (auto it = state.begin(); it != state.end(); ++it)
{
result.emplace_back(construct(maybeStoi(it.key()), it.value()));
}
return result;
}
//! \brief Get resource specified by id
//! \param id Identifier of the resource
//! \returns The resource matching the id
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when id does not exist
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
Resource get(const IdType& id)
{
const nlohmann::json& state = stateCache->getValue();
std::string key = maybeToString(id);
if (!state.count(key))
{
throw HueException(FileInfo {__FILE__, __LINE__, __func__}, "Resource id is not valid");
}
return construct(id, state[key]);
}
//! \brief Checks whether resource with id exists
//! \param id Identifier of the resource to check
//! \returns true when the resource with given id exists
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contains no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
bool exists(const IdType& id) { return stateCache->getValue().count(maybeToString(id)) != 0; }
//! \brief Checks whether resource with id exists
//! \param id Identifier of the resource to check
//! \returns true when the resource with given id exists
//! \note This will not update the cache
//! \throws HueException when the cache is empty
bool exists(const IdType& id) const { return stateCache->getValue().count(maybeToString(id)) != 0; }
//! \brief Removes the resource
//! \param id Identifier of the resource to remove
//! \returns true on success
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contains no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
//!
//! If successful, invalidates references to the Resource removed.
bool remove(const IdType& id)
{
std::string requestPath = path + maybeToString(id);
nlohmann::json result = stateCache->getCommandAPI().DELETERequest(
requestPath, nlohmann::json::object(), FileInfo {__FILE__, __LINE__, __func__});
bool success = utils::safeGetMember(result, 0, "success") == requestPath + " deleted";
return success;
}
protected:
//! \brief Calls std::stoi if IdType is int
static IdType maybeStoi(const std::string& key) { return maybeStoi(key, std::is_integral<IdType> {}); }
//! \brief Calls std::to_string if IdType is int
static std::string maybeToString(const IdType& id) { return maybeToString(id, std::is_integral<IdType> {}); }
//! \brief Constructs resource using factory or constructor, if available
//! \throws HueException when factory is nullptr and Resource cannot be constructed as specified above.
Resource construct(const IdType& id, const nlohmann::json& state)
{
return construct(id, state,
std::is_constructible<Resource, IdType, HueCommandAPI, std::chrono::steady_clock::duration,
const nlohmann::json&> {});
}
//! \brief Protected defaulted move constructor
ResourceList(ResourceList&&) = default;
//! \brief Protected defaulted move assignment
ResourceList& operator=(ResourceList&&) = default;
private:
// Resource is constructible
Resource construct(const IdType& id, const nlohmann::json& state, std::true_type)
{
if (factory)
{
return factory(id, state, sharedState ? stateCache : std::shared_ptr<APICache>());
}
else
{
if (sharedState)
{
return Resource(id, stateCache);
}
else
{
return Resource(id, stateCache->getCommandAPI(), stateCache->getRefreshDuration(), state);
}
}
}
// Resource is not constructible
Resource construct(const IdType& id, const nlohmann::json& state, std::false_type)
{
if (!factory)
{
throw HueException(FileInfo {__FILE__, __LINE__, __func__},
"Resource is not constructable with default parameters, but no factory given");
}
return factory(id, state, sharedState ? stateCache : std::shared_ptr<APICache>());
}
private:
static IdType maybeStoi(const std::string& key, std::true_type) { return std::stoi(key); }
static IdType maybeStoi(const std::string& key, std::false_type) { return key; }
static std::string maybeToString(IdType id, std::true_type) { return std::to_string(id); }
static std::string maybeToString(const IdType& id, std::false_type) { return id; }
protected:
std::shared_ptr<APICache> stateCache;
std::function<Resource(IdType, const nlohmann::json&, const std::shared_ptr<APICache>&)> factory;
std::string path;
bool sharedState;
};
//! \brief Handles a ResourceList of physical devices which can be searched for
//! \tparam Resource Resource type that is in the list
template <typename Resource>
class SearchableResourceList : public ResourceList<Resource, int>
{
public:
using ResourceList<Resource, int>::ResourceList;
//! \brief Start search for new devices
//! \param deviceIds Serial numbers of the devices to search for (max. 10)
//!
//! Takes more than 40s. If many devices were found a second search command might be necessary.
void search(const std::vector<std::string>& deviceIds = {})
{
std::string requestPath = this->path;
// Remove trailing slash
requestPath.pop_back();
if (deviceIds.empty())
{
this->stateCache->getCommandAPI().POSTRequest(
requestPath, nlohmann::json::object(), FileInfo {__FILE__, __LINE__, __func__});
}
else
{
this->stateCache->getCommandAPI().POSTRequest(
requestPath, nlohmann::json {{"deviceid", deviceIds}}, FileInfo {__FILE__, __LINE__, __func__});
}
}
//! \brief Get devices found in last search
NewDeviceList getNewDevices() const
{
nlohmann::json response = this->stateCache->getCommandAPI().GETRequest(
this->path + "new", nlohmann::json::object(), FileInfo {__FILE__, __LINE__, __func__});
return NewDeviceList::parse(response);
}
protected:
//! \brief Protected defaulted move constructor
SearchableResourceList(SearchableResourceList&&) = default;
//! \brief Protected defaulted move assignment
SearchableResourceList& operator=(SearchableResourceList&&) = default;
};
//! \brief Handles a ResourceList where Resources can be added by the user
//! \tparam BaseResourceList Base resource list type (ResourceList or SearchableResourceList).
//! \tparam CreateType Type that provides parameters for creation.
//! Must have a const getRequest() function returning the JSON for the POST request.
template <typename BaseResourceList, typename CreateType>
class CreateableResourceList : public BaseResourceList
{
public:
using BaseResourceList::BaseResourceList;
//! \brief Create a new resource
//! \param params Parameters for the new resource
//! \returns The id of the created resource or 0/an empty string if failed.
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contains no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
//! \throws std::invalid_argument when IdType is int and std::stoi fails
typename BaseResourceList::IdType create(const CreateType& params)
{
std::string requestPath = this->path;
// Remove slash
requestPath.pop_back();
nlohmann::json response = this->stateCache->getCommandAPI().POSTRequest(
requestPath, params.getRequest(), FileInfo {__FILE__, __LINE__, __func__});
nlohmann::json id = utils::safeGetMember(response, 0, "success", "id");
if (id.is_string())
{
std::string idStr = id.get<std::string>();
if (idStr.find(this->path) == 0)
{
idStr.erase(0, this->path.size());
}
this->stateCache->refresh();
return this->maybeStoi(idStr);
}
return typename BaseResourceList::IdType {};
}
protected:
//! \brief Protected defaulted move constructor
CreateableResourceList(CreateableResourceList&&) = default;
//! \brief Protected defaulted move assignment
CreateableResourceList& operator=(CreateableResourceList&&) = default;
};
//! \brief Handles a group list with the special group 0
//! \tparam Resource Resource type that is in the list
//! \tparam CreateType Type that provides parameters for creation.
//! Must have a const getRequest() function returning the JSON for the POST request.
template <typename Resource, typename CreateType>
class GroupResourceList : public CreateableResourceList<ResourceList<Resource, int>, CreateType>
{
using Base = CreateableResourceList<ResourceList<Resource, int>, CreateType>;
public:
using Base::Base;
//! \brief Get group, specially handles group 0
//! \see ResourceList::get
Resource get(const int& id)
{
const nlohmann::json& state = this->stateCache->getValue();
std::string key = this->maybeToString(id);
if (!state.count(key) && id != 0)
{
throw HueException(FileInfo {__FILE__, __LINE__, __func__}, "Resource id is not valid");
}
return this->construct(id, id == 0 ? nlohmann::json {nullptr} : state[key]);
}
//! \brief Get group, specially handles group 0
//! \see ResourceList::exists
bool exists(int id) const { return id == 0 || Base::exists(id); }
protected:
//! \brief Protected defaulted move constructor
GroupResourceList(GroupResourceList&&) = default;
//! \brief Protected defaulted move assignment
GroupResourceList& operator=(GroupResourceList&&) = default;
};
} // namespace hueplusplus
#endif
@@ -0,0 +1,175 @@
/**
\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 <http://www.gnu.org/licenses/>.
**/
#ifndef INCLUDE_HUEPLUSPLUS_RULE_H
#define INCLUDE_HUEPLUSPLUS_RULE_H
#include <string>
#include "APICache.h"
#include "Action.h"
#include "Condition.h"
#include "HueCommandAPI.h"
#include "TimePattern.h"
namespace hueplusplus
{
//! \brief Rule stored in the bridge.
//!
//! Rules are used to automatically trigger Action%s when certain events happen.
//! The bridge can only support a limited number of rules, conditions and actions.
//!
//! They are deactivated if any errors occur when they are evaluated.
class Rule
{
public:
//! \brief Creates rule with shared cache
//! \param id Rule id in the bridge
//! \param baseCache Cache of the rule list.
Rule(int id, const std::shared_ptr<APICache>& baseCache);
//! \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.
//! \param currentState The current state, may be null.
Rule(int id, const HueCommandAPI& commands, std::chrono::steady_clock::duration refreshDuration, const nlohmann::json& currentState);
//! \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 Sets custom refresh interval for this rule.
//! \param refreshDuration The new minimum duration between refreshes. May be 0 or \ref c_refreshNever.
void setRefreshDuration(std::chrono::steady_clock::duration refreshDuration);
//! \brief Get rule identifier
int getId() const;
//! \brief Get rule name
//!
//! The rule name is always unique for the bridge.
std::string getName() const;
//! \brief Set rule name.
//! \param name New name for the rule.
//! Must be unique for all rules, otherwise a number is added.
//! \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 setName(const std::string& name);
//! \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 Enable or disable rule.
//! \param enabled whether the rule is triggered.
//! \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 setEnabled(bool enabled);
//! \brief Get user that created or last changed the rule.
std::string getOwner() const;
//! \brief Get the conditions that have to be met
//!
//! The rule triggers the actions when all conditions are true.
//! At least one condition must exist.
std::vector<Condition> getConditions() const;
//! \brief Get the actions that are executed
//!
//! At least one action must exist.
std::vector<Action> getActions() const;
//! \brief Set conditions for the rule
//! \param conditions All conditions that need to be fulfilled. Must not be empty.
//! \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 setConditions(const std::vector<Condition>& conditions);
//! \brief Set actions for the rule
//! \param actions The actions that are triggered when the conditions are met.
//! Must not be empty.
void setActions(const std::vector<Action>& actions);
private:
//! \brief Utility function to send a put request to the group.
//!
//! \param request The request to send
//! \param fileInfo FileInfo from calling function for exception details.
//! \returns The parsed reply
//! \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
nlohmann::json sendPutRequest(const nlohmann::json& request, FileInfo fileInfo);
private:
int id;
APICache state;
};
//! \brief Parameters for creating a new Rule.
//!
//! Can be used like a builder object with chained calls.
class CreateRule
{
public:
//! \brief Construct with necessary parameters
//! \param conditions Conditions for the rule. Must not be empty
//! \param actions Actions for the rule. Must not be empty
CreateRule(const std::vector<Condition>& conditions, const std::vector<Action>& actions);
//! \brief Set name
//! \see Rule::setName
CreateRule& setName(const std::string& name);
//! \brief Set status
//! \see Rule::setEnabled
CreateRule& setStatus(bool enabled);
//! \brief Get request to create the rule.
//! \returns JSON request for a POST to create the new rule.
nlohmann::json getRequest() const;
private:
nlohmann::json request;
};
} // namespace hueplusplus
#endif
@@ -0,0 +1,305 @@
/**
\file Scene.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 <http://www.gnu.org/licenses/>.
**/
#ifndef INCLUDE_HUEPLUSPLUS_SCENE_H
#define INCLUDE_HUEPLUSPLUS_SCENE_H
#include <map>
#include <string>
#include <vector>
#include <nlohmann/json.hpp>
#include "APICache.h"
#include "ColorUnits.h"
#include "TimePattern.h"
namespace hueplusplus
{
//! \brief Immutable state of a light
class LightState
{
public:
//! \brief Create LightState from json
//! \note Use LightStateBuilder for easier creation.
explicit LightState(const nlohmann::json& state);
//! \brief Get whether the light is on
bool isOn() const;
//! \brief Get whether a brightness is stored
bool hasBrightness() const;
//! \brief Get brightness of the light
//! \returns Stored brightness, or 0
int getBrightness() const;
//! \brief Get whether hue and saturation is stored
bool hasHueSat() const;
//! \brief Get hue and saturation of the light
//! \returns Stored hue and saturation, or {0,0} if not stored
HueSaturation getHueSat() const;
//! \brief Get whether xy color is stored
bool hasXY() const;
//! \brief Get xy color of the light
//! \returns Stored x,y and brightness, or zeros if not stored
XYBrightness getXY() const;
//! \brief Get whether color temperature is stored
bool hasCt() const;
//! \brief Get color temperature of the light
//! \returns Stored color temperature in mired, or 0 if not stored
int getCt() const;
//! \brief Get whether effect is stored
bool hasEffect() const;
//! \brief Get whether colorloop effect is active
//! \returns true when colorloop is enabled, false otherwise or if not stored
bool getColorloop() const;
//! \brief Get transition time to this light state
//! \returns Stored transition time or 4 by default
int getTransitionTime() const;
//! \brief Convert to json representation
nlohmann::json toJson() const;
//! \brief Equality comparison
bool operator==(const LightState& other) const;
//! \brief Inequality comparison
bool operator!=(const LightState& other) const;
private:
nlohmann::json state;
};
//! \brief Builder to create LightState
class LightStateBuilder
{
public:
LightStateBuilder& setOn(bool on);
LightStateBuilder& setBrightness(int brightness);
LightStateBuilder& setHueSat(const HueSaturation& hueSat);
LightStateBuilder& setXY(const XY& xy);
LightStateBuilder& setCt(int mired);
LightStateBuilder& setColorloop(bool enabled);
LightStateBuilder& setTransitionTime(int time);
LightState create();
private:
nlohmann::json state;
};
//! \brief Scene stored in the bridge
//!
//! Scenes bundle the state of multiple lights so it can be recalled later.
class Scene
{
public:
//! \brief Type of the scen
enum class Type
{
lightScene, //!< The scene affects specific lights
groupScene //!< The scene affects all light of a specific group
};
public:
//! \brief Creates scene with shared cache
//! \param id Scene id in the bridge
//! \param baseCache Cache of the scene list.
Scene(const std::string& id, const std::shared_ptr<APICache>& baseCache);
//! \brief Construct existing Scene
//! \param id Scene id
//! \param commands HueCommandAPI for requests
//! \param refreshDuration Time between refreshing the cached state
//! \param currentState The current state, may be null.
Scene(const std::string& id, const HueCommandAPI& commands, std::chrono::steady_clock::duration refreshDuration, const nlohmann::json& currentState);
//! \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 Sets custom refresh interval for this group.
//! \param refreshDuration The new minimum duration between refreshes. May be 0 or \ref c_refreshNever.
void setRefreshDuration(std::chrono::steady_clock::duration refreshDuration);
//! \brief Get scene identifier
std::string getId() const;
//! \brief Get scene name
//!
//! The scene name is always unique for the bridge. It defaults to the id.
std::string getName() const;
//! \brief Set scene name
//! \param name New name for the scene.
//! Must be unique for all schedules, otherwise a number is added.
//! \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 setName(const std::string& name);
//! \brief Get scene type
//!
//! GroupScenes are deleted when the group is deleted.
Type getType() const;
//! \brief Get group id for a GroupScene
//! \returns Group id or 0 if the scene is a LightScene.
int getGroupId() const;
//! \brief Get light ids
//!
//! For a GroupScene, the light ids are the lights in the group.
std::vector<int> getLightIds() const;
//! \brief Set light ids for LightScene
//! \param ids New light ids
//!
//! Light ids cannot be changed on GroupScene. Change the lights in the group instead.
//! \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 setLightIds(const std::vector<int>& ids);
//! \brief Get user that created or last changed the scene.
std::string getOwner() const;
//! \brief Get whether the scene can be automatically deleted
bool getRecycle() const;
//! \brief Get whether scene is locked by a rule or schedule
bool isLocked() const;
//! \brief Get app specific data
std::string getAppdata() const;
//! \brief Get version of app specific data
int getAppdataVersion() const;
//! \brief Set app specific data
//! \param data Custom data in any format, max length 16.
//! \param version Version of the data
//! \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 setAppdata(const std::string& data, int version);
//! \brief Get picture, reserved for future use.
//!
//! Currently always an empty string.
std::string getPicture() const;
//! \brief Get time the scene was created/updated.
time::AbsoluteTime getLastUpdated() const;
//! \brief Get version of the scene
//! \returns 1 for legacy scene without lightstates
//! \returns 2 for updated scenes with lightstates
int getVersion() const;
//! \brief Get stored states of the lights
//! \returns LightStates for each light in the scene, or an empty map for legacy scenes.
std::map<int, LightState> getLightStates() const;
//! \brief Set light states
//! \param states New states for each light in the scene.
//! Should contain exactly the lights in the scene. Additional states might cause an error.
//! \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 setLightStates(const std::map<int, LightState>& states);
//! \brief Store current light state of every light in the scene
//! \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 storeCurrentLightState();
//! \brief Store current light state and update transition time
//! \param transition The updated transition time to this scene
//! \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 storeCurrentLightState(int transition);
//! \brief Recall scene, putting every light in the stored state
//! \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 recall();
private:
//! \brief Send put request to specified sub path
//! \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 sendPutRequest(const std::string& path, const nlohmann::json& request, FileInfo fileInfo);
private:
std::string id;
APICache state;
};
//! \brief Parameters for creating a new Scene
//!
//! Can be used like a builder object with chained calls.
class CreateScene
{
public:
//! \brief Set name
//! \see Scene::setName
CreateScene& setName(const std::string& name);
//! \brief Set group id, making the scene a GroupScene
//! \param id Group id for the scene, not 0
//!
//! The group id cannot be changed after the scene was created.
//! \throws HueException when used after setLightIds
CreateScene& setGroupId(int id);
//! \brief Set light ids, making the scene a LightScene
//! \param ids Ids of lights in the scene
//! \throws HueException when used after setGroupId
CreateScene& setLightIds(const std::vector<int>& ids);
//! \brief Set whether the scene can be automatically deleted
//!
//! Cannot be changed after the scene was created.
CreateScene& setRecycle(bool recycle);
//! \brief Set app specific data
//! \see Scene::setAppdata
CreateScene& setAppdata(const std::string& data, int version);
//! \brief Set light states of the scene
//!
//! When omitted, the current light states are stored.
//! \see Scene::setLightStates
CreateScene& setLightStates(const std::map<int, LightState>& states);
//! \brief Get request to create the scene.
//! \returns JSON request for a POST to create the new scene
nlohmann::json getRequest() const;
private:
nlohmann::json request;
};
} // namespace hueplusplus
#endif
@@ -0,0 +1,186 @@
/**
\file Schedule.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 <http://www.gnu.org/licenses/>.
**/
#ifndef INCLUDE_HUEPLUSPLUS_SCHEDULE_H
#define INCLUDE_HUEPLUSPLUS_SCHEDULE_H
#include "APICache.h"
#include "Action.h"
#include "TimePattern.h"
namespace hueplusplus
{
//! \brief Schedule stored in the bridge
//!
//! A schedule can be created by the user to trigger actions at specific times.
class Schedule
{
public:
//! \brief Creates schedule with shared cache
//! \param id Schedule id in the bridge
//! \param baseCache Cache of the schedule list.
Schedule(int id, const std::shared_ptr<APICache>& baseCache);
//! \brief Construct Schedule that exists in the bridge
//! \param id Schedule ID
//! \param commands HueCommandAPI for requests
//! \param refreshDuration Time between refreshing the cached state
//! \param currentState The current state, may be null.
Schedule(int id, const HueCommandAPI& commands, std::chrono::steady_clock::duration refreshDuration, const nlohmann::json& currentState);
//! \brief Refreshes internal cached state
//! \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();
//! \brief Sets custom refresh interval for this schedule.
//! \param refreshDuration The new minimum duration between refreshes. May be 0 or \ref c_refreshNever.
void setRefreshDuration(std::chrono::steady_clock::duration refreshDuration);
//! \brief Get schedule identifier
int getId() const;
//! \brief Get schedule name
//!
//! The schedule name is always unique for the bridge.
std::string getName() const;
//! \brief Get schedule description
std::string getDescription() const;
//! \brief Get schedule command
Action getCommand() const;
//! \brief Get time when the event(s) will occur
//! \returns TimePattern in local timezone
time::TimePattern getTime() const;
//! \brief Check 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.
//! Only for non-recurring schedules.
bool getAutodelete() const;
//! \brief Get created time
//! \returns AbsoluteTime without variation
time::AbsoluteTime getCreated() const;
//! \brief Get start time for timers
//! \returns AbsoluteTime without variation when the timer was started.
//! \throws nlohmann::json::out_of_range when the schedule does not have a start time
time::AbsoluteTime getStartTime() const;
//! \brief Set schedule name
//! \param name New name for the schedule. Max size is 32.
//! Must be unique for all schedules, otherwise a number is added.
//! \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 setName(const std::string& name);
//! \brief Set schedule description
//! \param description New description, may be empty. Max size is 64.
//! \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 setDescription(const std::string& description);
//! \brief Set schedule command
//! \param command New action that is executed when the time event occurs.
//! \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 setCommand(const Action& command);
//! \brief Set new time when the event will occur
//! \param timePattern Any possible value of TimePattern
//! \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 setTime(const time::TimePattern& timePattern);
//! \brief Enable or disable schedule
//! \param enabled true to enable, false to disable.
//!
//! Can be used to reset a timer by setting to disabled and enabled again.
//! \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 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
//! \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 setAutodelete(bool autodelete);
private:
//! \brief Utility function to send put request to the schedule.
//! \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 sendPutRequest(const nlohmann::json& request, FileInfo fileInfo);
private:
int id;
APICache state;
};
//! \brief Parameters for creating a new Schedule.
//!
//! Can be used like a builder object with chained calls.
class CreateSchedule
{
public:
//! \brief Set name
//! \see Schedule::setName
CreateSchedule& setName(const std::string& name);
//! \brief Set description
//! \see Schedule::setDescription
CreateSchedule& setDescription(const std::string& description);
//! \brief Set command
//! \see Schedule::setCommand
CreateSchedule& setCommand(const Action& command);
//! \brief Set time
//! \see Schedule::setTime
CreateSchedule& setTime(const time::TimePattern& time);
//! \brief Set status
//! \see Schedule::setEnabled
CreateSchedule& setStatus(bool enabled);
//! \brief Set autodelete
//! \see Schedule::setAutodelete
CreateSchedule& setAutodelete(bool autodelete);
//! \brief Set recycle
//!
//! When recycle is true, it is deleted when no resourcelinks refer to it.
CreateSchedule& setRecycle(bool recycle);
//! \brief Get request to create the schedule.
//! \returns JSON request for a POST to create the new schedule
nlohmann::json getRequest() const;
private:
nlohmann::json request;
};
} // namespace hueplusplus
#endif
@@ -0,0 +1,394 @@
/**
\file Sensor.h
Copyright Notice\n
Copyright (C) 2020 Stefan Herbrechtsmeier - developer\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 <http://www.gnu.org/licenses/>.
**/
#ifndef INCLUDE_HUEPLUSPLUS_HUE_SENSOR_H
#define INCLUDE_HUEPLUSPLUS_HUE_SENSOR_H
#include <memory>
#include "BaseDevice.h"
#include "Condition.h"
#include "HueCommandAPI.h"
#include "TimePattern.h"
#include <nlohmann/json.hpp>
namespace hueplusplus
{
//! \brief Specifies light alert modes
enum class Alert
{
none, //!< No alert
select, //!< Select alert (breathe cycle)
lselect //!< Long select alert (15s breathe)
};
//! \brief Convert alert to string form
//! \param alert Enum value
//! \returns "none", "select" or "lselect"
std::string alertToString(Alert alert);
//! \brief Convert string to Alert enum
//! \param s String representation
//! \returns Alert::select or Alert::lselect when \c s matches, otherwise Alert::none
Alert alertFromString(const std::string& s);
//! \brief Class for generic or unknown sensor types
//!
//! It is recommended to instead use the classes for specific types in \ref sensors.
//! This class should only be used if the type cannot be known or is not supported.
class Sensor : public BaseDevice
{
public:
//! \brief Construct Sensor with shared cache
//! \param id Integer that specifies the id of this sensor
//! \param baseCache Cache of the SensorList.
Sensor(int id, const std::shared_ptr<APICache>& baseCache);
//! \brief Construct Sensor.
//! \param id Integer that specifies the id of this sensor
//! \param commands HueCommandAPI for communication with the bridge
//! \param refreshDuration Time between refreshing the cached state.
//! \param currentState The current state, may be null.
Sensor(int id, const HueCommandAPI& commands, std::chrono::steady_clock::duration refreshDuration, const nlohmann::json& currentState);
//!\name Config attributes
///@{
//! \brief Check whether the sensor has an on attribute
bool hasOn() const;
//! \brief check whether the sensor is turned on
//!
//! Sensors which are off do not change their status
//! \throws nlohmann::json::out_of_range when on attribute does not exist.
bool isOn() const;
//! \brief Turn sensor on or off
//! \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 setOn(bool on);
//! \brief Check whether the sensor has a battery state
bool hasBatteryState() const;
//! \brief Get battery state
//! \returns Battery state in percent
//! \throws nlohmann::json::out_of_range when sensor has no battery status.
int getBatteryState() const;
//! \brief Set battery state
//! \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 setBatteryState(int percent);
//! \brief Check whether the sensor has alerts
bool hasAlert() const;
//! \brief Get last sent alert
//! \note This is not cleared when the alert ends.
//! \throws nlohmann::json::out_of_range when sensor has no alert.
Alert getLastAlert() const;
//! \brief Send alert
//! \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 sendAlert(Alert type);
//! \brief Check whether the sensor has reachable validation
bool hasReachable() const;
//! \brief Get whether sensor is reachable
//! \throws nlohmann::json::out_of_range when sensor has no reachable validation
bool isReachable() const;
//! \brief Check whether the sensor has a user test mode
bool hasUserTest() const;
//! \brief Enable or disable user test mode
//!
//! In user test mode, changes are reported more frequently.#
//! It remains on for 120 seconds or until turned off.
//! \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 setUserTest(bool enabled);
//! \brief Check whether the sensor has a URL
bool hasURL() const;
//! \brief Get sensor URL
//!
//! Only CLIP sensors can have a URL.
std::string getURL() const;
//! \brief Set sensor URL
//! \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 setURL(const std::string& url);
//! \brief Get pending config entries, if they exist
//! \returns The keys of config entries which have been modified,
//! but were not committed to the device.
//!
//! Attempts to set pending config entries may cause errors.
std::vector<std::string> getPendingConfig() const;
//! \brief Check whether the sensor has an LED indicator
bool hasLEDIndication() const;
//! \brief Get whether the indicator LED is on
//! \throws nlohmann::json::out_of_range when sensor has no LED
bool getLEDIndication() const;
//! \brief Turn LED indicator on or off
//! \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 setLEDIndication(bool on);
//! \brief Get entire config object
//! \returns A json object with the sensor configuration.
nlohmann::json getConfig() const;
//! \brief Set attribute in the sensor config
//! \param key Key of the config attribute
//! \param value Any value to set the attribute to
//!
//! Can be used to configure sensors with additional config entries.
void setConfigAttribute(const std::string& key, const nlohmann::json& value);
///@}
//! \brief Get time of last status update
//! \returns The last update time, or a time with a zero duration from epoch
//! if the last update time is not set.
time::AbsoluteTime getLastUpdated() const;
//! \brief Get state object
nlohmann::json getState() const;
//! \brief Set part of the sensor state
//! \param key Key in the state object
//! \param value New value
//!
//! The state can usually only be set on CLIP sensors, not on physical devices.
void setStateAttribute(const std::string& key, const nlohmann::json& value);
//! \brief Get address of the given state attribute, used for conditions
//! \param key Key in the state object
//! \returns \c key prefixed with the path to the sensor state
std::string getStateAddress(const std::string& key) const;
//! \brief Check if the sensor is Hue certified
bool isCertified() const;
//! \brief Check if the sensor is primary sensor of the device
//!
//! When there are multiple sensors on one physical device (same MAC address),
//! the primary device is used for the device information.
bool isPrimary() const;
//! \brief Convert sensor to a specific type
//! \tparam T Sensor type to convert to (from \ref sensors)
//! \throws HueException when sensor type does not match requested type
template <typename T>
T asSensorType() const&
{
if (getType() != T::typeStr)
{
throw HueException(FileInfo {__FILE__, __LINE__, __func__}, "Sensor type does not match: " + getType());
}
return T(*this);
}
//! \brief Convert sensor to a specific type
//! \tparam T Sensor type to convert to (from \ref sensors)
//! \throws HueException when sensor type does not match requested type
//!
//! Move construct \c T to be more efficient when the type is wanted directly.
template <typename T>
T asSensorType() &&
{
if (getType() != T::typeStr)
{
throw HueException(FileInfo {__FILE__, __LINE__, __func__}, "Sensor type does not match: " + getType());
}
return T(std::move(*this));
}
};
//! \brief Parameters for creating a new Sensor
//!
//! Can be used like a builder object with chained calls.
class CreateSensor
{
public:
//! \brief Construct with necessary parameters
//! \param name Human readable name
//! \param modelid Model id of the sensor
//! \param swversion Software version, may be empty
//! \param type Sensor type name (see types in \ref sensors)
//! \param uniqueid Globally unique ID
//! (MAC address of the device, extended with a unique endpoint id)
//! \param manufacturername Name of the device manufacturer
CreateSensor(const std::string& name, const std::string& modelid, const std::string& swversion,
const std::string& type, const std::string& uniqueid, const std::string& manufacturername);
//! \brief Set state object
//! \param state Sensor state, contents depend on the type.
//! \returns this object for chaining calls
CreateSensor& setState(const nlohmann::json& state);
//! \brief Set config object
//! \param config Sensor config, configs depend on the type. See getters in Sensor for examples.
//! \returns this object for chaining calls
CreateSensor& setConfig(const nlohmann::json& config);
//! \brief Enable recycling, delete automatically when not referenced
//! \returns this object for chaining calls
CreateSensor& setRecycle(bool recycle);
//! \brief Get request to create the sensor
//! \returns JSON request for a POST to create the new sensor
nlohmann::json getRequest() const;
protected:
nlohmann::json request;
};
//! \brief Classes for specific sensor types
//!
//! Classes should have a typeStr member with the type name.
namespace sensors
{
//! \brief Daylight sensor to detect sunrise and sunset
//!
//! Every bridge has a daylight sensor always available.
class DaylightSensor : public BaseDevice
{
public:
//! \brief Construct from generic sensor
explicit DaylightSensor(Sensor sensor) : BaseDevice(std::move(sensor)) { }
//! \brief Check if the sensor is on
//!
//! Sensors which are off do not change their status
bool isOn() const;
//! \brief Enable or disable sensor
//! \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 setOn(bool on);
//! \brief Check whether the sensor has a battery state
bool hasBatteryState() const;
//! \brief Get battery state
//! \returns Battery state in percent
//! \throws nlohmann::json::out_of_range when sensor has no battery state.
int getBatteryState() const;
//! \brief Set battery state
//! \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 setBatteryState(int percent);
//! \brief Set GPS coordinates for the calculation
//! \param latitude Decimal latitude coordinate "DDD.DDDD{N|S}" with leading zeros ending with N or S.
//! "none" to reset. (Empty string is null, which may be used instead of none in the future)
//! \param longitude Longitude coordinate (same format as latitude), ending with W or E
//! \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 setCoordinates(const std::string& latitude, const std::string& longitude);
//! \brief Check whether coordinates are configured
//!
//! There is no way to retrieve the configured coordinates.
bool isConfigured() const;
//! \brief Get time offset in minutes to sunrise
//!
//! The daylight is true if it is \c offset minutes after sunrise.
int getSunriseOffset() const;
//! \brief Set sunrise offset time
//! \param minutes Minutes from -120 to 120
//! \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 setSunriseOffset(int minutes);
//! \brief Get time offset in minutes to sunset
//!
//! The daylight is false if it is \c offset minutes after sunset.
int getSunsetOffset() const;
//! \brief Set sunset offset time
//! \param minutes Minutes from -120 to 120
//! \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 setSunsetOffset(int minutes);
//! \brief Check whether it is daylight or not
bool isDaylight() const;
//! \brief Get time of last status update
//! \returns The last update time, or a time with a zero duration from epoch
//! if the last update time is not set.
time::AbsoluteTime getLastUpdated() const;
//! \brief Daylight sensor type name
static constexpr const char* typeStr = "Daylight";
};
detail::ConditionHelper<bool> makeCondition(const DaylightSensor& sensor);
template <typename SensorT, detail::void_t<decltype(std::declval<const SensorT>().getLastUpdated())>* = nullptr>
detail::ConditionHelper<time::AbsoluteTime> makeConditionLastUpdate(const SensorT& sensor)
{
return detail::ConditionHelper<time::AbsoluteTime>(
"/sensors/" + std::to_string(sensor.getId()) + "/state/lastupdated");
}
template <typename ButtonSensor, detail::void_t<decltype(std::declval<const ButtonSensor>().getButtonEvent())>* = nullptr>
detail::ConditionHelper<int> makeCondition(const ButtonSensor& sensor)
{
return detail::ConditionHelper<int>(
"/sensors/" + std::to_string(sensor.getId()) + "/state/buttonevent");
}
template <typename PresenceSensor, detail::void_t<decltype(std::declval<const PresenceSensor>().getPresence())>* = nullptr>
detail::ConditionHelper<bool> makeCondition(const PresenceSensor& sensor)
{
return detail::ConditionHelper<bool>(
"/sensors/" + std::to_string(sensor.getId()) + "/state/presence");
}
template <typename TemperatureSensor, detail::void_t<decltype(std::declval<const TemperatureSensor>().getPresence())>* = nullptr>
detail::ConditionHelper<int> makeCondition(const TemperatureSensor& sensor)
{
return detail::ConditionHelper<int>(
"/sensors/" + std::to_string(sensor.getId()) + "/state/temperature");
}
} // namespace sensors
} // namespace hueplusplus
#endif
@@ -0,0 +1,82 @@
/**
\file SensorList.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 <http://www.gnu.org/licenses/>.
**/
#ifndef INCLUDE_HUEPLUSPLUS_SENSOR_LIST_H
#define INCLUDE_HUEPLUSPLUS_SENSOR_LIST_H
#include "ResourceList.h"
#include "Sensor.h"
namespace hueplusplus
{
//! \brief Handles a list of Sensor%s with type specific getters
//!
//! Allows to directly get the requested sensor type or all sensors of a given type.
class SensorList : public CreateableResourceList<SearchableResourceList<Sensor>, CreateSensor>
{
public:
using CreateableResourceList::CreateableResourceList;
//! \brief Get sensor specified by id, convert to \c T
//! \param id Sensor id
//! \tparam T Sensor type to convert to (from \ref sensors)
//! \returns The sensor matching the id and type
//! \throws HueException when id does not exist or type does not match
//! \throws std::system_error when system or socket operations fail
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
template <typename T>
T getAsType(int id)
{
return get(id).asSensorType<T>();
}
//! \brief Get all sensors of type \c T
//! \tparam T Sensor type to get (from \ref sensors)
//! \returns All sensors matching the type
//! \throws HueException when response contains no body
//! \throws std::system_error when system or socket operations fail
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
template <typename T>
std::vector<T> getAllByType()
{
nlohmann::json state = this->stateCache->getValue();
std::vector<T> result;
for (auto it = state.begin(); it != state.end(); ++it)
{
// Only parse the sensors with the correct type
if (it->value("type", "") == T::typeStr)
{
result.push_back(get(maybeStoi(it.key())).asSensorType<T>());
}
}
return result;
}
protected:
//! \brief Protected defaulted move constructor
SensorList(SensorList&&) = default;
//! \brief Protected defaulted move assignment
SensorList& operator=(SensorList&&) = default;
};
} // namespace hueplusplus
#endif
@@ -0,0 +1,58 @@
/**
\file SimpleBrightnessStrategy.h
Copyright Notice\n
Copyright (C) 2017 Jan Rogall - developer\n
Copyright (C) 2017 Moritz Wirger - 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 <http://www.gnu.org/licenses/>.
**/
#ifndef INCLUDE_HUEPLUSPLUS_SIMPLE_BRIGHTNESS_STRATEGY_H
#define INCLUDE_HUEPLUSPLUS_SIMPLE_BRIGHTNESS_STRATEGY_H
#include "BrightnessStrategy.h"
#include "Light.h"
namespace hueplusplus
{
//! Class implementing the functions of BrightnessStrategy
class SimpleBrightnessStrategy : public BrightnessStrategy
{
public:
//! \brief Function for changing a lights brightness with a specified
//! transition.
//!
//! \param bri The brightness raning from 0 = off to 255 = fully lit
//! \param transition The time it takes to fade to the new brightness in
//! multiples of 100ms, 4 = 400ms and should be seen as the default \param
//! light A reference of the light
bool setBrightness(unsigned int bri, uint8_t transition, Light& light) const override;
//! \brief Function that returns the current brightness of the light
//!
//! Updates the lights state by calling refreshState()
//! \param light A reference of the light
//! \return Unsigned int representing the brightness
unsigned int getBrightness(Light& light) const override;
//! \brief Function that returns the current brightness of the light
//!
//! \note This does not update the lights state
//! \param light A const reference of the light
//! \return Unsigned int representing the brightness
unsigned int getBrightness(const Light& light) const override;
};
} // namespace hueplusplus
#endif
@@ -0,0 +1,127 @@
/**
\file SimpleColorHueStrategy.h
Copyright Notice\n
Copyright (C) 2017 Jan Rogall - developer\n
Copyright (C) 2017 Moritz Wirger - 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 <http://www.gnu.org/licenses/>.
**/
#ifndef INCLUDE_HUEPLUSPLUS_SIMPLE_COLOR_HUE_STRATEGY_H
#define INCLUDE_HUEPLUSPLUS_SIMPLE_COLOR_HUE_STRATEGY_H
#include "ColorHueStrategy.h"
#include "Light.h"
namespace hueplusplus
{
//! Class implementing the functions of ColorHueStrategy
//!
//! To be used for lights that have only color and no color temperature.
class SimpleColorHueStrategy : public ColorHueStrategy
{
public:
//! \brief Function for changing a lights color in hue with a specified
//! transition.
//!
//! The hue ranges from 0 to 65535, whereas 65535 and 0 are red, 25500 is
//! green and 46920 is blue. \param hue The hue of the color \param transition
//! The time it takes to fade to the new color in multiples of 100ms, 4 =
//! 400ms and should be seen as the default \param light A reference of the
//! light
bool setColorHue(uint16_t hue, uint8_t transition, Light& light) const override;
//! \brief Function for changing a lights color in saturation with a specified
//! transition.
//!
//! The saturation ranges from 0 to 254, whereas 0 is least saturated (white)
//! and 254 is most saturated (vibrant). \param sat The saturation of the
//! color \param transition The time it takes to fade to the new color in
//! multiples of 100ms, 4 = 400ms and should be seen as the default \param
//! light A reference of the light
bool setColorSaturation(uint8_t sat, uint8_t transition, Light& light) const override;
//! \brief Function for changing a lights color in hue and saturation format
//! with a specified transition.
//!
//! \param hueSat Color in hue and satuation.
//! \param transition The time it takes to fade to the new color in multiples of
//! 100ms, 4 = 400ms and should be seen as the default
//! \param light A reference of the light
bool setColorHueSaturation(const HueSaturation& hueSat, uint8_t transition, Light& light) const override;
//! \brief Function for changing a lights color in CIE format with a specified
//! transition.
//!
//! \param xy The color in XY and brightness
//! \param transition The time it takes to fade to the new color in multiples
//! of 100ms, 4 = 400ms and should be seen as the default \param light A
//! reference of the light
bool setColorXY(const XYBrightness& xy, uint8_t transition, Light& light) const override;
//! \brief Function for turning on/off the color loop feature of a light.
//!
//! Can be theoretically set for any light, but it only works for lights that
//! support this feature. When this feature is activated the light will fade
//! through every color on the current hue and saturation settings. Notice
//! that none of the setter functions check whether this feature is enabled
//! and the colorloop can only be disabled with this function or by simply
//! calling off() and then on(), so you could
//! alternatively call off() and then use any of the setter functions.
//! \param on Boolean to turn this feature on or off, true/1 for on and
//! false/0 for off \param light A reference of the light
bool setColorLoop(bool on, Light& light) const override;
//! \brief Function that lets the light perform one breath cycle in the
//! specified color.
//! \param hueSat The color in hue and saturation
//! \param light A reference of the light
//!
//! Blocks for the time a \ref Light::alert() needs
bool alertHueSaturation(const HueSaturation& hueSat, Light& light) const override;
//! \brief Function that lets the light perform one breath cycle in the
//! specified color.
//! \param xy The color in XY and brightness
//! \param light A reference of the light
bool alertXY(const XYBrightness& xy, Light& light) const override;
//! \brief Function that returns the current color of the light as hue and
//! saturation
//!
//! Updates the lights state by calling refreshState()
//! \param light A reference of the light
//! \return Pair containing the hue as first value and saturation as second
//! value
HueSaturation getColorHueSaturation(Light& light) const override;
//! \brief Function that returns the current color of the light as hue and
//! saturation
//!
//! \note This does not update the lights state
//! \param light A const reference of the light
//! \return Pair containing the hue as first value and saturation as second
//! value
HueSaturation getColorHueSaturation(const Light& light) const override;
//! \brief Function that returns the current color of the light as xy
//!
//! Updates the lights state by calling refreshState()
//! \param light A reference of the light
//! \return XY and brightness of current color
XYBrightness getColorXY(Light& light) const override;
//! \brief Function that returns the current color of the light as xy
//!
//! \note This does not update the lights state
//! \param light A const reference of the light
//! \return XY and brightness of current color
XYBrightness getColorXY(const Light& light) const override;
};
} // namespace hueplusplus
#endif
@@ -0,0 +1,69 @@
/**
\file SimpleColorTemperatureStrategy.h
Copyright Notice\n
Copyright (C) 2017 Jan Rogall - developer\n
Copyright (C) 2017 Moritz Wirger - 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 <http://www.gnu.org/licenses/>.
**/
#ifndef INCLUDE_HUEPLUSPLUS_SIMPLE_COLOR_TEMPERATURE_STRATEGY_H
#define INCLUDE_HUEPLUSPLUS_SIMPLE_COLOR_TEMPERATURE_STRATEGY_H
#include "ColorTemperatureStrategy.h"
#include "Light.h"
namespace hueplusplus
{
//! Class implementing the functions of ColorTemperatureStrategy
class SimpleColorTemperatureStrategy : public ColorTemperatureStrategy
{
public:
//! \brief Function for changing a lights color temperature in mired with a
//! specified transition.
//!
//! The color temperature in mired ranges from 153 to 500 whereas 153 is cold
//! and 500 is warm. \param mired The color temperature in mired \param
//! transition The time it takes to fade to the new color in multiples of
//! 100ms, 4 = 400ms and should be seen as the default \param light A
//! reference of the light
bool setColorTemperature(unsigned int mired, uint8_t transition, Light& light) const override;
//! \brief Function that lets the light perform one breath cycle in the
//! specified color.
//!
//! It uses this_thread::sleep_for to accomodate for the time an \ref
//! Light::alert() needs The color temperature in mired ranges from 153 to
//! 500 whereas 153 is cold and 500 is warm. \param mired The color
//! temperature in mired \param light A reference of the light
bool alertTemperature(unsigned int mired, Light& light) const override;
//! \brief Function that returns the current color temperature of the light
//!
//! Updates the lights state by calling refreshState()
//! The color temperature in mired ranges from 153 to 500 whereas 153 is cold
//! and 500 is warm. \param light A reference of the light \return Unsigned
//! int representing the color temperature in mired
unsigned int getColorTemperature(Light& light) const override;
//! \brief Function that returns the current color temperature of the light
//!
//! The color temperature in mired ranges from 153 to 500 whereas 153 is cold
//! and 500 is warm. \note This does not update the lights state \param light
//! A const reference of the light \return Unsigned int representing the color
//! temperature in mired
unsigned int getColorTemperature(const Light& light) const override;
};
} // namespace hueplusplus
#endif
@@ -0,0 +1,203 @@
/**
\file StateTransaction.h
Copyright Notice\n
Copyright (C) 2020 Jan Rogall - developer\n
Copyright (C) 2020 Moritz Wirger - 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 <http://www.gnu.org/licenses/>.
**/
#ifndef INCLUDE_HUEPLUSPLUS_STATE_TRANSACTION_H
#define INCLUDE_HUEPLUSPLUS_STATE_TRANSACTION_H
#include <string>
#include "Action.h"
#include "ColorUnits.h"
#include "HueCommandAPI.h"
#include <nlohmann/json.hpp>
namespace hueplusplus
{
//! \brief Transaction class which can be used for either light or group state.
//!
//! This is intended to be used in-line, all calls are chained until a \ref commit() call.
//! \code
//! light.transaction().setOn(true).setBrightness(29).setColorHue(3000).setColorSaturation(128).commit();
//! \endcode
//! \note The transaction has an internal reference to the light state.
//! You must not cause a refresh of the state between creating and committing the transaction
//! (e.g. non-const getters/setters), because that invalidates the reference.
//!
//! <h3>Advanced usage</h3>
//! Another way to use the transaction is by storing it and building up the calls separately.
//! \code
//! hueplusplus::StateTransaction t = light.transaction();
//! if(shouldTurnOn)
//! t.setOn(true);
//! t.commit();
//! \endcode
//! In this case, it is especially important that the light and the state of the light MUST NOT invalidate.
//! That means
//! \li the light variable has to live longer than the transaction
//! \li especially no non-const method calls on the light while the transaction is open,
//! or committing other transactions
//!
//! In general, this method is easier to screw up and should only be used when really necessary.
class StateTransaction
{
public:
//! \brief Creates a StateTransaction to a group or light state
//! \param commands HueCommandAPI for making requests
//! \param path Path to which the final PUT request is made (without username)
//! \param currentState Optional, the current state to check whether changes are needed.
//! Pass nullptr to always include all requests (for groups, because individual lights might be different).
StateTransaction(const HueCommandAPI& commands, const std::string& path, nlohmann::json* currentState);
//! \brief Deleted copy constructor, do not store StateTransaction in a variable.
StateTransaction(const StateTransaction&) = delete;
StateTransaction(StateTransaction&&) = default;
//! \brief Commit transaction and make request.
//! \param trimRequest Optional. When true, request parameters that are unneccessary based on
//! the current state are removed. This reduces load on the bridge. On the other hand, an outdated
//! state might cause requests to be dropped unexpectedly. Has no effect on groups.
//! \returns true on success or when no change was requested.
//! \note After changing the state of a Light or Group,
//! refresh() must be called if the updated values are needed immediately.
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contains no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
bool commit(bool trimRequest = true);
//! \brief Create an Action from the transaction
//! \returns An Action that can be used to execute this transaction on a Schedule or Rule.
Action toAction();
//! \brief Turn light on or off.
//! \param on true for on, false for off
//! \returns This transaction for chaining calls
StateTransaction& setOn(bool on);
//! \brief Set light brightness.
//! \param brightness Brightness from 0 = off to 254 = fully lit.
//! \returns This transaction for chaining calls
//! \note If this transaction is for a light, the light needs to have brightness control.
//! \note Brightness 0 will also turn off the light if nothing else is specified,
//! any other value will also turn on the light.
StateTransaction& setBrightness(uint8_t brightness);
//! \brief Set light hue.
//! \param hue Color hue from 0 to 65535
//! \returns This transaction for chaining calls
//! \note If this transaction is for a light, the light needs to have rgb color control.
//! \note Will also turn on the light if nothing else is specified
StateTransaction& setColorHue(uint16_t hue);
//! \brief Set light saturation.
//! \param saturation Color saturation from 0 to 254
//! \returns This transaction for chaining calls
//! \note If this transaction is for a light, the light needs to have rgb color control.
//! \note Will also turn on the light if nothing else is specified
StateTransaction& setColorSaturation(uint8_t saturation);
//! \brief Set light color in hue and saturation
//! \param hueSat Color in hue and saturation
//! \returns This transaction for chaining calls
//! \note If this transaction is for a light, the light needs to have rgb color control.
//! \note Will also turn on the light if nothing else is specified
StateTransaction& setColor(const HueSaturation& hueSat);
//! \brief Set light color in xy space (without brightness).
//! \param xy x and y coordinates in CIE color space
//! \returns This transaction for chaining calls
//! \note If this transaction is for a light, the light needs to have rgb color control.
//! \note Will also turn on the light if nothing else is specified
StateTransaction& setColor(const XY& xy);
//! \brief Set light color and brightness in xy space
//! \param xy x,y and brightness in CIE color space
//! \returns This transaction for chaining calls
//! \note If this transaction is for a light, the light needs to have rgb color control.
//! \note Will also turn on the light if nothing else is specified
StateTransaction& setColor(const XYBrightness& xy);
//! \brief Set light color temperature.
//! \param mired Color temperature in mired from 153 to 500
//! \returns This transaction for chaining calls
//! \note If this transaction is for a light, the light needs to have color temperature control.
//! \note Will also turn on the light if nothing else is specified
StateTransaction& setColorTemperature(unsigned int mired);
//! \brief Enables or disables color loop.
//! \param on true to enable, false to disable color loop.
//! \returns This transaction for chaining calls
//! \note If this transaction is for a light, the light needs to have rgb color control.
//! \note Will also turn on the light if nothing else is specified
StateTransaction& setColorLoop(bool on);
//! \brief Increment/Decrement brightness.
//! \param increment Brightness change from -254 to 254.
//! \returns This transaction for chaining calls
//! \note If this transaction is for a light, the light needs to have brightness control.
StateTransaction& incrementBrightness(int increment);
//! \brief Increment/Decrement saturaction.
//! \param increment Saturation change from -254 to 254.
//! \returns This transaction for chaining calls
//! \note If this transaction is for a light, the light needs to have rgb color control.
StateTransaction& incrementSaturation(int increment);
//! \brief Increment/Decrement hue.
//! \param increment Hue change from -65535 to 65535.
//! \returns This transaction for chaining calls
//! \note If this transaction is for a light, the light needs to have rgb color control.
StateTransaction& incrementHue(int increment);
//! \brief Increment/Decrement color temperature.
//! \param increment Color temperature change in mired from -65535 to 65535.
//! \returns This transaction for chaining calls
//! \note If this transaction is for a light, the light needs to have color temperature control.
StateTransaction& incrementColorTemperature(int increment);
//! \brief Increment/Decrement color xy.
//! \param xInc x color coordinate change from -0.5 to 0.5.
//! \param yInc y color coordinate change from -0.5 to 0.5.
//! \returns This transaction for chaining calls
//! \note If this transaction is for a light, the light needs to have rgb color control.
StateTransaction& incrementColorXY(float xInc, float yInc);
//! \brief Set transition time for the request.
//! \param transition Transition time in 100ms, default for any request is 400ms.
//! \returns This transaction for chaining calls
//! \note The transition only applies to the current request.
//! A request without any changes only containing a transition is pointless and is not sent.
StateTransaction& setTransition(uint16_t transition);
//! \brief Trigger an alert.
//!
//! The light performs one breathe cycle.
//! \returns This transaction for chaining calls
StateTransaction& alert();
//! \brief Trigger a long alert (15s).
//! \returns This transaction for chaining calls
StateTransaction& longAlert();
//! \brief Stop an ongoing long alert.
//! \returns This transaction for chaining calls
StateTransaction& stopAlert();
protected:
//! \brief Remove parts from request that are already set in state
void trimRequest();
protected:
const HueCommandAPI& commands;
std::string path;
nlohmann::json* state;
nlohmann::json request;
};
} // namespace hueplusplus
#endif
@@ -0,0 +1,436 @@
/**
\file TimePattern.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 <http://www.gnu.org/licenses/>.
**/
#ifndef INCLUDE_HUEPLUSPLUS_TIME_PATTERN
#define INCLUDE_HUEPLUSPLUS_TIME_PATTERN
#include <chrono>
#include <string>
#include <cstddef>
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
//! <code>YYYY-MM-DD</code><strong>T</strong><code>hh:mm:ss</code>.
//!
//! 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
//! <code>YYYY-MM-DD</code><strong>T</strong><code>hh:mm:ss</code>
//! \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 an UTC timestamp to a time_point
//! \param timestamp UTC Timestamp the format
//! <code>YYYY-MM-DD</code><strong>T</strong><code>hh:mm:ss</code>
//! \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 parseUTCTimestamp(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 <code>hh:mm:ss</code>
//! \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 <code>hh:mm:ss</code>
//! \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
class AbsoluteTime
{
using clock = std::chrono::system_clock;
public:
//! \brief Create absolute time point
//! \param baseTime Absolute time point
explicit AbsoluteTime(clock::time_point baseTime);
//! \brief Get base time point
//!
//! Can be used for calculation with other system_clock time_points
clock::time_point getBaseTime() const;
//! \brief Get formatted string as expected by Hue API
//! \returns Timestamp in the format
//! <code>YYYY-MM-DD</code><strong>T</strong><code>hh:mm:ss</code> in local timezone
std::string toString() const;
//! \brief Parse AbsoluteTime from formatted string in local timezone
//! \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);
//! \brief Parse AbsoluteTime from formatted UTC 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 parseUTC(const std::string& s);
private:
clock::time_point base;
};
//! 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 AbsoluteVariedTime : public 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
//! <code>baseTime - variation</code> and <code>baseTime + variation</code>
explicit AbsoluteVariedTime(clock::time_point baseTime, clock::duration variation = std::chrono::seconds(0));
//! \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
//! <code>YYYY-MM-DD</code><strong>T</strong><code>hh:mm:ss</code>
//! \returns when there is variation: Timestamp in the format
//! <code>YYYY-MM-DD</code><strong>T</strong><code>hh:mm:ss</code><strong>A</strong><code>hh:mm:ss</code>
//! with base time first, variation second
std::string toString() const;
//! \brief Parse AbsoluteTime from formatted string in local timezone
//! \param s Timestamp in the same format as returned by \ref toString()
//! \returns AbsoluteVariedTime with base time and variation from \c s
static AbsoluteVariedTime parse(const std::string& s);
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 <code>daytime - variation</code> and <code>daytime + variation</code>
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:
//! <strong>W</strong><code>bbb</code><strong>/T</strong><code>hh:mm:ss</code>
//! \returns with variation:
//! <strong>W</strong><code>bbb</code><strong>/T</strong><code>hh:mm:ss</code><strong>A</strong><code>hh:mm:ss</code>,
//! where daytime is first and variation is second.
std::string toString() const;
private:
clock::duration time;
clock::duration variation;
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:
//! <strong>T</strong><code>hh:mm:ss</code><strong>/T</strong><code>hh:mm:ss</code>,
//! with start time first and end time second.
//! \returns with repetition that is not daily:
//! <strong>W</strong><code>bbb</code><strong>/T</strong><code>hh:mm:ss</code><strong>/T</strong><code>hh:mm:ss</code>
std::string toString() const;
private:
clock::duration start;
clock::duration end;
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: <strong>PT</strong><code>hh:mm:ss</code>
//! \returns one-off timer with variation:
//! <strong>PT</strong><code>hh:mm:ss</code><strong>A</strong><code>hh:mm:ss</code>,
//! with expiry time first and variation second.
//! \returns recurring timer: <strong>R/PT</strong><code>hh:mm:ss</code>
//! \returns recurring timer with n repetitions:
//! <strong>R</strong><code>nn</code><strong>/PT</strong><code>hh:mm:ss</code>
//! \returns recurring timer with random variation:
//! <strong>R</strong><code>nn</code><strong>/PT</strong><code>hh:mm:ss</code><strong>A</strong><code>hh:mm:ss</code>
//! \returns infinite recurring timer with random variation:
//! <strong>R</strong><strong>/PT</strong><code>hh:mm:ss</code><strong>A</strong><code>hh:mm:ss</code>
std::string toString() const;
private:
clock::duration expires;
clock::duration variation;
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, //!< \brief No active type
absolute, //!< \brief Active type is AbsoluteVariedTime
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 AbsoluteVariedTime
explicit TimePattern(const AbsoluteVariedTime& 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
AbsoluteVariedTime 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:
//! \brief Calls destructor of active union member
void destroy();
private:
Type type;
union
{
std::nullptr_t undefined;
AbsoluteVariedTime absolute;
RecurringTime recurring;
TimeInterval interval;
Timer timer;
};
};
} // namespace time
} // namespace hueplusplus
#endif
@@ -0,0 +1,49 @@
/**
\file UPnP.h
Copyright Notice\n
Copyright (C) 2017 Jan Rogall - developer\n
Copyright (C) 2017 Moritz Wirger - 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 <http://www.gnu.org/licenses/>.
**/
#ifndef INCLUDE_HUEPLUSPLUS_UPNP_H
#define INCLUDE_HUEPLUSPLUS_UPNP_H
#include <memory>
#include <string>
#include <vector>
#include "IHttpHandler.h"
namespace hueplusplus
{
//! Class that looks for UPnP devices using an m-search package
class UPnP
{
public:
//! \brief Searches for UPnP devices and returns all found ones.
//!
//! It does it by sending an m-search packet and waits for all responses.
//! Since responses can be received multiple times this function conveniently removes all duplicates.
//! \param handler HttpHandler for communication
//! \return A vector containing pairs of address and name of all found devices
//! \throws std::system_error when system or socket operations fail
std::vector<std::pair<std::string, std::string>> getDevices(std::shared_ptr<const IHttpHandler> handler);
};
} // namespace hueplusplus
#endif
@@ -0,0 +1,117 @@
/**
\file Utils.h
Copyright Notice\n
Copyright (C) 2020 Jan Rogall - developer\n
Copyright (C) 2020 Moritz Wirger - 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 <http://www.gnu.org/licenses/>.
**/
#ifndef INCLUDE_HUEPLUSPLUS_UTILS_H
#define INCLUDE_HUEPLUSPLUS_UTILS_H
#include <nlohmann/json.hpp>
namespace hueplusplus
{
//! \brief Utility functions used in multiple places.
namespace utils
{
namespace detail
{
// Forward declaration
template <typename KeyT, typename... Paths>
nlohmann::json safeGetMemberHelper(const nlohmann::json& json, std::size_t index, Paths&&... otherPaths);
inline nlohmann::json safeGetMemberHelper(const nlohmann::json& json)
{
return json;
}
template <typename KeyT, typename... Paths,
std::enable_if_t<!std::is_integral<std::remove_reference_t<KeyT>>::value>* = nullptr>
nlohmann::json safeGetMemberHelper(const nlohmann::json& json, KeyT&& key, Paths&&... otherPaths)
{
auto memberIt = json.find(std::forward<KeyT>(key));
if (memberIt == json.end())
{
return nullptr;
}
return safeGetMemberHelper(*memberIt, std::forward<Paths>(otherPaths)...);
}
// Needs to be after the other safeGetMemberHelper, otherwise another forward declaration is needed
template <typename... Paths>
nlohmann::json safeGetMemberHelper(const nlohmann::json& json, std::size_t index, Paths&&... otherPaths)
{
if (!json.is_array() || json.size() <= index)
{
return nullptr;
}
return safeGetMemberHelper(json[index], std::forward<Paths>(otherPaths)...);
}
} // namespace detail
//! \brief Function for validating that a request was executed correctly
//!
//! \param path The path the PUT request was made to
//! \param request The request that was sent initially
//! \param reply The reply that was received
//! \return True if request was executed correctly
bool validatePUTReply(const std::string& path, const nlohmann::json& request, const nlohmann::json& reply);
bool validateReplyForLight(const nlohmann::json& request, const nlohmann::json& reply, int lightId);
//! \brief Checks equality to 4 decimal places
//!
//! Floats in Hue json responses are rounded to 4 decimal places.
inline bool floatEquals(float lhs, float rhs)
{
return std::abs(lhs - rhs) <= 1E-4f;
}
//! \brief Returns the object/array member or null if it does not exist
//!
//! \param json The base json value
//! \param paths Any number of child accesses (e.g. 0, "key" would access json[0]["key"])
//! \returns The specified member or null if any intermediate object does not contain the specified child.
template <typename... Paths>
nlohmann::json safeGetMember(const nlohmann::json& json, Paths&&... paths)
{
return detail::safeGetMemberHelper(json, std::forward<Paths>(paths)...);
}
} // namespace utils
namespace detail
{
//! \brief Makes a class with protected copy constructor copyable.
//!
//! Used in private members to expose mutable references to \c T
//! while not allowing them to be assigned to.
//! Make sure \c T is actually designed to be used this way!
template <typename T>
class MakeCopyable : public T
{
public:
// Make copy constructor and assignment operator public
using T::T;
using T::operator=;
};
} // namespace detail
} // namespace hueplusplus
#endif
@@ -0,0 +1,72 @@
/**
\file WinHttpHandler.h
Copyright Notice\n
Copyright (C) 2017 Jan Rogall - developer\n
Copyright (C) 2017 Moritz Wirger - 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 <http://www.gnu.org/licenses/>.
**/
#ifndef INCLUDE_HUEPLUSPLUS_WINHTTPHANDLER_H
#define INCLUDE_HUEPLUSPLUS_WINHTTPHANDLER_H
#include <string>
#include <vector>
#include <winsock2.h>
#include "BaseHttpHandler.h"
namespace hueplusplus
{
//! Class to handle http requests and multicast requests on windows systems
class WinHttpHandler : public BaseHttpHandler
{
public:
//! \brief Ctor needed for initializing wsaData
WinHttpHandler();
//! \brief Dtor needed for wsaData cleanup
~WinHttpHandler();
//! \brief Function that sends a given message to the specified host and
//! returns the response.
//!
//! \param msg String that contains the message that is sent to the specified
//! address \param adr String that contains an ip or hostname in dotted
//! decimal notation like "192.168.2.1" \param port Optional integer that
//! specifies the port to which the request is sent to. Default is 80 \return
//! String containing the response of the host
std::string send(const std::string& msg, const std::string& adr, int port = 80) const override;
//! \brief Function that sends a multicast request with the specified message.
//!
//! \param msg String that contains the request that is sent to the specified
//! address \param adr Optional String that contains an ip or hostname in
//! dotted decimal notation, default is "239.255.255.250" \param port Optional
//! integer that specifies the port to which the request is sent. Default is
//! 1900 \param timeout Optional The timeout of the
//! request. Default is 5 seconds \return Vector containing strings of each
//! answer received
std::vector<std::string> sendMulticast(const std::string& msg, const std::string& adr = "239.255.255.250",
int port = 1900, std::chrono::steady_clock::duration timeout = std::chrono::seconds(5)) const override;
private:
WSADATA wsaData;
};
} // namespace hueplusplus
#endif
@@ -0,0 +1,329 @@
/**
\file ZLLSensors.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 <http://www.gnu.org/licenses/>.
*/
#ifndef INCLUDE_HUEPLUSPLUS_ZLL_SENSORS_H
#define INCLUDE_HUEPLUSPLUS_ZLL_SENSORS_H
#include "Sensor.h"
namespace hueplusplus
{
namespace sensors
{
//! \brief ZigBee Green Power sensor for button presses
class ZGPSwitch : public BaseDevice
{
public:
//! \brief Construct from generic sensor
explicit ZGPSwitch(Sensor sensor) : BaseDevice(std::move(sensor)) { }
//! \brief Check if sensor is on
//!
//! Sensors which are off do not change their status
bool isOn() const;
//! \brief Enable or disable sensor
//! \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 setOn(bool on);
//! \brief Get the code of the last switch event.
//!
//! Possible values are \ref c_button1 etc., or any other value.
int getButtonEvent() const;
//! \brief Code for tap button 1
static constexpr int c_button1 = 34;
//! \brief Code for tap button 2
static constexpr int c_button2 = 16;
//! \brief Code for tap button 3
static constexpr int c_button3 = 17;
//! \brief Code for tap button 4
static constexpr int c_button4 = 18;
//! \brief ZGPSwitch sensor type name
static constexpr const char* typeStr = "ZGPSwitch";
};
//! \brief ZigBee sensor reporting button presses
class ZLLSwitch : public BaseDevice
{
public:
//! \brief Construct from generic sensor
explicit ZLLSwitch(Sensor sensor) : BaseDevice(std::move(sensor)) { }
//! \brief Check if sensor is on
//!
//! Sensors which are off do not change their status
bool isOn() const;
//! \brief Enable or disable sensor
//! \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 setOn(bool on);
//! \brief Check whether the sensor has a battery state
bool hasBatteryState() const;
//! \brief Get battery state
//! \returns Battery state in percent
int getBatteryState() const;
//! \brief Get last sent alert
//! \note This is not cleared when the alert ends.
Alert getLastAlert() const;
//! \brief Send alert
//! \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 sendAlert(Alert type);
//! \brief Check whether the sensor is reachable
bool isReachable() const;
//! \brief Get the code of the last switch event.
//!
//! Possible values are \ref c_ON_INITIAL_PRESS etc., or any other value.
int getButtonEvent() const;
//! \brief Get time of last status update
//! \returns The last update time, or a time with a zero duration from epoch
//! if the last update time is not set.
time::AbsoluteTime getLastUpdated() const;
//! \brief Button 1 (ON) pressed
static constexpr int c_ON_INITIAL_PRESS = 1000;
//! \brief Button 1 (ON) held
static constexpr int c_ON_HOLD = 1001;
//! \brief Button 1 (ON) released short press
static constexpr int c_ON_SHORT_RELEASED = 1002;
//! \brief Button 1 (ON) released long press
static constexpr int c_ON_LONG_RELEASED = 1003;
//! \brief Button 2 (DIM UP) pressed
static constexpr int c_UP_INITIAL_PRESS = 2000;
//! \brief Button 2 (DIM UP) held
static constexpr int c_UP_HOLD = 2001;
//! \brief Button 2 (DIM UP) released short press
static constexpr int c_UP_SHORT_RELEASED = 2002;
//! \brief Button 2 (DIM UP) released long press
static constexpr int c_UP_LONG_RELEASED = 2003;
//! \brief Button 3 (DIM DOWN) pressed
static constexpr int c_DOWN_INITIAL_PRESS = 3000;
//! \brief Button 3 (DIM DOWN) held
static constexpr int c_DOWN_HOLD = 3001;
//! \brief Button 3 (DIM DOWN) released short press
static constexpr int c_DOWN_SHORT_RELEASED = 3002;
//! \brief Button 3 (DIM DOWN) released long press
static constexpr int c_DOWN_LONG_RELEASED = 3003;
//! \brief Button 4 (OFF) pressed
static constexpr int c_OFF_INITIAL_PRESS = 4000;
//! \brief Button 4 (OFF) held
static constexpr int c_OFF_HOLD = 4001;
//! \brief Button 4 (OFF) released short press
static constexpr int c_OFF_SHORT_RELEASED = 4002;
//! \brief Button 4 (OFF) released long press
static constexpr int c_OFF_LONG_RELEASED = 4003;
//! \brief ZLLSwitch sensor type name
static constexpr const char* typeStr = "ZLLSwitch";
};
//! \brief Sensor detecting presence in the vicinity
class ZLLPresence : public BaseDevice
{
public:
//! \brief Construct from generic sensor
explicit ZLLPresence(Sensor sensor) : BaseDevice(std::move(sensor)) { }
//! \brief Check if sensor is on
//!
//! Sensors which are off do not change their status
bool isOn() const;
//! \brief Enable or disable sensor
//! \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 setOn(bool on);
//! \brief Check whether the sensor has a battery state
bool hasBatteryState() const;
//! \brief Get battery state
//! \returns Battery state in percent
int getBatteryState() const;
//! \brief Get last sent alert
//! \note This is not cleared when the alert ends.
Alert getLastAlert() const;
//! \brief Send alert
//! \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 sendAlert(Alert type);
//! \brief Check whether the sensor is reachable
bool isReachable() const;
//! \brief Get sensor sensitivity
int getSensitivity() const;
//! \brief Get maximum sensitivity
int getMaxSensitivity() const;
//! \brief Set sensor sensitivity
//! \param sensitivity Sensitivity from 0 to max sensitivity (inclusive)
void setSensitivity(int sensitivity);
//! \brief Get presence status
bool getPresence() const;
//! \brief Get time of last status update
//! \returns The last update time, or a time with a zero duration from epoch
//! if the last update time is not set.
time::AbsoluteTime getLastUpdated() const;
//! \brief ZLLPresence sensor type name
static constexpr const char* typeStr = "ZLLPresence";
};
//! \brief ZigBee temperature sensor
class ZLLTemperature : public BaseDevice
{
public:
//! \brief Construct from generic sensor
explicit ZLLTemperature(Sensor sensor) : BaseDevice(std::move(sensor)) { }
//! \brief Check if sensor is on
//!
//! Sensors which are off do not change their status
bool isOn() const;
//! \brief Enable or disable sensor
//! \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 setOn(bool on);
//! \brief Check whether the sensor has a battery state
bool hasBatteryState() const;
//! \brief Get battery state
//! \returns Battery state in percent
int getBatteryState() const;
//! \brief Get last sent alert
//! \note This is not cleared when the alert ends.
Alert getLastAlert() const;
//! \brief Send alert
//! \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 sendAlert(Alert type);
//! \brief Check whether the sensor is reachable
bool isReachable() const;
//! \brief Get recorded temperature
//! \returns Temperature in 0.01 degrees Celsius.
int getTemperature() const;
//! \brief Get time of last status update
//! \returns The last update time, or a time with a zero duration from epoch
//! if the last update time is not set.
time::AbsoluteTime getLastUpdated() const;
//! \brief ZLLTemperature sensor type name
static constexpr const char* typeStr = "ZLLTemperature";
};
//! \brief ZigBee sensor detecting ambient light level
class ZLLLightLevel : public BaseDevice
{
public:
//! \brief Construct from generic sensor
explicit ZLLLightLevel(Sensor sensor) : BaseDevice(std::move(sensor)) { }
//! \brief Check if sensor is on
//!
//! Sensors which are off do not change their status
bool isOn() const;
//! \brief Enable or disable sensor
//! \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 setOn(bool on);
//! \brief Check whether the sensor has a battery state
bool hasBatteryState() const;
//! \brief Get battery state
//! \returns Battery state in percent
int getBatteryState() const;
//! \brief Check whether the sensor is reachable
bool isReachable() const;
//! \brief Get threshold to detect darkness
int getDarkThreshold() const;
//! \brief Set threshold to detect darkness
//! \param threshold Light level as reported by \ref getLightLevel
//! \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 setDarkThreshold(int threshold);
//! \brief Get offset over dark threshold to detect daylight
int getThresholdOffset() const;
//! \brief Set offset to detect daylight
//! \param offset Offset to dark threshold to detect daylight. Must be greater than 1.
//! \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 setThresholdOffset(int offset);
//! \brief Get measured light level
//! \returns Light level in <code>10000*log10(lux)+1</code> (logarithmic scale)
int getLightLevel() const;
//! \brief Check whether light level is below dark threshold
bool isDark() const;
//! \brief Check whether light level is above light threshold
//!
//! Light threshold is dark threshold + offset
bool isDaylight() const;
//! \brief Get time of last status update
//! \returns The last update time, or a time with a zero duration from epoch
//! if the last update time is not set.
time::AbsoluteTime getLastUpdated() const;
//! \brief ZLLLightLevel sensor type name
static constexpr const char* typeStr = "ZLLLightLevel";
};
detail::ConditionHelper<bool> makeConditionDark(const ZLLLightLevel& sensor);
detail::ConditionHelper<bool> makeConditionDaylight(const ZLLLightLevel& sensor);
detail::ConditionHelper<int> makeConditionLightLevel(const ZLLLightLevel& sensor);
} // namespace sensors
} // namespace hueplusplus
#endif
+25
View File
@@ -0,0 +1,25 @@
path_classifiers:
#docs:
# The default behavior is to tag all files created during the
# build as `generated`. Results are hidden for generated code. You can tag
# further files as being generated by adding them to the `generated` section.
#generated:
# By default, all files not checked into the repository are considered to be
# 'generated'.
# The default behavior is to tag library code as `library`. Results are hidden
# for library code. You can tag further files as being library code by adding them
# to the `library` section.
library:
# Classify all files in the top-level directory lib/mbedtls as library code.
- lib/mbedtls
# The default behavior is to tag template files as `template`. Results are hidden
# for template files. You can tag further files as being template files by adding
# them to the `template` section.
#template:
test:
# Classify all files in the top-level directory test/ as test code.
- test
+177
View File
@@ -0,0 +1,177 @@
#include "hueplusplus/APICache.h"
/**
\file BaseHttpHandler.cpp
Copyright Notice\n
Copyright (C) 2020 Jan Rogall - developer\n
Copyright (C) 2020 Moritz Wirger - 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 <http://www.gnu.org/licenses/>.
**/
#include "hueplusplus/HueExceptionMacro.h"
namespace hueplusplus
{
APICache::APICache(
std::shared_ptr<APICache> baseCache, const std::string& subEntry, std::chrono::steady_clock::duration refresh)
: base(baseCache),
path(subEntry),
commands(baseCache->commands),
refreshDuration(refresh),
lastRefresh(baseCache->lastRefresh)
{ }
APICache::APICache(const std::string& path, const HueCommandAPI& commands, std::chrono::steady_clock::duration refresh,
const nlohmann::json& initial)
: path(path),
commands(commands),
refreshDuration(refresh),
lastRefresh(initial.is_null() ? std::chrono::steady_clock::time_point() : std::chrono::steady_clock::now()),
value(initial)
{ }
void APICache::refresh()
{
// Only refresh part of the cache, because that is more efficient
if (base && base->needsRefresh())
{
base->refresh();
}
else
{
nlohmann::json result = commands.GETRequest(getRequestPath(), nlohmann::json::object(), CURRENT_FILE_INFO);
lastRefresh = std::chrono::steady_clock::now();
if (base)
{
base->value[path] = std::move(result);
}
else
{
value = std::move(result);
}
}
}
nlohmann::json& APICache::getValue()
{
if (needsRefresh())
{
refresh();
}
if (base)
{
// Do not call getValue here, because that could cause another refresh
// if base has refresh duration 0
nlohmann::json& baseState = base->value;
auto pos = baseState.find(path);
if (pos != baseState.end())
{
return *pos;
}
else
{
throw HueException(CURRENT_FILE_INFO, "Child path not present in base cache");
}
}
else
{
return value;
}
}
const nlohmann::json& APICache::getValue() const
{
if (base)
{
// Make const reference to not refresh
const APICache& b = *base;
return b.getValue().at(path);
}
else
{
if (lastRefresh.time_since_epoch().count() == 0)
{
// No value has been requested yet
throw HueException(CURRENT_FILE_INFO,
"Tried to call const getValue(), but no value was cached. "
"Call refresh() or non-const getValue() first.");
}
return value;
}
}
void APICache::setRefreshDuration(std::chrono::steady_clock::duration refreshDuration)
{
this->refreshDuration = refreshDuration;
}
std::chrono::steady_clock::duration APICache::getRefreshDuration() const
{
return refreshDuration;
}
HueCommandAPI& APICache::getCommandAPI()
{
return commands;
}
const HueCommandAPI& APICache::getCommandAPI() const
{
return commands;
}
bool APICache::needsRefresh()
{
using clock = std::chrono::steady_clock;
if (base)
{
// Update lastRefresh in case base was refreshed
lastRefresh = std::max(lastRefresh, base->lastRefresh);
}
// Explicitly check for zero in case refreshDuration is duration::max()
// Negative duration causes overflow check to overflow itself
if (lastRefresh.time_since_epoch().count() == 0 || refreshDuration.count() < 0)
{
// No value set yet
return true;
}
// Check if nextRefresh would overflow (assumes lastRefresh is not negative, which it should not be).
// If addition would overflow, do not refresh
else if (clock::duration::max() - refreshDuration > lastRefresh.time_since_epoch())
{
clock::time_point nextRefresh = lastRefresh + refreshDuration;
if (clock::now() >= nextRefresh)
{
return true;
}
}
return false;
}
std::string APICache::getRequestPath() const
{
std::string result;
if (base)
{
result = base->getRequestPath();
result.push_back('/');
}
result.append(path);
return result;
}
} // namespace hueplusplus
+83
View File
@@ -0,0 +1,83 @@
/**
\file Action.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 <http://www.gnu.org/licenses/>.
**/
#include <hueplusplus/Action.h>
#include <hueplusplus/HueExceptionMacro.h>
namespace hueplusplus
{
Action::Action(const nlohmann::json& json) : json(json) { }
std::string Action::getAddress() const
{
return json.at("address").get<std::string>();
}
Action::Method Action::getMethod() const
{
return parseMethod(json.at("method").get<std::string>());
}
const nlohmann::json& Action::getBody() const
{
return json.at("body");
}
const nlohmann::json& Action::toJson() const
{
return json;
}
Action::Method Action::parseMethod(const std::string& s)
{
if (s == "POST")
{
return Method::post;
}
else if (s == "PUT")
{
return Method::put;
}
else if (s == "DELETE")
{
return Method::deleteMethod;
}
throw HueException(CURRENT_FILE_INFO, "Unknown ScheduleCommand method: " + s);
}
std::string Action::methodToString(Method m)
{
switch (m)
{
case Method::post:
return "POST";
case Method::put:
return "PUT";
case Method::deleteMethod:
return "DELETE";
default:
throw HueException(
CURRENT_FILE_INFO, "Unknown ScheduleCommand method enum: " + std::to_string(static_cast<int>(m)));
}
}
} // namespace hueplusplus
+128
View File
@@ -0,0 +1,128 @@
/**
\file BaseDevice.cpp
Copyright Notice\n
Copyright (C) 2020 Stefan Herbrechtsmeier - 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 <http://www.gnu.org/licenses/>.
**/
#include "hueplusplus/BaseDevice.h"
#include <cmath>
#include <iostream>
#include <thread>
#include "hueplusplus/HueExceptionMacro.h"
#include "hueplusplus/Utils.h"
#include <nlohmann/json.hpp>
namespace hueplusplus
{
int BaseDevice::getId() const
{
return id;
}
std::string BaseDevice::getType() const
{
return state.getValue().at("type").get<std::string>();
}
std::string BaseDevice::getName()
{
return state.getValue().at("name").get<std::string>();
}
std::string BaseDevice::getName() const
{
return state.getValue().at("name").get<std::string>();
}
std::string BaseDevice::getModelId() const
{
return state.getValue().at("modelid").get<std::string>();
}
std::string BaseDevice::getUId() const
{
return state.getValue().value("uniqueid", "");
}
std::string BaseDevice::getManufacturername() const
{
return state.getValue().value("manufacturername", "");
}
std::string BaseDevice::getProductname() const
{
return state.getValue().value("productname", "");
}
std::string BaseDevice::getSwVersion()
{
return state.getValue().at("swversion").get<std::string>();
}
std::string BaseDevice::getSwVersion() const
{
return state.getValue().at("swversion").get<std::string>();
}
bool BaseDevice::setName(const std::string& name)
{
nlohmann::json request = {{"name", name}};
nlohmann::json reply = sendPutRequest("/name", request, CURRENT_FILE_INFO);
// Check whether request was successful (returned name is not necessarily the actually set name)
// If it already exists, a number is added, if it is too long to be returned, "Updated" is returned
return utils::safeGetMember(reply, 0, "success", "/lights/" + std::to_string(id) + "/name").is_string();
}
BaseDevice::BaseDevice(int id, const std::shared_ptr<APICache>& baseCache)
: id(id), state(baseCache, std::to_string(id), baseCache->getRefreshDuration())
{ }
BaseDevice::BaseDevice(
int id, const HueCommandAPI& commands, const std::string& path, std::chrono::steady_clock::duration refreshDuration, const nlohmann::json& currentState)
: id(id), state(path + std::to_string(id), commands, refreshDuration, currentState)
{
// Initialize value if not null
state.getValue();
}
nlohmann::json BaseDevice::sendPutRequest(const std::string& subPath, const nlohmann::json& request, FileInfo fileInfo)
{
return state.getCommandAPI().PUTRequest(state.getRequestPath() + subPath, request, std::move(fileInfo));
}
void BaseDevice::refresh(bool force)
{
if (force)
{
state.refresh();
}
else
{
state.getValue();
}
}
void BaseDevice::setRefreshDuration(std::chrono::steady_clock::duration refreshDuration)
{
state.setRefreshDuration(refreshDuration);
}
} // namespace hueplusplus
+126
View File
@@ -0,0 +1,126 @@
/**
\file BaseHttpHandler.cpp
Copyright Notice\n
Copyright (C) 2020 Jan Rogall - developer\n
Copyright (C) 2020 Moritz Wirger - 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 <http://www.gnu.org/licenses/>.
**/
#include "hueplusplus/BaseHttpHandler.h"
#include "hueplusplus/HueExceptionMacro.h"
namespace hueplusplus
{
std::string BaseHttpHandler::sendGetHTTPBody(const std::string& msg, const std::string& adr, int port) const
{
std::string response = send(msg, adr, port);
size_t start = response.find("\r\n\r\n");
if (start == std::string::npos)
{
std::cerr << "BaseHttpHandler: Failed to find body in response\n";
std::cerr << "Request:\n";
std::cerr << "\"" << msg << "\"\n";
std::cerr << "Response:\n";
std::cerr << "\"" << response << "\"\n";
throw HueException(CURRENT_FILE_INFO, "Failed to find body in response");
}
response.erase(0, start + 4);
return response;
}
std::string BaseHttpHandler::sendHTTPRequest(const std::string& method, const std::string& uri,
const std::string& contentType, const std::string& body, const std::string& adr, int port) const
{
std::string request;
// Protocol reference:
// https://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html Request-Line
request.append(method); // Method
request.append(" "); // Separation
request.append(uri); // Request-URI
request.append(" "); // Separation
request.append("HTTP/1.0"); // HTTP-Version
request.append("\r\n"); // Ending
// Entities
if (!contentType.empty())
{
request.append("Content-Type:"); // entity-header
request.append(" "); // Separation
request.append(contentType); // media-type
request.append("\r\n"); // Entity ending
}
if (!body.empty())
{
request.append("Content-Length:"); // entity-header
request.append(" "); // Separation
request.append(std::to_string(body.size())); // length
request.append("\r\n\r\n"); // Entity ending & Request-Line ending
}
request.append(body); // message-body
request.append("\r\n\r\n"); // Ending
return sendGetHTTPBody(request.c_str(), adr, port);
}
std::string BaseHttpHandler::GETString(const std::string& uri, const std::string& contentType, const std::string& body,
const std::string& adr, int port) const
{
return sendHTTPRequest("GET", uri, contentType, body, adr, port);
}
std::string BaseHttpHandler::POSTString(const std::string& uri, const std::string& contentType, const std::string& body,
const std::string& adr, int port) const
{
return sendHTTPRequest("POST", uri, contentType, body, adr, port);
}
std::string BaseHttpHandler::PUTString(const std::string& uri, const std::string& contentType, const std::string& body,
const std::string& adr, int port) const
{
return sendHTTPRequest("PUT", uri, contentType, body, adr, port);
}
std::string BaseHttpHandler::DELETEString(const std::string& uri, const std::string& contentType,
const std::string& body, const std::string& adr, int port) const
{
return sendHTTPRequest("DELETE", uri, contentType, body, adr, port);
}
nlohmann::json BaseHttpHandler::GETJson(
const std::string& uri, const nlohmann::json& body, const std::string& adr, int port) const
{
return nlohmann::json::parse(GETString(uri, "application/json", body.dump(), adr, port));
}
nlohmann::json BaseHttpHandler::POSTJson(
const std::string& uri, const nlohmann::json& body, const std::string& adr, int port) const
{
return nlohmann::json::parse(POSTString(uri, "application/json", body.dump(), adr, port));
}
nlohmann::json BaseHttpHandler::PUTJson(
const std::string& uri, const nlohmann::json& body, const std::string& adr, int port) const
{
return nlohmann::json::parse(PUTString(uri, "application/json", body.dump(), adr, port));
}
nlohmann::json BaseHttpHandler::DELETEJson(
const std::string& uri, const nlohmann::json& body, const std::string& adr, int port) const
{
return nlohmann::json::parse(DELETEString(uri, "application/json", body.dump(), adr, port));
}
} // namespace hueplusplus
+415
View File
@@ -0,0 +1,415 @@
/**
\file Bridge.cpp
Copyright Notice\n
Copyright (C) 2017 Jan Rogall - developer\n
Copyright (C) 2017 Moritz Wirger - 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 <http://www.gnu.org/licenses/>.
**/
#include "hueplusplus/Bridge.h"
#include <algorithm>
#include <cctype>
#include <chrono>
#include <cstring>
#include <iostream>
#include <locale>
#include <stdexcept>
#include <thread>
#include "hueplusplus/HueExceptionMacro.h"
#include "hueplusplus/LibConfig.h"
#include "hueplusplus/UPnP.h"
#include "hueplusplus/Utils.h"
namespace hueplusplus
{
BridgeFinder::BridgeFinder(std::shared_ptr<const IHttpHandler> handler) : http_handler(std::move(handler)) { }
std::vector<BridgeFinder::BridgeIdentification> BridgeFinder::findBridges() const
{
UPnP uplug;
std::vector<std::pair<std::string, std::string>> foundDevices = uplug.getDevices(http_handler);
std::vector<BridgeIdentification> foundBridges;
for (const std::pair<std::string, std::string>& p : foundDevices)
{
size_t found = p.second.find("IpBridge");
if (found != std::string::npos)
{
BridgeIdentification bridge;
size_t start = p.first.find("//") + 2;
size_t length = p.first.find(":", start) - start;
bridge.ip = p.first.substr(start, length);
try
{
std::string desc
= http_handler->GETString("/description.xml", "application/xml", "", bridge.ip, bridge.port);
std::string mac = parseDescription(desc);
if (!mac.empty())
{
bridge.mac = normalizeMac(mac);
foundBridges.push_back(std::move(bridge));
}
}
catch (const HueException&)
{
// No body found in response, skip this device
}
}
}
return foundBridges;
}
Bridge BridgeFinder::getBridge(const BridgeIdentification& identification, bool sharedState)
{
std::string normalizedMac = normalizeMac(identification.mac);
auto pos = usernames.find(normalizedMac);
auto key = clientkeys.find(normalizedMac);
if (pos != usernames.end())
{
if (key != clientkeys.end())
{
return Bridge(identification.ip, identification.port, pos->second, http_handler, key->second,
std::chrono::seconds(10), sharedState);
}
else
{
return Bridge(identification.ip, identification.port, pos->second, http_handler, "",
std::chrono::seconds(10), sharedState);
}
}
Bridge bridge(identification.ip, identification.port, "", http_handler, "", std::chrono::seconds(10), sharedState);
bridge.requestUsername();
if (bridge.getUsername().empty())
{
std::cerr << "Failed to request username for ip " << identification.ip << std::endl;
throw HueException(CURRENT_FILE_INFO, "Failed to request username!");
}
addUsername(normalizedMac, bridge.getUsername());
addClientKey(normalizedMac, bridge.getClientKey());
return bridge;
}
void BridgeFinder::addUsername(const std::string& mac, const std::string& username)
{
usernames[normalizeMac(mac)] = username;
}
void BridgeFinder::addClientKey(const std::string& mac, const std::string& clientkey)
{
clientkeys[normalizeMac(mac)] = clientkey;
}
const std::map<std::string, std::string>& BridgeFinder::getAllUsernames() const
{
return usernames;
}
std::string BridgeFinder::normalizeMac(std::string input)
{
// Remove any non alphanumeric characters (e.g. ':' and whitespace)
input.erase(std::remove_if(input.begin(), input.end(), [](char c) { return !std::isalnum(c, std::locale()); }),
input.end());
// Convert to lower case
std::transform(input.begin(), input.end(), input.begin(), [](char c) { return std::tolower(c, std::locale()); });
return input;
}
std::string BridgeFinder::parseDescription(const std::string& description)
{
const char* model = "<modelName>Philips hue bridge";
const char* serialBegin = "<serialNumber>";
const char* serialEnd = "</serialNumber>";
if (description.find(model) != std::string::npos)
{
std::size_t begin = description.find(serialBegin);
std::size_t end = description.find(serialEnd, begin);
if (begin != std::string::npos && end != std::string::npos)
{
begin += std::strlen(serialBegin);
if (begin < description.size())
{
std::string result = description.substr(begin, end - begin);
return result;
}
}
}
return std::string();
}
Bridge::Bridge(const std::string& ip, const int port, const std::string& username,
std::shared_ptr<const IHttpHandler> handler, const std::string& clientkey,
std::chrono::steady_clock::duration refreshDuration, bool sharedState)
: ip(ip),
username(username),
clientkey(clientkey),
port(port),
http_handler(std::move(handler)),
refreshDuration(refreshDuration),
stateCache(std::make_shared<APICache>(
"", HueCommandAPI(ip, port, username, http_handler), std::chrono::steady_clock::duration::max(), nullptr)),
lightList(stateCache, "lights", refreshDuration, sharedState,
[factory = LightFactory(stateCache->getCommandAPI(), refreshDuration)](
int id, const nlohmann::json& state, const std::shared_ptr<APICache>& baseCache) mutable {
return factory.createLight(state, id, baseCache);
}),
groupList(stateCache, "groups", refreshDuration, sharedState),
scheduleList(stateCache, "schedules", refreshDuration, sharedState),
sceneList(stateCache, "scenes", refreshDuration, sharedState),
sensorList(stateCache, "sensors", refreshDuration, sharedState),
ruleList(stateCache, "rules", refreshDuration, sharedState),
bridgeConfig(stateCache, refreshDuration),
sharedState(sharedState)
{ }
void Bridge::refresh()
{
stateCache->refresh();
}
void Bridge::setRefreshDuration(std::chrono::steady_clock::duration refreshDuration)
{
stateCache->setRefreshDuration(refreshDuration);
lightList.setRefreshDuration(refreshDuration);
groupList.setRefreshDuration(refreshDuration);
scheduleList.setRefreshDuration(refreshDuration);
sceneList.setRefreshDuration(refreshDuration);
sensorList.setRefreshDuration(refreshDuration);
ruleList.setRefreshDuration(refreshDuration);
bridgeConfig.setRefreshDuration(refreshDuration);
}
std::string Bridge::getBridgeIP() const
{
return ip;
}
int Bridge::getBridgePort() const
{
return port;
}
std::string Bridge::requestUsername()
{
std::chrono::steady_clock::duration timeout = Config::instance().getRequestUsernameTimeout();
std::chrono::steady_clock::duration checkInterval = Config::instance().getRequestUsernameAttemptInterval();
std::cout << "Please press the link Button! You've got "
<< std::chrono::duration_cast<std::chrono::seconds>(timeout).count() << " secs!\n";
// when the link button was pressed we got 30 seconds to get our username for control
nlohmann::json request;
request["devicetype"] = "HuePlusPlus#User";
request["generateclientkey"] = true;
nlohmann::json answer;
std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
// do-while loop to check at least once when timeout is 0
do
{
std::this_thread::sleep_for(checkInterval);
answer = http_handler->POSTJson("/api", request, ip, port);
nlohmann::json jsonUser = utils::safeGetMember(answer, 0, "success", "username");
nlohmann::json jsonKey = utils::safeGetMember(answer, 0, "success", "clientkey");
if (jsonUser != nullptr)
{
// [{"success":{"username": "<username>"}}]
username = jsonUser.get<std::string>();
// Update commands with new username and ip
setHttpHandler(http_handler);
std::cout << "Success! Link button was pressed!\n";
std::cout << "Username is \"" << username << "\"\n";
if (jsonKey != nullptr)
{
clientkey = jsonKey.get<std::string>();
std::cout << "Client key is \"" << clientkey << "\"\n";
}
break;
}
else if (answer.size() > 0 && answer[0].count("error"))
{
HueAPIResponseException exception = HueAPIResponseException::Create(CURRENT_FILE_INFO, answer[0]);
// All errors except 101: Link button not pressed
if (exception.GetErrorNumber() != 101)
{
throw exception;
}
}
} while (std::chrono::steady_clock::now() - start < timeout);
return username;
}
bool Bridge::startStreaming(std::string group_identifier)
{
if (clientkey.empty())
{
throw HueException(CURRENT_FILE_INFO, "Cannot stream without client key!");
}
nlohmann::json request;
request["stream"]["active"] = true;
nlohmann::json answer;
std::string uri = "/api/" + username + "/groups/" + group_identifier;
answer = http_handler->PUTJson(uri, request, ip, port);
std::string key = "/groups/" + group_identifier + "/stream/active";
nlohmann::json success = utils::safeGetMember(answer, 0, "success", key);
return success == true;
}
bool Bridge::stopStreaming(std::string group_identifier)
{
nlohmann::json request;
request["stream"]["active"] = false;
nlohmann::json answer;
std::string uri = "/api/" + username + "/groups/" + group_identifier;
answer = http_handler->PUTJson(uri, request, ip, port);
if (answer[0].contains("success"))
{
std::string key = "/groups/" + group_identifier + "/stream/active";
if (answer[0]["success"].contains(key))
{
if (answer[0]["success"][key] == false)
{
return true;
}
}
}
return false;
}
std::string Bridge::getUsername() const
{
return username;
}
std::string Bridge::getClientKey() const
{
return clientkey;
}
void Bridge::setIP(const std::string& ip)
{
this->ip = ip;
}
void Bridge::setPort(const int port)
{
this->port = port;
}
BridgeConfig& Bridge::config()
{
return bridgeConfig;
}
const BridgeConfig& Bridge::config() const
{
return bridgeConfig;
}
Bridge::LightList& Bridge::lights()
{
return lightList;
}
const Bridge::LightList& Bridge::lights() const
{
return lightList;
}
Bridge::GroupList& Bridge::groups()
{
return groupList;
}
const Bridge::GroupList& Bridge::groups() const
{
return groupList;
}
Bridge::ScheduleList& Bridge::schedules()
{
return scheduleList;
}
const Bridge::ScheduleList& Bridge::schedules() const
{
return scheduleList;
}
Bridge::SceneList& Bridge::scenes()
{
return sceneList;
}
const Bridge::SceneList& Bridge::scenes() const
{
return sceneList;
}
hueplusplus::SensorList& Bridge::sensors()
{
return sensorList;
}
const hueplusplus::SensorList& Bridge::sensors() const
{
return sensorList;
}
Bridge::RuleList& Bridge::rules()
{
return ruleList;
}
const Bridge::RuleList& Bridge::rules() const
{
return ruleList;
}
void Bridge::setHttpHandler(std::shared_ptr<const IHttpHandler> handler)
{
http_handler = handler;
stateCache = std::make_shared<APICache>("", HueCommandAPI(ip, port, username, handler), refreshDuration, nullptr);
lightList = LightList(stateCache, "lights", refreshDuration, sharedState,
[factory = LightFactory(stateCache->getCommandAPI(), refreshDuration)](int id, const nlohmann::json& state,
const std::shared_ptr<APICache>& baseCache) mutable { return factory.createLight(state, id, baseCache); });
groupList = GroupList(stateCache, "groups", refreshDuration, sharedState);
scheduleList = ScheduleList(stateCache, "schedules", refreshDuration, sharedState);
sceneList = SceneList(stateCache, "scenes", refreshDuration, sharedState);
sensorList = SensorList(stateCache, "sensors", refreshDuration, sharedState);
ruleList = RuleList(stateCache, "rules", refreshDuration, sharedState);
bridgeConfig = BridgeConfig(stateCache, refreshDuration);
stateCache->refresh();
}
} // namespace hueplusplus
+88
View File
@@ -0,0 +1,88 @@
/**
\file BridgeConfig.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 <http://www.gnu.org/licenses/>.
**/
#include <hueplusplus/BridgeConfig.h>
#include <hueplusplus/HueExceptionMacro.h>
namespace hueplusplus
{
BridgeConfig::BridgeConfig(std::shared_ptr<APICache> baseCache, std::chrono::steady_clock::duration refreshDuration)
: cache(std::move(baseCache), "config", refreshDuration)
{ }
void BridgeConfig::refresh(bool force)
{
if (force)
{
cache.refresh();
}
else
{
cache.getValue();
}
}
void BridgeConfig::setRefreshDuration(std::chrono::steady_clock::duration refreshDuration)
{
cache.setRefreshDuration(refreshDuration);
}
std::vector<WhitelistedUser> BridgeConfig::getWhitelistedUsers() const
{
const nlohmann::json& whitelist = cache.getValue().at("whitelist");
std::vector<WhitelistedUser> users;
for (auto it = whitelist.begin(); it != whitelist.end(); ++it)
{
users.push_back({it.key(), it->at("name").get<std::string>(),
time::AbsoluteTime::parseUTC(it->at("last use date").get<std::string>()),
time::AbsoluteTime::parseUTC(it->at("create date").get<std::string>())});
}
return users;
}
void BridgeConfig::removeUser(const std::string& userKey)
{
cache.getCommandAPI().DELETERequest("/config/whitelist/" + userKey, nlohmann::json::object());
}
bool BridgeConfig::getLinkButton() const
{
return cache.getValue().at("linkbutton").get<bool>();
}
void BridgeConfig::pressLinkButton()
{
cache.getCommandAPI().PUTRequest("/config", {{"linkbutton", true}}, CURRENT_FILE_INFO);
}
void BridgeConfig::touchLink()
{
cache.getCommandAPI().PUTRequest("/config", {{"touchlink", true}}, CURRENT_FILE_INFO);
}
std::string BridgeConfig::getMACAddress() const
{
return cache.getValue().at("mac").get<std::string>();
}
time::AbsoluteTime BridgeConfig::getUTCTime() const
{
return time::AbsoluteTime::parseUTC(cache.getValue().at("UTC").get<std::string>());
}
std::string BridgeConfig::getTimezone() const
{
return cache.getValue().at("timezone").get<std::string>();
}
} // namespace hueplusplus
+220
View File
@@ -0,0 +1,220 @@
/**
\file CLIPSensors.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 <http://www.gnu.org/licenses/>.
*/
#include "hueplusplus/CLIPSensors.h"
#include "hueplusplus/HueExceptionMacro.h"
namespace hueplusplus
{
namespace sensors
{
bool BaseCLIP::isOn() const
{
return state.getValue().at("config").at("on").get<bool>();
}
void BaseCLIP::setOn(bool on)
{
sendPutRequest("/config", nlohmann::json {{"on", on}}, CURRENT_FILE_INFO);
}
bool BaseCLIP::hasBatteryState() const
{
return state.getValue().at("config").count("battery") != 0;
}
int BaseCLIP::getBatteryState() const
{
return state.getValue().at("config").at("battery").get<int>();
}
void BaseCLIP::setBatteryState(int percent)
{
sendPutRequest("/config", nlohmann::json {{"battery", percent}}, CURRENT_FILE_INFO);
}
bool BaseCLIP::isReachable() const
{
return state.getValue().at("config").at("reachable").get<bool>();
}
bool BaseCLIP::hasURL() const
{
return state.getValue().at("config").count("url") != 0;
}
std::string BaseCLIP::getURL() const
{
return state.getValue().at("config").at("url").get<std::string>();
}
void BaseCLIP::setURL(const std::string& url)
{
sendPutRequest("/config", nlohmann::json {{"url", url}}, CURRENT_FILE_INFO);
}
time::AbsoluteTime BaseCLIP::getLastUpdated() const
{
const nlohmann::json& stateJson = state.getValue().at("state");
auto it = stateJson.find("lastupdated");
if (it == stateJson.end() || !it->is_string() || *it == "none")
{
return time::AbsoluteTime(std::chrono::system_clock::time_point(std::chrono::seconds {0}));
}
return time::AbsoluteTime::parseUTC(it->get<std::string>());
}
constexpr const char* CLIPSwitch::typeStr;
int CLIPSwitch::getButtonEvent() const
{
return state.getValue().at("state").at("buttonevent").get<int>();
}
void CLIPSwitch::setButtonEvent(int code)
{
sendPutRequest("/state", nlohmann::json {{"buttonevent", code}}, CURRENT_FILE_INFO);
}
constexpr const char* CLIPOpenClose::typeStr;
bool CLIPOpenClose::isOpen() const
{
return state.getValue().at("state").at("open").get<bool>();
}
void CLIPOpenClose::setOpen(bool open)
{
sendPutRequest("/state", nlohmann::json {{"open", open}}, CURRENT_FILE_INFO);
}
detail::ConditionHelper<bool> makeCondition(const CLIPOpenClose& sensor)
{
return detail::ConditionHelper<bool>("/sensors/" + std::to_string(sensor.getId()) + "/state/open");
}
constexpr const char* CLIPPresence::typeStr;
bool CLIPPresence::getPresence() const
{
return state.getValue().at("state").at("presence").get<bool>();
}
void CLIPPresence::setPresence(bool presence)
{
sendPutRequest("/state", nlohmann::json {{"presence", presence}}, CURRENT_FILE_INFO);
}
constexpr const char* CLIPTemperature::typeStr;
int CLIPTemperature::getTemperature() const
{
return state.getValue().at("state").at("temperature").get<int>();
}
void CLIPTemperature::setTemperature(int temperature)
{
sendPutRequest("/state", nlohmann::json {{"temperature", temperature}}, CURRENT_FILE_INFO);
}
constexpr const char* CLIPHumidity::typeStr;
int CLIPHumidity::getHumidity() const
{
return state.getValue().at("state").at("humidity").get<int>();
}
void CLIPHumidity::setHumidity(int humidity)
{
sendPutRequest("/state", nlohmann::json {{"humidity", humidity}}, CURRENT_FILE_INFO);
}
detail::ConditionHelper<int> makeCondition(const CLIPHumidity& sensor)
{
return detail::ConditionHelper<int>("/sensors/" + std::to_string(sensor.getId()) + "/state/humidity");
}
constexpr const char* CLIPLightLevel::typeStr;
int CLIPLightLevel::getDarkThreshold() const
{
return state.getValue().at("config").at("tholddark").get<int>();
}
void CLIPLightLevel::setDarkThreshold(int threshold)
{
sendPutRequest("/config", nlohmann::json {{"tholddark", threshold}}, CURRENT_FILE_INFO);
}
int CLIPLightLevel::getThresholdOffset() const
{
return state.getValue().at("config").at("tholdoffset").get<int>();
}
void CLIPLightLevel::setThresholdOffset(int offset)
{
sendPutRequest("/config", nlohmann::json {{"tholdoffset", offset}}, CURRENT_FILE_INFO);
}
int CLIPLightLevel::getLightLevel() const
{
return state.getValue().at("state").at("lightlevel").get<int>();
}
void CLIPLightLevel::setLightLevel(int level)
{
sendPutRequest("/state", nlohmann::json {{"lightlevel", level}}, CURRENT_FILE_INFO);
}
bool CLIPLightLevel::isDark() const
{
return state.getValue().at("state").at("dark").get<bool>();
}
bool CLIPLightLevel::isDaylight() const
{
return state.getValue().at("state").at("daylight").get<bool>();
}
constexpr const char* CLIPGenericFlag::typeStr;
bool CLIPGenericFlag::getFlag() const
{
return state.getValue().at("state").at("flag").get<bool>();
}
void CLIPGenericFlag::setFlag(bool flag)
{
sendPutRequest("/state", nlohmann::json {{"flag", flag}}, CURRENT_FILE_INFO);
}
detail::ConditionHelper<bool> makeCondition(const CLIPGenericFlag& sensor)
{
return detail::ConditionHelper<bool>("/sensors/" + std::to_string(sensor.getId()) + "/state/flag");
}
constexpr const char* CLIPGenericStatus::typeStr;
int CLIPGenericStatus::getStatus() const
{
return state.getValue().at("state").at("status").get<int>();
}
void CLIPGenericStatus::setStatus(int status)
{
sendPutRequest("/state", nlohmann::json {{"status", status}}, CURRENT_FILE_INFO);
}
detail::ConditionHelper<int> makeCondition(const CLIPGenericStatus& sensor)
{
return detail::ConditionHelper<int>("/sensors/" + std::to_string(sensor.getId()) + "/state/status");
}
} // namespace sensors
} // namespace hueplusplus
+94
View File
@@ -0,0 +1,94 @@
set(hueplusplus_SOURCES
Action.cpp
APICache.cpp
BaseDevice.cpp
BaseHttpHandler.cpp
Bridge.cpp
BridgeConfig.cpp
CLIPSensors.cpp
ColorUnits.cpp
EntertainmentMode.cpp
ExtendedColorHueStrategy.cpp
ExtendedColorTemperatureStrategy.cpp
Group.cpp
HueCommandAPI.cpp
HueDeviceTypes.cpp
HueException.cpp
Light.cpp
ModelPictures.cpp
NewDeviceList.cpp
Rule.cpp
Scene.cpp
Schedule.cpp
Sensor.cpp
SimpleBrightnessStrategy.cpp
SimpleColorHueStrategy.cpp
SimpleColorTemperatureStrategy.cpp
StateTransaction.cpp
TimePattern.cpp
UPnP.cpp
Utils.cpp
ZLLSensors.cpp)
# on windows we want to compile the WinHttpHandler
if(WIN32)
set(hueplusplus_SOURCES
${hueplusplus_SOURCES}
WinHttpHandler.cpp
)
endif()
# whereas on linux we want the LinHttpHandler
if(UNIX)
set(hueplusplus_SOURCES
${hueplusplus_SOURCES}
LinHttpHandler.cpp
)
endif()
if(ESP_PLATFORM)
set(hueplusplus_SOURCES
${hueplusplus_SOURCES}
LinHttpHandler.cpp
)
endif()
# append current source dir before files
foreach(src ${hueplusplus_SOURCES})
list(APPEND _srcList "${CMAKE_CURRENT_SOURCE_DIR}/${src}")
endforeach()
set(hueplusplus_SOURCES ${_srcList} PARENT_SCOPE)
# For install dir variables
include(GNUInstallDirs)
# hueplusplus shared library
add_library(hueplusplusshared SHARED ${hueplusplus_SOURCES})
target_link_libraries(hueplusplusshared PRIVATE MbedTLS::mbedtls)
target_link_libraries(hueplusplusshared PUBLIC nlohmann_json::nlohmann_json)
target_compile_features(hueplusplusshared PUBLIC cxx_std_14)
target_include_directories(hueplusplusshared PUBLIC $<BUILD_INTERFACE:${hueplusplus_SOURCE_DIR}/include> $<INSTALL_INTERFACE:include>)
# hueplusplus static library
add_library(hueplusplusstatic STATIC ${hueplusplus_SOURCES})
target_link_libraries(hueplusplusstatic PRIVATE MbedTLS::mbedtls)
target_link_libraries(hueplusplusstatic PUBLIC nlohmann_json::nlohmann_json)
target_compile_features(hueplusplusstatic PUBLIC cxx_std_14)
if(NOT WIN32)
# On windows, a shared library will also generate a .lib import library, making different names necessary.
# On other platforms the file endings are different, so the names can be the same.
set_target_properties(hueplusplusshared PROPERTIES OUTPUT_NAME hueplusplus SOVERSION 1)
set_target_properties(hueplusplusstatic PROPERTIES OUTPUT_NAME hueplusplus)
endif()
install(TARGETS hueplusplusshared DESTINATION ${CMAKE_INSTALL_LIBDIR})
install(TARGETS hueplusplusstatic DESTINATION ${CMAKE_INSTALL_LIBDIR})
target_include_directories(hueplusplusstatic PUBLIC $<BUILD_INTERFACE:${hueplusplus_SOURCE_DIR}/include> $<INSTALL_INTERFACE:include>)
install(DIRECTORY "${PROJECT_SOURCE_DIR}/include/" DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
# Export the package for use from the build-tree
# (this registers the build-tree with a global CMake-registry)
export(PACKAGE hueplusplus)
# Create the hueplusplus-config.cmake
configure_file ("${PROJECT_SOURCE_DIR}/cmake/hueplusplus-config.cmake.in" "${hueplusplus_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/hueplusplus-config.cmake" @ONLY)
# Install hueplusplus-config.cmake
install(FILES "${PROJECT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/hueplusplus-config.cmake" DESTINATION "${INSTALL_CMAKE_DIR}" COMPONENT dev)

Some files were not shown because too many files have changed in this diff Show More