From 8875787935862da2b2d5470295371c8238dea754 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Fri, 2 Aug 2019 14:23:41 +0200 Subject: [PATCH] Refactor IrRemote commands Refactor IrRemote commands --- sonoff/xdrv_05_irremote.ino | 715 ++++++++++++++++++------------------ 1 file changed, 360 insertions(+), 355 deletions(-) diff --git a/sonoff/xdrv_05_irremote.ino b/sonoff/xdrv_05_irremote.ino index af99171e7..29f4bbbc3 100644 --- a/sonoff/xdrv_05_irremote.ino +++ b/sonoff/xdrv_05_irremote.ino @@ -28,8 +28,17 @@ enum IrErrors { IE_NO_ERROR, IE_INVALID_RAWDATA, IE_INVALID_JSON, IE_SYNTAX_IRSEND, IE_SYNTAX_IRHVAC }; -enum IrRemoteCommands { CMND_IRSEND, CMND_IRHVAC }; -const char kIrRemoteCommands[] PROGMEM = D_CMND_IRSEND "|" D_CMND_IRHVAC ; +const char kIrRemoteCommands[] PROGMEM = +#ifdef USE_IR_HVAC + D_CMND_IRHVAC "|" +#endif + D_CMND_IRSEND ; + +void (* const IrRemoteCommand[])(void) PROGMEM = { +#ifdef USE_IR_HVAC + &CmndIrHvac, +#endif + &CmndIrSend }; // Based on IRremoteESP8266.h enum decode_type_t static const uint8_t MAX_STANDARD_IR = SHARP; // this is the last code mapped to decode_type_t @@ -46,22 +55,6 @@ const char kIrRemoteProtocols[] PROGMEM = #endif // USE_IR_SEND_PIONEER ; -#ifdef USE_IR_HVAC - -#include -#include - -enum IrHvacVendors { - VNDR_TOSHIBA, VNDR_MITSUBISHI, VNDR_LG, VNDR_FUJITSU, VNDR_MIDEA }; -const char kIrHvacVendors[] PROGMEM = - "Toshiba|Mitsubishi|LG|Fujitsu|Midea" ; - -IRMitsubishiAC *mitsubir = nullptr; - -const char kFanSpeedOptions[] = "A12345S"; -const char kHvacModeOptions[] = "HDCA"; -#endif // USE_IR_HVAC - /*********************************************************************************************\ * IR Send \*********************************************************************************************/ @@ -75,10 +68,6 @@ void IrSendInit(void) { irsend = new IRsend(pin[GPIO_IRSEND]); // an IR led is at GPIO_IRSEND irsend->begin(); - -#ifdef USE_IR_HVAC - mitsubir = new IRMitsubishiAC(pin[GPIO_IRSEND]); -#endif //USE_IR_HVAC } char* IrUint64toHex(uint64_t value, char *str, uint16_t bits) @@ -102,6 +91,7 @@ char* IrUint64toHex(uint64_t value, char *str, uint16_t bits) return str; } + #ifdef USE_IR_RECEIVE /*********************************************************************************************\ * IR Receive @@ -202,15 +192,22 @@ void IrReceiveCheck(void) } #endif // USE_IR_RECEIVE + #ifdef USE_IR_HVAC -/********************************************************************************************* \ - * IR Heating, Ventilation and Air Conditioning using IRMitsubishiAC library +/*********************************************************************************************\ + * IR Heating, Ventilation and Air Conditioning \*********************************************************************************************/ +enum IrHvacVendors { VNDR_TOSHIBA, VNDR_MITSUBISHI, VNDR_LG, VNDR_FUJITSU, VNDR_MIDEA }; +const char kIrHvacVendors[] PROGMEM = "Toshiba|Mitsubishi|LG|Fujitsu|Midea" ; + +const char kFanSpeedOptions[] = "A12345S"; +const char kHvacModeOptions[] = "HDCA"; + #ifdef USE_IR_HVAC_TOSHIBA -/******************* - TOSHIBA -********************/ +/*-------------------------------------------------------------------------------------------*\ + * Toshiba +\*-------------------------------------------------------------------------------------------*/ const uint16_t HVAC_TOSHIBA_HDR_MARK = 4400; const uint16_t HVAC_TOSHIBA_HDR_SPACE = 4300; @@ -303,7 +300,6 @@ uint8_t IrHvacToshiba(const char *HVAC_Mode, const char *HVAC_FanMode, bool HVAC rawdata[i++] = HVAC_TOSHIBA_RPT_SPACE; // noInterrupts(); - irsend_active = true; irsend->sendRaw(rawdata, i, 38); irsend->sendRaw(rawdata, i, 38); // interrupts(); @@ -313,9 +309,9 @@ uint8_t IrHvacToshiba(const char *HVAC_Mode, const char *HVAC_FanMode, bool HVAC #endif // USE_IR_HVAC_TOSHIBA #ifdef USE_IR_HVAC_MIDEA -/******************* - MIDEA / KOMECO -********************/ +/*-------------------------------------------------------------------------------------------*\ + * Midea / Komeco +\*-------------------------------------------------------------------------------------------*/ // http://veillard.com/embedded/midea.html // https://github.com/sheinz/esp-midea-ir/blob/master/midea-ir.c @@ -431,27 +427,29 @@ uint8_t IrHvacMidea(const char *HVAC_Mode, const char *HVAC_FanMode, bool HVAC_P rawdata[i++] = HVAC_MIDEA_RPT_MARK; rawdata[i++] = HVAC_MIDEA_RPT_SPACE; -// noInterrupts(); // this takes ~180 ms : irsend->sendRaw(rawdata, i, 38); irsend->sendRaw(rawdata, i, 38); -// interrupts(); return IE_NO_ERROR; } #endif // USE_IR_HVAC_MIDEA #ifdef USE_IR_HVAC_MITSUBISHI -/******************* - MITSUBISHI -********************/ +/*-------------------------------------------------------------------------------------------*\ + * Mitsubishi +\*-------------------------------------------------------------------------------------------*/ + +#include uint8_t IrHvacMitsubishi(const char *HVAC_Mode, const char *HVAC_FanMode, bool HVAC_Power, int HVAC_Temp) { char *p; uint8_t mode; - mitsubir->stateReset(); + IRMitsubishiAC mitsubir(pin[GPIO_IRSEND]); + + mitsubir.stateReset(); if (HVAC_Mode == nullptr) { p = (char *)kHvacModeOptions; // default HVAC_HOT @@ -463,9 +461,9 @@ uint8_t IrHvacMitsubishi(const char *HVAC_Mode, const char *HVAC_FanMode, bool H return IE_SYNTAX_IRHVAC; } mode = (p - kHvacModeOptions + 1) << 3; // HOT = 0x08, DRY = 0x10, COOL = 0x18, AUTO = 0x20 - mitsubir->setMode(mode); + mitsubir.setMode(mode); - mitsubir->setPower(HVAC_Power); + mitsubir.setPower(HVAC_Power); if (HVAC_FanMode == nullptr) { p = (char *)kFanSpeedOptions; // default FAN_SPEED_AUTO @@ -477,11 +475,11 @@ uint8_t IrHvacMitsubishi(const char *HVAC_Mode, const char *HVAC_FanMode, bool H return IE_SYNTAX_IRHVAC; } mode = p - kFanSpeedOptions; // AUTO = 0, SPEED = 1 .. 5, SILENT = 6 - mitsubir->setFan(mode); + mitsubir.setFan(mode); - mitsubir->setTemp(HVAC_Temp); - mitsubir->setVane(MITSUBISHI_AC_VANE_AUTO); - mitsubir->send(); + mitsubir.setTemp(HVAC_Temp); + mitsubir.setVane(MITSUBISHI_AC_VANE_AUTO); + mitsubir.send(); // AddLog_P2(LOG_LEVEL_DEBUG, PSTR("IRHVAC: Mitsubishi Power %d, Mode %d, FanSpeed %d, Temp %d, VaneMode %d"), // mitsubir->getPower(), mitsubir->getMode(), mitsubir->getFan(), mitsubir->getTemp(), mitsubir->getVane()); @@ -491,9 +489,9 @@ uint8_t IrHvacMitsubishi(const char *HVAC_Mode, const char *HVAC_FanMode, bool H #endif // USE_IR_HVAC_MITSUBISHI #ifdef USE_IR_HVAC_LG -/******************* - LG -********************/ +/*-------------------------------------------------------------------------------------------*\ + * LG +\*-------------------------------------------------------------------------------------------*/ const uint8_t HVAC_LG_DATALEN = 7; @@ -600,19 +598,18 @@ uint8_t IrHvacLG(const char *HVAC_Mode, const char *HVAC_FanMode, bool HVAC_Powe // AddLog_P2(LOG_LEVEL_DEBUG, PSTR("IRHVAC: LG_Code %d"), LG_Code); // Send LG IR Code -// noInterrupts(); - irsend_active = true; irsend->sendLG(LG_Code, 28); -// interrupts(); return IE_NO_ERROR; } #endif // USE_IR_HVAC_LG #ifdef USE_IR_HVAC_FUJITSU -/******************* - Fujitsu -********************/ +/*-------------------------------------------------------------------------------------------*\ + * Fujitsu +\*-------------------------------------------------------------------------------------------*/ + +#include uint8_t IrHvacFujitsu(const char *HVAC_Mode, const char *HVAC_FanMode, bool HVAC_Power, int HVAC_Temp) { @@ -622,8 +619,6 @@ uint8_t IrHvacFujitsu(const char *HVAC_Mode, const char *HVAC_FanMode, bool HVAC IRFujitsuAC ac(pin[GPIO_IRSEND]); - irsend_active = true; - if (0 == HVAC_Power) { ac.off(); ac.send(); @@ -665,331 +660,341 @@ uint8_t IrHvacFujitsu(const char *HVAC_Mode, const char *HVAC_FanMode, bool HVAC } #endif // USE_IR_HVAC_FUJITSU +/*-------------------------------------------------------------------------------------------*/ + +uint32_t IrRemoteCmndIrHvacJson(void) +{ + // IrHvac { "Vendor": "", "Power": <0|1>, "Mode": "", "FanSpeed": "<1|2|3|4|5|Auto|Silence>", "Temp": <17..30> } + const char *HVAC_Mode; + const char *HVAC_FanMode; + const char *HVAC_Vendor; + int HVAC_Temp = 21; + bool HVAC_Power = true; + + char dataBufUc[XdrvMailbox.data_len]; + UpperCase(dataBufUc, XdrvMailbox.data); + RemoveSpace(dataBufUc); + if (strlen(dataBufUc) < 8) { + return IE_INVALID_JSON; + } + + StaticJsonBuffer<164> jsonBufer; + JsonObject &root = jsonBufer.parseObject(dataBufUc); + if (!root.success()) { + return IE_INVALID_JSON; + } + + HVAC_Vendor = root[D_JSON_IRHVAC_VENDOR]; + HVAC_Power = root[D_JSON_IRHVAC_POWER]; + HVAC_Mode = root[D_JSON_IRHVAC_MODE]; + HVAC_FanMode = root[D_JSON_IRHVAC_FANSPEED]; + HVAC_Temp = root[D_JSON_IRHVAC_TEMP]; + +// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("IRHVAC: Received Vendor %s, Power %d, Mode %s, FanSpeed %s, Temp %d"), HVAC_Vendor, HVAC_Power, HVAC_Mode, HVAC_FanMode, HVAC_Temp); + + char vendor[20]; + int vendor_code = GetCommandCode(vendor, sizeof(vendor), HVAC_Vendor, kIrHvacVendors); + irsend_active = true; + switch (vendor_code) { +#ifdef USE_IR_HVAC_TOSHIBA + case VNDR_TOSHIBA: + return IrHvacToshiba(HVAC_Mode, HVAC_FanMode, HVAC_Power, HVAC_Temp); +#endif +#ifdef USE_IR_HVAC_MITSUBISHI + case VNDR_MITSUBISHI: + return IrHvacMitsubishi(HVAC_Mode, HVAC_FanMode, HVAC_Power, HVAC_Temp); +#endif +#ifdef USE_IR_HVAC_LG + case VNDR_LG: + return IrHvacLG(HVAC_Mode, HVAC_FanMode, HVAC_Power, HVAC_Temp); +#endif +#ifdef USE_IR_HVAC_FUJITSU + case VNDR_FUJITSU: + return IrHvacFujitsu(HVAC_Mode, HVAC_FanMode, HVAC_Power, HVAC_Temp); +#endif +#ifdef USE_IR_HVAC_MIDEA + case VNDR_MIDEA: + return IrHvacMidea(HVAC_Mode, HVAC_FanMode, HVAC_Power, HVAC_Temp); +#endif + default: + irsend_active = false; + } + + return IE_SYNTAX_IRHVAC; +} + +void CmndIrHvac(void) +{ + uint8_t error = IE_SYNTAX_IRHVAC; + + if (XdrvMailbox.data_len) { + error = IrRemoteCmndIrHvacJson(); + } + IrRemoteCmndResponse(error); +} + #endif // USE_IR_HVAC /*********************************************************************************************\ * Commands \*********************************************************************************************/ -/* - * ArduinoJSON entry used to calculate jsonBuf: JSON_OBJECT_SIZE(3) + 40 = 96 - IRsend: - { "protocol": "RC5", "bits": 12, "data":"0xC86" } - { "protocol": "SAMSUNG", "bits": 32, "data": 551502015 } - IRhvac: - { "Vendor": "", "Power": <0|1>, "Mode": "", "FanSpeed": "<1|2|3|4|5|Auto|Silence>", "Temp": <17..30> } -*/ - -bool IrSendCommand(void) +uint32_t IrRemoteCmndIrSendRaw(void) { - char command [CMDSZ]; - bool serviced = true; - uint8_t error = IE_NO_ERROR; + // IRsend ,, ... + // or + // IRsend raw,,, (one space = zero space *2) + // IRsend raw,,,, + // IRsend raw,,,, + // IRsend raw,,
,
,,,, - int command_code = GetCommandCode(command, sizeof(command), XdrvMailbox.topic, kIrRemoteCommands); - if (-1 == command_code) { - serviced = false; // Unknown command + char *p; + char *str = strtok_r(XdrvMailbox.data, ", ", &p); + if (p == nullptr) { + return IE_INVALID_RAWDATA; } - else if (CMND_IRSEND == command_code) { - if (XdrvMailbox.data_len) { - Response_P(S_JSON_COMMAND_SVALUE, command, D_JSON_DONE); - if (strstr(XdrvMailbox.data, "{") == nullptr) { // If no JSON it must be rawdata - // IRsend ,, ... - // or - // IRsend raw,,, (one space = zero space *2) - // IRsend raw,,,, - // IRsend raw,,,, - // IRsend raw,,
,
,,,, - char *p; - char *str = strtok_r(XdrvMailbox.data, ", ", &p); - if (p == nullptr) { - error = IE_INVALID_RAWDATA; + uint16_t freq = atoi(str); + if (!freq && (*str != '0')) { // First parameter is any string + uint16_t count = 0; + char *q = p; + for (; *q; count += (*q++ == ',')); + if (count < 2) { + return IE_INVALID_RAWDATA; + } // Parameters must be at least 3 + + uint16_t parm[count]; + for (uint32_t i = 0; i < count; i++) { + parm[i] = strtol(strtok_r(nullptr, ", ", &p), nullptr, 0); + if (!parm[i]) { + if (!i) { + parm[0] = 38000; // Frequency default to 38kHz } else { - uint16_t freq = atoi(str); - if (!freq && (*str != '0')) { // First parameter is any string - uint16_t count = 0; - char *q = p; - for (; *q; count += (*q++ == ',')); - if (count < 2) { // Parameters must be at least 3 - error = IE_INVALID_RAWDATA; - } else { - uint16_t parm[count]; - for (uint32_t i = 0; i < count; i++) { - parm[i] = strtol(strtok_r(nullptr, ", ", &p), nullptr, 0); - if (!parm[i]) { - if (!i) { - parm[0] = 38000; // Frequency default to 38kHz - } else { - error = IE_INVALID_RAWDATA; // Other parameters may not be 0 - break; - } - } - } - if (IE_NO_ERROR == error) { - uint16_t i = 0; - if (count < 4) { - // IRsend raw,0,889,000000100110000001001 - uint16_t mark = parm[1] *2; // Protocol where 0 = t, 1 = 2t (RC5) - if (3 == count) { - if (parm[2] < parm[1]) { - // IRsend raw,0,889,2,000000100110000001001 - mark = parm[1] * parm[2]; // Protocol where 0 = t1, 1 = t1*t2 (Could be RC5) - } else { - // IRsend raw,0,889,1778,000000100110000001001 - mark = parm[2]; // Protocol where 0 = t1, 1 = t2 (Could be RC5) - } - } - uint16_t raw_array[strlen(p)]; // Bits - for (; *p; *p++) { - if (*p == '0') { - raw_array[i++] = parm[1]; // Space - } - else if (*p == '1') { - raw_array[i++] = mark; // Mark - } - } - irsend_active = true; - irsend->sendRaw(raw_array, i, parm[0]); - } - else if (6 == count) { // NEC Protocol - // IRsend raw,0,8620,4260,544,411,1496,010101101000111011001110000000001100110000000001100000000000000010001100 - uint16_t raw_array[strlen(p)*2+3]; // Header + bits + end - raw_array[i++] = parm[1]; // Header mark - raw_array[i++] = parm[2]; // Header space - for (; *p; *p++) { - if (*p == '0') { - raw_array[i++] = parm[3]; // Bit mark - raw_array[i++] = parm[4]; // Zero space - } - else if (*p == '1') { - raw_array[i++] = parm[3]; // Bit mark - raw_array[i++] = parm[5]; // One space - } - } - raw_array[i++] = parm[3]; // Trailing mark - irsend_active = true; - irsend->sendRaw(raw_array, i, parm[0]); - } - else { - error = IE_INVALID_RAWDATA; // Invalid number of parameters - } - } - } - } else { - if (!freq) { freq = 38000; } // Default to 38kHz - uint16_t count = 0; - char *q = p; - for (; *q; count += (*q++ == ',')); - if (0 == count) { - error = IE_INVALID_RAWDATA; - } else { // At least two raw data values - // IRsend 0,896,876,900,888,894,876,1790,874,872,1810,1736,948,872,880,872,936,872,1792,900,888,1734 - count++; - if (count < 200) { - uint16_t raw_array[count]; // It's safe to use stack for up to 200 packets (limited by mqtt_data length) - for (uint32_t i = 0; i < count; i++) { - raw_array[i] = strtol(strtok_r(nullptr, ", ", &p), nullptr, 0); // Allow decimal (20496) and hexadecimal (0x5010) input - } - -// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DBG: stack count %d"), count); - - irsend_active = true; - irsend->sendRaw(raw_array, count, freq); - } else { - uint16_t *raw_array = reinterpret_cast(malloc(count * sizeof(uint16_t))); - if (raw_array == nullptr) { - error = IE_INVALID_RAWDATA; - } else { - for (uint32_t i = 0; i < count; i++) { - raw_array[i] = strtol(strtok_r(nullptr, ", ", &p), nullptr, 0); // Allow decimal (20496) and hexadecimal (0x5010) input - } - -// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DBG: heap count %d"), count); - - irsend_active = true; - irsend->sendRaw(raw_array, count, freq); - free(raw_array); - } - } - } - } - } - } else { - char dataBufUc[XdrvMailbox.data_len]; - UpperCase(dataBufUc, XdrvMailbox.data); - RemoveSpace(dataBufUc); - if (strlen(dataBufUc) < 8) { - error = IE_INVALID_JSON; - } else { - StaticJsonBuffer<140> jsonBuf; - JsonObject &root = jsonBuf.parseObject(dataBufUc); - if (!root.success()) { - error = IE_INVALID_JSON; - } else { - // IRsend { "protocol": "SAMSUNG", "bits": 32, "data": 551502015 } - // IRsend { "protocol": "NEC", "bits": 32, "data":"0x02FDFE80", "repeat": 2 } - char parm_uc[10]; - const char *protocol = root[UpperCase_P(parm_uc, PSTR(D_JSON_IR_PROTOCOL))]; - uint16_t bits = root[UpperCase_P(parm_uc, PSTR(D_JSON_IR_BITS))]; - uint64_t data = strtoull(root[UpperCase_P(parm_uc, PSTR(D_JSON_IR_DATA))], nullptr, 0); - uint16_t repeat = root[UpperCase_P(parm_uc, PSTR(D_JSON_IR_REPEAT))]; - if (protocol && bits) { - char protocol_text[20]; - int protocol_code = GetCommandCode(protocol_text, sizeof(protocol_text), protocol, kIrRemoteProtocols); - - char dvalue[64]; - char hvalue[64]; - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("IRS: protocol_text %s, protocol %s, bits %d, data %s (%s), repeat %d, protocol_code %d"), - protocol_text, protocol, bits, ulltoa(data, dvalue, 10), IrUint64toHex(data, hvalue, bits), repeat, protocol_code); - - irsend_active = true; - switch (protocol_code) { // Equals IRremoteESP8266.h enum decode_type_t -#ifdef USE_IR_SEND_RC5 - case RC5: - irsend->sendRC5(data, bits, repeat); break; -#endif -#ifdef USE_IR_SEND_RC6 - case RC6: - irsend->sendRC6(data, bits, repeat); break; -#endif -#ifdef USE_IR_SEND_NEC - case NEC: - irsend->sendNEC(data, (bits > NEC_BITS) ? NEC_BITS : bits, repeat); break; -#endif -#ifdef USE_IR_SEND_SONY - case SONY: - irsend->sendSony(data, (bits > SONY_20_BITS) ? SONY_20_BITS : bits, repeat > kSonyMinRepeat ? repeat : kSonyMinRepeat); break; -#endif -#ifdef USE_IR_SEND_PANASONIC - case PANASONIC: - irsend->sendPanasonic64(data, bits, repeat); break; -#endif -#ifdef USE_IR_SEND_JVC - case JVC: - irsend->sendJVC(data, (bits > JVC_BITS) ? JVC_BITS : bits, repeat > 1 ? repeat : 1); break; -#endif -#ifdef USE_IR_SEND_SAMSUNG - case SAMSUNG: - irsend->sendSAMSUNG(data, (bits > SAMSUNG_BITS) ? SAMSUNG_BITS : bits, repeat); break; -#endif -#ifdef USE_IR_SEND_WHYNTER - case WHYNTER: - irsend->sendWhynter(data, bits, repeat); break; -#endif -#ifdef USE_IR_SEND_AIWA - case AIWA_RC_T501: - irsend->sendAiwaRCT501(data, bits, repeat); break; -#endif -#ifdef USE_IR_SEND_LG - case LG: - irsend->sendLG(data, bits, repeat); break; -#endif -#ifdef USE_IR_SEND_SANYO - case SANYO: - irsend->sendSanyoLC7461(data, bits, repeat); break; -#endif -#ifdef USE_IR_SEND_MITSUBISHI - case MITSUBISHI: - irsend->sendMitsubishi(data, bits, repeat); break; -#endif -#ifdef USE_IR_SEND_DISH - case DISH: - irsend->sendDISH(data, (bits > DISH_BITS) ? DISH_BITS : bits, repeat > kDishMinRepeat ? repeat : kDishMinRepeat); break; -#endif -#ifdef USE_IR_SEND_SHARP - case SHARP: - irsend->sendSharpRaw(data, bits, repeat); break; -#endif -#ifdef USE_IR_SEND_PIONEER - case IR_PIONEER: - irsend->sendPioneer(data, bits, repeat); break; -#endif // USE_IR_SEND_PIONEER - default: - irsend_active = false; - Response_P(S_JSON_COMMAND_SVALUE, command, D_JSON_PROTOCOL_NOT_SUPPORTED); - } - } else { - error = IE_SYNTAX_IRSEND; - } - } + return IE_INVALID_RAWDATA; // Other parameters may not be 0 } } - } else { - error = IE_SYNTAX_IRSEND; } - } -#ifdef USE_IR_HVAC - else if (CMND_IRHVAC == command_code) { - const char *HVAC_Mode; - const char *HVAC_FanMode; - const char *HVAC_Vendor; - int HVAC_Temp = 21; - bool HVAC_Power = true; - if (XdrvMailbox.data_len) { - char dataBufUc[XdrvMailbox.data_len]; - UpperCase(dataBufUc, XdrvMailbox.data); - RemoveSpace(dataBufUc); - if (strlen(dataBufUc) < 8) { - error = IE_INVALID_JSON; - } else { - StaticJsonBuffer<164> jsonBufer; - JsonObject &root = jsonBufer.parseObject(dataBufUc); - if (!root.success()) { - error = IE_INVALID_JSON; + uint16_t i = 0; + if (count < 4) { + // IRsend raw,0,889,000000100110000001001 + uint16_t mark = parm[1] *2; // Protocol where 0 = t, 1 = 2t (RC5) + if (3 == count) { + if (parm[2] < parm[1]) { + // IRsend raw,0,889,2,000000100110000001001 + mark = parm[1] * parm[2]; // Protocol where 0 = t1, 1 = t1*t2 (Could be RC5) } else { - Response_P(S_JSON_COMMAND_SVALUE, command, D_JSON_DONE); - HVAC_Vendor = root[D_JSON_IRHVAC_VENDOR]; - HVAC_Power = root[D_JSON_IRHVAC_POWER]; - HVAC_Mode = root[D_JSON_IRHVAC_MODE]; - HVAC_FanMode = root[D_JSON_IRHVAC_FANSPEED]; - HVAC_Temp = root[D_JSON_IRHVAC_TEMP]; - - // AddLog_P2(LOG_LEVEL_DEBUG, PSTR("IRHVAC: Received Vendor %s, Power %d, Mode %s, FanSpeed %s, Temp %d"), HVAC_Vendor, HVAC_Power, HVAC_Mode, HVAC_FanMode, HVAC_Temp); - - char vendor[20]; - int vendor_code = GetCommandCode(vendor, sizeof(vendor), HVAC_Vendor, kIrHvacVendors); - switch (vendor_code) { -#ifdef USE_IR_HVAC_TOSHIBA - case VNDR_TOSHIBA: - error = IrHvacToshiba(HVAC_Mode, HVAC_FanMode, HVAC_Power, HVAC_Temp); break; -#endif -#ifdef USE_IR_HVAC_MITSUBISHI - case VNDR_MITSUBISHI: - error = IrHvacMitsubishi(HVAC_Mode, HVAC_FanMode, HVAC_Power, HVAC_Temp); break; -#endif -#ifdef USE_IR_HVAC_LG - case VNDR_LG: - error = IrHvacLG(HVAC_Mode, HVAC_FanMode, HVAC_Power, HVAC_Temp); break; -#endif -#ifdef USE_IR_HVAC_FUJITSU - case VNDR_FUJITSU: - error = IrHvacFujitsu(HVAC_Mode, HVAC_FanMode, HVAC_Power, HVAC_Temp); break; -#endif -#ifdef USE_IR_HVAC_MIDEA - case VNDR_MIDEA: - error = IrHvacMidea(HVAC_Mode, HVAC_FanMode, HVAC_Power, HVAC_Temp); break; -#endif - default: - error = IE_SYNTAX_IRHVAC; - } + // IRsend raw,0,889,1778,000000100110000001001 + mark = parm[2]; // Protocol where 0 = t1, 1 = t2 (Could be RC5) } } + uint16_t raw_array[strlen(p)]; // Bits + for (; *p; *p++) { + if (*p == '0') { + raw_array[i++] = parm[1]; // Space + } + else if (*p == '1') { + raw_array[i++] = mark; // Mark + } + } + irsend_active = true; + irsend->sendRaw(raw_array, i, parm[0]); + } + else if (6 == count) { // NEC Protocol + // IRsend raw,0,8620,4260,544,411,1496,010101101000111011001110000000001100110000000001100000000000000010001100 + uint16_t raw_array[strlen(p)*2+3]; // Header + bits + end + raw_array[i++] = parm[1]; // Header mark + raw_array[i++] = parm[2]; // Header space + for (; *p; *p++) { + if (*p == '0') { + raw_array[i++] = parm[3]; // Bit mark + raw_array[i++] = parm[4]; // Zero space + } + else if (*p == '1') { + raw_array[i++] = parm[3]; // Bit mark + raw_array[i++] = parm[5]; // One space + } + } + raw_array[i++] = parm[3]; // Trailing mark + irsend_active = true; + irsend->sendRaw(raw_array, i, parm[0]); } else { - error = IE_SYNTAX_IRHVAC; + return IE_INVALID_RAWDATA; // Invalid number of parameters + } + } else { + if (!freq) { freq = 38000; } // Default to 38kHz + uint16_t count = 0; + char *q = p; + for (; *q; count += (*q++ == ',')); + if (0 == count) { + return IE_INVALID_RAWDATA; + } + + // IRsend 0,896,876,900,888,894,876,1790,874,872,1810,1736,948,872,880,872,936,872,1792,900,888,1734 + count++; + if (count < 200) { + uint16_t raw_array[count]; // It's safe to use stack for up to 200 packets (limited by mqtt_data length) + for (uint32_t i = 0; i < count; i++) { + raw_array[i] = strtol(strtok_r(nullptr, ", ", &p), nullptr, 0); // Allow decimal (20496) and hexadecimal (0x5010) input + } + +// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DBG: stack count %d"), count); + + irsend_active = true; + irsend->sendRaw(raw_array, count, freq); + } else { + uint16_t *raw_array = reinterpret_cast(malloc(count * sizeof(uint16_t))); + if (raw_array == nullptr) { + return IE_INVALID_RAWDATA; + } + + for (uint32_t i = 0; i < count; i++) { + raw_array[i] = strtol(strtok_r(nullptr, ", ", &p), nullptr, 0); // Allow decimal (20496) and hexadecimal (0x5010) input + } + +// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DBG: heap count %d"), count); + + irsend_active = true; + irsend->sendRaw(raw_array, count, freq); + free(raw_array); } } -#endif // USE_IR_HVAC - else serviced = false; // Unknown command + return IE_NO_ERROR; +} + +uint32_t IrRemoteCmndIrSendJson(void) +{ + // ArduinoJSON entry used to calculate jsonBuf: JSON_OBJECT_SIZE(3) + 40 = 96 + // IRsend { "protocol": "RC5", "bits": 12, "data":"0xC86" } + // IRsend { "protocol": "SAMSUNG", "bits": 32, "data": 551502015 } + + char dataBufUc[XdrvMailbox.data_len]; + UpperCase(dataBufUc, XdrvMailbox.data); + RemoveSpace(dataBufUc); + if (strlen(dataBufUc) < 8) { + return IE_INVALID_JSON; + } + + StaticJsonBuffer<140> jsonBuf; + JsonObject &root = jsonBuf.parseObject(dataBufUc); + if (!root.success()) { + return IE_INVALID_JSON; + } + + // IRsend { "protocol": "SAMSUNG", "bits": 32, "data": 551502015 } + // IRsend { "protocol": "NEC", "bits": 32, "data":"0x02FDFE80", "repeat": 2 } + char parm_uc[10]; + const char *protocol = root[UpperCase_P(parm_uc, PSTR(D_JSON_IR_PROTOCOL))]; + uint16_t bits = root[UpperCase_P(parm_uc, PSTR(D_JSON_IR_BITS))]; + uint64_t data = strtoull(root[UpperCase_P(parm_uc, PSTR(D_JSON_IR_DATA))], nullptr, 0); + uint16_t repeat = root[UpperCase_P(parm_uc, PSTR(D_JSON_IR_REPEAT))]; + if (!(protocol && bits)) { + return IE_SYNTAX_IRSEND; + } + + char protocol_text[20]; + int protocol_code = GetCommandCode(protocol_text, sizeof(protocol_text), protocol, kIrRemoteProtocols); + + char dvalue[64]; + char hvalue[64]; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("IRS: protocol_text %s, protocol %s, bits %d, data %s (%s), repeat %d, protocol_code %d"), + protocol_text, protocol, bits, ulltoa(data, dvalue, 10), IrUint64toHex(data, hvalue, bits), repeat, protocol_code); + + irsend_active = true; + switch (protocol_code) { // Equals IRremoteESP8266.h enum decode_type_t +#ifdef USE_IR_SEND_RC5 + case RC5: + irsend->sendRC5(data, bits, repeat); break; +#endif +#ifdef USE_IR_SEND_RC6 + case RC6: + irsend->sendRC6(data, bits, repeat); break; +#endif +#ifdef USE_IR_SEND_NEC + case NEC: + irsend->sendNEC(data, (bits > NEC_BITS) ? NEC_BITS : bits, repeat); break; +#endif +#ifdef USE_IR_SEND_SONY + case SONY: + irsend->sendSony(data, (bits > SONY_20_BITS) ? SONY_20_BITS : bits, repeat > kSonyMinRepeat ? repeat : kSonyMinRepeat); break; +#endif +#ifdef USE_IR_SEND_PANASONIC + case PANASONIC: + irsend->sendPanasonic64(data, bits, repeat); break; +#endif +#ifdef USE_IR_SEND_JVC + case JVC: + irsend->sendJVC(data, (bits > JVC_BITS) ? JVC_BITS : bits, repeat > 1 ? repeat : 1); break; +#endif +#ifdef USE_IR_SEND_SAMSUNG + case SAMSUNG: + irsend->sendSAMSUNG(data, (bits > SAMSUNG_BITS) ? SAMSUNG_BITS : bits, repeat); break; +#endif +#ifdef USE_IR_SEND_WHYNTER + case WHYNTER: + irsend->sendWhynter(data, bits, repeat); break; +#endif +#ifdef USE_IR_SEND_AIWA + case AIWA_RC_T501: + irsend->sendAiwaRCT501(data, bits, repeat); break; +#endif +#ifdef USE_IR_SEND_LG + case LG: + irsend->sendLG(data, bits, repeat); break; +#endif +#ifdef USE_IR_SEND_SANYO + case SANYO: + irsend->sendSanyoLC7461(data, bits, repeat); break; +#endif +#ifdef USE_IR_SEND_MITSUBISHI + case MITSUBISHI: + irsend->sendMitsubishi(data, bits, repeat); break; +#endif +#ifdef USE_IR_SEND_DISH + case DISH: + irsend->sendDISH(data, (bits > DISH_BITS) ? DISH_BITS : bits, repeat > kDishMinRepeat ? repeat : kDishMinRepeat); break; +#endif +#ifdef USE_IR_SEND_SHARP + case SHARP: + irsend->sendSharpRaw(data, bits, repeat); break; +#endif +#ifdef USE_IR_SEND_PIONEER + case IR_PIONEER: + irsend->sendPioneer(data, bits, repeat); break; +#endif // USE_IR_SEND_PIONEER + default: + irsend_active = false; + Response_P(S_JSON_COMMAND_SVALUE, XdrvMailbox.command, D_JSON_PROTOCOL_NOT_SUPPORTED); + } + + return IE_NO_ERROR; +} + +void CmndIrSend(void) +{ + uint8_t error = IE_SYNTAX_IRSEND; + + if (XdrvMailbox.data_len) { +// error = (strstr(XdrvMailbox.data, "{") == nullptr) ? IrRemoteCmndIrSendRaw() : IrRemoteCmndIrSendJson(); + if (strstr(XdrvMailbox.data, "{") == nullptr) { + error = IrRemoteCmndIrSendRaw(); + } else { + error = IrRemoteCmndIrSendJson(); + } + } + IrRemoteCmndResponse(error); +} + +void IrRemoteCmndResponse(uint32_t error) +{ switch (error) { case IE_INVALID_RAWDATA: - Response_P(S_JSON_COMMAND_SVALUE, command, D_JSON_INVALID_RAWDATA); + Response_P(S_JSON_COMMAND_SVALUE, XdrvMailbox.command, D_JSON_INVALID_RAWDATA); break; case IE_INVALID_JSON: - Response_P(S_JSON_COMMAND_SVALUE, command, D_JSON_INVALID_JSON); + Response_P(S_JSON_COMMAND_SVALUE, XdrvMailbox.command, D_JSON_INVALID_JSON); break; case IE_SYNTAX_IRSEND: Response_P(PSTR("{\"" D_CMND_IRSEND "\":\"" D_JSON_NO " " D_JSON_IR_PROTOCOL ", " D_JSON_IR_BITS " " D_JSON_OR " " D_JSON_IR_DATA "\"}")); @@ -999,9 +1004,9 @@ bool IrSendCommand(void) Response_P(PSTR("{\"" D_CMND_IRHVAC "\":\"" D_JSON_WRONG " " D_JSON_IRHVAC_VENDOR ", " D_JSON_IRHVAC_MODE " " D_JSON_OR " " D_JSON_IRHVAC_FANSPEED "\"}")); break; #endif // USE_IR_HVAC + default: // IE_NO_ERROR + Response_P(S_JSON_COMMAND_SVALUE, XdrvMailbox.command, D_JSON_DONE); } - - return serviced; } /*********************************************************************************************\ @@ -1034,7 +1039,7 @@ bool Xdrv05(uint8_t function) break; case FUNC_COMMAND: if (pin[GPIO_IRSEND] < 99) { - result = IrSendCommand(); + result = DecodeCommand(kIrRemoteCommands, IrRemoteCommand); } break; }