diff --git a/libsrc/leddevice/CMakeLists.txt b/libsrc/leddevice/CMakeLists.txt old mode 100644 new mode 100755 index 443e845d..098ac38c --- a/libsrc/leddevice/CMakeLists.txt +++ b/libsrc/leddevice/CMakeLists.txt @@ -29,6 +29,7 @@ SET(Leddevice_HEADERS ${CURRENT_SOURCE_DIR}/LedDeviceSedu.h ${CURRENT_SOURCE_DIR}/LedDeviceTest.h ${CURRENT_SOURCE_DIR}/LedDeviceHyperionUsbasp.h + ${CURRENT_SOURCE_DIR}/LedDevicePhilipsHue.h ) SET(Leddevice_SOURCES @@ -44,6 +45,7 @@ SET(Leddevice_SOURCES ${CURRENT_SOURCE_DIR}/LedDeviceSedu.cpp ${CURRENT_SOURCE_DIR}/LedDeviceTest.cpp ${CURRENT_SOURCE_DIR}/LedDeviceHyperionUsbasp.cpp + ${CURRENT_SOURCE_DIR}/LedDevicePhilipsHue.cpp ) if(ENABLE_SPIDEV) diff --git a/libsrc/leddevice/LedDeviceFactory.cpp b/libsrc/leddevice/LedDeviceFactory.cpp old mode 100644 new mode 100755 index b5bc6f5e..71aeda06 --- a/libsrc/leddevice/LedDeviceFactory.cpp +++ b/libsrc/leddevice/LedDeviceFactory.cpp @@ -28,6 +28,7 @@ #include "LedDeviceSedu.h" #include "LedDeviceTest.h" #include "LedDeviceHyperionUsbasp.h" +#include "LedDevicePhilipsHue.h" LedDevice * LedDeviceFactory::construct(const Json::Value & deviceConfig) { @@ -159,6 +160,11 @@ LedDevice * LedDeviceFactory::construct(const Json::Value & deviceConfig) deviceHyperionUsbasp->open(); device = deviceHyperionUsbasp; } + else if (type == "philipshue") + { + const std::string output = deviceConfig["output"].asString(); + device = new LedDevicePhilipsHue(output); + } else if (type == "test") { const std::string output = deviceConfig["output"].asString(); diff --git a/libsrc/leddevice/LedDevicePhilipsHue.cpp b/libsrc/leddevice/LedDevicePhilipsHue.cpp new file mode 100755 index 00000000..01334403 --- /dev/null +++ b/libsrc/leddevice/LedDevicePhilipsHue.cpp @@ -0,0 +1,81 @@ +// Local-Hyperion includes +#include "LedDevicePhilipsHue.h" + +#include + +#include +#include +#include +#include + +LedDevicePhilipsHue::LedDevicePhilipsHue(const std::string& output) : + host(output.c_str()), username("newdeveloper") { + http = new QHttp(host, 80); +} + +LedDevicePhilipsHue::~LedDevicePhilipsHue() { + delete http; +} + +int LedDevicePhilipsHue::write(const std::vector & ledValues) { + // Due to rate limiting (max. 30 request per seconds), discard new values if + // the previous request have not been completed yet. + if (http->hasPendingRequests()) { + return -1; + } + unsigned int lightId = 1; + for (const ColorRgb& color : ledValues) { + float x, y, b; + // Scale colors from [0, 255] to [0, 1] and convert to xy space. + rgbToXYBrightness(color.red / 255.0f, color.green / 255.0f, color.blue / 255.0f, x, y, b); + // Send adjust color command in JSON format. + put(getRoute(lightId), QString("{\"xy\": [%1, %2]}").arg(x).arg(y)); + // Send brightness color command in JSON format. + put(getRoute(lightId), QString("{\"bri\": %1}").arg(qRound(b * 255.0f))); + // Next light id. + lightId++; + } + return 0; +} + +int LedDevicePhilipsHue::switchOff() { + return 0; +} + +void LedDevicePhilipsHue::put(QString route, QString content) { + QString url = QString("/api/%1/%2").arg(username).arg(route); + QHttpRequestHeader header("PUT", url); + header.setValue("Host", host); + header.setValue("Accept-Encoding", "identity"); + header.setValue("Content-Length", QString("%1").arg(content.size())); + http->setHost(host); + http->request(header, content.toAscii()); + // std::cout << "LedDevicePhilipsHue::put(): " << header.toString().toUtf8().constData() << std::endl; + // std::cout << "LedDevicePhilipsHue::put(): " << content.toUtf8().constData() << std::endl; +} + +QString LedDevicePhilipsHue::getRoute(unsigned int lightId) { + return QString("lights/%1/state").arg(lightId); +} + +void LedDevicePhilipsHue::rgbToXYBrightness(float red, float green, float blue, float& x, float& y, float& brightness) { + // Apply gamma correction. + red = (red > 0.04045f) ? qPow((red + 0.055f) / (1.0f + 0.055f), 2.4f) : (red / 12.92f); + green = (green > 0.04045f) ? qPow ((green + 0.055f) / (1.0f + 0.055f), 2.4f) : (green / 12.92f); + blue = (blue > 0.04045f) ? qPow ((blue + 0.055f) / (1.0f + 0.055f), 2.4f) : (blue / 12.92f); + // Convert to XYZ space. + float X = red * 0.649926f + green * 0.103455f + blue * 0.197109f; + float Y = red * 0.234327f + green * 0.743075f + blue * 0.022598f; + float Z = red * 0.0000000f + green * 0.053077f + blue * 1.035763f; + // Convert to x,y space. + x = X / (X + Y + Z); + y = Y / (X + Y + Z); + if (isnan(x)) { + x = 0.0f; + } + if (isnan(y)) { + y = 0.0f; + } + // Brightness is simply Y in the XYZ space. + brightness = Y; +} diff --git a/libsrc/leddevice/LedDevicePhilipsHue.h b/libsrc/leddevice/LedDevicePhilipsHue.h new file mode 100755 index 00000000..e5710f40 --- /dev/null +++ b/libsrc/leddevice/LedDevicePhilipsHue.h @@ -0,0 +1,90 @@ +#pragma once + +// STL includes +#include + +// Qt includes +#include +#include + +// Leddevice includes +#include + +/** + * Implementation for the Philips Hue system. + * + * To use set the device to "philipshue". + * Uses the official Philips Hue API (http://developers.meethue.com). + * Framegrabber should be limited to 30 Hz / numer of lights to avoid rate limitation by the hue bridge. + * Create a new API user name "newdeveloper" on the bridge (http://developers.meethue.com/gettingstarted.html) + */ +class LedDevicePhilipsHue : public LedDevice +{ +public: + /// + /// Constructs the device. + /// + /// @param output the ip address of the bridge + /// + LedDevicePhilipsHue(const std::string& output); + + /// + /// Destructor of this device + /// + virtual ~LedDevicePhilipsHue(); + + /// + /// Sends the given led-color values via put request to the hue system + /// + /// @param ledValues The color-value per led + /// + /// @return Zero on success else negative + /// + virtual int write(const std::vector & ledValues); + + /// Switch the leds off + virtual int switchOff(); + +private: + /// Ip address of the bridge + QString host; + /// User name for the API ("newdeveloper") + QString username; + QHttp* http; + + /// + /// Sends a HTTP PUT request + /// + /// @param route the URI of the request + /// + /// @param content content of the request + /// + void put(QString route, QString content); + + /// + /// @param lightId the id of the hue light (starting from 1) + /// + /// @return the URI of the light + /// + QString getRoute(unsigned int lightId); + + /// + /// Converts an RGB color to the Hue xy color space and brightness + /// https://github.com/PhilipsHue/PhilipsHueSDK-iOS-OSX/blob/master/ApplicationDesignNotes/RGB%20to%20xy%20Color%20conversion.md + /// + /// @param red the red component in [0, 1] + /// + /// @param green the green component in [0, 1] + /// + /// @param blue the blue component in [0, 1] + /// + /// @param x converted x component + /// + /// @param y converted y component + /// + /// @param brightness converted brightness component + /// + void rgbToXYBrightness(float red, float green, float blue, + float& x, float& y, float& brightness); + +};