Skip to content
This repository was archived by the owner on May 6, 2021. It is now read-only.

Commit 28352b1

Browse files
committed
Merge pull request #95 from ntim/support_for_philips_hue
Support for the Philips Hue system.
2 parents 515208c + 1ee26e8 commit 28352b1

File tree

4 files changed

+269
-0
lines changed

4 files changed

+269
-0
lines changed

libsrc/leddevice/CMakeLists.txt

100644100755
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ SET(Leddevice_HEADERS
2929
${CURRENT_SOURCE_DIR}/LedDeviceSedu.h
3030
${CURRENT_SOURCE_DIR}/LedDeviceTest.h
3131
${CURRENT_SOURCE_DIR}/LedDeviceHyperionUsbasp.h
32+
${CURRENT_SOURCE_DIR}/LedDevicePhilipsHue.h
3233
)
3334

3435
SET(Leddevice_SOURCES
@@ -44,6 +45,7 @@ SET(Leddevice_SOURCES
4445
${CURRENT_SOURCE_DIR}/LedDeviceSedu.cpp
4546
${CURRENT_SOURCE_DIR}/LedDeviceTest.cpp
4647
${CURRENT_SOURCE_DIR}/LedDeviceHyperionUsbasp.cpp
48+
${CURRENT_SOURCE_DIR}/LedDevicePhilipsHue.cpp
4749
)
4850

4951
if(ENABLE_SPIDEV)

libsrc/leddevice/LedDeviceFactory.cpp

100644100755
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include "LedDeviceSedu.h"
2929
#include "LedDeviceTest.h"
3030
#include "LedDeviceHyperionUsbasp.h"
31+
#include "LedDevicePhilipsHue.h"
3132

3233
LedDevice * LedDeviceFactory::construct(const Json::Value & deviceConfig)
3334
{
@@ -159,6 +160,11 @@ LedDevice * LedDeviceFactory::construct(const Json::Value & deviceConfig)
159160
deviceHyperionUsbasp->open();
160161
device = deviceHyperionUsbasp;
161162
}
163+
else if (type == "philipshue")
164+
{
165+
const std::string output = deviceConfig["output"].asString();
166+
device = new LedDevicePhilipsHue(output);
167+
}
162168
else if (type == "test")
163169
{
164170
const std::string output = deviceConfig["output"].asString();
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
// Local-Hyperion includes
2+
#include "LedDevicePhilipsHue.h"
3+
4+
// jsoncpp includes
5+
#include <json/json.h>
6+
7+
// qt includes
8+
#include <QtCore/qmath.h>
9+
#include <QUrl>
10+
#include <QHttpRequestHeader>
11+
#include <QEventLoop>
12+
13+
LedDevicePhilipsHue::LedDevicePhilipsHue(const std::string& output) :
14+
host(output.c_str()), username("newdeveloper") {
15+
http = new QHttp(host);
16+
}
17+
18+
LedDevicePhilipsHue::~LedDevicePhilipsHue() {
19+
delete http;
20+
}
21+
22+
int LedDevicePhilipsHue::write(const std::vector<ColorRgb> & ledValues) {
23+
// Save light states if not done before.
24+
if (!statesSaved()) {
25+
saveStates(ledValues.size());
26+
}
27+
// Iterate through colors and set light states.
28+
unsigned int lightId = 1;
29+
for (const ColorRgb& color : ledValues) {
30+
float x, y, b;
31+
// Scale colors from [0, 255] to [0, 1] and convert to xy space.
32+
rgbToXYBrightness(color.red / 255.0f, color.green / 255.0f, color.blue / 255.0f, x, y, b);
33+
// Send adjust color command in JSON format.
34+
put(getStateRoute(lightId), QString("{\"xy\": [%1, %2]}").arg(x).arg(y));
35+
// Send brightness color command in JSON format.
36+
put(getStateRoute(lightId), QString("{\"bri\": %1}").arg(qRound(b * 255.0f)));
37+
// Next light id.
38+
lightId++;
39+
}
40+
return 0;
41+
}
42+
43+
int LedDevicePhilipsHue::switchOff() {
44+
// If light states have been saved before, ...
45+
if (statesSaved()) {
46+
// ... restore them.
47+
restoreStates();
48+
}
49+
return 0;
50+
}
51+
52+
void LedDevicePhilipsHue::put(QString route, QString content) {
53+
QString url = QString("/api/%1/%2").arg(username).arg(route);
54+
QHttpRequestHeader header("PUT", url);
55+
header.setValue("Host", host);
56+
header.setValue("Accept-Encoding", "identity");
57+
header.setValue("Content-Length", QString("%1").arg(content.size()));
58+
http->setHost(host);
59+
http->request(header, content.toAscii());
60+
}
61+
62+
QByteArray LedDevicePhilipsHue::get(QString route) {
63+
QString url = QString("/api/%1/%2").arg(username).arg(route);
64+
// Event loop to block until request finished.
65+
QEventLoop loop;
66+
// Connect requestFinished signal to quit slot of the loop.
67+
loop.connect(http, SIGNAL(requestFinished(int, bool)), SLOT(quit()));
68+
// Perfrom request
69+
http->get(url);
70+
// Go into the loop until the request is finished.
71+
loop.exec();
72+
// Read all data of the response.
73+
return http->readAll();
74+
}
75+
76+
QString LedDevicePhilipsHue::getStateRoute(unsigned int lightId) {
77+
return QString("lights/%1/state").arg(lightId);
78+
}
79+
80+
QString LedDevicePhilipsHue::getRoute(unsigned int lightId) {
81+
return QString("lights/%1").arg(lightId);
82+
}
83+
84+
void LedDevicePhilipsHue::saveStates(unsigned int nLights) {
85+
// Clear saved light states.
86+
states.clear();
87+
// Use json parser to parse reponse.
88+
Json::Reader reader;
89+
Json::FastWriter writer;
90+
// Iterate lights.
91+
for (unsigned int i = 0; i < nLights; i++) {
92+
// Read the response.
93+
QByteArray response = get(getRoute(i + 1));
94+
// Parse JSON.
95+
Json::Value state;
96+
if (!reader.parse(QString(response).toStdString(), state)) {
97+
// Error occured, break loop.
98+
break;
99+
}
100+
// Save state object.
101+
states.push_back(QString(writer.write(state["state"]).c_str()));
102+
}
103+
}
104+
105+
void LedDevicePhilipsHue::restoreStates() {
106+
unsigned int lightId = 1;
107+
for (QString state : states) {
108+
put(getStateRoute(lightId), state);
109+
lightId++;
110+
}
111+
// Clear saved light states.
112+
states.clear();
113+
}
114+
115+
bool LedDevicePhilipsHue::statesSaved() {
116+
return !states.empty();
117+
}
118+
119+
void LedDevicePhilipsHue::rgbToXYBrightness(float red, float green, float blue, float& x, float& y, float& brightness) {
120+
// Apply gamma correction.
121+
red = (red > 0.04045f) ? qPow((red + 0.055f) / (1.0f + 0.055f), 2.4f) : (red / 12.92f);
122+
green = (green > 0.04045f) ? qPow((green + 0.055f) / (1.0f + 0.055f), 2.4f) : (green / 12.92f);
123+
blue = (blue > 0.04045f) ? qPow((blue + 0.055f) / (1.0f + 0.055f), 2.4f) : (blue / 12.92f);
124+
// Convert to XYZ space.
125+
float X = red * 0.649926f + green * 0.103455f + blue * 0.197109f;
126+
float Y = red * 0.234327f + green * 0.743075f + blue * 0.022598f;
127+
float Z = red * 0.0000000f + green * 0.053077f + blue * 1.035763f;
128+
// Convert to x,y space.
129+
x = X / (X + Y + Z);
130+
y = Y / (X + Y + Z);
131+
if (isnan(x)) {
132+
x = 0.0f;
133+
}
134+
if (isnan(y)) {
135+
y = 0.0f;
136+
}
137+
// Brightness is simply Y in the XYZ space.
138+
brightness = Y;
139+
}
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
#pragma once
2+
3+
// STL includes
4+
#include <string>
5+
6+
// Qt includes
7+
#include <QString>
8+
#include <QHttp>
9+
10+
// Leddevice includes
11+
#include <leddevice/LedDevice.h>
12+
13+
/**
14+
* Implementation for the Philips Hue system.
15+
*
16+
* To use set the device to "philipshue".
17+
* Uses the official Philips Hue API (http://developers.meethue.com).
18+
* Framegrabber must be limited to 10 Hz / numer of lights to avoid rate limitation by the hue bridge.
19+
* Create a new API user name "newdeveloper" on the bridge (http://developers.meethue.com/gettingstarted.html)
20+
*/
21+
class LedDevicePhilipsHue: public LedDevice {
22+
public:
23+
///
24+
/// Constructs the device.
25+
///
26+
/// @param output the ip address of the bridge
27+
///
28+
LedDevicePhilipsHue(const std::string& output);
29+
30+
///
31+
/// Destructor of this device
32+
///
33+
virtual ~LedDevicePhilipsHue();
34+
35+
///
36+
/// Sends the given led-color values via put request to the hue system
37+
///
38+
/// @param ledValues The color-value per led
39+
///
40+
/// @return Zero on success else negative
41+
///
42+
virtual int write(const std::vector<ColorRgb> & ledValues);
43+
44+
/// Switch the leds off
45+
virtual int switchOff();
46+
47+
private:
48+
/// Array to save the light states.
49+
std::vector<QString> states;
50+
/// Ip address of the bridge
51+
QString host;
52+
/// User name for the API ("newdeveloper")
53+
QString username;
54+
/// Qhttp object for sending requests.
55+
QHttp* http;
56+
57+
///
58+
/// Sends a HTTP GET request (blocking).
59+
///
60+
/// @param route the URI of the request
61+
///
62+
/// @return response of the request
63+
///
64+
QByteArray get(QString route);
65+
66+
///
67+
/// Sends a HTTP PUT request (non-blocking).
68+
///
69+
/// @param route the URI of the request
70+
///
71+
/// @param content content of the request
72+
///
73+
void put(QString route, QString content);
74+
75+
///
76+
/// @param lightId the id of the hue light (starting from 1)
77+
///
78+
/// @return the URI of the light state for PUT requests.
79+
///
80+
QString getStateRoute(unsigned int lightId);
81+
82+
///
83+
/// @param lightId the id of the hue light (starting from 1)
84+
///
85+
/// @return the URI of the light for GET requests.
86+
///
87+
QString getRoute(unsigned int lightId);
88+
89+
///
90+
/// Queries the status of all lights and saves it.
91+
///
92+
/// @param nLights the number of lights
93+
///
94+
void saveStates(unsigned int nLights);
95+
96+
/// Restores the status of all lights.
97+
void restoreStates();
98+
99+
///
100+
/// @return true if light states have been saved.
101+
///
102+
bool statesSaved();
103+
104+
///
105+
/// Converts an RGB color to the Hue xy color space and brightness
106+
/// https://github.com/PhilipsHue/PhilipsHueSDK-iOS-OSX/blob/master/ApplicationDesignNotes/RGB%20to%20xy%20Color%20conversion.md
107+
///
108+
/// @param red the red component in [0, 1]
109+
///
110+
/// @param green the green component in [0, 1]
111+
///
112+
/// @param blue the blue component in [0, 1]
113+
///
114+
/// @param x converted x component
115+
///
116+
/// @param y converted y component
117+
///
118+
/// @param brightness converted brightness component
119+
///
120+
void rgbToXYBrightness(float red, float green, float blue, float& x, float& y, float& brightness);
121+
122+
};

0 commit comments

Comments
 (0)