1 #include "stdafx.h"
2 #include "OnkyoAVTCP.h"
3 #include "../main/Logger.h"
4 #include "../main/Helper.h"
5 #include "../main/SQLHelper.h"
6 #include <iostream>
7 #include "../main/localtime_r.h"
8 #include "../main/mainworker.h"
9 #include "../hardware/hardwaretypes.h"
10 #include <json/json.h>
11 #include "../tinyxpath/tinyxml.h"
12 #include "../main/WebServer.h"
13
14 #include <sstream>
15
16 #define RETRY_DELAY 30
17
18
19 struct eiscp_packet {
20 uint32_t magic;
21 uint32_t hdr_size;
22 uint32_t data_size;
23 uint8_t version;
24 uint8_t reserved[3];
25 char start;
26 char dest;
27 char message[2];
28 };
29
30 // Do not change except to append to these, or you break compatibility.
31 enum DevNr {
32 ID_MVL = 0,
33 ID_ZVL,
34 ID_VL3,
35 ID_VL4,
36 ID_PWR,
37 ID_ZPW,
38 ID_PW3,
39 ID_PW4,
40 ID_SLI,
41 ID_SLZ,
42 ID_SL3,
43 ID_SL4,
44 ID_HDO,
45 };
46
47 struct selector_name {
48 int val;
49 const char *name;
50 };
51
52 static struct selector_name input_names[] = {
53 { 0x00, "STB/DVR" },
54 { 0x01, "CBL/SAT" },
55 { 0x02, "GAME/TV" },
56 { 0x03, "AUX1" },
57 { 0x04, "AUX2/GAME2" },
58 { 0x05, "PC" },
59 { 0x06, "VIDEO7" },
60 { 0x07, "Hidden1" },
61 { 0x08, "Hidden2" },
62 { 0x09, "Hidden3" },
63 { 0x10, "BD/DVD" },
64 { 0x11, "STRM BOX" },
65 { 0x12, "TV" },
66 { 0x20, "TV/TAPE" },
67 { 0x21, "TAPE2" },
68 { 0x22, "PHONO" },
69 { 0x23, "TV/CD" },
70 { 0x24, "FM" },
71 { 0x25, "AM" },
72 { 0x26, "TUNER" },
73 { 0x27, "Music Server" },
74 { 0x28, "Internet Radio" },
75 { 0x29, "USB/USB(Front)" },
76 { 0x2a, "USB(Rear)" },
77 { 0x2c, "USB(Toggle)" },
78 { 0x2d, "Airplay" },
79 { 0x2e, "Bluetooth" },
80 { 0x30, "MULTI CH" },
81 { 0x31, "XM" },
82 { 0x32, "SIRIUS" },
83 { 0x33, "DAB" },
84 { 0x40, "Universal PORT" },
85 { 0x55, "HDMI5" },
86 { 0x56, "HDMI6" },
87 { 0x57, "HDMI7" },
88 { 0x80, "Source" },
89 { 0, NULL },
90 };
91
92 #define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
93 static struct {
94 const char *iscpCmd;
95 const char *iscpMute;
96 const char *name;
97 uint8_t switchType;
98 uint8_t subtype;
99 int customImage;
100 const char *options;
101 struct selector_name *default_names;
102 } switch_types[] = {
103 { "MVL", "AMT", "Master volume", STYPE_Dimmer, sSwitchGeneralSwitch, 8, NULL, NULL },
104 { "ZVL", "ZMT", "Zone 2 volume", STYPE_Dimmer, sSwitchGeneralSwitch, 8, NULL, NULL },
105 { "VL3", "MT3", "Zone 3 volume", STYPE_Dimmer, sSwitchGeneralSwitch, 8, NULL, NULL },
106 { "VL4", "MT4", "Zone 4 volume", STYPE_Dimmer, sSwitchGeneralSwitch, 8, NULL, NULL },
107 { "PWR", NULL, "Master power", STYPE_OnOff, sSwitchGeneralSwitch, 5, NULL, NULL },
108 { "ZPW", NULL, "Zone 2 power", STYPE_OnOff, sSwitchGeneralSwitch, 5, NULL, NULL },
109 { "PW3", NULL, "Zone 3 power", STYPE_OnOff, sSwitchGeneralSwitch, 5, NULL, NULL },
110 { "PW4", NULL, "Zone 4 power", STYPE_OnOff, sSwitchGeneralSwitch, 5, NULL, NULL },
111 { "SLI", NULL, "Master selector", STYPE_Selector, sSwitchTypeSelector, 5, "SelectorStyle:1;LevelNames:Off;LevelOffHidden:true;LevelActions:00", input_names },
112 { "SLZ", NULL, "Zone 2 selector", STYPE_Selector, sSwitchTypeSelector, 5, "SelectorStyle:1;LevelNames:Off;LevelOffHidden:true;LevelActions:00", input_names },
113 { "SL2", NULL, "Zone 3 selector", STYPE_Selector, sSwitchTypeSelector, 5, "SelectorStyle:1;LevelNames:Off;LevelOffHidden:true;LevelActions:00", input_names },
114 { "SL3", NULL, "Zone 4 selector", STYPE_Selector, sSwitchTypeSelector, 5, "SelectorStyle:1;LevelNames:Off;LevelOffHidden:true;LevelActions:00", input_names },
115 { "HDO", NULL, "HDMI Output", STYPE_Selector, sSwitchTypeSelector, 5, "SelectorStyle:0;LevelNames:Off|Main|Sub|Main+Sub;LevelOffHidden:true;LevelActions:00|01|02|03" },
116 };
117
118 static struct {
119 const char *iscpCmd;
120 const char *name;
121 } text_types[] = {
122 { "NAL", "Album name" },
123 { "NAT", "Artist name" },
124 { "NTI", "Title name" },
125 { "NTM", "Playback time" },
126 { "NTR", "Track info" },
127 };
128
129 #define IS_END_CHAR(x) ((x) == 0x1a)
130
131
OnkyoAVTCP(const int ID,const std::string & IPAddress,const unsigned short usIPPort)132 OnkyoAVTCP::OnkyoAVTCP(const int ID, const std::string &IPAddress, const unsigned short usIPPort) :
133 m_szIPAddress(IPAddress)
134 {
135 m_HwdID=ID;
136 m_usIPPort=usIPPort;
137 m_retrycntr = RETRY_DELAY;
138 m_pPartialPkt = NULL;
139 m_PPktLen = 0;
140
141 // Ooops, changing Device ID was a mistake. Fix up migration for Main/Z2 power switches from
142 // the 3.8153 release. Don't change IDs again!
143 for (int i = ID_PWR; i < ID_PW3; i++)
144 m_sql.safe_query("UPDATE DeviceStatus SET DeviceID='%08X' WHERE (HardwareID==%d) AND (DeviceID=='%08X') AND (SwitchType==%d) AND (SubType==%d)",
145 i, m_HwdID, i - 2, switch_types[i].switchType, switch_types[i].subtype);
146 }
147
~OnkyoAVTCP(void)148 OnkyoAVTCP::~OnkyoAVTCP(void)
149 {
150 free(m_pPartialPkt);
151 }
152
StartHardware()153 bool OnkyoAVTCP::StartHardware()
154 {
155 RequestStart();
156
157 //force connect the next first time
158 m_retrycntr=RETRY_DELAY;
159 m_bIsStarted=true;
160
161 //Start worker thread
162 m_thread = std::make_shared<std::thread>(&OnkyoAVTCP::Do_Work, this);
163 SetThreadNameInt(m_thread->native_handle());
164 return (m_thread != nullptr);
165 }
166
StopHardware()167 bool OnkyoAVTCP::StopHardware()
168 {
169 if (m_thread)
170 {
171 RequestStop();
172 m_thread->join();
173 m_thread.reset();
174 }
175 m_bIsStarted=false;
176 return true;
177 }
178
OnConnect()179 void OnkyoAVTCP::OnConnect()
180 {
181 _log.Log(LOG_STATUS,"OnkyoAVTCP: connected to: %s:%d", m_szIPAddress.c_str(), m_usIPPort);
182 m_bIsStarted=true;
183
184 SendPacket("NRIQSTN");
185 sOnConnected(this);
186 }
187
OnDisconnect()188 void OnkyoAVTCP::OnDisconnect()
189 {
190 _log.Log(LOG_STATUS,"OnkyoAVTCP: disconnected");
191 }
192
Do_Work()193 void OnkyoAVTCP::Do_Work()
194 {
195 int sec_counter = 0;
196 connect(m_szIPAddress, m_usIPPort);
197 while (!IsStopRequested(1000))
198 {
199 sec_counter++;
200
201 if (sec_counter % 12 == 0) {
202 m_LastHeartbeat = mytime(NULL);
203 }
204 }
205 terminate();
206
207 _log.Log(LOG_STATUS,"OnkyoAVTCP: TCP/IP Worker stopped...");
208 }
209
OnData(const unsigned char * pData,size_t length)210 void OnkyoAVTCP::OnData(const unsigned char *pData, size_t length)
211 {
212 ParseData(pData,length);
213 }
214
OnError(const boost::system::error_code & error)215 void OnkyoAVTCP::OnError(const boost::system::error_code& error)
216 {
217 if (
218 (error == boost::asio::error::address_in_use) ||
219 (error == boost::asio::error::connection_refused) ||
220 (error == boost::asio::error::access_denied) ||
221 (error == boost::asio::error::host_unreachable) ||
222 (error == boost::asio::error::timed_out)
223 )
224 {
225 _log.Log(LOG_ERROR, "OnkyoAVTCP: Can not connect to: %s:%d", m_szIPAddress.c_str(), m_usIPPort);
226 }
227 else if (
228 (error == boost::asio::error::eof) ||
229 (error == boost::asio::error::connection_reset)
230 )
231 {
232 _log.Log(LOG_STATUS, "OnkyoAVTCP: Connection reset!");
233 }
234 else
235 _log.Log(LOG_ERROR, "OnkyoAVTCP: %s", error.message().c_str());
236 }
237
WriteToHardware(const char * pdata,const unsigned char)238 bool OnkyoAVTCP::WriteToHardware(const char *pdata, const unsigned char /*length*/)
239 {
240 if (!isConnected() || !pdata)
241 {
242 return false;
243 }
244 const tRBUF *pCmd = reinterpret_cast<const tRBUF *>(pdata);
245 unsigned char packettype = pCmd->ICMND.packettype;
246 std::string message = "";
247
248 if (packettype == pTypeGeneralSwitch) {
249 const _tGeneralSwitch *xcmd = reinterpret_cast<const _tGeneralSwitch*>(pdata);
250 int ID = xcmd->id;
251 int level = xcmd->level;
252 char buf[9];
253 if (ID >= ARRAY_SIZE(switch_types))
254 return false;
255 switch(xcmd->cmnd) {
256 case gswitch_sSetLevel:
257 if (switch_types[ID].subtype == sSwitchTypeSelector) {
258 std::vector<std::vector<std::string> > result;
259 result = m_sql.safe_query("SELECT Options FROM DeviceStatus WHERE (HardwareID==%d) AND (DeviceID=='%08X')", m_HwdID, ID);
260 if (result.empty()) {
261 _log.Log(LOG_ERROR, "OnkyoAVTCP: No device with ID %08X", ID);
262 return false;
263 }
264 std::string val = GetSelectorSwitchLevelAction(m_sql.BuildDeviceOptions(result[0][0]), level);
265 if (val.empty())
266 snprintf(buf, 6, "%s%02X", switch_types[ID].iscpCmd, level / 10);
267 else {
268 // Seriously. They *send* us the ID with lower case, and then refuse to accept it like that.
269 boost::to_upper(val);
270 snprintf(buf, 6, "%s%s", switch_types[ID].iscpCmd, val.c_str());
271 }
272 } else {
273 snprintf(buf, 6, "%s%02X", switch_types[ID].iscpCmd, level);
274 }
275 SendPacket(buf);
276 break;
277 case gswitch_sOn:
278 if (switch_types[ID].iscpMute)
279 sprintf(buf, "%s00", switch_types[ID].iscpMute);
280 else
281 sprintf(buf, "%s01", switch_types[ID].iscpCmd);
282 SendPacket(buf);
283 break;
284 case gswitch_sOff:
285 if (switch_types[ID].iscpMute)
286 sprintf(buf, "%s01", switch_types[ID].iscpMute);
287 else
288 sprintf(buf, "%s00", switch_types[ID].iscpCmd);
289 SendPacket(buf);
290 break;
291 }
292 }
293 return true;
294 }
295
SendPacket(const char * pdata)296 bool OnkyoAVTCP::SendPacket(const char *pdata)
297 {
298 if (!isConnected() || !pdata)
299 {
300 return false;
301 }
302
303 _log.Log(LOG_NORM, "OnkyoAVTCP: Send %s", pdata);
304 size_t length = strlen(pdata);
305
306 struct eiscp_packet *pPkt = (struct eiscp_packet *)malloc(sizeof(*pPkt) + length);
307 if (!pPkt)
308 {
309 return false;
310 }
311
312 pPkt->magic = htonl(0x49534350); // "ISCP"
313 pPkt->hdr_size = htonl(16);
314 pPkt->data_size = htonl(length + 3);
315 pPkt->version = 1;
316 memset(pPkt->reserved, 0, sizeof(pPkt->reserved));
317 pPkt->start = '!';
318 pPkt->dest = '1';
319 memcpy(pPkt->message, pdata, length);
320 pPkt->message[length] = 0x0d;
321 pPkt->message[length + 1] = 0x0a;
322
323 write((unsigned char *)pPkt, length + sizeof(*pPkt));
324 free(pPkt);
325 return true;
326 }
327
SendPacket(const char * pCmd,const char * pArg)328 bool OnkyoAVTCP::SendPacket(const char *pCmd, const char *pArg)
329 {
330 std::string str(pCmd);
331 str += pArg;
332
333 return SendPacket(str.c_str());
334 }
335
ReceiveSwitchMsg(const char * pData,int Len,bool muting,int ID)336 void OnkyoAVTCP::ReceiveSwitchMsg(const char *pData, int Len, bool muting, int ID)
337 {
338 char *endp;
339 int level = strtol(pData + 3, &endp, 16);
340 int action = gswitch_sSetLevel;
341
342 if (!IS_END_CHAR(*endp))
343 return;
344
345 if (muting)
346 action = level ? gswitch_sOff : gswitch_sOn;
347 else if (switch_types[ID].switchType == STYPE_OnOff)
348 action = level ? gswitch_sOn : gswitch_sOff;
349
350
351 std::vector<std::vector<std::string> > result;
352 result = m_sql.safe_query("SELECT Name,nValue,sValue,Options,ID FROM DeviceStatus WHERE (HardwareID==%d) AND (DeviceID=='%08X') AND (Unit == %d)",
353 m_HwdID, ID, 0);
354 if (result.empty()) {
355 EnsureSwitchDevice(ID, NULL);
356 result = m_sql.safe_query("SELECT Name,nValue,sValue,Options,ID FROM DeviceStatus WHERE (HardwareID==%d) AND (DeviceID=='%08X') AND (Unit == %d)",
357 m_HwdID, ID, 0);
358 if (result.empty())
359 return;
360 }
361
362 if (switch_types[ID].subtype == sSwitchTypeSelector) {
363 std::map<std::string, std::string> options = m_sql.BuildDeviceOptions(result[0][3]);
364 std::map<std::string, std::string>::const_iterator itt = options.find("LevelActions");
365 if (itt == options.end())
366 return;
367 std::string sOptions = itt->second;
368 std::vector<std::string> strarray;
369 boost::split(strarray, sOptions, boost::is_any_of("|"), boost::token_compress_off);
370 std::vector<std::string>::iterator itt2;
371 int i = 0;
372 for (itt2 = strarray.begin(); itt2 != strarray.end(); ++itt2) {
373 if (strtoul(itt2->c_str(), NULL, 16) == (unsigned long)level)
374 break;
375 i += 10;
376 }
377 if (itt2 == strarray.end()) {
378 // Add previously unknown value to a selector
379 std::string str(reinterpret_cast<const char*>(pData + 3), Len - 4);
380 std::string level_name = str;
381 struct selector_name *n = switch_types[ID].default_names;
382 while (n && n->name) {
383 if (n->val == level) {
384 level_name = n->name;
385 break;
386 }
387 n++;
388 }
389 options["LevelActions"] = options["LevelActions"] + "|" + str;
390 options["LevelNames"] = options["LevelNames"] + "|" + level_name;
391 m_sql.SetDeviceOptions(atoi(result[0][4].c_str()), options);
392 }
393 level = i;
394 }
395 if (!result.empty() && action == gswitch_sSetLevel) {
396 //check if we have a change, if not do not update it
397 int nvalue = atoi(result[0][1].c_str());
398 if ((!level) && (nvalue == gswitch_sOff))
399 return;
400 if ((level && (nvalue != gswitch_sOff))) {
401 //Check Level
402 int slevel = atoi(result[0][2].c_str());
403 if (slevel == level)
404 return;
405 }
406 }
407
408 _tGeneralSwitch gswitch;
409 gswitch.subtype = switch_types[ID].subtype;
410 gswitch.id = ID;
411 gswitch.unitcode = 0;
412 gswitch.cmnd = (uint8_t)action;
413 gswitch.level = (uint8_t)level;
414 gswitch.battery_level = 255;
415 gswitch.rssi = 12;
416 gswitch.seqnbr = 0;
417
418 sDecodeRXMessage(this, (const unsigned char *)&gswitch, switch_types[ID].name, 255);
419 }
420
EnsureSwitchDevice(int ID,const char * options)421 void OnkyoAVTCP::EnsureSwitchDevice(int ID, const char *options)
422 {
423 std::vector<std::vector<std::string> > result;
424 std::string options_str;
425 result = m_sql.safe_query("SELECT ID FROM DeviceStatus WHERE (HardwareID==%d) AND (DeviceID=='%08X')", m_HwdID, ID);
426 if (result.empty()) {
427 if (!options && switch_types[ID].options) {
428 options_str = m_sql.FormatDeviceOptions(m_sql.BuildDeviceOptions(switch_types[ID].options, false));
429 options = options_str.c_str();
430 _log.Log(LOG_ERROR, "Options from '%s': '%s'\n", switch_types[ID].options, options);
431 }
432 m_sql.safe_query(
433 "INSERT INTO DeviceStatus (HardwareID, DeviceID, Unit, Type, SubType, SwitchType, Used, SignalLevel, BatteryLevel, Name, nValue, sValue, CustomImage, Options) "
434 "VALUES (%d, '%08X', %d, %d, %d, %d, %d, 12, 255, '%q', 0, '%q', %d, '%q')",
435 m_HwdID, ID, 0, pTypeGeneralSwitch, switch_types[ID].subtype, switch_types[ID].switchType, 1, switch_types[ID].name, "", switch_types[ID].customImage, options?options:"");
436
437 }
438 }
439
440
BuildSelectorOptions(const std::string & names,const std::string & ids)441 std::string OnkyoAVTCP::BuildSelectorOptions(const std::string & names, const std::string & ids)
442 {
443 std::map<std::string, std::string> optionsMap;
444 optionsMap.insert(std::pair<std::string, std::string>("LevelOffHidden", "true"));
445 optionsMap.insert(std::pair<std::string, std::string>("SelectorStyle", "1"));
446 optionsMap.insert(std::pair<std::string, std::string>("LevelNames", names));
447 optionsMap.insert(std::pair<std::string, std::string>("LevelActions", ids));
448 return m_sql.FormatDeviceOptions(optionsMap);
449 }
450
ReceiveXML(const char * pData,int Len)451 bool OnkyoAVTCP::ReceiveXML(const char *pData, int Len)
452 {
453 TiXmlDocument doc;
454
455 ((char *)pData)[Len - 1] = 0;
456 pData += 3;
457
458 doc.Parse(pData);
459 if (doc.Error()) {
460 _log.Log(LOG_ERROR, "OnkyoAVTCP: Failed to parse NRIQSTN result: %s", doc.ErrorDesc());
461 // XX: Fallback, add defaults anyway? */
462 return false;
463 }
464 TiXmlElement *pResponseNode = doc.FirstChildElement("response");
465 if (!pResponseNode) {
466 _log.Log(LOG_ERROR, "No <response> element in NRIQSTN result");
467 return false;
468 }
469 TiXmlElement *pDeviceNode = pResponseNode->FirstChildElement("device");
470 if (!pDeviceNode) {
471 _log.Log(LOG_ERROR, "No <device> element in NRIQSTN result");
472 return false;
473 }
474 std::string InputNames[4], InputIDs[4];
475 TiXmlElement *pSelectorlistNode = pDeviceNode->FirstChildElement("selectorlist");
476 if (pSelectorlistNode) {
477 TiXmlElement *pSelector;
478 for ( pSelector = pSelectorlistNode->FirstChildElement(); pSelector;
479 pSelector = pSelector->NextSiblingElement() ) {
480 const char *id = pSelector->Attribute("id");
481 const char *name = pSelector->Attribute("name");
482 const char *zone = pSelector->Attribute("zone");
483 int value;
484 if (!id || !name || !zone ||
485 pSelector->QueryIntAttribute("value", &value) != TIXML_SUCCESS ||
486 !value)
487 continue;
488
489 char *escaped_name = strdup(name);
490 int nlen = strlen(name);
491 while (nlen) {
492 if (escaped_name[nlen - 1] == ' ' && escaped_name[nlen] == 0)
493 escaped_name[nlen - 1] = 0;
494 else if (escaped_name[nlen - 1] == '|')
495 escaped_name[nlen - 1] = '!';
496 nlen--;
497 }
498 if (!escaped_name[0])
499 continue;
500
501 for (int i = 0; i < 3; i++) {
502 int zone_nr = strtoul(zone, NULL, 16);
503 if (zone_nr & (1 << i)) {
504 if (InputNames[i].empty())
505 InputNames[i] = "Off";
506 InputNames[i] += '|';
507 InputNames[i] += escaped_name;
508 if (InputIDs[i].empty())
509 InputIDs[i] = "Off";
510 InputIDs[i] += '|';
511 InputIDs[i] += id;
512 }
513 }
514 free(escaped_name);
515 }
516 }
517 TiXmlElement *pZonelistNode = pDeviceNode->FirstChildElement("zonelist");
518 if (pZonelistNode) {
519 TiXmlElement *pZone;
520 for ( pZone = pZonelistNode->FirstChildElement(); pZone;
521 pZone = pZone->NextSiblingElement() ) {
522 int id, value, volmax, volstep;
523 const char *name = pZone->Attribute("name");
524
525 if (!name)
526 continue;
527
528 if (pZone->QueryIntAttribute("value", &value) != TIXML_SUCCESS ||
529 pZone->QueryIntAttribute("id", &id) != TIXML_SUCCESS ||
530 pZone->QueryIntAttribute("volmax", &volmax) != TIXML_SUCCESS ||
531 pZone->QueryIntAttribute("volstep", &volstep) != TIXML_SUCCESS)
532 continue;
533
534 if (!value)
535 continue;
536
537 if (id < 1 || id > 4)
538 continue;
539
540 // Create the input selector immediately, with the known options.
541 std::string options = BuildSelectorOptions(InputNames[id - 1], InputIDs[id - 1]);;
542 EnsureSwitchDevice(ID_SLI + id - 1, options.c_str());
543
544 // Send queries for it, and the power and volume for this zone.
545 SendPacket(switch_types[ID_SLI + id - 1].iscpCmd, "QSTN");
546 SendPacket(switch_types[ID_PWR + id - 1].iscpCmd, "QSTN");
547 SendPacket(switch_types[ID_MVL + id - 1].iscpCmd, "QSTN");
548 }
549 }
550
551 SendPacket("HDOQSTN");
552 return true;
553 }
554
ReceiveMessage(const char * pData,int Len)555 void OnkyoAVTCP::ReceiveMessage(const char *pData, int Len)
556 {
557 while (Len && (pData[Len - 1] == '\r' || pData[Len - 1] == '\n'))
558 Len--;
559
560 if (Len < 4 || !IS_END_CHAR(pData[Len-1]))
561 return;
562
563 // Special-case the startup XML data
564 if (!memcmp(pData, "NRI", 3)) {
565 ReceiveXML(pData, Len);
566 return;
567 }
568 _log.Log(LOG_NORM, "OnkyoAVTCP: Packet received: %d %.*s", Len, Len-1, pData);
569
570 int i;
571 for (i = 0; i < ARRAY_SIZE(text_types); i++) {
572 if (!memcmp(pData, text_types[i].iscpCmd, 3)) {
573 std::string str(reinterpret_cast<const char*>(pData + 3), Len - 4);
574 SendTextSensor(1, i, 255, str, text_types[i].name);
575 return;
576 }
577 }
578
579 for (i = 0; i < ARRAY_SIZE(switch_types); i++) {
580 if (!memcmp(pData, switch_types[i].iscpCmd, 3)) {
581 ReceiveSwitchMsg(pData, Len, false, i);
582 return;
583 } else if (switch_types[i].iscpMute && !memcmp(pData, switch_types[i].iscpMute, 3)) {
584 ReceiveSwitchMsg(pData, Len, true, i);
585 return;
586 }
587 }
588 }
589
ParseData(const unsigned char * pData,int Len)590 void OnkyoAVTCP::ParseData(const unsigned char *pData, int Len)
591 {
592 if (m_pPartialPkt) {
593 unsigned char *new_data = (unsigned char *)realloc(m_pPartialPkt, m_PPktLen + Len);
594 if (!new_data) {
595 free(m_pPartialPkt);
596 m_pPartialPkt = NULL;
597 m_PPktLen = 0;
598 _log.Log(LOG_ERROR, "OnkyoAVTCP: Failed to prepend previous data");
599 // We'll attempt to resync
600 } else {
601 memcpy(new_data + m_PPktLen, pData, Len);
602 m_pPartialPkt = new_data;
603 Len += m_PPktLen;
604 pData = new_data;
605 }
606 }
607 while (Len >= 18) {
608 const struct eiscp_packet *pkt = (const struct eiscp_packet *)pData;
609 if (pkt->magic != htonl(0x49534350) || // "ISCP"
610 pkt->hdr_size != htonl(16) || pkt->version != 1) {
611 Len--;
612 pData++;
613 continue;
614 }
615 int data_size = static_cast<int>(ntohl(pkt->data_size));
616 if (Len < 16 + data_size)
617 break;
618
619 ReceiveMessage(pkt->message, data_size - 2);
620 Len -= 16 + data_size;
621 pData += 16 + data_size;
622 }
623 unsigned char *new_partial = NULL;
624 if (Len) {
625 if (pData == m_pPartialPkt) {
626 m_PPktLen = Len;
627 return;
628 }
629 new_partial = (unsigned char *)malloc(Len);
630 if (new_partial)
631 memcpy(new_partial, pData, Len);
632 else Len = 0;
633 }
634 free(m_pPartialPkt);
635 m_PPktLen = Len;
636 m_pPartialPkt = new_partial;
637 }
638
CustomCommand(const uint64_t,const std::string & sCommand)639 bool OnkyoAVTCP::CustomCommand(const uint64_t /*idx*/, const std::string &sCommand)
640 {
641 return SendPacket(sCommand.c_str());
642 }
643
644 //Webserver helpers
645 namespace http {
646 namespace server {
Cmd_OnkyoEiscpCommand(WebEmSession & session,const request & req,Json::Value & root)647 void CWebServer::Cmd_OnkyoEiscpCommand(WebEmSession & session, const request& req, Json::Value &root)
648 {
649 if (session.rights != 2)
650 {
651 session.reply_status = reply::forbidden;
652 return; //Only admin user allowed
653 }
654
655 std::string sIdx = request::findValue(&req, "idx");
656 std::string sAction = request::findValue(&req, "action");
657 if (sIdx.empty())
658 return;
659 //int idx = atoi(sIdx.c_str());
660
661 std::vector<std::vector<std::string> > result;
662 result = m_sql.safe_query("SELECT DS.SwitchType, DS.DeviceID, H.Type, H.ID FROM DeviceStatus DS, Hardware H WHERE (DS.ID=='%q') AND (DS.HardwareID == H.ID)", sIdx.c_str());
663
664 root["status"] = "ERR";
665 if (result.size() == 1)
666 {
667 _eHardwareTypes hType = (_eHardwareTypes)atoi(result[0][2].c_str());
668 switch (hType) {
669 // We allow raw EISCP commands to be sent on *any* of the logical devices
670 // associated with the hardware.
671 case HTYPE_OnkyoAVTCP:
672 CDomoticzHardwareBase *pBaseHardware = m_mainworker.GetHardwareByIDType(result[0][3].c_str(), HTYPE_OnkyoAVTCP);
673 if (pBaseHardware == NULL)
674 return;
675 OnkyoAVTCP *pOnkyoAVTCP = reinterpret_cast<OnkyoAVTCP*>(pBaseHardware);
676
677 pOnkyoAVTCP->SendPacket(sAction.c_str());
678 root["status"] = "OK";
679 root["title"] = "OnkyoEiscpCommand";
680 break;
681 }
682 }
683 }
684
685 }
686 }
687