1 #include "stdafx.h"
2 #include "BleBox.h"
3 #include "hardwaretypes.h"
4 #include "../main/json_helper.h"
5 #include "../main/Helper.h"
6 #include "../main/HTMLSanitizer.h"
7 #include "../main/localtime_r.h"
8 #include "../main/Logger.h"
9 #include "../main/mainworker.h"
10 #include "../main/SQLHelper.h"
11 #include "../main/WebServer.h"
12 #include "../httpclient/HTTPClient.h"
13 
14 struct STR_DEVICE {
15 	int			unit;
16 	std::string api_name;
17 	std::string name;
18 	int			deviceID;
19 	uint8_t		subType;
20 	int			switchType;
21 	std::string api_state;
22 };
23 
24 #define TOT_DEVICE_TYPES 9
25 
26 const STR_DEVICE DevicesType[TOT_DEVICE_TYPES] =
27 {
28 	{ 0, "switchBox", "Switch Box",pTypeLighting2, sTypeAC, STYPE_OnOff, "relay" },
29 	{ 1, "shutterBox", "Shutter Box", pTypeLighting2, sTypeAC, STYPE_BlindsPercentageInverted, "shutter" },
30 	{ 2, "wLightBoxS", "Light Box S", pTypeLighting2, sTypeAC, STYPE_Dimmer, "light" },
31 	{ 3, "wLightBox", "Light Box", pTypeColorSwitch, sTypeColor_RGB_W, STYPE_Dimmer, "rgbw" },
32 	{ 4, "gateBox", "Gate Box", pTypeGeneral, sTypePercentage, 0, "gate" },
33 	{ 5, "dimmerBox", "Dimmer Box", pTypeLighting2, sTypeAC, STYPE_Dimmer, "dimmer" },
34 	{ 6, "switchBoxD", "Switch Box D", pTypeLighting2, sTypeAC, STYPE_OnOff, "relay" },
35 	{ 7, "airSensor", "Air Sensor", pTypeAirQuality, sTypeVoltcraft, 0, "air" },
36 	{ 8, "tempSensor", "Temp Sensor", pTypeGeneral, sTypeTemperature, 0, "tempsensor" },
37 };
38 
BleBox(const int id,const int pollIntervalsec)39 BleBox::BleBox(const int id, const int pollIntervalsec)
40 {
41 	m_HwdID = id;
42 	m_RGBWisWhiteState = true;
43 	m_RGBWbrightnessState = 255;
44 	SetSettings(pollIntervalsec);
45 }
46 
~BleBox()47 BleBox::~BleBox()
48 {
49 }
50 
StartHardware()51 bool BleBox::StartHardware()
52 {
53 	RequestStart();
54 
55 	LoadNodes();
56 
57 	m_thread = std::make_shared<std::thread>(&BleBox::Do_Work, this);
58 	SetThreadNameInt(m_thread->native_handle());
59 	m_bIsStarted = true;
60 	sOnConnected(this);
61 	return (m_thread != nullptr);
62 }
63 
StopHardware()64 bool BleBox::StopHardware()
65 {
66 	if (m_thread)
67 	{
68 		RequestStop();
69 		m_thread->join();
70 		m_thread.reset();
71 	}
72 
73 	m_bIsStarted = false;
74 	return true;
75 }
76 
Do_Work()77 void BleBox::Do_Work()
78 {
79 	int sec_counter = m_PollInterval - 1;
80 
81 	Log(LOG_STATUS, "Worker started...");
82 	while (!IsStopRequested(1000))
83 	{
84 		sec_counter++;
85 
86 		if (sec_counter % 12 == 0) {
87 			m_LastHeartbeat = mytime(NULL);
88 		}
89 
90 		if (sec_counter % m_PollInterval == 0)
91 		{
92 			GetDevicesState();
93 		}
94 	}
95 	Log(LOG_STATUS, "Worker stopped...");
96 }
97 
GetDevicesState()98 void BleBox::GetDevicesState()
99 {
100 	std::lock_guard<std::mutex> l(m_mutex);
101 
102 	for (const auto& itt : m_devices)
103 	{
104 		std::stringstream sstr;
105 		sstr << "/api/" << DevicesType[itt.second].api_state << "/state";
106 		std::string command = sstr.str();
107 
108 		Json::Value root = SendCommand(itt.first, command, 2);
109 		if (root.empty())
110 			continue;
111 
112 		int IP = IPToUInt(itt.first);
113 		if (IP != 0)
114 		{
115 			switch (itt.second)
116 			{
117 			case 0:
118 			{
119 				if (DoesNodeExists(root, "state") == false)
120 					break;
121 
122 				const bool state = root["state"].asBool();
123 
124 				SendSwitch(IP, 0, 255, state, 0, DevicesType[itt.second].name);
125 				break;
126 			}
127 			case 1:
128 			{
129 				if (DoesNodeExists(root, "shutter") == false)
130 					break;
131 
132 				root = root["shutter"];
133 
134 				if (DoesNodeExists(root, "state") == false)
135 					break;
136 
137 				const int state = root["state"].asInt();
138 
139 				if (DoesNodeExists(root, "currentPos", "position") == false)
140 					break;
141 
142 				const int currentPos = root["currentPos"]["position"].asInt();
143 				const int pos = currentPos;
144 
145 				bool opened = true;
146 				if ((state == 2 && pos == 100) || (state == 3))
147 					opened = false;
148 
149 				SendSwitch(IP, 0, 255, opened, 100 - pos, DevicesType[itt.second].name);
150 				break;
151 			}
152 			case 2:
153 			{
154 				if (DoesNodeExists(root, "light", "currentColor") == false)
155 					break;
156 
157 				const std::string currentColor = root["light"]["currentColor"].asString();
158 				unsigned int hexNumber;
159 				sscanf(currentColor.c_str(), "%x", &hexNumber);
160 				int level = (int)(hexNumber / (255.0 / 100.0));
161 
162 				SendSwitch(IP, 0, 255, level > 0, level, DevicesType[itt.second].name);
163 				break;
164 			}
165 			case 3:
166 			{
167 				if (DoesNodeExists(root, "rgbw", "currentColor") == false)
168 					break;
169 
170 				const std::string currentColor = root["rgbw"]["currentColor"].asString();
171 				unsigned int hexNumber;
172 				sscanf(currentColor.c_str(), "%x", &hexNumber);
173 
174 				SendRGBWSwitch(IP, 0, 255, hexNumber, true, DevicesType[itt.second].name);
175 				break;
176 			}
177 			case 4:
178 			{
179 				if (DoesNodeExists(root, "currentPos") == false)
180 					break;
181 
182 				const float level = root["currentPos"].asFloat();
183 
184 				SendPercentageSensor(IP, 1, 255, level, DevicesType[itt.second].name);
185 				break;
186 			}
187 			case 5:
188 			{
189 				if (DoesNodeExists(root, "dimmer", "currentBrightness") == false)
190 					break;
191 
192 				const int currentPos = root["dimmer"]["currentBrightness"].asInt();
193 				int level = (int)(currentPos / (255.0 / 100.0));
194 
195 				SendSwitch(IP, 0, 255, level > 0, level, DevicesType[itt.second].name);
196 				break;
197 			}
198 			case 6:
199 			{
200 				if ((DoesNodeExists(root, "relays") == false) || (!root["relays"].isArray()))
201 					break;
202 
203 				Json::Value relays = root["relays"];
204 				Json::ArrayIndex count = relays.size();
205 				for (Json::ArrayIndex index = 0; index < count; index++)
206 				{
207 					Json::Value relay = relays[index];
208 					if ((DoesNodeExists(relay, "relay") == false) || (DoesNodeExists(relay, "state") == false))
209 						break;
210 					uint8_t relayNumber = (uint8_t)relay["relay"].asInt(); // 0 or 1
211 					bool currentState = relay["state"].asBool(); // 0 or 1
212 					//std::string name = DevicesType[itt.second].name + " " + relay["state"].asString();
213 					SendSwitch(IP, relayNumber, 255, currentState, 0, DevicesType[itt.second].name);
214 				}
215 
216 				break;
217 			}
218 			case 7:
219 			{
220 				if (DoesNodeExists(root, "air") == false)
221 					break;
222 
223 				root = root["air"];
224 
225 				if ((DoesNodeExists(root, "sensors") == false) || (!root["sensors"].isArray()))
226 					break;
227 
228 				Json::Value sensors = root["sensors"];
229 				Json::ArrayIndex count = sensors.size();
230 				for (Json::ArrayIndex index = 0; index < count; index++)
231 				{
232 					Json::Value sensor = sensors[index];
233 					if ((DoesNodeExists(sensor, "type") == false) || (DoesNodeExists(sensor, "value") == false))
234 						break;
235 					uint8_t value = (uint8_t)sensor["value"].asInt();
236 					std::string type = sensor["type"].asString();
237 
238 					//TODO - how save IP address ??
239 					SendAirQualitySensor(IP, index + 1, 255, value, type);
240 				}
241 
242 				break;
243 			}
244 			case 8:
245 			{
246 				if (DoesNodeExists(root, "tempSensor") == false)
247 					break;
248 
249 				root = root["tempSensor"];
250 
251 				if ((DoesNodeExists(root, "sensors") == false) || (!root["sensors"].isArray()))
252 					break;
253 
254 				Json::Value sensors = root["sensors"];
255 				Json::ArrayIndex count = sensors.size();
256 				for (Json::ArrayIndex index = 0; index < count; index++)
257 				{
258 					Json::Value sensor = sensors[index];
259 
260 					if ((DoesNodeExists(sensor, "value") == false) || (DoesNodeExists(sensor, "state") == false))
261 						break;
262 
263 					if (sensor["state"] != 2)
264 					{
265 						Log(LOG_ERROR, "temp sensor error!");
266 						break;
267 					}
268 
269 					std::string temperature = sensor["value"].asString(); // xxxx (xx.xx = temperature)
270 					float ftemp = static_cast<float>(std::stoi(temperature.substr(0, 2)) + std::stoi(temperature.substr(2, 2)) / 100.0);
271 
272 					//TODO - how save IP address ??
273 					SendTempSensor(IP, 255, ftemp, DevicesType[itt.second].name);
274 				}
275 
276 				break;
277 			}
278 			}
279 			SetHeartbeatReceived();
280 		}
281 	}
282 }
283 
GetDeviceIP(const tRBUF * id)284 std::string BleBox::GetDeviceIP(const tRBUF * id)
285 {
286 	char ip[20];
287 
288 	sprintf(ip, "%d.%d.%d.%d", id->LIGHTING2.id1, id->LIGHTING2.id2, id->LIGHTING2.id3, id->LIGHTING2.id4);
289 	return ip;
290 }
291 
GetDeviceRevertIP(const tRBUF * id)292 std::string BleBox::GetDeviceRevertIP(const tRBUF * id)
293 {
294 	char ip[20];
295 
296 	sprintf(ip, "%d.%d.%d.%d", id->LIGHTING2.id4, id->LIGHTING2.id3, id->LIGHTING2.id2, id->LIGHTING2.id1);
297 	return ip;
298 }
299 
GetDeviceIP(const std::string & id)300 std::string BleBox::GetDeviceIP(const std::string & id)
301 {
302 	BYTE id1, id2, id3, id4;
303 	char ip[20];
304 
305 	sscanf(id.c_str(), "%2hhx%2hhx%2hhx%2hhx", &id1, &id2, &id3, &id4);
306 	sprintf(ip, "%d.%d.%d.%d", id1, id2, id3, id4);
307 
308 	return ip;
309 }
310 
GetDeviceTypeByApiName(const std::string & apiName)311 int BleBox::GetDeviceTypeByApiName(const std::string & apiName)
312 {
313 	for (unsigned int i = 0; i < TOT_DEVICE_TYPES; ++i)
314 	{
315 		if (DevicesType[i].api_name == apiName)
316 		{
317 			return DevicesType[i].unit;
318 		}
319 	}
320 	Log(LOG_ERROR, "unknown device api name(%s)", apiName.c_str());
321 	return -1;
322 }
323 
IPToHex(const std::string & IPAddress,const int type)324 std::string BleBox::IPToHex(const std::string & IPAddress, const int type)
325 {
326 	std::vector<std::string> strarray;
327 	StringSplit(IPAddress, ".", strarray);
328 	if (strarray.size() != 4)
329 		return "";
330 
331 	char szIdx[10];
332 	// because exists inconsistency when comparing deviceID in method decode_xxx in mainworker(Limitless uses small letter, lighting2 etc uses capital letter)
333 	if (type != pTypeColorSwitch)
334 	{
335 		uint32_t sID = (uint32_t)(atoi(strarray[0].c_str()) << 24) | (uint32_t)(atoi(strarray[1].c_str()) << 16) | (atoi(strarray[2].c_str()) << 8) | atoi(strarray[3].c_str());
336 		sprintf(szIdx, "%08X", (unsigned int)sID);
337 	}
338 	else
339 	{
340 		sprintf(szIdx, "%X%02X%02X%02X", atoi(strarray[0].data()), atoi(strarray[1].data()), atoi(strarray[2].data()), atoi(strarray[3].data()));
341 	}
342 	return szIdx;
343 }
344 
WriteToHardware(const char * pdata,const unsigned char)345 bool BleBox::WriteToHardware(const char* pdata, const unsigned char /*length*/)
346 {
347 	const tRBUF* output = reinterpret_cast<const tRBUF*>(pdata);
348 
349 	if (output->ICMND.packettype == pTypeLighting2 && output->LIGHTING2.subtype == sTypeAC)
350 	{
351 		std::string IPAddress = GetDeviceIP(output);
352 
353 		int type = GetDeviceType(IPAddress);
354 
355 		if (type != -1)
356 		{
357 			switch (type)
358 			{
359 			case 0:
360 			{
361 				std::string state;
362 				if (output->LIGHTING2.cmnd == light2_sOn)
363 				{
364 					state = "1";
365 				}
366 				else
367 				{
368 					state = "0";
369 				}
370 
371 				Json::Value root = SendCommand(IPAddress, "/s/" + state);
372 				if (root.empty())
373 					return false;
374 
375 				if (DoesNodeExists(root, "state") == false)
376 					return false;
377 
378 				if (root["state"].asString() != state)
379 				{
380 					Log(LOG_ERROR, "state not changed!");
381 					return false;
382 				}
383 				break;
384 			}
385 
386 			case 1: // shutterbox
387 			{
388 				int percentage = 0;
389 				switch (output->LIGHTING2.cmnd) {
390 				case light2_sOn:
391 					percentage = 0;
392 					break;
393 				case light2_sOff:
394 					percentage = 100;
395 					break;
396 				default:
397 					percentage = 100 - output->LIGHTING2.level * 100 / 15;
398 					break;
399 				}
400 
401 				Json::Value root = SendCommand(IPAddress, "/s/p/" + std::to_string(percentage));
402 
403 				if (root.empty())
404 					return false;
405 
406 				if (DoesNodeExists(root, "shutter") == false)
407 					return false;
408 
409 				root = root["shutter"];
410 
411 				if (DoesNodeExists(root, "desiredPos", "position") == false)
412 					return false;
413 
414 				if (root["desiredPos"]["position"].asInt() != percentage)
415 					return false;
416 
417 				break;
418 			}
419 
420 			case 2:
421 			{
422 				std::string level;
423 				if (output->LIGHTING2.cmnd == light2_sOn)
424 				{
425 					level = "ff";
426 				}
427 				else
428 					if (output->LIGHTING2.cmnd == light2_sOff)
429 					{
430 						level = "00";
431 					}
432 					else
433 					{
434 						uint8_t percentage = static_cast<uint8_t>(output->LIGHTING2.level * 255 / 15);
435 
436 						char value[4];
437 						sprintf(value, "%x", percentage);
438 						level = value;
439 					}
440 
441 				Json::Value root = SendCommand(IPAddress, "/s/" + level);
442 				if (root.empty())
443 					return false;
444 
445 				if (DoesNodeExists(root, "light", "desiredColor") == false)
446 					return false;
447 
448 				if (root["light"]["desiredColor"].asString() != level)
449 				{
450 					Log(LOG_ERROR, "light not changed!");
451 					return false;
452 				}
453 
454 				break;
455 			}
456 
457 			case 5: // dimmerBox
458 			{
459 				std::string level;
460 				if (output->LIGHTING2.cmnd == light2_sOn)
461 				{
462 					level = "ff";
463 				}
464 				else
465 					if (output->LIGHTING2.cmnd == light2_sOff)
466 					{
467 						level = "0";
468 					}
469 					else
470 					{
471 						uint8_t percentage = static_cast<uint8_t>(output->LIGHTING2.level * 255 / 15);
472 
473 						char value[4];
474 						sprintf(value, "%x", percentage);
475 						level = value;
476 					}
477 
478 				Json::Value root = SendCommand(IPAddress, "/s/" + level);
479 				if (root.empty())
480 					return false;
481 
482 				if (DoesNodeExists(root, "dimmer", "desiredBrightness") == false)
483 					return false;
484 
485 				std::stringstream ss;
486 				ss << std::hex << root["dimmer"]["desiredBrightness"].asInt();
487 				std::string state = ss.str();
488 
489 				if (state != level)
490 				{
491 					Log(LOG_ERROR, "dimmer not changed!");
492 					return false;
493 				}
494 
495 				break;
496 			}
497 
498 			case 6: //switchboxd
499 			{
500 				std::string state;
501 				std::string relayNumber;
502 				if (output->LIGHTING2.cmnd == light2_sOn)
503 				{
504 					state = "1";
505 				}
506 				else
507 				{
508 					state = "0";
509 				}
510 				if (output->LIGHTING2.unitcode == 0)
511 				{
512 					relayNumber = "0";
513 				}
514 				else
515 				{
516 					relayNumber = "1";
517 				}
518 
519 				std::stringstream ss;
520 				ss << "/s/" << relayNumber << "/" << state;
521 
522 				Json::Value root = SendCommand(IPAddress, ss.str());
523 				if (root.empty())
524 					return false;
525 
526 				if ((DoesNodeExists(root, "relays") == false) || (!root["relays"].isArray()))
527 					return false;
528 
529 				bool success = false;
530 				Json::Value relays = root["relays"];
531 				Json::ArrayIndex count = relays.size();
532 				for (Json::ArrayIndex index = 0; index < count; index++)
533 				{
534 					Json::Value relay = relays[index];
535 					if ((DoesNodeExists(relay, "relay") == false) || (DoesNodeExists(relay, "state") == false))
536 						continue;
537 					int relayNumber2 = relay["relay"].asInt(); // 0 or 1
538 					std::string currentState = relay["state"].asString(); // 0 or 1
539 					if (((output->LIGHTING2.unitcode) == relayNumber2) && (state == currentState))
540 					{
541 						success = true;
542 						break;
543 					}
544 				}
545 				return success;
546 
547 				break;
548 			}
549 			default:
550 			{
551 				return false;
552 			}
553 			}
554 			return true;
555 		}
556 	}
557 
558 	if (output->ICMND.packettype == pTypeGeneralSwitch && output->LIGHTING2.subtype == sTypeAC)
559 	{
560 		std::string IPAddress = GetDeviceRevertIP(output);
561 
562 		int type = GetDeviceType(IPAddress);
563 
564 		switch (type)
565 		{
566 		case 4: // gatebox
567 		{
568 			std::string command;
569 			if (output->ICMND.msg4 == 2) // 2=primary, 3=secondary
570 			{
571 				command = "p";
572 			}
573 			else
574 			{
575 				command = "s";
576 			}
577 
578 			Json::Value root = SendCommand(IPAddress, "/s/" + command);
579 			if (root.empty())
580 				return false;
581 
582 			if (DoesNodeExists(root, "gate") == false)
583 				return false;
584 
585 			break;
586 		}
587 		default:
588 		{
589 			return false;
590 		}
591 
592 		}
593 		return true;
594 	}
595 
596 	if (output->ICMND.packettype == pTypeColorSwitch && output->LIGHTING2.subtype == sTypeColor_RGB_W)
597 	{
598 		std::string IPAddress = GetDeviceRevertIP(output);
599 
600 		const _tColorSwitch* pLed = reinterpret_cast<const _tColorSwitch*>(pdata);
601 		int red, green, blue, white;
602 		bool setColor = true;
603 
604 		switch (pLed->command)
605 		{
606 		case Color_LedOn: {
607 			if (m_RGBWColorState.mode != ColorModeNone && !m_RGBWisWhiteState)
608 			{
609 				red = int(round(m_RGBWColorState.r * m_RGBWbrightnessState / 255.0f));
610 				green = int(round(m_RGBWColorState.g * m_RGBWbrightnessState / 255.0f));
611 				blue = int(round(m_RGBWColorState.b * m_RGBWbrightnessState / 255.0f));
612 				white = 0;
613 			}
614 			else
615 			{
616 				red = 0;
617 				green = 0;
618 				blue = 0;
619 				white = m_RGBWbrightnessState;
620 			}
621 			break;
622 		}
623 		case Color_LedOff:
624 			red = 0;
625 			green = 0;
626 			blue = 0;
627 			white = 0;
628 			break;
629 		case Color_SetColorToWhite: {
630 			m_RGBWisWhiteState = true;
631 			m_RGBWColorState = pLed->color; //TODO: Is there any point of doing this?
632 			setColor = false;//Sending is done by SetBrightnessLevel
633 			break;
634 		}
635 		case Color_SetColor: {
636 			if (pLed->color.mode == ColorModeWhite)
637 			{
638 				m_RGBWisWhiteState = true;
639 				m_RGBWColorState = pLed->color; //TODO: Is there any point of doing this?
640 			}
641 			else if (pLed->color.mode == ColorModeRGB)
642 			{
643 				m_RGBWisWhiteState = false;
644 				m_RGBWColorState = pLed->color;
645 			}
646 			else {
647 				Log(LOG_STATUS, "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);
648 			}
649 			// No break, fall through to send combined color + brightness command
650 		}
651 		case Color_SetBrightnessLevel: {
652 			int BrightnessBase = (int)pLed->value;
653 			int dMax_Send = (int)(round((255.0f / 100.0f) * float(BrightnessBase)));
654 
655 			m_RGBWbrightnessState = dMax_Send;
656 
657 			if (m_RGBWisWhiteState) // TODO: Check m_RGBWColorState.mode instead
658 			{
659 				red = 0;
660 				green = 0;
661 				blue = 0;
662 				white = dMax_Send;
663 			}
664 			else
665 			{
666 				red = int(round(m_RGBWColorState.r * dMax_Send / 255.0f));
667 				green = int(round(m_RGBWColorState.g * dMax_Send / 255.0f));
668 				blue = int(round(m_RGBWColorState.b * dMax_Send / 255.0f));
669 				white = 0;
670 			}
671 			break;
672 		}
673 		default:
674 			setColor = false;
675 			break;
676 		}
677 
678 		if (!setColor)
679 			return false;
680 
681 		char level[10];
682 		sprintf(level, "%02x%02x%02x%02x", red, green, blue, white);
683 		std::string state(level);
684 
685 		Json::Value root = SendCommand(IPAddress, "/s/" + state);
686 		if (root.empty())
687 			return false;
688 
689 		if (DoesNodeExists(root, "rgbw", "desiredColor") == false)
690 			return false;
691 
692 		if (root["rgbw"]["desiredColor"].asString() != state)
693 		{
694 			Log(LOG_ERROR, "rgbw not changed!");
695 			return false;
696 		}
697 		return true;
698 	}
699 
700 	return false;
701 }
702 
DoesNodeExists(const Json::Value & root,const std::string & node)703 bool BleBox::DoesNodeExists(const Json::Value & root, const std::string & node)
704 {
705 	if (root[node].empty() == true)
706 	{
707 		Log(LOG_ERROR, "node '%s' missing!", node.c_str());
708 		return false;
709 	}
710 	return true;
711 }
712 
DoesNodeExists(const Json::Value & root,const std::string & node,const std::string & value)713 bool BleBox::DoesNodeExists(const Json::Value & root, const std::string & node, const std::string & value)
714 {
715 	if (DoesNodeExists(root, node) == false)
716 		return false;
717 
718 	if (root[node][value].empty() == true)
719 	{
720 		Log(LOG_ERROR, "value '%s' missing!", value.c_str());
721 		return false;
722 	}
723 	return true;
724 }
725 
SetSettings(const int pollIntervalSec)726 void BleBox::SetSettings(const int pollIntervalSec)
727 {
728 	m_PollInterval = 30;
729 
730 	if (pollIntervalSec > 0)
731 		m_PollInterval = pollIntervalSec;
732 }
733 
SendSwitch(const int NodeID,const uint8_t ChildID,const int BatteryLevel,const bool bOn,const double Level,const std::string & defaultname)734 void BleBox::SendSwitch(const int NodeID, const uint8_t ChildID, const int BatteryLevel, const bool bOn, const double Level, const std::string & defaultname)
735 { //TODO - remove this method, when in DomoticzHardware bug is fix (15 instead 16)
736 	double rlevel = (15.0 / 100.0) * Level;
737 	uint8_t level = (uint8_t)(rlevel);
738 
739 	//make device ID
740 	unsigned char ID1 = (unsigned char)((NodeID & 0xFF000000) >> 24);
741 	unsigned char ID2 = (unsigned char)((NodeID & 0xFF0000) >> 16);
742 	unsigned char ID3 = (unsigned char)((NodeID & 0xFF00) >> 8);
743 	unsigned char ID4 = (unsigned char)NodeID & 0xFF;
744 
745 	char szIdx[10];
746 	sprintf(szIdx, "%X%02X%02X%02X", ID1, ID2, ID3, ID4);
747 	std::vector<std::vector<std::string> > result;
748 	result = m_sql.safe_query("SELECT Name,nValue,sValue FROM DeviceStatus WHERE (HardwareID==%d) AND (DeviceID=='%q') AND (Unit == %d) AND (Type==%d) AND (Subtype==%d)",
749 		m_HwdID, szIdx, ChildID, int(pTypeLighting2), int(sTypeAC));
750 	if (!result.empty())
751 	{
752 		//check if we have a change, if not do not update it
753 		int nvalue = atoi(result[0][1].c_str());
754 		if ((!bOn) && (nvalue == light2_sOff))
755 			return;
756 		if ((bOn && (nvalue != light2_sOff)))
757 		{
758 			//Check Level
759 			int slevel = atoi(result[0][2].c_str());
760 			if (slevel == level)
761 				return;
762 		}
763 	}
764 
765 	//Send as Lighting 2
766 	tRBUF lcmd;
767 	memset(&lcmd, 0, sizeof(RBUF));
768 	lcmd.LIGHTING2.packetlength = sizeof(lcmd.LIGHTING2) - 1;
769 	lcmd.LIGHTING2.packettype = pTypeLighting2;
770 	lcmd.LIGHTING2.subtype = sTypeAC;
771 	lcmd.LIGHTING2.id1 = ID1;
772 	lcmd.LIGHTING2.id2 = ID2;
773 	lcmd.LIGHTING2.id3 = ID3;
774 	lcmd.LIGHTING2.id4 = ID4;
775 	lcmd.LIGHTING2.unitcode = ChildID;
776 	if (!bOn)
777 	{
778 		lcmd.LIGHTING2.cmnd = light2_sOff;
779 	}
780 	else
781 	{
782 		if (level != 0)
783 			lcmd.LIGHTING2.cmnd = light2_sSetLevel;
784 		else
785 			lcmd.LIGHTING2.cmnd = light2_sOn;
786 	}
787 	lcmd.LIGHTING2.level = level;
788 	lcmd.LIGHTING2.filler = 0;
789 	lcmd.LIGHTING2.rssi = 12;
790 	sDecodeRXMessage(this, (const unsigned char*)& lcmd.LIGHTING2, defaultname.c_str(), BatteryLevel);
791 }
792 
793 
794 //Webserver helpers
795 namespace http {
796 	namespace server {
Cmd_BleBoxGetNodes(WebEmSession & session,const request & req,Json::Value & root)797 		void CWebServer::Cmd_BleBoxGetNodes(WebEmSession& session, const request& req, Json::Value& root)
798 		{
799 			if (session.rights != 2)
800 			{
801 				session.reply_status = reply::forbidden;
802 				return; //Only admin user allowed
803 			}
804 
805 			std::string hwid = request::findValue(&req, "idx");
806 			CDomoticzHardwareBase* pBaseHardware = m_mainworker.GetHardwareByIDType(hwid, HTYPE_BleBox);
807 			if (pBaseHardware == NULL)
808 				return;
809 
810 			root["status"] = "OK";
811 			root["title"] = "BleBoxGetNodes";
812 
813 			std::vector<std::vector<std::string> > result;
814 			result = m_sql.safe_query("SELECT ID,Name,DeviceID FROM DeviceStatus WHERE (HardwareID=='%d')", pBaseHardware->m_HwdID);
815 			if (!result.empty())
816 			{
817 				int ii = 0;
818 				for (const auto& itt : result)
819 				{
820 					std::vector<std::string> sd = itt;
821 
822 					BYTE id1, id2, id3, id4;
823 					char ip[20];
824 					sscanf(sd[2].c_str(), "%2hhx%2hhx%2hhx%2hhx", &id1, &id2, &id3, &id4);
825 					sprintf(ip, "%d.%d.%d.%d", id1, id2, id3, id4);
826 
827 					root["result"][ii]["idx"] = sd[0];
828 					root["result"][ii]["Name"] = sd[1];
829 					root["result"][ii]["IP"] = ip;
830 					root["result"][ii]["Type"] = "unknown";
831 					root["result"][ii]["Uptime"] = "unknown";
832 					root["result"][ii]["hv"] = "unknown";
833 					root["result"][ii]["fv"] = "unknown";
834 
835 					BleBox* pHardware = reinterpret_cast<BleBox*>(pBaseHardware);
836 
837 					int type = pHardware->GetDeviceType(ip);
838 					if (type != -1)
839 					{
840 						root["result"][ii]["Type"] = DevicesType[type].name;
841 
842 						Json::Value state = pHardware->GetApiDeviceState(ip);
843 						if (!state.isNull())
844 						{
845 							if (pHardware->DoesNodeExists(state, "fv"))
846 								root["result"][ii]["fv"] = state["fv"].asString();
847 
848 							if (pHardware->DoesNodeExists(state, "hv"))
849 								root["result"][ii]["hv"] = state["hv"].asString();
850 
851 							std::string uptime = pHardware->GetUptime(ip);
852 							root["result"][ii]["Uptime"] = uptime;
853 						}
854 					}
855 
856 					ii++;
857 				}
858 			}
859 		}
860 
Cmd_BleBoxSetMode(WebEmSession & session,const request & req,Json::Value & root)861 		void CWebServer::Cmd_BleBoxSetMode(WebEmSession& session, const request& req, Json::Value& root)
862 		{
863 			if (session.rights != 2)
864 			{
865 				session.reply_status = reply::forbidden;
866 				return; //Only admin user allowed
867 			}
868 
869 			std::string hwid = request::findValue(&req, "idx");
870 			std::string mode1 = request::findValue(&req, "mode1");
871 			std::string mode2 = request::findValue(&req, "mode2");
872 			if (
873 				(hwid == "") ||
874 				(mode1 == "") ||
875 				(mode2 == "")
876 				)
877 				return;
878 			CDomoticzHardwareBase * pBaseHardware = m_mainworker.GetHardwareByIDType(hwid, HTYPE_BleBox);
879 			if (pBaseHardware == NULL)
880 				return;
881 			BleBox * pHardware = reinterpret_cast<BleBox*>(pBaseHardware);
882 
883 			root["status"] = "OK";
884 			root["title"] = "BleBoxSetMode";
885 
886 			int iMode1 = atoi(mode1.c_str());
887 			int iMode2 = atoi(mode2.c_str());
888 
889 			m_sql.safe_query("UPDATE Hardware SET Mode1=%d, Mode2=%d WHERE (ID == '%q')", iMode1, iMode2, hwid.c_str());
890 			pHardware->SetSettings(iMode1);
891 			pHardware->Restart();
892 		}
893 
894 
Cmd_BleBoxAddNode(WebEmSession & session,const request & req,Json::Value & root)895 		void CWebServer::Cmd_BleBoxAddNode(WebEmSession & session, const request & req, Json::Value & root)
896 		{
897 			if (session.rights != 2)
898 			{
899 				session.reply_status = reply::forbidden;
900 				return; //Only admin user allowed
901 			}
902 
903 			std::string hwid = request::findValue(&req, "idx");
904 			std::string name = HTMLSanitizer::Sanitize(request::findValue(&req, "name"));
905 			std::string ip = HTMLSanitizer::Sanitize(request::findValue(&req, "ip"));
906 			if (
907 				(hwid == "") ||
908 				(name == "") ||
909 				(ip == "")
910 				)
911 				return;
912 			CDomoticzHardwareBase * pBaseHardware = m_mainworker.GetHardwareByIDType(hwid, HTYPE_BleBox);
913 			if (pBaseHardware == NULL)
914 				return;
915 			BleBox * pHardware = reinterpret_cast<BleBox*>(pBaseHardware);
916 
917 			root["status"] = "OK";
918 			root["title"] = "BleBoxAddNode";
919 			pHardware->AddNode(name, ip, true);
920 		}
921 
Cmd_BleBoxRemoveNode(WebEmSession & session,const request & req,Json::Value & root)922 		void CWebServer::Cmd_BleBoxRemoveNode(WebEmSession & session, const request & req, Json::Value & root)
923 		{
924 			if (session.rights != 2)
925 			{
926 				session.reply_status = reply::forbidden;
927 				return; //Only admin user allowed
928 			}
929 
930 			std::string hwid = request::findValue(&req, "idx");
931 			std::string nodeid = request::findValue(&req, "nodeid");
932 			if (
933 				(hwid == "") ||
934 				(nodeid == "")
935 				)
936 				return;
937 			CDomoticzHardwareBase * pBaseHardware = m_mainworker.GetHardwareByIDType(hwid, HTYPE_BleBox);
938 			if (pBaseHardware == NULL)
939 				return;
940 			BleBox * pHardware = reinterpret_cast<BleBox*>(pBaseHardware);
941 
942 			root["status"] = "OK";
943 			root["title"] = "BleBoxRemoveNode";
944 			int ID = atoi(nodeid.c_str());
945 			pHardware->RemoveNode(ID);
946 		}
947 
Cmd_BleBoxClearNodes(WebEmSession & session,const request & req,Json::Value & root)948 		void CWebServer::Cmd_BleBoxClearNodes(WebEmSession & session, const request & req, Json::Value & root)
949 		{
950 			if (session.rights != 2)
951 			{
952 				session.reply_status = reply::forbidden;
953 				return; //Only admin user allowed
954 			}
955 
956 			std::string hwid = request::findValue(&req, "idx");
957 			CDomoticzHardwareBase* pBaseHardware = m_mainworker.GetHardwareByIDType(hwid, HTYPE_BleBox);
958 			if (pBaseHardware == NULL)
959 				return;
960 			BleBox * pHardware = reinterpret_cast<BleBox*>(pBaseHardware);
961 
962 			root["status"] = "OK";
963 			root["title"] = "BleBoxClearNodes";
964 			pHardware->RemoveAllNodes();
965 		}
966 
Cmd_BleBoxAutoSearchingNodes(WebEmSession & session,const request & req,Json::Value & root)967 		void CWebServer::Cmd_BleBoxAutoSearchingNodes(WebEmSession & session, const request & req, Json::Value & root)
968 		{
969 			if (session.rights != 2)
970 			{
971 				session.reply_status = reply::forbidden;
972 				return; //Only admin user allowed
973 			}
974 
975 			std::string hwid = request::findValue(&req, "idx");
976 			std::string ipmask = request::findValue(&req, "ipmask");
977 			if (
978 				(hwid == "") ||
979 				(ipmask == "")
980 				)
981 				return;
982 			CDomoticzHardwareBase * pBaseHardware = m_mainworker.GetHardwareByIDType(hwid, HTYPE_BleBox);
983 			if (pBaseHardware == NULL)
984 				return;
985 			BleBox * pHardware = reinterpret_cast<BleBox*>(pBaseHardware);
986 
987 			root["status"] = "OK";
988 			root["title"] = "BleBoxAutoSearchingNodes";
989 			pHardware->SearchNodes(ipmask);
990 		}
991 
Cmd_BleBoxUpdateFirmware(WebEmSession & session,const request & req,Json::Value & root)992 		void CWebServer::Cmd_BleBoxUpdateFirmware(WebEmSession & session, const request & req, Json::Value & root)
993 		{
994 			if (session.rights != 2)
995 			{
996 				session.reply_status = reply::forbidden;
997 				return; //Only admin user allowed
998 			}
999 
1000 			std::string hwid = request::findValue(&req, "idx");
1001 			CDomoticzHardwareBase* pBaseHardware = m_mainworker.GetHardwareByIDType(hwid, HTYPE_BleBox);
1002 			if (pBaseHardware == NULL)
1003 				return;
1004 			BleBox * pHardware = reinterpret_cast<BleBox*>(pBaseHardware);
1005 
1006 			root["status"] = "OK";
1007 			root["title"] = "BleBoxUpdateFirmware";
1008 			pHardware->UpdateFirmware();
1009 		}
1010 
1011 	}
1012 }
1013 
SendCommand(const std::string & IPAddress,const std::string & command,const int timeOut)1014 Json::Value BleBox::SendCommand(const std::string & IPAddress, const std::string & command, const int timeOut)
1015 {
1016 	std::string result;
1017 	Json::Value root;
1018 
1019 	std::stringstream sstr;
1020 	sstr << "http://" << IPAddress << command;
1021 
1022 	std::string sURL = sstr.str();
1023 	HTTPClient::SetConnectionTimeout(timeOut);
1024 	HTTPClient::SetTimeout(4);
1025 	if (!HTTPClient::GET(sURL, result))
1026 	{
1027 		Log(LOG_ERROR, "send '%s'command to %s failed!", command.c_str(), IPAddress.c_str());
1028 		return root;
1029 	}
1030 
1031 	if (!ParseJSon(result, root))
1032 	{
1033 		Log(LOG_ERROR, "Invalid json received!");
1034 		return root;
1035 	}
1036 
1037 	if (root.size() == 0)
1038 	{
1039 		Log(LOG_ERROR, "Json is empty!");
1040 		return root;
1041 	}
1042 
1043 	if (root.isArray())
1044 		root = root[0];
1045 
1046 	return root;
1047 }
1048 
IdentifyDevice(const std::string & IPAddress)1049 std::string BleBox::IdentifyDevice(const std::string & IPAddress)
1050 {
1051 	Json::Value root = SendCommand(IPAddress, "/api/device/state", 2);
1052 	if (!root.isObject())
1053 		return "";
1054 
1055 	std::string result;
1056 
1057 	if (root["device"].empty() == true)
1058 	{
1059 		if (DoesNodeExists(root, "type") == false)
1060 			return "";
1061 		else
1062 			result = root["type"].asString();
1063 	}
1064 	else
1065 	{
1066 		if (root["device"]["type"].empty() == true)
1067 		{
1068 			Log(LOG_ERROR, "Invalid device type received!");
1069 			return "";
1070 		}
1071 		result = root["device"]["type"].asString();
1072 	}
1073 	return result;
1074 }
1075 
GetApiDeviceState(const std::string & IPAddress)1076 Json::Value BleBox::GetApiDeviceState(const std::string & IPAddress)
1077 {
1078 	Json::Value empty;
1079 
1080 	Json::Value root = SendCommand(IPAddress, "/api/device/state", 2);
1081 	if (!root.isObject())
1082 		return empty;
1083 
1084 	if (root["device"].empty() == true)
1085 	{
1086 		return root;
1087 	}
1088 	else
1089 	{
1090 		return root["device"];
1091 	}
1092 }
1093 
GetUptime(const std::string & IPAddress)1094 std::string BleBox::GetUptime(const std::string & IPAddress)
1095 {
1096 	Json::Value root = SendCommand(IPAddress, "/api/device/uptime", 2);
1097 	if (root.empty())
1098 	{
1099 		root = SendCommand(IPAddress, "/api/device/runtime", 2);
1100 		if (root.empty())
1101 			return "unknown";
1102 		if (DoesNodeExists(root, "runtime") == false)
1103 			return "unknown";
1104 		root = root["runtime"];
1105 	}
1106 
1107 	uint64_t total_minutes = 0;
1108 	if (root["uptime"].empty() == false)
1109 	{
1110 		uint64_t msec = root["uptime"].asUInt64();
1111 		total_minutes = msec / (1000 * 60);
1112 	}
1113 	else if (root["uptimeS"].empty() == false)
1114 	{
1115 		uint64_t sec = root["uptimeS"].asUInt64();
1116 		total_minutes = sec / 60;
1117 	}
1118 	else if (root["timeH"].empty() == false)
1119 	{
1120 		uint64_t h = root["timeH"].asUInt64();
1121 		total_minutes = h * 60;
1122 	}
1123 	else
1124 		return "unknown";
1125 
1126 	int days = static_cast<int>(total_minutes / (24 * 60));
1127 	int hours = static_cast<int>(total_minutes / 60 - days * 24);
1128 	int mins = static_cast<int>(total_minutes - days * 24 * 60 - hours * 60);   //sec / 60 - day * (24 * 60) - hour * 60;
1129 
1130 	char timestring[32];
1131 	sprintf(timestring, "%d:%02d:%02d", days, hours, mins);
1132 
1133 	return timestring;
1134 }
1135 
GetDeviceType(const std::string & IPAddress)1136 int BleBox::GetDeviceType(const std::string & IPAddress)
1137 {
1138 	std::map<const std::string, const int>::const_iterator itt = m_devices.find(IPAddress);
1139 	if (itt == m_devices.end())
1140 	{
1141 		Log(LOG_ERROR, "unknown device (%s)", IPAddress.c_str());
1142 		return -1;
1143 	}
1144 	else
1145 	{
1146 		return itt->second;
1147 	}
1148 }
1149 
AddNode(const std::string & name,const std::string & IPAddress,bool reloadNodes)1150 void BleBox::AddNode(const std::string & name, const std::string & IPAddress, bool reloadNodes)
1151 {
1152 	std::string deviceApiName = IdentifyDevice(IPAddress);
1153 	if (deviceApiName.empty())
1154 		return;
1155 
1156 	int deviceTypeID = GetDeviceTypeByApiName(deviceApiName);
1157 	if (deviceTypeID == -1)
1158 		return;
1159 
1160 	STR_DEVICE deviceType = DevicesType[deviceTypeID];
1161 
1162 	std::string szIdx = IPToHex(IPAddress, deviceType.deviceID);
1163 
1164 	if (deviceType.unit == 4) // gatebox
1165 	{
1166 		m_sql.InsertDevice(m_HwdID, szIdx.c_str(), 1, (uint8_t)deviceType.deviceID, deviceType.subType, deviceType.switchType, 0, "Unavailable", name);
1167 		m_sql.InsertDevice(m_HwdID, szIdx.c_str(), 2, pTypeGeneralSwitch, sTypeAC, STYPE_PushOn, 0, "Unavailable", name);
1168 		m_sql.InsertDevice(m_HwdID, szIdx.c_str(), 3, pTypeGeneralSwitch, sTypeAC, STYPE_PushOn, 0, "Unavailable", name);
1169 	}
1170 	else
1171 		if (deviceType.unit == 6) // switchboxd
1172 		{
1173 			m_sql.InsertDevice(m_HwdID, szIdx.c_str(), 0, (uint8_t)deviceType.deviceID, deviceType.subType, deviceType.switchType, 0, "Unavailable", name);
1174 			m_sql.InsertDevice(m_HwdID, szIdx.c_str(), 1, (uint8_t)deviceType.deviceID, deviceType.subType, deviceType.switchType, 0, "Unavailable", name);
1175 		}
1176 		else
1177 		{
1178 			m_sql.InsertDevice(m_HwdID, szIdx.c_str(), 0, (uint8_t)deviceType.deviceID, deviceType.subType, deviceType.switchType, 0, "Unavailable", name);
1179 		}
1180 
1181 	if (reloadNodes)
1182 	{
1183 		ReloadNodes();
1184 	}
1185 }
1186 
RemoveNode(const int id)1187 void BleBox::RemoveNode(const int id)
1188 {
1189 	m_sql.safe_query("DELETE FROM DeviceStatus WHERE (HardwareID==%d) AND (ID=='%d')", m_HwdID, id);
1190 
1191 	ReloadNodes();
1192 }
1193 
RemoveAllNodes()1194 void BleBox::RemoveAllNodes()
1195 {
1196 	m_sql.safe_query("DELETE FROM DeviceStatus WHERE (HardwareID==%d)", m_HwdID);
1197 
1198 	UnloadNodes();
1199 }
1200 
UnloadNodes()1201 void BleBox::UnloadNodes()
1202 {
1203 	std::lock_guard<std::mutex> l(m_mutex);
1204 
1205 	m_devices.clear();
1206 }
1207 
LoadNodes()1208 bool BleBox::LoadNodes()
1209 {
1210 	std::lock_guard<std::mutex> l(m_mutex);
1211 
1212 	std::vector<std::vector<std::string> > result;
1213 	result = m_sql.safe_query("SELECT ID,DeviceID FROM DeviceStatus WHERE (HardwareID==%d)", m_HwdID);
1214 	if (!result.empty())
1215 	{
1216 		for (const auto& itt : result)
1217 		{
1218 			const std::vector<std::string>& sd = itt;
1219 			std::string addressIP = GetDeviceIP(sd[1]);
1220 
1221 			std::string deviceApiName = IdentifyDevice(addressIP);
1222 			if (deviceApiName.empty())
1223 				continue;
1224 
1225 			int deviceTypeID = GetDeviceTypeByApiName(deviceApiName);
1226 			if (deviceTypeID == -1)
1227 				continue;
1228 
1229 			m_devices.insert(std::pair<const std::string, const int>(addressIP, deviceTypeID));
1230 		}
1231 		return true;
1232 	}
1233 	else
1234 	{
1235 		Log(LOG_ERROR, "Cannot find any devices...");
1236 		return false;
1237 	}
1238 }
1239 
ReloadNodes()1240 void BleBox::ReloadNodes()
1241 {
1242 	UnloadNodes();
1243 	LoadNodes();
1244 }
1245 
UpdateFirmware()1246 void BleBox::UpdateFirmware()
1247 {
1248 	for (const auto& itt : m_devices)
1249 	{
1250 		Json::Value root = SendCommand(itt.first, "/api/ota/update", 2);
1251 	}
1252 }
1253 
SearchNodes(const std::string & pattern)1254 void BleBox::SearchNodes(const std::string & pattern)
1255 {
1256 	std::vector<std::string> hosts;
1257 	if(!PrepareHostList(pattern, hosts))
1258 	{
1259 		Log(LOG_ERROR, "Invalid or unsupported IP pattern : %s (expected e.g. 192.168.1.*)", pattern.c_str());
1260 		return;
1261 	}
1262 
1263 	std::vector<std::thread> searchingThreads;
1264 	for(auto&& host : hosts)
1265 		if (m_devices.find(host) == m_devices.end())
1266 			searchingThreads.emplace_back(&BleBox::AddNode, this, "unknown", std::ref(host), false);
1267 
1268 	for (auto&& thread : searchingThreads)
1269 	{
1270 		thread.join();
1271 	}
1272 
1273 	ReloadNodes();
1274 }
1275 
PrepareHostList(const std::string & pattern,std::vector<std::string> & hosts)1276 bool BleBox::PrepareHostList(const std::string& pattern, std::vector<std::string>& hosts)
1277 {
1278 	std::vector<std::string> strarray;
1279 	StringSplit(pattern, ".", strarray);
1280 
1281 	if (strarray.size() != 4)
1282 		return false;
1283 
1284 	if (strarray[3] != "*")
1285 		return false;
1286 
1287 	if (!isInt(strarray[0]) || !isInt(strarray[1]) || !isInt(strarray[2]))
1288 		return false;
1289 
1290 	std::stringstream sstr;
1291 	sstr << strarray[0] << "." << strarray[1] << "." << strarray[2] << ".";
1292 	const std::string ipStart = sstr.str();
1293 
1294 	for (unsigned int i = 1; i < 255; ++i)
1295 	{
1296 		std::string host = ipStart + std::to_string(i);
1297 		hosts.push_back(host);
1298 	}
1299 
1300 	return !hosts.empty();
1301 }
1302