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 &LType)
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