Philips Hue (Standard and Entertainment Mode) Support using hueplusplus library

* Dependency hueplusplus-1.0.0 added from https://github.com/enwi/hueplusplus/releases/tag/v1.0.0
* Dependency mbedtls-2.24.0 added (Windows-only) from https://github.com/ARMmbed/mbedtls/releases/tag/mbedtls-2.24.0
This commit is contained in:
Adam Honse
2020-09-14 17:31:25 -05:00
parent 22acdd1fed
commit 2aaf7017ee
1346 changed files with 377277 additions and 2 deletions

View File

@@ -143,7 +143,7 @@ before_script:
stage: build
script:
- apt update
- apt install -y build-essential qtcreator qtbase5-dev libusb-1.0-0-dev libhidapi-dev pkgconf wget git file debhelper
- apt install -y build-essential qtcreator qtbase5-dev libusb-1.0-0-dev libhidapi-dev libmbedtls-dev pkgconf wget git file debhelper
- dpkg-architecture -l
- dpkg-buildpackage --target-arch i386 -us -B
- rm -v ../openrgb-dbgsym*.deb
@@ -166,7 +166,7 @@ before_script:
stage: build
script:
- apt update
- apt install -y build-essential qtcreator qtbase5-dev libusb-1.0-0-dev libhidapi-dev pkgconf wget git file debhelper
- apt install -y build-essential qtcreator qtbase5-dev libusb-1.0-0-dev libhidapi-dev libmbedtls-dev pkgconf wget git file debhelper
- dpkg-architecture -l
- dpkg-buildpackage -us -B
- rm -v ../openrgb-dbgsym*.deb

View File

@@ -0,0 +1,81 @@
/*---------------------------------------------------------*\
| Driver for Philips Hue |
| |
| Adam Honse (calcprogrammer1@gmail.com), 9/15/2020 |
\*---------------------------------------------------------*/
#include "PhilipsHueController.h"
PhilipsHueController::PhilipsHueController(hueplusplus::Light& light_ptr, std::string bridge_ip):light(light_ptr)
{
dark = false;
location = "IP: " + bridge_ip;
}
PhilipsHueController::~PhilipsHueController()
{
}
std::string PhilipsHueController::GetLocation()
{
return(location);
}
std::string PhilipsHueController::GetName()
{
return(light.getModelId());
}
std::string PhilipsHueController::GetVersion()
{
return(light.getSwVersion());
}
std::string PhilipsHueController::GetManufacturer()
{
return(light.getManufacturername());
}
std::string PhilipsHueController::GetUniqueID()
{
return(light.getUId());
}
void PhilipsHueController::SetColor(unsigned char red, unsigned char green, unsigned char blue)
{
hueplusplus::RGB rgb;
rgb.r = red;
rgb.g = green;
rgb.b = blue;
if((red == 0) && (green == 0) && (blue == 0))
{
if(!dark)
{
try
{
light.setColorRGB(rgb, 0);
}
catch(std::exception& e)
{
}
}
dark = true;
}
else
{
dark = false;
try
{
light.setColorRGB(rgb, 0);
}
catch(std::exception& e)
{
}
}
}

View File

@@ -0,0 +1,33 @@
/*---------------------------------------------------------*\
| Definitions for Philips Hue |
| |
| Adam Honse (calcprogrammer1@gmail.com), 9/15/2020 |
\*---------------------------------------------------------*/
#include "RGBController.h"
#include "HueDeviceTypes.h"
#include <string>
#include <vector>
#pragma once
class PhilipsHueController
{
public:
PhilipsHueController(hueplusplus::Light& light_ptr, std::string bridge_ip);
~PhilipsHueController();
std::string GetLocation();
std::string GetName();
std::string GetVersion();
std::string GetManufacturer();
std::string GetUniqueID();
void SetColor(unsigned char red, unsigned char green, unsigned char blue);
private:
hueplusplus::Light& light;
std::string location;
bool dark;
};

View File

@@ -0,0 +1,232 @@
#include "Bridge.h"
#include "HueDeviceTypes.h"
#ifdef _WIN32
#include "WinHttpHandler.h"
#else
#include "LinHttpHandler.h"
#endif
#include "Detector.h"
#include "LogManager.h"
#include "PhilipsHueController.h"
#include "PhilipsHueEntertainmentController.h"
#include "RGBController_PhilipsHue.h"
#include "RGBController_PhilipsHueEntertainment.h"
#include "SettingsManager.h"
/******************************************************************************************\
* *
* DetectPhilipsHueControllers *
* *
* Detect Philips Hue lighting devices with RGB control *
* *
\******************************************************************************************/
void DetectPhilipsHueControllers(std::vector<RGBController*>& rgb_controllers)
{
json hue_settings;
/*-------------------------------------------------*\
| Create an HTTP handler |
\*-------------------------------------------------*/
#ifdef _WIN32
using SystemHttpHandler = hueplusplus::WinHttpHandler;
#else
using SystemHttpHandler = hueplusplus::LinHttpHandler;
#endif
/*-------------------------------------------------*\
| Get Philips Hue settings from settings manager |
\*-------------------------------------------------*/
hue_settings = ResourceManager::get()->GetSettingsManager()->GetSettings("PhilipsHueDevices");
/*-------------------------------------------------*\
| Create a finder and find bridges |
\*-------------------------------------------------*/
static hueplusplus::BridgeFinder finder(std::make_shared<SystemHttpHandler>());
std::vector<hueplusplus::BridgeFinder::BridgeIdentification> bridges;// = finder.findBridges();
/*-------------------------------------------------*\
| If no bridges were detected, manually add bridge |
| IP and MAC (need to get these from file) |
\*-------------------------------------------------*/
if(bridges.empty())
{
if(hue_settings.contains("bridges"))
{
hueplusplus::BridgeFinder::BridgeIdentification ident;
if(hue_settings["bridges"][0].contains("ip"))
{
ident.ip = hue_settings["bridges"][0]["ip"];
}
if(hue_settings["bridges"][0].contains("mac"))
{
ident.mac = hue_settings["bridges"][0]["mac"];
}
bridges.push_back(ident);
}
}
/*-------------------------------------------------*\
| If no bridges were found, return, otherwise |
| connect to the first bridge |
\*-------------------------------------------------*/
if(bridges.empty())
{
return;
}
else
{
/*-------------------------------------------------*\
| Check if a saved username exists |
\*-------------------------------------------------*/
if(hue_settings.contains("bridges"))
{
/*-------------------------------------------------*\
| Add the username if it exists |
\*-------------------------------------------------*/
if(hue_settings["bridges"][0].contains("username"))
{
finder.addUsername(bridges[0].mac, hue_settings["bridges"][0]["username"]);
}
/*-------------------------------------------------*\
| Add the client key if it exists |
\*-------------------------------------------------*/
if(hue_settings["bridges"][0].contains("clientkey"))
{
finder.addClientKey(bridges[0].mac, hue_settings["bridges"][0]["clientkey"]);
}
}
/*-------------------------------------------------*\
| If username was added, this should connect right |
| away. If not, the user will have to push the |
| connect button on the bridge. |
\*-------------------------------------------------*/
try
{
static hueplusplus::Bridge bridge = finder.getBridge(bridges[0]);
bridge.refresh();
/*-------------------------------------------------*\
| Check to see if we need to save the settings |
| Settings need to be saved if either username or |
| client key either do not exist or have changed |
\*-------------------------------------------------*/
bool save_settings = false;
bool use_entertainment = false;
if(hue_settings.contains("bridges"))
{
if(hue_settings["bridges"][0].contains("username"))
{
if(hue_settings["bridges"][0]["username"] != bridge.getUsername())
{
save_settings = true;
}
}
else
{
save_settings = true;
}
if(hue_settings["bridges"][0].contains("clientkey"))
{
if(hue_settings["bridges"][0]["clientkey"] != bridge.getClientKey())
{
use_entertainment = true;
save_settings = true;
}
}
else
{
save_settings = true;
}
}
/*-------------------------------------------------*\
| Save the settings if needed |
\*-------------------------------------------------*/
if(save_settings)
{
hue_settings["bridges"][0]["username"] = bridge.getUsername();
hue_settings["bridges"][0]["clientkey"] = bridge.getClientKey();
hue_settings["bridges"][0]["entertainment"] = use_entertainment;
ResourceManager::get()->GetSettingsManager()->SetSettings("PhilipsHueDevices", hue_settings);
ResourceManager::get()->GetSettingsManager()->SaveSettings();
}
/*-------------------------------------------------*\
| Get all groups from the bridge |
\*-------------------------------------------------*/
if(hue_settings["bridges"][0].contains("entertainment"))
{
use_entertainment = hue_settings["bridges"][0]["entertainment"];
}
/*-------------------------------------------------*\
| Get all groups from the bridge |
\*-------------------------------------------------*/
if(use_entertainment)
{
std::vector<hueplusplus::Group> groups = bridge.groups().getAll();
if(groups.size() > 0)
{
/*-------------------------------------------------*\
| Loop through all available groups and check to |
| see if any are Entertainment groups |
\*-------------------------------------------------*/
for(unsigned int group_idx = 0; group_idx < groups.size(); group_idx++)
{
if(groups[group_idx].getType() == "Entertainment")
{
PhilipsHueEntertainmentController* new_controller = new PhilipsHueEntertainmentController(bridge, groups[group_idx]);
RGBController_PhilipsHueEntertainment* new_rgbcontroller = new RGBController_PhilipsHueEntertainment(new_controller);
rgb_controllers.push_back(new_rgbcontroller);
}
}
}
}
/*-------------------------------------------------*\
| Get all lights from the bridge |
\*-------------------------------------------------*/
else
{
std::vector<hueplusplus::Light> lights = bridge.lights().getAll();
if(lights.size() > 0)
{
/*-------------------------------------------------*\
| Loop through all available lights and add those |
| that have color (RGB) control capability |
\*-------------------------------------------------*/
for(unsigned int light_idx = 0; light_idx < lights.size(); light_idx++)
{
if(lights[light_idx].hasColorControl())
{
PhilipsHueController* new_controller = new PhilipsHueController(lights[light_idx], bridge.getBridgeIP());
RGBController_PhilipsHue* new_rgbcontroller = new RGBController_PhilipsHue(new_controller);
rgb_controllers.push_back(new_rgbcontroller);
}
}
}
}
}
catch(std::exception &e)
{
LOG_NOTICE("Exception occurred in Philips Hue detection");
}
}
} /* DetectPhilipsHueControllers() */
REGISTER_DETECTOR("Philips Hue", DetectPhilipsHueControllers);

View File

@@ -0,0 +1,82 @@
/*---------------------------------------------------------*\
| Driver for Philips Hue Entertainment Mode |
| |
| Adam Honse (calcprogrammer1@gmail.com), 11/6/2020 |
\*---------------------------------------------------------*/
#include "PhilipsHueEntertainmentController.h"
PhilipsHueEntertainmentController::PhilipsHueEntertainmentController(hueplusplus::Bridge& bridge_ptr, hueplusplus::Group& group_ptr):bridge(bridge_ptr),group(group_ptr)
{
/*-------------------------------------------------*\
| Fill in location string with bridge IP |
\*-------------------------------------------------*/
location = "IP: " + bridge.getBridgeIP();
num_leds = group.getLightIds().size();
/*-------------------------------------------------*\
| Create Entertainment Mode from bridge and group |
\*-------------------------------------------------*/
entertainment = new hueplusplus::EntertainmentMode(bridge, group);
/*-------------------------------------------------*\
| Connect Hue Entertainment Mode |
\*-------------------------------------------------*/
entertainment->connect();
}
PhilipsHueEntertainmentController::~PhilipsHueEntertainmentController()
{
/*-------------------------------------------------*\
| Disconnect Hue Entertainment Mode |
\*-------------------------------------------------*/
entertainment->disconnect();
}
std::string PhilipsHueEntertainmentController::GetLocation()
{
return(location);
}
std::string PhilipsHueEntertainmentController::GetName()
{
return(group.getName());
}
std::string PhilipsHueEntertainmentController::GetVersion()
{
return("");
}
std::string PhilipsHueEntertainmentController::GetManufacturer()
{
return("");
}
std::string PhilipsHueEntertainmentController::GetUniqueID()
{
return("");
}
unsigned int PhilipsHueEntertainmentController::GetNumLEDs()
{
return(num_leds);
}
void PhilipsHueEntertainmentController::SetColor(RGBColor* colors)
{
/*-------------------------------------------------*\
| Fill in Entertainment Mode light data |
\*-------------------------------------------------*/
for(unsigned int light_idx = 0; light_idx < num_leds; light_idx++)
{
RGBColor color = colors[light_idx];
unsigned char red = RGBGetRValue(color);
unsigned char green = RGBGetGValue(color);
unsigned char blue = RGBGetBValue(color);
entertainment->setColorRGB(light_idx, red, green, blue);
}
entertainment->update();
}

View File

@@ -0,0 +1,42 @@
/*---------------------------------------------------------*\
| Definitions for Philips Hue Entertainment Mode |
| |
| Adam Honse (calcprogrammer1@gmail.com), 11/6/2020 |
\*---------------------------------------------------------*/
#include "RGBController.h"
#include "Bridge.h"
#include "EntertainmentMode.h"
#include "Group.h"
#include <string>
#include <vector>
#pragma once
#define HUE_ENTERTAINMENT_HEADER_SIZE 16
#define HUE_ENTERTAINMENT_LIGHT_SIZE 9
class PhilipsHueEntertainmentController
{
public:
PhilipsHueEntertainmentController(hueplusplus::Bridge& bridge_ptr, hueplusplus::Group& group_ptr);
~PhilipsHueEntertainmentController();
std::string GetLocation();
std::string GetName();
std::string GetVersion();
std::string GetManufacturer();
std::string GetUniqueID();
unsigned int GetNumLEDs();
void SetColor(RGBColor* colors);
private:
hueplusplus::Bridge& bridge;
hueplusplus::Group& group;
hueplusplus::EntertainmentMode* entertainment;
std::string location;
unsigned int num_leds;
};

View File

@@ -0,0 +1,85 @@
/*-----------------------------------------*\
| RGBController_PhilipsHue.cpp |
| |
| Generic RGB Interface for Philips Hue |
| |
| Adam Honse (CalcProgrammer1) 9/15/2020 |
\*-----------------------------------------*/
#include "RGBController_PhilipsHue.h"
RGBController_PhilipsHue::RGBController_PhilipsHue(PhilipsHueController* light_ptr)
{
light = light_ptr;
name = light->GetManufacturer() + " " + light->GetName();
type = DEVICE_TYPE_LIGHT;
version = light->GetVersion();
description = "Philips Hue Device";
serial = light->GetUniqueID();
location = light->GetLocation();
mode Direct;
Direct.name = "Direct";
Direct.value = 0;
Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR;
Direct.color_mode = MODE_COLORS_PER_LED;
modes.push_back(Direct);
SetupZones();
}
void RGBController_PhilipsHue::SetupZones()
{
zone led_zone;
led_zone.name = "RGB Light";
led_zone.type = ZONE_TYPE_SINGLE;
led_zone.leds_min = 1;
led_zone.leds_max = 1;
led_zone.leds_count = 1;
led_zone.matrix_map = NULL;
zones.push_back(led_zone);
led new_led;
new_led.name = "RGB Light";
leds.push_back(new_led);
SetupColors();
}
void RGBController_PhilipsHue::ResizeZone(int /*zone*/, int /*new_size*/)
{
/*---------------------------------------------------------*\
| This device does not support resizing zones |
\*---------------------------------------------------------*/
}
void RGBController_PhilipsHue::DeviceUpdateLEDs()
{
unsigned char red = RGBGetRValue(colors[0]);
unsigned char grn = RGBGetGValue(colors[0]);
unsigned char blu = RGBGetBValue(colors[0]);
light->SetColor(red, grn, blu);
}
void RGBController_PhilipsHue::UpdateZoneLEDs(int /*zone*/)
{
DeviceUpdateLEDs();
}
void RGBController_PhilipsHue::UpdateSingleLED(int /*led*/)
{
DeviceUpdateLEDs();
}
void RGBController_PhilipsHue::SetCustomMode()
{
}
void RGBController_PhilipsHue::DeviceUpdateMode()
{
}

View File

@@ -0,0 +1,31 @@
/*-----------------------------------------*\
| RGBController_PhilipsHue.h |
| |
| Generic RGB Interface for Philips Hue |
| |
| Adam Honse (CalcProgrammer1) 9/15/2020 |
\*-----------------------------------------*/
#pragma once
#include "RGBController.h"
#include "PhilipsHueController.h"
class RGBController_PhilipsHue : public RGBController
{
public:
RGBController_PhilipsHue(PhilipsHueController* light_ptr);
void SetupZones();
void ResizeZone(int zone, int new_size);
void DeviceUpdateLEDs();
void UpdateZoneLEDs(int zone);
void UpdateSingleLED(int led);
void SetCustomMode();
void DeviceUpdateMode();
private:
PhilipsHueController* light;
};

View File

@@ -0,0 +1,113 @@
/*-----------------------------------------*\
| RGBController_PhilipsHueEntertainment.cpp|
| |
| Generic RGB Interface for Philips Hue |
| Entertainment Mode |
| |
| Adam Honse (CalcProgrammer1) 11/7/2020 |
\*-----------------------------------------*/
#include "RGBController_PhilipsHueEntertainment.h"
using namespace std::chrono_literals;
RGBController_PhilipsHueEntertainment::RGBController_PhilipsHueEntertainment(PhilipsHueEntertainmentController* light_ptr)
{
light = light_ptr;
name = light->GetManufacturer() + " " + light->GetName();
type = DEVICE_TYPE_LIGHT;
version = light->GetVersion();
description = "Philips Hue Entertainment Mode Device";
serial = light->GetUniqueID();
location = light->GetLocation();
mode Direct;
Direct.name = "Direct";
Direct.value = 0;
Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR;
Direct.color_mode = MODE_COLORS_PER_LED;
modes.push_back(Direct);
SetupZones();
/*-----------------------------------------------------*\
| The Philips Hue Entertainment Mode requires a packet |
| within 10 seconds of sending the lighting change in |
| order to not exit entertainment mode. Start a thread |
| to continuously send a packet every 5s |
\*-----------------------------------------------------*/
KeepaliveThreadRunning = true;
KeepaliveThread = new std::thread(&RGBController_PhilipsHueEntertainment::KeepaliveThreadFunction, this);
}
void RGBController_PhilipsHueEntertainment::SetupZones()
{
zone led_zone;
led_zone.name = "RGB Light";
led_zone.type = ZONE_TYPE_SINGLE;
led_zone.leds_min = light->GetNumLEDs();
led_zone.leds_max = light->GetNumLEDs();
led_zone.leds_count = light->GetNumLEDs();
led_zone.matrix_map = NULL;
zones.push_back(led_zone);
for(unsigned int led_idx = 0; led_idx < light->GetNumLEDs(); led_idx++)
{
led new_led;
new_led.name = "RGB Light";
leds.push_back(new_led);
}
SetupColors();
}
void RGBController_PhilipsHueEntertainment::ResizeZone(int /*zone*/, int /*new_size*/)
{
/*---------------------------------------------------------*\
| This device does not support resizing zones |
\*---------------------------------------------------------*/
}
void RGBController_PhilipsHueEntertainment::DeviceUpdateLEDs()
{
last_update_time = std::chrono::steady_clock::now();
light->SetColor(&colors[0]);
}
void RGBController_PhilipsHueEntertainment::UpdateZoneLEDs(int /*zone*/)
{
DeviceUpdateLEDs();
}
void RGBController_PhilipsHueEntertainment::UpdateSingleLED(int /*led*/)
{
DeviceUpdateLEDs();
}
void RGBController_PhilipsHueEntertainment::SetCustomMode()
{
active_mode = 0;
}
void RGBController_PhilipsHueEntertainment::DeviceUpdateMode()
{
}
void RGBController_PhilipsHueEntertainment::KeepaliveThreadFunction()
{
while(KeepaliveThreadRunning)
{
if(active_mode == 0)
{
if((std::chrono::steady_clock::now() - last_update_time) > std::chrono::seconds(5))
{
UpdateLEDs();
}
}
std::this_thread::sleep_for(1s);
}
}

View File

@@ -0,0 +1,42 @@
/*-----------------------------------------*\
| RGBController_PhilipsHueEntertainment.h |
| |
| Generic RGB Interface for Philips Hue |
| Entertainment Mode |
| |
| Adam Honse (CalcProgrammer1) 11/7/2020 |
\*-----------------------------------------*/
#pragma once
#include "RGBController.h"
#include "PhilipsHueEntertainmentController.h"
#include <atomic>
#include <thread>
class RGBController_PhilipsHueEntertainment : public RGBController
{
public:
RGBController_PhilipsHueEntertainment(PhilipsHueEntertainmentController* light_ptr);
void SetupZones();
void ResizeZone(int zone, int new_size);
void DeviceUpdateLEDs();
void UpdateZoneLEDs(int zone);
void UpdateSingleLED(int led);
void SetCustomMode();
void DeviceUpdateMode();
void KeepaliveThreadFunction();
private:
PhilipsHueEntertainmentController* light;
std::atomic<bool> KeepaliveThreadRunning;
std::thread* KeepaliveThread;
std::chrono::time_point<std::chrono::steady_clock> last_update_time;
};

View File

@@ -49,6 +49,8 @@ DEFINES +=
INCLUDEPATH += \
dependencies/ColorWheel \
dependencies/CRCpp/ \
dependencies/hueplusplus-1.0.0/include \
dependencies/hueplusplus-1.0.0/include/hueplusplus \
dependencies/json/ \
dependencies/libe131/src/ \
dependencies/libcmmk/include/ \
@@ -110,6 +112,7 @@ INCLUDEPATH +=
Controllers/NZXTKrakenController/ \
Controllers/OpenRazerController/ \
Controllers/PatriotViperController/ \
Controllers/PhilipsHueController/ \
Controllers/PhilipsWizController/ \
Controllers/QMKOpenRGBController/ \
Controllers/RazerController/ \
@@ -316,6 +319,8 @@ HEADERS +=
Controllers/OpenRazerController/OpenRazerDevices.h \
Controllers/PatriotViperController/PatriotViperController.h \
Controllers/PatriotViperController/RGBController_PatriotViper.h \
Controllers/PhilipsHueController/PhilipsHueController.h \
Controllers/PhilipsHueController/RGBController_PhilipsHue.h \
Controllers/PhilipsWizController/PhilipsWizController.h \
Controllers/PhilipsWizController/RGBController_PhilipsWiz.h \
Controllers/QMKOpenRGBController/QMKOpenRGBController.h \
@@ -376,6 +381,35 @@ SOURCES +=
Controllers/LogitechController/LogitechProtocolCommon.cpp \
dependencies/dmiinfo.cpp \
dependencies/ColorWheel/ColorWheel.cpp \
dependencies/hueplusplus-1.0.0/src/Action.cpp \
dependencies/hueplusplus-1.0.0/src/APICache.cpp \
dependencies/hueplusplus-1.0.0/src/BaseDevice.cpp \
dependencies/hueplusplus-1.0.0/src/BaseHttpHandler.cpp \
dependencies/hueplusplus-1.0.0/src/Bridge.cpp \
dependencies/hueplusplus-1.0.0/src/BridgeConfig.cpp \
dependencies/hueplusplus-1.0.0/src/CLIPSensors.cpp \
dependencies/hueplusplus-1.0.0/src/ColorUnits.cpp \
dependencies/hueplusplus-1.0.0/src/EntertainmentMode.cpp \
dependencies/hueplusplus-1.0.0/src/ExtendedColorHueStrategy.cpp \
dependencies/hueplusplus-1.0.0/src/ExtendedColorTemperatureStrategy.cpp \
dependencies/hueplusplus-1.0.0/src/Group.cpp \
dependencies/hueplusplus-1.0.0/src/HueCommandAPI.cpp \
dependencies/hueplusplus-1.0.0/src/HueDeviceTypes.cpp \
dependencies/hueplusplus-1.0.0/src/HueException.cpp \
dependencies/hueplusplus-1.0.0/src/Light.cpp \
dependencies/hueplusplus-1.0.0/src/ModelPictures.cpp \
dependencies/hueplusplus-1.0.0/src/NewDeviceList.cpp \
dependencies/hueplusplus-1.0.0/src/Scene.cpp \
dependencies/hueplusplus-1.0.0/src/Schedule.cpp \
dependencies/hueplusplus-1.0.0/src/Sensor.cpp \
dependencies/hueplusplus-1.0.0/src/SimpleBrightnessStrategy.cpp \
dependencies/hueplusplus-1.0.0/src/SimpleColorHueStrategy.cpp \
dependencies/hueplusplus-1.0.0/src/SimpleColorTemperatureStrategy.cpp \
dependencies/hueplusplus-1.0.0/src/StateTransaction.cpp \
dependencies/hueplusplus-1.0.0/src/TimePattern.cpp \
dependencies/hueplusplus-1.0.0/src/UPnP.cpp \
dependencies/hueplusplus-1.0.0/src/Utils.cpp \
dependencies/hueplusplus-1.0.0/src/ZLLSensors.cpp \
dependencies/libe131/src/e131.c \
dependencies/libcmmk/src/libcmmk.c \
main.cpp \
@@ -606,6 +640,11 @@ SOURCES +=
Controllers/PatriotViperController/PatriotViperController.cpp \
Controllers/PatriotViperController/PatriotViperControllerDetect.cpp \
Controllers/PatriotViperController/RGBController_PatriotViper.cpp \
Controllers/PhilipsHueController/PhilipsHueController.cpp \
Controllers/PhilipsHueController/PhilipsHueControllerDetect.cpp \
Controllers/PhilipsHueController/PhilipsHueEntertainmentController.cpp \
Controllers/PhilipsHueController/RGBController_PhilipsHue.cpp \
Controllers/PhilipsHueController/RGBController_PhilipsHueEntertainment.cpp \
Controllers/PhilipsWizController/PhilipsWizController.cpp \
Controllers/PhilipsWizController/PhilipsWizControllerDetect.cpp \
Controllers/PhilipsWizController/RGBController_PhilipsWiz.cpp \
@@ -699,6 +738,7 @@ win32:INCLUDEPATH +=
dependencies/hidapi \
dependencies/inpout32_1501/Win32/ \
dependencies/libusb-1.0.22/include \
dependencies/mbedtls-2.24.0/include \
dependencies/NVFC \
dependencies/openrazer-win32 \
wmi/ \
@@ -706,6 +746,91 @@ win32:INCLUDEPATH +=
win32:SOURCES += \
# dependencies/hidapi/hidapi.c \
dependencies/hueplusplus-1.0.0/src/WinHttpHandler.cpp \
dependencies/mbedtls-2.24.0/library/aes.c \
dependencies/mbedtls-2.24.0/library/aesni.c \
dependencies/mbedtls-2.24.0/library/arc4.c \
dependencies/mbedtls-2.24.0/library/aria.c \
dependencies/mbedtls-2.24.0/library/asn1parse.c \
dependencies/mbedtls-2.24.0/library/asn1write.c \
dependencies/mbedtls-2.24.0/library/base64.c \
dependencies/mbedtls-2.24.0/library/bignum.c \
dependencies/mbedtls-2.24.0/library/blowfish.c \
dependencies/mbedtls-2.24.0/library/camellia.c \
dependencies/mbedtls-2.24.0/library/ccm.c \
dependencies/mbedtls-2.24.0/library/certs.c \
dependencies/mbedtls-2.24.0/library/chacha20.c \
dependencies/mbedtls-2.24.0/library/chachapoly.c \
dependencies/mbedtls-2.24.0/library/cipher.c \
dependencies/mbedtls-2.24.0/library/cipher_wrap.c \
dependencies/mbedtls-2.24.0/library/cmac.c \
dependencies/mbedtls-2.24.0/library/ctr_drbg.c \
dependencies/mbedtls-2.24.0/library/debug.c \
dependencies/mbedtls-2.24.0/library/des.c \
dependencies/mbedtls-2.24.0/library/dhm.c \
dependencies/mbedtls-2.24.0/library/ecdh.c \
dependencies/mbedtls-2.24.0/library/ecdsa.c \
dependencies/mbedtls-2.24.0/library/ecjpake.c \
dependencies/mbedtls-2.24.0/library/ecp.c \
dependencies/mbedtls-2.24.0/library/ecp_curves.c \
dependencies/mbedtls-2.24.0/library/entropy.c \
dependencies/mbedtls-2.24.0/library/entropy_poll.c \
dependencies/mbedtls-2.24.0/library/error.c \
dependencies/mbedtls-2.24.0/library/gcm.c \
dependencies/mbedtls-2.24.0/library/havege.c \
dependencies/mbedtls-2.24.0/library/hkdf.c \
dependencies/mbedtls-2.24.0/library/hmac_drbg.c \
dependencies/mbedtls-2.24.0/library/md.c \
dependencies/mbedtls-2.24.0/library/md2.c \
dependencies/mbedtls-2.24.0/library/md4.c \
dependencies/mbedtls-2.24.0/library/md5.c \
dependencies/mbedtls-2.24.0/library/memory_buffer_alloc.c \
dependencies/mbedtls-2.24.0/library/net_sockets.c \
dependencies/mbedtls-2.24.0/library/nist_kw.c \
dependencies/mbedtls-2.24.0/library/oid.c \
dependencies/mbedtls-2.24.0/library/padlock.c \
dependencies/mbedtls-2.24.0/library/pem.c \
dependencies/mbedtls-2.24.0/library/pk.c \
dependencies/mbedtls-2.24.0/library/pk_wrap.c \
dependencies/mbedtls-2.24.0/library/pkcs5.c \
dependencies/mbedtls-2.24.0/library/pkcs11.c \
dependencies/mbedtls-2.24.0/library/pkcs12.c \
dependencies/mbedtls-2.24.0/library/pkparse.c \
dependencies/mbedtls-2.24.0/library/pkwrite.c \
dependencies/mbedtls-2.24.0/library/platform.c \
dependencies/mbedtls-2.24.0/library/platform_util.c \
dependencies/mbedtls-2.24.0/library/poly1305.c \
dependencies/mbedtls-2.24.0/library/psa_crypto.c \
dependencies/mbedtls-2.24.0/library/psa_crypto_se.c \
dependencies/mbedtls-2.24.0/library/psa_crypto_slot_management.c \
dependencies/mbedtls-2.24.0/library/psa_crypto_storage.c \
dependencies/mbedtls-2.24.0/library/psa_its_file.c \
dependencies/mbedtls-2.24.0/library/ripemd160.c \
dependencies/mbedtls-2.24.0/library/rsa.c \
dependencies/mbedtls-2.24.0/library/rsa_internal.c \
dependencies/mbedtls-2.24.0/library/sha1.c \
dependencies/mbedtls-2.24.0/library/sha256.c \
dependencies/mbedtls-2.24.0/library/sha512.c \
dependencies/mbedtls-2.24.0/library/ssl_cache.c \
dependencies/mbedtls-2.24.0/library/ssl_ciphersuites.c \
dependencies/mbedtls-2.24.0/library/ssl_cli.c \
dependencies/mbedtls-2.24.0/library/ssl_cookie.c \
dependencies/mbedtls-2.24.0/library/ssl_msg.c \
dependencies/mbedtls-2.24.0/library/ssl_srv.c \
dependencies/mbedtls-2.24.0/library/ssl_ticket.c \
dependencies/mbedtls-2.24.0/library/ssl_tls.c \
dependencies/mbedtls-2.24.0/library/threading.c \
dependencies/mbedtls-2.24.0/library/timing.c \
dependencies/mbedtls-2.24.0/library/version.c \
dependencies/mbedtls-2.24.0/library/version_features.c \
dependencies/mbedtls-2.24.0/library/x509.c \
dependencies/mbedtls-2.24.0/library/x509_create.c \
dependencies/mbedtls-2.24.0/library/x509_crl.c \
dependencies/mbedtls-2.24.0/library/x509_crt.c \
dependencies/mbedtls-2.24.0/library/x509_csr.c \
dependencies/mbedtls-2.24.0/library/x509write_crt.c \
dependencies/mbedtls-2.24.0/library/x509write_csr.c \
dependencies/mbedtls-2.24.0/library/xtea.c \
dependencies/NVFC/nvapi.cpp \
i2c_smbus/i2c_smbus_amdadl.cpp \
i2c_smbus/i2c_smbus_i801.cpp \
@@ -828,6 +953,9 @@ unix:!macx {
LIBS += \
-lusb-1.0 \
-lstdc++fs \
-lmbedx509 \
-lmbedcrypto \
-lmbedtls \
#-------------------------------------------------------------------------------------------#
# Determine which hidapi to use based on availability #
@@ -853,6 +981,7 @@ unix:!macx {
}
SOURCES += \
dependencies/hueplusplus-1.0.0/src/LinHttpHandler.cpp \
i2c_smbus/i2c_smbus_linux.cpp \
serial_port/find_usb_serial_port_linux.cpp \
Controllers/FaustusController/RGBController_Faustus.cpp \
@@ -902,11 +1031,15 @@ macx {
USE_HID_USAGE \
SOURCES += \
dependencies/hueplusplus-1.0.0/src/LinHttpHandler.cpp \
serial_port/find_usb_serial_port_linux.cpp \
LIBS += \
-lusb-1.0 \
-lhidapi \
-lmbedx509 \
-lmbedcrypto \
-lmbedtls \
}
#-------------------------------------------------------------------------------------------#
@@ -926,9 +1059,11 @@ macx:contains(QMAKE_HOST.arch, arm64) {
macx:contains(QMAKE_HOST.arch, x86_64) {
INCLUDEPATH += \
/usr/local/include \
/usr/local/homebrew/include \
LIBS += \
-L/usr/local/lib \
-L/usr/local/homebrew/lib \
}
DISTFILES += \

View File

@@ -197,6 +197,7 @@ There have been two instances of hardware damage in OpenRGB's development and we
* Qt-Plus (ColorWheel): https://github.com/liuyanghejerry/Qt-Plus
* AMD ADL Libraries: https://github.com/GPUOpen-LibrariesAndSDKs/display-library
* libcmmk: https://github.com/chmod222/libcmmk
* hueplusplus: https://github.com/enwi/hueplusplus
## Projects Researched

1
debian/control vendored
View File

@@ -11,6 +11,7 @@ Build-Depends:
qt5-qmake,
libusb-1.0-0-dev,
libhidapi-dev,
libmbedtls-dev,
Homepage: https://gitlab.com/CalcProgrammer1/OpenRGB
Package: openrgb

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

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.

View File

@@ -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.

View File

@@ -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.

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

View File

@@ -0,0 +1,3 @@
[submodule "lib/mbedtls"]
path = lib/mbedtls
url = https://github.com/ARMmbed/mbedtls.git

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

View File

@@ -0,0 +1,105 @@
cmake_minimum_required(VERSION 3.8)
if(${CMAKE_VERSION} VERSION_LESS 3.11)
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
else()
cmake_policy(VERSION 3.11)
endif()
# 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.0.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(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)
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()
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)
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()

2612
dependencies/hueplusplus-1.0.0/Doxyfile vendored Normal file
View File

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,55 @@
timestamps {
node('master')
{
stage('SCM')
{
checkout scm
}
stage('Build')
{
sh returnStatus: true, script: 'rm -r build'
sh '''#!/bin/bash
mkdir build
cd build
cmake .. -Dhueplusplus_TESTS=ON
make -j8 2>&1 | tee buildlog.txt
test ${PIPESTATUS[0]} -eq 0'''
}
stage('Test')
{
sh '''cd build
hueplusplus/test/test_HuePlusPlus --gtest_output=xml:test.xml
make -j8 coveragetest'''
step([$class: 'XUnitBuilder', testTimeMargin: '3000', thresholdMode: 1,
thresholds: [
[$class: 'FailedThreshold', failureNewThreshold: '', failureThreshold: '', unstableNewThreshold: '', unstableThreshold: ''],
[$class: 'SkippedThreshold', failureNewThreshold: '', failureThreshold: '', unstableNewThreshold: '', unstableThreshold: '']
],
tools: [
[$class: 'GoogleTestType', deleteOutputFiles: true, failIfNotNew: true, pattern: 'build/test.xml', skipNoTestFiles: false, stopProcessingIfError: true]
]
])
publishHTML(
[allowMissing: false, alwaysLinkToLastBuild: false, keepAll: false, reportDir: 'build/coveragetest', reportFiles: 'index.html', reportName: 'Coveragetest', reportTitles: '']
)
}
stage('CppCheck')
{
sh 'cppcheck -j 8 --force -ihueplusplus/test -ihueplusplus/jsoncpp.cpp hueplusplus/ 2>build/CppCheckResult'
rtp nullAction: '1', parserName: 'HTML', stableText: '${FILE:build/CppCheckResult}'
}
stage('Documentation')
{
sh 'doxygen Doxyfile'
publishHTML([allowMissing: false, alwaysLinkToLastBuild: false, keepAll: false, reportDir: 'doc/html/', reportFiles: 'index.html', reportName: 'Doxygen'])
}
stage('Parse warnings')
{
warnings canComputeNew: false, canResolveRelativePaths: false, categoriesPattern: '', defaultEncoding: '', excludePattern: '', healthy: '', includePattern: '', messagesPattern: '', parserConfigurations: [[parserName: 'Doxygen', pattern: 'doxylog.txt']], unHealthy: ''
sh returnStatus: true, script: 'rm doxylog.txt'
warnings canComputeNew: false, canResolveRelativePaths: false, categoriesPattern: '', defaultEncoding: '', excludePattern: '', healthy: '', includePattern: '', messagesPattern: '', parserConfigurations: [[parserName: 'GNU C Compiler 4 (gcc)', pattern: 'build/buildlog.txt']], unHealthy: ''
sh returnStatus: true, script: 'rm build/buildlog.txt'
}
}
}

165
dependencies/hueplusplus-1.0.0/LICENSE vendored Normal file
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
dependencies/hueplusplus-1.0.0/README.md vendored Normal file
View File

@@ -0,0 +1,165 @@
# hueplusplus
A simple and easy to use library for Philips Hue Lights
| Branch | Travis CI | Codecov | LGTM
|:-|:-|:-|:-|
| [Master](https://github.com/enwi/hueplusplus/tree/master) | [![Build Status](https://travis-ci.com/enwi/hueplusplus.svg?branch=master)](https://travis-ci.com/enwi/hueplusplus) | [![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) | [![Build Status](https://travis-ci.com/enwi/hueplusplus.svg?branch=development)](https://travis-ci.com/enwi/hueplusplus) | [![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.

View File

@@ -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)

View 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@")

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

View File

@@ -0,0 +1,86 @@
# 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.8 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
```

View File

@@ -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)

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)

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.

View File

@@ -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.

View File

@@ -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

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;
}

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)

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;
}

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;
}

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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 "json/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

View File

@@ -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 "json/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

View File

@@ -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 "json/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

View File

@@ -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 "json/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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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 "json/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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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 "json/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

View File

@@ -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

View File

@@ -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

View File

@@ -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 "json/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

View File

@@ -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

View File

@@ -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 "json/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

View File

@@ -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

View File

@@ -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 "json/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

View File

@@ -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 "json/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

View File

@@ -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

View File

@@ -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 "json/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

View File

@@ -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

View File

@@ -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

View File

@@ -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 <json/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

View File

@@ -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

View File

@@ -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 "json/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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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 "json/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

View File

@@ -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

View File

@@ -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

View File

@@ -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 "json/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

View File

@@ -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

View File

@@ -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

View File

File diff suppressed because it is too large Load Diff

25
dependencies/hueplusplus-1.0.0/lgtm.yml vendored Normal file
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

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

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

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 "json/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

View File

@@ -0,0 +1,120 @@
/**
\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
request.append("Content-Type:"); // entity-header
request.append(" "); // Separation
request.append(contentType); // media-type
request.append("\r\n"); // Entity ending
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

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

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

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

View File

@@ -0,0 +1,81 @@
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)
# hueplusplus shared library
add_library(hueplusplusshared SHARED ${hueplusplus_SOURCES})
target_link_libraries(hueplusplusshared PRIVATE mbedtls)
target_compile_features(hueplusplusshared PUBLIC cxx_std_14)
target_include_directories(hueplusplusshared PUBLIC $<BUILD_INTERFACE:${hueplusplus_SOURCE_DIR}/include> $<INSTALL_INTERFACE:include>)
install(TARGETS hueplusplusshared DESTINATION lib)
# hueplusplus static library
add_library(hueplusplusstatic STATIC ${hueplusplus_SOURCES})
target_link_libraries(hueplusplusstatic PRIVATE mbedtls)
target_compile_features(hueplusplusstatic PUBLIC cxx_std_14)
install(TARGETS hueplusplusstatic DESTINATION lib)
target_include_directories(hueplusplusstatic PUBLIC $<BUILD_INTERFACE:${hueplusplus_SOURCE_DIR}/include> $<INSTALL_INTERFACE:include>)
install(DIRECTORY "${PROJECT_SOURCE_DIR}/include/" DESTINATION include)
# 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)

View File

@@ -0,0 +1,248 @@
/**
\file ColorUnits.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 <algorithm>
#include <cmath>
#include <hueplusplus/ColorUnits.h>
namespace hueplusplus
{
namespace
{
float sign(const XY& p0, const XY& p1, const XY& p2)
{
return (p0.x - p2.x) * (p1.y - p2.y) - (p1.x - p2.x) * (p0.y - p2.y);
}
bool isRightOf(const XY& xy, const XY& p1, const XY& p2)
{
return sign(xy, p1, p2) < 0;
}
XY projectOntoLine(const XY& xy, const XY& p1, const XY& p2)
{
// Using dot product to project onto line
// Vector AB = B - A
// Vector AX = X - A
// Projected length l = (AX dot AB) / len(AB)
// Result: E = A + l*AB/len(AB) = A + AB * (AX dot AB) / (len(AB))^2
const float abX = p2.x - p1.x;
const float abY = p2.y - p1.y;
const float lenABSquared = abX * abX + abY * abY;
const float dot = (xy.x - p1.x) * abX + (xy.y - p1.y) * abY;
const float eX = p1.x + abX * dot / lenABSquared;
const float eY = p1.y + abY * dot / lenABSquared;
return XY {eX, eY};
}
} // namespace
bool ColorGamut::contains(const XY& xy) const
{
return !isRightOf(xy, redCorner, greenCorner) && !isRightOf(xy, greenCorner, blueCorner)
&& !isRightOf(xy, blueCorner, redCorner);
}
XY ColorGamut::corrected(const XY& xy) const
{
// red, green and blue are in counterclockwise orientation
if (isRightOf(xy, redCorner, greenCorner))
{
// Outside of triangle, check whether to use nearest corner or point on line
if (isRightOf(xy, greenCorner, blueCorner))
{
// Point is outside of red-green line, closest to green corner
return greenCorner;
}
else if (isRightOf(xy, blueCorner, redCorner))
{
// Point is outside of red-green line, closest to red corner
return redCorner;
}
else
{
// Point is closest to line, project onto it
return projectOntoLine(xy, redCorner, greenCorner);
}
}
else if (isRightOf(xy, greenCorner, blueCorner))
{
// Green corner already checked above
if (isRightOf(xy, blueCorner, redCorner))
{
// Point is outside of green-blue line, closest to blue corner
return blueCorner;
}
else
{
return projectOntoLine(xy, greenCorner, blueCorner);
}
}
else if (isRightOf(xy, blueCorner, redCorner))
{
// All corners already checked
return projectOntoLine(xy, blueCorner, redCorner);
}
return xy;
}
XYBrightness RGB::toXY() const
{
if (r == 0 && g == 0 && b == 0)
{
// Return white with minimum brightness
return XYBrightness {XY {0.32272673f, 0.32902291f}, 0.f};
}
const float red = r / 255.f;
const float green = g / 255.f;
const float blue = b / 255.f;
const float redCorrected = (red > 0.04045f) ? pow((red + 0.055f) / (1.0f + 0.055f), 2.4f) : (red / 12.92f);
const float greenCorrected = (green > 0.04045f) ? pow((green + 0.055f) / (1.0f + 0.055f), 2.4f) : (green / 12.92f);
const float blueCorrected = (blue > 0.04045f) ? pow((blue + 0.055f) / (1.0f + 0.055f), 2.4f) : (blue / 12.92f);
const float X = redCorrected * 0.664511f + greenCorrected * 0.154324f + blueCorrected * 0.162028f;
const float Y = redCorrected * 0.283881f + greenCorrected * 0.668433f + blueCorrected * 0.047685f;
const float Z = redCorrected * 0.000088f + greenCorrected * 0.072310f + blueCorrected * 0.986039f;
const float x = X / (X + Y + Z);
const float y = Y / (X + Y + Z);
// Set brightness to the brightest channel value (rather than average of them),
// so full red/green/blue can be displayed
return XYBrightness {XY {x, y}, std::max({red, green, blue})};
}
XYBrightness RGB::toXY(const ColorGamut& gamut) const
{
XYBrightness xy = toXY();
if (!gamut.contains(xy.xy))
{
xy.xy = gamut.corrected(xy.xy);
}
return xy;
}
HueSaturation RGB::toHueSaturation() const
{
const uint8_t cmax = std::max(r, std::max(g, b));
const uint8_t cmin = std::min(r, std::min(g, b));
const float diff = cmax - cmin;
int h = -1;
int s = -1;
if (cmax == cmin)
{
h = 0;
}
else if (cmax == r)
{
h = (int)(9307 * ((g - b) / diff) + 65535) % 65535;
}
else if (cmax == g)
{
h = (int)(12750 * ((b - r) / diff) + 25500) % 65535;
}
else if (cmax == b)
{
h = (int)(10710 * ((r - g) / diff) + 46920) % 65535;
}
if (cmax == 0)
{
s = 0;
}
else
{
s = std::round((diff / cmax) * 254);
}
return {h, s};
}
RGB RGB::fromXY(const XYBrightness& xy)
{
if (xy.brightness < 1e-4)
{
return RGB {0, 0, 0};
}
const float z = 1.f - xy.xy.x - xy.xy.y;
// use a fixed luminosity and rescale the resulting rgb values using brightness
// randomly sampled conversions shown a minimum difference between original values
// and values after rgb -> xy -> rgb conversion for Y = 0.3
// (r-r')^2, (g-g')^2, (b-b')^2:
// 4.48214, 4.72039, 3.12141
// Max. Difference:
// 9, 9, 8
const float Y = 0.3f;
const float X = (Y / xy.xy.y) * xy.xy.x;
const float Z = (Y / xy.xy.y) * z;
const float r = X * 1.656492f - Y * 0.354851f - Z * 0.255038f;
const float g = -X * 0.707196f + Y * 1.655397f + Z * 0.036152f;
const float b = X * 0.051713f - Y * 0.121364f + Z * 1.011530f;
// Reverse gamma correction
const float gammaR = r <= 0.0031308f ? 12.92f * r : (1.0f + 0.055f) * pow(r, (1.0f / 2.4f)) - 0.055f;
const float gammaG = g <= 0.0031308f ? 12.92f * g : (1.0f + 0.055f) * pow(g, (1.0f / 2.4f)) - 0.055f;
const float gammaB = b <= 0.0031308f ? 12.92f * b : (1.0f + 0.055f) * pow(b, (1.0f / 2.4f)) - 0.055f;
// Scale color values so that the brightness matches
const float maxColor = std::max({gammaR, gammaG, gammaB});
if (maxColor < 1e-4)
{
// Low color values, out of gamut?
return RGB {0, 0, 0};
}
const float rScaled = gammaR / maxColor * xy.brightness * 255.f;
const float gScaled = gammaG / maxColor * xy.brightness * 255.f;
const float bScaled = gammaB / maxColor * xy.brightness * 255.f;
return RGB {static_cast<uint8_t>(std::round(std::max(0.f, rScaled))),
static_cast<uint8_t>(std::round(std::max(0.f, gScaled))),
static_cast<uint8_t>(std::round(std::max(0.f, bScaled)))};
}
RGB RGB::fromXY(const XYBrightness& xy, const ColorGamut& gamut)
{
if (gamut.contains(xy.xy))
{
return fromXY(xy);
}
else
{
return fromXY(XYBrightness {gamut.corrected(xy.xy), xy.brightness});
}
}
unsigned int kelvinToMired(unsigned int kelvin)
{
return int(std::round(1000000.f / kelvin));
}
unsigned int miredToKelvin(unsigned int mired)
{
return int(std::round(1000000.f / mired));
}
} // namespace hueplusplus

View File

@@ -0,0 +1,317 @@
/**
\file EntertainmentMode.cpp
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/>.
**/
#include "hueplusplus/EntertainmentMode.h"
#include "mbedtls/certs.h"
#include "mbedtls/ctr_drbg.h"
#include "mbedtls/debug.h"
#include "mbedtls/entropy.h"
#include "mbedtls/error.h"
#include "mbedtls/net_sockets.h"
#include "mbedtls/ssl.h"
#include "mbedtls/timing.h"
namespace hueplusplus
{
constexpr uint8_t HUE_ENTERTAINMENT_HEADER_SIZE = 16;
constexpr uint8_t HUE_ENTERTAINMENT_LIGHT_SIZE = 9;
struct TLSContext
{
mbedtls_ssl_context ssl;
mbedtls_net_context server_fd;
mbedtls_entropy_context entropy;
mbedtls_ctr_drbg_context ctr_drbg;
mbedtls_ssl_config conf;
mbedtls_x509_crt cacert;
mbedtls_timing_delay_context timer;
};
std::vector<char> hexToBytes(const std::string& hex)
{
std::vector<char> bytes;
for (unsigned int i = 0; i < hex.length(); i += 2)
{
std::string byteString = hex.substr(i, 2);
char byte = (char)strtol(byteString.c_str(), NULL, 16);
bytes.push_back(byte);
}
return bytes;
}
EntertainmentMode::EntertainmentMode(Bridge& b, Group& g)
: bridge(&b), group(&g), tls_context(std::make_unique<TLSContext>(TLSContext {}))
{
/*-------------------------------------------------*\
| Signal the bridge to start streaming |
\*-------------------------------------------------*/
bridge->startStreaming(std::to_string(group->getId()));
/*-------------------------------------------------*\
| Get the number of lights from the group |
\*-------------------------------------------------*/
entertainment_num_lights = group->getLightIds().size();
/*-------------------------------------------------*\
| Resize Entertainment Mode message buffer |
\*-------------------------------------------------*/
entertainment_msg.resize(HUE_ENTERTAINMENT_HEADER_SIZE + (entertainment_num_lights * HUE_ENTERTAINMENT_LIGHT_SIZE));
/*-------------------------------------------------*\
| Fill in Entertainment Mode message header |
\*-------------------------------------------------*/
memcpy(&entertainment_msg[0], "HueStream", 9);
entertainment_msg[9] = 0x01; // Version Major (1)
entertainment_msg[10] = 0x00; // Version Minor (0)
entertainment_msg[11] = 0x00; // Sequence ID
entertainment_msg[12] = 0x00; // Reserved
entertainment_msg[13] = 0x00; // Reserved
entertainment_msg[14] = 0x00; // Color Space (RGB)
entertainment_msg[15] = 0x00; // Reserved
/*-------------------------------------------------*\
| Fill in Entertainment Mode light data |
\*-------------------------------------------------*/
for (unsigned int light_idx = 0; light_idx < entertainment_num_lights; light_idx++)
{
unsigned int msg_idx = HUE_ENTERTAINMENT_HEADER_SIZE + (light_idx * HUE_ENTERTAINMENT_LIGHT_SIZE);
entertainment_msg[msg_idx + 0] = 0x00; // Type (Light)
entertainment_msg[msg_idx + 1] = group->getLightIds()[light_idx] >> 8; // ID MSB
entertainment_msg[msg_idx + 2] = group->getLightIds()[light_idx] & 0xFF; // ID LSB
entertainment_msg[msg_idx + 3] = 0x00; // Red MSB
entertainment_msg[msg_idx + 4] = 0x00; // Red LSB;
entertainment_msg[msg_idx + 5] = 0x00; // Green MSB;
entertainment_msg[msg_idx + 6] = 0x00; // Green LSB;
entertainment_msg[msg_idx + 7] = 0x00; // Blue MSB;
entertainment_msg[msg_idx + 8] = 0x00; // Blue LSB;
}
/*-------------------------------------------------*\
| Initialize mbedtls contexts |
\*-------------------------------------------------*/
mbedtls_net_init(&tls_context->server_fd);
mbedtls_ssl_init(&tls_context->ssl);
mbedtls_ssl_config_init(&tls_context->conf);
mbedtls_x509_crt_init(&tls_context->cacert);
mbedtls_ctr_drbg_init(&tls_context->ctr_drbg);
mbedtls_entropy_init(&tls_context->entropy);
/*-------------------------------------------------*\
| Seed the Deterministic Random Bit Generator (RNG) |
\*-------------------------------------------------*/
int ret = mbedtls_ctr_drbg_seed(&tls_context->ctr_drbg, mbedtls_entropy_func, &tls_context->entropy, NULL, 0);
/*-------------------------------------------------*\
| Parse certificate |
\*-------------------------------------------------*/
ret = mbedtls_x509_crt_parse(
&tls_context->cacert, (const unsigned char*)mbedtls_test_cas_pem, mbedtls_test_cas_pem_len);
}
EntertainmentMode::~EntertainmentMode()
{
mbedtls_entropy_free(&tls_context->entropy);
mbedtls_ctr_drbg_free(&tls_context->ctr_drbg);
mbedtls_x509_crt_free(&tls_context->cacert);
mbedtls_ssl_config_free(&tls_context->conf);
mbedtls_ssl_free(&tls_context->ssl);
mbedtls_net_free(&tls_context->server_fd);
}
bool EntertainmentMode::connect()
{
/*-------------------------------------------------*\
| Signal the bridge to start streaming |
| If successful, connect to the UDP port |
\*-------------------------------------------------*/
if (bridge->startStreaming(std::to_string(group->getId())))
{
/*-------------------------------------------------*\
| Connect to the Hue bridge UDP server |
\*-------------------------------------------------*/
int ret = mbedtls_net_connect(
&tls_context->server_fd, bridge->getBridgeIP().c_str(), "2100", MBEDTLS_NET_PROTO_UDP);
/*-------------------------------------------------*\
| If connecting failed, close and return false |
\*-------------------------------------------------*/
if (ret != 0)
{
mbedtls_ssl_close_notify(&tls_context->ssl);
bridge->stopStreaming(std::to_string(group->getId()));
return false;
}
/*-------------------------------------------------*\
| Configure defaults |
\*-------------------------------------------------*/
ret = mbedtls_ssl_config_defaults(
&tls_context->conf, MBEDTLS_SSL_IS_CLIENT, MBEDTLS_SSL_TRANSPORT_DATAGRAM, MBEDTLS_SSL_PRESET_DEFAULT);
/*-------------------------------------------------*\
| If configuring failed, close and return false |
\*-------------------------------------------------*/
if (ret != 0)
{
mbedtls_ssl_close_notify(&tls_context->ssl);
bridge->stopStreaming(std::to_string(group->getId()));
return false;
}
mbedtls_ssl_conf_authmode(&tls_context->conf, MBEDTLS_SSL_VERIFY_OPTIONAL);
mbedtls_ssl_conf_ca_chain(&tls_context->conf, &tls_context->cacert, NULL);
mbedtls_ssl_conf_rng(&tls_context->conf, mbedtls_ctr_drbg_random, &tls_context->ctr_drbg);
/*-------------------------------------------------*\
| Convert client key to binary array |
\*-------------------------------------------------*/
std::vector<char> psk_binary = hexToBytes(bridge->getClientKey());
/*-------------------------------------------------*\
| Configure SSL pre-shared key and identity |
| PSK - binary array from client key |
| Identity - username (ASCII) |
\*-------------------------------------------------*/
ret = mbedtls_ssl_conf_psk(&tls_context->conf, (const unsigned char*)&psk_binary[0], psk_binary.size(),
(const unsigned char*)bridge->getUsername().c_str(), bridge->getUsername().length());
/*-------------------------------------------------*\
| If configuring failed, close and return false |
\*-------------------------------------------------*/
if (ret != 0)
{
mbedtls_ssl_close_notify(&tls_context->ssl);
bridge->stopStreaming(std::to_string(group->getId()));
return false;
}
/*-------------------------------------------------*\
| Set up the SSL |
\*-------------------------------------------------*/
ret = mbedtls_ssl_setup(&tls_context->ssl, &tls_context->conf);
/*-------------------------------------------------*\
| If setup failed, close and return false |
\*-------------------------------------------------*/
if (ret != 0)
{
mbedtls_ssl_close_notify(&tls_context->ssl);
bridge->stopStreaming(std::to_string(group->getId()));
return false;
}
ret = mbedtls_ssl_set_hostname(&tls_context->ssl, "localhost");
/*-------------------------------------------------*\
| If set hostname failed, close and return false |
\*-------------------------------------------------*/
if (ret != 0)
{
mbedtls_ssl_close_notify(&tls_context->ssl);
bridge->stopStreaming(std::to_string(group->getId()));
return false;
}
mbedtls_ssl_set_bio(
&tls_context->ssl, &tls_context->server_fd, mbedtls_net_send, mbedtls_net_recv, mbedtls_net_recv_timeout);
mbedtls_ssl_set_timer_cb(
&tls_context->ssl, &tls_context->timer, mbedtls_timing_set_delay, mbedtls_timing_get_delay);
/*-------------------------------------------------*\
| Handshake |
\*-------------------------------------------------*/
do
{
ret = mbedtls_ssl_handshake(&tls_context->ssl);
} while (ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE);
/*-------------------------------------------------*\
| If set hostname failed, close and return false |
\*-------------------------------------------------*/
if (ret != 0)
{
mbedtls_ssl_close_notify(&tls_context->ssl);
bridge->stopStreaming(std::to_string(group->getId()));
return false;
}
return true;
}
else
{
return false;
}
}
bool EntertainmentMode::disconnect()
{
mbedtls_ssl_close_notify(&tls_context->ssl);
return bridge->stopStreaming(std::to_string(group->getId()));
}
bool EntertainmentMode::setColorRGB(uint8_t light_index, uint8_t red, uint8_t green, uint8_t blue)
{
if (light_index < entertainment_num_lights)
{
unsigned int msg_idx = HUE_ENTERTAINMENT_HEADER_SIZE + (light_index * HUE_ENTERTAINMENT_LIGHT_SIZE);
entertainment_msg[msg_idx + 3] = red; // Red MSB
entertainment_msg[msg_idx + 4] = red; // Red LSB;
entertainment_msg[msg_idx + 5] = green; // Green MSB;
entertainment_msg[msg_idx + 6] = green; // Green LSB;
entertainment_msg[msg_idx + 7] = blue; // Blue MSB;
entertainment_msg[msg_idx + 8] = blue; // Blue LSB;
return true;
}
else
{
return false;
}
}
bool EntertainmentMode::update()
{
int ret;
unsigned int total = 0;
while (total < entertainment_msg.size())
{
ret = mbedtls_ssl_write(
&tls_context->ssl, (const unsigned char*)&entertainment_msg[total], entertainment_msg.size());
if (ret < 0)
{
// Return if mbedtls_ssl_write errors
return false;
}
else
{
total += ret;
}
}
return true;
}
} // namespace hueplusplus

View File

@@ -0,0 +1,94 @@
/**
\file ExtendedColorHueStrategy.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/ExtendedColorHueStrategy.h"
#include <cmath>
#include <iostream>
#include <thread>
#include "hueplusplus/LibConfig.h"
namespace hueplusplus
{
bool ExtendedColorHueStrategy::alertHueSaturation(const HueSaturation& hueSat, Light& light) const
{
// Careful, only use state until any light function might refresh the value and invalidate the reference
const nlohmann::json& state = light.state.getValue()["state"];
std::string cType = state["colormode"].get<std::string>();
bool on = state["on"].get<bool>();
if (cType != "ct")
{
return SimpleColorHueStrategy::alertHueSaturation(hueSat, light);
}
else
{
uint16_t oldCT = state["ct"].get<uint16_t>();
if (!light.setColorHueSaturation(hueSat, 1))
{
return false;
}
std::this_thread::sleep_for(Config::instance().getPreAlertDelay());
if (!light.alert())
{
return false;
}
std::this_thread::sleep_for(Config::instance().getPostAlertDelay());
return light.transaction().setColorTemperature(oldCT).setOn(on).setTransition(1).commit();
}
}
bool ExtendedColorHueStrategy::alertXY(const XYBrightness& xy, Light& light) const
{
// Careful, only use state until any light function might refresh the value and invalidate the reference
const nlohmann::json& state = light.state.getValue()["state"];
std::string cType = state["colormode"].get<std::string>();
bool on = state["on"].get<bool>();
// const reference to prevent refreshes
const Light& cLight = light;
if (cType != "ct")
{
return SimpleColorHueStrategy::alertXY(xy, light);
}
else
{
uint16_t oldCT = state["ct"].get<uint16_t>();
uint8_t oldBrightness = cLight.getBrightness();
if (!light.setColorXY(xy, 1))
{
return false;
}
std::this_thread::sleep_for(Config::instance().getPreAlertDelay());
if (!light.alert())
{
return false;
}
std::this_thread::sleep_for(Config::instance().getPostAlertDelay());
return light.transaction()
.setColorTemperature(oldCT)
.setBrightness(oldBrightness)
.setOn(on)
.setTransition(1)
.commit();
}
}
} // namespace hueplusplus

View File

@@ -0,0 +1,81 @@
/**
\file ExtendedColorTemperatureStrategy.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/ExtendedColorTemperatureStrategy.h"
#include <cmath>
#include <iostream>
#include <thread>
#include "hueplusplus/LibConfig.h"
#include "hueplusplus/HueExceptionMacro.h"
#include "hueplusplus/Utils.h"
namespace hueplusplus
{
bool ExtendedColorTemperatureStrategy::alertTemperature(unsigned int mired, Light& light) const
{
// Careful, only use state until any light function might refresh the value and invalidate the reference
const nlohmann::json& state = light.state.getValue()["state"];
std::string cType = state["colormode"].get<std::string>();
bool on = state["on"].get<bool>();
const Light& cLight = light;
if (cType == "ct")
{
return SimpleColorTemperatureStrategy::alertTemperature(mired, light);
}
else if (cType == "hs")
{
HueSaturation oldHueSat = cLight.getColorHueSaturation();
if (!light.setColorTemperature(mired, 1))
{
return false;
}
std::this_thread::sleep_for(Config::instance().getPreAlertDelay());
if (!light.alert())
{
return false;
}
std::this_thread::sleep_for(Config::instance().getPostAlertDelay());
return light.transaction().setColor(oldHueSat).setOn(on).setTransition(1).commit();
}
else if (cType == "xy")
{
XYBrightness oldXy = cLight.getColorXY();
if (!light.setColorTemperature(mired, 1))
{
return false;
}
std::this_thread::sleep_for(Config::instance().getPreAlertDelay());
if (!light.alert())
{
return false;
}
std::this_thread::sleep_for(Config::instance().getPostAlertDelay());
return light.transaction().setColor(oldXy).setOn(on).setTransition(1).commit();
}
else
{
return false;
}
}
} // namespace hueplusplus

View File

@@ -0,0 +1,282 @@
#include "hueplusplus/Group.h"
#include "hueplusplus/HueExceptionMacro.h"
namespace hueplusplus
{
Group::Group(int id, const std::shared_ptr<APICache>& baseCache)
: id(id), state(baseCache, std::to_string(id), baseCache->getRefreshDuration())
{ }
Group::Group(int id, const HueCommandAPI& commands, std::chrono::steady_clock::duration refreshDuration, const nlohmann::json& currentState)
: id(id), state("/groups/" + std::to_string(id), commands, refreshDuration, currentState)
{
// Initialize value if not null
state.getValue();
}
void Group::refresh(bool force)
{
if (force)
{
state.refresh();
}
else
{
state.getValue();
}
}
void Group::setRefreshDuration(std::chrono::steady_clock::duration refreshDuration)
{
state.setRefreshDuration(refreshDuration);
}
int Group::getId() const
{
return id;
}
std::string Group::getName() const
{
return state.getValue().at("name").get<std::string>();
}
std::string Group::getType() const
{
return state.getValue().at("type").get<std::string>();
}
std::vector<int> Group::getLightIds() const
{
const nlohmann::json& lights = state.getValue().at("lights");
std::vector<int> ids;
ids.reserve(lights.size());
for (const nlohmann::json& id : lights)
{
// Luminaires can have null ids if not all light have been added
if (!id.is_null())
{
ids.push_back(std::stoi(id.get<std::string>()));
}
}
return ids;
}
void Group::setName(const std::string& name)
{
nlohmann::json request = {{"name", name}};
sendPutRequest("", request, CURRENT_FILE_INFO);
refresh(true);
}
void Group::setLights(const std::vector<int>& ids)
{
nlohmann::json lights = nlohmann::json::array();
for (int id : ids)
{
lights.push_back(std::to_string(id));
}
sendPutRequest("", {{"lights", lights}}, CURRENT_FILE_INFO);
refresh(true);
}
bool Group::getAllOn()
{
return state.getValue().at("state").at("all_on").get<bool>();
}
bool Group::getAllOn() const
{
return state.getValue().at("state").at("all_on").get<bool>();
}
bool Group::getAnyOn()
{
return state.getValue().at("state").at("any_on").get<bool>();
}
bool Group::getAnyOn() const
{
return state.getValue().at("state").at("any_on").get<bool>();
}
bool Group::getActionOn()
{
return state.getValue().at("action").at("on").get<bool>();
}
bool Group::getActionOn() const
{
return state.getValue().at("action").at("on").get<bool>();
}
std::pair<uint16_t, uint8_t> Group::getActionHueSaturation()
{
const nlohmann::json& action = state.getValue().at("action");
return std::make_pair(action.at("hue").get<int>(), action.at("sat").get<int>());
}
std::pair<uint16_t, uint8_t> Group::getActionHueSaturation() const
{
const nlohmann::json& action = state.getValue().at("action");
return std::make_pair(action.at("hue").get<int>(), action.at("sat").get<int>());
}
unsigned int Group::getActionBrightness()
{
return state.getValue().at("action").at("bri").get<int>();
}
unsigned int Group::getActionBrightness() const
{
return state.getValue().at("action").at("bri").get<int>();
}
unsigned int Group::getActionColorTemperature()
{
return state.getValue().at("action").at("ct").get<int>();
}
unsigned int Group::getActionColorTemperature() const
{
return state.getValue().at("action").at("ct").get<int>();
}
std::pair<float, float> Group::getActionColorXY()
{
const nlohmann::json& xy = state.getValue().at("action").at("xy");
return std::pair<float, float>(xy[0].get<float>(), xy[1].get<float>());
}
std::pair<float, float> Group::getActionColorXY() const
{
const nlohmann::json& xy = state.getValue().at("action").at("xy");
return std::pair<float, float>(xy[0].get<float>(), xy[1].get<float>());
}
std::string Group::getActionColorMode()
{
return state.getValue().at("action").at("colormode").get<std::string>();
}
std::string Group::getActionColorMode() const
{
return state.getValue().at("action").at("colormode").get<std::string>();
}
StateTransaction Group::transaction()
{
// Do not pass state, because it is not the state of ALL lights in the group
return StateTransaction(state.getCommandAPI(), "/groups/" + std::to_string(id) + "/action", nullptr);
}
void Group::setOn(bool on, uint8_t transition)
{
transaction().setOn(on).setTransition(transition).commit();
}
void Group::setBrightness(uint8_t brightness, uint8_t transition)
{
transaction().setBrightness(brightness).setTransition(transition).commit();
}
void Group::setColor(const HueSaturation& hueSat, uint8_t transition)
{
transaction().setColor(hueSat).setTransition(transition).commit();
}
void Group::setColor(const XYBrightness& xy, uint8_t transition)
{
transaction().setColor(xy).setTransition(transition).commit();
}
void Group::setColorTemperature(unsigned int mired, uint8_t transition)
{
transaction().setColorTemperature(mired).setTransition(transition).commit();
}
void Group::setColorLoop(bool on, uint8_t transition)
{
transaction().setColorLoop(on).setTransition(transition);
}
void Group::setScene(const std::string& scene)
{
sendPutRequest("/action", {{"scene", scene}}, CURRENT_FILE_INFO);
}
Action Group::createSceneAction(const std::string& scene) const
{
const nlohmann::json command {{"method", "PUT"},
{"address", state.getCommandAPI().combinedPath("/groups/" + std::to_string(id) + "/action")},
{"body", {{"scene", scene}}}};
return Action(command);
}
nlohmann::json Group::sendPutRequest(const std::string& subPath, const nlohmann::json& request, FileInfo fileInfo)
{
return state.getCommandAPI().PUTRequest("/groups/" + std::to_string(id) + subPath, request, std::move(fileInfo));
}
std::string Group::getRoomType() const
{
return state.getValue().at("class").get<std::string>();
}
void Group::setRoomType(const std::string& type)
{
sendPutRequest("", {{"class", type}}, CURRENT_FILE_INFO);
refresh(true);
}
std::string Group::getModelId() const
{
return state.getValue().at("modelid").get<std::string>();
}
std::string Group::getUniqueId() const
{
return state.getValue().at("uniqueid").get<std::string>();
}
CreateGroup CreateGroup::LightGroup(const std::vector<int>& lights, const std::string& name)
{
return CreateGroup(lights, name, "LightGroup", "");
}
CreateGroup CreateGroup::Room(const std::vector<int>& lights, const std::string& name, const std::string& roomType)
{
return CreateGroup(lights, name, "Room", roomType);
}
CreateGroup CreateGroup::Entertainment(const std::vector<int>& lights, const std::string& name)
{
return CreateGroup(lights, name, "Entertainment", "");
}
CreateGroup CreateGroup::Zone(const std::vector<int>& lights, const std::string& name)
{
return CreateGroup(lights, name, "Zone", "");
}
nlohmann::json CreateGroup::getRequest() const
{
nlohmann::json lightStrings = nlohmann::json::array();
for (int light : lights)
{
lightStrings.push_back(std::to_string(light));
}
nlohmann::json result = {{"lights", lightStrings}, {"type", type}};
if (!name.empty())
{
result["name"] = name;
}
if (!roomType.empty())
{
result["class"] = roomType;
}
return result;
}
CreateGroup::CreateGroup(
const std::vector<int>& lights, const std::string& name, const std::string& type, const std::string& roomType)
: lights(lights), name(name), type(type), roomType(roomType)
{ }
} // namespace hueplusplus

View File

@@ -0,0 +1,158 @@
/**
\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/>.
**/
#include "hueplusplus/HueCommandAPI.h"
#include <thread>
#include "hueplusplus/LibConfig.h"
#include "hueplusplus/HueExceptionMacro.h"
namespace hueplusplus
{
namespace
{
// Runs functor with appropriate timeout and retries when timed out or connection reset
template <typename Timeout, typename Fun>
nlohmann::json RunWithTimeout(std::shared_ptr<Timeout> timeout, std::chrono::steady_clock::duration minDelay, Fun fun)
{
auto now = std::chrono::steady_clock::now();
std::lock_guard<std::mutex> lock(timeout->mutex);
if (timeout->timeout > now)
{
std::this_thread::sleep_until(timeout->timeout);
}
try
{
nlohmann::json response = fun();
timeout->timeout = now + minDelay;
return response;
}
catch (const std::system_error& e)
{
if (e.code() == std::errc::connection_reset || e.code() == std::errc::timed_out)
{
// Happens when hue is too busy, wait and try again (once)
std::this_thread::sleep_for(minDelay);
nlohmann::json v = fun();
timeout->timeout = std::chrono::steady_clock::now() + minDelay;
return v;
}
// Cannot recover from other types of errors
throw;
}
}
} // namespace
HueCommandAPI::HueCommandAPI(
const std::string& ip, const int port, const std::string& username, std::shared_ptr<const IHttpHandler> httpHandler)
: ip(ip),
port(port),
username(username),
httpHandler(std::move(httpHandler)),
timeout(new TimeoutData {std::chrono::steady_clock::now(), {}})
{}
nlohmann::json HueCommandAPI::PUTRequest(const std::string& path, const nlohmann::json& request) const
{
return PUTRequest(path, request, CURRENT_FILE_INFO);
}
nlohmann::json HueCommandAPI::PUTRequest(
const std::string& path, const nlohmann::json& request, FileInfo fileInfo) const
{
return HandleError(std::move(fileInfo), RunWithTimeout(timeout, Config::instance().getBridgeRequestDelay(), [&]() {
return httpHandler->PUTJson(combinedPath(path), request, ip, port);
}));
}
nlohmann::json HueCommandAPI::GETRequest(const std::string& path, const nlohmann::json& request) const
{
return GETRequest(path, request, CURRENT_FILE_INFO);
}
nlohmann::json HueCommandAPI::GETRequest(
const std::string& path, const nlohmann::json& request, FileInfo fileInfo) const
{
return HandleError(std::move(fileInfo), RunWithTimeout(timeout, Config::instance().getBridgeRequestDelay(), [&]() {
return httpHandler->GETJson(combinedPath(path), request, ip, port);
}));
}
nlohmann::json HueCommandAPI::DELETERequest(const std::string& path, const nlohmann::json& request) const
{
return DELETERequest(path, request, CURRENT_FILE_INFO);
}
nlohmann::json HueCommandAPI::DELETERequest(
const std::string& path, const nlohmann::json& request, FileInfo fileInfo) const
{
return HandleError(std::move(fileInfo), RunWithTimeout(timeout, Config::instance().getBridgeRequestDelay(), [&]() {
return httpHandler->DELETEJson(combinedPath(path), request, ip, port);
}));
}
nlohmann::json HueCommandAPI::POSTRequest(const std::string& path, const nlohmann::json& request) const
{
return POSTRequest(path, request, CURRENT_FILE_INFO);
}
nlohmann::json HueCommandAPI::POSTRequest(
const std::string& path, const nlohmann::json& request, FileInfo fileInfo) const
{
return HandleError(std::move(fileInfo), RunWithTimeout(timeout, Config::instance().getBridgeRequestDelay(), [&]() {
return httpHandler->POSTJson(combinedPath(path), request, ip, port);
}));
}
nlohmann::json HueCommandAPI::HandleError(FileInfo fileInfo, const nlohmann::json& response) const
{
if (response.count("error"))
{
throw HueAPIResponseException::Create(std::move(fileInfo), response);
}
else if (response.is_array())
{
// Check if array contains error response
auto it
= std::find_if(response.begin(), response.end(), [](const nlohmann::json& v) { return v.count("error"); });
if (it != response.end())
{
throw HueAPIResponseException::Create(std::move(fileInfo), it.value());
}
}
return response;
}
std::string HueCommandAPI::combinedPath(const std::string& path) const
{
std::string result = "/api/";
result.append(username);
// If path does not begin with '/', insert it unless it is empty
if (!path.empty() && path.front() != '/')
{
result.append("/");
}
result.append(path);
return result;
}
} // namespace hueplusplus

View File

@@ -0,0 +1,167 @@
/**
\file HueDeviceTypes.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/HueDeviceTypes.h"
#include <set>
#include "hueplusplus/ExtendedColorHueStrategy.h"
#include "hueplusplus/ExtendedColorTemperatureStrategy.h"
#include "hueplusplus/HueDeviceTypes.h"
#include "hueplusplus/HueExceptionMacro.h"
#include "hueplusplus/SimpleBrightnessStrategy.h"
#include "hueplusplus/SimpleColorHueStrategy.h"
#include "hueplusplus/SimpleColorTemperatureStrategy.h"
#include "hueplusplus/Utils.h"
namespace hueplusplus
{
namespace
{
const std::set<std::string>& getGamutBTypes()
{
static const std::set<std::string> c_EXTENDEDCOLORLIGHT_GAMUTB_TYPES
= {"LCT001", "LCT002", "LCT003", "LCT007", "LLM001"};
return c_EXTENDEDCOLORLIGHT_GAMUTB_TYPES;
};
const std::set<std::string>& getGamutCTypes()
{
static const std::set<std::string> c_EXTENDEDCOLORLIGHT_GAMUTC_TYPES
= {"LCT010", "LCT011", "LCT012", "LCT014", "LCT015", "LCT016", "LLC020", "LST002", "LCA003", "LCB001" };
return c_EXTENDEDCOLORLIGHT_GAMUTC_TYPES;
}
const std::set<std::string>& getGamutATypes()
{
static const std::set<std::string> c_EXTENDEDCOLORLIGHT_GAMUTA_TYPES
= {"LST001", "LLC005", "LLC006", "LLC007", "LLC010", "LLC011", "LLC012", "LLC013", "LLC014"};
return c_EXTENDEDCOLORLIGHT_GAMUTA_TYPES;
}
} // namespace
LightFactory::LightFactory(const HueCommandAPI& commands, std::chrono::steady_clock::duration refreshDuration)
: commands(commands),
refreshDuration(refreshDuration),
simpleBrightness(std::make_shared<SimpleBrightnessStrategy>()),
simpleColorTemperature(std::make_shared<SimpleColorTemperatureStrategy>()),
extendedColorTemperature(std::make_shared<ExtendedColorTemperatureStrategy>()),
simpleColorHue(std::make_shared<SimpleColorHueStrategy>()),
extendedColorHue(std::make_shared<ExtendedColorHueStrategy>())
{ }
Light LightFactory::createLight(const nlohmann::json& lightState, int id, const std::shared_ptr<APICache>& baseCache)
{
std::string type = lightState.value("type", "");
// Ignore case
std::transform(type.begin(), type.end(), type.begin(), [](char c) { return std::tolower(c); });
Light light = baseCache ? Light(id, baseCache) : Light(id, commands, nullptr, nullptr, nullptr, refreshDuration, lightState);
if (type == "on/off light" || type == "on/off plug-in unit")
{
light.colorType = ColorType::NONE;
return light;
}
else if (type == "dimmable light" || type == "dimmable plug-in unit")
{
light.setBrightnessStrategy(simpleBrightness);
light.colorType = ColorType::NONE;
return light;
}
else if (type == "color temperature light")
{
light.setBrightnessStrategy(simpleBrightness);
light.setColorTemperatureStrategy(simpleColorTemperature);
light.colorType = ColorType::TEMPERATURE;
return light;
}
else if (type == "color light")
{
light.setBrightnessStrategy(simpleBrightness);
light.setColorHueStrategy(simpleColorHue);
light.colorType = getColorType(lightState, false);
return light;
}
else if (type == "extended color light")
{
light.setBrightnessStrategy(simpleBrightness);
light.setColorTemperatureStrategy(extendedColorTemperature);
light.setColorHueStrategy(extendedColorHue);
light.colorType = getColorType(lightState, true);
return light;
}
std::cerr << "Could not determine Light type:" << type << "!\n";
throw HueException(CURRENT_FILE_INFO, "Could not determine Light type!");
}
ColorType LightFactory::getColorType(const nlohmann::json& lightState, bool hasCt) const
{
// Try to get color type via capabilities
const nlohmann::json& gamuttype = utils::safeGetMember(lightState, "capabilities", "control", "colorgamuttype");
if (gamuttype.is_string())
{
const std::string gamut = gamuttype.get<std::string>();
if (gamut == "A")
{
return hasCt ? ColorType::GAMUT_A_TEMPERATURE : ColorType::GAMUT_A;
}
else if (gamut == "B")
{
return hasCt ? ColorType::GAMUT_B_TEMPERATURE : ColorType::GAMUT_B;
}
else if (gamut == "C")
{
return hasCt ? ColorType::GAMUT_C_TEMPERATURE : ColorType::GAMUT_C;
}
else
{
// Only other type is "Other" which does not have an enum value
return hasCt ? ColorType::GAMUT_OTHER_TEMPERATURE : ColorType::GAMUT_OTHER;
}
}
else
{
// Old version without capabilities, fall back to hardcoded types
std::string modelid = lightState.at("modelid").get<std::string>();
if (getGamutATypes().count(modelid))
{
return hasCt ? ColorType::GAMUT_A_TEMPERATURE : ColorType::GAMUT_A;
}
else if (getGamutBTypes().count(modelid))
{
return hasCt ? ColorType::GAMUT_B_TEMPERATURE : ColorType::GAMUT_B;
}
else if (getGamutCTypes().count(modelid))
{
return hasCt ? ColorType::GAMUT_C_TEMPERATURE : ColorType::GAMUT_C;
}
else
{
std::cerr << "Warning: Could not determine Light color type:" << modelid
<< "!\n"
"Results may not be correct.\n";
return ColorType::UNDEFINED;
}
}
}
} // namespace hueplusplus

View File

@@ -0,0 +1,117 @@
/**
\file HueException.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/HueException.h"
namespace hueplusplus
{
HueException::HueException(FileInfo fileInfo, const std::string& message)
: HueException("HueException", std::move(fileInfo), message)
{}
const char* HueException::what() const noexcept
{
return whatMessage.c_str();
}
const FileInfo& HueException::GetFile() const noexcept
{
return fileInfo;
}
HueException::HueException(const char* exceptionName, FileInfo fileInfo, const std::string& message)
: fileInfo(std::move(fileInfo))
{
whatMessage = exceptionName;
whatMessage.append(" from ");
whatMessage.append(this->fileInfo.ToString());
whatMessage.append(" ");
whatMessage.append(message);
}
HueAPIResponseException::HueAPIResponseException(
FileInfo fileInfo, int error, std::string address, std::string description)
: HueException("HueApiResponseException", std::move(fileInfo), GetMessage(error, address, description)),
error(error),
address(std::move(address)),
description(std::move(description))
{}
int HueAPIResponseException::GetErrorNumber() const noexcept
{
return error;
}
const std::string& HueAPIResponseException::GetAddress() const noexcept
{
return address;
}
const std::string& HueAPIResponseException::GetDescription() const noexcept
{
return description;
}
HueAPIResponseException HueAPIResponseException::Create(FileInfo fileInfo, const nlohmann::json& response)
{
const nlohmann::json error = response.at("error");
int errorCode = -1;
if (error.count("type"))
{
if (error["type"].is_number_integer())
{
errorCode = error["type"].get<int>();
}
else if (error["type"].is_string())
{
errorCode = std::stoi(error["type"].get<std::string>());
}
}
std::string address = error.value("address", "");
std::string description = error.value("description", "");
return HueAPIResponseException(std::move(fileInfo), errorCode, std::move(address), std::move(description));
}
std::string HueAPIResponseException::GetMessage(int error, const std::string& addr, const std::string& description)
{
std::string result = std::to_string(error);
result.append(" ");
result.append(addr);
result.append(" ");
result.append(description);
return result;
}
std::string FileInfo::ToString() const
{
if (filename.empty() || line < 0)
{
return "Unknown file";
}
std::string result = func;
result.append(" in ");
result.append(filename);
result.append(":");
result.append(std::to_string(line));
return result;
}
} // namespace hueplusplus

View File

@@ -0,0 +1,123 @@
/**
\file Light.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 <cmath>
#include <iostream>
#include <thread>
#include "hueplusplus/HueExceptionMacro.h"
#include "hueplusplus/Light.h"
#include "hueplusplus/Utils.h"
#include "json/json.hpp"
namespace hueplusplus
{
bool Light::on(uint8_t transition)
{
return transaction().setOn(true).setTransition(transition).commit();
}
bool Light::off(uint8_t transition)
{
return transaction().setOn(false).setTransition(transition).commit();
}
bool Light::isOn()
{
return state.getValue().at("state").at("on").get<bool>();
}
bool Light::isOn() const
{
return state.getValue().at("state").at("on").get<bool>();
}
std::string Light::getLuminaireUId() const
{
return state.getValue().value("luminaireuniqueid", std::string());
}
ColorType Light::getColorType() const
{
return colorType;
}
ColorGamut Light::getColorGamut() const
{
switch (colorType)
{
case ColorType::GAMUT_A:
case ColorType::GAMUT_A_TEMPERATURE:
return gamut::gamutA;
case ColorType::GAMUT_B:
case ColorType::GAMUT_B_TEMPERATURE:
return gamut::gamutB;
case ColorType::GAMUT_C:
case ColorType::GAMUT_C_TEMPERATURE:
return gamut::gamutC;
case ColorType::UNDEFINED:
return gamut::maxGamut;
default: { // GAMUT_OTHER, GAMUT_OTHER_TEMPERATURE
const nlohmann::json& capabilitiesGamut
= utils::safeGetMember(state.getValue(), "capabilities", "control", "colorgamut");
if (capabilitiesGamut.is_array() && capabilitiesGamut.size() == 3)
{
// Other gamut
return ColorGamut {{capabilitiesGamut[0].at(0), capabilitiesGamut[0].at(1)},
{capabilitiesGamut[1].at(0), capabilitiesGamut[1].at(1)},
{capabilitiesGamut[2].at(0), capabilitiesGamut[2].at(1)}};
}
// Unknown or no color light
return gamut::maxGamut;
}
}
}
bool Light::alert()
{
return transaction().alert().commit();
}
StateTransaction Light::transaction()
{
return StateTransaction(
state.getCommandAPI(), "/lights/" + std::to_string(id) + "/state", &state.getValue().at("state"));
}
Light::Light(int id, const HueCommandAPI& commands)
: Light(id, commands, nullptr, nullptr, nullptr, std::chrono::seconds(10), nullptr)
{ }
Light::Light(int id, const std::shared_ptr<APICache>& baseCache) : BaseDevice(id, baseCache), colorType(ColorType::NONE)
{ }
Light::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)
: BaseDevice(id, commands, "/lights/", refreshDuration, currentState),
colorType(ColorType::NONE),
brightnessStrategy(std::move(brightnessStrategy)),
colorTemperatureStrategy(std::move(colorTempStrategy)),
colorHueStrategy(std::move(colorHueStrategy))
{ }
} // namespace hueplusplus

View File

@@ -0,0 +1,226 @@
/**
\file LinHttpHandler.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/LinHttpHandler.h"
#include <chrono>
#include <cstring>
#include <iostream>
#include <memory>
#include <stdexcept>
#include <system_error>
#include <arpa/inet.h>
#include <netdb.h> // struct hostent, gethostbyname
#include <netinet/in.h> // struct sockaddr_in, struct sockaddr
#include <stdio.h> // printf, sprintf
#include <stdlib.h> // exit
#include <string.h> // functions for C style null-terminated strings
#include <sys/socket.h> // socket, connect
#include <unistd.h> // read, write, close
namespace hueplusplus
{
class SocketCloser
{
public:
explicit SocketCloser(int sockFd) : s(sockFd) {}
~SocketCloser() { close(s); }
private:
int s;
};
std::string LinHttpHandler::send(const std::string& msg, const std::string& adr, int port) const
{
// create socket
int socketFD = socket(AF_INET, SOCK_STREAM, 0);
SocketCloser closeMySocket(socketFD);
if (socketFD < 0)
{
int errCode = errno;
std::cerr << "LinHttpHandler: Failed to open socket: " << std::strerror(errCode) << "\n";
throw(std::system_error(errCode, std::generic_category(), "LinHttpHandler: Failed to open socket"));
}
// lookup ip address
hostent* server;
server = gethostbyname(adr.c_str());
if (server == NULL)
{
int errCode = errno;
std::cerr << "LinHttpHandler: Failed to find host with address " << adr << ": " << std::strerror(errCode)
<< "\n";
throw(std::system_error(errCode, std::generic_category(), "LinHttpHandler: gethostbyname"));
}
// fill in the structure
sockaddr_in server_addr;
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(port);
memcpy(&server_addr.sin_addr.s_addr, server->h_addr, server->h_length);
// connect the socket
if (connect(socketFD, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0)
{
int errCode = errno;
std::cerr << "LinHttpHandler: Failed to connect socket: " << std::strerror(errCode) << "\n";
throw(std::system_error(errCode, std::generic_category(), "LinHttpHandler: Failed to connect socket"));
}
// send the request
size_t total = msg.length();
size_t sent = 0;
do
{
ssize_t bytes = write(socketFD, msg.c_str() + sent, total - sent);
if (bytes < 0)
{
int errCode = errno;
std::cerr << "LinHttpHandler: Failed to write message to socket: " << std::strerror(errCode) << "\n";
throw(std::system_error(
errCode, std::generic_category(), "LinHttpHandler: Failed to write message to socket"));
}
else if (bytes == 0)
{
break;
}
else
{
sent += bytes;
}
} while (sent < total);
// receive the response
std::string response;
char buffer[128] = {};
do
{
ssize_t bytes = read(socketFD, buffer, 127);
if (bytes < 0)
{
int errCode = errno;
std::cerr << "LinHttpHandler: Failed to read response from socket: " << std::strerror(errCode) << std::endl;
throw(std::system_error(
errCode, std::generic_category(), "LinHttpHandler: Failed to read response from socket"));
}
else if (bytes == 0)
{
break;
}
else
{
response.append(buffer, bytes);
}
} while (true);
return response;
}
std::vector<std::string> LinHttpHandler::sendMulticast(
const std::string& msg, const std::string& adr, int port, std::chrono::steady_clock::duration timeout) const
{
hostent* server; // host information
sockaddr_in server_addr; // server address
// fill in the server's address and data
memset((char*)&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(port);
// look up the address of the server given its name
server = gethostbyname(adr.c_str());
if (!server)
{
int errCode = errno;
std::cerr << "LinHttpHandler: sendMulticast: Failed to obtain address of " << msg << ": "
<< std::strerror(errCode) << "\n";
throw(std::system_error(
errCode, std::generic_category(), "LinHttpHandler: sendMulticast: Failed to obtain address of host"));
}
// put the host's address into the server address structure
memcpy((void*)&server_addr.sin_addr, server->h_addr_list[0], server->h_length);
// create the socket
int socketFD = socket(AF_INET, SOCK_DGRAM, 0);
SocketCloser closeMySendSocket(socketFD);
if (socketFD < 0)
{
int errCode = errno;
std::cerr << "LinHttpHandler: sendMulticast: Failed to open socket: " << std::strerror(errCode) << "\n";
throw(std::system_error(
errCode, std::generic_category(), "LinHttpHandler: sendMulticast: Failed to open socket"));
}
// send a message to the server
if (sendto(socketFD, msg.c_str(), strlen(msg.c_str()), 0, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0)
{
int errCode = errno;
std::cerr << "LinHttpHandler: sendMulticast: Failed to send message: " << std::strerror(errCode) << "\n";
throw(std::system_error(
errCode, std::generic_category(), "LinHttpHandler: sendMulticast: Failed to send message"));
}
std::string response;
char buffer[2048] = {}; // receive buffer
std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
while (std::chrono::steady_clock::now() - start < timeout)
{
ssize_t bytesReceived = recv(socketFD, &buffer, 2048, MSG_DONTWAIT);
if (bytesReceived < 0)
{
int errCode = errno;
if (errCode != EAGAIN && errCode != EWOULDBLOCK)
{
std::cerr << "LinHttpHandler: sendMulticast: Failed to read response "
"from socket: "
<< std::strerror(errCode) << "\n";
throw(std::system_error(errCode, std::generic_category(),
"LinHttpHandler: sendMulticast: Failed to read "
"response from socket"));
}
continue;
}
if (bytesReceived)
{
response.append(buffer, bytesReceived);
}
}
// construct return vector
std::vector<std::string> returnString;
size_t pos = response.find("\r\n\r\n");
size_t prevpos = 0;
while (pos != std::string::npos)
{
returnString.push_back(response.substr(prevpos, pos - prevpos));
pos += 4;
prevpos = pos;
pos = response.find("\r\n\r\n", pos);
}
return returnString;
}
} // namespace hueplusplus

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