1 #include "stdafx.h"
2 #include "PhilipsHue.h"
3 #include "PhilipsHueSensors.h"
4 #include "../../main/Helper.h"
5 #include "../../main/Logger.h"
6 #include "../../main/localtime_r.h"
7 #include "../../main/RFXtrx.h"
8 #include "../../main/SQLHelper.h"
9 #include "../../main/mainworker.h"
10 #include "../../main/WebServer.h"
11 #include "../../webserver/cWebem.h"
12 #include "../../httpclient/HTTPClient.h"
13 #include "../../main/json_helper.h"
14 #include "../hardwaretypes.h"
15
16 #define round(a) ( int ) ( a + .5 )
17
18 #define HUE_DEFAULT_POLL_INTERVAL 10
19 #define HUE_NOT_ADD_GROUPS 0x01
20 #define HUE_NOT_ADD_SCENES 0x02
21
22 #define SensorTypeDaylight "Daylight"
23 #define SensorTypeZGPSwitch "ZGPSwitch"
24 #define SensorTypeZLLSwitch "ZLLSwitch"
25 #define SensorTypeZLLPresence "ZLLPresence"
26 #define SensorTypeZLLTemperature "ZLLTemperature"
27 #define SensorTypeZLLLightLevel "ZLLLightLevel"
28 #define SensorTypeGeofence "Geofence"
29
30 //#define DEBUG_PhilipsHue
31
32 #ifdef DEBUG_PhilipsHue
SaveString2Disk(std::string str,std::string filename)33 void SaveString2Disk(std::string str, std::string filename)
34 {
35 FILE *fOut = fopen(filename.c_str(), "wb+");
36 if (fOut)
37 {
38 fwrite(str.c_str(), 1, str.size(), fOut);
39 fclose(fOut);
40 }
41 }
ReadFile(std::string filename)42 std::string ReadFile(std::string filename)
43 {
44 std::ifstream file;
45 std::string sResult = "";
46 file.open(filename.c_str());
47 if (!file.is_open())
48 return "";
49 std::string sLine;
50 while (!file.eof())
51 {
52 getline(file, sLine);
53 sResult += sLine;
54 }
55 file.close();
56 return sResult;
57 }
58 #endif
59
CPhilipsHue(const int ID,const std::string & IPAddress,const unsigned short Port,const std::string & Username,const int PollInterval,const int Options)60 CPhilipsHue::CPhilipsHue(const int ID, const std::string &IPAddress, const unsigned short Port, const std::string &Username, const int PollInterval, const int Options) :
61 m_IPAddress(IPAddress),
62 m_UserName(Username)
63 {
64 m_HwdID=ID;
65 m_Port = Port;
66 m_poll_interval = PollInterval;
67 m_add_groups = (Options & HUE_NOT_ADD_GROUPS) != 0;
68 m_add_scenes = (Options & HUE_NOT_ADD_SCENES) != 0;
69
70 // Catch uninitialised Mode1 entry.
71 if (m_poll_interval < 1)
72 {
73 m_poll_interval = HUE_DEFAULT_POLL_INTERVAL;
74 _log.Log(LOG_STATUS, "Philips Hue: Using default poll interval of %d secs.", m_poll_interval);
75 }
76 else
77 {
78 _log.Log(LOG_STATUS, "Philips Hue: Using poll interval of %d secs.", m_poll_interval);
79 }
80
81 Init();
82 }
83
~CPhilipsHue(void)84 CPhilipsHue::~CPhilipsHue(void)
85 {
86 }
87
Init()88 void CPhilipsHue::Init()
89 {
90 }
91
StartHardware()92 bool CPhilipsHue::StartHardware()
93 {
94 RequestStart();
95
96 Init();
97 //Start worker thread
98 m_thread = std::make_shared<std::thread>(&CPhilipsHue::Do_Work, this);
99 SetThreadNameInt(m_thread->native_handle());
100 m_bIsStarted = true;
101 sOnConnected(this);
102 return (m_thread != nullptr);
103 }
104
StopHardware()105 bool CPhilipsHue::StopHardware()
106 {
107 if (m_thread)
108 {
109 RequestStop();
110 m_thread->join();
111 m_thread.reset();
112 }
113 m_bIsStarted=false;
114 return true;
115 }
116
117
Do_Work()118 void CPhilipsHue::Do_Work()
119 {
120 int msec_counter = 0;
121 int sec_counter = m_poll_interval - 1;
122
123 _log.Log(LOG_STATUS,"Philips Hue: Worker started...");
124
125 while (!IsStopRequested(500))
126 {
127 msec_counter++;
128 if (msec_counter == 2)
129 {
130 msec_counter = 0;
131 sec_counter++;
132 if (sec_counter % m_poll_interval == 0)
133 {
134 m_LastHeartbeat = mytime(NULL);
135 GetStates();
136 }
137 }
138 }
139 _log.Log(LOG_STATUS,"Philips Hue: Worker stopped...");
140 }
141
WriteToHardware(const char * pdata,const unsigned char)142 bool CPhilipsHue::WriteToHardware(const char *pdata, const unsigned char /*length*/)
143 {
144 const tRBUF *pSen = reinterpret_cast<const tRBUF*>(pdata);
145
146 unsigned char packettype = pSen->ICMND.packettype;
147
148 int svalue = 0;
149 int svalue2 = 0;
150 int svalue3 = 0;
151 std::string LCmd = "";
152 int nodeID = 0;
153
154 if (packettype == pTypeGeneralSwitch)
155 {
156 const _tGeneralSwitch *pSwitch = reinterpret_cast<const _tGeneralSwitch*>(pSen);
157 //light command
158 nodeID = static_cast<int>(pSwitch->id);
159 if ((pSwitch->cmnd == gswitch_sOff) || (pSwitch->cmnd == gswitch_sGroupOff))
160 {
161 LCmd = "Off";
162 svalue = 0;
163 }
164 else if ((pSwitch->cmnd == gswitch_sOn) || (pSwitch->cmnd == gswitch_sGroupOn))
165 {
166 LCmd = "On";
167 svalue = 254;
168 }
169 else if (pSwitch->cmnd == gswitch_sSetLevel)
170 {
171 // From Philips Hue API documentation:
172 // Brightness is a scale from 1 (the minimum the light is capable of) to 254 (the maximum). Note: a brightness of 1 is not off.
173 LCmd = "Set Level";
174 float fvalue = (254.0f / 100.0f)*float(pSwitch->level);
175 if (fvalue > 254.0f)
176 fvalue = 254.0f;
177 svalue = round(fvalue);
178 }
179 SwitchLight(nodeID, LCmd, svalue);
180 }
181 else if (packettype == pTypeColorSwitch)
182 {
183 const _tColorSwitch *pLed = reinterpret_cast<const _tColorSwitch*>(pSen);
184 nodeID = static_cast<int>(pLed->id);
185
186 if (pLed->command == Color_LedOff)
187 {
188 LCmd = "Off";
189 svalue = 0;
190 SwitchLight(nodeID, LCmd, svalue);
191 return true;
192 }
193 else if (pLed->command == Color_LedOn)
194 {
195 LCmd = "On";
196 svalue = 254;
197 SwitchLight(nodeID, LCmd, svalue);
198 return true;
199 }
200 else if (pLed->command == Color_SetBrightnessLevel)
201 {
202 if (pLed->value == 0)
203 {
204 //Off
205 LCmd = "Off";
206 svalue = 0;
207 SwitchLight(nodeID, LCmd, svalue);
208 }
209 else
210 {
211 // From Philips Hue API documentation:
212 // Brightness is a scale from 1 (the minimum the light is capable of) to 254 (the maximum). Note: a brightness of 1 is not off.
213 LCmd = "Set Level";
214 float fvalue = (254.0f / 100.0f)*float(pLed->value);
215 if (fvalue > 254.0f)
216 fvalue = 254.0f;
217 svalue = round(fvalue);
218 SwitchLight(nodeID, LCmd, svalue);
219 }
220 return true;
221 }
222 else if (pLed->command == Color_SetColorToWhite)
223 {
224 LCmd = "Set White";
225 SwitchLight(nodeID, LCmd, 0);
226 return true;
227 }
228 else if (pLed->command == Color_SetColor)
229 {
230 // From Philips Hue API documentation:
231 // bri: Brightness is a scale from 1 (the minimum the light is capable of) to 254 (the maximum). Note: a brightness of 1 is not off.
232 // hue: The hue value is a wrapping value between 0 and 65535. Both 0 and 65535 are red, 25500 is green and 46920 is blue.
233 // sat: Saturation of the light. 254 is the most saturated (colored) and 0 is the least saturated (white).
234 // ct: The Mired Color temperature of the light. 2012 connected lights are capable of 153 (6500K) to 500 (2000K).
235 if (pLed->value == 0)
236 {
237 //Off
238 LCmd = "Off";
239 svalue = 0;
240 SwitchLight(nodeID, LCmd, svalue);
241 }
242 else if (pLed->color.mode == ColorModeWhite)
243 {
244 LCmd = "Set Hue";
245 //TODO: Is this correct way to turn off RGB LED and turn on white LED?
246 svalue2 = 0; // Hue
247 svalue3 = 0; // sat
248 }
249 else if (pLed->color.mode == ColorModeTemp)
250 {
251 LCmd = "Set CT";
252 svalue2 = round(float(pLed->color.t)*(500.0f-153.0f)/255.0f+153.0f);
253 }
254 else if (pLed->color.mode == ColorModeRGB)
255 {
256 float hsb[3];
257 rgb2hsb(pLed->color.r, pLed->color.g, pLed->color.b, hsb);
258 float cHue = (65535.0f)*hsb[0]; // Scale hue from 0..1 to 0..65535
259 float cSat = (254.0f)*hsb[1]; // Scale saturation from 0..1 to 0..254
260 LCmd = "Set Hue";
261 svalue2 = round(cHue);
262 svalue3 = round(cSat);
263 }
264 else{
265 _log.Log(LOG_STATUS, "Philips Hue: SetRGBColour - Color mode %d is unhandled, if you have a suggestion for what it should do, please post on the Domoticz forum", pLed->color.mode);
266 }
267 float fvalue = (254.0f / 100.0f)*float(pLed->value);
268 if (fvalue > 254.0f)
269 fvalue = 254.0f;
270 svalue = round(fvalue);
271 SwitchLight(nodeID, LCmd, svalue, svalue2, svalue3);
272 return true;
273 }
274 }
275 return true;
276 }
277
SwitchLight(const int nodeID,const std::string & LCmd,const int svalue,const int svalue2,const int svalue3)278 bool CPhilipsHue::SwitchLight(const int nodeID, const std::string &LCmd, const int svalue, const int svalue2 /*= 0*/, const int svalue3 /*= 0*/)
279 {
280 std::vector<std::string> ExtraHeaders;
281 ExtraHeaders.push_back("Content-Type: application/json");
282 std::string sResult;
283 std::stringstream sPostData;
284 bool setOn = false;
285 bool On = true;
286 bool setLevel = false;
287 bool setHueSat = false;
288 bool setCt = false;
289 bool setMode = false;
290 _eHueColorMode mode;
291
292 if (LCmd=="On")
293 {
294 sPostData << "{\"on\": true }";
295 setOn = true;
296 }
297 else if (LCmd == "Off")
298 {
299 sPostData << "{\"on\": false }";
300 setOn = true;
301 On = false;
302 }
303 else if (LCmd == "Set Level")
304 {
305 sPostData << "{\"on\": true, \"bri\": " << svalue << " }";
306 setOn = true;
307 setLevel = true;
308 }
309 else if (LCmd == "Set White")
310 {
311 sPostData << "{\"on\": true, \"sat\": 0 , \"bri\": 255, \"hue\": 0 }";
312 // Do state update next time the light is polled
313 }
314 else if (LCmd == "Set Hue")
315 {
316 sPostData << "{\"on\": true, \"sat\": " << svalue3 << ", \"hue\": " << svalue2 << ", \"bri\": " << svalue << " }";
317 setOn = true;
318 setLevel = true;
319 setHueSat = true;
320 setMode = true;
321 mode = HLMODE_HS;
322 }
323 else if (LCmd == "Set CT")
324 {
325 sPostData << "{\"on\": true, \"ct\": " << svalue2 << ", \"bri\": " << svalue << " }";
326 setOn = true;
327 setLevel = true;
328 setCt = true;
329 setMode = true;
330 mode = HLMODE_CT;
331 }
332 else
333 {
334 _log.Log(LOG_ERROR, "Philips Hue: Invalid light command received!");
335 return false;
336 }
337
338 // Update cached state
339 _tHueLightState *pState = NULL;
340
341 if (nodeID < 1000)
342 {
343 //Light
344 auto && ittLight = m_lights.find(nodeID);
345 if (ittLight != m_lights.end())
346 {
347 pState = &ittLight->second;
348 }
349 }
350 else if (nodeID < 2000)
351 {
352 //Group
353 auto && ittGroup = m_groups.find(nodeID - 1000);
354 if (ittGroup != m_groups.end())
355 {
356 pState = &ittGroup->second.gstate;
357 }
358 }
359 if (pState)
360 {
361 if (setOn) pState->on = On;
362 if (setLevel) pState->level = int((100.0f / 254.0f)*float(svalue));
363 if (setHueSat) pState->hue = svalue2;
364 if (setHueSat) pState->sat = svalue3;
365 if (setCt) pState->ct = int((float(svalue2)-153.0)/(500.0-153.0));
366 if (setMode) pState->mode = mode;
367 }
368
369 std::stringstream sstr2;
370 if (nodeID < 1000)
371 {
372 //Light
373 sstr2 << "http://" << m_IPAddress
374 << ":" << m_Port
375 << "/api/" << m_UserName
376 << "/lights/" << nodeID << "/state";
377 }
378 else if (nodeID < 2000)
379 {
380 //Group
381 sstr2 << "http://" << m_IPAddress
382 << ":" << m_Port
383 << "/api/" << m_UserName
384 << "/groups/" << nodeID - 1000 << "/action";
385 }
386 else
387 {
388 //Scene
389 //Because Scenes does not have a unique numeric value (but a string as ID),
390 //lookup the Options, and activate this scene
391
392 std::vector<std::vector<std::string> > result;
393 result = m_sql.safe_query("SELECT MacAddress FROM WOLNodes WHERE (HardwareID==%d) AND (ID==%d)", m_HwdID, nodeID - 2000);
394 if (result.empty())
395 {
396 _log.Log(LOG_ERROR, "Philips Hue: Scene not found!");
397 return false;
398 }
399 sPostData.clear();
400 sPostData.str("");
401 sPostData << "{\"scene\": \"" << result[0][0] << "\"}";
402 sstr2 << "http://" << m_IPAddress
403 << ":" << m_Port
404 << "/api/" << m_UserName
405 << "/groups/0/action";
406 }
407 std::string sURL = sstr2.str();
408 if (!HTTPClient::PUT(sURL, sPostData.str(), ExtraHeaders, sResult))
409 {
410 _log.Log(LOG_ERROR, "Philips Hue: Error connecting to Hue bridge (Switch Light/Scene), (Check IPAddress/Username)");
411 return false;
412 }
413
414 Json::Value root;
415
416 bool ret = ParseJSon(sResult, root);
417 if (!ret)
418 {
419 _log.Log(LOG_ERROR, "Philips Hue: Invalid data received (Switch Light/Scene), or invalid IPAddress/Username!");
420 return false;
421 }
422
423 if (sResult.find("error") != std::string::npos)
424 {
425 //We had an error
426 _log.Log(LOG_ERROR, "Philips Hue: Error received: %s", root[0]["error"]["description"].asString().c_str());
427 return false;
428 }
429 return true;
430 }
431
RegisterUser(const std::string & IPAddress,const unsigned short Port,const std::string & username)432 std::string CPhilipsHue::RegisterUser(const std::string &IPAddress, const unsigned short Port, const std::string &username)
433 {
434 std::string retStr = "Error;Unknown";
435 std::vector<std::string> ExtraHeaders;
436 ExtraHeaders.push_back("Content-Type: application/json");
437 std::string sResult;
438 std::string sPostData;
439
440 //Providing own username is not allowed, so don't use it and only provide devicetype
441 sPostData = "{ \"devicetype\": \"domoticz\" }";
442
443 std::stringstream sstr2;
444 sstr2 << "http://" << IPAddress
445 << ":" << Port
446 << "/api";
447 std::string sURL = sstr2.str();
448
449 if (!HTTPClient::POST(sURL, sPostData, ExtraHeaders, sResult))
450 {
451 retStr = "Error;Error connecting to Hue bridge:";
452 return retStr;
453 }
454
455 Json::Value root;
456
457 bool ret = ParseJSon(sResult, root);
458 if (!ret)
459 {
460 retStr = "Error;Registration failed (Wrong IPAddress?)";
461 return retStr;
462 }
463
464 if (sResult.find("error") != std::string::npos)
465 {
466 retStr = "Error;" + root[0]["error"]["description"].asString();
467 return retStr;
468 }
469
470 std::string new_username = root[0]["success"]["username"].asString();
471 retStr = "OK;" + new_username;
472 return retStr;
473 }
474
InsertUpdateLamp(const int NodeID,const _eHueLightType LType,const _tHueLightState tstate,const std::string & Name,const std::string & Options,const std::string & modelid,const bool AddMissingDevice)475 void CPhilipsHue::InsertUpdateLamp(const int NodeID, const _eHueLightType LType, const _tHueLightState tstate, const std::string &Name, const std::string &Options, const std::string &modelid, const bool AddMissingDevice)
476 {
477 if (LType == HLTYPE_RGB_W || LType == HLTYPE_CW_WW || LType == HLTYPE_RGB_CW_WW)
478 {
479 char szID[10];
480 char szSValue[20];
481 if (NodeID==1)
482 sprintf(szID, "%d", NodeID);
483 else
484 sprintf(szID, "%08X", (unsigned int)NodeID);
485 sprintf(szSValue, "%d;%d", tstate.sat, tstate.hue);
486 unsigned char unitcode = 1;
487 int cmd = (tstate.on ? Color_LedOn : Color_LedOff);
488 _tColor color = NoColor;
489
490 unsigned sType;
491 switch (LType)
492 {
493 case HLTYPE_CW_WW:
494 sType = sTypeColor_CW_WW;
495 break;
496 case HLTYPE_RGB_CW_WW:
497 sType = sTypeColor_RGB_CW_WW;
498 break;
499 case HLTYPE_RGB_W:
500 default:
501 sType = sTypeColor_RGB_W;
502 break;
503 }
504
505 //Get current nValue if exist
506 std::vector<std::vector<std::string> > result;
507 result = m_sql.safe_query("SELECT nValue, LastLevel, Color, SubType, ID, Used, Name FROM DeviceStatus WHERE (HardwareID==%d) AND (Unit==%d) AND (Type==%d) AND (DeviceID=='%q')",
508 m_HwdID, int(unitcode), pTypeColorSwitch, szID);
509
510 if (result.empty() && !AddMissingDevice)
511 return;
512
513 if (!result.empty())
514 {
515 //Already in the system
516 //Update state
517 int nvalue = atoi(result[0][0].c_str());
518 unsigned sTypeOld = atoi(result[0][3].c_str());
519 std::string sID = result[0][4];
520 bool bUsed = std::stoi(result[0][5]) != 0;
521 std::string curName = result[0][6];
522 if (!bUsed)
523 {
524 if (curName != Name)
525 {
526 //Update device name
527 _log.Log(LOG_STATUS, "Philips Hue: Updating Name of light '%s' from %s to %s", szID, curName.c_str(), Name.c_str());
528 m_sql.UpdateDeviceName(sID, Name);
529 }
530 }
531 if (sTypeOld != sType)
532 {
533 _log.Log(LOG_STATUS, "Philips Hue: Updating SubType of light '%s' from %u to %u", szID, sTypeOld, sType);
534 m_sql.UpdateDeviceValue("SubType", (int)sType, sID);
535 }
536 }
537
538 if (tstate.on && (tstate.level != 100))
539 cmd = Color_SetBrightnessLevel;
540
541 if (tstate.on && (tstate.mode != HLMODE_NONE))
542 {
543 if (tstate.mode == HLMODE_HS)
544 {
545 int r, g, b;
546
547 //convert hue+sat to RGB
548 float iHue = float(tstate.hue)*360.0f/65535.0f;
549 float iSat = float(tstate.sat)/254.0f;
550 hsb2rgb(iHue, iSat, 1.0f, r, g, b, 255);
551
552 color = _tColor(r, g, b, 0, 0, ColorModeRGB);
553 }
554 if (tstate.mode == HLMODE_XY)
555 {
556 uint8_t r, g, b;
557
558 //convert xy to RGB
559 RgbFromXY(tstate.x, tstate.y, 1.0, modelid, r, g, b);
560
561 color = _tColor(r, g, b, 0, 0, ColorModeRGB);
562 }
563 if (tstate.mode == HLMODE_CT)
564 {
565 float iCt = (float(tstate.ct)-153.0f)/(500.0f-153.0f)*255.0f;
566 color = _tColor(round(iCt), ColorModeTemp);
567 }
568 cmd = Color_SetColor;
569 }
570
571 //Send as ColorSwitch
572 _tColorSwitch lcmd;
573 lcmd.id = NodeID;
574 lcmd.command = cmd;
575 lcmd.value = tstate.level;
576 lcmd.color = color;
577 lcmd.subtype = sType;
578 m_mainworker.PushAndWaitRxMessage(this, (const unsigned char *)&lcmd, Name.c_str(), 255);
579
580 if (result.empty())
581 {
582 //Set SwitchType to STYPE_Dimmer
583 m_sql.safe_query("UPDATE DeviceStatus SET SwitchType=%d WHERE(HardwareID == %d) AND (DeviceID == '%q')",
584 int(STYPE_Dimmer), m_HwdID, szID);
585 }
586 }
587 else if (LType == HLTYPE_SCENE)
588 {
589 char szID[10];
590 sprintf(szID, "%08X", (unsigned int)NodeID);
591 unsigned char unitcode = 1;
592 int cmd = (tstate.on ? Color_LedOn : Color_LedOff);
593
594 //Get current nValue if exist
595 std::vector<std::vector<std::string> > result;
596 result = m_sql.safe_query("SELECT nValue, ID, Used, Name FROM DeviceStatus WHERE (HardwareID==%d) AND (Unit==%d) AND (Type==%d) AND (SubType==%d) AND (DeviceID=='%q')",
597 m_HwdID, int(unitcode), pTypeColorSwitch, sTypeColor_RGB_W, szID);
598
599 if (result.empty() && !AddMissingDevice)
600 return;
601
602 if (!result.empty())
603 {
604 //Already in the system
605 int nvalue = atoi(result[0][0].c_str());
606 std::string sID = result[0][1];
607 bool bUsed = std::stoi(result[0][2]) != 0;
608 std::string curName = result[0][3];
609 if (!bUsed)
610 {
611 if (curName != Name)
612 {
613 //Update device name
614 _log.Log(LOG_STATUS, "Philips Hue: Updating Name of scene '%s' from %s to %s", szID, curName.c_str(), Name.c_str());
615 m_sql.UpdateDeviceName(sID, Name);
616 }
617 }
618
619 bool tIsOn = (nvalue != 0);
620 if (tstate.on == tIsOn) //Check if the scene was switched
621 return;
622 }
623 //Send as ColorSwitch
624 _tColorSwitch lcmd;
625 lcmd.id = NodeID;
626 lcmd.command = cmd;
627 lcmd.value = 0;
628 //lcmd.subtype = sType; // TODO: set type also for groups?
629 m_mainworker.PushAndWaitRxMessage(this, (const unsigned char *)&lcmd, Name.c_str(), 255);
630
631 if (result.empty())
632 {
633 //Set SwitchType to STYPE_PushOn
634 m_sql.safe_query("UPDATE DeviceStatus SET SwitchType=%d WHERE(HardwareID == %d) AND (DeviceID == '%q')",
635 int(STYPE_Dimmer), m_HwdID, szID);
636 }
637 }
638 else
639 {
640 //Send as GeneralSwitch
641 char szID[10];
642 sprintf(szID, "%08X", (unsigned int)NodeID);
643 unsigned char unitcode = 1;
644 int cmd = (tstate.on ? gswitch_sOn : gswitch_sOff);
645
646 //Check if we already exist
647 std::vector<std::vector<std::string> > result;
648 result = m_sql.safe_query("SELECT nValue, LastLevel, ID, Used, Name FROM DeviceStatus WHERE (HardwareID==%d) AND (Unit==%d) AND (Type==%d) AND (SubType==%d) AND (DeviceID=='%q')",
649 m_HwdID, int(unitcode), pTypeGeneralSwitch, sSwitchGeneralSwitch, szID);
650 //_log.Log(LOG_STATUS, "HueBridge state change for DeviceID '%s': Level = %d", szID, tstate.level);
651
652 if (result.empty() && !AddMissingDevice)
653 return;
654
655 if (!result.empty())
656 {
657 //Already in the system
658
659 std::string sID = result[0][2];
660 bool bUsed = std::stoi(result[0][3]) != 0;
661 std::string curName = result[0][4];
662
663 if (!bUsed)
664 {
665 if (curName != Name)
666 {
667 //Update device name
668 _log.Log(LOG_STATUS, "Philips Hue: Updating Name of light/switch '%s' from %s to %s", szID, curName.c_str(), Name.c_str());
669 m_sql.UpdateDeviceName(sID, Name);
670 }
671 }
672
673 //Update state
674 int nvalue = atoi(result[0][0].c_str());
675 }
676 else
677 {
678 _log.Log(LOG_STATUS, "Philips Hue: adding device '%s'", Name.c_str());
679 }
680
681 //Change command to SetLevel for dimmer type switch
682 if (LType == HLTYPE_DIM && tstate.on && (tstate.level != 100))
683 cmd = gswitch_sSetLevel;
684
685 _tGeneralSwitch lcmd;
686 lcmd.subtype = sSwitchGeneralSwitch;
687 lcmd.id = NodeID;
688 lcmd.unitcode = unitcode;
689 lcmd.cmnd = cmd;
690 lcmd.level = tstate.level;
691 lcmd.seqnbr = 1;
692 m_mainworker.PushAndWaitRxMessage(this, (const unsigned char *)&lcmd, Name.c_str(), 255);
693
694 if (result.empty())
695 {
696 //Set SwitchType
697 m_sql.safe_query("UPDATE DeviceStatus SET SwitchType=%d WHERE(HardwareID == %d) AND (DeviceID == '%q')",
698 int(LType == HLTYPE_DIM ? STYPE_Dimmer : STYPE_OnOff), m_HwdID, szID);
699 }
700 }
701 }
702
GetStates()703 bool CPhilipsHue::GetStates()
704 {
705 std::string sResult;
706
707 #ifdef DEBUG_PhilipsHue
708 sResult= ReadFile("E:\\philipshue.json");
709 #else
710 std::stringstream sstr2;
711 sstr2 << "http://" << m_IPAddress
712 << ":" << m_Port
713 << "/api/" << m_UserName;
714 //Get Data
715 std::string sURL = sstr2.str();
716 std::vector<std::string> ExtraHeaders;
717 if (!HTTPClient::GET(sURL, ExtraHeaders, sResult))
718 {
719 _log.Log(LOG_ERROR, "Philips Hue: Error getting Light States, (Check IPAddress/Username)");
720 return false;
721 }
722 #endif
723 #ifdef DEBUG_PhilipsHue2
724 SaveString2Disk(sResult, "E:\\philipshue.json");
725 #endif
726
727 Json::Value root;
728
729 bool ret = ParseJSon(sResult, root);
730 if ((!ret) || (!root.isObject()))
731 {
732 _log.Log(LOG_ERROR, "Philips Hue: Invalid data received, or invalid IPAddress/Username!");
733 return false;
734 }
735
736 if (sResult.find("\"error\":") != std::string::npos)
737 {
738 //We had an error
739 _log.Log(LOG_ERROR, "Philips Hue: Error received: %s", root[0]["error"]["description"].asString().c_str());
740 return false;
741 }
742
743
744 if (!GetLights(root))
745 {
746 //_log.Log(LOG_ERROR, "Philips Hue: No Lights found!");
747 return false;
748 }
749
750 GetGroups(root);
751 GetScenes(root);
752 GetSensors(root);
753
754 return true;
755 }
756
LightStateFromJSON(const Json::Value & lightstate,_tHueLightState & tlight,_eHueLightType & LType)757 void CPhilipsHue::LightStateFromJSON(const Json::Value &lightstate, _tHueLightState &tlight, _eHueLightType <ype)
758 {
759 if (lightstate.isObject())
760 {
761 tlight.level = 0; // Brightness of the light. This is a scale from the minimum brightness the light is capable of, 1,
762 // to the maximum capable brightness, 254.
763 tlight.sat = 0; // Saturation of the light. 254 is the most saturated (colored) and 0 is the least saturated (white).
764 tlight.hue = 0; // Hue of the light. This is a wrapping value between 0 and 65535.
765 tlight.ct = 0; // The Mired Color temperature of the light. 2012 connected lights are capable of 153 (6500K) to 500 (2000K).
766 tlight.mode = HLMODE_NONE; // Indicates the color mode in which the light is working, this is the last command type it received. Values
767 // are "hs" for Hue and Saturation, "xy" for XY and "ct" for Color Temperature.
768 tlight.x = 0.0; // The x and y coordinates of a color in CIE color space.
769 tlight.y = 0.0; // The first entry is the x coordinate and the second entry is the y coordinate. Both x and y must be between 0 and 1.
770 // If the specified coordinates are not in the CIE color space, the closest color to the coordinates will be chosen.
771 tlight.on = false;
772
773 bool hasBri = false;
774 bool hasHueSat = false;
775 bool hasTemp = false;
776
777 LType = HLTYPE_NORMAL;
778
779 if (!lightstate["on"].empty())
780 {
781 tlight.on = lightstate["on"].asBool();
782 }
783 if (!lightstate["colormode"].empty())
784 {
785 std::string sMode = lightstate["colormode"].asString();
786 if (sMode == "hs") tlight.mode = HLMODE_HS;
787 if (sMode == "xy") tlight.mode = HLMODE_XY;
788 if (sMode == "ct") tlight.mode = HLMODE_CT;
789 }
790 if (!lightstate["bri"].empty())
791 {
792 //Lamp with brightness control
793 hasBri = true;
794 int tbri = lightstate["bri"].asInt();
795 // Clamp to conform to HUE API
796 tbri = std::max(1, tbri);
797 tbri = std::min(254, tbri);
798 tlight.level = int(ceil((100.0f / 254.0f)*float(tbri)));
799 }
800 if (!lightstate["sat"].empty())
801 {
802 //Lamp with color control
803 hasHueSat = true;
804 tlight.sat = lightstate["sat"].asInt();
805 // Clamp to conform to HUE API
806 tlight.sat = std::max(0, tlight.sat);
807 tlight.sat = std::min(254, tlight.sat);
808 }
809 if (!lightstate["hue"].empty())
810 {
811 //Lamp with color control
812 hasHueSat = true;
813 tlight.hue = lightstate["hue"].asInt();
814 // Clamp to conform to HUE API
815 tlight.hue = std::max(0, tlight.hue);
816 tlight.hue = std::min(65535, tlight.hue);
817 }
818 if (!lightstate["ct"].empty())
819 {
820 //Lamp with color temperature control
821 hasTemp = true;
822 tlight.ct = lightstate["ct"].asInt();
823 // Clamp to conform to HUE API
824 tlight.ct = std::max(153, tlight.ct);
825 tlight.ct = std::min(500, tlight.ct);
826 }
827 if (!lightstate["xy"].empty())
828 {
829 //Lamp with color control
830 hasHueSat = true;
831 tlight.x = lightstate["xy"][0].asDouble();
832 tlight.y = lightstate["xy"][1].asDouble();
833 }
834
835 if (hasBri) LType = HLTYPE_DIM;
836 if (hasBri && hasHueSat && !hasTemp) LType = HLTYPE_RGB_W;
837 if (hasBri && !hasHueSat && hasTemp) LType = HLTYPE_CW_WW;
838 if (hasBri && hasHueSat && hasTemp) LType = HLTYPE_RGB_CW_WW;
839 }
840 }
841
GetLights(const Json::Value & root)842 bool CPhilipsHue::GetLights(const Json::Value &root)
843 {
844 if (root["lights"].empty())
845 return false;
846
847 for (auto iLight = root["lights"].begin(); iLight != root["lights"].end(); ++iLight)
848 {
849 Json::Value light = *iLight;
850 if (light.isObject())
851 {
852 int lID = atoi(iLight.key().asString().c_str());
853
854 _tHueLightState tlight;
855 _eHueLightType LType;
856 bool bDoSend = true;
857 LightStateFromJSON(light["state"], tlight, LType);
858
859 auto myLight = m_lights.find(lID);
860 if (myLight != m_lights.end())
861 {
862 if (StatesSimilar(myLight->second, tlight))
863 bDoSend = false;
864 }
865 if (bDoSend)
866 {
867 //_log.Log(LOG_STATUS, "HueBridge state change: tbri = %d, level = %d", tbri, tlight.level);
868 m_lights[lID] = tlight;
869 std::string modelid = light["modelid"].asString();
870 InsertUpdateLamp(lID, LType, tlight, light["name"].asString(), "", modelid, true);
871 }
872 }
873 }
874 return true;
875 }
876
GetGroups(const Json::Value & root)877 bool CPhilipsHue::GetGroups(const Json::Value &root)
878 {
879 //Groups (0=All)
880
881 if (root["groups"].empty())
882 return false;
883
884 for (auto iGroup = root["groups"].begin(); iGroup != root["groups"].end(); ++iGroup)
885 {
886 Json::Value group = *iGroup;
887 if (group.isObject())
888 {
889 int gID = atoi(iGroup.key().asString().c_str());
890
891 _tHueLightState tstate;
892 _eHueLightType LType;
893 bool bDoSend = true;
894 LightStateFromJSON(group["action"], tstate, LType); //TODO: Verify there is no crash with "bad" key
895
896 auto myGroup = m_groups.find(gID);
897 if (myGroup != m_groups.end())
898 {
899 if (StatesSimilar(myGroup->second.gstate, tstate))
900 bDoSend = false;
901 }
902 if (bDoSend)
903 {
904 m_groups[gID].gstate = tstate;
905 std::string Name = "Group " + group["name"].asString();
906 InsertUpdateLamp(1000 + gID, LType, tstate, Name, "", "", m_add_groups);
907 }
908 }
909 }
910 //Special Request for Group0 (All Lights)
911 std::stringstream sstr2;
912 sstr2 << "http://" << m_IPAddress
913 << ":" << m_Port
914 << "/api/" << m_UserName
915 << "/groups/0";
916 std::string sResult;
917 std::vector<std::string> ExtraHeaders;
918 if (!HTTPClient::GET(sstr2.str(), ExtraHeaders, sResult))
919 {
920 //No group all(0)
921 return true;
922 }
923 Json::Value root2;
924 bool ret = ParseJSon(sResult, root2);
925 if ((!ret) || (!root2.isObject()))
926 {
927 _log.Log(LOG_ERROR, "Philips Hue: Invalid data received, or invalid IPAddress/Username!");
928 return false;
929 }
930
931 if (sResult.find("\"error\":") != std::string::npos)
932 {
933 //We had an error
934 _log.Log(LOG_ERROR, "Philips Hue: Error received: %s", root2[0]["error"]["description"].asString().c_str());
935 return false;
936 }
937
938 if (sResult.find("lights") == std::string::npos)
939 {
940 return false;
941 }
942 _tHueLightState tstate;
943 tstate.on = false;
944 tstate.level = 0;
945 tstate.hue = 0;
946 tstate.sat = 0;
947 tstate.ct = 0;
948 tstate.x = 0.0;
949 tstate.y = 0.0;
950 tstate.mode = HLMODE_NONE;
951
952 _eHueLightType LType = HLTYPE_RGB_W;// HLTYPE_NORMAL;
953
954 if (!root2["action"]["on"].empty())
955 {
956 tstate.on = root2["action"]["on"].asBool();
957 if (tstate.on) tstate.level = 100; // Set default full brightness for non dimmable group
958 }
959 if (!root2["action"]["bri"].empty())
960 {
961 int tbri = root2["action"]["bri"].asInt();
962 if ((tbri != 0) && (tbri < 3))
963 tbri = 3;
964 tstate.level = int((100.0f / 254.0f)*float(tbri));
965 }
966 if (!root2["action"]["sat"].empty())
967 {
968 tstate.sat = root2["action"]["sat"].asInt();
969 //LType = HLTYPE_RGB_W;
970 }
971 if (!root2["action"]["hue"].empty())
972 {
973 tstate.hue = root2["action"]["hue"].asInt();
974 //LType = HLTYPE_RGB_W;
975 }
976
977 int gID = 0;
978 std::map<int, _tHueGroup>::iterator myGroup = m_groups.find(gID);
979 if (myGroup != m_groups.end())
980 {
981 if (!StatesSimilar(myGroup->second.gstate, tstate))
982 {
983 myGroup->second.gstate = tstate;
984 std::string Name = "Group All Lights";
985 InsertUpdateLamp(1000 + gID, LType, tstate, Name, "", "", m_add_groups);
986 }
987 }
988 return true;
989 }
990
GetScenes(const Json::Value & root)991 bool CPhilipsHue::GetScenes(const Json::Value &root)
992 {
993 if (root["scenes"].empty())
994 return false;
995
996 for (auto iScene = root["scenes"].begin(); iScene != root["scenes"].end(); ++iScene)
997 {
998 Json::Value scene = *iScene;
999 if (scene.isObject())
1000 {
1001 _tHueScene hscene;
1002 hscene.id = iScene.key().asString();
1003 hscene.name = scene["name"].asString();
1004 hscene.lastupdated = scene["lastupdated"].asString();
1005 if (hscene.lastupdated.empty())
1006 continue; //old scene/legacy scene
1007
1008 //Strip some info
1009 size_t tpos = hscene.name.find(" from ");
1010 if (tpos != std::string::npos)
1011 {
1012 hscene.name = hscene.name.substr(0, tpos);
1013 }
1014
1015 bool bDoSend = true;
1016 if (m_scenes.find(hscene.id) != m_scenes.end())
1017 {
1018 _tHueScene ascene = m_scenes[hscene.id];
1019 if (ascene.lastupdated == hscene.lastupdated)
1020 {
1021 bDoSend = false;
1022 }
1023 }
1024 m_scenes[hscene.id] = hscene;
1025
1026 if (bDoSend)
1027 {
1028 int sID = -1;
1029 std::vector<std::vector<std::string> > result;
1030 result = m_sql.safe_query("SELECT ID FROM WOLNodes WHERE (HardwareID==%d) AND (MacAddress=='%q')", m_HwdID, hscene.id.c_str());
1031 if (!result.empty())
1032 {
1033 //existing scene
1034 sID = atoi(result[0][0].c_str());
1035 }
1036 else
1037 {
1038 //New scene
1039 m_sql.safe_query("INSERT INTO WOLNodes (HardwareID, Name, MacAddress) VALUES (%d, '%q', '%q')", m_HwdID, hscene.name.c_str(), hscene.id.c_str());
1040 result = m_sql.safe_query("SELECT ID FROM WOLNodes WHERE (HardwareID==%d) AND (MacAddress=='%q')", m_HwdID, hscene.id.c_str());
1041 if (result.empty())
1042 {
1043 _log.Log(LOG_ERROR, "Philips Hue: Problem adding new Scene!!");
1044 return false;
1045 }
1046 sID = atoi(result[0][0].c_str());
1047 }
1048 std::string Name = "Scene " + hscene.name;
1049 _tHueLightState tstate;
1050 tstate.on = false;
1051 tstate.level = 100;
1052 tstate.hue = 0;
1053 tstate.sat = 0;
1054 tstate.ct = 0;
1055 tstate.x = 0.0;
1056 tstate.y = 0.0;
1057 tstate.mode = HLMODE_NONE;
1058 InsertUpdateLamp(2000 + sID, HLTYPE_SCENE, tstate, Name, hscene.id, "", m_add_scenes);
1059 }
1060 }
1061 }
1062 return true;
1063 }
1064
GetSensors(const Json::Value & root)1065 bool CPhilipsHue::GetSensors(const Json::Value &root)
1066 {
1067 if (root["sensors"].empty())
1068 return false;
1069
1070 for (auto iSensor = root["sensors"].begin(); iSensor != root["sensors"].end(); ++iSensor)
1071 {
1072 Json::Value sensor = *iSensor;
1073 if (sensor.isObject())
1074 {
1075 bool bNewSensor = false;
1076 int sID = atoi(iSensor.key().asString().c_str());
1077
1078 CPHSensor current_sensor(sensor);
1079 CPHSensor previous_sensor;
1080 // Check if sensor exists and whether last update is changed
1081 if (m_sensors.find(sID) != m_sensors.end())
1082 {
1083 previous_sensor = m_sensors[sID];
1084 if (previous_sensor.m_state == current_sensor.m_state)
1085 {
1086 //Nothing changed
1087 continue;
1088 }
1089 }
1090 else
1091 {
1092 // New sensor found, always update it's value
1093 previous_sensor = CPHSensor();
1094 bNewSensor = true;
1095 }
1096 m_sensors[sID] = current_sensor;
1097 uint8_t unitcode = 1;
1098
1099 sID += 3000;
1100 std::string device_name = current_sensor.m_type + " " + current_sensor.m_name;
1101 if (current_sensor.m_type == SensorTypeDaylight)
1102 {
1103 }
1104 else if (
1105 (current_sensor.m_type == SensorTypeZGPSwitch)
1106 || (current_sensor.m_type == SensorTypeZLLSwitch))
1107 {
1108 int32_t selectorLevel = current_sensor.m_state.GetSelectorLevel(previous_sensor.m_state);
1109 if (selectorLevel >= 0)
1110 {
1111 if (InsertUpdateSelectorSwitch(sID, unitcode, selectorLevel, device_name, current_sensor.m_config.m_battery))
1112 {
1113 //New switch. Set levels and options for selector
1114 SetSwitchOptions(sID, unitcode, current_sensor.m_state.GetButtonOptions());
1115 }
1116 }
1117 }
1118 else if (current_sensor.m_type == SensorTypeZLLPresence)
1119 {
1120 if ((previous_sensor.m_state.m_presence != current_sensor.m_state.m_presence)
1121 || (bNewSensor))
1122 {
1123 InsertUpdateSwitch(sID, unitcode, STYPE_Motion, current_sensor.m_state.m_presence, device_name, current_sensor.m_config.m_battery);
1124 }
1125 }
1126 else if (current_sensor.m_type == SensorTypeGeofence)
1127 {
1128 if ((previous_sensor.m_state.m_presence != current_sensor.m_state.m_presence)
1129 || (bNewSensor))
1130 {
1131 InsertUpdateSwitch(sID, unitcode, STYPE_Motion, current_sensor.m_state.m_presence, device_name, current_sensor.m_config.m_battery);
1132 }
1133 }
1134 else if (current_sensor.m_type == SensorTypeZLLTemperature)
1135 {
1136 if ((previous_sensor.m_state.m_temperature != current_sensor.m_state.m_temperature)
1137 || (bNewSensor))
1138 {
1139 SendTempSensor(sID, current_sensor.m_config.m_battery, float(current_sensor.m_state.m_temperature / 100.0f), device_name);
1140 }
1141 }
1142 else if (current_sensor.m_type == SensorTypeZLLLightLevel)
1143 {
1144 if ((previous_sensor.m_state.m_dark != current_sensor.m_state.m_dark)
1145 || (bNewSensor))
1146 {
1147 InsertUpdateSwitch(sID, unitcode, STYPE_Dusk, current_sensor.m_state.m_dark, device_name, current_sensor.m_config.m_battery);
1148 }
1149
1150 if ((previous_sensor.m_state.m_lightlevel != current_sensor.m_state.m_lightlevel)
1151 || (bNewSensor))
1152 {
1153 double lux = 0.00001;
1154 if (current_sensor.m_state.m_lightlevel != 0)
1155 {
1156 float convertedLightLevel = float((current_sensor.m_state.m_lightlevel - 1) / 10000.00f);
1157 lux = pow(10, convertedLightLevel);
1158 }
1159 SendLuxSensor(sID, 0, current_sensor.m_config.m_battery, (const float)lux, current_sensor.m_type + " Lux " + current_sensor.m_name);
1160 }
1161 }
1162 else
1163 {
1164 //_log.Log(LOG_STATUS, "Ignoring Philips Hue CLIP Sensors: (%s)", device_name.c_str());
1165 }
1166 }
1167 }
1168
1169 return true;
1170 }
1171
InsertUpdateSelectorSwitch(const int NodeID,const uint8_t Unitcode,const uint8_t selectorLevel,const std::string & Name,const uint8_t BatteryLevel)1172 bool CPhilipsHue::InsertUpdateSelectorSwitch(const int NodeID, const uint8_t Unitcode, const uint8_t selectorLevel, const std::string& Name, const uint8_t BatteryLevel)
1173 {
1174 _tGeneralSwitch xcmd;
1175 xcmd.len = sizeof(_tGeneralSwitch) - 1;
1176 xcmd.id = NodeID;
1177 xcmd.unitcode = 1;
1178 xcmd.type = pTypeGeneralSwitch;
1179 xcmd.subtype = sSwitchTypeSelector;
1180 xcmd.battery_level = BatteryLevel;
1181 xcmd.level = selectorLevel;
1182 xcmd.rssi = 12;
1183 xcmd.cmnd = gswitch_sSetLevel;
1184
1185 std::vector<std::vector<std::string> > result;
1186 result = m_sql.safe_query("SELECT nValue FROM DeviceStatus WHERE (HardwareID==%d) AND (DeviceID=='%08X') AND (Unit == '%d')", m_HwdID, NodeID, xcmd.unitcode);
1187 m_mainworker.PushAndWaitRxMessage(this, (const unsigned char*)&xcmd, Name.c_str(), BatteryLevel);
1188 if (result.empty())
1189 {
1190 //_log.Log(LOG_STATUS, "Philips Hue Switch: New Device Found (%s)", Name.c_str());
1191 m_sql.safe_query("UPDATE DeviceStatus SET Name='%q', SwitchType=%d, CustomImage=%i WHERE(HardwareID == %d) AND (DeviceID == '%08X') AND (Unit == '%d')", Name.c_str(), (STYPE_Selector), 0, m_HwdID, NodeID, xcmd.unitcode);
1192 return true;
1193 }
1194 return false;
1195 }
1196
InsertUpdateSwitch(const int NodeID,const uint8_t Unitcode,const _eSwitchType SType,const bool bIsOn,const std::string & Name,const uint8_t BatteryLevel)1197 void CPhilipsHue::InsertUpdateSwitch(const int NodeID, const uint8_t Unitcode, const _eSwitchType SType, const bool bIsOn, const std::string &Name, const uint8_t BatteryLevel)
1198 {
1199 _tGeneralSwitch xcmd;
1200 xcmd.len = sizeof(_tGeneralSwitch) - 1;
1201 xcmd.id = NodeID;
1202 xcmd.unitcode = Unitcode;
1203 xcmd.type = pTypeGeneralSwitch;
1204 xcmd.subtype = sSwitchGeneralSwitch;
1205 xcmd.battery_level = BatteryLevel;
1206 xcmd.rssi = 12;
1207
1208 if (bIsOn)
1209 xcmd.cmnd = gswitch_sOn;
1210 else
1211 xcmd.cmnd = gswitch_sOff;
1212
1213 std::vector<std::vector<std::string> > result;
1214 result = m_sql.safe_query("SELECT nValue FROM DeviceStatus WHERE (HardwareID==%d) AND (DeviceID=='%08X') AND (Unit == '%d')", m_HwdID, NodeID, xcmd.unitcode);
1215 m_mainworker.PushAndWaitRxMessage(this, (const unsigned char*)&xcmd, Name.c_str(), BatteryLevel);
1216 if (result.empty())
1217 {
1218 //_log.Log(LOG_STATUS, "Philips Hue Switch: New Device Found (%s)", Name.c_str());
1219 m_sql.safe_query("UPDATE DeviceStatus SET Name='%q', SwitchType=%d, CustomImage=%i WHERE(HardwareID == %d) AND (DeviceID == '%08X') AND (Unit == '%d')", Name.c_str(), (SType), 0, m_HwdID, NodeID, xcmd.unitcode);
1220 }
1221 }
1222
SetSwitchOptions(const int NodeID,const uint8_t Unitcode,const std::map<std::string,std::string> options)1223 void CPhilipsHue::SetSwitchOptions(const int NodeID, const uint8_t Unitcode, const std::map<std::string, std::string> options)
1224 {
1225 std::vector<std::vector<std::string> > result;
1226 result = m_sql.safe_query("SELECT ID FROM DeviceStatus WHERE (HardwareID==%d) AND (DeviceID=='%08X') AND (Unit == '%d')", m_HwdID, NodeID, Unitcode);
1227 if (!result.empty())
1228 {
1229 int sIdx = std::stoi(result[0][0]);
1230 m_sql.SetDeviceOptions(sIdx, options);
1231 }
1232 }
1233
1234
1235 //Webserver helpers
1236 namespace http {
1237 namespace server {
Cmd_PhilipsHueRegister(WebEmSession & session,const request & req,Json::Value & root)1238 void CWebServer::Cmd_PhilipsHueRegister(WebEmSession & session, const request& req, Json::Value &root)
1239 {
1240 if (session.rights != 2)
1241 {
1242 session.reply_status = reply::forbidden;
1243 return; //Only admin user allowed
1244 }
1245 root["title"] = "RegisterOnHue";
1246
1247 std::string sipaddress = request::findValue(&req, "ipaddress");
1248 std::string sport = request::findValue(&req, "port");
1249 std::string susername = request::findValue(&req, "username");
1250 if (
1251 (sipaddress == "") ||
1252 (sport == "")
1253 )
1254 return;
1255
1256 std::string sresult = CPhilipsHue::RegisterUser(sipaddress, (unsigned short)atoi(sport.c_str()), susername);
1257 std::vector<std::string> strarray;
1258 StringSplit(sresult, ";", strarray);
1259 if (strarray.size() != 2)
1260 return;
1261
1262 if (strarray[0] == "Error") {
1263 root["statustext"] = strarray[1];
1264 return;
1265 }
1266 root["status"] = "OK";
1267 root["username"] = strarray[1];
1268 }
Cmd_PhilipsHueGetGroups(WebEmSession & session,const request & req,Json::Value & root)1269 void CWebServer::Cmd_PhilipsHueGetGroups(WebEmSession & session, const request& req, Json::Value &root)
1270 {
1271
1272 }
Cmd_PhilipsHueAddGroup(WebEmSession & session,const request & req,Json::Value & root)1273 void CWebServer::Cmd_PhilipsHueAddGroup(WebEmSession & session, const request& req, Json::Value &root)
1274 {
1275
1276 }
Cmd_PhilipsHueDeleteGroup(WebEmSession & session,const request & req,Json::Value & root)1277 void CWebServer::Cmd_PhilipsHueDeleteGroup(WebEmSession & session, const request& req, Json::Value &root)
1278 {
1279
1280 }
Cmd_PhilipsHueGroupAddLight(WebEmSession & session,const request & req,Json::Value & root)1281 void CWebServer::Cmd_PhilipsHueGroupAddLight(WebEmSession & session, const request& req, Json::Value &root)
1282 {
1283
1284 }
Cmd_PhilipsHueGroupRemoveLight(WebEmSession & session,const request & req,Json::Value & root)1285 void CWebServer::Cmd_PhilipsHueGroupRemoveLight(WebEmSession & session, const request& req, Json::Value &root)
1286 {
1287
1288 }
1289 }
1290 }
1291