1
+ #include < iostream>
1
2
// Local-Hyperion includes
2
3
#include " LedDevicePhilipsHue.h"
3
4
10
11
#include < QHttpRequestHeader>
11
12
#include < QEventLoop>
12
13
13
- LedDevicePhilipsHue::LedDevicePhilipsHue (const std::string& output) :
14
- host(output.c_str()), username(" newdeveloper" ) {
14
+ LedDevicePhilipsHue::LedDevicePhilipsHue (const std::string & output) :
15
+ host(output.c_str()), username(" newdeveloper" ) {
15
16
http = new QHttp (host);
16
- timer.setInterval (3000 );
17
+ /* timer.setInterval(3000);
17
18
timer.setSingleShot(true);
18
- connect (&timer, SIGNAL (timeout ()), this , SLOT (restoreStates ()));
19
+ connect(&timer, SIGNAL(timeout()), this, SLOT(restoreStates()));*/
19
20
}
20
21
21
22
LedDevicePhilipsHue::~LedDevicePhilipsHue () {
22
23
delete http;
23
24
}
24
25
25
- int LedDevicePhilipsHue::write (const std::vector<ColorRgb> & ledValues) {
26
+ int LedDevicePhilipsHue::write (const std::vector<ColorRgb> &ledValues) {
26
27
// Save light states if not done before.
27
- if (!statesSaved ()) {
28
+ if (!statesSaved ())
28
29
saveStates (ledValues.size ());
29
- switchOn (ledValues.size ());
30
- }
31
30
// Iterate through colors and set light states.
32
- unsigned int lightId = 1 ;
33
- for (const ColorRgb& color : ledValues) {
34
- float x, y, b;
35
- // Scale colors from [0, 255] to [0, 1] and convert to xy space.
36
- rgbToXYBrightness (color.red / 255 .0f , color.green / 255 .0f , color.blue / 255 .0f , x, y, b);
37
- // Send adjust color command in JSON format.
38
- put (getStateRoute (lightId), QString (" {\" xy\" : [%1, %2]}" ).arg (x).arg (y));
39
- // Send brightness color command in JSON format.
40
- put (getStateRoute (lightId), QString (" {\" bri\" : %1}" ).arg (qRound (b * 255 .0f )));
41
- // Next light id.
31
+ unsigned int lightId = 0 ;
32
+ for (const ColorRgb &color : ledValues) {
42
33
lightId++;
34
+ // Send only request to the brigde if color changed (prevents DDOS --> 503)
35
+ if (!oldLedValues.empty ())
36
+ if (!hasColorChanged (lightId, &color))
37
+ continue ;
38
+
39
+ float r = color.red / 255 .0f ;
40
+ float g = color.green / 255 .0f ;
41
+ float b = color.blue / 255 .0f ;
42
+
43
+ // set color gamut triangle
44
+ if (std::find (hueBulbs.begin (), hueBulbs.end (), modelIds[(lightId - 1 )]) != hueBulbs.end ()) {
45
+ Red = {0 .675f , 0 .322f };
46
+ Green = {0 .4091f , 0 .518f };
47
+ Blue = {0 .167f , 0 .04f };
48
+ } else if (std::find (livingColors.begin (),
49
+ livingColors.end (), modelIds[(lightId - 1 )]) != livingColors.end ()) {
50
+ Red = {0 .703f , 0 .296f };
51
+ Green = {0 .214f , 0 .709f };
52
+ Blue = {0 .139f , 0 .081f };
53
+ } else {
54
+ Red = {1 .0f , 0 .0f };
55
+ Green = {0 .0f , 1 .0f };
56
+ Blue = {0 .0f , 0 .0f };
57
+ }
58
+ // if color equal black, switch off lamp ...
59
+ if (r == 0 .0f && g == 0 .0f && b == 0 .0f ) {
60
+ switchLampOff (lightId);
61
+ continue ;
62
+ }
63
+ // ... and if lamp off, switch on
64
+ if (!checkOnStatus (states[(lightId - 1 )]))
65
+ switchLampOn (lightId);
66
+
67
+ float bri;
68
+ CGPoint p = CGPointMake (0 , 0 );
69
+ // Scale colors from [0, 255] to [0, 1] and convert to xy space.
70
+ rgbToXYBrightness (r, g, b, &p, bri);
71
+ // Send adjust color and brightness command in JSON format.
72
+ put (getStateRoute (lightId),
73
+ QString (" {\" xy\" : [%1, %2], \" bri\" : %3}" ).arg (p.x ).arg (p.y ).arg (qRound (b * 255 .0f )));
43
74
}
44
- timer.start ();
75
+ oldLedValues = ledValues;
76
+ // timer.start();
45
77
return 0 ;
46
78
}
47
79
80
+ bool LedDevicePhilipsHue::hasColorChanged (unsigned int lightId, const ColorRgb *color) {
81
+ bool matchFound = true ;
82
+ const ColorRgb &tmpOldColor = oldLedValues[(lightId - 1 )];
83
+ if ((*color).red == tmpOldColor.red )
84
+ matchFound = false ;
85
+ if (!matchFound && (*color).green == tmpOldColor.green )
86
+ matchFound = false ;
87
+ else
88
+ matchFound = true ;
89
+ if (!matchFound && (*color).blue == tmpOldColor.blue )
90
+ matchFound = false ;
91
+ else
92
+ matchFound = true ;
93
+
94
+ return matchFound;
95
+ }
96
+
48
97
int LedDevicePhilipsHue::switchOff () {
49
- timer.stop ();
98
+ // timer.stop();
50
99
// If light states have been saved before, ...
51
100
if (statesSaved ()) {
52
101
// ... restore them.
@@ -55,6 +104,10 @@ int LedDevicePhilipsHue::switchOff() {
55
104
return 0 ;
56
105
}
57
106
107
+ bool LedDevicePhilipsHue::checkOnStatus (QString status) {
108
+ return status.contains (" \" on\" :true" );
109
+ }
110
+
58
111
void LedDevicePhilipsHue::put (QString route, QString content) {
59
112
QString url = QString (" /api/%1/%2" ).arg (username).arg (route);
60
113
QHttpRequestHeader header (" PUT" , url);
@@ -69,6 +122,7 @@ void LedDevicePhilipsHue::put(QString route, QString content) {
69
122
http->request (header, content.toAscii ());
70
123
// Go into the loop until the request is finished.
71
124
loop.exec ();
125
+ // std::cout << http->readAll().data() << std::endl;
72
126
}
73
127
74
128
QByteArray LedDevicePhilipsHue::get (QString route) {
@@ -96,6 +150,7 @@ QString LedDevicePhilipsHue::getRoute(unsigned int lightId) {
96
150
void LedDevicePhilipsHue::saveStates (unsigned int nLights) {
97
151
// Clear saved light states.
98
152
states.clear ();
153
+ modelIds.clear ();
99
154
// Use json parser to parse reponse.
100
155
Json::Reader reader;
101
156
Json::FastWriter writer;
@@ -117,14 +172,19 @@ void LedDevicePhilipsHue::saveStates(unsigned int nLights) {
117
172
state[" bri" ] = json[" state" ][" bri" ];
118
173
}
119
174
// Save state object.
175
+ modelIds.push_back (QString (writer.write (json[" modelid" ]).c_str ()).trimmed ().replace (" \" " , " " ));
120
176
states.push_back (QString (writer.write (state).c_str ()).trimmed ());
121
177
}
122
178
}
123
179
124
- void LedDevicePhilipsHue::switchOn (unsigned int nLights) {
125
- for (unsigned int i = 0 ; i < nLights; i++) {
126
- put (getStateRoute (i + 1 ), " {\" on\" : true}" );
127
- }
180
+ void LedDevicePhilipsHue::switchLampOn (unsigned int lightId) {
181
+ put (getStateRoute (lightId), " {\" on\" : true}" );
182
+ states[(lightId - 1 )].replace (" \" on\" :false" , " \" on\" :true" );
183
+ }
184
+
185
+ void LedDevicePhilipsHue::switchLampOff (unsigned int lightId) {
186
+ put (getStateRoute (lightId), " {\" on\" : false}" );
187
+ states[(lightId - 1 )].replace (" \" on\" :true" , " \" on\" :false" );
128
188
}
129
189
130
190
void LedDevicePhilipsHue::restoreStates () {
@@ -135,30 +195,119 @@ void LedDevicePhilipsHue::restoreStates() {
135
195
}
136
196
// Clear saved light states.
137
197
states.clear ();
198
+ modelIds.clear ();
199
+ oldLedValues.clear ();
138
200
}
139
201
140
202
bool LedDevicePhilipsHue::statesSaved () {
141
203
return !states.empty ();
142
204
}
143
205
144
- void LedDevicePhilipsHue::rgbToXYBrightness (float red, float green, float blue, float & x, float & y, float & brightness) {
145
- // Apply gamma correction.
146
- red = (red > 0 .04045f ) ? qPow ((red + 0 .055f ) / (1 .0f + 0 .055f ), 2 .4f ) : (red / 12 .92f );
147
- green = (green > 0 .04045f ) ? qPow ((green + 0 .055f ) / (1 .0f + 0 .055f ), 2 .4f ) : (green / 12 .92f );
148
- blue = (blue > 0 .04045f ) ? qPow ((blue + 0 .055f ) / (1 .0f + 0 .055f ), 2 .4f ) : (blue / 12 .92f );
149
- // Convert to XYZ space.
150
- float X = red * 0 .649926f + green * 0 .103455f + blue * 0 .197109f ;
151
- float Y = red * 0 .234327f + green * 0 .743075f + blue * 0 .022598f ;
152
- float Z = red * 0 .0000000f + green * 0 .053077f + blue * 1 .035763f ;
153
- // Convert to x,y space.
154
- x = X / (X + Y + Z);
155
- y = Y / (X + Y + Z);
156
- if (isnan (x)) {
157
- x = 0 .0f ;
158
- }
159
- if (isnan (y)) {
160
- y = 0 .0f ;
206
+ CGPoint LedDevicePhilipsHue::CGPointMake (float x, float y) {
207
+ CGPoint p;
208
+ p.x = x;
209
+ p.y = y;
210
+
211
+ return p;
212
+ }
213
+
214
+ float LedDevicePhilipsHue::CrossProduct (CGPoint p1, CGPoint p2) {
215
+ return (p1.x * p2.y - p1.y * p2.x );
216
+ }
217
+
218
+ bool LedDevicePhilipsHue::CheckPointInLampsReach (CGPoint p) {
219
+ CGPoint v1 = CGPointMake (Green.x - Red.x , Green.y - Red.y );
220
+ CGPoint v2 = CGPointMake (Blue.x - Red.x , Blue.y - Red.y );
221
+
222
+ CGPoint q = CGPointMake (p.x - Red.x , p.y - Red.y );
223
+
224
+ float s = CrossProduct (q, v2) / CrossProduct (v1, v2);
225
+ float t = CrossProduct (v1, q) / CrossProduct (v1, v2);
226
+ if ((s >= 0 .0f ) && (t >= 0 .0f ) && (s + t <= 1 .0f ))
227
+ return true ;
228
+ else
229
+ return false ;
230
+ }
231
+
232
+ CGPoint LedDevicePhilipsHue::GetClosestPointToPoint (CGPoint A, CGPoint B, CGPoint P) {
233
+ CGPoint AP = CGPointMake (P.x - A.x , P.y - A.y );
234
+ CGPoint AB = CGPointMake (B.x - A.x , B.y - A.y );
235
+ float ab2 = AB.x * AB.x + AB.y * AB.y ;
236
+ float ap_ab = AP.x * AB.x + AP.y * AB.y ;
237
+
238
+ float t = ap_ab / ab2;
239
+
240
+ if (t < 0 .0f )
241
+ t = 0 .0f ;
242
+ else if (t > 1 .0f )
243
+ t = 1 .0f ;
244
+
245
+ return CGPointMake (A.x + AB.x * t, A.y + AB.y * t);
246
+ }
247
+
248
+ float LedDevicePhilipsHue::GetDistanceBetweenTwoPoints (CGPoint one, CGPoint two) {
249
+ float dx = one.x - two.x ; // horizontal difference
250
+ float dy = one.y - two.y ; // vertical difference
251
+ float dist = sqrt (dx * dx + dy * dy);
252
+
253
+ return dist;
254
+ }
255
+
256
+ void LedDevicePhilipsHue::rgbToXYBrightness (float red, float green, float blue, CGPoint *xyPoint, float &brightness) {
257
+ // Apply gamma correction.
258
+ float r = (red > 0 .04045f ) ? powf ((red + 0 .055f ) / (1 .0f + 0 .055f ), 2 .4f ) : (red / 12 .92f );
259
+ float g = (green > 0 .04045f ) ? powf ((green + 0 .055f ) / (1 .0f + 0 .055f ), 2 .4f ) : (green / 12 .92f );
260
+ float b = (blue > 0 .04045f ) ? powf ((blue + 0 .055f ) / (1 .0f + 0 .055f ), 2 .4f ) : (blue / 12 .92f );
261
+ // Convert to XYZ space.
262
+ float X = r * 0 .649926f + g * 0 .103455f + b * 0 .197109f ;
263
+ float Y = r * 0 .234327f + g * 0 .743075f + b * 0 .022598f ;
264
+ float Z = r * 0 .0000000f + g * 0 .053077f + b * 1 .035763f ;
265
+ // Convert to x,y space.
266
+ float cx = X / (X + Y + Z + 0 .0000001f );
267
+ float cy = Y / (X + Y + Z + 0 .0000001f );
268
+
269
+ if (isnan (cx))
270
+ cx = 0 .0f ;
271
+ if (isnan (cy))
272
+ cy = 0 .0f ;
273
+
274
+ (*xyPoint).x = cx;
275
+ (*xyPoint).y = cy;
276
+
277
+ // Check if the given XY value is within the colourreach of our lamps.
278
+ bool inReachOfLamps = CheckPointInLampsReach (*xyPoint);
279
+
280
+ if (!inReachOfLamps) {
281
+ // It seems the colour is out of reach
282
+ // let's find the closes colour we can produce with our lamp and send this XY value out.
283
+
284
+ // Find the closest point on each line in the triangle.
285
+ CGPoint pAB = GetClosestPointToPoint (Red, Green, *xyPoint);
286
+ CGPoint pAC = GetClosestPointToPoint (Blue, Red, *xyPoint);
287
+ CGPoint pBC = GetClosestPointToPoint (Green, Blue, *xyPoint);
288
+
289
+ // Get the distances per point and see which point is closer to our Point.
290
+ float dAB = GetDistanceBetweenTwoPoints (*xyPoint, pAB);
291
+ float dAC = GetDistanceBetweenTwoPoints (*xyPoint, pAC);
292
+ float dBC = GetDistanceBetweenTwoPoints (*xyPoint, pBC);
293
+
294
+ float lowest = dAB;
295
+ CGPoint closestPoint = pAB;
296
+
297
+ if (dAC < lowest) {
298
+ lowest = dAC;
299
+ closestPoint = pAC;
300
+ }
301
+ if (dBC < lowest) {
302
+ lowest = dBC;
303
+ closestPoint = pBC;
304
+ }
305
+
306
+ // Change the xy value to a value which is within the reach of the lamp.
307
+ (*xyPoint).x = closestPoint.x ;
308
+ (*xyPoint).y = closestPoint.y ;
161
309
}
310
+
162
311
// Brightness is simply Y in the XYZ space.
163
312
brightness = Y;
164
313
}
0 commit comments