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