xdrv_05_irremote.ino 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702
  1. /*
  2. xdrv_05_irremote.ino - infra red support for Sonoff-Tasmota
  3. Copyright (C) 2018 Heiko Krupp, Lazar Obradovic and Theo Arends
  4. This program is free software: you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation, either version 3 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program. If not, see <http://www.gnu.org/licenses/>.
  14. */
  15. #ifdef USE_IR_REMOTE
  16. /*********************************************************************************************\
  17. * IR Remote send and receive using IRremoteESP8266 library
  18. \*********************************************************************************************/
  19. #define XDRV_05 5
  20. #include <IRremoteESP8266.h>
  21. enum IrRemoteCommands { CMND_IRSEND, CMND_IRHVAC };
  22. const char kIrRemoteCommands[] PROGMEM = D_CMND_IRSEND "|" D_CMND_IRHVAC ;
  23. // Based on IRremoteESP8266.h enum decode_type_t
  24. const char kIrRemoteProtocols[] PROGMEM =
  25. "UNKNOWN|RC5|RC6|NEC|SONY|PANASONIC|JVC|SAMSUNG|WHYNTER|AIWA_RC_T501|LG|SANYO|MITSUBISHI|DISH|SHARP";
  26. #ifdef USE_IR_HVAC
  27. #include <ir_Mitsubishi.h>
  28. #include <ir_Fujitsu.h>
  29. enum IrHvacVendors { VNDR_TOSHIBA, VNDR_MITSUBISHI, VNDR_LG, VNDR_FUJITSU };
  30. const char kIrHvacVendors[] PROGMEM = "Toshiba|Mitsubishi|LG|Fujitsu" ;
  31. // HVAC TOSHIBA_
  32. #define HVAC_TOSHIBA_HDR_MARK 4400
  33. #define HVAC_TOSHIBA_HDR_SPACE 4300
  34. #define HVAC_TOSHIBA_BIT_MARK 543
  35. #define HVAC_TOSHIBA_ONE_SPACE 1623
  36. #define HVAC_MISTUBISHI_ZERO_SPACE 472
  37. #define HVAC_TOSHIBA_RPT_MARK 440
  38. #define HVAC_TOSHIBA_RPT_SPACE 7048 // Above original iremote limit
  39. #define HVAC_TOSHIBA_DATALEN 9
  40. // HVAC LG
  41. #define HVAC_LG_DATALEN 7
  42. IRMitsubishiAC *mitsubir = NULL;
  43. const char kFanSpeedOptions[] = "A12345S";
  44. const char kHvacModeOptions[] = "HDCA";
  45. #endif
  46. /*********************************************************************************************\
  47. * IR Send
  48. \*********************************************************************************************/
  49. #include <IRsend.h>
  50. IRsend *irsend = NULL;
  51. void IrSendInit(void)
  52. {
  53. irsend = new IRsend(pin[GPIO_IRSEND]); // an IR led is at GPIO_IRSEND
  54. irsend->begin();
  55. #ifdef USE_IR_HVAC
  56. mitsubir = new IRMitsubishiAC(pin[GPIO_IRSEND]);
  57. #endif //USE_IR_HVAC
  58. }
  59. #ifdef USE_IR_RECEIVE
  60. /*********************************************************************************************\
  61. * IR Receive
  62. \*********************************************************************************************/
  63. #define IR_RCV_SAVE_BUFFER 0 // 0 = do not use buffer, 1 = use buffer for decoding
  64. #define IR_TIME_AVOID_DUPLICATE 500 // Milliseconds
  65. #include <IRrecv.h>
  66. IRrecv *irrecv = NULL;
  67. unsigned long ir_lasttime = 0;
  68. void IrReceiveInit(void)
  69. {
  70. // an IR led is at GPIO_IRRECV
  71. irrecv = new IRrecv(pin[GPIO_IRRECV], IR_RCV_BUFFER_SIZE, IR_RCV_TIMEOUT, IR_RCV_SAVE_BUFFER);
  72. irrecv->setUnknownThreshold(IR_RCV_MIN_UNKNOWN_SIZE);
  73. irrecv->enableIRIn(); // Start the receiver
  74. // AddLog_P(LOG_LEVEL_DEBUG, PSTR("IrReceive initialized"));
  75. }
  76. void IrReceiveCheck(void)
  77. {
  78. char sirtype[14]; // Max is AIWA_RC_T501
  79. char stemp[16];
  80. int8_t iridx = 0;
  81. decode_results results;
  82. if (irrecv->decode(&results)) {
  83. snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_IRR "RawLen %d, Overflow %d, Bits %d, Value %08X, Decode %d"),
  84. results.rawlen, results.overflow, results.bits, results.value, results.decode_type);
  85. AddLog(LOG_LEVEL_DEBUG);
  86. unsigned long now = millis();
  87. // if ((now - ir_lasttime > IR_TIME_AVOID_DUPLICATE) && (UNKNOWN != results.decode_type) && (results.bits > 0)) {
  88. if (now - ir_lasttime > IR_TIME_AVOID_DUPLICATE) {
  89. ir_lasttime = now;
  90. iridx = results.decode_type;
  91. if ((iridx < 0) || (iridx > 14)) {
  92. iridx = 0; // UNKNOWN
  93. }
  94. if (Settings.flag.ir_receive_decimal) {
  95. snprintf_P(stemp, sizeof(stemp), PSTR("%u"), (uint32_t)results.value);
  96. } else {
  97. snprintf_P(stemp, sizeof(stemp), PSTR("\"%lX\""), (uint32_t)results.value);
  98. }
  99. snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_JSON_IRRECEIVED "\":{\"" D_JSON_IR_PROTOCOL "\":\"%s\",\"" D_JSON_IR_BITS "\":%d,\"" D_JSON_IR_DATA "\":%s"),
  100. GetTextIndexed(sirtype, sizeof(sirtype), iridx, kIrRemoteProtocols), results.bits, stemp);
  101. if (Settings.flag3.receive_raw) {
  102. snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"" D_JSON_IR_RAWDATA "\":["), mqtt_data);
  103. uint16_t i;
  104. for (i = 1; i < results.rawlen; i++) {
  105. if (i > 1) { snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,"), mqtt_data); }
  106. uint32_t usecs;
  107. for (usecs = results.rawbuf[i] * kRawTick; usecs > UINT16_MAX; usecs -= UINT16_MAX) {
  108. snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s%d,0,"), mqtt_data, UINT16_MAX);
  109. }
  110. snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s%d"), mqtt_data, usecs);
  111. if (strlen(mqtt_data) > sizeof(mqtt_data) - 40) { break; } // Quit if char string becomes too long
  112. }
  113. uint16_t extended_length = results.rawlen - 1;
  114. for (uint16_t j = 0; j < results.rawlen - 1; j++) {
  115. uint32_t usecs = results.rawbuf[j] * kRawTick;
  116. // Add two extra entries for multiple larger than UINT16_MAX it is.
  117. extended_length += (usecs / (UINT16_MAX + 1)) * 2;
  118. }
  119. snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s],\"" D_JSON_IR_RAWDATA "Info\":[%d,%d,%d]"), mqtt_data, extended_length, i -1, results.overflow);
  120. }
  121. snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s}}"), mqtt_data);
  122. MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_IRRECEIVED));
  123. if (iridx) {
  124. XdrvRulesProcess();
  125. #ifdef USE_DOMOTICZ
  126. unsigned long value = results.value | (iridx << 28); // [Protocol:4, Data:28]
  127. DomoticzSensor(DZ_COUNT, value); // Send data as Domoticz Counter value
  128. #endif // USE_DOMOTICZ
  129. }
  130. }
  131. irrecv->resume();
  132. }
  133. }
  134. #endif // USE_IR_RECEIVE
  135. #ifdef USE_IR_HVAC
  136. /********************************************************************************************* \
  137. * IR Heating, Ventilation and Air Conditioning using IRMitsubishiAC library
  138. \*********************************************************************************************/
  139. /*******************
  140. TOSHIBA
  141. ********************/
  142. boolean IrHvacToshiba(const char *HVAC_Mode, const char *HVAC_FanMode, boolean HVAC_Power, int HVAC_Temp)
  143. {
  144. uint16_t rawdata[2 + 2 * 8 * HVAC_TOSHIBA_DATALEN + 2];
  145. byte data[HVAC_TOSHIBA_DATALEN] = {0xF2, 0x0D, 0x03, 0xFC, 0x01, 0x00, 0x00, 0x00, 0x00};
  146. char *p;
  147. uint8_t mode;
  148. if (HVAC_Mode == NULL) {
  149. p = (char *)kHvacModeOptions; // default HVAC_HOT
  150. }
  151. else {
  152. p = strchr(kHvacModeOptions, toupper(HVAC_Mode[0]));
  153. }
  154. if (!p) {
  155. return true;
  156. }
  157. data[6] = (p - kHvacModeOptions) ^ 0x03; // HOT = 0x03, DRY = 0x02, COOL = 0x01, AUTO = 0x00
  158. if (!HVAC_Power) {
  159. data[6] = (byte)0x07; // Turn OFF HVAC
  160. }
  161. if (HVAC_FanMode == NULL) {
  162. p = (char *)kFanSpeedOptions; // default FAN_SPEED_AUTO
  163. }
  164. else {
  165. p = strchr(kFanSpeedOptions, toupper(HVAC_FanMode[0]));
  166. }
  167. if (!p) {
  168. return true;
  169. }
  170. mode = p - kFanSpeedOptions + 1;
  171. if ((1 == mode) || (7 == mode)) {
  172. mode = 0;
  173. }
  174. mode = mode << 5; // AUTO = 0x00, SPEED = 0x40, 0x60, 0x80, 0xA0, 0xC0, SILENT = 0x00
  175. data[6] = data[6] | mode;
  176. byte Temp;
  177. if (HVAC_Temp > 30) {
  178. Temp = 30;
  179. }
  180. else if (HVAC_Temp < 17) {
  181. Temp = 17;
  182. }
  183. else {
  184. Temp = HVAC_Temp;
  185. }
  186. data[5] = (byte)(Temp - 17) << 4;
  187. data[HVAC_TOSHIBA_DATALEN - 1] = 0;
  188. for (int x = 0; x < HVAC_TOSHIBA_DATALEN - 1; x++) {
  189. data[HVAC_TOSHIBA_DATALEN - 1] = (byte)data[x] ^ data[HVAC_TOSHIBA_DATALEN - 1]; // CRC is a simple bits addition
  190. }
  191. int i = 0;
  192. byte mask = 1;
  193. //header
  194. rawdata[i++] = HVAC_TOSHIBA_HDR_MARK;
  195. rawdata[i++] = HVAC_TOSHIBA_HDR_SPACE;
  196. //data
  197. for (int b = 0; b < HVAC_TOSHIBA_DATALEN; b++) {
  198. for (mask = B10000000; mask > 0; mask >>= 1) { //iterate through bit mask
  199. if (data[b] & mask) { // Bit ONE
  200. rawdata[i++] = HVAC_TOSHIBA_BIT_MARK;
  201. rawdata[i++] = HVAC_TOSHIBA_ONE_SPACE;
  202. }
  203. else { // Bit ZERO
  204. rawdata[i++] = HVAC_TOSHIBA_BIT_MARK;
  205. rawdata[i++] = HVAC_MISTUBISHI_ZERO_SPACE;
  206. }
  207. }
  208. }
  209. //trailer
  210. rawdata[i++] = HVAC_TOSHIBA_RPT_MARK;
  211. rawdata[i++] = HVAC_TOSHIBA_RPT_SPACE;
  212. noInterrupts();
  213. irsend->sendRaw(rawdata, i, 38);
  214. irsend->sendRaw(rawdata, i, 38);
  215. interrupts();
  216. return false;
  217. }
  218. /*******************
  219. MITSUBISHI
  220. ********************/
  221. boolean IrHvacMitsubishi(const char *HVAC_Mode, const char *HVAC_FanMode, boolean HVAC_Power, int HVAC_Temp)
  222. {
  223. char *p;
  224. uint8_t mode;
  225. mitsubir->stateReset();
  226. if (HVAC_Mode == NULL) {
  227. p = (char *)kHvacModeOptions; // default HVAC_HOT
  228. }
  229. else {
  230. p = strchr(kHvacModeOptions, toupper(HVAC_Mode[0]));
  231. }
  232. if (!p) {
  233. return true;
  234. }
  235. mode = (p - kHvacModeOptions + 1) << 3; // HOT = 0x08, DRY = 0x10, COOL = 0x18, AUTO = 0x20
  236. mitsubir->setMode(mode);
  237. mitsubir->setPower(HVAC_Power);
  238. if (HVAC_FanMode == NULL) {
  239. p = (char *)kFanSpeedOptions; // default FAN_SPEED_AUTO
  240. }
  241. else {
  242. p = strchr(kFanSpeedOptions, toupper(HVAC_FanMode[0]));
  243. }
  244. if (!p) {
  245. return true;
  246. }
  247. mode = p - kFanSpeedOptions; // AUTO = 0, SPEED = 1 .. 5, SILENT = 6
  248. mitsubir->setFan(mode);
  249. mitsubir->setTemp(HVAC_Temp);
  250. mitsubir->setVane(MITSUBISHI_AC_VANE_AUTO);
  251. mitsubir->send();
  252. // snprintf_P(log_data, sizeof(log_data), PSTR("IRHVAC: Mitsubishi Power %d, Mode %d, FanSpeed %d, Temp %d, VaneMode %d"),
  253. // mitsubir->getPower(), mitsubir->getMode(), mitsubir->getFan(), mitsubir->getTemp(), mitsubir->getVane());
  254. // AddLog(LOG_LEVEL_DEBUG);
  255. return false;
  256. }
  257. /*******************
  258. LG
  259. ********************/
  260. boolean IrHvacLG(const char *HVAC_Mode, const char *HVAC_FanMode, boolean HVAC_Power, int HVAC_Temp)
  261. {
  262. uint32_t LG_Code;
  263. byte data[HVAC_LG_DATALEN];
  264. static boolean hvacOn = false;
  265. char *p;
  266. uint8_t mode;
  267. byte Temp;
  268. // Constant data
  269. data[0] = 0x08;
  270. data[1] = 0x08;
  271. data[2] = 0x00;
  272. if (!HVAC_Power) {
  273. data[2] = (byte)0x0C; // Turn OFF HVAC, code 0x88C0051
  274. data[3] = (byte)0x00;
  275. data[4] = (byte)0x00;
  276. data[5] = (byte)0x05;
  277. data[6] = (byte)0x01;
  278. hvacOn = false;
  279. }
  280. else {
  281. // Set code for HVAC Mode - data[3]
  282. if (HVAC_Mode == NULL) {
  283. p = (char *)kHvacModeOptions; // default HVAC_HOT
  284. }
  285. else {
  286. p = strchr(kHvacModeOptions, toupper(HVAC_Mode[0]));
  287. }
  288. if (!p) {
  289. return true;
  290. }
  291. mode = (p - kHvacModeOptions) ^ 0x03; // HOT = 0x03, DRY = 0x02, COOL = 0x01, AUTO = 0x00
  292. switch (mode) {
  293. case 0: // AUTO
  294. data[3] = 11;
  295. break;
  296. case 1: // COOL
  297. data[3] = 8;
  298. break;
  299. case 2: // DRY
  300. data[3] = 9;
  301. break;
  302. case 3: // HOT
  303. data[3] = 12;
  304. break;
  305. }
  306. if (!hvacOn) {
  307. data[3] = data[3] & 7; // reset bit3
  308. hvacOn = true;
  309. }
  310. // snprintf_P(log_data, sizeof(log_data), PSTR("IRHVAC: HvacMode %s, ModeVal %d, Code %d"), p, mode, data[3]);
  311. // AddLog(LOG_LEVEL_DEBUG);
  312. // Set code for HVAC temperature - data[4]
  313. if (HVAC_Temp > 30) {
  314. Temp = 30;
  315. }
  316. else if (HVAC_Temp < 18) {
  317. Temp = 18;
  318. }
  319. else {
  320. Temp = HVAC_Temp;
  321. }
  322. data[4] = (byte)(Temp - 15);
  323. // Set code for HVAC fan mode - data[5]
  324. if (HVAC_FanMode == NULL) {
  325. p = (char *)kFanSpeedOptions; // default FAN_SPEED_AUTO
  326. }
  327. else {
  328. p = strchr(kFanSpeedOptions, toupper(HVAC_FanMode[0]));
  329. }
  330. if (!p) {
  331. return true;
  332. }
  333. mode = p - kFanSpeedOptions;
  334. if ((mode == 0) || (mode > 3)) {
  335. data[5] = 5; // Auto = 0x05
  336. }
  337. else {
  338. data[5] = (mode * 2) - 2; // Low = 0x00, Mid = 0x02, High = 0x04
  339. }
  340. // snprintf_P(log_data, sizeof(log_data), PSTR("IRHVAC: FanMode %s, ModeVal %d, Code %d"), p, mode, data[5]);
  341. // AddLog(LOG_LEVEL_DEBUG);
  342. // Set CRC code - data[6]
  343. data[6] = (data[3] + data[4] + data[5]) & 0x0f; // CRC
  344. }
  345. // Build LG IR code
  346. LG_Code = data[0] << 4;
  347. for (int i = 1; i < 6; i++) {
  348. LG_Code = (LG_Code + data[i]) << 4;
  349. }
  350. LG_Code = LG_Code + data[6];
  351. // snprintf_P(log_data, sizeof(log_data), PSTR("IRHVAC: LG_Code %d"), LG_Code);
  352. // AddLog(LOG_LEVEL_DEBUG);
  353. // Send LG IR Code
  354. noInterrupts();
  355. irsend->sendLG(LG_Code, 28);
  356. interrupts();
  357. return false;
  358. }
  359. /*******************
  360. Fujitsu
  361. ********************/
  362. boolean IrHvacFujitsu(const char *HVAC_Mode, const char *HVAC_FanMode, boolean HVAC_Power, int HVAC_Temp)
  363. {
  364. const char kFujitsuHvacModeOptions[] = "HDCAF";
  365. // snprintf_P(log_data, sizeof(log_data), PSTR("FUJITSU: mode:%s, fan:%s, power:%u, temp:%u"), HVAC_Mode, HVAC_FanMode, HVAC_Power, HVAC_Temp);
  366. // AddLog(LOG_LEVEL_DEBUG);
  367. IRFujitsuAC ac(pin[GPIO_IRSEND]);
  368. if (0 == HVAC_Power) {
  369. ac.off();
  370. ac.send();
  371. return false;
  372. }
  373. byte modes[5] = {FUJITSU_AC_MODE_HEAT, FUJITSU_AC_MODE_DRY, FUJITSU_AC_MODE_COOL, FUJITSU_AC_MODE_AUTO, FUJITSU_AC_MODE_FAN};
  374. byte fanModes[7] = {FUJITSU_AC_FAN_AUTO, FUJITSU_AC_FAN_LOW, FUJITSU_AC_FAN_MED, FUJITSU_AC_FAN_HIGH, FUJITSU_AC_FAN_HIGH, FUJITSU_AC_FAN_HIGH, FUJITSU_AC_FAN_QUIET};
  375. ac.setCmd(FUJITSU_AC_CMD_TURN_ON);
  376. ac.setSwing(FUJITSU_AC_SWING_VERT);
  377. char *p;
  378. if (NULL == HVAC_Mode) {
  379. p = (char *)kFujitsuHvacModeOptions;
  380. }
  381. else {
  382. p = strchr(kFujitsuHvacModeOptions, toupper(HVAC_Mode[0]));
  383. }
  384. if (!p) {
  385. return true;
  386. }
  387. ac.setMode(modes[p - kFujitsuHvacModeOptions]);
  388. if (HVAC_FanMode == NULL) {
  389. p = (char *)kFanSpeedOptions; // default FAN_SPEED_AUTO
  390. }
  391. else {
  392. p = strchr(kFanSpeedOptions, toupper(HVAC_FanMode[0]));
  393. }
  394. if (!p) {
  395. return true;
  396. }
  397. ac.setFanSpeed(fanModes[p - kFanSpeedOptions]);
  398. ac.setTemp(HVAC_Temp);
  399. ac.send();
  400. return false;
  401. }
  402. #endif // USE_IR_HVAC
  403. /*********************************************************************************************\
  404. * Commands
  405. \*********************************************************************************************/
  406. /*
  407. * ArduinoJSON entry used to calculate jsonBuf: JSON_OBJECT_SIZE(3) + 40 = 96
  408. IRsend:
  409. { "protocol": "SAMSUNG", "bits": 32, "data": 551502015 }
  410. IRhvac:
  411. { "Vendor": "<Toshiba|Mitsubishi>", "Power": <0|1>, "Mode": "<Hot|Cold|Dry|Auto>", "FanSpeed": "<1|2|3|4|5|Auto|Silence>", "Temp": <17..30> }
  412. */
  413. boolean IrSendCommand(void)
  414. {
  415. char command [CMDSZ];
  416. boolean serviced = true;
  417. boolean error = false;
  418. int command_code = GetCommandCode(command, sizeof(command), XdrvMailbox.topic, kIrRemoteCommands);
  419. if (-1 == command_code) {
  420. serviced = false; // Unknown command
  421. }
  422. else if (CMND_IRSEND == command_code) {
  423. if (XdrvMailbox.data_len) {
  424. snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, D_JSON_DONE);
  425. if (!strstr(XdrvMailbox.data, "{")) { // If no JSON it must be rawdata
  426. // IRSend frequency, rawdata, rawdata ...
  427. char *p;
  428. char *str = strtok_r(XdrvMailbox.data, ", ", &p);
  429. uint16_t freq = atoi(str);
  430. if (!freq) { freq = 38000; } // Default to 38kHz
  431. uint16_t count = 0;
  432. char *q = p;
  433. for (; *q; count += (*q++ == ','));
  434. if (count) { // At least two raw data values
  435. count++;
  436. uint16_t raw_array[count]; // It's safe to use stack for up to 240 packets (limited by mqtt_data length)
  437. byte i = 0;
  438. for (str = strtok_r(NULL, ", ", &p); str && i < count; str = strtok_r(NULL, ", ", &p)) {
  439. raw_array[i++] = strtoul(str, NULL, 0); // Allow decimal (5246996) and hexadecimal (0x501014) input
  440. }
  441. // snprintf_P(log_data, sizeof(log_data), PSTR("IRS: Count %d, Freq %d, Arr[0] %d, Arr[count -1] %d"),
  442. // count, freq, raw_array[0], raw_array[count -1]);
  443. // AddLog(LOG_LEVEL_DEBUG);
  444. irsend->sendRaw(raw_array, count, freq);
  445. if (!count) {
  446. snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, D_JSON_FAILED);
  447. }
  448. }
  449. else {
  450. snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, D_JSON_INVALID_RAWDATA);
  451. }
  452. }
  453. else {
  454. char dataBufUc[XdrvMailbox.data_len];
  455. UpperCase(dataBufUc, XdrvMailbox.data);
  456. StaticJsonBuffer<128> jsonBuf;
  457. JsonObject &root = jsonBuf.parseObject(dataBufUc);
  458. if (!root.success()) {
  459. snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, D_JSON_INVALID_JSON);
  460. }
  461. else {
  462. // IRsend { "protocol": "SAMSUNG", "bits": 32, "data": 551502015 }
  463. char parm_uc[10];
  464. const char *protocol = root[UpperCase_P(parm_uc, PSTR(D_JSON_IR_PROTOCOL))];
  465. uint32_t bits = root[UpperCase_P(parm_uc, PSTR(D_JSON_IR_BITS))];
  466. uint32_t data = strtoul(root[UpperCase_P(parm_uc, PSTR(D_JSON_IR_DATA))], NULL, 0);
  467. if (protocol && bits) {
  468. char protocol_text[20];
  469. int protocol_code = GetCommandCode(protocol_text, sizeof(protocol_text), protocol, kIrRemoteProtocols);
  470. snprintf_P(log_data, sizeof(log_data), PSTR("IRS: protocol_text %s, protocol %s, bits %d, data %u (0x%lX), protocol_code %d"),
  471. protocol_text, protocol, bits, data, data, protocol_code);
  472. AddLog(LOG_LEVEL_DEBUG);
  473. switch (protocol_code) {
  474. case NEC:
  475. irsend->sendNEC(data, (bits > NEC_BITS) ? NEC_BITS : bits); break;
  476. case SONY:
  477. irsend->sendSony(data, (bits > SONY_20_BITS) ? SONY_20_BITS : bits, 2); break;
  478. case RC5:
  479. irsend->sendRC5(data, bits); break;
  480. case RC6:
  481. irsend->sendRC6(data, bits); break;
  482. case DISH:
  483. irsend->sendDISH(data, (bits > DISH_BITS) ? DISH_BITS : bits); break;
  484. case JVC:
  485. irsend->sendJVC(data, (bits > JVC_BITS) ? JVC_BITS : bits, 1); break;
  486. case SAMSUNG:
  487. irsend->sendSAMSUNG(data, (bits > SAMSUNG_BITS) ? SAMSUNG_BITS : bits); break;
  488. case PANASONIC:
  489. irsend->sendPanasonic(bits, data); break;
  490. default:
  491. snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, D_JSON_PROTOCOL_NOT_SUPPORTED);
  492. }
  493. }
  494. else {
  495. error = true;
  496. }
  497. }
  498. }
  499. }
  500. else {
  501. error = true;
  502. }
  503. if (error) {
  504. snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_IRSEND "\":\"" D_JSON_NO " " D_JSON_IR_PROTOCOL ", " D_JSON_IR_BITS " " D_JSON_OR " " D_JSON_IR_DATA "\"}"));
  505. }
  506. }
  507. #ifdef USE_IR_HVAC
  508. else if (CMND_IRHVAC == command_code) {
  509. const char *HVAC_Mode;
  510. const char *HVAC_FanMode;
  511. const char *HVAC_Vendor;
  512. int HVAC_Temp = 21;
  513. boolean HVAC_Power = true;
  514. if (XdrvMailbox.data_len) {
  515. char dataBufUc[XdrvMailbox.data_len];
  516. UpperCase(dataBufUc, XdrvMailbox.data);
  517. StaticJsonBuffer<164> jsonBufer;
  518. JsonObject &root = jsonBufer.parseObject(dataBufUc);
  519. if (!root.success()) {
  520. snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, D_JSON_INVALID_JSON);
  521. }
  522. else {
  523. snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, D_JSON_DONE);
  524. HVAC_Vendor = root[D_JSON_IRHVAC_VENDOR];
  525. HVAC_Power = root[D_JSON_IRHVAC_POWER];
  526. HVAC_Mode = root[D_JSON_IRHVAC_MODE];
  527. HVAC_FanMode = root[D_JSON_IRHVAC_FANSPEED];
  528. HVAC_Temp = root[D_JSON_IRHVAC_TEMP];
  529. // snprintf_P(log_data, sizeof(log_data), PSTR("IRHVAC: Received Vendor %s, Power %d, Mode %s, FanSpeed %s, Temp %d"),
  530. // HVAC_Vendor, HVAC_Power, HVAC_Mode, HVAC_FanMode, HVAC_Temp);
  531. // AddLog(LOG_LEVEL_DEBUG);
  532. char vendor[20];
  533. int vendor_code = GetCommandCode(vendor, sizeof(vendor), HVAC_Vendor, kIrHvacVendors);
  534. switch (vendor_code) {
  535. case VNDR_TOSHIBA:
  536. error = IrHvacToshiba(HVAC_Mode, HVAC_FanMode, HVAC_Power, HVAC_Temp); break;
  537. case VNDR_MITSUBISHI:
  538. error = IrHvacMitsubishi(HVAC_Mode, HVAC_FanMode, HVAC_Power, HVAC_Temp); break;
  539. case VNDR_LG:
  540. error = IrHvacLG(HVAC_Mode, HVAC_FanMode, HVAC_Power, HVAC_Temp); break;
  541. case VNDR_FUJITSU:
  542. error = IrHvacFujitsu(HVAC_Mode, HVAC_FanMode, HVAC_Power, HVAC_Temp); break;
  543. default:
  544. error = true;
  545. }
  546. }
  547. }
  548. else {
  549. error = true;
  550. }
  551. if (error) {
  552. snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_IRHVAC "\":\"" D_JSON_WRONG " " D_JSON_IRHVAC_VENDOR ", " D_JSON_IRHVAC_MODE " " D_JSON_OR " " D_JSON_IRHVAC_FANSPEED "\"}"));
  553. }
  554. }
  555. #endif // USE_IR_HVAC
  556. else serviced = false; // Unknown command
  557. return serviced;
  558. }
  559. /*********************************************************************************************\
  560. * Interface
  561. \*********************************************************************************************/
  562. boolean Xdrv05(byte function)
  563. {
  564. boolean result = false;
  565. if ((pin[GPIO_IRSEND] < 99) || (pin[GPIO_IRRECV] < 99)) {
  566. switch (function) {
  567. case FUNC_PRE_INIT:
  568. if (pin[GPIO_IRSEND] < 99) {
  569. IrSendInit();
  570. }
  571. #ifdef USE_IR_RECEIVE
  572. if (pin[GPIO_IRRECV] < 99) {
  573. IrReceiveInit();
  574. }
  575. #endif // USE_IR_RECEIVE
  576. break;
  577. case FUNC_EVERY_50_MSECOND:
  578. #ifdef USE_IR_RECEIVE
  579. if (pin[GPIO_IRRECV] < 99) {
  580. IrReceiveCheck(); // check if there's anything on IR side
  581. }
  582. #endif // USE_IR_RECEIVE
  583. break;
  584. case FUNC_COMMAND:
  585. if (pin[GPIO_IRSEND] < 99) {
  586. result = IrSendCommand();
  587. }
  588. break;
  589. }
  590. }
  591. return result;
  592. }
  593. #endif // USE_IR_REMOTE