From e83351864ee8cba836eb15e70b67eee29b2f6483 Mon Sep 17 00:00:00 2001 From: Stephan Hadinger Date: Wed, 29 Jul 2020 13:57:44 +0200 Subject: [PATCH] Change IRRemoteESP8266 IR lib to pre-2.7.9, fixing Samsung and Pioneer protocols (#8938) --- .../TurnOnToshibaAC/TurnOnToshibaAC.ino | 2 +- lib/IRremoteESP8266-2.7.8/src/IRac.cpp | 228 ++++-- lib/IRremoteESP8266-2.7.8/src/IRac.h | 31 +- lib/IRremoteESP8266-2.7.8/src/IRrecv.cpp | 10 +- lib/IRremoteESP8266-2.7.8/src/IRrecv.h | 10 +- .../src/IRremoteESP8266.h | 18 +- lib/IRremoteESP8266-2.7.8/src/IRsend.cpp | 7 + lib/IRremoteESP8266-2.7.8/src/IRsend.h | 7 +- lib/IRremoteESP8266-2.7.8/src/IRtext.cpp | 2 + lib/IRremoteESP8266-2.7.8/src/IRtext.h | 3 +- lib/IRremoteESP8266-2.7.8/src/IRtimer.cpp | 4 +- lib/IRremoteESP8266-2.7.8/src/IRtimer.h | 8 +- lib/IRremoteESP8266-2.7.8/src/IRutils.cpp | 32 + lib/IRremoteESP8266-2.7.8/src/IRutils.h | 2 + lib/IRremoteESP8266-2.7.8/src/ir_Airwell.cpp | 217 ++++- lib/IRremoteESP8266-2.7.8/src/ir_Airwell.h | 97 +++ lib/IRremoteESP8266-2.7.8/src/ir_Daikin.h | 5 +- lib/IRremoteESP8266-2.7.8/src/ir_Hitachi.cpp | 12 +- lib/IRremoteESP8266-2.7.8/src/ir_LG.cpp | 10 +- lib/IRremoteESP8266-2.7.8/src/ir_LG.h | 3 +- lib/IRremoteESP8266-2.7.8/src/ir_Midea.cpp | 43 +- lib/IRremoteESP8266-2.7.8/src/ir_Midea.h | 15 + lib/IRremoteESP8266-2.7.8/src/ir_Mitsubishi.h | 4 +- .../src/ir_MitsubishiHeavy.cpp | 36 +- lib/IRremoteESP8266-2.7.8/src/ir_Pioneer.cpp | 28 +- lib/IRremoteESP8266-2.7.8/src/ir_Samsung.cpp | 40 +- lib/IRremoteESP8266-2.7.8/src/ir_Samsung.h | 2 + lib/IRremoteESP8266-2.7.8/src/ir_Sanyo.cpp | 480 ++++++++++- lib/IRremoteESP8266-2.7.8/src/ir_Sanyo.h | 163 ++++ lib/IRremoteESP8266-2.7.8/src/ir_Toshiba.cpp | 282 +++++-- lib/IRremoteESP8266-2.7.8/src/ir_Toshiba.h | 89 ++- .../src/locale/defaults.h | 6 + lib/IRremoteESP8266-2.7.8/test/IRac_test.cpp | 326 +++++--- .../test/IRutils_test.cpp | 84 +- .../test/ir_Airwell_test.cpp | 150 +++- lib/IRremoteESP8266-2.7.8/test/ir_LG_test.cpp | 51 +- .../test/ir_Midea_test.cpp | 49 +- .../test/ir_Pioneer_test.cpp | 146 +++- .../test/ir_Samsung_test.cpp | 132 ++-- .../test/ir_Sanyo_test.cpp | 275 ++++++- .../test/ir_Toshiba_test.cpp | 743 +++++++++--------- .../tools/RawToGlobalCache.sh | 0 .../tools/auto_analyse_raw_data.py | 0 .../tools/auto_analyse_raw_data_test.py | 0 .../tools/generate_irtext_h.sh | 0 lib/IRremoteESP8266-2.7.8/tools/mkkeywords | 0 .../tools/scrape_supported_devices.py | 0 tasmota/CHANGELOG.md | 1 + 48 files changed, 2991 insertions(+), 862 deletions(-) create mode 100644 lib/IRremoteESP8266-2.7.8/src/ir_Airwell.h create mode 100644 lib/IRremoteESP8266-2.7.8/src/ir_Sanyo.h mode change 100644 => 100755 lib/IRremoteESP8266-2.7.8/tools/RawToGlobalCache.sh mode change 100644 => 100755 lib/IRremoteESP8266-2.7.8/tools/auto_analyse_raw_data.py mode change 100644 => 100755 lib/IRremoteESP8266-2.7.8/tools/auto_analyse_raw_data_test.py mode change 100644 => 100755 lib/IRremoteESP8266-2.7.8/tools/generate_irtext_h.sh mode change 100644 => 100755 lib/IRremoteESP8266-2.7.8/tools/mkkeywords mode change 100644 => 100755 lib/IRremoteESP8266-2.7.8/tools/scrape_supported_devices.py diff --git a/lib/IRremoteESP8266-2.7.8/examples/TurnOnToshibaAC/TurnOnToshibaAC.ino b/lib/IRremoteESP8266-2.7.8/examples/TurnOnToshibaAC/TurnOnToshibaAC.ino index a37a07e5c..fb71c0486 100644 --- a/lib/IRremoteESP8266-2.7.8/examples/TurnOnToshibaAC/TurnOnToshibaAC.ino +++ b/lib/IRremoteESP8266-2.7.8/examples/TurnOnToshibaAC/TurnOnToshibaAC.ino @@ -38,7 +38,7 @@ void printState() { // Display the encoded IR sequence. unsigned char* ir_code = ac.getRaw(); Serial.print("IR Code: 0x"); - for (uint8_t i = 0; i < kToshibaACStateLength; i++) + for (uint8_t i = 0; i < ac.getStateLength(); i++) Serial.printf("%02X", ir_code[i]); Serial.println(); } diff --git a/lib/IRremoteESP8266-2.7.8/src/IRac.cpp b/lib/IRremoteESP8266-2.7.8/src/IRac.cpp index fda16aba5..4e54d26f0 100644 --- a/lib/IRremoteESP8266-2.7.8/src/IRac.cpp +++ b/lib/IRremoteESP8266-2.7.8/src/IRac.cpp @@ -16,6 +16,7 @@ #include "IRremoteESP8266.h" #include "IRtext.h" #include "IRutils.h" +#include "ir_Airwell.h" #include "ir_Amcor.h" #include "ir_Argo.h" #include "ir_Carrier.h" @@ -34,6 +35,7 @@ #include "ir_Neoclima.h" #include "ir_Panasonic.h" #include "ir_Samsung.h" +#include "ir_Sanyo.h" #include "ir_Sharp.h" #include "ir_Tcl.h" #include "ir_Teco.h" @@ -132,6 +134,9 @@ stdAc::state_t IRac::getStatePrev(void) { return _prev; } /// @return true if the protocol is supported by this class, otherwise false. bool IRac::isProtocolSupported(const decode_type_t protocol) { switch (protocol) { +#if SEND_AIRWELL + case decode_type_t::AIRWELL: +#endif #if SEND_AMCOR case decode_type_t::AMCOR: #endif @@ -236,6 +241,9 @@ bool IRac::isProtocolSupported(const decode_type_t protocol) { #if SEND_SAMSUNG_AC case decode_type_t::SAMSUNG_AC: #endif +#if SEND_SANYO_AC + case decode_type_t::SANYO_AC: +#endif #if SEND_SHARP_AC case decode_type_t::SHARP_AC: #endif @@ -269,6 +277,34 @@ bool IRac::isProtocolSupported(const decode_type_t protocol) { } } +#if SEND_AIRWELL +/// Send an Airwell A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRAirwellAc object to use. +/// @param[in] on The power setting. +/// @param[in] mode The operation mode setting. +/// @param[in] degrees The temperature setting in degrees. +/// @param[in] fan The speed setting for the fan. +void IRac::airwell(IRAirwellAc *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan) { + ac->begin(); + ac->setPowerToggle(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + // No Swing setting available. + // No Quiet setting available. + // No Light setting available. + // No Filter setting available. + // No Turbo setting available. + // No Economy setting available. + // No Clean setting available. + // No Beep setting available. + // No Sleep setting available. + ac->send(); +} +#endif // SEND_AIRWELL + #if SEND_AMCOR /// Send an Amcor A/C message with the supplied settings. /// @param[in, out] ac A Ptr to an IRAmcorAc object to use. @@ -1231,11 +1267,14 @@ void IRac::lg(IRLgAc *ac, const lg_ac_remote_model_t model, /// @param[in] degrees The temperature setting in degrees. /// @param[in] fan The speed setting for the fan. /// @param[in] swingv The vertical swing setting. +/// @param[in] econo Run the device in economical mode. /// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. +/// @note On Danby A/C units, swingv controls the Ion Filter instead. void IRac::midea(IRMideaAC *ac, const bool on, const stdAc::opmode_t mode, const bool celsius, const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const int16_t sleep) { + const stdAc::swingv_t swingv, const bool econo, + const int16_t sleep) { ac->begin(); ac->setPower(on); ac->setMode(ac->convertMode(mode)); @@ -1246,6 +1285,7 @@ void IRac::midea(IRMideaAC *ac, // No Horizontal swing setting available. // No Quiet setting available. // No Turbo setting available. + ac->setEconoToggle(econo); // No Light setting available. // No Filter setting available. // No Clean setting available. @@ -1572,6 +1612,45 @@ void IRac::samsung(IRSamsungAc *ac, } #endif // SEND_SAMSUNG_AC +#if SEND_SANYO_AC +/// Send a Toshiba A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRSanyoAc object to use. +/// @param[in] on The power setting. +/// @param[in] mode The operation mode setting. +/// @param[in] degrees The temperature setting in degrees. +/// @param[in] fan The speed setting for the fan. +/// @param[in] swingv The vertical swing setting. +/// @param[in] beep Enable/Disable beeps when receiving IR messages. +/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, > 0 is on. +void IRac::sanyo(IRSanyoAc *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const bool beep, + const int16_t sleep) { + ac->begin(); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingV(ac->convertSwingV(swingv)); + // No Horizontal swing setting available. + // No Quiet setting available. + // No Turbo setting available. + // No Econo setting available. + // No Light setting available. + // No Filter setting available. + // No Clean setting available. + ac->setBeep(beep); + ac->setSleep(sleep >= 0); // Sleep is either on/off, so convert to boolean. + // No Clock setting available. + + // Extra + ac->setSensor(true); // Set the A/C to use the temp sensor in the Unit/Wall. + ac->setSensorTemp(degrees); // Set the sensor temp to the desired temp. + ac->send(); +} +#endif // SEND_SANYO_AC + #if SEND_SHARP_AC /// Send a Sharp A/C message with the supplied settings. /// @note Multiple IR messages may be generated & sent. @@ -1703,18 +1782,26 @@ void IRac::teco(IRTecoAc *ac, /// @param[in] mode The operation mode setting. /// @param[in] degrees The temperature setting in degrees. /// @param[in] fan The speed setting for the fan. +/// @param[in] swingv The vertical swing setting. +/// @param[in] turbo Run the device in turbo/powerful mode. +/// @param[in] econo Run the device in economical mode. void IRac::toshiba(IRToshibaAC *ac, const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan) { + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, + const bool turbo, const bool econo) { ac->begin(); ac->setPower(on); ac->setMode(ac->convertMode(mode)); ac->setTemp(degrees); ac->setFan(ac->convertFan(fan)); - // No Vertical swing setting available. + // The API has no "step" option, so off is off, anything else is on. + ac->setSwing((swingv == stdAc::swingv_t::kOff) ? kToshibaAcSwingOff + : kToshibaAcSwingOn); // No Horizontal swing setting available. // No Quiet setting available. - // No Turbo setting available. + ac->setTurbo(turbo); + ac->setEcono(econo); // No Light setting available. // No Filter setting available. // No Clean setting available. @@ -1881,10 +1968,12 @@ stdAc::state_t IRac::handleToggles(const stdAc::state_t desired, case decode_type_t::ELECTRA_AC: result.light = desired.light ^ prev->light; break; + case decode_type_t::MIDEA: + result.econo = desired.econo ^ prev->econo; + // FALL THRU case decode_type_t::CORONA_AC: case decode_type_t::HITACHI_AC344: case decode_type_t::HITACHI_AC424: - case decode_type_t::MIDEA: case decode_type_t::SHARP_AC: if ((desired.swingv == stdAc::swingv_t::kOff) ^ (prev->swingv == stdAc::swingv_t::kOff)) // It changed, so toggle. @@ -1892,6 +1981,7 @@ stdAc::state_t IRac::handleToggles(const stdAc::state_t desired, else result.swingv = stdAc::swingv_t::kOff; // No change, so no toggle. break; + case decode_type_t::AIRWELL: case decode_type_t::DAIKIN64: case decode_type_t::WHIRLPOOL_AC: result.power = desired.power ^ prev->power; @@ -1959,6 +2049,14 @@ bool IRac::sendAc(const stdAc::state_t desired, const stdAc::state_t *prev) { stdAc::state_t send = this->handleToggles(this->cleanState(desired), prev); // Per vendor settings & setup. switch (send.protocol) { +#if SEND_AIRWELL + case AIRWELL: + { + IRAirwellAc ac(_pin, _inverted, _modulation); + airwell(&ac, send.power, send.mode, degC, send.fanspeed); + break; + } +#endif // SEND_AIRWELL #if SEND_AMCOR case AMCOR: { @@ -2210,7 +2308,7 @@ bool IRac::sendAc(const stdAc::state_t desired, const stdAc::state_t *prev) { { IRMideaAC ac(_pin, _inverted, _modulation); midea(&ac, send.power, send.mode, send.celsius, send.degrees, - send.fanspeed, send.swingv, send.sleep); + send.fanspeed, send.swingv, send.econo, send.sleep); break; } #endif // SEND_MIDEA @@ -2288,6 +2386,15 @@ bool IRac::sendAc(const stdAc::state_t desired, const stdAc::state_t *prev) { break; } #endif // SEND_SAMSUNG_AC +#if SEND_SANYO_AC + case SANYO_AC: + { + IRSanyoAc ac(_pin, _inverted, _modulation); + sanyo(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, + send.beep, send.sleep); + break; + } +#endif // SEND_SANYO_AC #if SEND_SHARP_AC case SHARP_AC: { @@ -2321,7 +2428,8 @@ bool IRac::sendAc(const stdAc::state_t desired, const stdAc::state_t *prev) { case TOSHIBA_AC: { IRToshibaAC ac(_pin, _inverted, _modulation); - toshiba(&ac, send.power, send.mode, degC, send.fanspeed); + toshiba(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, + send.turbo, send.econo); break; } #endif // SEND_TOSHIBA_AC @@ -2715,16 +2823,23 @@ namespace IRAcUtils { /// An empty string if we can't. String resultAcToString(const decode_results * const result) { switch (result->decode_type) { +#if DECODE_AIRWELL + case decode_type_t::AIRWELL: { + IRAirwellAc ac(kGpioUnused); + ac.setRaw(result->value); // AIRWELL uses value instead of state. + return ac.toString(); + } +#endif // DECODE_AIRWELL #if DECODE_AMCOR case decode_type_t::AMCOR: { - IRAmcorAc ac(0); + IRAmcorAc ac(kGpioUnused); ac.setRaw(result->state); return ac.toString(); } #endif // DECODE_AMCOR #if DECODE_ARGO case decode_type_t::ARGO: { - IRArgoAC ac(0); + IRArgoAC ac(kGpioUnused); ac.setRaw(result->state); return ac.toString(); } @@ -2738,49 +2853,49 @@ namespace IRAcUtils { #endif // DECODE_CARRIER_AC64 #if DECODE_DAIKIN case decode_type_t::DAIKIN: { - IRDaikinESP ac(0); + IRDaikinESP ac(kGpioUnused); ac.setRaw(result->state); return ac.toString(); } #endif // DECODE_DAIKIN #if DECODE_DAIKIN128 case decode_type_t::DAIKIN128: { - IRDaikin128 ac(0); + IRDaikin128 ac(kGpioUnused); ac.setRaw(result->state); return ac.toString(); } #endif // DECODE_DAIKIN128 #if DECODE_DAIKIN152 case decode_type_t::DAIKIN152: { - IRDaikin152 ac(0); + IRDaikin152 ac(kGpioUnused); ac.setRaw(result->state); return ac.toString(); } #endif // DECODE_DAIKIN152 #if DECODE_DAIKIN160 case decode_type_t::DAIKIN160: { - IRDaikin160 ac(0); + IRDaikin160 ac(kGpioUnused); ac.setRaw(result->state); return ac.toString(); } #endif // DECODE_DAIKIN160 #if DECODE_DAIKIN176 case decode_type_t::DAIKIN176: { - IRDaikin176 ac(0); + IRDaikin176 ac(kGpioUnused); ac.setRaw(result->state); return ac.toString(); } #endif // DECODE_DAIKIN160 #if DECODE_DAIKIN2 case decode_type_t::DAIKIN2: { - IRDaikin2 ac(0); + IRDaikin2 ac(kGpioUnused); ac.setRaw(result->state); return ac.toString(); } #endif // DECODE_DAIKIN2 #if DECODE_DAIKIN216 case decode_type_t::DAIKIN216: { - IRDaikin216 ac(0); + IRDaikin216 ac(kGpioUnused); ac.setRaw(result->state); return ac.toString(); } @@ -2801,131 +2916,138 @@ namespace IRAcUtils { #endif // DECODE_DELONGHI_AC #if DECODE_ELECTRA_AC case decode_type_t::ELECTRA_AC: { - IRElectraAc ac(0); + IRElectraAc ac(kGpioUnused); ac.setRaw(result->state); return ac.toString(); } #endif // DECODE_ELECTRA_AC #if DECODE_FUJITSU_AC case decode_type_t::FUJITSU_AC: { - IRFujitsuAC ac(0); + IRFujitsuAC ac(kGpioUnused); ac.setRaw(result->state, result->bits / 8); return ac.toString(); } #endif // DECODE_FUJITSU_AC #if DECODE_KELVINATOR case decode_type_t::KELVINATOR: { - IRKelvinatorAC ac(0); + IRKelvinatorAC ac(kGpioUnused); ac.setRaw(result->state); return ac.toString(); } #endif // DECODE_KELVINATOR #if DECODE_MITSUBISHI_AC case decode_type_t::MITSUBISHI_AC: { - IRMitsubishiAC ac(0); + IRMitsubishiAC ac(kGpioUnused); ac.setRaw(result->state); return ac.toString(); } #endif // DECODE_MITSUBISHI_AC #if DECODE_MITSUBISHI112 case decode_type_t::MITSUBISHI112: { - IRMitsubishi112 ac(0); + IRMitsubishi112 ac(kGpioUnused); ac.setRaw(result->state); return ac.toString(); } #endif // DECODE_MITSUBISHI112 #if DECODE_MITSUBISHI136 case decode_type_t::MITSUBISHI136: { - IRMitsubishi136 ac(0); + IRMitsubishi136 ac(kGpioUnused); ac.setRaw(result->state); return ac.toString(); } #endif // DECODE_MITSUBISHI136 #if DECODE_MITSUBISHIHEAVY case decode_type_t::MITSUBISHI_HEAVY_88: { - IRMitsubishiHeavy88Ac ac(0); + IRMitsubishiHeavy88Ac ac(kGpioUnused); ac.setRaw(result->state); return ac.toString(); } case decode_type_t::MITSUBISHI_HEAVY_152: { - IRMitsubishiHeavy152Ac ac(0); + IRMitsubishiHeavy152Ac ac(kGpioUnused); ac.setRaw(result->state); return ac.toString(); } #endif // DECODE_MITSUBISHIHEAVY #if DECODE_NEOCLIMA case decode_type_t::NEOCLIMA: { - IRNeoclimaAc ac(0); + IRNeoclimaAc ac(kGpioUnused); ac.setRaw(result->state); return ac.toString(); } #endif // DECODE_NEOCLIMA #if DECODE_TOSHIBA_AC case decode_type_t::TOSHIBA_AC: { - IRToshibaAC ac(0); + IRToshibaAC ac(kGpioUnused); ac.setRaw(result->state); return ac.toString(); } #endif // DECODE_TOSHIBA_AC #if DECODE_TROTEC case decode_type_t::TROTEC: { - IRTrotecESP ac(0); + IRTrotecESP ac(kGpioUnused); ac.setRaw(result->state); return ac.toString(); } #endif // DECODE_TROTEC #if DECODE_GOODWEATHER case decode_type_t::GOODWEATHER: { - IRGoodweatherAc ac(0); + IRGoodweatherAc ac(kGpioUnused); ac.setRaw(result->value); // Goodweather uses value instead of state. return ac.toString(); } #endif // DECODE_GOODWEATHER #if DECODE_GREE case decode_type_t::GREE: { - IRGreeAC ac(0); + IRGreeAC ac(kGpioUnused); ac.setRaw(result->state); return ac.toString(); } #endif // DECODE_GREE #if DECODE_MIDEA case decode_type_t::MIDEA: { - IRMideaAC ac(0); + IRMideaAC ac(kGpioUnused); ac.setRaw(result->value); // Midea uses value instead of state. return ac.toString(); } #endif // DECODE_MIDEA #if DECODE_HAIER_AC case decode_type_t::HAIER_AC: { - IRHaierAC ac(0); + IRHaierAC ac(kGpioUnused); ac.setRaw(result->state); return ac.toString(); } #endif // DECODE_HAIER_AC #if DECODE_HAIER_AC_YRW02 case decode_type_t::HAIER_AC_YRW02: { - IRHaierACYRW02 ac(0); + IRHaierACYRW02 ac(kGpioUnused); ac.setRaw(result->state); return ac.toString(); } #endif // DECODE_HAIER_AC_YRW02 #if DECODE_SAMSUNG_AC case decode_type_t::SAMSUNG_AC: { - IRSamsungAc ac(0); + IRSamsungAc ac(kGpioUnused); ac.setRaw(result->state, result->bits / 8); return ac.toString(); } #endif // DECODE_SAMSUNG_AC +#if DECODE_SANYO_AC + case decode_type_t::SANYO_AC: { + IRSanyoAc ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_SANYO_AC #if DECODE_SHARP_AC case decode_type_t::SHARP_AC: { - IRSharpAc ac(0); + IRSharpAc ac(kGpioUnused); ac.setRaw(result->state); return ac.toString(); } #endif // DECODE_SHARP_AC #if DECODE_COOLIX case decode_type_t::COOLIX: { - IRCoolixAC ac(0); + IRCoolixAC ac(kGpioUnused); ac.on(); ac.setRaw(result->value); // Coolix uses value instead of state. return ac.toString(); @@ -2941,7 +3063,7 @@ namespace IRAcUtils { #if DECODE_PANASONIC_AC case decode_type_t::PANASONIC_AC: { if (result->bits > kPanasonicAcShortBits) { - IRPanasonicAc ac(0); + IRPanasonicAc ac(kGpioUnused); ac.setRaw(result->state); return ac.toString(); } @@ -2950,7 +3072,7 @@ namespace IRAcUtils { #endif // DECODE_PANASONIC_AC #if DECODE_HITACHI_AC case decode_type_t::HITACHI_AC: { - IRHitachiAc ac(0); + IRHitachiAc ac(kGpioUnused); ac.setRaw(result->state); return ac.toString(); } @@ -2971,35 +3093,35 @@ namespace IRAcUtils { #endif // DECODE_HITACHI_AC344 #if DECODE_HITACHI_AC424 case decode_type_t::HITACHI_AC424: { - IRHitachiAc424 ac(0); + IRHitachiAc424 ac(kGpioUnused); ac.setRaw(result->state); return ac.toString(); } #endif // DECODE_HITACHI_AC424 #if DECODE_WHIRLPOOL_AC case decode_type_t::WHIRLPOOL_AC: { - IRWhirlpoolAc ac(0); + IRWhirlpoolAc ac(kGpioUnused); ac.setRaw(result->state); return ac.toString(); } #endif // DECODE_WHIRLPOOL_AC #if DECODE_VESTEL_AC case decode_type_t::VESTEL_AC: { - IRVestelAc ac(0); + IRVestelAc ac(kGpioUnused); ac.setRaw(result->value); // Like Coolix, use value instead of state. return ac.toString(); } #endif // DECODE_VESTEL_AC #if DECODE_TECO case decode_type_t::TECO: { - IRTecoAc ac(0); + IRTecoAc ac(kGpioUnused); ac.setRaw(result->value); // Like Coolix, use value instead of state. return ac.toString(); } #endif // DECODE_TECO #if DECODE_TCL112AC case decode_type_t::TCL112AC: { - IRTcl112Ac ac(0); + IRTcl112Ac ac(kGpioUnused); ac.setRaw(result->state); return ac.toString(); } @@ -3007,7 +3129,7 @@ namespace IRAcUtils { #if DECODE_LG case decode_type_t::LG: case decode_type_t::LG2: { - IRLgAc ac(0); + IRLgAc ac(kGpioUnused); ac.setRaw(result->value); // Like Coolix, use value instead of state. switch (result->decode_type) { case decode_type_t::LG2: @@ -3040,6 +3162,14 @@ namespace IRAcUtils { ) { if (decode == NULL || result == NULL) return false; // Safety check. switch (decode->decode_type) { +#if DECODE_AIRWELL + case decode_type_t::AIRWELL: { + IRAirwellAc ac(kGpioUnused); + ac.setRaw(decode->value); // Uses value instead of state. + *result = ac.toCommon(); + break; + } +#endif // DECODE_AIRWELL #if DECODE_AMCOR case decode_type_t::AMCOR: { IRAmcorAc ac(kGpioUnused); @@ -3090,7 +3220,7 @@ namespace IRAcUtils { #endif // DECODE_DAIKIN #if DECODE_DAIKIN128 case decode_type_t::DAIKIN128: { - IRDaikin128 ac(0); + IRDaikin128 ac(kGpioUnused); ac.setRaw(decode->state); *result = ac.toCommon(); break; @@ -3098,7 +3228,7 @@ namespace IRAcUtils { #endif // DECODE_DAIKIN128 #if DECODE_DAIKIN152 case decode_type_t::DAIKIN152: { - IRDaikin152 ac(0); + IRDaikin152 ac(kGpioUnused); ac.setRaw(decode->state); *result = ac.toCommon(); break; @@ -3327,6 +3457,14 @@ namespace IRAcUtils { break; } #endif // DECODE_SAMSUNG_AC +#if DECODE_SANYO_AC + case decode_type_t::SANYO_AC: { + IRSanyoAc ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_SANYO_AC #if DECODE_SHARP_AC case decode_type_t::SHARP_AC: { IRSharpAc ac(kGpioUnused); diff --git a/lib/IRremoteESP8266-2.7.8/src/IRac.h b/lib/IRremoteESP8266-2.7.8/src/IRac.h index 01365a180..5721f2bd6 100644 --- a/lib/IRremoteESP8266-2.7.8/src/IRac.h +++ b/lib/IRremoteESP8266-2.7.8/src/IRac.h @@ -7,6 +7,7 @@ #include #endif #include "IRremoteESP8266.h" +#include "ir_Airwell.h" #include "ir_Amcor.h" #include "ir_Argo.h" #include "ir_Carrier.h" @@ -28,6 +29,7 @@ #include "ir_Neoclima.h" #include "ir_Panasonic.h" #include "ir_Samsung.h" +#include "ir_Sanyo.h" #include "ir_Sharp.h" #include "ir_Tcl.h" #include "ir_Teco.h" @@ -37,9 +39,10 @@ #include "ir_Whirlpool.h" // Constants -const int8_t kGpioUnused = -1; +const int8_t kGpioUnused = -1; ///< A placeholder for not using an actual GPIO. // Class +/// A universal/common/generic interface for controling supported A/Cs. class IRac { public: explicit IRac(const uint16_t pin, const bool inverted = false, @@ -93,10 +96,15 @@ class IRac { private: #endif - uint16_t _pin; - bool _inverted; - bool _modulation; - stdAc::state_t _prev; // The state we expect the device to currently be in. + uint16_t _pin; ///< The GPIO to use to transmit messages from. + bool _inverted; ///< IR LED is lit when GPIO is LOW (true) or HIGH (false)? + bool _modulation; ///< Is frequency modulation to be used? + stdAc::state_t _prev; ///< The state we expect the device to currently be in. +#if SEND_AIRWELL + void airwell(IRAirwellAc *ac, + const bool on, const stdAc::opmode_t mode, const float degrees, + const stdAc::fanspeed_t fan); +#endif // SEND_AIRWELL #if SEND_AMCOR void amcor(IRAmcorAc *ac, const bool on, const stdAc::opmode_t mode, const float degrees, @@ -286,7 +294,8 @@ void electra(IRElectraAc *ac, void midea(IRMideaAC *ac, const bool on, const stdAc::opmode_t mode, const bool celsius, const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const int16_t sleep = -1); + const stdAc::swingv_t swingv, const bool econo, + const int16_t sleep = -1); #endif // SEND_MIDEA #if SEND_MITSUBISHI_AC void mitsubishi(IRMitsubishiAC *ac, @@ -350,6 +359,12 @@ void electra(IRElectraAc *ac, const bool beep, const bool prevpower = true, const bool forcepower = true); #endif // SEND_SAMSUNG_AC +#if SEND_SANYO_AC + void sanyo(IRSanyoAc *ac, + const bool on, const stdAc::opmode_t mode, const float degrees, + const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, + const bool beep, const int16_t sleep = -1); +#endif // SEND_SANYO_AC #if SEND_SHARP_AC void sharp(IRSharpAc *ac, const bool on, const bool prev_power, const stdAc::opmode_t mode, @@ -374,7 +389,8 @@ void electra(IRElectraAc *ac, #if SEND_TOSHIBA_AC void toshiba(IRToshibaAC *ac, const bool on, const stdAc::opmode_t mode, const float degrees, - const stdAc::fanspeed_t fan); + const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, + const bool turbo, const bool econo); #endif // SEND_TOSHIBA_AC #if SEND_TROTEC void trotec(IRTrotecESP *ac, @@ -401,6 +417,7 @@ static stdAc::state_t handleToggles(const stdAc::state_t desired, const stdAc::state_t *prev = NULL); }; // IRac class +/// Common functions for use with all A/Cs supported by the IRac class. namespace IRAcUtils { String resultAcToString(const decode_results * const results); bool decodeToState(const decode_results *decode, stdAc::state_t *result, diff --git a/lib/IRremoteESP8266-2.7.8/src/IRrecv.cpp b/lib/IRremoteESP8266-2.7.8/src/IRrecv.cpp index efddcc17c..24ea0b021 100644 --- a/lib/IRremoteESP8266-2.7.8/src/IRrecv.cpp +++ b/lib/IRremoteESP8266-2.7.8/src/IRrecv.cpp @@ -614,8 +614,12 @@ bool IRrecv::decode(decode_results *results, irparams_t *save, if (decodeDaikin216(results, offset)) return true; #endif #if DECODE_TOSHIBA_AC - DPRINTLN("Attempting Toshiba AC decode"); + DPRINTLN("Attempting Toshiba AC 72bit decode"); if (decodeToshibaAC(results, offset)) return true; + DPRINTLN("Attempting Toshiba AC 80bit decode"); + if (decodeToshibaAC(results, offset, kToshibaACBitsLong)) return true; + DPRINTLN("Attempting Toshiba AC 56bit decode"); + if (decodeToshibaAC(results, offset, kToshibaACBitsShort)) return true; #endif #if DECODE_MIDEA DPRINTLN("Attempting Midea decode"); @@ -852,6 +856,10 @@ bool IRrecv::decode(decode_results *results, irparams_t *save, DPRINTLN("Attempting Zepeal decode"); if (decodeZepeal(results, offset)) return true; #endif // DECODE_ZEPEAL +#if DECODE_SANYO_AC + DPRINTLN("Attempting Sanyo AC decode"); + if (decodeSanyoAc(results, offset)) return true; +#endif // DECODE_SANYO_AC // Typically new protocols are added above this line. } #if DECODE_HASH diff --git a/lib/IRremoteESP8266-2.7.8/src/IRrecv.h b/lib/IRremoteESP8266-2.7.8/src/IRrecv.h index 36da8df1b..62cdeed6b 100644 --- a/lib/IRremoteESP8266-2.7.8/src/IRrecv.h +++ b/lib/IRremoteESP8266-2.7.8/src/IRrecv.h @@ -283,8 +283,14 @@ class IRrecv { bool decodeSanyoLC7461(decode_results *results, uint16_t offset = kStartOffset, const uint16_t nbits = kSanyoLC7461Bits, - bool strict = true); + const bool strict = true); #endif +#if DECODE_SANYO_AC + bool decodeSanyoAc(decode_results *results, + uint16_t offset = kStartOffset, + const uint16_t nbits = kSanyoAcBits, + const bool strict = true); +#endif // DECODE_SANYO_AC #if DECODE_MITSUBISHI bool decodeMitsubishi(decode_results *results, uint16_t offset = kStartOffset, const uint16_t nbits = kMitsubishiBits, @@ -469,7 +475,7 @@ class IRrecv { #endif #if DECODE_TOSHIBA_AC bool decodeToshibaAC(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbytes = kToshibaACBits, + const uint16_t nbits = kToshibaACBits, const bool strict = true); #endif #if DECODE_TROTEC diff --git a/lib/IRremoteESP8266-2.7.8/src/IRremoteESP8266.h b/lib/IRremoteESP8266-2.7.8/src/IRremoteESP8266.h index 4490baa3b..007645073 100644 --- a/lib/IRremoteESP8266-2.7.8/src/IRremoteESP8266.h +++ b/lib/IRremoteESP8266-2.7.8/src/IRremoteESP8266.h @@ -201,6 +201,13 @@ #define SEND_SANYO _IR_ENABLE_DEFAULT_ #endif // SEND_SANYO +#ifndef DECODE_SANYO_AC +#define DECODE_SANYO_AC _IR_ENABLE_DEFAULT_ +#endif // DECODE_SANYO_AC +#ifndef SEND_SANYO_AC +#define SEND_SANYO_AC _IR_ENABLE_DEFAULT_ +#endif // SEND_SANYO_AC + #ifndef DECODE_MITSUBISHI #define DECODE_MITSUBISHI _IR_ENABLE_DEFAULT_ #endif // DECODE_MITSUBISHI @@ -674,7 +681,7 @@ DECODE_NEOCLIMA || DECODE_DAIKIN176 || DECODE_DAIKIN128 || \ DECODE_AMCOR || DECODE_DAIKIN152 || DECODE_MITSUBISHI136 || \ DECODE_MITSUBISHI112 || DECODE_HITACHI_AC424 || DECODE_HITACHI_AC3 || \ - DECODE_HITACHI_AC344 || DECODE_CORONA_AC) + DECODE_HITACHI_AC344 || DECODE_CORONA_AC || DECODE_SANYO_AC) // Add any DECODE to the above if it uses result->state (see kStateSizeMax) // you might also want to add the protocol to hasACState function #define DECODE_AC true // We need some common infrastructure for decoding A/Cs. @@ -802,8 +809,9 @@ enum decode_type_t { CORONA_AC, MIDEA24, ZEPEAL, + SANYO_AC, // Add new entries before this one, and update it to point to the last entry. - kLastDecodeType = ZEPEAL, + kLastDecodeType = SANYO_AC, }; // Message lengths & required repeat values @@ -971,6 +979,8 @@ const uint16_t kSamsungAcBits = kSamsungAcStateLength * 8; const uint16_t kSamsungAcExtendedStateLength = 21; const uint16_t kSamsungAcExtendedBits = kSamsungAcExtendedStateLength * 8; const uint16_t kSamsungAcDefaultRepeat = kNoRepeat; +const uint16_t kSanyoAcStateLength = 9; +const uint16_t kSanyoAcBits = kSanyoAcStateLength * 8; const uint16_t kSanyoSA8650BBits = 12; const uint16_t kSanyoLC7461AddressBits = 13; const uint16_t kSanyoLC7461CommandBits = 8; @@ -999,6 +1009,10 @@ const uint16_t kTecoDefaultRepeat = kNoRepeat; const uint16_t kToshibaACStateLength = 9; const uint16_t kToshibaACBits = kToshibaACStateLength * 8; const uint16_t kToshibaACMinRepeat = kSingleRepeat; +const uint16_t kToshibaACStateLengthShort = kToshibaACStateLength - 2; +const uint16_t kToshibaACBitsShort = kToshibaACStateLengthShort * 8; +const uint16_t kToshibaACStateLengthLong = kToshibaACStateLength + 1; +const uint16_t kToshibaACBitsLong = kToshibaACStateLengthLong * 8; const uint16_t kTrotecStateLength = 9; const uint16_t kTrotecBits = kTrotecStateLength * 8; const uint16_t kTrotecDefaultRepeat = kNoRepeat; diff --git a/lib/IRremoteESP8266-2.7.8/src/IRsend.cpp b/lib/IRremoteESP8266-2.7.8/src/IRsend.cpp index d3e097839..e3537381f 100644 --- a/lib/IRremoteESP8266-2.7.8/src/IRsend.cpp +++ b/lib/IRremoteESP8266-2.7.8/src/IRsend.cpp @@ -721,6 +721,8 @@ uint16_t IRsend::defaultBits(const decode_type_t protocol) { return kNeoclimaBits; case SAMSUNG_AC: return kSamsungAcBits; + case SANYO_AC: + return kSanyoAcBits; case SHARP_AC: return kSharpAcBits; case TCL112AC: @@ -1152,6 +1154,11 @@ bool IRsend::send(const decode_type_t type, const uint8_t *state, sendSamsungAC(state, nbytes); break; #endif // SEND_SAMSUNG_AC +#if SEND_SANYO_AC + case SANYO_AC: + sendSanyoAc(state, nbytes); + break; +#endif // SEND_SANYO_AC #if SEND_SHARP_AC case SHARP_AC: sendSharpAc(state, nbytes); diff --git a/lib/IRremoteESP8266-2.7.8/src/IRsend.h b/lib/IRremoteESP8266-2.7.8/src/IRsend.h index fe206583f..0b237ecc0 100644 --- a/lib/IRremoteESP8266-2.7.8/src/IRsend.h +++ b/lib/IRremoteESP8266-2.7.8/src/IRsend.h @@ -294,6 +294,11 @@ class IRsend { const uint16_t nbits = kSanyoLC7461Bits, const uint16_t repeat = kNoRepeat); #endif +#if SEND_SANYO_AC + void sendSanyoAc(const uint8_t *data, + const uint16_t nbytes = kSanyoAcStateLength, + const uint16_t repeat = kNoRepeat); +#endif // SEND_SANYO_AC #if SEND_DISH // sendDISH() should typically be called with repeat=3 as DISH devices // expect the code to be sent at least 4 times. (code + 3 repeats = 4 codes) @@ -461,7 +466,7 @@ class IRsend { uint16_t repeat = kNoRepeat); #endif #if SEND_TOSHIBA_AC - void sendToshibaAC(const unsigned char data[], + void sendToshibaAC(const uint8_t data[], const uint16_t nbytes = kToshibaACStateLength, const uint16_t repeat = kToshibaACMinRepeat); #endif diff --git a/lib/IRremoteESP8266-2.7.8/src/IRtext.cpp b/lib/IRremoteESP8266-2.7.8/src/IRtext.cpp index 7af2ffd0b..80e39b0a1 100644 --- a/lib/IRremoteESP8266-2.7.8/src/IRtext.cpp +++ b/lib/IRremoteESP8266-2.7.8/src/IRtext.cpp @@ -130,6 +130,7 @@ const PROGMEM char* kTopStr = D_STR_TOP; ///< "Top" const PROGMEM char* kBottomStr = D_STR_BOTTOM; ///< "Bottom" // Compound words/phrases/descriptions from pre-defined words. +const PROGMEM char* kEconoToggleStr = D_STR_ECONOTOGGLE; ///< "Econo Toggle" const PROGMEM char* kEyeAutoStr = D_STR_EYEAUTO; ///< "Eye Auto" const PROGMEM char* kLightToggleStr = D_STR_LIGHTTOGGLE; ///< "Light Toggle" const PROGMEM char* kOutsideQuietStr = D_STR_OUTSIDEQUIET; ///< "Outside Quiet" @@ -264,5 +265,6 @@ const PROGMEM char *kAllProtocolNamesStr = D_STR_CORONA_AC "\x0" D_STR_MIDEA24 "\x0" D_STR_ZEPEAL "\x0" + D_STR_SANYO_AC "\x0" ///< New protocol strings should be added just above this line. "\x0"; ///< This string requires double null termination. diff --git a/lib/IRremoteESP8266-2.7.8/src/IRtext.h b/lib/IRremoteESP8266-2.7.8/src/IRtext.h index 57ba2858b..73b42edc1 100644 --- a/lib/IRremoteESP8266-2.7.8/src/IRtext.h +++ b/lib/IRremoteESP8266-2.7.8/src/IRtext.h @@ -45,6 +45,7 @@ extern const char* kDisplayTempStr; extern const char* kDownStr; extern const char* kDryStr; extern const char* kEconoStr; +extern const char* kEconoToggleStr; extern const char* kEyeAutoStr; extern const char* kEyeStr; extern const char* kFalseStr; @@ -104,10 +105,10 @@ extern const char* kOnStr; extern const char* kOnTimerStr; extern const char* kOutsideQuietStr; extern const char* kOutsideStr; +extern const char* kPowerButtonStr; extern const char* kPowerfulStr; extern const char* kPowerStr; extern const char* kPowerToggleStr; -extern const char* kPowerButtonStr; extern const char* kPreviousPowerStr; extern const char* kProtocolStr; extern const char* kPurifyStr; diff --git a/lib/IRremoteESP8266-2.7.8/src/IRtimer.cpp b/lib/IRremoteESP8266-2.7.8/src/IRtimer.cpp index edd98a2d7..e1f098b28 100644 --- a/lib/IRremoteESP8266-2.7.8/src/IRtimer.cpp +++ b/lib/IRremoteESP8266-2.7.8/src/IRtimer.cpp @@ -14,7 +14,7 @@ uint32_t _TimerMs_unittest_now = 0; /// Class constructor. IRtimer::IRtimer() { reset(); } -/// Resets the IRtimer object. +/// Resets the IRtimer object. I.e. The counter starts again from now. void IRtimer::reset() { #ifndef UNIT_TEST start = micros(); @@ -47,7 +47,7 @@ void IRtimer::add(uint32_t usecs) { _IRtimer_unittest_now += usecs; } /// Class constructor. TimerMs::TimerMs() { reset(); } -/// Resets the TimerMs object. +/// Resets the TimerMs object. I.e. The counter starts again from now. void TimerMs::reset() { #ifndef UNIT_TEST start = millis(); diff --git a/lib/IRremoteESP8266-2.7.8/src/IRtimer.h b/lib/IRremoteESP8266-2.7.8/src/IRtimer.h index 1a2215fd5..659819d8f 100644 --- a/lib/IRremoteESP8266-2.7.8/src/IRtimer.h +++ b/lib/IRremoteESP8266-2.7.8/src/IRtimer.h @@ -8,7 +8,7 @@ // Classes -/// This class performs a simple timer in useconds since instantiated. +/// This class offers a simple counter in micro-seconds since instantiated. /// @note Handles when the system timer wraps around (once). class IRtimer { public: @@ -20,10 +20,10 @@ class IRtimer { #endif // UNIT_TEST private: - uint32_t start; + uint32_t start; ///< Time in uSeconds when the class was instantiated/reset. }; -/// This class performs a simple timer in milli-seoncds since instantiated. +/// This class offers a simple counter in milli-seconds since instantiated. /// @note Handles when the system timer wraps around (once). class TimerMs { public: @@ -35,6 +35,6 @@ class TimerMs { #endif // UNIT_TEST private: - uint32_t start; + uint32_t start; ///< Time in mSeconds when the class was instantiated/reset. }; #endif // IRTIMER_H_ diff --git a/lib/IRremoteESP8266-2.7.8/src/IRutils.cpp b/lib/IRremoteESP8266-2.7.8/src/IRutils.cpp index 9393fcf2d..beec2fbfb 100644 --- a/lib/IRremoteESP8266-2.7.8/src/IRutils.cpp +++ b/lib/IRremoteESP8266-2.7.8/src/IRutils.cpp @@ -161,6 +161,7 @@ bool hasACState(const decode_type_t protocol) { case NEOCLIMA: case PANASONIC_AC: case SAMSUNG_AC: + case SANYO_AC: case SHARP_AC: case TCL112AC: case TOSHIBA_AC: @@ -915,4 +916,35 @@ namespace irutils { // Merge in the data. *dst |= ((data & mask) << offset); } + + /// Create byte pairs where the second byte of the pair is a bit + /// inverted/flipped copy of the first/previous byte of the pair. + /// @param[in,out] ptr A pointer to the start of array to modify. + /// @param[in] length The byte size of the array. + /// @note A length of `<= 1` will do nothing. + /// @return A ptr to the modified array. + uint8_t * invertBytePairs(uint8_t *ptr, const uint16_t length) { + for (uint16_t i = 1; i < length; i += 2) { + // Code done this way to avoid a compiler warning bug. + uint8_t inv = ~*(ptr + i - 1); + *(ptr + i) = inv; + } + return ptr; + } + + /// Check an array to see if every second byte of a pair is a bit + /// inverted/flipped copy of the first/previous byte of the pair. + /// @param[in] ptr A pointer to the start of array to check. + /// @param[in] length The byte size of the array. + /// @note A length of `<= 1` will always return true. + /// @return true, if every second byte is inverted. Otherwise false. + bool checkInvertedBytePairs(const uint8_t * const ptr, + const uint16_t length) { + for (uint16_t i = 1; i < length; i += 2) { + // Code done this way to avoid a compiler warning bug. + uint8_t inv = ~*(ptr + i - 1); + if (*(ptr + i) != inv) return false; + } + return true; + } } // namespace irutils diff --git a/lib/IRremoteESP8266-2.7.8/src/IRutils.h b/lib/IRremoteESP8266-2.7.8/src/IRutils.h index 3c865dfcf..a3320ae93 100644 --- a/lib/IRremoteESP8266-2.7.8/src/IRutils.h +++ b/lib/IRremoteESP8266-2.7.8/src/IRutils.h @@ -107,5 +107,7 @@ namespace irutils { const uint32_t data); void setBits(uint64_t * const dst, const uint8_t offset, const uint8_t nbits, const uint64_t data); + uint8_t * invertBytePairs(uint8_t *ptr, const uint16_t length); + bool checkInvertedBytePairs(const uint8_t * const ptr, const uint16_t length); } // namespace irutils #endif // IRUTILS_H_ diff --git a/lib/IRremoteESP8266-2.7.8/src/ir_Airwell.cpp b/lib/IRremoteESP8266-2.7.8/src/ir_Airwell.cpp index 2bf0a2db8..106e9c7bf 100644 --- a/lib/IRremoteESP8266-2.7.8/src/ir_Airwell.cpp +++ b/lib/IRremoteESP8266-2.7.8/src/ir_Airwell.cpp @@ -1,23 +1,28 @@ // Copyright 2020 David Conran - +#include "ir_Airwell.h" +#include #include "IRrecv.h" #include "IRsend.h" +#include "IRtext.h" +#include "IRutils.h" /// @file /// @brief Airwell "Manchester code" based protocol. /// Some other Airwell products use the COOLIX protocol. -// Supports: -// Brand: Airwell, Model: RC08W remote -// Brand: Airwell, Model: RC04 remote -// Brand: Airwell, Model: DLS 21 DCI R410 AW A/C - const uint8_t kAirwellOverhead = 4; const uint16_t kAirwellHalfClockPeriod = 950; // uSeconds const uint16_t kAirwellHdrMark = 3 * kAirwellHalfClockPeriod; // uSeconds const uint16_t kAirwellHdrSpace = 3 * kAirwellHalfClockPeriod; // uSeconds const uint16_t kAirwellFooterMark = 5 * kAirwellHalfClockPeriod; // uSeconds +using irutils::addBoolToString; +using irutils::addModeToString; +using irutils::addFanToString; +using irutils::addTempToString; +using irutils::setBit; +using irutils::setBits; + #if SEND_AIRWELL /// Send an Airwell Manchester Code formatted message. /// Status: BETA / Appears to be working. @@ -74,3 +79,203 @@ bool IRrecv::decodeAirwell(decode_results *results, uint16_t offset, return true; } #endif + +/// Class constructor +/// @param[in] pin GPIO to be used when sending. +/// @param[in] inverted Is the output signal to be inverted? +/// @param[in] use_modulation Is frequency modulation to be used? +IRAirwellAc::IRAirwellAc(const uint16_t pin, const bool inverted, + const bool use_modulation) + : _irsend(pin, inverted, use_modulation) { stateReset(); } + +/// Set up hardware to be able to send a message. +void IRAirwellAc::begin(void) { _irsend.begin(); } + +/// Get the raw state of the object, suitable to be sent with the appropriate +/// IRsend object method. +/// @return A copy of the internal state. +uint64_t IRAirwellAc::getRaw(void) { + return remote_state; +} + +/// Set the raw state of the object. +/// @param[in] state The raw state from the native IR message. +void IRAirwellAc::setRaw(const uint64_t state) { + remote_state = state; +} + +#if SEND_AIRWELL +/// Send the current internal state as an IR message. +/// @param[in] repeat Nr. of times the message will be repeated. +void IRAirwellAc::send(const uint16_t repeat) { + _irsend.sendAirwell(getRaw(), kAirwellBits, repeat); +} +#endif // SEND_AIRWELL + +/// Reset the internals of the object to a known good state. +void IRAirwellAc::stateReset(void) { + remote_state = kAirwellKnownGoodState; +} + +/// Turn on/off the Power Airwell setting. +/// @param[in] on The desired setting state. +void IRAirwellAc::setPowerToggle(const bool on) { + setBit(&remote_state, kAirwellPowerToggleBit, on); +} + +/// Get the power toggle setting from the internal state. +/// @return A boolean indicating the setting. +bool IRAirwellAc::getPowerToggle(void) { + return GETBIT64(remote_state, kAirwellPowerToggleBit); +} + +/// Get the current operation mode setting. +/// @return The current operation mode. +uint8_t IRAirwellAc::getMode(void) { + return GETBITS64(remote_state, kAirwellModeOffset, kAirwellModeSize); +} + +/// Set the desired operation mode. +/// @param[in] mode The desired operation mode. +void IRAirwellAc::setMode(const uint8_t mode) { + switch (mode) { + case kAirwellFan: + case kAirwellCool: + case kAirwellHeat: + case kAirwellDry: + case kAirwellAuto: + setBits(&remote_state, kAirwellModeOffset, kAirwellModeSize, mode); + break; + default: + setMode(kAirwellAuto); + } + setFan(getFan()); // Ensure the fan is at the correct speed for the new mode. +} + +/// Convert a stdAc::opmode_t enum into its native mode. +/// @param[in] mode The enum to be converted. +/// @return The native equivilant of the enum. +uint8_t IRAirwellAc::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: return kAirwellCool; + case stdAc::opmode_t::kHeat: return kAirwellHeat; + case stdAc::opmode_t::kDry: return kAirwellDry; + case stdAc::opmode_t::kFan: return kAirwellFan; + default: return kAirwellAuto; + } +} + +/// Convert a native mode into its stdAc equivilant. +/// @param[in] mode The native setting to be converted. +/// @return The stdAc equivilant of the native setting. +stdAc::opmode_t IRAirwellAc::toCommonMode(const uint8_t mode) { + switch (mode) { + case kAirwellCool: return stdAc::opmode_t::kCool; + case kAirwellHeat: return stdAc::opmode_t::kHeat; + case kAirwellDry: return stdAc::opmode_t::kDry; + case kAirwellFan: return stdAc::opmode_t::kFan; + default: return stdAc::opmode_t::kAuto; + } +} + +/// Set the speed of the fan. +/// @param[in] speed The desired setting. +/// @note The speed is locked to Low when in Dry mode. +void IRAirwellAc::setFan(const uint8_t speed) { + setBits(&remote_state, kAirwellFanOffset, kAirwellFanSize, + (getMode() == kAirwellDry) ? kAirwellFanLow + : std::min(speed, kAirwellFanAuto)); +} + +/// Get the current fan speed setting. +/// @return The current fan speed. +uint8_t IRAirwellAc::getFan(void) { + return GETBITS64(remote_state, kAirwellFanOffset, kAirwellFanSize); +} + +/// Convert a stdAc::fanspeed_t enum into it's native speed. +/// @param[in] speed The enum to be converted. +/// @return The native equivilant of the enum. +uint8_t IRAirwellAc::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: + case stdAc::fanspeed_t::kLow: + return kAirwellFanLow; + case stdAc::fanspeed_t::kMedium: + return kAirwellFanMedium; + case stdAc::fanspeed_t::kHigh: + case stdAc::fanspeed_t::kMax: + return kAirwellFanHigh; + default: + return kAirwellFanAuto; + } +} + +/// Convert a native fan speed into its stdAc equivilant. +/// @param[in] speed The native setting to be converted. +/// @return The stdAc equivilant of the native setting. +stdAc::fanspeed_t IRAirwellAc::toCommonFanSpeed(const uint8_t speed) { + switch (speed) { + case kAirwellFanHigh: return stdAc::fanspeed_t::kMax; + case kAirwellFanMedium: return stdAc::fanspeed_t::kMedium; + case kAirwellFanLow: return stdAc::fanspeed_t::kMin; + default: return stdAc::fanspeed_t::kAuto; + } +} + +/// Set the temperature. +/// @param[in] degrees The temperature in degrees celsius. +void IRAirwellAc::setTemp(const uint8_t degrees) { + uint8_t temp = std::max(kAirwellMinTemp, degrees); + temp = std::min(kAirwellMaxTemp, temp); + setBits(&remote_state, kAirwellTempOffset, kAirwellTempSize, + temp - kAirwellMinTemp + 1); +} + +/// Get the current temperature setting. +/// @return Get current setting for temp. in degrees celsius. +uint8_t IRAirwellAc::getTemp(void) { + return GETBITS64(remote_state, kAirwellTempOffset, + kAirwellTempSize) + kAirwellMinTemp - 1; +} + +/// Convert the current internal state into its stdAc::state_t equivilant. +/// @return The stdAc equivilant of the native settings. +stdAc::state_t IRAirwellAc::toCommon(void) { + stdAc::state_t result; + result.protocol = decode_type_t::AIRWELL; + result.power = getPowerToggle(); + result.mode = toCommonMode(getMode()); + result.celsius = true; + result.degrees = getTemp(); + result.fanspeed = toCommonFanSpeed(getFan()); + // Not supported. + result.model = -1; + result.turbo = false; + result.swingv = stdAc::swingv_t::kOff; + result.swingh = stdAc::swingh_t::kOff; + result.light = false; + result.filter = false; + result.econo = false; + result.quiet = false; + result.clean = false; + result.beep = false; + result.sleep = -1; + result.clock = -1; + return result; +} + +/// Convert the current internal state into a human readable string. +/// @return A human readable string. +String IRAirwellAc::toString(void) { + String result = ""; + result.reserve(70); // Reserve some heap for the string to reduce fragging. + result += addBoolToString(getPowerToggle(), kPowerToggleStr, false); + result += addModeToString(getMode(), kAirwellAuto, kAirwellCool, + kAirwellHeat, kAirwellDry, kAirwellFan); + result += addFanToString(getFan(), kAirwellFanHigh, kAirwellFanLow, + kAirwellFanAuto, kAirwellFanAuto, + kAirwellFanMedium); + result += addTempToString(getTemp()); + return result; +} diff --git a/lib/IRremoteESP8266-2.7.8/src/ir_Airwell.h b/lib/IRremoteESP8266-2.7.8/src/ir_Airwell.h new file mode 100644 index 000000000..3ffbd21c7 --- /dev/null +++ b/lib/IRremoteESP8266-2.7.8/src/ir_Airwell.h @@ -0,0 +1,97 @@ +// Copyright 2020 David Conran + +/// @file +/// @brief Airwell "Manchester code" based protocol. +/// Some other Airwell products use the COOLIX protocol. + +// Supports: +// Brand: Airwell, Model: RC08W remote +// Brand: Airwell, Model: RC04 remote +// Brand: Airwell, Model: DLS 21 DCI R410 AW A/C + +#ifndef IR_AIRWELL_H_ +#define IR_AIRWELL_H_ + +#define __STDC_LIMIT_MACROS +#include +#ifndef UNIT_TEST +#include +#endif +#include "IRremoteESP8266.h" +#include "IRsend.h" +#ifdef UNIT_TEST +#include "IRsend_test.h" +#endif + + +// Constants +const uint64_t kAirwellKnownGoodState = 0x140500002; // Mode Fan, Speed 1, 25C +// Temperature +const uint8_t kAirwellMinTemp = 16; // Celsius +const uint8_t kAirwellMaxTemp = 30; // Celsius +const uint8_t kAirwellTempSize = 4; // Bits +const uint8_t kAirwellTempOffset = 19; // 0b1111 << 19 +// Fan +const uint8_t kAirwellFanSize = 2; // Bits +const uint8_t kAirwellFanOffset = 28; // 0b11 << 28 +const uint8_t kAirwellFanLow = 0; // 0b00 +const uint8_t kAirwellFanMedium = 1; // 0b01 +const uint8_t kAirwellFanHigh = 2; // 0b10 +const uint8_t kAirwellFanAuto = 3; // 0b11 +// Modes +const uint8_t kAirwellModeSize = 3; // Bits +const uint8_t kAirwellModeOffset = 30; // 0b111 << 30 +const uint8_t kAirwellCool = 1; // 0b001 +const uint8_t kAirwellHeat = 2; // 0b010 +const uint8_t kAirwellAuto = 3; // 0b011 +const uint8_t kAirwellDry = 4; // 0b100 +const uint8_t kAirwellFan = 5; // 0b101 +// Power +const uint8_t kAirwellPowerToggleBit = 33; // 0b1 << 33 + + +// Classes +/// Class for handling detailed Airwell A/C messages. +class IRAirwellAc { + public: + explicit IRAirwellAc(const uint16_t pin, const bool inverted = false, + const bool use_modulation = true); + void stateReset(); +#if SEND_AIRWELL + void send(const uint16_t repeat = kAirwellMinRepeats); + /// Run the calibration to calculate uSec timing offsets for this platform. + /// @return The uSec timing offset needed per modulation of the IR Led. + /// @note This will produce a 65ms IR signal pulse at 38kHz. + /// Only ever needs to be run once per object instantiation, if at all. + int8_t calibrate(void) { return _irsend.calibrate(); } +#endif // SEND_AIRWELL + void begin(); + void setPowerToggle(const bool on); + bool getPowerToggle(); + void setTemp(const uint8_t temp); + uint8_t getTemp(); + void setFan(const uint8_t speed); + uint8_t getFan(); + void setMode(const uint8_t mode); + uint8_t getMode(); + uint64_t getRaw(); + void setRaw(const uint64_t state); + uint8_t convertMode(const stdAc::opmode_t mode); + uint8_t convertFan(const stdAc::fanspeed_t speed); + static stdAc::opmode_t toCommonMode(const uint8_t mode); + static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed); + stdAc::state_t toCommon(void); + String toString(); +#ifndef UNIT_TEST + + private: + IRsend _irsend; ///< Instance of the IR send class +#else + /// @cond IGNORE + IRsendTest _irsend; ///< Instance of the testing IR send class + /// @endcond +#endif + uint64_t remote_state; // The state of the IR remote in native IR code form. + void checksum(void); +}; +#endif // IR_AIRWELL_H_ diff --git a/lib/IRremoteESP8266-2.7.8/src/ir_Daikin.h b/lib/IRremoteESP8266-2.7.8/src/ir_Daikin.h index 8c11dfb9f..35e62302b 100644 --- a/lib/IRremoteESP8266-2.7.8/src/ir_Daikin.h +++ b/lib/IRremoteESP8266-2.7.8/src/ir_Daikin.h @@ -598,7 +598,7 @@ class IRDaikinESP { }; /// Class for handling detailed Daikin 312-bit A/C messages. -/// Code by crankyoldgit, Reverse engineering analysis by sheppy99 +/// @note Code by crankyoldgit, Reverse engineering analysis by sheppy99 class IRDaikin2 { public: explicit IRDaikin2(const uint16_t pin, const bool inverted = false, @@ -859,8 +859,7 @@ class IRDaikin176 { }; /// Class for handling detailed Daikin 128-bit A/C messages. -/// Code by crankyoldgit. -/// Analysis by Daniel Vena +/// @note Code by crankyoldgit. Analysis by Daniel Vena class IRDaikin128 { public: explicit IRDaikin128(const uint16_t pin, const bool inverted = false, diff --git a/lib/IRremoteESP8266-2.7.8/src/ir_Hitachi.cpp b/lib/IRremoteESP8266-2.7.8/src/ir_Hitachi.cpp index 368f9995a..68d562fce 100644 --- a/lib/IRremoteESP8266-2.7.8/src/ir_Hitachi.cpp +++ b/lib/IRremoteESP8266-2.7.8/src/ir_Hitachi.cpp @@ -52,6 +52,8 @@ using irutils::addModeToString; using irutils::addModelToString; using irutils::addFanToString; using irutils::addTempToString; +using irutils::checkInvertedBytePairs; +using irutils::invertBytePairs; using irutils::minsToString; using irutils::setBit; using irutils::setBits; @@ -1047,8 +1049,7 @@ void IRHitachiAc424::stateReset(void) { /// Update the internal consistency check for the protocol. void IRHitachiAc424::setInvertedStates(void) { - for (uint8_t i = 3; i < kHitachiAc424StateLength - 1; i += 2) - remote_state[i + 1] = ~remote_state[i]; + invertBytePairs(remote_state + 3, kHitachiAc424StateLength - 3); } /// Set up hardware to be able to send a message. @@ -1402,8 +1403,7 @@ void IRHitachiAc3::stateReset(void) { /// @param[in] length The size of the state array. /// @note This is this protocols integrity check. void IRHitachiAc3::setInvertedStates(const uint16_t length) { - for (uint8_t i = 3; i < length - 1; i += 2) - remote_state[i + 1] = ~remote_state[i]; + if (length > 3) invertBytePairs(remote_state + 3, length - 3); } /// Check if every second byte of the state, after the fixed header @@ -1413,9 +1413,7 @@ void IRHitachiAc3::setInvertedStates(const uint16_t length) { /// @note This is this protocols integrity check. bool IRHitachiAc3::hasInvertedStates(const uint8_t state[], const uint16_t length) { - for (uint8_t i = 3; i < length - 1; i += 2) - if ((state[i + 1] ^ state[i]) != 0xFF) return false; - return true; + return (length <= 3 || checkInvertedBytePairs(state + 3, length - 3)); } /// Set up hardware to be able to send a message. diff --git a/lib/IRremoteESP8266-2.7.8/src/ir_LG.cpp b/lib/IRremoteESP8266-2.7.8/src/ir_LG.cpp index 65fe91883..40b141176 100644 --- a/lib/IRremoteESP8266-2.7.8/src/ir_LG.cpp +++ b/lib/IRremoteESP8266-2.7.8/src/ir_LG.cpp @@ -400,6 +400,7 @@ uint8_t IRLgAc::getTemp(void) { void IRLgAc::setFan(const uint8_t speed) { switch (speed) { case kLgAcFanAuto: + case kLgAcFanLowest: case kLgAcFanLow: case kLgAcFanMedium: case kLgAcFanHigh: @@ -469,12 +470,12 @@ stdAc::opmode_t IRLgAc::toCommonMode(const uint8_t mode) { /// @return The native equivilant of the enum. uint8_t IRLgAc::convertFan(const stdAc::fanspeed_t speed) { switch (speed) { - case stdAc::fanspeed_t::kMin: + case stdAc::fanspeed_t::kMin: return kLgAcFanLowest; case stdAc::fanspeed_t::kLow: return kLgAcFanLow; case stdAc::fanspeed_t::kMedium: return kLgAcFanMedium; case stdAc::fanspeed_t::kHigh: - case stdAc::fanspeed_t::kMax: return kHitachiAcFanHigh; - default: return kHitachiAcFanAuto; + case stdAc::fanspeed_t::kMax: return kLgAcFanHigh; + default: return kLgAcFanAuto; } } @@ -486,6 +487,7 @@ stdAc::fanspeed_t IRLgAc::toCommonFanSpeed(const uint8_t speed) { case kLgAcFanHigh: return stdAc::fanspeed_t::kMax; case kLgAcFanMedium: return stdAc::fanspeed_t::kMedium; case kLgAcFanLow: return stdAc::fanspeed_t::kLow; + case kLgAcFanLowest: return stdAc::fanspeed_t::kMin; default: return stdAc::fanspeed_t::kAuto; } } @@ -528,7 +530,7 @@ String IRLgAc::toString(void) { kLgAcHeat, kLgAcDry, kLgAcFan); result += addTempToString(getTemp()); result += addFanToString(getFan(), kLgAcFanHigh, kLgAcFanLow, - kLgAcFanAuto, kLgAcFanAuto, kLgAcFanMedium); + kLgAcFanAuto, kLgAcFanLowest, kLgAcFanMedium); } return result; } diff --git a/lib/IRremoteESP8266-2.7.8/src/ir_LG.h b/lib/IRremoteESP8266-2.7.8/src/ir_LG.h index bf0cdd0b6..1748d143e 100644 --- a/lib/IRremoteESP8266-2.7.8/src/ir_LG.h +++ b/lib/IRremoteESP8266-2.7.8/src/ir_LG.h @@ -32,7 +32,8 @@ const uint8_t kLgAcChecksumOffset = 0; // Nr. of bits const uint8_t kLgAcChecksumSize = kNibbleSize; // Nr. of bits const uint8_t kLgAcFanOffset = 4; // Nr. of bits const uint8_t kLgAcFanSize = 3; // Nr. of bits -const uint8_t kLgAcFanLow = 0; // 0b000 +const uint8_t kLgAcFanLowest = 0; // 0b000 +const uint8_t kLgAcFanLow = 1; // 0b001 const uint8_t kLgAcFanMedium = 2; // 0b010 const uint8_t kLgAcFanHigh = 4; // 0b100 const uint8_t kLgAcFanAuto = 5; // 0b101 diff --git a/lib/IRremoteESP8266-2.7.8/src/ir_Midea.cpp b/lib/IRremoteESP8266-2.7.8/src/ir_Midea.cpp index 5d270d3d4..d02088cef 100644 --- a/lib/IRremoteESP8266-2.7.8/src/ir_Midea.cpp +++ b/lib/IRremoteESP8266-2.7.8/src/ir_Midea.cpp @@ -3,7 +3,9 @@ /// @brief Support for Midea protocols. /// Midea added by crankyoldgit & bwze. /// send: bwze/crankyoldgit, decode: crankyoldgit +/// @note SwingV has the function of an Ion Filter on Danby A/C units. /// @see https://docs.google.com/spreadsheets/d/1TZh4jWrx4h9zzpYUI9aYXMl1fYOiqu-xVuOOMqagxrs/edit?usp=sharing +/// @see https://github.com/crankyoldgit/IRremoteESP8266/pull/1213 #include "ir_Midea.h" #include "ir_NEC.h" @@ -99,6 +101,7 @@ void IRMideaAC::stateReset(void) { // Power On, Mode Auto, Fan Auto, Temp = 25C/77F remote_state = 0xA1826FFFFF62; _SwingVToggle = false; + _EconoToggle = false; } /// Set up hardware to be able to send a message. @@ -110,11 +113,14 @@ void IRMideaAC::begin(void) { _irsend.begin(); } void IRMideaAC::send(const uint16_t repeat) { this->checksum(); // Ensure correct checksum before sending. _irsend.sendMidea(remote_state, kMideaBits, repeat); - // Handle toggling the swing if we need to. - if (_SwingVToggle && !isSwingVToggle()) { + // Handle toggling the swing & econo mode if we need to. + if (_SwingVToggle && !isSwingVToggle()) _irsend.sendMidea(kMideaACToggleSwingV, kMideaBits, repeat); - } - _SwingVToggle = false; // The toggle message has been sent, so reset. + if (_EconoToggle && !isEconoToggle()) + _irsend.sendMidea(kMideaACToggleEcono, kMideaBits, repeat); + // The toggle messages has been sent, so reset. + _SwingVToggle = false; + _EconoToggle = false; } #endif // SEND_MIDEA @@ -246,22 +252,42 @@ bool IRMideaAC::getSleep(void) { } /// Set the A/C to toggle the vertical swing toggle for the next send. +/// @note On Danby A/C units, this is associated with the Ion Filter instead. /// @param[in] on true, the setting is on. false, the setting is off. void IRMideaAC::setSwingVToggle(const bool on) { _SwingVToggle = on; } /// Is the current state a vertical swing toggle message? +/// @note On Danby A/C units, this is associated with the Ion Filter instead. /// @return true, it is. false, it isn't. bool IRMideaAC::isSwingVToggle(void) { return remote_state == kMideaACToggleSwingV; } // Get the vertical swing toggle state of the A/C. +/// @note On Danby A/C units, this is associated with the Ion Filter instead. /// @return true, the setting is on. false, the setting is off. bool IRMideaAC::getSwingVToggle(void) { _SwingVToggle |= isSwingVToggle(); return _SwingVToggle; } +/// Set the A/C to toggle the Econo (energy saver) mode for the next send. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRMideaAC::setEconoToggle(const bool on) { _EconoToggle = on; } + +/// Is the current state an Econo (energy saver) toggle message? +/// @return true, it is. false, it isn't. +bool IRMideaAC::isEconoToggle(void) { + return remote_state == kMideaACToggleEcono; +} + +// Get the Econo (energy saver) toggle state of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRMideaAC::getEconoToggle(void) { + _EconoToggle |= isEconoToggle(); + return _EconoToggle; +} + /// Calculate the checksum for a given state. /// @param[in] state The value to calc the checksum of. /// @return The calculated checksum value. @@ -376,6 +402,7 @@ stdAc::state_t IRMideaAC::toCommon(const stdAc::state_t *prev) { result.degrees = this->getTemp(result.celsius); result.fanspeed = this->toCommonFanSpeed(this->getFan()); result.sleep = this->getSleep() ? 0 : -1; + result.econo = this->getEconoToggle(); return result; } @@ -384,7 +411,8 @@ stdAc::state_t IRMideaAC::toCommon(const stdAc::state_t *prev) { String IRMideaAC::toString(void) { String result = ""; result.reserve(100); // Reserve some heap for the string to reduce fragging. - if (!isSwingVToggle()) { + bool needComma = false; + if (!isSwingVToggle() && !isEconoToggle()) { result += addBoolToString(getPower(), kPowerStr, false); result += addModeToString(getMode(), kMideaACAuto, kMideaACCool, kMideaACHeat, kMideaACDry, kMideaACFan); @@ -396,9 +424,10 @@ String IRMideaAC::toString(void) { result += addFanToString(getFan(), kMideaACFanHigh, kMideaACFanLow, kMideaACFanAuto, kMideaACFanAuto, kMideaACFanMed); result += addBoolToString(getSleep(), kSleepStr); + needComma = true; } - result += addBoolToString(getSwingVToggle(), kSwingVToggleStr, - !isSwingVToggle()); + result += addBoolToString(getSwingVToggle(), kSwingVToggleStr, needComma); + result += addBoolToString(getEconoToggle(), kEconoToggleStr); return result; } diff --git a/lib/IRremoteESP8266-2.7.8/src/ir_Midea.h b/lib/IRremoteESP8266-2.7.8/src/ir_Midea.h index b7825f569..96a1ffec4 100644 --- a/lib/IRremoteESP8266-2.7.8/src/ir_Midea.h +++ b/lib/IRremoteESP8266-2.7.8/src/ir_Midea.h @@ -11,6 +11,10 @@ // Brand: Comfee, Model: MPD1-12CRN7 A/C (MIDEA) // Brand: Keystone, Model: RG57H4(B)BGEF remote (MIDEA) // Brand: Midea, Model: FS40-7AR Stand Fan (MIDEA24) +// Brand: Danby, Model: DAC080BGUWDB (MIDEA) +// Brand: Danby, Model: DAC100BGUWDB (MIDEA) +// Brand: Danby, Model: DAC120BGUWDB (MIDEA) +// Brand: Danby, Model: R09C/BCGE remote (MIDEA) #ifndef IR_MIDEA_H_ #define IR_MIDEA_H_ @@ -26,6 +30,10 @@ #include "IRsend_test.h" #endif +#if DANBY_DAC + kSwingVToggleStr = kIonStr; +#endif + // Constants const uint8_t kMideaACTempOffset = 24; const uint8_t kMideaACTempSize = 5; // Bits @@ -49,6 +57,9 @@ const uint8_t kMideaACFanHigh = 3; // 0b11 const uint8_t kMideaACSleepOffset = 38; const uint8_t kMideaACPowerOffset = 39; const uint64_t kMideaACToggleSwingV = 0x0000A201FFFFFF7C; +// For Danby DAC unit, the Ionizer toggle is the same as ToggleSwingV +// const uint64_t kMideaACToggleIonizer = 0x0000A201FFFFFF7C; +const uint64_t kMideaACToggleEcono = 0x0000A202FFFFFF7E; // Legacy defines. (Deprecated) #define MIDEA_AC_COOL kMideaACCool @@ -104,6 +115,9 @@ class IRMideaAC { bool isSwingVToggle(void); void setSwingVToggle(const bool on); bool getSwingVToggle(void); + bool isEconoToggle(void); + void setEconoToggle(const bool on); + bool getEconoToggle(void); uint8_t convertMode(const stdAc::opmode_t mode); uint8_t convertFan(const stdAc::fanspeed_t speed); static stdAc::opmode_t toCommonMode(const uint8_t mode); @@ -121,6 +135,7 @@ class IRMideaAC { #endif // UNIT_TEST uint64_t remote_state; ///< The state of the IR remote in IR code form. bool _SwingVToggle; + bool _EconoToggle; void checksum(void); static uint8_t calcChecksum(const uint64_t state); }; diff --git a/lib/IRremoteESP8266-2.7.8/src/ir_Mitsubishi.h b/lib/IRremoteESP8266-2.7.8/src/ir_Mitsubishi.h index 04c7bf57d..60f997095 100644 --- a/lib/IRremoteESP8266-2.7.8/src/ir_Mitsubishi.h +++ b/lib/IRremoteESP8266-2.7.8/src/ir_Mitsubishi.h @@ -163,7 +163,7 @@ const uint8_t kMitsubishi112SwingHAuto = 0b1100; /// Class for handling detailed Mitsubishi 144-bit A/C messages. -/// Inspired and derived from the work done at: https://github.com/r45635/HVAC-IR-Control +/// @note Inspired and derived from the work done at: https://github.com/r45635/HVAC-IR-Control /// @warning Consider this very alpha code. Seems to work, but not validated. class IRMitsubishiAC { public: @@ -282,7 +282,7 @@ class IRMitsubishi136 { void checksum(void); }; - +/// Class for handling detailed Mitsubishi 122-bit A/C messages. class IRMitsubishi112 { public: explicit IRMitsubishi112(const uint16_t pin, const bool inverted = false, diff --git a/lib/IRremoteESP8266-2.7.8/src/ir_MitsubishiHeavy.cpp b/lib/IRremoteESP8266-2.7.8/src/ir_MitsubishiHeavy.cpp index 7747ded6d..8a52bb762 100644 --- a/lib/IRremoteESP8266-2.7.8/src/ir_MitsubishiHeavy.cpp +++ b/lib/IRremoteESP8266-2.7.8/src/ir_MitsubishiHeavy.cpp @@ -33,6 +33,8 @@ using irutils::addIntToString; using irutils::addLabeledString; using irutils::addModeToString; using irutils::addTempToString; +using irutils::checkInvertedBytePairs; +using irutils::invertBytePairs; using irutils::setBit; using irutils::setBits; @@ -321,32 +323,24 @@ bool IRMitsubishiHeavy152Ac::checkZmsSig(const uint8_t *state) { } /// Calculate the checksum for the current internal state of the remote. -/// Note: Technically it has no checksum, but does has inverted byte pairs. +/// Note: Technically it has no checksum, but does have inverted byte pairs. void IRMitsubishiHeavy152Ac::checksum(void) { - for (uint8_t i = kMitsubishiHeavySigLength - 2; - i < kMitsubishiHeavy152StateLength; - i += 2) { - remote_state[i + 1] = ~remote_state[i]; - } + const uint8_t kOffset = kMitsubishiHeavySigLength - 2; + invertBytePairs(remote_state + kOffset, + kMitsubishiHeavy152StateLength - kOffset); } /// Verify the checksum is valid for a given state. /// @param[in] state The array to verify the checksum of. /// @param[in] length The length/size of the state array. /// @return true, if the state has a valid checksum. Otherwise, false. -/// Note: Technically it has no checksum, but does has inverted byte pairs. +/// Note: Technically it has no checksum, but does have inverted byte pairs. bool IRMitsubishiHeavy152Ac::validChecksum(const uint8_t *state, const uint16_t length) { // Assume anything too short is fine. if (length < kMitsubishiHeavySigLength) return true; - // Check all the byte pairs. - for (uint16_t i = kMitsubishiHeavySigLength - 2; - i < length; - i += 2) { - // XOR of a byte and it's self inverted should be 0xFF; - if ((state[i] ^ state[i + 1]) != 0xFF) return false; - } - return true; + const uint8_t kOffset = kMitsubishiHeavySigLength - 2; + return checkInvertedBytePairs(state + kOffset, length - kOffset); } /// Convert a stdAc::opmode_t enum into its native mode. @@ -856,20 +850,18 @@ bool IRMitsubishiHeavy88Ac::checkZjsSig(const uint8_t *state) { } /// Calculate the checksum for the current internal state of the remote. -/// Note: Technically it has no checksum, but does has inverted byte pairs. +/// Note: Technically it has no checksum, but does have inverted byte pairs. void IRMitsubishiHeavy88Ac::checksum(void) { - for (uint8_t i = kMitsubishiHeavySigLength - 2; - i < kMitsubishiHeavy88StateLength; - i += 2) { - remote_state[i + 1] = ~remote_state[i]; - } + const uint8_t kOffset = kMitsubishiHeavySigLength - 2; + invertBytePairs(remote_state + kOffset, + kMitsubishiHeavy88StateLength - kOffset); } /// Verify the checksum is valid for a given state. /// @param[in] state The array to verify the checksum of. /// @param[in] length The length/size of the state array. /// @return true, if the state has a valid checksum. Otherwise, false. -/// Note: Technically it has no checksum, but does has inverted byte pairs. +/// Note: Technically it has no checksum, but does have inverted byte pairs. bool IRMitsubishiHeavy88Ac::validChecksum(const uint8_t *state, const uint16_t length) { return IRMitsubishiHeavy152Ac::validChecksum(state, length); diff --git a/lib/IRremoteESP8266-2.7.8/src/ir_Pioneer.cpp b/lib/IRremoteESP8266-2.7.8/src/ir_Pioneer.cpp index d5ac89765..90f58c6ed 100644 --- a/lib/IRremoteESP8266-2.7.8/src/ir_Pioneer.cpp +++ b/lib/IRremoteESP8266-2.7.8/src/ir_Pioneer.cpp @@ -8,9 +8,12 @@ /// @see http://www.adrian-kingston.com/IRFormatPioneer.htm /// @see https://github.com/crankyoldgit/IRremoteESP8266/pull/547 /// @see https://www.pioneerelectronics.com/PUSA/Support/Home-Entertainment-Custom-Install/IR+Codes/A+V+Receivers +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1220 // Supports: // Brand: Pioneer, Model: AV Receivers +// Brand: Pioneer, Model: VSX-324 AV Receiver +// Brand: Pioneer, Model: AXD7690 Remote #define __STDC_LIMIT_MACROS #include @@ -20,22 +23,15 @@ #include "IRutils.h" // Constants -const uint16_t kPioneerTick = 534; -const uint16_t kPioneerHdrMarkTicks = 16; -const uint16_t kPioneerHdrMark = kPioneerHdrMarkTicks * kPioneerTick; -const uint16_t kPioneerHdrSpaceTicks = 8; -const uint16_t kPioneerHdrSpace = kPioneerHdrSpaceTicks * kPioneerTick; -const uint16_t kPioneerBitMarkTicks = 1; -const uint16_t kPioneerBitMark = kPioneerBitMarkTicks * kPioneerTick; -const uint16_t kPioneerOneSpaceTicks = 3; -const uint16_t kPioneerOneSpace = kPioneerOneSpaceTicks * kPioneerTick; -const uint16_t kPioneerZeroSpaceTicks = 1; -const uint16_t kPioneerZeroSpace = kPioneerZeroSpaceTicks * kPioneerTick; -const uint16_t kPioneerMinCommandLengthTicks = 159; -const uint32_t kPioneerMinCommandLength = kPioneerMinCommandLengthTicks * - kPioneerTick; -const uint16_t kPioneerMinGapTicks = 47; -const uint32_t kPioneerMinGap = kPioneerMinGapTicks * kPioneerTick; +// Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/1220 +const uint16_t kPioneerTick = 534; ///< uSeconds. +const uint16_t kPioneerHdrMark = 8506; ///< uSeconds. +const uint16_t kPioneerHdrSpace = 4191; ///< uSeconds. +const uint16_t kPioneerBitMark = 568; ///< uSeconds. +const uint16_t kPioneerOneSpace = 1542; ///< uSeconds. +const uint16_t kPioneerZeroSpace = 487; ///< uSeconds. +const uint32_t kPioneerMinCommandLength = 84906; ///< uSeconds. +const uint32_t kPioneerMinGap = 25181; ///< uSeconds. #if SEND_PIONEER /// Send a raw Pioneer formatted message. diff --git a/lib/IRremoteESP8266-2.7.8/src/ir_Samsung.cpp b/lib/IRremoteESP8266-2.7.8/src/ir_Samsung.cpp index 02aa4abdd..c3ff5ec7d 100644 --- a/lib/IRremoteESP8266-2.7.8/src/ir_Samsung.cpp +++ b/lib/IRremoteESP8266-2.7.8/src/ir_Samsung.cpp @@ -53,6 +53,14 @@ const uint16_t kSamsungAcBitMark = 586; const uint16_t kSamsungAcOneSpace = 1432; const uint16_t kSamsungAcZeroSpace = 436; +// Data from https://github.com/crankyoldgit/IRremoteESP8266/issues/1220 +// Values calculated based on the average of ten messages. +const uint16_t kSamsung36HdrMark = 4515; /// < uSeconds +const uint16_t kSamsung36HdrSpace = 4438; /// < uSeconds +const uint16_t kSamsung36BitMark = 512; /// < uSeconds +const uint16_t kSamsung36OneSpace = 1468; /// < uSeconds +const uint16_t kSamsung36ZeroSpace = 490; /// < uSeconds + using irutils::addBoolToString; using irutils::addFanToString; using irutils::addIntToString; @@ -146,7 +154,7 @@ bool IRrecv::decodeSAMSUNG(decode_results *results, uint16_t offset, #if SEND_SAMSUNG36 /// Send a Samsung 36-bit formatted message. -/// Status: Alpha / Experimental. +/// Status: STABLE / Works on real devices. /// @param[in] data The message to be sent. /// @param[in] nbits The number of bits of message to be sent. /// @param[in] repeat The number of times the command is to be repeated. @@ -156,16 +164,16 @@ void IRsend::sendSamsung36(const uint64_t data, const uint16_t nbits, if (nbits < 16) return; // To small to send. for (uint16_t r = 0; r <= repeat; r++) { // Block #1 (16 bits) - sendGeneric(kSamsungHdrMark, kSamsungHdrSpace, - kSamsungBitMark, kSamsungOneSpace, - kSamsungBitMark, kSamsungZeroSpace, - kSamsungBitMark, kSamsungHdrSpace, + sendGeneric(kSamsung36HdrMark, kSamsung36HdrSpace, + kSamsung36BitMark, kSamsung36OneSpace, + kSamsung36BitMark, kSamsung36ZeroSpace, + kSamsung36BitMark, kSamsung36HdrSpace, data >> (nbits - 16), 16, 38, true, 0, kDutyDefault); // Block #2 (The rest, typically 20 bits) sendGeneric(0, 0, // No header - kSamsungBitMark, kSamsungOneSpace, - kSamsungBitMark, kSamsungZeroSpace, - kSamsungBitMark, kSamsungMinGap, // Gap is just a guess. + kSamsung36BitMark, kSamsung36OneSpace, + kSamsung36BitMark, kSamsung36ZeroSpace, + kSamsung36BitMark, kSamsungMinGap, // Gap is just a guess. // Mask off the rest of the bits. data & ((1ULL << (nbits - 16)) - 1), nbits - 16, 38, true, 0, kDutyDefault); @@ -175,7 +183,7 @@ void IRsend::sendSamsung36(const uint64_t data, const uint16_t nbits, #if DECODE_SAMSUNG36 /// Decode the supplied Samsung36 message. -/// Status: Alpha / Experimental +/// Status: STABLE / Expected to work. /// @param[in,out] results Ptr to the data to decode & where to store the result /// @param[in] offset The starting index to use when attempting to decode the /// raw data. Typically/Defaults to kStartOffset. @@ -198,10 +206,10 @@ bool IRrecv::decodeSamsung36(decode_results *results, uint16_t offset, uint16_t used; used = matchGeneric(results->rawbuf + offset, &data, results->rawlen - offset, 16, - kSamsungHdrMark, kSamsungHdrSpace, - kSamsungBitMark, kSamsungOneSpace, - kSamsungBitMark, kSamsungZeroSpace, - kSamsungBitMark, kSamsungHdrSpace, false); + kSamsung36HdrMark, kSamsung36HdrSpace, + kSamsung36BitMark, kSamsung36OneSpace, + kSamsung36BitMark, kSamsung36ZeroSpace, + kSamsung36BitMark, kSamsung36HdrSpace, false); if (!used) return false; offset += used; // Data (Block #2) @@ -209,9 +217,9 @@ bool IRrecv::decodeSamsung36(decode_results *results, uint16_t offset, if (!matchGeneric(results->rawbuf + offset, &data2, results->rawlen - offset, nbits - 16, 0, 0, - kSamsungBitMark, kSamsungOneSpace, - kSamsungBitMark, kSamsungZeroSpace, - kSamsungBitMark, kSamsungMinGap, true)) return false; + kSamsung36BitMark, kSamsung36OneSpace, + kSamsung36BitMark, kSamsung36ZeroSpace, + kSamsung36BitMark, kSamsungMinGap, true)) return false; data <<= (nbits - 16); data += data2; diff --git a/lib/IRremoteESP8266-2.7.8/src/ir_Samsung.h b/lib/IRremoteESP8266-2.7.8/src/ir_Samsung.h index fa6f9f2f3..d5c9955ec 100644 --- a/lib/IRremoteESP8266-2.7.8/src/ir_Samsung.h +++ b/lib/IRremoteESP8266-2.7.8/src/ir_Samsung.h @@ -14,6 +14,8 @@ // Brand: Samsung, Model: DB93-16761C remote // Brand: Samsung, Model: IEC-R03 remote // Brand: Samsung, Model: AK59-00167A Bluray remote (SAMSUNG36) +// Brand: Samsung, Model: AH59-02692E Soundbar remote (SAMSUNG36) +// Brand: Samsung, Model: HW-J551 Soundbar (SAMSUNG36) // Brand: Samsung, Model: AR09FSSDAWKNFA A/C (SAMSUNG_AC) // Brand: Samsung, Model: AR12KSFPEWQNET A/C (SAMSUNG_AC) // Brand: Samsung, Model: AR12HSSDBWKNEU A/C (SAMSUNG_AC) diff --git a/lib/IRremoteESP8266-2.7.8/src/ir_Sanyo.cpp b/lib/IRremoteESP8266-2.7.8/src/ir_Sanyo.cpp index 25baf380b..04a9f4f2f 100644 --- a/lib/IRremoteESP8266-2.7.8/src/ir_Sanyo.cpp +++ b/lib/IRremoteESP8266-2.7.8/src/ir_Sanyo.cpp @@ -1,6 +1,6 @@ // Copyright 2009 Ken Shirriff // Copyright 2016 marcosamarinho -// Copyright 2017 David Conran +// Copyright 2017-2020 David Conran /// @file /// @brief Support for Sanyo protocols. @@ -11,15 +11,27 @@ /// @see http://pdf.datasheetcatalog.com/datasheet/sanyo/LC7461.pdf /// @see https://github.com/marcosamarinho/IRremoteESP8266/blob/master/ir_Sanyo.cpp /// @see http://slydiman.narod.ru/scr/kb/sanyo.htm +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1211 +/// @see https://docs.google.com/spreadsheets/d/1dYfLsnYvpjV-SgO8pdinpfuBIpSzm8Q1R5SabrLeskw/edit?usp=sharing -// Supports: -// Brand: Sanyo, Model: SA 8650B - disabled -// Brand: Sanyo, Model: LC7461 transmitter IC (SANYO_LC7461) - +#include "ir_Sanyo.h" #include +#include #include "IRrecv.h" #include "IRsend.h" +#include "IRtext.h" +#include "IRutils.h" +using irutils::addBoolToString; +using irutils::addFanToString; +using irutils::addIntToString; +using irutils::addLabeledString; +using irutils::addModeToString; +using irutils::addTempToString; +using irutils::minsToString; +using irutils::sumNibbles; +using irutils::setBit; +using irutils::setBits; // Constants // Sanyo SA 8650B @@ -48,6 +60,14 @@ const uint16_t kSanyoLc7461MinGap = (kSanyoLc7461OneSpace + kSanyoLc7461ZeroSpace) / 2) + kSanyoLc7461BitMark); +const uint16_t kSanyoAcHdrMark = 8500; ///< uSeconds +const uint16_t kSanyoAcHdrSpace = 4200; ///< uSeconds +const uint16_t kSanyoAcBitMark = 500; ///< uSeconds +const uint16_t kSanyoAcOneSpace = 1600; ///< uSeconds +const uint16_t kSanyoAcZeroSpace = 550; ///< uSeconds +const uint32_t kSanyoAcGap = kDefaultMessageGap; ///< uSeconds (Guess only) +const uint16_t kSanyoAcFreq = 38000; ///< Hz. (Guess only) + #if SEND_SANYO /// Construct a Sanyo LC7461 message. /// @param[in] address The 13 bit value of the address(Custom) portion of the @@ -221,3 +241,453 @@ bool IRrecv::decodeSanyo(decode_results *results, uint16_t nbits, bool strict) { } */ #endif // DECODE_SANYO + + +#if SEND_SANYO_AC +/// Send a SanyoAc formatted message. +/// Status: STABLE / Reported as working. +/// @param[in] data An array of bytes containing the IR command. +/// @param[in] nbytes Nr. of bytes of data in the array. +/// @param[in] repeat Nr. of times the message is to be repeated. +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1211 +void IRsend::sendSanyoAc(const uint8_t data[], const uint16_t nbytes, + const uint16_t repeat) { + // Header + Data + Footer + sendGeneric(kSanyoAcHdrMark, kSanyoAcHdrSpace, + kSanyoAcBitMark, kSanyoAcOneSpace, + kSanyoAcBitMark, kSanyoAcZeroSpace, + kSanyoAcBitMark, kSanyoAcGap, + data, nbytes, kSanyoAcFreq, false, repeat, kDutyDefault); +} +#endif // SEND_SANYO_AC + +#if DECODE_SANYO_AC +/// Decode the supplied SanyoAc message. +/// Status: STABLE / Reported as working. +/// @param[in,out] results Ptr to the data to decode & where to store the decode +/// @param[in] offset The starting index to use when attempting to decode the +/// raw data. Typically/Defaults to kStartOffset. +/// @param[in] nbits The number of data bits to expect. +/// @param[in] strict Flag indicating if we should perform strict matching. +/// @return A boolean. True if it can decode it, false if it can't. +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1211 +bool IRrecv::decodeSanyoAc(decode_results *results, uint16_t offset, + const uint16_t nbits, const bool strict) { + if (strict && nbits != kSanyoAcBits) + return false; + + // Header + Data + Footer + if (!matchGeneric(results->rawbuf + offset, results->state, + results->rawlen - offset, nbits, + kSanyoAcHdrMark, kSanyoAcHdrSpace, + kSanyoAcBitMark, kSanyoAcOneSpace, + kSanyoAcBitMark, kSanyoAcZeroSpace, + kSanyoAcBitMark, kSanyoAcGap, + true, kUseDefTol, kMarkExcess, false)) return false; + // Compliance + if (strict) + if (!IRSanyoAc::validChecksum(results->state, nbits / 8)) return false; + + // Success + results->decode_type = decode_type_t::SANYO_AC; + results->bits = nbits; + // No need to record the state as we stored it as we decoded it. + // As we use result->state, we don't record value, address, or command as it + // is a union data type. + return true; +} +#endif // DECODE_SANYO_AC + +/// Class constructor +/// @param[in] pin GPIO to be used when sending. +/// @param[in] inverted Is the output signal to be inverted? +/// @param[in] use_modulation Is frequency modulation to be used? +IRSanyoAc::IRSanyoAc(const uint16_t pin, const bool inverted, + const bool use_modulation) + : _irsend(pin, inverted, use_modulation) { stateReset(); } + +/// Reset the state of the remote to a known good state/sequence. +/// @see https://docs.google.com/spreadsheets/d/1dYfLsnYvpjV-SgO8pdinpfuBIpSzm8Q1R5SabrLeskw/edit?ts=5f0190a5#gid=1050142776&range=A2:B2 +void IRSanyoAc::stateReset(void) { + static const uint8_t kReset[kSanyoAcStateLength] = { + 0x6A, 0x6D, 0x51, 0x00, 0x10, 0x45, 0x00, 0x00, 0x33}; + memcpy(remote_state, kReset, kSanyoAcStateLength); +} + +/// Set up hardware to be able to send a message. +void IRSanyoAc::begin(void) { _irsend.begin(); } + +#if SEND_SANYO_AC +/// Send the current internal state as IR messages. +/// @param[in] repeat Nr. of times the message will be repeated. +void IRSanyoAc::send(const uint16_t repeat) { + _irsend.sendSanyoAc(getRaw(), kSanyoAcStateLength, repeat); +} +#endif // SEND_SANYO_AC + +/// Get a PTR to the internal state/code for this protocol with all integrity +/// checks passing. +/// @return PTR to a code for this protocol based on the current internal state. +uint8_t* IRSanyoAc::getRaw(void) { + checksum(); + return remote_state; +} + +/// Set the internal state from a valid code for this protocol. +/// @param[in] newState A valid code for this protocol. +void IRSanyoAc::setRaw(const uint8_t newState[]) { + memcpy(remote_state, newState, kSanyoAcStateLength); +} + +/// Calculate the checksum for a given state. +/// @param[in] state The array to calc the checksum of. +/// @param[in] length The length/size of the array. +/// @return The calculated checksum value. +uint8_t IRSanyoAc::calcChecksum(const uint8_t state[], + const uint16_t length) { + return length ? sumNibbles(state, length - 1) : 0; +} + +/// Verify the checksum is valid for a given state. +/// @param[in] state The array to verify the checksum of. +/// @param[in] length The length/size of the array. +/// @return true, if the state has a valid checksum. Otherwise, false. +bool IRSanyoAc::validChecksum(const uint8_t state[], const uint16_t length) { + return length && state[length - 1] == IRSanyoAc::calcChecksum(state, length); +} + +/// Calculate & set the checksum for the current internal state of the remote. +void IRSanyoAc::checksum(void) { + // Stored the checksum value in the last byte. + remote_state[kSanyoAcStateLength - 1] = calcChecksum(remote_state); +} + + +/// Set the requested power state of the A/C to on. +void IRSanyoAc::on(void) { setPower(true); } + +/// Set the requested power state of the A/C to off. +void IRSanyoAc::off(void) { setPower(false); } + +/// Change the power setting. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRSanyoAc::setPower(const bool on) { + setBits(&remote_state[kSanyoAcPowerByte], kSanyoAcPowerOffset, + kSanyoAcPowerSize, on ? kSanyoAcPowerOn : kSanyoAcPowerOff); +} + +/// Get the value of the current power setting. +/// @return true, the setting is on. false, the setting is off. +bool IRSanyoAc::getPower(void) { + return GETBITS8(remote_state[kSanyoAcPowerByte], kSanyoAcPowerOffset, + kSanyoAcPowerSize) == kSanyoAcPowerOn; +} + +/// Get the operating mode setting of the A/C. +/// @return The current operating mode setting. +uint8_t IRSanyoAc::getMode(void) { + return GETBITS8(remote_state[kSanyoAcModeByte], kSanyoAcModeOffset, + kSanyoAcModeSize); +} + +/// Set the operating mode of the A/C. +/// @param[in] mode The desired operating mode. +/// @note If we get an unexpected mode, default to AUTO. +void IRSanyoAc::setMode(const uint8_t mode) { + switch (mode) { + case kSanyoAcAuto: + case kSanyoAcCool: + case kSanyoAcDry: + case kSanyoAcHeat: + setBits(&remote_state[kSanyoAcModeByte], kSanyoAcModeOffset, + kSanyoAcModeSize, mode); + break; + default: setMode(kSanyoAcAuto); + } +} + +/// Convert a stdAc::opmode_t enum into its native mode. +/// @param[in] mode The enum to be converted. +/// @return The native equivilant of the enum. +uint8_t IRSanyoAc::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: return kSanyoAcCool; + case stdAc::opmode_t::kHeat: return kSanyoAcHeat; + case stdAc::opmode_t::kDry: return kSanyoAcDry; + default: return kSanyoAcAuto; + } +} + +/// Convert a native mode into its stdAc equivilant. +/// @param[in] mode The native setting to be converted. +/// @return The stdAc equivilant of the native setting. +stdAc::opmode_t IRSanyoAc::toCommonMode(const uint8_t mode) { + switch (mode) { + case kSanyoAcCool: return stdAc::opmode_t::kCool; + case kSanyoAcHeat: return stdAc::opmode_t::kHeat; + case kSanyoAcDry: return stdAc::opmode_t::kDry; + default: return stdAc::opmode_t::kAuto; + } +} + +/// Set the temperature at a given location. +/// @param[out] ptr A pointer to a temperature byte. +/// @param[in] degrees The temperature in degrees celsius. +void IRSanyoAc::_setTemp(uint8_t *ptr, const uint8_t degrees) { + uint8_t temp = std::max((uint8_t)kSanyoAcTempMin, degrees); + temp = std::min((uint8_t)kSanyoAcTempMax, temp); + setBits(ptr, kSanyoAcTempOffset, kSanyoAcTempSize, temp - kSanyoAcTempDelta); +} + +/// Get the temperature from a given location. +/// @param[in] ptr A pointer to a temperature byte. +/// @return The current setting for temp. in degrees celsius. +uint8_t IRSanyoAc::_getTemp(uint8_t *ptr) { + return GETBITS8(*ptr, kSanyoAcTempOffset, kSanyoAcTempSize) + + kSanyoAcTempDelta; +} + +/// Set the desired temperature. +/// @param[in] degrees The temperature in degrees celsius. +void IRSanyoAc::setTemp(const uint8_t degrees) { + _setTemp(&remote_state[kSanyoAcTempByte], degrees); +} + +/// Get the current desired temperature setting. +/// @return The current setting for temp. in degrees celsius. +uint8_t IRSanyoAc::getTemp(void) { + return _getTemp(&remote_state[kSanyoAcTempByte]); +} + +/// Set the sensor temperature. +/// @param[in] degrees The temperature in degrees celsius. +void IRSanyoAc::setSensorTemp(const uint8_t degrees) { + _setTemp(&remote_state[kSanyoAcSensorByte], degrees); +} + +/// Get the current sensor temperature setting. +/// @return The current setting for temp. in degrees celsius. +uint8_t IRSanyoAc::getSensorTemp(void) { + return _getTemp(&remote_state[kSanyoAcSensorByte]); +} + +/// Set the speed of the fan. +/// @param[in] speed The desired setting. +void IRSanyoAc::setFan(const uint8_t speed) { + setBits(&remote_state[kSanyoAcModeByte], kSanyoAcFanOffset, kSanyoAcFanSize, + speed); +} + +/// Get the current fan speed setting. +/// @return The current fan speed/mode. +uint8_t IRSanyoAc::getFan(void) { + return GETBITS8(remote_state[kSanyoAcModeByte], kSanyoAcFanOffset, + kSanyoAcFanSize); +} + +/// Convert a stdAc::fanspeed_t enum into it's native speed. +/// @param[in] speed The enum to be converted. +/// @return The native equivilant of the enum. +uint8_t IRSanyoAc::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: + case stdAc::fanspeed_t::kLow: return kSanyoAcFanLow; + case stdAc::fanspeed_t::kMedium: return kSanyoAcFanMedium; + case stdAc::fanspeed_t::kHigh: + case stdAc::fanspeed_t::kMax: return kSanyoAcFanHigh; + default: return kSanyoAcFanAuto; + } +} + +/// Convert a native fan speed into its stdAc equivilant. +/// @param[in] spd The native setting to be converted. +/// @return The stdAc equivilant of the native setting. +stdAc::fanspeed_t IRSanyoAc::toCommonFanSpeed(const uint8_t spd) { + switch (spd) { + case kSanyoAcFanHigh: return stdAc::fanspeed_t::kHigh; + case kSanyoAcFanMedium: return stdAc::fanspeed_t::kMedium; + case kSanyoAcFanLow: return stdAc::fanspeed_t::kLow; + default: return stdAc::fanspeed_t::kAuto; + } +} + +/// Get the vertical swing setting of the A/C. +/// @return The current swing mode setting. +uint8_t IRSanyoAc::getSwingV(void) { + return GETBITS8(remote_state[kSanyoAcPowerByte], kSanyoAcSwingVOffset, + kSanyoAcSwingVSize); +} + +/// Set the vertical swing setting of the A/C. +/// @param[in] setting The value of the desired setting. +void IRSanyoAc::setSwingV(const uint8_t setting) { + if (setting == kSanyoAcSwingVAuto || + (setting >= kSanyoAcSwingVLowest && setting <= kSanyoAcSwingVHighest)) + setBits(&remote_state[kSanyoAcPowerByte], kSanyoAcSwingVOffset, + kSanyoAcSwingVSize, setting); + + else + setSwingV(kSanyoAcSwingVAuto); +} + +/// Convert a stdAc::swingv_t enum into it's native setting. +/// @param[in] position The enum to be converted. +/// @return The native equivilant of the enum. +uint8_t IRSanyoAc::convertSwingV(const stdAc::swingv_t position) { + switch (position) { + case stdAc::swingv_t::kHighest: return kSanyoAcSwingVHighest; + case stdAc::swingv_t::kHigh: return kSanyoAcSwingVHigh; + case stdAc::swingv_t::kMiddle: return kSanyoAcSwingVUpperMiddle; + case stdAc::swingv_t::kLow: return kSanyoAcSwingVLow; + case stdAc::swingv_t::kLowest: return kSanyoAcSwingVLowest; + default: return kSanyoAcSwingVAuto; + } +} + +/// Convert a native vertical swing postion to it's common equivalent. +/// @param[in] setting A native position to convert. +/// @return The common vertical swing position. +stdAc::swingv_t IRSanyoAc::toCommonSwingV(const uint8_t setting) { + switch (setting) { + case kSanyoAcSwingVHighest: return stdAc::swingv_t::kHighest; + case kSanyoAcSwingVHigh: return stdAc::swingv_t::kHigh; + case kSanyoAcSwingVUpperMiddle: + case kSanyoAcSwingVLowerMiddle: return stdAc::swingv_t::kMiddle; + case kSanyoAcSwingVLow: return stdAc::swingv_t::kLow; + case kSanyoAcSwingVLowest: return stdAc::swingv_t::kLowest; + default: return stdAc::swingv_t::kAuto; + } +} + +/// Set the Sleep (Night Setback) setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRSanyoAc::setSleep(const bool on) { + setBit(&remote_state[kSanyoAcSleepByte], kSanyoAcSleepBit, on); +} + +/// Get the Sleep (Night Setback) setting of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRSanyoAc::getSleep(void) { + return GETBIT8(remote_state[kSanyoAcSleepByte], kSanyoAcSleepBit); +} + +/// Set the Sensor Location setting of the A/C. +/// i.e. Where the ambient temperature is measured. +/// @param[in] location true is Unit/Wall, false is Remote/Room. +void IRSanyoAc::setSensor(const bool location) { + setBit(&remote_state[kSanyoAcSensorByte], kSanyoAcSensorBit, location); +} + +/// Get the Sensor Location setting of the A/C. +/// i.e. Where the ambient temperature is measured. +/// @return true is Unit/Wall, false is Remote/Room. +bool IRSanyoAc::getSensor(void) { + return GETBIT8(remote_state[kSanyoAcSensorByte], kSanyoAcSensorBit); +} + +/// Set the Beep setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRSanyoAc::setBeep(const bool on) { + setBit(&remote_state[kSanyoAcSensorByte], kSanyoAcBeepBit, on); +} + +/// Get the Beep setting of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRSanyoAc::getBeep(void) { + return GETBIT8(remote_state[kSanyoAcSensorByte], kSanyoAcBeepBit); +} + +/// Get the nr of minutes the Off Timer is set to. +/// @return The timer time expressed as the number of minutes. +/// A value of 0 means the Off Timer is off/disabled. +/// @note The internal precission has a resolution of 1 hour. +uint16_t IRSanyoAc::getOffTimer(void) { + if (GETBIT8(remote_state[kSanyoAcModeByte], kSanyoAcOffTimerEnableBit)) + return GETBITS8(remote_state[kSanyoAcOffHourByte], kSanyoAcOffHourOffset, + kSanyoAcOffHourSize) * 60; + else + return 0; +} + +/// Set the nr of minutes for the Off Timer. +/// @param[in] mins The timer time expressed as nr. of minutes. +/// A value of 0 means the Off Timer is off/disabled. +/// @note The internal precission has a resolution of 1 hour. +void IRSanyoAc::setOffTimer(const uint16_t mins) { + const uint8_t hours = std::min((uint8_t)(mins / 60), kSanyoAcHourMax); + setBit(&remote_state[kSanyoAcModeByte], kSanyoAcOffTimerEnableBit, hours > 0); + setBits(&remote_state[kSanyoAcOffHourByte], kSanyoAcOffHourOffset, + kSanyoAcOffHourSize, hours); +} + +/// Convert the current internal state into its stdAc::state_t equivilant. +/// @return The stdAc equivilant of the native settings. +stdAc::state_t IRSanyoAc::toCommon(void) { + stdAc::state_t result; + result.protocol = decode_type_t::SANYO_AC; + result.model = -1; // Not supported. + result.power = getPower(); + result.mode = toCommonMode(getMode()); + result.celsius = true; + result.degrees = getTemp(); + result.fanspeed = toCommonFanSpeed(getFan()); + result.sleep = getSleep() ? 0 : -1; + result.swingv = toCommonSwingV(getSwingV()); + result.beep = getBeep(); + // Not supported. + result.swingh = stdAc::swingh_t::kOff; + result.turbo = false; + result.econo = false; + result.light = false; + result.filter = false; + result.quiet = false; + result.clean = false; + result.clock = -1; + return result; +} + +/// Convert the current internal state into a human readable string. +/// @return A human readable string. +String IRSanyoAc::toString(void) { + String result = ""; + result.reserve(140); + result += addBoolToString(getPower(), kPowerStr, false); + result += addModeToString(getMode(), kSanyoAcAuto, kSanyoAcCool, + kSanyoAcHeat, kSanyoAcDry, kSanyoAcAuto); + result += addTempToString(getTemp()); + result += addFanToString(getFan(), kSanyoAcFanHigh, kSanyoAcFanLow, + kSanyoAcFanAuto, kSanyoAcFanAuto, + kSanyoAcFanMedium); + result += addIntToString(getSwingV(), kSwingVStr); + result += kSpaceLBraceStr; + switch (getSwingV()) { + case kSanyoAcSwingVHighest: result += kHighestStr; break; + case kSanyoAcSwingVHigh: result += kHighStr; break; + case kSanyoAcSwingVUpperMiddle: + result += kUpperStr; + result += ' '; + result += kMiddleStr; + break; + case kSanyoAcSwingVLowerMiddle: + result += kLowerStr; + result += ' '; + result += kMiddleStr; + break; + case kSanyoAcSwingVLow: result += kLowStr; break; + case kSanyoAcSwingVLowest: result += kLowestStr; break; + case kSanyoAcSwingVAuto: result += kAutoStr; break; + default: result += kUnknownStr; + } + result += ')'; + result += addBoolToString(getSleep(), kSleepStr); + result += addBoolToString(getBeep(), kBeepStr); + result += addLabeledString(getSensor() ? kRoomStr : kWallStr, kSensorStr); + result += kCommaSpaceStr; + result += kSensorStr; + result += ' '; + result += addTempToString(getSensorTemp(), true, false); + const uint16_t offtime = getOffTimer(); + result += addLabeledString(offtime ? minsToString(offtime) : kOffStr, + kOffTimerStr); + return result; +} diff --git a/lib/IRremoteESP8266-2.7.8/src/ir_Sanyo.h b/lib/IRremoteESP8266-2.7.8/src/ir_Sanyo.h new file mode 100644 index 000000000..9cdde61c8 --- /dev/null +++ b/lib/IRremoteESP8266-2.7.8/src/ir_Sanyo.h @@ -0,0 +1,163 @@ +// Copyright 2020 David Conran + +/// @file +/// @brief Support for Sanyo protocols. +/// Sanyo LC7461 support originally by marcosamarinho +/// Sanyo SA 8650B originally added from +/// https://github.com/shirriff/Arduino-IRremote/ +/// @see https://github.com/z3t0/Arduino-IRremote/blob/master/ir_Sanyo.cpp +/// @see http://pdf.datasheetcatalog.com/datasheet/sanyo/LC7461.pdf +/// @see https://github.com/marcosamarinho/IRremoteESP8266/blob/master/ir_Sanyo.cpp +/// @see http://slydiman.narod.ru/scr/kb/sanyo.htm +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1211 +/// @see https://docs.google.com/spreadsheets/d/1dYfLsnYvpjV-SgO8pdinpfuBIpSzm8Q1R5SabrLeskw/edit?usp=sharing + +// Supports: +// Brand: Sanyo, Model: SA 8650B - disabled +// Brand: Sanyo, Model: LC7461 transmitter IC (SANYO_LC7461) +// Brand: Sanyo, Model: SAP-K121AHA A/C (SANYO_AC) +// Brand: Sanyo, Model: RCS-2HS4E remote (SANYO_AC) +// Brand: Sanyo, Model: SAP-K242AH A/C (SANYO_AC) +// Brand: Sanyo, Model: RCS-2S4E remote (SANYO_AC) + +#ifndef IR_SANYO_H_ +#define IR_SANYO_H_ + +#define __STDC_LIMIT_MACROS +#include +#ifdef ARDUINO +#include +#endif +#include "IRremoteESP8266.h" +#include "IRsend.h" +#ifdef UNIT_TEST +#include "IRsend_test.h" +#endif + +// Constants + +// Sanyo A/C +// Ref: https://docs.google.com/spreadsheets/d/1dYfLsnYvpjV-SgO8pdinpfuBIpSzm8Q1R5SabrLeskw/edit?usp=sharing +// Byte[0] - 0x6A (Fixed?) +// Byte[1] - Address + Temperature +const uint8_t kSanyoAcTempByte = 1; ///< Index +const uint8_t kSanyoAcTempOffset = 0; ///< Mask 0b000xxxxx +const uint8_t kSanyoAcTempSize = 5; ///< Mask 0b000xxxxx +const uint8_t kSanyoAcTempMin = 16; ///< Celsius +const uint8_t kSanyoAcTempMax = 30; ///< Celsius +const uint8_t kSanyoAcTempDelta = 4; ///< Celsius to Native Temp difference. +// Byte[2] - Ambient Temp + Sensor +const uint8_t kSanyoAcSensorByte = 2; ///< Index +const uint8_t kSanyoAcSensorBit = 2; ///< Mask 0b00x00000 +// Ambient Temp Mask 0b000xxxxx +const uint8_t kSanyoAcBeepBit = 6; ///< Mask 0b0x000000 +// Byte[3] - Off Hour +const uint8_t kSanyoAcOffHourByte = 3; ///< Index +const uint8_t kSanyoAcOffHourOffset = 0; ///< Mask 0b0000xxxx +const uint8_t kSanyoAcOffHourSize = 4; ///< Mask 0b0000xxxx +const uint8_t kSanyoAcHourMax = 15; ///< 0b1111 +// Byte[4] - Mode + Fan + Timer Enables +const uint8_t kSanyoAcModeByte = 4; ///< Index +const uint8_t kSanyoAcModeOffset = 4; ///< Mask 0b0xxx0000 +const uint8_t kSanyoAcModeSize = 3; ///< Mask 0b0xxx0000 +const uint8_t kSanyoAcHeat = 1; ///< 0b001 +const uint8_t kSanyoAcCool = 2; ///< 0b010 +const uint8_t kSanyoAcDry = 3; ///< 0b011 +const uint8_t kSanyoAcAuto = 4; ///< 0b100 +const uint8_t kSanyoAcOffTimerEnableBit = 2; ///< Mask 0b00000x00 +const uint8_t kSanyoAcFanOffset = 0; ///< Mask 0b000000xx +const uint8_t kSanyoAcFanSize = 2; ///< Mask 0b000000xx +const uint8_t kSanyoAcFanAuto = 0; ///< 0b00 +const uint8_t kSanyoAcFanHigh = 1; ///< 0b01 +const uint8_t kSanyoAcFanLow = 2; ///< 0b10 +const uint8_t kSanyoAcFanMedium = 3; ///< 0b11 +// Byte[5] - Power + SwingV +const uint8_t kSanyoAcPowerByte = 5; ///< Index +const uint8_t kSanyoAcPowerOffset = 6; ///< Mask 0bxx000000 +const uint8_t kSanyoAcPowerSize = 2; ///< Mask 0bxx000000 +// const uint8_t kSanyoAcPowerStandby = 0b00; ///< Standby? +const uint8_t kSanyoAcPowerOff = 0b01; ///< Off +const uint8_t kSanyoAcPowerOn = 0b10; ///< On +const uint8_t kSanyoAcSwingVOffset = 0; ///< Mask 0b00000xxx +const uint8_t kSanyoAcSwingVSize = 3; ///< Mask 0b00000xxx +const uint8_t kSanyoAcSwingVAuto = 0; ///< 0b000 +const uint8_t kSanyoAcSwingVLowest = 2; ///< 0b010 +const uint8_t kSanyoAcSwingVLow = 3; ///< 0b011 +const uint8_t kSanyoAcSwingVLowerMiddle = 4; ///< 0b100 +const uint8_t kSanyoAcSwingVUpperMiddle = 5; ///< 0b101 +const uint8_t kSanyoAcSwingVHigh = 6; ///< 0b110 +const uint8_t kSanyoAcSwingVHighest = 7; ///< 0b111 +// Byte[6] - Sleep +const uint8_t kSanyoAcSleepByte = 6; ///< Index +const uint8_t kSanyoAcSleepBit = 3; ///< Mask 0b0000x000 +// Byte[8] - Checksum (8-bit Sum of all preceeding nibbles) + + +// Classes +/// Class for handling detailed Sanyo A/C messages. +class IRSanyoAc { + public: + explicit IRSanyoAc(const uint16_t pin, const bool inverted = false, + const bool use_modulation = true); + void stateReset(void); +#if SEND_SANYO_AC + void send(const uint16_t repeat = kNoRepeat); + /// Run the calibration to calculate uSec timing offsets for this platform. + /// @return The uSec timing offset needed per modulation of the IR Led. + /// @note This will produce a 65ms IR signal pulse at 38kHz. + /// Only ever needs to be run once per object instantiation, if at all. + int8_t calibrate(void) { return _irsend.calibrate(); } +#endif // SEND_SANYO_AC + void begin(void); + void on(void); + void off(void); + void setPower(const bool on); + bool getPower(void); + void setTemp(const uint8_t degrees); + uint8_t getTemp(void); + void setSensorTemp(const uint8_t degrees); + uint8_t getSensorTemp(void); + void setFan(const uint8_t speed); + uint8_t getFan(void); + void setMode(const uint8_t mode); + uint8_t getMode(void); + void setSleep(const bool on); + bool getSleep(void); + void setSensor(const bool location); + bool getSensor(void); + void setBeep(const bool on); + bool getBeep(void); + void setSwingV(const uint8_t setting); + uint8_t getSwingV(void); + void setRaw(const uint8_t newState[]); + uint8_t* getRaw(void); + uint16_t getOffTimer(void); + void setOffTimer(const uint16_t mins); + static bool validChecksum(const uint8_t state[], + const uint16_t length = kSanyoAcStateLength); + uint8_t convertMode(const stdAc::opmode_t mode); + uint8_t convertFan(const stdAc::fanspeed_t speed); + uint8_t convertSwingV(const stdAc::swingv_t position); + static stdAc::opmode_t toCommonMode(const uint8_t mode); + static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed); + static stdAc::swingv_t toCommonSwingV(const uint8_t setting); + stdAc::state_t toCommon(void); + String toString(void); +#ifndef UNIT_TEST + + private: + IRsend _irsend; ///< Instance of the IR send class +#else // UNIT_TEST + /// @cond IGNORE + IRsendTest _irsend; ///< Instance of the testing IR send class + /// @endcond +#endif // UNIT_TEST + uint8_t remote_state[kSanyoAcStateLength]; ///< The state in IR code form. + void checksum(void); + static uint8_t calcChecksum(const uint8_t state[], + const uint16_t length = kSanyoAcStateLength); + void _setTemp(uint8_t *ptr, const uint8_t degrees); + uint8_t _getTemp(uint8_t *ptr); +}; + +#endif // IR_SANYO_H_ diff --git a/lib/IRremoteESP8266-2.7.8/src/ir_Toshiba.cpp b/lib/IRremoteESP8266-2.7.8/src/ir_Toshiba.cpp index c28b700f8..f799885b3 100644 --- a/lib/IRremoteESP8266-2.7.8/src/ir_Toshiba.cpp +++ b/lib/IRremoteESP8266-2.7.8/src/ir_Toshiba.cpp @@ -4,6 +4,9 @@ /// @brief Support for Toshiba protocols. /// @see https://github.com/r45635/HVAC-IR-Control /// @see https://github.com/r45635/HVAC-IR-Control/blob/master/HVAC_ESP8266/HVAC_ESP8266T.ino#L77 +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1205 +/// @see https://www.toshiba-carrier.co.jp/global/about/index.htm +/// @see http://www.toshiba-carrier.co.th/AboutUs/Pages/CompanyProfile.aspx #include "ir_Toshiba.h" #include @@ -21,10 +24,10 @@ // Toshiba A/C const uint16_t kToshibaAcHdrMark = 4400; const uint16_t kToshibaAcHdrSpace = 4300; -const uint16_t kToshibaAcBitMark = 543; -const uint16_t kToshibaAcOneSpace = 1623; -const uint16_t kToshibaAcZeroSpace = 472; -const uint16_t kToshibaAcMinGap = 7048; +const uint16_t kToshibaAcBitMark = 580; +const uint16_t kToshibaAcOneSpace = 1600; +const uint16_t kToshibaAcZeroSpace = 490; +const uint16_t kToshibaAcMinGap = 7400; using irutils::addBoolToString; using irutils::addFanToString; @@ -32,6 +35,8 @@ using irutils::addIntToString; using irutils::addLabeledString; using irutils::addModeToString; using irutils::addTempToString; +using irutils::checkInvertedBytePairs; +using irutils::invertBytePairs; using irutils::setBit; using irutils::setBits; @@ -41,10 +46,8 @@ using irutils::setBits; /// @param[in] data The message to be sent. /// @param[in] nbytes The number of bytes of message to be sent. /// @param[in] repeat The number of times the command is to be repeated. -void IRsend::sendToshibaAC(const unsigned char data[], const uint16_t nbytes, +void IRsend::sendToshibaAC(const uint8_t data[], const uint16_t nbytes, const uint16_t repeat) { - if (nbytes < kToshibaACStateLength) - return; // Not enough bytes to send a proper message. sendGeneric(kToshibaAcHdrMark, kToshibaAcHdrSpace, kToshibaAcBitMark, kToshibaAcOneSpace, kToshibaAcBitMark, kToshibaAcZeroSpace, kToshibaAcBitMark, kToshibaAcMinGap, data, nbytes, 38, true, @@ -58,7 +61,7 @@ void IRsend::sendToshibaAC(const unsigned char data[], const uint16_t nbytes, /// @param[in] use_modulation Is frequency modulation to be used? IRToshibaAC::IRToshibaAC(const uint16_t pin, const bool inverted, const bool use_modulation) - : _irsend(pin, inverted, use_modulation) { this->stateReset(); } + : _irsend(pin, inverted, use_modulation) { stateReset(); } /// Reset the state of the remote to a known good state/sequence. /// @see https://github.com/r45635/HVAC-IR-Control/blob/master/HVAC_ESP8266/HVAC_ESP8266T.ino#L103 @@ -66,24 +69,72 @@ void IRToshibaAC::stateReset(void) { static const uint8_t kReset[kToshibaACStateLength] = { 0xF2, 0x0D, 0x03, 0xFC, 0x01}; memcpy(remote_state, kReset, kToshibaACStateLength); - mode_state = getMode(true); + setTemp(22); // Remote defaults to 22C after factory reset. So do the same. + setSwing(kToshibaAcSwingOff); + prev_mode = getMode(); } /// Set up hardware to be able to send a message. void IRToshibaAC::begin(void) { _irsend.begin(); } #if SEND_TOSHIBA_AC -/// Send the current internal state as an IR message. +/// Send the current internal state as IR messages. /// @param[in] repeat Nr. of times the message will be repeated. void IRToshibaAC::send(const uint16_t repeat) { - _irsend.sendToshibaAC(getRaw(), kToshibaACStateLength, repeat); + _backupState(); + _irsend.sendToshibaAC(getRaw(), getStateLength(), repeat); + if (_send_swing && (getStateLength() != kToshibaACStateLengthShort)) { + setStateLength(kToshibaACStateLengthShort); + // Swing settings expect the min temp to be set. + // Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/1205#issuecomment-653922374 + setTemp(kToshibaAcMinTemp); + setSwing(_swing_mode); + _irsend.sendToshibaAC(getRaw(), getStateLength(), repeat); + _restoreState(); + } + _send_swing = false; } #endif // SEND_TOSHIBA_AC -/// Get a PTR to the internal state/code for this protocol. +/// Get the length of the supplied Toshiba state per it's protocol structure. +/// @param[in] state The array to get the built-in length from. +/// @param[in] size The physical size of the state array. +/// @return Nr. of bytes in use for the provided state message. +uint16_t IRToshibaAC::getInternalStateLength(const uint8_t state[], + const uint16_t size) { + if (size < kToshibaAcLengthByte) return 0; + return std::min((uint16_t)(state[kToshibaAcLengthByte] + kToshibaAcMinLength), + kToshibaACStateLengthLong); +} + +/// Get the length of the current internal state per the protocol structure. +/// @return Nr. of bytes in use for the current internal state message. +uint16_t IRToshibaAC::getStateLength(void) { + return getInternalStateLength(remote_state, kToshibaACStateLengthLong); +} + +/// Set the internal length of the current internal state per the protocol. +/// @param[in] size Nr. of bytes in use for the current internal state message. +void IRToshibaAC::setStateLength(const uint16_t size) { + if (size < kToshibaAcMinLength) return; + remote_state[kToshibaAcLengthByte] = size - kToshibaAcMinLength; +} + +/// Make a copy of the internal code-form A/C state. +void IRToshibaAC::_backupState(void) { + memcpy(backup, remote_state, kToshibaACStateLengthLong); +} + +/// Recover the internal code-form A/C state from the backup. +void IRToshibaAC::_restoreState(void) { + memcpy(remote_state, backup, kToshibaACStateLengthLong); +} + +/// Get a PTR to the internal state/code for this protocol with all integrity +/// checks passing. /// @return PTR to a code for this protocol based on the current internal state. uint8_t* IRToshibaAC::getRaw(void) { - this->checksum(); + checksum(getStateLength()); return remote_state; } @@ -91,7 +142,8 @@ uint8_t* IRToshibaAC::getRaw(void) { /// @param[in] newState A valid code for this protocol. void IRToshibaAC::setRaw(const uint8_t newState[]) { memcpy(remote_state, newState, kToshibaACStateLength); - mode_state = this->getMode(true); + prev_mode = getMode(); + _send_swing = true; } /// Calculate the checksum for a given state. @@ -100,13 +152,7 @@ void IRToshibaAC::setRaw(const uint8_t newState[]) { /// @return The calculated checksum value. uint8_t IRToshibaAC::calcChecksum(const uint8_t state[], const uint16_t length) { - uint8_t checksum = 0; - // Only calculate it for valid lengths. - if (length > 1) { - // Checksum is simple XOR of all bytes except the last one. - for (uint8_t i = 0; i < length - 1; i++) checksum ^= state[i]; - } - return checksum; + return length ? xorBytes(state, length - 1) : 0; } /// Verify the checksum is valid for a given state. @@ -114,17 +160,27 @@ uint8_t IRToshibaAC::calcChecksum(const uint8_t state[], /// @param[in] length The length/size of the array. /// @return true, if the state has a valid checksum. Otherwise, false. bool IRToshibaAC::validChecksum(const uint8_t state[], const uint16_t length) { - return (length > 1 && state[length - 1] == IRToshibaAC::calcChecksum(state, - length)); + return length >= kToshibaAcMinLength && + state[length - 1] == IRToshibaAC::calcChecksum(state, length) && + checkInvertedBytePairs(state, kToshibaAcInvertedLength) && + IRToshibaAC::getInternalStateLength(state, length) == length; } /// Calculate & set the checksum for the current internal state of the remote. /// @param[in] length The length/size of the internal array to checksum. - void IRToshibaAC::checksum(const uint16_t length) { // Stored the checksum value in the last byte. - if (length > 1) remote_state[length - 1] = this->calcChecksum(remote_state, - length); + if (length >= kToshibaAcMinLength) { + // Set/clear the short msg bit. + setBit(&remote_state[4], kToshibaAcShortMsgBit, + getStateLength() == kToshibaACStateLengthShort); + // Set/clear the long msg bit. + setBit(&remote_state[4], kToshibaAcLongMsgBit, + getStateLength() == kToshibaACStateLengthLong); + invertBytePairs(remote_state, kToshibaAcInvertedLength); + // Always do the Xor checksum LAST! + remote_state[length - 1] = calcChecksum(remote_state, length); + } } /// Set the requested power state of the A/C to on. @@ -136,19 +192,18 @@ void IRToshibaAC::off(void) { setPower(false); } /// Change the power setting. /// @param[in] on true, the setting is on. false, the setting is off. void IRToshibaAC::setPower(const bool on) { - setBit(&remote_state[6], kToshibaAcPowerOffset, !on); // Cleared when on. - if (on) - setMode(mode_state); - else - setBits(&remote_state[6], kToshibaAcModeOffset, kToshibaAcModeSize, - kToshibaAcHeat); + if (on) { // On + // If not already on, pick the last non-off mode used + if (!getPower()) setMode(prev_mode); + } else { // Off + setMode(kToshibaAcOff); + } } - /// Get the value of the current power setting. /// @return true, the setting is on. false, the setting is off. bool IRToshibaAC::getPower(void) { - return !GETBIT8(remote_state[6], kToshibaAcPowerOffset); + return getMode(true) != kToshibaAcOff; } /// Set the temperature. @@ -187,32 +242,104 @@ uint8_t IRToshibaAC::getFan(void) { return --fan; } +/// Get the swing setting of the A/C. +/// @param[in] raw Calculate the answer from just the state data. +/// @return The current swing mode setting. +uint8_t IRToshibaAC::getSwing(const bool raw) { + return raw ? GETBITS8(remote_state[5], kToshibaAcSwingOffset, + kToshibaAcSwingSize) : _swing_mode; +} + +/// Set the swing setting of the A/C. +/// @param[in] setting The value of the desired setting. +void IRToshibaAC::setSwing(const uint8_t setting) { + switch (setting) { + case kToshibaAcSwingStep: + case kToshibaAcSwingOn: + case kToshibaAcSwingOff: + _send_swing = true; + _swing_mode = setting; + if (getStateLength() == kToshibaACStateLengthShort) + setBits(&remote_state[5], kToshibaAcSwingOffset, kToshibaAcSwingSize, + setting); + } +} + /// Get the operating mode setting of the A/C. -/// @param[in] useRaw Indicate to get the mode from the internal state array. +/// @param[in] raw Get the value without any intelligent processing. /// @return The current operating mode setting. -uint8_t IRToshibaAC::getMode(const bool useRaw) { - if (useRaw) - return GETBITS8(remote_state[6], kToshibaAcModeOffset, kToshibaAcModeSize); - else - return mode_state; +uint8_t IRToshibaAC::getMode(const bool raw) { + const uint8_t mode = GETBITS8(remote_state[6], kToshibaAcModeOffset, + kToshibaAcModeSize); + if (raw) return mode; + switch (mode) { + case kToshibaAcOff: return prev_mode; + default: return mode; + } } /// Set the operating mode of the A/C. /// @param[in] mode The desired operating mode. /// @note If we get an unexpected mode, default to AUTO. +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1205#issuecomment-654446771 void IRToshibaAC::setMode(const uint8_t mode) { + if (mode != prev_mode) + // Changing mode or power turns Econo & Turbo to off on a real remote. + // Setting the internal message length to "normal" will do that. + setStateLength(kToshibaACStateLength); switch (mode) { case kToshibaAcAuto: case kToshibaAcCool: case kToshibaAcDry: case kToshibaAcHeat: - mode_state = mode; - // Only adjust the remote_state if we have power set to on. - if (getPower()) - setBits(&remote_state[6], kToshibaAcModeOffset, kToshibaAcModeSize, - mode_state); - return; - default: this->setMode(kToshibaAcAuto); // There is no Fan mode. + case kToshibaAcFan: + prev_mode = mode; + // FALL-THRU + case kToshibaAcOff: + setBits(&remote_state[6], kToshibaAcModeOffset, kToshibaAcModeSize, + mode); + break; + default: setMode(kToshibaAcAuto); + } +} + +/// Get the Turbo (Powerful) setting of the A/C. +/// @return true, if the current setting is on. Otherwise, false. +bool IRToshibaAC::getTurbo(void) { + if (getStateLength() == kToshibaACStateLengthLong) + return remote_state[8] == kToshibaAcTurboOn; + return false; +} + +/// Set the Turbo (Powerful) setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +/// Note: Turbo mode is mutually exclusive with Economy mode. +void IRToshibaAC::setTurbo(const bool on) { + if (on) { + remote_state[8] = kToshibaAcTurboOn; + setStateLength(kToshibaACStateLengthLong); + } else { + if (!getEcono()) setStateLength(kToshibaACStateLength); + } +} + +/// Get the Economy mode setting of the A/C. +/// @return true, if the current setting is on. Otherwise, false. +bool IRToshibaAC::getEcono(void) { + if (getStateLength() == kToshibaACStateLengthLong) + return remote_state[8] == kToshibaAcEconoOn; + return false; +} + +/// Set the Economy mode setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +/// Note: Economy mode is mutually exclusive with Turbo mode. +void IRToshibaAC::setEcono(const bool on) { + if (on) { + remote_state[8] = kToshibaAcEconoOn; + setStateLength(kToshibaACStateLengthLong); + } else { + if (!getTurbo()) setStateLength(kToshibaACStateLength); } } @@ -224,7 +351,8 @@ uint8_t IRToshibaAC::convertMode(const stdAc::opmode_t mode) { case stdAc::opmode_t::kCool: return kToshibaAcCool; case stdAc::opmode_t::kHeat: return kToshibaAcHeat; case stdAc::opmode_t::kDry: return kToshibaAcDry; - // No Fan mode. + case stdAc::opmode_t::kFan: return kToshibaAcFan; + case stdAc::opmode_t::kOff: return kToshibaAcOff; default: return kToshibaAcAuto; } } @@ -251,6 +379,8 @@ stdAc::opmode_t IRToshibaAC::toCommonMode(const uint8_t mode) { case kToshibaAcCool: return stdAc::opmode_t::kCool; case kToshibaAcHeat: return stdAc::opmode_t::kHeat; case kToshibaAcDry: return stdAc::opmode_t::kDry; + case kToshibaAcFan: return stdAc::opmode_t::kFan; + case kToshibaAcOff: return stdAc::opmode_t::kOff; default: return stdAc::opmode_t::kAuto; } } @@ -280,12 +410,13 @@ stdAc::state_t IRToshibaAC::toCommon(void) { result.celsius = true; result.degrees = this->getTemp(); result.fanspeed = this->toCommonFanSpeed(this->getFan()); + result.swingv = (getSwing() == kToshibaAcSwingOn) ? stdAc::swingv_t::kAuto + : stdAc::swingv_t::kOff; + result.turbo = getTurbo(); + result.econo = getEcono(); // Not supported. - result.turbo = false; result.light = false; result.filter = false; - result.econo = false; - result.swingv = stdAc::swingv_t::kOff; result.swingh = stdAc::swingh_t::kOff; result.quiet = false; result.clean = false; @@ -299,14 +430,33 @@ stdAc::state_t IRToshibaAC::toCommon(void) { /// @return A human readable string. String IRToshibaAC::toString(void) { String result = ""; - result.reserve(40); - result += addBoolToString(getPower(), kPowerStr, false); - result += addModeToString(getMode(), kToshibaAcAuto, kToshibaAcCool, - kToshibaAcHeat, kToshibaAcDry, kToshibaAcAuto); - result += addTempToString(getTemp()); - result += addFanToString(getFan(), kToshibaAcFanMax, kToshibaAcFanMin, - kToshibaAcFanAuto, kToshibaAcFanAuto, - kToshibaAcFanMed); + result.reserve(80); + result += addTempToString(getTemp(), true, false); + switch (getStateLength()) { + case kToshibaACStateLengthShort: + result += addIntToString(getSwing(true), kSwingVStr); + result += kSpaceLBraceStr; + switch (getSwing(true)) { + case kToshibaAcSwingOff: result += kOffStr; break; + case kToshibaAcSwingOn: result += kOnStr; break; + case kToshibaAcSwingStep: result += kStepStr; break; + default: result += kUnknownStr; + } + result += ')'; + break; + case kToshibaACStateLengthLong: + case kToshibaACStateLength: + default: + result += addBoolToString(getPower(), kPowerStr); + if (getPower()) + result += addModeToString(getMode(), kToshibaAcAuto, kToshibaAcCool, + kToshibaAcHeat, kToshibaAcDry, kToshibaAcFan); + result += addFanToString(getFan(), kToshibaAcFanMax, kToshibaAcFanMin, + kToshibaAcFanAuto, kToshibaAcFanAuto, + kToshibaAcFanMed); + result += addBoolToString(getTurbo(), kTurboStr); + result += addBoolToString(getEcono(), kEconoStr); + } return result; } @@ -322,8 +472,16 @@ String IRToshibaAC::toString(void) { bool IRrecv::decodeToshibaAC(decode_results* results, uint16_t offset, const uint16_t nbits, const bool strict) { // Compliance - if (strict && nbits != kToshibaACBits) - return false; // Must be called with the correct nr. of bytes. + if (strict) { + switch (nbits) { // Must be called with the correct nr. of bits. + case kToshibaACBits: + case kToshibaACBitsShort: + case kToshibaACBitsLong: + break; + default: + return false; + } + } // Match Header + Data + Footer if (!matchGeneric(results->rawbuf + offset, results->state, @@ -336,7 +494,7 @@ bool IRrecv::decodeToshibaAC(decode_results* results, uint16_t offset, // Compliance if (strict) { // Check that the checksum of the message is correct. - if (!IRToshibaAC::validChecksum(results->state)) return false; + if (!IRToshibaAC::validChecksum(results->state, nbits / 8)) return false; } // Success diff --git a/lib/IRremoteESP8266-2.7.8/src/ir_Toshiba.h b/lib/IRremoteESP8266-2.7.8/src/ir_Toshiba.h index 0e5022ae7..803cc59b5 100644 --- a/lib/IRremoteESP8266-2.7.8/src/ir_Toshiba.h +++ b/lib/IRremoteESP8266-2.7.8/src/ir_Toshiba.h @@ -4,6 +4,10 @@ /// @brief Support for Toshiba protocols. /// @see https://github.com/r45635/HVAC-IR-Control /// @see https://github.com/r45635/HVAC-IR-Control/blob/master/HVAC_ESP8266/HVAC_ESP8266T.ino#L77 +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1205 +/// @see https://docs.google.com/spreadsheets/d/1yidE2fvaO9kpCHfKafIdH31q4uaskYR1OwwrkyOxbp0/edit?usp=drivesdk +/// @see https://www.toshiba-carrier.co.jp/global/about/index.htm +/// @see http://www.toshiba-carrier.co.th/AboutUs/Pages/CompanyProfile.aspx // Supports: // Brand: Toshiba, Model: RAS-B13N3KV2 @@ -12,6 +16,10 @@ // Brand: Toshiba, Model: RAS 18SKP-ES // Brand: Toshiba, Model: WH-TA04NE // Brand: Toshiba, Model: WC-L03SE +// Brand: Carrier, Model: 42NQV060M2 / 38NYV060M2 A/C +// Brand: Carrier, Model: 42NQV050M2 / 38NYV050M2 A/C +// Brand: Carrier, Model: 42NQV035M2 / 38NYV035M2 A/C +// Brand: Carrier, Model: 42NQV025M2 / 38NYV025M2 A/C #ifndef IR_TOSHIBA_H_ #define IR_TOSHIBA_H_ @@ -28,23 +36,53 @@ #endif // Constants +// Byte[0] - 0xF2 +// Byte[1] - 0x0D (inverted previous byte's value) +// Byte[2] - The expected payload length (in bytes) past the Byte[4]. +const uint8_t kToshibaAcLengthByte = 2; ///< Byte pos of the "length" attribute +const uint8_t kToshibaAcMinLength = 6; ///< Min Nr. of bytes in a message. +///< Known lengths are: +///< 1 (56 bit message) +///< 3 (72 bit message) +///< 4 (80 bit message) +// Byte[3] - The bit-inverted value of the "length" byte. +const uint16_t kToshibaAcInvertedLength = 4; ///< Nr. of leading bytes in + ///< inverted pairs. +// Byte[4] +const uint8_t kToshibaAcShortMsgBit = 5; ///< Mask 0b00x00000 +const uint8_t kToshibaAcLongMsgBit = 3; ///< Mask 0b00001000 +// Byte[5] +const uint8_t kToshibaAcSwingOffset = 0; ///< Bit offset. +const uint8_t kToshibaAcSwingSize = 2; ///< Mask 0b000000xx +const uint8_t kToshibaAcSwingStep = 0; ///< 0b00 +const uint8_t kToshibaAcSwingOn = 1; ///< 0b01 +const uint8_t kToshibaAcSwingOff = 2; ///< 0b10 + +const uint8_t kToshibaAcTempOffset = 4; ///< Bit offset. +const uint8_t kToshibaAcTempSize = 4; ///< Mask 0bxxxx0000 +const uint8_t kToshibaAcMinTemp = 17; ///< 17C +const uint8_t kToshibaAcMaxTemp = 30; ///< 30C +// Byte[6] const uint8_t kToshibaAcModeOffset = 0; -const uint8_t kToshibaAcModeSize = 2; // Nr. of bits -const uint8_t kToshibaAcAuto = 0; -const uint8_t kToshibaAcCool = 1; -const uint8_t kToshibaAcDry = 2; -const uint8_t kToshibaAcHeat = 3; -const uint8_t kToshibaAcPowerOffset = 2; +const uint8_t kToshibaAcModeSize = 3; // Mask 0b00000xxx +const uint8_t kToshibaAcAuto = 0; // 0b000 +const uint8_t kToshibaAcCool = 1; // 0b001 +const uint8_t kToshibaAcDry = 2; // 0b010 +const uint8_t kToshibaAcHeat = 3; // 0b011 +const uint8_t kToshibaAcFan = 4; // 0b100 +const uint8_t kToshibaAcOff = 7; // 0b111 const uint8_t kToshibaAcFanOffset = 5; -const uint8_t kToshibaAcFanSize = 3; // Nr. of bits -const uint8_t kToshibaAcFanAuto = 0b000; -const uint8_t kToshibaAcFanMin = 0b001; -const uint8_t kToshibaAcFanMed = 0b011; -const uint8_t kToshibaAcFanMax = 0b101; -const uint8_t kToshibaAcTempOffset = 4; -const uint8_t kToshibaAcTempSize = 4; // Nr. of bits -const uint8_t kToshibaAcMinTemp = 17; // 17C -const uint8_t kToshibaAcMaxTemp = 30; // 30C +const uint8_t kToshibaAcFanSize = 3; // Mask 0bxxx00000 +const uint8_t kToshibaAcFanAuto = 0; // 0b000 +const uint8_t kToshibaAcFanMin = 1; // 0b001 +const uint8_t kToshibaAcFanMed = 3; // 0b011 +const uint8_t kToshibaAcFanMax = 5; // 0b101 +// Byte[8] (Checksum for 72 bit messages, Eco/Turbo for long 80 bit messages) +const uint8_t kToshibaAcEcoTurboOffset = 0; +const uint8_t kToshibaAcEcoTurboSize = 2; // Mask 0b000000xx +const uint8_t kToshibaAcTurboOn = 1; // 0b01 +const uint8_t kToshibaAcEconoOn = 3; // 0b11 +// Byte[last] - Checksum (xor) // Legacy defines. (Deperecated) #define TOSHIBA_AC_AUTO kToshibaAcAuto @@ -81,12 +119,21 @@ class IRToshibaAC { uint8_t getTemp(void); void setFan(const uint8_t speed); uint8_t getFan(void); + void setTurbo(const bool on); + bool getTurbo(void); + void setEcono(const bool on); + bool getEcono(void); void setMode(const uint8_t mode); - uint8_t getMode(const bool useRaw = false); + uint8_t getMode(const bool raw = false); void setRaw(const uint8_t newState[]); uint8_t* getRaw(void); + static uint16_t getInternalStateLength(const uint8_t state[], + const uint16_t size); + uint16_t getStateLength(void); static bool validChecksum(const uint8_t state[], const uint16_t length = kToshibaACStateLength); + uint8_t getSwing(const bool raw = true); + void setSwing(const uint8_t setting); uint8_t convertMode(const stdAc::opmode_t mode); uint8_t convertFan(const stdAc::fanspeed_t speed); static stdAc::opmode_t toCommonMode(const uint8_t mode); @@ -102,11 +149,17 @@ class IRToshibaAC { IRsendTest _irsend; ///< Instance of the testing IR send class /// @endcond #endif // UNIT_TEST - uint8_t remote_state[kToshibaACStateLength]; ///< The state in IR code form. + uint8_t remote_state[kToshibaACStateLengthLong]; ///< The state in code form. + uint8_t backup[kToshibaACStateLengthLong]; ///< A backup copy of the state. + uint8_t prev_mode; ///< Store of the previously set mode. + bool _send_swing; ///< Flag indicating if we need to send a swing message. + uint8_t _swing_mode; ///< The saved swing state/mode/command. void checksum(const uint16_t length = kToshibaACStateLength); static uint8_t calcChecksum(const uint8_t state[], const uint16_t length = kToshibaACStateLength); - uint8_t mode_state; + void setStateLength(const uint16_t size); + void _backupState(void); + void _restoreState(void); }; #endif // IR_TOSHIBA_H_ diff --git a/lib/IRremoteESP8266-2.7.8/src/locale/defaults.h b/lib/IRremoteESP8266-2.7.8/src/locale/defaults.h index 340e2b8fe..7fc0990e1 100644 --- a/lib/IRremoteESP8266-2.7.8/src/locale/defaults.h +++ b/lib/IRremoteESP8266-2.7.8/src/locale/defaults.h @@ -360,6 +360,9 @@ // Compound words/phrases/descriptions from pre-defined words. // Note: Obviously these need to be defined *after* their component words. +#ifndef D_STR_ECONOTOGGLE +#define D_STR_ECONOTOGGLE D_STR_ECONO " " D_STR_TOGGLE +#endif // D_STR_ECONOTOGGLE #ifndef D_STR_EYEAUTO #define D_STR_EYEAUTO D_STR_EYE " " D_STR_AUTO #endif // D_STR_EYEAUTO @@ -685,6 +688,9 @@ #ifndef D_STR_SANYO #define D_STR_SANYO "SANYO" #endif // D_STR_SANYO +#ifndef D_STR_SANYO_AC +#define D_STR_SANYO_AC "SANYO_AC" +#endif // D_STR_SANYO_AC #ifndef D_STR_SANYO_LC7461 #define D_STR_SANYO_LC7461 "SANYO_LC7461" #endif // D_STR_SANYO_LC7461 diff --git a/lib/IRremoteESP8266-2.7.8/test/IRac_test.cpp b/lib/IRremoteESP8266-2.7.8/test/IRac_test.cpp index a0f975666..f91be3459 100644 --- a/lib/IRremoteESP8266-2.7.8/test/IRac_test.cpp +++ b/lib/IRremoteESP8266-2.7.8/test/IRac_test.cpp @@ -1,6 +1,7 @@ // Copyright 2019 David Conran #include +#include "ir_Airwell.h" #include "ir_Amcor.h" #include "ir_Argo.h" #include "ir_Carrier.h" @@ -39,10 +40,33 @@ // Tests for IRac class. +TEST(TestIRac, Airwell) { + IRAirwellAc ac(kGpioUnused); + IRac irac(kGpioUnused); + IRrecv capture(kGpioUnused); + char expected[] = + "Power Toggle: On, Mode: 3 (Auto), Fan: 1 (Medium), Temp: 18C"; + + ac.begin(); + irac.airwell(&ac, + true, // Power + stdAc::opmode_t::kAuto, // Mode + 18, // Celsius + stdAc::fanspeed_t::kMedium); // Fan speed + ASSERT_EQ(expected, ac.toString()); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(AIRWELL, ac._irsend.capture.decode_type); + ASSERT_EQ(kAirwellBits, ac._irsend.capture.bits); + ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture)); + stdAc::state_t r, p; + ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p)); +} + TEST(TestIRac, Amcor) { - IRAmcorAc ac(0); - IRac irac(0); - IRrecv capture(0); + IRAmcorAc ac(kGpioUnused); + IRac irac(kGpioUnused); + IRrecv capture(kGpioUnused); char expected[] = "Power: On, Mode: 5 (Auto), Fan: 3 (High), Temp: 19C, Max: Off"; @@ -63,8 +87,8 @@ TEST(TestIRac, Amcor) { } TEST(TestIRac, Argo) { - IRArgoAC ac(0); - IRac irac(0); + IRArgoAC ac(kGpioUnused); + IRac irac(kGpioUnused); ac.begin(); irac.argo(&ac, @@ -118,9 +142,9 @@ TEST(TestIRac, Carrier64) { } TEST(TestIRac, Coolix) { - IRCoolixAC ac(0); - IRac irac(0); - IRrecv capture(0); + IRCoolixAC ac(kGpioUnused); + IRac irac(kGpioUnused); + IRrecv capture(kGpioUnused); char expected[] = "Power: On, Mode: 3 (Heat), Fan: 1 (Max), Temp: 21C, Zone Follow: Off, " "Sensor Temp: Off"; @@ -221,9 +245,9 @@ TEST(TestIRac, Corona) { } TEST(TestIRac, Daikin) { - IRDaikinESP ac(0); - IRac irac(0); - IRrecv capture(0); + IRDaikinESP ac(kGpioUnused); + IRac irac(kGpioUnused); + IRrecv capture(kGpioUnused); char expected[] = "Power: On, Mode: 3 (Cool), Temp: 19C, Fan: 5 (High), Powerful: Off, " "Quiet: Off, Sensor: Off, Mould: On, Comfort: Off, " @@ -254,9 +278,9 @@ TEST(TestIRac, Daikin) { } TEST(TestIRac, Daikin128) { - IRDaikin128 ac(0); - IRac irac(0); - IRrecv capture(0); + IRDaikin128 ac(kGpioUnused); + IRac irac(kGpioUnused); + IRrecv capture(kGpioUnused); char expected[] = "Power Toggle: On, Mode: 8 (Heat), Temp: 27C, Fan: 9 (Quiet), " "Powerful: Off, Quiet: On, Swing(V): On, Sleep: On, " @@ -287,9 +311,9 @@ TEST(TestIRac, Daikin128) { } TEST(TestIRac, Daikin152) { - IRDaikin152 ac(0); - IRac irac(0); - IRrecv capture(0); + IRDaikin152 ac(kGpioUnused); + IRac irac(kGpioUnused); + IRrecv capture(kGpioUnused); char expected[] = "Power: On, Mode: 3 (Cool), Temp: 27C, Fan: 3 (Medium), Swing(V): On, " "Powerful: Off, Quiet: Off, Econo: On, Sensor: Off, Comfort: Off"; @@ -315,9 +339,9 @@ TEST(TestIRac, Daikin152) { } TEST(TestIRac, Daikin160) { - IRDaikin160 ac(0); - IRac irac(0); - IRrecv capture(0); + IRDaikin160 ac(kGpioUnused); + IRac irac(kGpioUnused); + IRrecv capture(kGpioUnused); char expected[] = "Power: On, Mode: 2 (Dry), Temp: 23C, Fan: 1 (Low), " "Swing(V): 3 (Middle)"; @@ -340,9 +364,9 @@ TEST(TestIRac, Daikin160) { } TEST(TestIRac, Daikin176) { - IRDaikin176 ac(0); - IRac irac(0); - IRrecv capture(0); + IRDaikin176 ac(kGpioUnused); + IRac irac(kGpioUnused); + IRrecv capture(kGpioUnused); char expected[] = "Power: On, Mode: 7 (Cool), Temp: 26C, Fan: 1 (Low), Swing(H): 5 (Auto)"; @@ -364,9 +388,9 @@ TEST(TestIRac, Daikin176) { } TEST(TestIRac, Daikin2) { - IRDaikin2 ac(0); - IRac irac(0); - IRrecv capture(0); + IRDaikin2 ac(kGpioUnused); + IRac irac(kGpioUnused); + IRrecv capture(kGpioUnused); char expected[] = "Power: On, Mode: 3 (Cool), Temp: 19C, Fan: 1 (Low), " "Swing(V): 14 (Auto), Swing(H): 170 (UNKNOWN), Clock: 00:00, " @@ -402,9 +426,9 @@ TEST(TestIRac, Daikin2) { } TEST(TestIRac, Daikin216) { - IRDaikin216 ac(0); - IRac irac(0); - IRrecv capture(0); + IRDaikin216 ac(kGpioUnused); + IRac irac(kGpioUnused); + IRrecv capture(kGpioUnused); char expected[] = "Power: On, Mode: 4 (Heat), Temp: 31C, Fan: 11 (Quiet), " "Swing(H): On, Swing(V): On, Quiet: On, Powerful: Off"; @@ -511,9 +535,9 @@ TEST(TestIRac, Electra) { } TEST(TestIRac, Fujitsu) { - IRFujitsuAC ac(0); - IRac irac(0); - IRrecv capture(0); + IRFujitsuAC ac(kGpioUnused); + IRac irac(kGpioUnused); + IRrecv capture(kGpioUnused); std::string ardb1_expected = "Model: 2 (ARDB1), Power: On, Mode: 1 (Cool), Temp: 19C, " "Fan: 2 (Medium), Command: N/A"; @@ -591,9 +615,9 @@ TEST(TestIRac, Fujitsu) { } TEST(TestIRac, Goodweather) { - IRGoodweatherAc ac(0); - IRac irac(0); - IRrecv capture(0); + IRGoodweatherAc ac(kGpioUnused); + IRac irac(kGpioUnused); + IRrecv capture(kGpioUnused); char expected[] = "Power: On, Mode: 1 (Cool), Temp: 19C, Fan: 2 (Medium), Turbo: Toggle, " "Light: Toggle, Sleep: Toggle, Swing: 1 (Slow), Command: 0 (Power)"; @@ -619,9 +643,9 @@ TEST(TestIRac, Goodweather) { } TEST(TestIRac, Gree) { - IRGreeAC ac(0); - IRac irac(0); - IRrecv capture(0); + IRGreeAC ac(kGpioUnused); + IRac irac(kGpioUnused); + IRrecv capture(kGpioUnused); char expected[] = "Model: 1 (YAW1F), Power: On, Mode: 1 (Cool), Temp: 71F, " "Fan: 2 (Medium), Turbo: Off, IFeel: Off, WiFi: Off, XFan: On, " @@ -652,9 +676,9 @@ TEST(TestIRac, Gree) { } TEST(TestIRac, Haier) { - IRHaierAC ac(0); - IRac irac(0); - IRrecv capture(0); + IRHaierAC ac(kGpioUnused); + IRac irac(kGpioUnused); + IRrecv capture(kGpioUnused); char expected[] = "Command: 1 (On), Mode: 1 (Cool), Temp: 24C, Fan: 2 (Medium), " "Swing: 1 (Up), Sleep: On, Health: On, Clock: 13:45, " @@ -682,9 +706,9 @@ TEST(TestIRac, Haier) { TEST(TestIRac, HaierYrwo2) { - IRHaierACYRW02 ac(0); - IRac irac(0); - IRrecv capture(0); + IRHaierACYRW02 ac(kGpioUnused); + IRac irac(kGpioUnused); + IRrecv capture(kGpioUnused); char expected[] = "Power: On, Button: 5 (Power), Mode: 1 (Cool), Temp: 23C, " "Fan: 2 (Medium), Turbo: 1 (High), Swing: 1 (Highest), Sleep: On, " @@ -711,9 +735,9 @@ TEST(TestIRac, HaierYrwo2) { } TEST(TestIRac, Hitachi) { - IRHitachiAc ac(0); - IRac irac(0); - IRrecv capture(0); + IRHitachiAc ac(kGpioUnused); + IRac irac(kGpioUnused); + IRrecv capture(kGpioUnused); char expected[] = "Power: On, Mode: 2 (Auto), Temp: 22C, Fan: 3 (Medium), " "Swing(V): Off, Swing(H): On"; @@ -821,9 +845,9 @@ TEST(TestIRac, Hitachi344) { } TEST(TestIRac, Hitachi424) { - IRHitachiAc424 ac(0); - IRac irac(0); - IRrecv capture(0); + IRHitachiAc424 ac(kGpioUnused); + IRac irac(kGpioUnused); + IRrecv capture(kGpioUnused); char expected[] = "Power: On, Mode: 6 (Heat), Temp: 25C, Fan: 6 (Max), " "Button: 19 (Power/Mode), Swing(V) Toggle: Off"; @@ -866,9 +890,9 @@ TEST(TestIRac, Hitachi424) { } TEST(TestIRac, Kelvinator) { - IRKelvinatorAC ac(0); - IRac irac(0); - IRrecv capture(0); + IRKelvinatorAC ac(kGpioUnused); + IRac irac(kGpioUnused); + IRrecv capture(kGpioUnused); char expected[] = "Power: On, Mode: 1 (Cool), Temp: 19C, Fan: 3 (Medium), Turbo: Off, " "Quiet: Off, XFan: On, Ion: On, Light: On, " @@ -899,9 +923,9 @@ TEST(TestIRac, Kelvinator) { } TEST(TestIRac, LG) { - IRLgAc ac(0); - IRac irac(0); - IRrecv capture(0); + IRLgAc ac(kGpioUnused); + IRac irac(kGpioUnused); + IRrecv capture(kGpioUnused); char expected[] = "Model: 1 (GE6711AR2853M), " "Power: On, Mode: 1 (Dry), Temp: 27C, Fan: 2 (Medium)"; @@ -925,12 +949,12 @@ TEST(TestIRac, LG) { } TEST(TestIRac, Midea) { - IRMideaAC ac(0); - IRac irac(0); - IRrecv capture(0); + IRMideaAC ac(kGpioUnused); + IRac irac(kGpioUnused); + IRrecv capture(kGpioUnused); char expected[] = "Power: On, Mode: 1 (Dry), Celsius: On, Temp: 27C/80F, Fan: 2 (Medium), " - "Sleep: On, Swing(V) Toggle: Off"; + "Sleep: On, Swing(V) Toggle: Off, Econo Toggle: Off"; ac.begin(); irac.midea(&ac, @@ -940,6 +964,7 @@ TEST(TestIRac, Midea) { 27, // Degrees stdAc::fanspeed_t::kMedium, // Fan speed stdAc::swingv_t::kOff, // Swing(V) + false, // Econo 8 * 60 + 0); // Sleep time ASSERT_EQ(expected, ac.toString()); @@ -953,9 +978,9 @@ TEST(TestIRac, Midea) { } TEST(TestIRac, Mitsubishi) { - IRMitsubishiAC ac(0); - IRac irac(0); - IRrecv capture(0); + IRMitsubishiAC ac(kGpioUnused); + IRac irac(kGpioUnused); + IRrecv capture(kGpioUnused); char expected[] = "Power: On, Mode: 3 (Cool), Temp: 20C, Fan: 2 (Medium), " "Swing(V): 0 (Auto), Swing(H): 3 (UNKNOWN), " @@ -982,9 +1007,9 @@ TEST(TestIRac, Mitsubishi) { } TEST(TestIRac, Mitsubishi136) { - IRMitsubishi136 ac(0); - IRac irac(0); - IRrecv capture(0); + IRMitsubishi136 ac(kGpioUnused); + IRac irac(kGpioUnused); + IRrecv capture(kGpioUnused); char expected[] = "Power: On, Mode: 5 (Dry), Temp: 22C, Fan: 3 (High), " "Swing(V): 3 (Highest), Quiet: Off"; @@ -1008,9 +1033,9 @@ TEST(TestIRac, Mitsubishi136) { } TEST(TestIRac, MitsubishiHeavy88) { - IRMitsubishiHeavy88Ac ac(0); - IRac irac(0); - IRrecv capture(0); + IRMitsubishiHeavy88Ac ac(kGpioUnused); + IRac irac(kGpioUnused); + IRrecv capture(kGpioUnused); char expected[] = "Power: On, Mode: 1 (Cool), Temp: 21C, Fan: 3 (Med), " "Swing(V): 4 (Auto), Swing(H): 0 (Off), Turbo: Off, Econo: Off, " @@ -1038,9 +1063,9 @@ TEST(TestIRac, MitsubishiHeavy88) { } TEST(TestIRac, MitsubishiHeavy152) { - IRMitsubishiHeavy152Ac ac(0); - IRac irac(0); - IRrecv capture(0); + IRMitsubishiHeavy152Ac ac(kGpioUnused); + IRac irac(kGpioUnused); + IRrecv capture(kGpioUnused); char expected[] = "Power: On, Mode: 1 (Cool), Temp: 20C, Fan: 6 (Econo), " "Swing(V): 6 (Off), Swing(H): 0 (Auto), Silent: On, Turbo: Off, " @@ -1071,9 +1096,9 @@ TEST(TestIRac, MitsubishiHeavy152) { } TEST(TestIRac, Neoclima) { - IRNeoclimaAc ac(0); - IRac irac(0); - IRrecv capture(0); + IRNeoclimaAc ac(kGpioUnused); + IRac irac(kGpioUnused); + IRrecv capture(kGpioUnused); char expected[] = "Power: On, Mode: 1 (Cool), Temp: 20C, Fan: 3 (Low), " "Swing(V): Off, Swing(H): On, Sleep: On, Turbo: Off, Hold: Off, Ion: On, " @@ -1103,9 +1128,9 @@ TEST(TestIRac, Neoclima) { } TEST(TestIRac, Panasonic) { - IRPanasonicAc ac(0); - IRac irac(0); - IRrecv capture(0); + IRPanasonicAc ac(kGpioUnused); + IRac irac(kGpioUnused); + IRrecv capture(kGpioUnused); char expected_nke[] = "Model: 2 (NKE), Power: On, Mode: 4 (Heat), Temp: 28C, Fan: 2 (Medium), " "Swing(V): 15 (Auto), Swing(H): 6 (Middle), Quiet: On, " @@ -1161,9 +1186,9 @@ TEST(TestIRac, Panasonic) { } TEST(TestIRac, Samsung) { - IRSamsungAc ac(0); - IRac irac(0); - IRrecv capture(0); + IRSamsungAc ac(kGpioUnused); + IRac irac(kGpioUnused); + IRrecv capture(kGpioUnused); char expected[] = "Power: On, Mode: 0 (Auto), Temp: 28C, Fan: 6 (Auto), Swing: On, " "Beep: On, Clean: On, Quiet: On, Powerful: Off, Breeze: Off, " @@ -1223,10 +1248,38 @@ TEST(TestIRac, Samsung) { ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p)); } +TEST(TestIRac, Sanyo) { + IRSanyoAc ac(kGpioUnused); + IRac irac(kGpioUnused); + IRrecv capture(kGpioUnused); + char expected[] = + "Power: On, Mode: 2 (Cool), Temp: 28C, Fan: 3 (Medium), " + "Swing(V): 7 (Highest), Sleep: On, Beep: On, " + "Sensor: Wall, Sensor Temp: 28C, Off Timer: Off"; + + ac.begin(); + irac.sanyo(&ac, + true, // Power + stdAc::opmode_t::kCool, // Mode + 28, // Celsius + stdAc::fanspeed_t::kMedium, // Fan speed + stdAc::swingv_t::kHighest, // Vertical Swing + true, // Beep + 17); // Sleep + ASSERT_EQ(expected, ac.toString()); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(SANYO_AC, ac._irsend.capture.decode_type); + ASSERT_EQ(kSanyoAcBits, ac._irsend.capture.bits); + ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture)); + stdAc::state_t r, p; + ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p)); +} + TEST(TestIRac, Sharp) { - IRSharpAc ac(0); - IRac irac(0); - IRrecv capture(0); + IRSharpAc ac(kGpioUnused); + IRac irac(kGpioUnused); + IRrecv capture(kGpioUnused); char expected[] = "Power: On, Mode: 2 (Cool), Temp: 28C, Fan: 3 (Medium), " "Turbo: Off, Swing(V) Toggle: On, Ion: On, Econo: -, Clean: Off"; @@ -1253,9 +1306,9 @@ TEST(TestIRac, Sharp) { } TEST(TestIRac, Tcl112) { - IRTcl112Ac ac(0); - IRac irac(0); - IRrecv capture(0); + IRTcl112Ac ac(kGpioUnused); + IRac irac(kGpioUnused); + IRrecv capture(kGpioUnused); char expected[] = "Power: On, Mode: 3 (Cool), Temp: 20C, Fan: 3 (Medium), Econo: On, " "Health: On, Light: On, Turbo: Off, Swing(H): On, Swing(V): Off"; @@ -1283,9 +1336,9 @@ TEST(TestIRac, Tcl112) { } TEST(TestIRac, Teco) { - IRTecoAc ac(0); - IRac irac(0); - IRrecv capture(0); + IRTecoAc ac(kGpioUnused); + IRac irac(kGpioUnused); + IRrecv capture(kGpioUnused); char expected[] = "Power: On, Mode: 0 (Auto), Temp: 21C, Fan: 2 (Medium), Sleep: On, " "Swing: On, Light: On, Humid: Off, Save: Off, Timer: Off"; @@ -1310,31 +1363,82 @@ TEST(TestIRac, Teco) { } TEST(TestIRac, Toshiba) { - IRToshibaAC ac(0); - IRac irac(0); - IRrecv capture(0); - char expected[] = "Power: On, Mode: 2 (Dry), Temp: 29C, Fan: 2 (UNKNOWN)"; + IRToshibaAC ac(kGpioUnused); + IRac irac(kGpioUnused); + IRrecv capture(kGpioUnused); + char expected[] = + "Temp: 29C, Power: On, Mode: 2 (Dry), Fan: 2 (UNKNOWN), " + "Turbo: Off, Econo: On"; ac.begin(); irac.toshiba(&ac, true, // Power stdAc::opmode_t::kDry, // Mode 29, // Celsius - stdAc::fanspeed_t::kLow); // Fan speed + stdAc::fanspeed_t::kLow, // Fan speed + stdAc::swingv_t::kOff, // Vertical Swing + false, // Turbo + true); // Econo ASSERT_EQ(expected, ac.toString()); + ASSERT_EQ(kToshibaACStateLengthLong, ac.getStateLength()); ac._irsend.makeDecodeResult(); EXPECT_TRUE(capture.decode(&ac._irsend.capture)); ASSERT_EQ(TOSHIBA_AC, ac._irsend.capture.decode_type); - ASSERT_EQ(kToshibaACBits, ac._irsend.capture.bits); + ASSERT_EQ(kToshibaACBitsLong, ac._irsend.capture.bits); ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture)); stdAc::state_t r, p; ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p)); + EXPECT_EQ( + "f38000d50" + "m4400s4300" + "m580s1600m580s1600m580s1600m580s1600m580s490m580s490m580s1600m580s490" + "m580s490m580s490m580s490m580s490m580s1600m580s1600m580s490m580s1600" + "m580s490m580s490m580s490m580s490m580s490m580s1600m580s490m580s490" + "m580s1600m580s1600m580s1600m580s1600m580s1600m580s490m580s1600m580s1600" + "m580s490m580s490m580s490m580s490m580s1600m580s490m580s490m580s1600" + "m580s1600m580s1600m580s490m580s490m580s490m580s490m580s490m580s490" + "m580s490m580s1600m580s1600m580s490m580s490m580s490m580s1600m580s490" + "m580s490m580s490m580s490m580s490m580s490m580s490m580s490m580s490" + "m580s490m580s490m580s490m580s490m580s490m580s490m580s1600m580s1600" + "m580s1600m580s490m580s1600m580s490m580s1600m580s490m580s490m580s490" + "m580s7400" + "m4400s4300" + "m580s1600m580s1600m580s1600m580s1600m580s490m580s490m580s1600m580s490" + "m580s490m580s490m580s490m580s490m580s1600m580s1600m580s490m580s1600" + "m580s490m580s490m580s490m580s490m580s490m580s1600m580s490m580s490" + "m580s1600m580s1600m580s1600m580s1600m580s1600m580s490m580s1600m580s1600" + "m580s490m580s490m580s490m580s490m580s1600m580s490m580s490m580s1600" + "m580s1600m580s1600m580s490m580s490m580s490m580s490m580s490m580s490" + "m580s490m580s1600m580s1600m580s490m580s490m580s490m580s1600m580s490" + "m580s490m580s490m580s490m580s490m580s490m580s490m580s490m580s490" + "m580s490m580s490m580s490m580s490m580s490m580s490m580s1600m580s1600" + "m580s1600m580s490m580s1600m580s490m580s1600m580s490m580s490m580s490" + "m580s7400" + "m4400s4300" + "m580s1600m580s1600m580s1600m580s1600m580s490m580s490m580s1600m580s490" + "m580s490m580s490m580s490m580s490m580s1600m580s1600m580s490m580s1600" + "m580s490m580s490m580s490m580s490m580s490m580s490m580s490m580s1600" + "m580s1600m580s1600m580s1600m580s1600m580s1600m580s1600m580s1600m580s490" + "m580s490m580s490m580s1600m580s490m580s490m580s490m580s490m580s1600" + "m580s490m580s490m580s490m580s490m580s490m580s490m580s1600m580s490" + "m580s490m580s490m580s1600m580s490m580s490m580s490m580s1600m580s1600" + "m580s7400" + "m4400s4300" + "m580s1600m580s1600m580s1600m580s1600m580s490m580s490m580s1600m580s490" + "m580s490m580s490m580s490m580s490m580s1600m580s1600m580s490m580s1600" + "m580s490m580s490m580s490m580s490m580s490m580s490m580s490m580s1600" + "m580s1600m580s1600m580s1600m580s1600m580s1600m580s1600m580s1600m580s490" + "m580s490m580s490m580s1600m580s490m580s490m580s490m580s490m580s1600" + "m580s490m580s490m580s490m580s490m580s490m580s490m580s1600m580s490" + "m580s490m580s490m580s1600m580s490m580s490m580s490m580s1600m580s1600" + "m580s7400", + ac._irsend.outputStr()); } TEST(TestIRac, Trotec) { - IRTrotecESP ac(0); - IRac irac(0); - IRrecv capture(0); + IRTrotecESP ac(kGpioUnused); + IRac irac(kGpioUnused); + IRrecv capture(kGpioUnused); char expected[] = "Power: On, Mode: 1 (Cool), Temp: 18C, Fan: 3 (High), Sleep: On"; @@ -1361,9 +1465,9 @@ TEST(TestIRac, Trotec) { } TEST(TestIRac, Vestel) { - IRVestelAc ac(0); - IRac irac(0); - IRrecv capture(0); + IRVestelAc ac(kGpioUnused); + IRac irac(kGpioUnused); + IRrecv capture(kGpioUnused); char expected[] = "Power: On, Mode: 0 (Auto), Temp: 22C, Fan: 5 (Low), Sleep: On, " "Turbo: Off, Ion: On, Swing: On"; @@ -1452,9 +1556,9 @@ TEST(TestIRac, Vestel) { TEST(TestIRac, Whirlpool) { - IRWhirlpoolAc ac(0); - IRac irac(0); - IRrecv capture(0); + IRWhirlpoolAc ac(kGpioUnused); + IRac irac(kGpioUnused); + IRrecv capture(kGpioUnused); char expected[] = "Model: 1 (DG11J13A), Power Toggle: On, Mode: 1 (Auto), Temp: 21C, " "Fan: 3 (Low), Swing: On, Light: On, Clock: 23:58, On Timer: Off, " @@ -1755,9 +1859,9 @@ TEST(TestIRac, Issue821) { next = prev; next.light = true; - IRac irac(0); - IRrecv capture(0); - IRCoolixAC ac(0); + IRac irac(kGpioUnused); + IRrecv capture(kGpioUnused); + IRCoolixAC ac(kGpioUnused); ac.begin(); result = irac.handleToggles(next, &prev); @@ -1844,9 +1948,9 @@ TEST(TestIRac, Issue1001) { desired = prev; desired.power = false; - IRac irac(0); - IRrecv capture(0); - IRWhirlpoolAc ac(0); + IRac irac(kGpioUnused); + IRrecv capture(kGpioUnused); + IRWhirlpoolAc ac(kGpioUnused); ac.begin(); ASSERT_TRUE(prev.power); diff --git a/lib/IRremoteESP8266-2.7.8/test/IRutils_test.cpp b/lib/IRremoteESP8266-2.7.8/test/IRutils_test.cpp index ecb23dbbf..0dde7ec04 100644 --- a/lib/IRremoteESP8266-2.7.8/test/IRutils_test.cpp +++ b/lib/IRremoteESP8266-2.7.8/test/IRutils_test.cpp @@ -220,32 +220,32 @@ TEST(TestResultToSourceCode, ComplexProtocols) { ASSERT_EQ(TOSHIBA_AC, irsend.capture.decode_type); ASSERT_EQ(kToshibaACBits, irsend.capture.bits); EXPECT_EQ( - "uint16_t rawData[296] = {4400, 4300, 542, 1622, 542, 1622, " - "542, 1622, 542, 1622, 542, 472, 542, 472, 542, 1622, 542, 472, " - "542, 472, 542, 472, 542, 472, 542, 472, 542, 1622, 542, 1622, " - "542, 472, 542, 1622, 542, 472, 542, 472, 542, 472, 542, 472, " - "542, 472, 542, 472, 542, 1622, 542, 1622, 542, 1622, 542, 1622, " - "542, 1622, 542, 1622, 542, 1622, 542, 1622, 542, 472, 542, 472, " - "542, 472, 542, 472, 542, 472, 542, 472, 542, 472, 542, 472, " - "542, 472, 542, 1622, 542, 472, 542, 472, 542, 472, 542, 472, " - "542, 472, 542, 472, 542, 472, 542, 472, 542, 472, 542, 472, " - "542, 472, 542, 472, 542, 472, 542, 472, 542, 472, 542, 472, " - "542, 472, 542, 472, 542, 472, 542, 472, 542, 472, 542, 472, " - "542, 472, 542, 472, 542, 472, 542, 472, 542, 472, 542, 472, " - "542, 472, 542, 472, 542, 472, 542, 1622, 542, 7048, 4400, 4300, " - "542, 1622, 542, 1622, 542, 1622, 542, 1622, 542, 472, 542, 472, " - "542, 1622, 542, 472, 542, 472, 542, 472, 542, 472, 542, 472, " - "542, 1622, 542, 1622, 542, 472, 542, 1622, 542, 472, 542, 472, " - "542, 472, 542, 472, 542, 472, 542, 472, 542, 1622, 542, 1622, " - "542, 1622, 542, 1622, 542, 1622, 542, 1622, 542, 1622, 542, 1622, " - "542, 472, 542, 472, 542, 472, 542, 472, 542, 472, 542, 472, " - "542, 472, 542, 472, 542, 472, 542, 1622, 542, 472, 542, 472, " - "542, 472, 542, 472, 542, 472, 542, 472, 542, 472, 542, 472, " - "542, 472, 542, 472, 542, 472, 542, 472, 542, 472, 542, 472, " - "542, 472, 542, 472, 542, 472, 542, 472, 542, 472, 542, 472, " - "542, 472, 542, 472, 542, 472, 542, 472, 542, 472, 542, 472, " - "542, 472, 542, 472, 542, 472, 542, 472, 542, 472, 542, 1622, " - "542, 7048 }; // TOSHIBA_AC\n" + "uint16_t rawData[296] = {4400, 4300, 580, 1600, 580, 1600, " + "580, 1600, 580, 1600, 580, 490, 580, 490, 580, 1600, 580, 490, " + "580, 490, 580, 490, 580, 490, 580, 490, 580, 1600, 580, 1600, " + "580, 490, 580, 1600, 580, 490, 580, 490, 580, 490, 580, 490, " + "580, 490, 580, 490, 580, 1600, 580, 1600, 580, 1600, 580, 1600, " + "580, 1600, 580, 1600, 580, 1600, 580, 1600, 580, 490, 580, 490, " + "580, 490, 580, 490, 580, 490, 580, 490, 580, 490, 580, 490, " + "580, 490, 580, 1600, 580, 490, 580, 490, 580, 490, 580, 490, " + "580, 490, 580, 490, 580, 490, 580, 490, 580, 490, 580, 490, " + "580, 490, 580, 490, 580, 490, 580, 490, 580, 490, 580, 490, " + "580, 490, 580, 490, 580, 490, 580, 490, 580, 490, 580, 490, " + "580, 490, 580, 490, 580, 490, 580, 490, 580, 490, 580, 490, " + "580, 490, 580, 490, 580, 490, 580, 1600, 580, 7400, 4400, 4300, " + "580, 1600, 580, 1600, 580, 1600, 580, 1600, 580, 490, 580, 490, " + "580, 1600, 580, 490, 580, 490, 580, 490, 580, 490, 580, 490, " + "580, 1600, 580, 1600, 580, 490, 580, 1600, 580, 490, 580, 490, " + "580, 490, 580, 490, 580, 490, 580, 490, 580, 1600, 580, 1600, " + "580, 1600, 580, 1600, 580, 1600, 580, 1600, 580, 1600, 580, 1600, " + "580, 490, 580, 490, 580, 490, 580, 490, 580, 490, 580, 490, " + "580, 490, 580, 490, 580, 490, 580, 1600, 580, 490, 580, 490, " + "580, 490, 580, 490, 580, 490, 580, 490, 580, 490, 580, 490, " + "580, 490, 580, 490, 580, 490, 580, 490, 580, 490, 580, 490, " + "580, 490, 580, 490, 580, 490, 580, 490, 580, 490, 580, 490, " + "580, 490, 580, 490, 580, 490, 580, 490, 580, 490, 580, 490, " + "580, 490, 580, 490, 580, 490, 580, 490, 580, 490, 580, 1600, " + "580, 7400 }; // TOSHIBA_AC\n" "uint8_t state[9] = {0xF2, 0x0D, 0x03, 0xFC, 0x01, 0x00, 0x00, 0x00, " "0x01};\n", resultToSourceCode(&irsend.capture)); @@ -744,3 +744,35 @@ TEST(TestUtils, setBits64Bit) { irutils::setBits(&data, 32, 4, 0b1001); EXPECT_EQ(0x4000000900000013, data); } + +TEST(TestUtils, InvertedBytePairs) { + const uint8_t correct[] = {0x00, 0xFF, 0x01, 0xFE, 0xAA, 0x55}; + uint8_t wrong[] = {0x00, 0xFF, 0x01, 0xFD, 0xAA, 0x55}; + + ASSERT_TRUE(irutils::checkInvertedBytePairs(correct, 6)); + ASSERT_TRUE(irutils::checkInvertedBytePairs(correct, 5)); + ASSERT_TRUE(irutils::checkInvertedBytePairs(correct, 4)); + ASSERT_TRUE(irutils::checkInvertedBytePairs(correct, 3)); + ASSERT_TRUE(irutils::checkInvertedBytePairs(correct, 2)); + ASSERT_TRUE(irutils::checkInvertedBytePairs(correct, 1)); + ASSERT_TRUE(irutils::checkInvertedBytePairs(correct, 0)); + + ASSERT_FALSE(irutils::checkInvertedBytePairs(wrong, 6)); + ASSERT_FALSE(irutils::checkInvertedBytePairs(wrong, 5)); + ASSERT_FALSE(irutils::checkInvertedBytePairs(wrong, 4)); + ASSERT_TRUE(irutils::checkInvertedBytePairs(wrong, 3)); + ASSERT_TRUE(irutils::checkInvertedBytePairs(wrong, 2)); + ASSERT_TRUE(irutils::checkInvertedBytePairs(wrong, 1)); + ASSERT_TRUE(irutils::checkInvertedBytePairs(wrong, 0)); + + irutils::invertBytePairs(wrong, 0); + ASSERT_FALSE(irutils::checkInvertedBytePairs(wrong, 6)); + irutils::invertBytePairs(wrong, 1); + ASSERT_FALSE(irutils::checkInvertedBytePairs(wrong, 6)); + irutils::invertBytePairs(wrong, 2); + ASSERT_FALSE(irutils::checkInvertedBytePairs(wrong, 6)); + + irutils::invertBytePairs(wrong, 6); + ASSERT_TRUE(irutils::checkInvertedBytePairs(wrong, 6)); + EXPECT_STATE_EQ(correct, wrong, 6 * 8); +} diff --git a/lib/IRremoteESP8266-2.7.8/test/ir_Airwell_test.cpp b/lib/IRremoteESP8266-2.7.8/test/ir_Airwell_test.cpp index 09300bac2..e5f28d4df 100644 --- a/lib/IRremoteESP8266-2.7.8/test/ir_Airwell_test.cpp +++ b/lib/IRremoteESP8266-2.7.8/test/ir_Airwell_test.cpp @@ -1,5 +1,6 @@ // Copyright 2020 David Conran +#include "ir_Airwell.h" #include "IRac.h" #include "IRrecv.h" #include "IRrecv_test.h" @@ -44,6 +45,9 @@ TEST(TestDecodeAirwell, RealExample) { EXPECT_EQ(0x2B0D0181B, irsend.capture.value); EXPECT_EQ(0x0, irsend.capture.address); EXPECT_EQ(0x0, irsend.capture.command); + EXPECT_EQ( + "Power Toggle: On, Mode: 2 (Heat), Fan: 3 (Auto), Temp: 25C", + IRAcUtils::resultAcToString(&irsend.capture)); const uint16_t rawData_2[175] = { 2862, 3892, @@ -76,6 +80,9 @@ TEST(TestDecodeAirwell, RealExample) { EXPECT_EQ(0x270F8181B, irsend.capture.value); EXPECT_EQ(0x0, irsend.capture.address); EXPECT_EQ(0x0, irsend.capture.command); + EXPECT_EQ( + "Power Toggle: On, Mode: 1 (Cool), Fan: 3 (Auto), Temp: 30C", + IRAcUtils::resultAcToString(&irsend.capture)); } TEST(TestDecodeAirwell, SyntheticExample) { @@ -192,6 +199,9 @@ TEST(TestDecodeAirwell, RealExample2) { EXPECT_EQ(0xB0C0181B, irsend.capture.value); EXPECT_EQ(0x0, irsend.capture.address); EXPECT_EQ(0x0, irsend.capture.command); + EXPECT_EQ( + "Power Toggle: Off, Mode: 2 (Heat), Fan: 3 (Auto), Temp: 23C", + IRAcUtils::resultAcToString(&irsend.capture)); // Resend it as a synthetic to see if it decodes to the same value. irsend.reset(); @@ -210,7 +220,7 @@ TEST(TestUtils, Housekeeping) { ASSERT_EQ("AIRWELL", typeToString(decode_type_t::AIRWELL)); ASSERT_EQ(decode_type_t::AIRWELL, strToDecodeType("AIRWELL")); ASSERT_FALSE(hasACState(decode_type_t::AIRWELL)); - ASSERT_FALSE(IRac::isProtocolSupported(decode_type_t::AIRWELL)); + ASSERT_TRUE(IRac::isProtocolSupported(decode_type_t::AIRWELL)); ASSERT_EQ(kAirwellBits, IRsend::defaultBits(decode_type_t::AIRWELL)); ASSERT_EQ(kAirwellMinRepeats, IRsend::minRepeats(decode_type_t::AIRWELL)); } @@ -250,4 +260,142 @@ TEST(TestDecodeAirwell, RealExample3) { EXPECT_EQ(0x60080002, irsend.capture.value); EXPECT_EQ(0x0, irsend.capture.address); EXPECT_EQ(0x0, irsend.capture.command); + EXPECT_EQ( + "Power Toggle: Off, Mode: 1 (Cool), Fan: 2 (High), Temp: 16C", + IRAcUtils::resultAcToString(&irsend.capture)); +} + +// Tests for IRAirwellAc class. + +TEST(TestAirwellAcClass, PowerToggle) { + IRAirwellAc ac(kGpioUnused); + ac.begin(); + + ac.setPowerToggle(true); + EXPECT_TRUE(ac.getPowerToggle()); + ac.setPowerToggle(false); + EXPECT_FALSE(ac.getPowerToggle()); + ac.setPowerToggle(true); + EXPECT_TRUE(ac.getPowerToggle()); +} + +TEST(TestAirwellAcClass, Temperature) { + IRAirwellAc ac(kGpioUnused); + ac.begin(); + + ac.setTemp(0); + EXPECT_EQ(kAirwellMinTemp, ac.getTemp()); + + ac.setTemp(255); + EXPECT_EQ(kAirwellMaxTemp, ac.getTemp()); + + ac.setTemp(kAirwellMinTemp); + EXPECT_EQ(kAirwellMinTemp, ac.getTemp()); + + ac.setTemp(kAirwellMaxTemp); + EXPECT_EQ(kAirwellMaxTemp, ac.getTemp()); + + ac.setTemp(kAirwellMinTemp - 1); + EXPECT_EQ(kAirwellMinTemp, ac.getTemp()); + + ac.setTemp(kAirwellMaxTemp + 1); + EXPECT_EQ(kAirwellMaxTemp, ac.getTemp()); + + ac.setTemp(17); + EXPECT_EQ(17, ac.getTemp()); + + ac.setTemp(21); + EXPECT_EQ(21, ac.getTemp()); + + ac.setTemp(25); + EXPECT_EQ(25, ac.getTemp()); + + ac.setTemp(29); + EXPECT_EQ(29, ac.getTemp()); +} + +TEST(TestAirwellAcClass, OperatingMode) { + IRAirwellAc ac(kGpioUnused); + ac.begin(); + + ac.setMode(kAirwellAuto); + EXPECT_EQ(kAirwellAuto, ac.getMode()); + + ac.setMode(kAirwellCool); + EXPECT_EQ(kAirwellCool, ac.getMode()); + + ac.setMode(kAirwellHeat); + EXPECT_EQ(kAirwellHeat, ac.getMode()); + + ac.setMode(kAirwellDry); + EXPECT_EQ(kAirwellDry, ac.getMode()); + + ac.setMode(kAirwellFan); + EXPECT_EQ(kAirwellFan, ac.getMode()); + + ac.setMode(kAirwellFan + 1); + EXPECT_EQ(kAirwellAuto, ac.getMode()); + + ac.setMode(255); + EXPECT_EQ(kAirwellAuto, ac.getMode()); +} + +TEST(TestAirwellAcClass, FanSpeed) { + IRAirwellAc ac(0); + ac.begin(); + + ac.setFan(0); + EXPECT_EQ(kAirwellFanLow, ac.getFan()); + + ac.setFan(255); + EXPECT_EQ(kAirwellFanAuto, ac.getFan()); + + ac.setFan(kAirwellFanHigh); + EXPECT_EQ(kAirwellFanHigh, ac.getFan()); + + ac.setFan(kAirwellFanHigh + 2); + EXPECT_EQ(kAirwellFanAuto, ac.getFan()); + + ac.setFan(kAirwellFanHigh - 1); + EXPECT_EQ(kAirwellFanHigh - 1, ac.getFan()); + + ac.setFan(1); + EXPECT_EQ(1, ac.getFan()); + + ac.setFan(1); + EXPECT_EQ(1, ac.getFan()); + + ac.setFan(3); + EXPECT_EQ(3, ac.getFan()); +} + +// Test human readable output. +TEST(TestAirwellAcClass, HumanReadable) { + IRAirwellAc ac(kGpioUnused); + EXPECT_EQ( + "Power Toggle: Off, Mode: 5 (Fan), Fan: 0 (Low), Temp: 25C", + ac.toString()); + ac.setPowerToggle(true); + ac.setMode(kAirwellHeat); + ac.setTemp(30); + ac.setFan(kAirwellFanAuto); + EXPECT_EQ( + "Power Toggle: On, Mode: 2 (Heat), Fan: 3 (Auto), Temp: 30C", + ac.toString()); +} + +TEST(TestAirwellAcClass, ReconstructKnownState) { + IRAirwellAc ac(kGpioUnused); + const uint64_t expected = 0x240380002; + ac.begin(); + ac.stateReset(); + ASSERT_NE(expected, ac.getRaw()); + ac.setPowerToggle(true); + ac.setMode(kAirwellCool); + ac.setTemp(22); + ac.setFan(kAirwellFanLow); + EXPECT_EQ(expected, ac.getRaw()); + EXPECT_EQ( + "Power Toggle: On, Mode: 1 (Cool), Fan: 0 (Low), Temp: 22C", + ac.toString()); } diff --git a/lib/IRremoteESP8266-2.7.8/test/ir_LG_test.cpp b/lib/IRremoteESP8266-2.7.8/test/ir_LG_test.cpp index d899576b9..8d5dc1246 100644 --- a/lib/IRremoteESP8266-2.7.8/test/ir_LG_test.cpp +++ b/lib/IRremoteESP8266-2.7.8/test/ir_LG_test.cpp @@ -448,7 +448,7 @@ TEST(TestDecodeLG, Issue620) { // Resend the same code as the report is a sent code doesn't decode // to the same message code. - IRLgAc ac(0); + IRLgAc ac(kGpioUnused); irsend.sendLG(0x8808721); irsend.makeDecodeResult(); ASSERT_TRUE(irrecv.decode(&irsend.capture)); @@ -477,7 +477,7 @@ TEST(TestDecodeLG, Issue620) { } TEST(TestIRLgAcClass, SetAndGetPower) { - IRLgAc ac(0); + IRLgAc ac(kGpioUnused); ac.on(); EXPECT_TRUE(ac.getPower()); ac.off(); @@ -489,7 +489,7 @@ TEST(TestIRLgAcClass, SetAndGetPower) { } TEST(TestIRLgAcClass, SetAndGetTemp) { - IRLgAc ac(0); + IRLgAc ac(kGpioUnused); ac.setTemp(25); EXPECT_EQ(25, ac.getTemp()); ac.setTemp(kLgAcMinTemp); @@ -503,7 +503,7 @@ TEST(TestIRLgAcClass, SetAndGetTemp) { } TEST(TestIRLgAcClass, SetAndGetMode) { - IRLgAc ac(0); + IRLgAc ac(kGpioUnused); ac.setMode(kLgAcCool); ac.setFan(kLgAcFanAuto); ac.setTemp(25); @@ -517,22 +517,22 @@ TEST(TestIRLgAcClass, SetAndGetMode) { } TEST(TestIRLgAcClass, SetAndGetFan) { - IRLgAc ac(0); + IRLgAc ac(kGpioUnused); ac.setMode(kLgAcCool); ac.setFan(kLgAcFanAuto); EXPECT_EQ(kLgAcFanAuto, ac.getFan()); - ac.setFan(kLgAcFanLow); - EXPECT_EQ(kLgAcFanLow, ac.getFan()); + ac.setFan(kLgAcFanLowest); + EXPECT_EQ(kLgAcFanLowest, ac.getFan()); ac.setFan(kLgAcFanHigh); EXPECT_EQ(kLgAcFanHigh, ac.getFan()); ac.setFan(kLgAcFanAuto + 1); EXPECT_EQ(kLgAcFanAuto, ac.getFan()); - ac.setFan(kLgAcFanLow - 1); + ac.setFan(kLgAcFanLowest - 1); EXPECT_EQ(kLgAcFanAuto, ac.getFan()); } TEST(TestIRLgAcClass, toCommon) { - IRLgAc ac(0); + IRLgAc ac(kGpioUnused); ac.setPower(true); ac.setMode(kLgAcCool); ac.setTemp(20); @@ -564,7 +564,7 @@ TEST(TestIRLgAcClass, toCommon) { } TEST(TestIRLgAcClass, HumanReadable) { - IRLgAc ac(0); + IRLgAc ac(kGpioUnused); EXPECT_EQ( "Model: 1 (GE6711AR2853M), " @@ -583,17 +583,17 @@ TEST(TestIRLgAcClass, HumanReadable) { ac.setTemp(kLgAcMinTemp); EXPECT_EQ( "Model: 1 (GE6711AR2853M), " - "Power: On, Mode: 0 (Cool), Temp: 16C, Fan: 0 (Low)", + "Power: On, Mode: 0 (Cool), Temp: 16C, Fan: 1 (Low)", ac.toString()); ac.setTemp(ac.getTemp() + 1); EXPECT_EQ( "Model: 1 (GE6711AR2853M), " - "Power: On, Mode: 0 (Cool), Temp: 17C, Fan: 0 (Low)", + "Power: On, Mode: 0 (Cool), Temp: 17C, Fan: 1 (Low)", ac.toString()); ac.setTemp(ac.getTemp() - 1); EXPECT_EQ( "Model: 1 (GE6711AR2853M), " - "Power: On, Mode: 0 (Cool), Temp: 16C, Fan: 0 (Low)", + "Power: On, Mode: 0 (Cool), Temp: 16C, Fan: 1 (Low)", ac.toString()); ac.setPower(false); EXPECT_EQ( @@ -603,7 +603,7 @@ TEST(TestIRLgAcClass, HumanReadable) { } TEST(TestIRLgAcClass, SetAndGetRaw) { - IRLgAc ac(0); + IRLgAc ac(kGpioUnused); ac.setRaw(0x8800A4E); ASSERT_EQ(0x8800A4E, ac.getRaw()); @@ -621,7 +621,7 @@ TEST(TestIRLgAcClass, SetAndGetRaw) { } TEST(TestIRLgAcClass, MessageConstruction) { - IRLgAc ac(0); + IRLgAc ac(kGpioUnused); ac.on(); ac.setMode(kLgAcCool); @@ -635,7 +635,7 @@ TEST(TestIRLgAcClass, MessageConstruction) { } TEST(TestIRLgAcClass, isValidLgAc) { - IRLgAc ac(0); + IRLgAc ac(kGpioUnused); ac.setRaw(0x8800A4E); ASSERT_TRUE(ac.isValidLgAc()); @@ -670,7 +670,7 @@ TEST(TestUtils, Housekeeping) { } TEST(TestIRLgAcClass, KnownExamples) { - IRLgAc ac(0); + IRLgAc ac(kGpioUnused); // Ref: // https://github.com/crankyoldgit/IRremoteESP8266/issues/1008#issuecomment-570646648 @@ -694,7 +694,7 @@ TEST(TestIRLgAcClass, KnownExamples) { ASSERT_TRUE(ac.isValidLgAc()); EXPECT_EQ( "Model: 1 (GE6711AR2853M), " - "Power: On, Mode: 1 (Dry), Temp: 21C, Fan: 0 (Low)", + "Power: On, Mode: 1 (Dry), Temp: 21C, Fan: 0 (Quiet)", ac.toString()); ac.setRaw(0x880C758); @@ -716,7 +716,7 @@ TEST(TestIRLgAcClass, KnownExamples) { ASSERT_TRUE(ac.isValidLgAc()); EXPECT_EQ( "Model: 1 (GE6711AR2853M), " - "Power: On, Mode: 0 (Cool), Temp: 22C, Fan: 0 (Low)", + "Power: On, Mode: 0 (Cool), Temp: 22C, Fan: 0 (Quiet)", ac.toString()); ac.setRaw(0x8808721); @@ -816,7 +816,7 @@ TEST(TestDecodeLG2, Issue1008) { EXPECT_EQ(0x8800347, irsend.capture.value); irsend.reset(); - IRLgAc ac(0); + IRLgAc ac(kGpioUnused); ac.setRaw(0x8800347); ac.setModel(lg_ac_remote_model_t::AKB75215403); // aka. 2 ac.send(); @@ -835,7 +835,7 @@ TEST(TestDecodeLG2, Issue1008) { } TEST(TestIRLgAcClass, DifferentModels) { - IRLgAc ac(0); + IRLgAc ac(kGpioUnused); IRrecv capture(0); ac.setRaw(0x8800347); @@ -872,3 +872,12 @@ TEST(TestIRLgAcClass, DifferentModels) { ASSERT_EQ(expected2, IRAcUtils::resultAcToString(&ac._irsend.capture)); ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p)); } + +TEST(TestIRLgAcClass, FanSpeedIssue1214) { + EXPECT_EQ(kLgAcFanLowest, IRLgAc::convertFan(stdAc::fanspeed_t::kMin)); + EXPECT_EQ(kLgAcFanLow, IRLgAc::convertFan(stdAc::fanspeed_t::kLow)); + EXPECT_EQ(kLgAcFanMedium, IRLgAc::convertFan(stdAc::fanspeed_t::kMedium)); + EXPECT_EQ(kLgAcFanHigh, IRLgAc::convertFan(stdAc::fanspeed_t::kHigh)); + EXPECT_EQ(kLgAcFanHigh, IRLgAc::convertFan(stdAc::fanspeed_t::kMax)); + EXPECT_EQ(kLgAcFanAuto, IRLgAc::convertFan(stdAc::fanspeed_t::kAuto)); +} diff --git a/lib/IRremoteESP8266-2.7.8/test/ir_Midea_test.cpp b/lib/IRremoteESP8266-2.7.8/test/ir_Midea_test.cpp index 24c2610a4..adce3f9d9 100644 --- a/lib/IRremoteESP8266-2.7.8/test/ir_Midea_test.cpp +++ b/lib/IRremoteESP8266-2.7.8/test/ir_Midea_test.cpp @@ -441,13 +441,13 @@ TEST(TestMideaACClass, Sleep) { } TEST(TestMideaACClass, HumanReadableOutput) { - IRMideaAC ac(0); + IRMideaAC ac(kGpioUnused); ac.begin(); ac.setRaw(0xA1826FFFFF62); EXPECT_EQ( "Power: On, Mode: 2 (Auto), Celsius: Off, Temp: 25C/77F, Fan: 0 (Auto), " - "Sleep: Off, Swing(V) Toggle: Off", ac.toString()); + "Sleep: Off, Swing(V) Toggle: Off, Econo Toggle: Off", ac.toString()); ac.off(); ac.setTemp(25, true); ac.setFan(kMideaACFanHigh); @@ -455,16 +455,16 @@ TEST(TestMideaACClass, HumanReadableOutput) { ac.setSleep(true); EXPECT_EQ( "Power: Off, Mode: 1 (Dry), Celsius: Off, Temp: 25C/77F, Fan: 3 (High), " - "Sleep: On, Swing(V) Toggle: Off", ac.toString()); + "Sleep: On, Swing(V) Toggle: Off, Econo Toggle: Off", ac.toString()); ac.setUseCelsius(true); EXPECT_EQ( "Power: Off, Mode: 1 (Dry), Celsius: On, Temp: 25C/77F, Fan: 3 (High), " - "Sleep: On, Swing(V) Toggle: Off", ac.toString()); + "Sleep: On, Swing(V) Toggle: Off, Econo Toggle: Off", ac.toString()); ac.setRaw(0xA19867FFFF7E); EXPECT_EQ( "Power: On, Mode: 0 (Cool), Celsius: Off, Temp: 21C/69F, Fan: 3 (High), " - "Sleep: Off, Swing(V) Toggle: Off", ac.toString()); + "Sleep: Off, Swing(V) Toggle: Off, Econo Toggle: Off", ac.toString()); } // Tests for decodeMidea(). @@ -672,14 +672,14 @@ TEST(TestDecodeMidea, DecodeRealExample) { EXPECT_EQ(0xA18263FFFF6E, irsend.capture.value); EXPECT_EQ( "Power: On, Mode: 2 (Auto), Celsius: Off, Temp: 18C/65F, Fan: 0 (Auto), " - "Sleep: Off, Swing(V) Toggle: Off", + "Sleep: Off, Swing(V) Toggle: Off, Econo Toggle: Off", IRAcUtils::resultAcToString(&irsend.capture)); stdAc::state_t r, p; ASSERT_TRUE(IRAcUtils::decodeToState(&irsend.capture, &r, &p)); } TEST(TestMideaACClass, toCommon) { - IRMideaAC ac(0); + IRMideaAC ac(kGpioUnused); ac.setPower(true); ac.setMode(kMideaACCool); ac.setUseCelsius(true); @@ -709,7 +709,7 @@ TEST(TestMideaACClass, toCommon) { // https://github.com/crankyoldgit/IRremoteESP8266/issues/819 TEST(TestMideaACClass, CelsiusRemoteTemp) { - IRMideaAC ac(0); + IRMideaAC ac(kGpioUnused); uint64_t on_cool_low_17c = 0xA18840FFFF56; uint64_t on_cool_low_30c = 0xA1884DFFFF5D; ac.on(); @@ -722,13 +722,13 @@ TEST(TestMideaACClass, CelsiusRemoteTemp) { EXPECT_EQ(on_cool_low_17c, ac.getRaw()); EXPECT_EQ( "Power: On, Mode: 0 (Cool), Celsius: On, Temp: 17C/62F, Fan: 1 (Low), " - "Sleep: Off, Swing(V) Toggle: Off", ac.toString()); + "Sleep: Off, Swing(V) Toggle: Off, Econo Toggle: Off", ac.toString()); ac.setRaw(on_cool_low_17c); EXPECT_EQ(17, ac.getTemp(true)); EXPECT_EQ(62, ac.getTemp(false)); EXPECT_EQ( "Power: On, Mode: 0 (Cool), Celsius: On, Temp: 17C/62F, Fan: 1 (Low), " - "Sleep: Off, Swing(V) Toggle: Off", ac.toString()); + "Sleep: Off, Swing(V) Toggle: Off, Econo Toggle: Off", ac.toString()); ac.setTemp(17, true); EXPECT_EQ(17, ac.getTemp(true)); EXPECT_EQ(62, ac.getTemp(false)); @@ -737,26 +737,45 @@ TEST(TestMideaACClass, CelsiusRemoteTemp) { ac.setRaw(on_cool_low_30c); EXPECT_EQ( "Power: On, Mode: 0 (Cool), Celsius: On, Temp: 30C/86F, Fan: 1 (Low), " - "Sleep: Off, Swing(V) Toggle: Off", ac.toString()); + "Sleep: Off, Swing(V) Toggle: Off, Econo Toggle: Off", ac.toString()); } // https://github.com/crankyoldgit/IRremoteESP8266/issues/819 TEST(TestMideaACClass, SwingV) { - IRMideaAC ac(0); + IRMideaAC ac(kGpioUnused); ac.setSwingVToggle(false); ASSERT_FALSE(ac.getSwingVToggle()); ac.setSwingVToggle(true); ASSERT_TRUE(ac.getSwingVToggle()); EXPECT_EQ( "Power: On, Mode: 2 (Auto), Celsius: Off, Temp: 25C/77F, Fan: 0 (Auto), " - "Sleep: Off, Swing(V) Toggle: On", ac.toString()); + "Sleep: Off, Swing(V) Toggle: On, Econo Toggle: Off", ac.toString()); ac.setSwingVToggle(false); ASSERT_FALSE(ac.getSwingVToggle()); EXPECT_EQ( "Power: On, Mode: 2 (Auto), Celsius: Off, Temp: 25C/77F, Fan: 0 (Auto), " - "Sleep: Off, Swing(V) Toggle: Off", ac.toString()); + "Sleep: Off, Swing(V) Toggle: Off, Econo Toggle: Off", ac.toString()); ac.setRaw(kMideaACToggleSwingV); - EXPECT_EQ("Swing(V) Toggle: On", ac.toString()); + EXPECT_EQ("Swing(V) Toggle: On, Econo Toggle: Off", ac.toString()); +} + +// https://github.com/crankyoldgit/IRremoteESP8266/pull/1213 +TEST(TestMideaACClass, Econo) { + IRMideaAC ac(kGpioUnused); + ac.setEconoToggle(false); + ASSERT_FALSE(ac.getEconoToggle()); + ac.setEconoToggle(true); + ASSERT_TRUE(ac.getEconoToggle()); + EXPECT_EQ( + "Power: On, Mode: 2 (Auto), Celsius: Off, Temp: 25C/77F, Fan: 0 (Auto), " + "Sleep: Off, Swing(V) Toggle: Off, Econo Toggle: On", ac.toString()); + ac.setEconoToggle(false); + ASSERT_FALSE(ac.getEconoToggle()); + EXPECT_EQ( + "Power: On, Mode: 2 (Auto), Celsius: Off, Temp: 25C/77F, Fan: 0 (Auto), " + "Sleep: Off, Swing(V) Toggle: Off, Econo Toggle: Off", ac.toString()); + ac.setRaw(kMideaACToggleEcono); + EXPECT_EQ("Swing(V) Toggle: Off, Econo Toggle: On", ac.toString()); } // Test abusing the protocol for sending 6 arbitary bytes. diff --git a/lib/IRremoteESP8266-2.7.8/test/ir_Pioneer_test.cpp b/lib/IRremoteESP8266-2.7.8/test/ir_Pioneer_test.cpp index 37cb56fa2..fce2503e4 100644 --- a/lib/IRremoteESP8266-2.7.8/test/ir_Pioneer_test.cpp +++ b/lib/IRremoteESP8266-2.7.8/test/ir_Pioneer_test.cpp @@ -14,34 +14,34 @@ TEST(TestSendPioneer, SendDataOnly) { irsend.sendPioneer(0); EXPECT_EQ( "f40000d33" - "m8544s4272" - "m534s534m534s534m534s534m534s534m534s534m534s534m534s534m534s534" - "m534s534m534s534m534s534m534s534m534s534m534s534m534s534m534s534" - "m534s534m534s534m534s534m534s534m534s534m534s534m534s534m534s534" - "m534s534m534s534m534s534m534s534m534s534m534s534m534s534m534s534" - "m534s37380" - "m8544s4272" - "m534s534m534s534m534s534m534s534m534s534m534s534m534s534m534s534" - "m534s534m534s534m534s534m534s534m534s534m534s534m534s534m534s534" - "m534s534m534s534m534s534m534s534m534s534m534s534m534s534m534s534" - "m534s534m534s534m534s534m534s534m534s534m534s534m534s534m534s534" - "m534s37380", + "m8506s4191" + "m568s487m568s487m568s487m568s487m568s487m568s487m568s487m568s487" + "m568s487m568s487m568s487m568s487m568s487m568s487m568s487m568s487" + "m568s487m568s487m568s487m568s487m568s487m568s487m568s487m568s487" + "m568s487m568s487m568s487m568s487m568s487m568s487m568s487m568s487" + "m568s37881" + "m8506s4191" + "m568s487m568s487m568s487m568s487m568s487m568s487m568s487m568s487" + "m568s487m568s487m568s487m568s487m568s487m568s487m568s487m568s487" + "m568s487m568s487m568s487m568s487m568s487m568s487m568s487m568s487" + "m568s487m568s487m568s487m568s487m568s487m568s487m568s487m568s487" + "m568s37881", irsend.outputStr()); irsend.sendPioneer(0x55FF00AAAA00FF55); EXPECT_EQ( "f40000d33" - "m8544s4272" - "m534s534m534s1602m534s534m534s1602m534s534m534s1602m534s534m534s1602" - "m534s1602m534s1602m534s1602m534s1602m534s1602m534s1602m534s1602m534s1602" - "m534s534m534s534m534s534m534s534m534s534m534s534m534s534m534s534" - "m534s1602m534s534m534s1602m534s534m534s1602m534s534m534s1602m534s534" - "m534s25098" - "m8544s4272" - "m534s1602m534s534m534s1602m534s534m534s1602m534s534m534s1602m534s534" - "m534s534m534s534m534s534m534s534m534s534m534s534m534s534m534s534" - "m534s1602m534s1602m534s1602m534s1602m534s1602m534s1602m534s1602m534s1602" - "m534s534m534s1602m534s534m534s1602m534s534m534s1602m534s534m534s1602" - "m534s25098", + "m8506s4191" + "m568s487m568s1542m568s487m568s1542m568s487m568s1542m568s487m568s1542" + "m568s1542m568s1542m568s1542m568s1542m568s1542m568s1542m568s1542m568s1542" + "m568s487m568s487m568s487m568s487m568s487m568s487m568s487m568s487" + "m568s1542m568s487m568s1542m568s487m568s1542m568s487m568s1542m568s487" + "m568s25181" + "m8506s4191" + "m568s1542m568s487m568s1542m568s487m568s1542m568s487m568s1542m568s487" + "m568s487m568s487m568s487m568s487m568s487m568s487m568s487m568s487" + "m568s1542m568s1542m568s1542m568s1542m568s1542m568s1542m568s1542m568s1542" + "m568s487m568s1542m568s487m568s1542m568s487m568s1542m568s487m568s1542" + "m568s25181", irsend.outputStr()); } @@ -139,17 +139,91 @@ TEST(TestDecodePioneer, SyntheticPioneerMessage) { irsend.sendPioneer(0x659A857AF50A3DC2, 64, 0); EXPECT_EQ( "f40000d33" - "m8544s4272" - "m534s534m534s1602m534s1602m534s534m534s534m534s1602m534s534m534s1602" - "m534s1602m534s534m534s534m534s1602m534s1602m534s534m534s1602m534s534" - "m534s1602m534s534m534s534m534s534m534s534m534s1602m534s534m534s1602" - "m534s534m534s1602m534s1602m534s1602m534s1602m534s534m534s1602m534s534" - "m534s25098" - "m8544s4272" - "m534s1602m534s1602m534s1602m534s1602m534s534m534s1602m534s534m534s1602" - "m534s534m534s534m534s534m534s534m534s1602m534s534m534s1602m534s534" - "m534s534m534s534m534s1602m534s1602m534s1602m534s1602m534s534m534s1602" - "m534s1602m534s1602m534s534m534s534m534s534m534s534m534s1602m534s534" - "m534s25098", + "m8506s4191" + "m568s487m568s1542m568s1542m568s487m568s487m568s1542m568s487m568s1542" + "m568s1542m568s487m568s487m568s1542m568s1542m568s487m568s1542m568s487" + "m568s1542m568s487m568s487m568s487m568s487m568s1542m568s487m568s1542" + "m568s487m568s1542m568s1542m568s1542m568s1542m568s487m568s1542m568s487" + "m568s25181" + "m8506s4191" + "m568s1542m568s1542m568s1542m568s1542m568s487m568s1542m568s487m568s1542" + "m568s487m568s487m568s487m568s487m568s1542m568s487m568s1542m568s487" + "m568s487m568s487m568s1542m568s1542m568s1542m568s1542m568s487m568s1542" + "m568s1542m568s1542m568s487m568s487m568s487m568s487m568s1542m568s487" + "m568s25181", irsend.outputStr()); } + +// Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/1220#issuecomment-661598412 +TEST(TestDecodePioneer, Issue1220) { + IRsendTest irsend(0); + IRrecv irrecv(0); + irsend.begin(); + + irsend.reset(); + // Pwr Toggle {"IrReceived":{"Protocol":"PIONEER","Bits":64, + // "Data":"0xA55A38C7A55A38C7"}} + const uint16_t rawPowerToggle[203] = { + 8510, 4188, 580, 1544, 556, 470, 582, 1566, 556, 494, 560, 488, 556, 1542, + 580, 470, 584, 1540, 582, 492, 552, 1522, 578, 496, 558, 1564, 558, 1542, + 580, 470, 586, 1564, 536, 512, 532, 494, 560, 464, 582, 1542, 580, 1544, + 578, 1544, 578, 498, 558, 492, 562, 488, 556, 1518, 582, 1542, 582, 492, + 552, 498, 556, 494, 550, 1572, 560, 1538, 562, 1536, 586, 25188, + 8512, 4186, 584, 1540, 584, 468, 576, 1572, 528, 522, 532, 492, 552, 1546, + 584, 492, 550, 1548, 582, 494, 560, 1538, 580, 468, 586, 1514, 586, 1538, + 584, 490, 554, 1546, 586, 488, 554, 470, 584, 490, 556, 1542, 556, 1542, + 580, 1544, 578, 496, 560, 466, 578, 496, 560, 1538, 584, 1516, 584, 490, + 554, 472, 582, 492, 552, 1546, 586, 1536, 586, 1538, 586, 25162, + 8514, 4184, 582, 1542, 580, 496, 562, 1536, 584, 490, 554, 496, 560, 1538, + 562, 514, 530, 1542, 580, 496, 560, 1538, 584, 466, 578, 1546, 576, 1524, + 578, 496, 558, 1542, 586, 488, 554, 496, 558, 466, 576, 1546, 576, 1548, + 586, 1512, 578, 498, 558, 492, 552, 496, 558, 1540, 580, 1542, 578, 498, + 556, 494, 550, 498, 556, 1516, 584, 1540, 584, 1540, 580}; + irsend.sendRaw(rawPowerToggle, 203, 40); // Pioneer uses 40kHz + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(PIONEER, irsend.capture.decode_type); + EXPECT_EQ(kPioneerBits, irsend.capture.bits); + EXPECT_EQ(0xA55A38C7A55A38C7, irsend.capture.value); + EXPECT_EQ(0xA51C, irsend.capture.address); + EXPECT_EQ(0xA51C, irsend.capture.command); + + irsend.reset(); + irsend.sendPioneer(0xA55A38C7A55A38C7, 64, 1); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(PIONEER, irsend.capture.decode_type); + EXPECT_EQ(kPioneerBits, irsend.capture.bits); + EXPECT_EQ(0xA55A38C7A55A38C7, irsend.capture.value); + EXPECT_EQ(0xA51C, irsend.capture.address); + EXPECT_EQ(0xA51C, irsend.capture.command); + EXPECT_EQ( + "f40000d33" + "m8506s4191" + "m568s1542m568s487m568s1542m568s487m568s487m568s1542m568s487m568s1542" + "m568s487m568s1542m568s487m568s1542m568s1542m568s487m568s1542m568s487" + "m568s487m568s487m568s1542m568s1542m568s1542m568s487m568s487m568s487" + "m568s1542m568s1542m568s487m568s487m568s487m568s1542m568s1542m568s1542" + "m568s25181" + "m8506s4191" + "m568s1542m568s487m568s1542m568s487m568s487m568s1542m568s487m568s1542" + "m568s487m568s1542m568s487m568s1542m568s1542m568s487m568s1542m568s487" + "m568s487m568s487m568s1542m568s1542m568s1542m568s487m568s487m568s487" + "m568s1542m568s1542m568s487m568s487m568s487m568s1542m568s1542m568s1542" + "m568s25181" + "m8506s4191" + "m568s1542m568s487m568s1542m568s487m568s487m568s1542m568s487m568s1542" + "m568s487m568s1542m568s487m568s1542m568s1542m568s487m568s1542m568s487" + "m568s487m568s487m568s1542m568s1542m568s1542m568s487m568s487m568s487" + "m568s1542m568s1542m568s487m568s487m568s487m568s1542m568s1542m568s1542" + "m568s25181" + "m8506s4191" + "m568s1542m568s487m568s1542m568s487m568s487m568s1542m568s487m568s1542" + "m568s487m568s1542m568s487m568s1542m568s1542m568s487m568s1542m568s487" + "m568s487m568s487m568s1542m568s1542m568s1542m568s487m568s487m568s487" + "m568s1542m568s1542m568s487m568s487m568s487m568s1542m568s1542m568s1542" + "m568s25181", + irsend.outputStr()); + + EXPECT_EQ(0xA55A38C7A55A38C7, irsend.encodePioneer(0xA51C, 0xA51C)); +} diff --git a/lib/IRremoteESP8266-2.7.8/test/ir_Samsung_test.cpp b/lib/IRremoteESP8266-2.7.8/test/ir_Samsung_test.cpp index 52165b780..59fc964e3 100644 --- a/lib/IRremoteESP8266-2.7.8/test/ir_Samsung_test.cpp +++ b/lib/IRremoteESP8266-2.7.8/test/ir_Samsung_test.cpp @@ -12,8 +12,10 @@ // General housekeeping TEST(TestSamsung, Housekeeping) { - ASSERT_EQ("SAMSUNG", typeToString(SAMSUNG)); - ASSERT_FALSE(hasACState(SAMSUNG)); + ASSERT_EQ("SAMSUNG", typeToString(decode_type_t::SAMSUNG)); + ASSERT_EQ(decode_type_t::SAMSUNG, strToDecodeType("SAMSUNG")); + ASSERT_FALSE(hasACState(decode_type_t::SAMSUNG)); + ASSERT_EQ(kSamsungBits, IRsend::defaultBits(decode_type_t::SAMSUNG)); } // Tests for sendSAMSUNG(). @@ -307,8 +309,12 @@ TEST(TestDecodeSamsung, FailToDecodeNonSamsungExample) { // General housekeeping TEST(TestSamsungAC, Housekeeping) { - ASSERT_EQ("SAMSUNG_AC", typeToString(SAMSUNG_AC)); - ASSERT_TRUE(hasACState(SAMSUNG_AC)); + ASSERT_EQ("SAMSUNG_AC", typeToString(decode_type_t::SAMSUNG_AC)); + ASSERT_EQ(decode_type_t::SAMSUNG_AC, strToDecodeType("SAMSUNG_AC")); + ASSERT_TRUE(hasACState(decode_type_t::SAMSUNG_AC)); + ASSERT_TRUE(IRac::isProtocolSupported(decode_type_t::SAMSUNG_AC)); + ASSERT_EQ(kSamsungAcBits, IRsend::defaultBits(decode_type_t::SAMSUNG_AC)); + ASSERT_EQ(kNoRepeat, IRsend::minRepeats(decode_type_t::SAMSUNG_AC)); } // Tests for sendSamsungAC(). @@ -1143,34 +1149,36 @@ TEST(TestSendSamsung36, SendDataOnly) { irsend.sendSamsung36(0); EXPECT_EQ( "f38000d50" - "m4480s4480" - "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" - "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" - "m560s4480" - "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" - "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" - "m560s560m560s560m560s560m560s560" - "m560s26880", + "m4515s4438" + "m512s490m512s490m512s490m512s490m512s490m512s490m512s490m512s490" + "m512s490m512s490m512s490m512s490m512s490m512s490m512s490m512s490" + "m512s4438" + "m512s490m512s490m512s490m512s490m512s490m512s490m512s490m512s490" + "m512s490m512s490m512s490m512s490m512s490m512s490m512s490m512s490" + "m512s490m512s490m512s490m512s490" + "m512s26880", irsend.outputStr()); irsend.sendSamsung36(0x400E00FF); EXPECT_EQ( "f38000d50" - "m4480s4480" - "m560s560m560s560m560s560m560s560m560s560m560s1680m560s560m560s560" - "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" - "m560s4480" - "m560s1680m560s1680m560s1680m560s560m560s560m560s560m560s560m560s560" - "m560s560m560s560m560s560m560s560m560s1680m560s1680m560s1680m560s1680" - "m560s1680m560s1680m560s1680m560s1680" - "m560s26880", + "m4515s4438" + "m512s490m512s490m512s490m512s490m512s490m512s1468m512s490m512s490" + "m512s490m512s490m512s490m512s490m512s490m512s490m512s490m512s490" + "m512s4438" + "m512s1468m512s1468m512s1468m512s490m512s490m512s490m512s490m512s490" + "m512s490m512s490m512s490m512s490m512s1468m512s1468m512s1468m512s1468" + "m512s1468m512s1468m512s1468m512s1468" + "m512s26880", irsend.outputStr()); irsend.reset(); } // General housekeeping TEST(TestSamsung36, Housekeeping) { - ASSERT_EQ("SAMSUNG36", typeToString(SAMSUNG36)); - ASSERT_FALSE(hasACState(SAMSUNG36)); + ASSERT_EQ("SAMSUNG36", typeToString(decode_type_t::SAMSUNG36)); + ASSERT_EQ(decode_type_t::SAMSUNG36, strToDecodeType("SAMSUNG36")); + ASSERT_FALSE(hasACState(decode_type_t::SAMSUNG36)); + ASSERT_EQ(kSamsung36Bits, IRsend::defaultBits(decode_type_t::SAMSUNG36)); } // Test sending with different repeats. @@ -1182,50 +1190,50 @@ TEST(TestSendSamsung36, SendWithRepeats) { irsend.sendSamsung36(0x400E00FF, kSamsung36Bits, 1); // 1 repeat. EXPECT_EQ( "f38000d50" - "m4480s4480" - "m560s560m560s560m560s560m560s560m560s560m560s1680m560s560m560s560" - "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" - "m560s4480" - "m560s1680m560s1680m560s1680m560s560m560s560m560s560m560s560m560s560" - "m560s560m560s560m560s560m560s560m560s1680m560s1680m560s1680m560s1680" - "m560s1680m560s1680m560s1680m560s1680" - "m560s26880" - "m4480s4480" - "m560s560m560s560m560s560m560s560m560s560m560s1680m560s560m560s560" - "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" - "m560s4480" - "m560s1680m560s1680m560s1680m560s560m560s560m560s560m560s560m560s560" - "m560s560m560s560m560s560m560s560m560s1680m560s1680m560s1680m560s1680" - "m560s1680m560s1680m560s1680m560s1680" - "m560s26880", + "m4515s4438" + "m512s490m512s490m512s490m512s490m512s490m512s1468m512s490m512s490" + "m512s490m512s490m512s490m512s490m512s490m512s490m512s490m512s490" + "m512s4438" + "m512s1468m512s1468m512s1468m512s490m512s490m512s490m512s490m512s490" + "m512s490m512s490m512s490m512s490m512s1468m512s1468m512s1468m512s1468" + "m512s1468m512s1468m512s1468m512s1468" + "m512s26880" + "m4515s4438" + "m512s490m512s490m512s490m512s490m512s490m512s1468m512s490m512s490" + "m512s490m512s490m512s490m512s490m512s490m512s490m512s490m512s490" + "m512s4438" + "m512s1468m512s1468m512s1468m512s490m512s490m512s490m512s490m512s490" + "m512s490m512s490m512s490m512s490m512s1468m512s1468m512s1468m512s1468" + "m512s1468m512s1468m512s1468m512s1468" + "m512s26880", irsend.outputStr()); irsend.sendSamsung36(0x400E00FF, kSamsung36Bits, 2); // 2 repeats. EXPECT_EQ( "f38000d50" - "m4480s4480" - "m560s560m560s560m560s560m560s560m560s560m560s1680m560s560m560s560" - "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" - "m560s4480" - "m560s1680m560s1680m560s1680m560s560m560s560m560s560m560s560m560s560" - "m560s560m560s560m560s560m560s560m560s1680m560s1680m560s1680m560s1680" - "m560s1680m560s1680m560s1680m560s1680" - "m560s26880" - "m4480s4480" - "m560s560m560s560m560s560m560s560m560s560m560s1680m560s560m560s560" - "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" - "m560s4480" - "m560s1680m560s1680m560s1680m560s560m560s560m560s560m560s560m560s560" - "m560s560m560s560m560s560m560s560m560s1680m560s1680m560s1680m560s1680" - "m560s1680m560s1680m560s1680m560s1680" - "m560s26880" - "m4480s4480" - "m560s560m560s560m560s560m560s560m560s560m560s1680m560s560m560s560" - "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" - "m560s4480" - "m560s1680m560s1680m560s1680m560s560m560s560m560s560m560s560m560s560" - "m560s560m560s560m560s560m560s560m560s1680m560s1680m560s1680m560s1680" - "m560s1680m560s1680m560s1680m560s1680" - "m560s26880", + "m4515s4438" + "m512s490m512s490m512s490m512s490m512s490m512s1468m512s490m512s490" + "m512s490m512s490m512s490m512s490m512s490m512s490m512s490m512s490" + "m512s4438" + "m512s1468m512s1468m512s1468m512s490m512s490m512s490m512s490m512s490" + "m512s490m512s490m512s490m512s490m512s1468m512s1468m512s1468m512s1468" + "m512s1468m512s1468m512s1468m512s1468" + "m512s26880" + "m4515s4438" + "m512s490m512s490m512s490m512s490m512s490m512s1468m512s490m512s490" + "m512s490m512s490m512s490m512s490m512s490m512s490m512s490m512s490" + "m512s4438" + "m512s1468m512s1468m512s1468m512s490m512s490m512s490m512s490m512s490" + "m512s490m512s490m512s490m512s490m512s1468m512s1468m512s1468m512s1468" + "m512s1468m512s1468m512s1468m512s1468" + "m512s26880" + "m4515s4438" + "m512s490m512s490m512s490m512s490m512s490m512s1468m512s490m512s490" + "m512s490m512s490m512s490m512s490m512s490m512s490m512s490m512s490" + "m512s4438" + "m512s1468m512s1468m512s1468m512s490m512s490m512s490m512s490m512s490" + "m512s490m512s490m512s490m512s490m512s1468m512s1468m512s1468m512s1468" + "m512s1468m512s1468m512s1468m512s1468" + "m512s26880", irsend.outputStr()); } diff --git a/lib/IRremoteESP8266-2.7.8/test/ir_Sanyo_test.cpp b/lib/IRremoteESP8266-2.7.8/test/ir_Sanyo_test.cpp index c59113cda..cbdd6ba39 100644 --- a/lib/IRremoteESP8266-2.7.8/test/ir_Sanyo_test.cpp +++ b/lib/IRremoteESP8266-2.7.8/test/ir_Sanyo_test.cpp @@ -1,5 +1,9 @@ -// Copyright 2017 David Conran +// Copyright 2017-2020 David Conran +#include "ir_Sanyo.h" +#include "IRac.h" +#include "IRrecv.h" +#include "IRrecv_test.h" #include "IRsend.h" #include "IRsend_test.h" #include "gtest/gtest.h" @@ -257,3 +261,272 @@ TEST(TestDecodeSanyoLC7461, FailToDecodeNonSanyoLC7461Example) { irrecv.decodeSanyoLC7461(&irsend.capture, kStartOffset, kSanyoLC7461Bits, false)); } + +TEST(TestUtils, Housekeeping) { + // Sanyo LC7461 + ASSERT_EQ("SANYO_LC7461", typeToString(decode_type_t::SANYO_LC7461)); + ASSERT_EQ(decode_type_t::SANYO_LC7461, strToDecodeType("SANYO_LC7461")); + ASSERT_FALSE(hasACState(decode_type_t::SANYO_LC7461)); + ASSERT_FALSE(IRac::isProtocolSupported(decode_type_t::SANYO_LC7461)); + ASSERT_EQ(kSanyoLC7461Bits, IRsend::defaultBits(decode_type_t::SANYO_LC7461)); + ASSERT_EQ(kNoRepeat, IRsend::minRepeats(decode_type_t::SANYO_LC7461)); + // Sanyo A/C + ASSERT_EQ("SANYO_AC", typeToString(decode_type_t::SANYO_AC)); + ASSERT_EQ(decode_type_t::SANYO_AC, strToDecodeType("SANYO_AC")); + ASSERT_TRUE(hasACState(decode_type_t::SANYO_AC)); + ASSERT_TRUE(IRac::isProtocolSupported(decode_type_t::SANYO_AC)); + ASSERT_EQ(kSanyoAcBits, IRsend::defaultBits(decode_type_t::SANYO_AC)); + ASSERT_EQ(kNoRepeat, IRsend::minRepeats(decode_type_t::SANYO_AC)); +} + +TEST(TestDecodeSanyoAc, DecodeRealExamples) { + IRsendTest irsend(kGpioUnused); + IRrecv irrecv(kGpioUnused); + // Ref: "On" from https://github.com/crankyoldgit/IRremoteESP8266/issues/1211#issue-650997449 + const uint16_t rawData[148] = { + 8456, 4192, + 624, 448, 584, 1508, 608, 452, 580, 1512, 628, 452, 604, 1468, 560, 1552, + 600, 472, 584, 1532, 580, 472, 528, 516, 512, 540, 576, 1516, 628, 1472, + 612, 1508, 612, 452, 580, 1512, 628, 1468, 612, 1496, 632, 444, 580, 480, + 580, 476, 580, 1496, 564, 508, 576, 480, 576, 480, 580, 476, 584, 472, + 584, 468, 584, 480, 520, 512, 580, 480, 576, 480, 580, 476, 584, 472, + 584, 472, 528, 508, 524, 1568, 600, 480, 576, 480, 584, 1492, 560, 512, + 580, 1536, 576, 480, 580, 476, 580, 476, 528, 528, 524, 1568, 580, 476, + 584, 476, 580, 476, 580, 472, 528, 512, 520, 536, 576, 480, 580, 480, + 576, 480, 576, 476, 532, 528, 520, 512, 576, 480, 584, 476, 580, 476, + 580, 480, 576, 472, 528, 1548, 600, 480, 576, 480, 576, 1520, 592, 1496, + 600, 476, 580, 480, 576}; + const uint8_t expectedState[kSanyoAcStateLength] = { + 0x6A, 0x71, 0x47, 0x00, 0x20, 0x85, 0x00, 0x00, 0x32}; + irsend.begin(); + irsend.reset(); + irsend.sendRaw(rawData, 148, 38000); + irsend.makeDecodeResult(); + + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(SANYO_AC, irsend.capture.decode_type); + EXPECT_EQ(kSanyoAcBits, irsend.capture.bits); + EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); + EXPECT_FALSE(irsend.capture.repeat); + EXPECT_EQ( + "Power: On, Mode: 2 (Cool), Temp: 21C, Fan: 0 (Auto), " + "Swing(V): 5 (Upper Middle), Sleep: Off, Beep: On, Sensor: Room, " + "Sensor Temp: 11C, Off Timer: Off", + IRAcUtils::resultAcToString(&irsend.capture)); +} + +TEST(TestDecodeSanyoAc, SyntheticSelfDecode) { + IRsendTest irsend(kGpioUnused); + IRrecv irrecv(kGpioUnused); + const uint8_t expectedState[kSanyoAcStateLength] = { + 0x6A, 0x71, 0x47, 0x00, 0x20, 0x85, 0x00, 0x00, 0x32}; + irsend.begin(); + irsend.reset(); + irsend.sendSanyoAc(expectedState); + irsend.makeDecodeResult(); + + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(SANYO_AC, irsend.capture.decode_type); + EXPECT_EQ(kSanyoAcBits, irsend.capture.bits); + EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); + EXPECT_FALSE(irsend.capture.repeat); + EXPECT_EQ( + "Power: On, Mode: 2 (Cool), Temp: 21C, Fan: 0 (Auto), " + "Swing(V): 5 (Upper Middle), Sleep: Off, Beep: On, Sensor: Room, " + "Sensor Temp: 11C, Off Timer: Off", + IRAcUtils::resultAcToString(&irsend.capture)); + EXPECT_EQ( + "f38000d50" + "m8500s4200" + "m500s550m500s1600m500s550m500s1600m500s550m500s1600m500s1600m500s550" + "m500s1600m500s550m500s550m500s550m500s1600m500s1600m500s1600m500s550" + "m500s1600m500s1600m500s1600m500s550m500s550m500s550m500s1600m500s550" + "m500s550m500s550m500s550m500s550m500s550m500s550m500s550m500s550m500s550" + "m500s550m500s550m500s550m500s550m500s1600m500s550m500s550m500s1600" + "m500s550m500s1600m500s550m500s550m500s550m500s550m500s1600m500s550" + "m500s550m500s550m500s550m500s550m500s550m500s550m500s550m500s550m500s550" + "m500s550m500s550m500s550m500s550m500s550m500s550m500s550m500s1600" + "m500s550m500s550m500s1600m500s1600m500s550m500s550" + "m500s100000", + irsend.outputStr()); +} + +// Tests for IRSanyoAc class. + +TEST(TestSanyoAcClass, Power) { + IRSanyoAc ac(kGpioUnused); + ac.begin(); + + ac.on(); + EXPECT_TRUE(ac.getPower()); + + ac.off(); + EXPECT_FALSE(ac.getPower()); + + ac.setPower(true); + EXPECT_TRUE(ac.getPower()); + + ac.setPower(false); + EXPECT_FALSE(ac.getPower()); +} + +TEST(TestSanyoAcClass, Temperature) { + IRSanyoAc ac(kGpioUnused); + ac.begin(); + + ac.setTemp(0); + EXPECT_EQ(kSanyoAcTempMin, ac.getTemp()); + + ac.setTemp(255); + EXPECT_EQ(kSanyoAcTempMax, ac.getTemp()); + + ac.setTemp(kSanyoAcTempMin); + EXPECT_EQ(kSanyoAcTempMin, ac.getTemp()); + + ac.setTemp(kSanyoAcTempMax); + EXPECT_EQ(kSanyoAcTempMax, ac.getTemp()); + + ac.setTemp(kSanyoAcTempMin - 1); + EXPECT_EQ(kSanyoAcTempMin, ac.getTemp()); + + ac.setTemp(kSanyoAcTempMax + 1); + EXPECT_EQ(kSanyoAcTempMax, ac.getTemp()); + + ac.setTemp(17); + EXPECT_EQ(17, ac.getTemp()); + + ac.setTemp(21); + EXPECT_EQ(21, ac.getTemp()); + + ac.setTemp(25); + EXPECT_EQ(25, ac.getTemp()); + + ac.setTemp(30); + EXPECT_EQ(30, ac.getTemp()); +} + +TEST(TestSanyoAcClass, OperatingMode) { + IRSanyoAc ac(kGpioUnused); + ac.begin(); + + ac.setMode(kSanyoAcAuto); + EXPECT_EQ(kSanyoAcAuto, ac.getMode()); + + ac.setMode(kSanyoAcCool); + EXPECT_EQ(kSanyoAcCool, ac.getMode()); + + ac.setMode(kSanyoAcHeat); + EXPECT_EQ(kSanyoAcHeat, ac.getMode()); + + ac.setMode(kSanyoAcDry); + EXPECT_EQ(kSanyoAcDry, ac.getMode()); + + ac.setMode(kSanyoAcAuto + 1); + EXPECT_EQ(kSanyoAcAuto, ac.getMode()); + + ac.setMode(0); + EXPECT_EQ(kSanyoAcAuto, ac.getMode()); + + ac.setMode(255); + EXPECT_EQ(kSanyoAcAuto, ac.getMode()); +} + +TEST(TestSanyoAcClass, FanSpeed) { + IRSanyoAc ac(kGpioUnused); + ac.begin(); + + ac.setFan(kSanyoAcFanAuto); + EXPECT_EQ(kSanyoAcFanAuto, ac.getFan()); + + ac.setFan(kSanyoAcFanHigh); + EXPECT_EQ(kSanyoAcFanHigh, ac.getFan()); + + ac.setFan(kSanyoAcFanLow); + EXPECT_EQ(kSanyoAcFanLow, ac.getFan()); + + ac.setFan(kSanyoAcFanMedium); + EXPECT_EQ(kSanyoAcFanMedium, ac.getFan()); +} + +TEST(TestSanyoAcClass, Sleep) { + IRSanyoAc ac(kGpioUnused); + ac.begin(); + + ac.setSleep(true); + EXPECT_TRUE(ac.getSleep()); + ac.setSleep(false); + EXPECT_FALSE(ac.getSleep()); + ac.setSleep(true); + EXPECT_TRUE(ac.getSleep()); +} + +TEST(TestSanyoAcClass, SwingV) { + IRSanyoAc ac(kGpioUnused); + ac.begin(); + + ac.setSwingV(kSanyoAcSwingVAuto); + EXPECT_EQ(kSanyoAcSwingVAuto, ac.getSwingV()); + + ac.setSwingV(kSanyoAcSwingVHigh); + EXPECT_EQ(kSanyoAcSwingVHigh, ac.getSwingV()); + + ac.setSwingV(kSanyoAcSwingVLow); + EXPECT_EQ(kSanyoAcSwingVLow, ac.getSwingV()); + + ac.setSwingV(kSanyoAcSwingVUpperMiddle); + EXPECT_EQ(kSanyoAcSwingVUpperMiddle, ac.getSwingV()); + + ac.setSwingV(0); + EXPECT_EQ(kSanyoAcSwingVAuto, ac.getSwingV()); + ac.setSwingV(255); + EXPECT_EQ(kSanyoAcSwingVAuto, ac.getSwingV()); +} + +TEST(TestSanyoAcClass, Timers) { + IRSanyoAc ac(kGpioUnused); + ac.begin(); + + ac.setOffTimer(0); + EXPECT_EQ(0, ac.getOffTimer()); + ac.setOffTimer(59); + EXPECT_EQ(0, ac.getOffTimer()); + ac.setOffTimer(60); + EXPECT_EQ(60, ac.getOffTimer()); + ac.setOffTimer(61); + EXPECT_EQ(60, ac.getOffTimer()); + ac.setOffTimer(15 * 60 + 59); + EXPECT_EQ(15 * 60, ac.getOffTimer()); + ac.setOffTimer(16 * 60); + EXPECT_EQ(15 * 60, ac.getOffTimer()); + + const uint8_t offTimer2Hr[kSanyoAcStateLength] = { + 0x6A, 0x6D, 0x4F, 0x02, 0x14, 0x85, 0x00, 0x00, 0x4A}; + ac.setRaw(offTimer2Hr); + EXPECT_EQ(2 * 60, ac.getOffTimer()); + EXPECT_EQ( + "Power: On, Mode: 1 (Heat), Temp: 17C, Fan: 0 (Auto), " + "Swing(V): 5 (Upper Middle), Sleep: Off, Beep: On, " + "Sensor: Room, Sensor Temp: 19C, Off Timer: 02:00", + ac.toString()); +} + +TEST(TestSanyoAcClass, Beep) { + IRSanyoAc ac(kGpioUnused); + ac.begin(); + + ac.setBeep(true); + EXPECT_TRUE(ac.getBeep()); + ac.setBeep(false); + EXPECT_FALSE(ac.getBeep()); + ac.setBeep(true); + EXPECT_TRUE(ac.getBeep()); + + const uint8_t beep_off[kSanyoAcStateLength] = { + 0x6A, 0x6D, 0x11, 0x00, 0x10, 0x85, 0x00, 0x00, 0x33}; + ac.setRaw(beep_off); + EXPECT_FALSE(ac.getBeep()); + const uint8_t beep_on[kSanyoAcStateLength] = { + 0x6A, 0x6E, 0x54, 0x00, 0x10, 0x83, 0x00, 0x00, 0x39}; + ac.setRaw(beep_on); + EXPECT_TRUE(ac.getBeep()); +} diff --git a/lib/IRremoteESP8266-2.7.8/test/ir_Toshiba_test.cpp b/lib/IRremoteESP8266-2.7.8/test/ir_Toshiba_test.cpp index e0ae82987..fcd0e510e 100644 --- a/lib/IRremoteESP8266-2.7.8/test/ir_Toshiba_test.cpp +++ b/lib/IRremoteESP8266-2.7.8/test/ir_Toshiba_test.cpp @@ -11,7 +11,7 @@ // Test sending typical data only. TEST(TestSendToshibaAC, SendDataOnly) { - IRsendTest irsend(4); + IRsendTest irsend(kGpioUnused); irsend.begin(); uint8_t toshiba_code[kToshibaACStateLength] = {0xF2, 0x0D, 0x03, 0xFC, 0x01, @@ -21,33 +21,33 @@ TEST(TestSendToshibaAC, SendDataOnly) { EXPECT_EQ( "f38000d50" "m4400s4300" - "m543s1623m543s1623m543s1623m543s1623m543s472m543s472m543s1623m543s472" - "m543s472m543s472m543s472m543s472m543s1623m543s1623m543s472m543s1623" - "m543s472m543s472m543s472m543s472m543s472m543s472m543s1623m543s1623" - "m543s1623m543s1623m543s1623m543s1623m543s1623m543s1623m543s472m543s472" - "m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s1623" - "m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s472" - "m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s472" - "m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s472" - "m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s472" - "m543s7048" + "m580s1600m580s1600m580s1600m580s1600m580s490m580s490m580s1600m580s490" + "m580s490m580s490m580s490m580s490m580s1600m580s1600m580s490m580s1600" + "m580s490m580s490m580s490m580s490m580s490m580s490m580s1600m580s1600" + "m580s1600m580s1600m580s1600m580s1600m580s1600m580s1600m580s490m580s490" + "m580s490m580s490m580s490m580s490m580s490m580s490m580s490m580s1600" + "m580s490m580s490m580s490m580s490m580s490m580s490m580s490m580s490" + "m580s490m580s490m580s490m580s490m580s490m580s490m580s490m580s490" + "m580s490m580s490m580s490m580s490m580s490m580s490m580s490m580s490" + "m580s490m580s490m580s490m580s490m580s490m580s490m580s490m580s490" + "m580s7400" "m4400s4300" - "m543s1623m543s1623m543s1623m543s1623m543s472m543s472m543s1623m543s472" - "m543s472m543s472m543s472m543s472m543s1623m543s1623m543s472m543s1623" - "m543s472m543s472m543s472m543s472m543s472m543s472m543s1623m543s1623" - "m543s1623m543s1623m543s1623m543s1623m543s1623m543s1623m543s472m543s472" - "m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s1623" - "m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s472" - "m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s472" - "m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s472" - "m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s472" - "m543s7048", + "m580s1600m580s1600m580s1600m580s1600m580s490m580s490m580s1600m580s490" + "m580s490m580s490m580s490m580s490m580s1600m580s1600m580s490m580s1600" + "m580s490m580s490m580s490m580s490m580s490m580s490m580s1600m580s1600" + "m580s1600m580s1600m580s1600m580s1600m580s1600m580s1600m580s490m580s490" + "m580s490m580s490m580s490m580s490m580s490m580s490m580s490m580s1600" + "m580s490m580s490m580s490m580s490m580s490m580s490m580s490m580s490" + "m580s490m580s490m580s490m580s490m580s490m580s490m580s490m580s490" + "m580s490m580s490m580s490m580s490m580s490m580s490m580s490m580s490" + "m580s490m580s490m580s490m580s490m580s490m580s490m580s490m580s490" + "m580s7400", irsend.outputStr()); } // Test sending with repeats. TEST(TestSendToshibaAC, SendWithRepeats) { - IRsendTest irsend(4); + IRsendTest irsend(kGpioUnused); irsend.begin(); irsend.reset(); @@ -58,16 +58,16 @@ TEST(TestSendToshibaAC, SendWithRepeats) { EXPECT_EQ( "f38000d50" "m4400s4300" - "m543s1623m543s1623m543s1623m543s1623m543s472m543s472m543s1623m543s472" - "m543s472m543s472m543s472m543s472m543s1623m543s1623m543s472m543s1623" - "m543s472m543s472m543s472m543s472m543s472m543s472m543s1623m543s1623" - "m543s1623m543s1623m543s1623m543s1623m543s1623m543s1623m543s472m543s472" - "m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s1623" - "m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s472" - "m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s472" - "m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s472" - "m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s472" - "m543s7048", + "m580s1600m580s1600m580s1600m580s1600m580s490m580s490m580s1600m580s490" + "m580s490m580s490m580s490m580s490m580s1600m580s1600m580s490m580s1600" + "m580s490m580s490m580s490m580s490m580s490m580s490m580s1600m580s1600" + "m580s1600m580s1600m580s1600m580s1600m580s1600m580s1600m580s490m580s490" + "m580s490m580s490m580s490m580s490m580s490m580s490m580s490m580s1600" + "m580s490m580s490m580s490m580s490m580s490m580s490m580s490m580s490" + "m580s490m580s490m580s490m580s490m580s490m580s490m580s490m580s490" + "m580s490m580s490m580s490m580s490m580s490m580s490m580s490m580s490" + "m580s490m580s490m580s490m580s490m580s490m580s490m580s490m580s490" + "m580s7400", irsend.outputStr()); irsend.reset(); @@ -75,242 +75,200 @@ TEST(TestSendToshibaAC, SendWithRepeats) { EXPECT_EQ( "f38000d50" "m4400s4300" - "m543s1623m543s1623m543s1623m543s1623m543s472m543s472m543s1623m543s472" - "m543s472m543s472m543s472m543s472m543s1623m543s1623m543s472m543s1623" - "m543s472m543s472m543s472m543s472m543s472m543s472m543s1623m543s1623" - "m543s1623m543s1623m543s1623m543s1623m543s1623m543s1623m543s472m543s472" - "m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s1623" - "m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s472" - "m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s472" - "m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s472" - "m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s472" - "m543s7048" + "m580s1600m580s1600m580s1600m580s1600m580s490m580s490m580s1600m580s490" + "m580s490m580s490m580s490m580s490m580s1600m580s1600m580s490m580s1600" + "m580s490m580s490m580s490m580s490m580s490m580s490m580s1600m580s1600" + "m580s1600m580s1600m580s1600m580s1600m580s1600m580s1600m580s490m580s490" + "m580s490m580s490m580s490m580s490m580s490m580s490m580s490m580s1600" + "m580s490m580s490m580s490m580s490m580s490m580s490m580s490m580s490" + "m580s490m580s490m580s490m580s490m580s490m580s490m580s490m580s490" + "m580s490m580s490m580s490m580s490m580s490m580s490m580s490m580s490" + "m580s490m580s490m580s490m580s490m580s490m580s490m580s490m580s490" + "m580s7400" "m4400s4300" - "m543s1623m543s1623m543s1623m543s1623m543s472m543s472m543s1623m543s472" - "m543s472m543s472m543s472m543s472m543s1623m543s1623m543s472m543s1623" - "m543s472m543s472m543s472m543s472m543s472m543s472m543s1623m543s1623" - "m543s1623m543s1623m543s1623m543s1623m543s1623m543s1623m543s472m543s472" - "m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s1623" - "m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s472" - "m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s472" - "m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s472" - "m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s472" - "m543s7048" + "m580s1600m580s1600m580s1600m580s1600m580s490m580s490m580s1600m580s490" + "m580s490m580s490m580s490m580s490m580s1600m580s1600m580s490m580s1600" + "m580s490m580s490m580s490m580s490m580s490m580s490m580s1600m580s1600" + "m580s1600m580s1600m580s1600m580s1600m580s1600m580s1600m580s490m580s490" + "m580s490m580s490m580s490m580s490m580s490m580s490m580s490m580s1600" + "m580s490m580s490m580s490m580s490m580s490m580s490m580s490m580s490" + "m580s490m580s490m580s490m580s490m580s490m580s490m580s490m580s490" + "m580s490m580s490m580s490m580s490m580s490m580s490m580s490m580s490" + "m580s490m580s490m580s490m580s490m580s490m580s490m580s490m580s490" + "m580s7400" "m4400s4300" - "m543s1623m543s1623m543s1623m543s1623m543s472m543s472m543s1623m543s472" - "m543s472m543s472m543s472m543s472m543s1623m543s1623m543s472m543s1623" - "m543s472m543s472m543s472m543s472m543s472m543s472m543s1623m543s1623" - "m543s1623m543s1623m543s1623m543s1623m543s1623m543s1623m543s472m543s472" - "m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s1623" - "m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s472" - "m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s472" - "m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s472" - "m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s472" - "m543s7048", - irsend.outputStr()); -} - -// Test sending atypical sizes. -TEST(TestSendToshibaAC, SendUnexpectedSizes) { - IRsendTest irsend(4); - irsend.begin(); - - uint8_t toshiba_short_code[8] = {0x01, 0x02, 0x03, 0x04, - 0x05, 0x06, 0x07, 0x08}; - uint8_t toshiba_long_code[10] = {0x01, 0x02, 0x03, 0x04, 0x05, - 0x06, 0x07, 0x08, 0x09, 0x0A}; - irsend.reset(); - irsend.sendToshibaAC(toshiba_short_code, kToshibaACStateLength - 1); - ASSERT_EQ("", irsend.outputStr()); - - irsend.reset(); - irsend.sendToshibaAC(toshiba_long_code, kToshibaACStateLength + 1); - ASSERT_EQ( - "f38000d50" - "m4400s4300" - "m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s1623" - "m543s472m543s472m543s472m543s472m543s472m543s472m543s1623m543s472" - "m543s472m543s472m543s472m543s472m543s472m543s472m543s1623m543s1623" - "m543s472m543s472m543s472m543s472m543s472m543s1623m543s472m543s472" - "m543s472m543s472m543s472m543s472m543s472m543s1623m543s472m543s1623" - "m543s472m543s472m543s472m543s472m543s472m543s1623m543s1623m543s472" - "m543s472m543s472m543s472m543s472m543s472m543s1623m543s1623m543s1623" - "m543s472m543s472m543s472m543s472m543s1623m543s472m543s472m543s472" - "m543s472m543s472m543s472m543s472m543s1623m543s472m543s472m543s1623" - "m543s472m543s472m543s472m543s472m543s1623m543s472m543s1623m543s472" - "m543s7048" - "m4400s4300" - "m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s1623" - "m543s472m543s472m543s472m543s472m543s472m543s472m543s1623m543s472" - "m543s472m543s472m543s472m543s472m543s472m543s472m543s1623m543s1623" - "m543s472m543s472m543s472m543s472m543s472m543s1623m543s472m543s472" - "m543s472m543s472m543s472m543s472m543s472m543s1623m543s472m543s1623" - "m543s472m543s472m543s472m543s472m543s472m543s1623m543s1623m543s472" - "m543s472m543s472m543s472m543s472m543s472m543s1623m543s1623m543s1623" - "m543s472m543s472m543s472m543s472m543s1623m543s472m543s472m543s472" - "m543s472m543s472m543s472m543s472m543s1623m543s472m543s472m543s1623" - "m543s472m543s472m543s472m543s472m543s1623m543s472m543s1623m543s472" - "m543s7048", + "m580s1600m580s1600m580s1600m580s1600m580s490m580s490m580s1600m580s490" + "m580s490m580s490m580s490m580s490m580s1600m580s1600m580s490m580s1600" + "m580s490m580s490m580s490m580s490m580s490m580s490m580s1600m580s1600" + "m580s1600m580s1600m580s1600m580s1600m580s1600m580s1600m580s490m580s490" + "m580s490m580s490m580s490m580s490m580s490m580s490m580s490m580s1600" + "m580s490m580s490m580s490m580s490m580s490m580s490m580s490m580s490" + "m580s490m580s490m580s490m580s490m580s490m580s490m580s490m580s490" + "m580s490m580s490m580s490m580s490m580s490m580s490m580s490m580s490" + "m580s490m580s490m580s490m580s490m580s490m580s490m580s490m580s490" + "m580s7400", irsend.outputStr()); } // Tests for IRToshibaAC class. TEST(TestToshibaACClass, Power) { - IRToshibaAC toshiba(0); - toshiba.begin(); + IRToshibaAC ac(kGpioUnused); + ac.begin(); - toshiba.on(); - EXPECT_TRUE(toshiba.getPower()); + ac.on(); + EXPECT_TRUE(ac.getPower()); - toshiba.off(); - EXPECT_FALSE(toshiba.getPower()); + ac.off(); + EXPECT_FALSE(ac.getPower()); - toshiba.setPower(true); - EXPECT_TRUE(toshiba.getPower()); + ac.setPower(true); + EXPECT_TRUE(ac.getPower()); - toshiba.setPower(false); - EXPECT_FALSE(toshiba.getPower()); + ac.setPower(false); + EXPECT_FALSE(ac.getPower()); } TEST(TestToshibaACClass, Temperature) { - IRToshibaAC toshiba(0); - toshiba.begin(); + IRToshibaAC ac(kGpioUnused); + ac.begin(); - toshiba.setTemp(0); - EXPECT_EQ(kToshibaAcMinTemp, toshiba.getTemp()); + ac.setTemp(0); + EXPECT_EQ(kToshibaAcMinTemp, ac.getTemp()); - toshiba.setTemp(255); - EXPECT_EQ(kToshibaAcMaxTemp, toshiba.getTemp()); + ac.setTemp(255); + EXPECT_EQ(kToshibaAcMaxTemp, ac.getTemp()); - toshiba.setTemp(kToshibaAcMinTemp); - EXPECT_EQ(kToshibaAcMinTemp, toshiba.getTemp()); + ac.setTemp(kToshibaAcMinTemp); + EXPECT_EQ(kToshibaAcMinTemp, ac.getTemp()); - toshiba.setTemp(kToshibaAcMaxTemp); - EXPECT_EQ(kToshibaAcMaxTemp, toshiba.getTemp()); + ac.setTemp(kToshibaAcMaxTemp); + EXPECT_EQ(kToshibaAcMaxTemp, ac.getTemp()); - toshiba.setTemp(kToshibaAcMinTemp - 1); - EXPECT_EQ(kToshibaAcMinTemp, toshiba.getTemp()); + ac.setTemp(kToshibaAcMinTemp - 1); + EXPECT_EQ(kToshibaAcMinTemp, ac.getTemp()); - toshiba.setTemp(kToshibaAcMaxTemp + 1); - EXPECT_EQ(kToshibaAcMaxTemp, toshiba.getTemp()); + ac.setTemp(kToshibaAcMaxTemp + 1); + EXPECT_EQ(kToshibaAcMaxTemp, ac.getTemp()); - toshiba.setTemp(17); - EXPECT_EQ(17, toshiba.getTemp()); + ac.setTemp(17); + EXPECT_EQ(17, ac.getTemp()); - toshiba.setTemp(21); - EXPECT_EQ(21, toshiba.getTemp()); + ac.setTemp(21); + EXPECT_EQ(21, ac.getTemp()); - toshiba.setTemp(25); - EXPECT_EQ(25, toshiba.getTemp()); + ac.setTemp(25); + EXPECT_EQ(25, ac.getTemp()); - toshiba.setTemp(30); - EXPECT_EQ(30, toshiba.getTemp()); + ac.setTemp(30); + EXPECT_EQ(30, ac.getTemp()); } TEST(TestToshibaACClass, OperatingMode) { - IRToshibaAC toshiba(0); - toshiba.begin(); + IRToshibaAC ac(kGpioUnused); + ac.begin(); - toshiba.setMode(kToshibaAcAuto); - EXPECT_EQ(kToshibaAcAuto, toshiba.getMode()); + ac.setMode(kToshibaAcAuto); + EXPECT_EQ(kToshibaAcAuto, ac.getMode()); - toshiba.setMode(kToshibaAcCool); - EXPECT_EQ(kToshibaAcCool, toshiba.getMode()); + ac.setMode(kToshibaAcCool); + EXPECT_EQ(kToshibaAcCool, ac.getMode()); - toshiba.setMode(kToshibaAcHeat); - EXPECT_EQ(kToshibaAcHeat, toshiba.getMode()); + ac.setMode(kToshibaAcHeat); + EXPECT_EQ(kToshibaAcHeat, ac.getMode()); - toshiba.setMode(kToshibaAcDry); - EXPECT_EQ(kToshibaAcDry, toshiba.getMode()); + ac.setMode(kToshibaAcDry); + EXPECT_EQ(kToshibaAcDry, ac.getMode()); - toshiba.setMode(kToshibaAcHeat + 1); - EXPECT_EQ(kToshibaAcAuto, toshiba.getMode()); + ac.setMode(kToshibaAcFan); + EXPECT_EQ(kToshibaAcFan, ac.getMode()); - toshiba.setMode(255); - EXPECT_EQ(kToshibaAcAuto, toshiba.getMode()); + ac.setMode(kToshibaAcFan + 1); + EXPECT_EQ(kToshibaAcAuto, ac.getMode()); - // Setting the power off changes the underlying mode in the state to heat. - toshiba.setPower(true); - toshiba.setMode(kToshibaAcCool); - EXPECT_EQ(kToshibaAcCool, toshiba.getMode()); - EXPECT_EQ(kToshibaAcCool, toshiba.getMode(true)); - toshiba.setPower(false); - EXPECT_EQ(kToshibaAcCool, toshiba.getMode()); - EXPECT_EQ(kToshibaAcHeat, toshiba.getMode(true)); + ac.setMode(255); + EXPECT_EQ(kToshibaAcAuto, ac.getMode()); + + // Setting the power off changes the underlying mode in the state to a special + // off mode. + ac.setPower(true); + ac.setMode(kToshibaAcCool); + EXPECT_EQ(kToshibaAcCool, ac.getMode()); + ac.setPower(false); + EXPECT_EQ(kToshibaAcCool, ac.getMode()); } TEST(TestToshibaACClass, FanSpeed) { - IRToshibaAC toshiba(0); - toshiba.begin(); + IRToshibaAC ac(kGpioUnused); + ac.begin(); - toshiba.setFan(kToshibaAcFanAuto); - EXPECT_EQ(kToshibaAcFanAuto, toshiba.getFan()); + ac.setFan(kToshibaAcFanAuto); + EXPECT_EQ(kToshibaAcFanAuto, ac.getFan()); - toshiba.setFan(255); - EXPECT_EQ(kToshibaAcFanMax, toshiba.getFan()); + ac.setFan(255); + EXPECT_EQ(kToshibaAcFanMax, ac.getFan()); - toshiba.setFan(kToshibaAcFanMax); - EXPECT_EQ(kToshibaAcFanMax, toshiba.getFan()); + ac.setFan(kToshibaAcFanMax); + EXPECT_EQ(kToshibaAcFanMax, ac.getFan()); - toshiba.setFan(kToshibaAcFanMax - 1); - EXPECT_EQ(kToshibaAcFanMax - 1, toshiba.getFan()); + ac.setFan(kToshibaAcFanMax - 1); + EXPECT_EQ(kToshibaAcFanMax - 1, ac.getFan()); - toshiba.setFan(1); - EXPECT_EQ(1, toshiba.getFan()); + ac.setFan(1); + EXPECT_EQ(1, ac.getFan()); - toshiba.setFan(2); - EXPECT_EQ(2, toshiba.getFan()); + ac.setFan(2); + EXPECT_EQ(2, ac.getFan()); - toshiba.setFan(3); - EXPECT_EQ(3, toshiba.getFan()); + ac.setFan(3); + EXPECT_EQ(3, ac.getFan()); - toshiba.setFan(4); - EXPECT_EQ(4, toshiba.getFan()); + ac.setFan(4); + EXPECT_EQ(4, ac.getFan()); - toshiba.setFan(kToshibaAcFanMax + 1); - EXPECT_EQ(kToshibaAcFanMax, toshiba.getFan()); + ac.setFan(kToshibaAcFanMax + 1); + EXPECT_EQ(kToshibaAcFanMax, ac.getFan()); } TEST(TestToshibaACClass, RawState) { - IRToshibaAC toshiba(0); - toshiba.begin(); + IRToshibaAC ac(kGpioUnused); + ac.begin(); uint8_t initial_state[kToshibaACStateLength] = {0xF2, 0x0D, 0x03, 0xFC, 0x01, - 0x00, 0x00, 0x00, 0x01}; + 0x50, 0x00, 0x00, 0x51}; uint8_t modified_state[kToshibaACStateLength] = {0xF2, 0x0D, 0x03, 0xFC, 0x01, 0x00, 0xC1, 0x00, 0xC0}; // Verify the starting state. - EXPECT_STATE_EQ(initial_state, toshiba.getRaw(), kToshibaACBits); - EXPECT_TRUE(toshiba.getPower()); - EXPECT_EQ(kToshibaAcAuto, toshiba.getMode()); - EXPECT_EQ(kToshibaAcFanAuto, toshiba.getFan()); + EXPECT_STATE_EQ(initial_state, ac.getRaw(), kToshibaACBits); + EXPECT_TRUE(ac.getPower()); + EXPECT_EQ(kToshibaAcAuto, ac.getMode()); + EXPECT_EQ(kToshibaAcFanAuto, ac.getFan()); // Change some settings. - toshiba.setMode(kToshibaAcCool); - toshiba.setFan(kToshibaAcFanMax); - toshiba.setTemp(kToshibaAcMinTemp); + ac.setMode(kToshibaAcCool); + ac.setFan(kToshibaAcFanMax); + ac.setTemp(kToshibaAcMinTemp); // Verify those were set. - EXPECT_EQ(kToshibaAcCool, toshiba.getMode()); - EXPECT_EQ(kToshibaAcFanMax, toshiba.getFan()); - EXPECT_EQ(kToshibaAcMinTemp, toshiba.getTemp()); + EXPECT_EQ(kToshibaAcCool, ac.getMode()); + EXPECT_EQ(kToshibaAcFanMax, ac.getFan()); + EXPECT_EQ(kToshibaAcMinTemp, ac.getTemp()); // Retrieve the modified state. - EXPECT_STATE_EQ(modified_state, toshiba.getRaw(), kToshibaACBits); + EXPECT_STATE_EQ(modified_state, ac.getRaw(), kToshibaACBits); // Set it back to the initial state. - toshiba.setRaw(initial_state); + ac.setRaw(initial_state); // Check the new state was set correctly. - EXPECT_TRUE(toshiba.getPower()); - EXPECT_EQ(kToshibaAcAuto, toshiba.getMode()); - EXPECT_EQ(kToshibaAcFanAuto, toshiba.getFan()); - EXPECT_STATE_EQ(initial_state, toshiba.getRaw(), kToshibaACBits); + EXPECT_TRUE(ac.getPower()); + EXPECT_EQ(kToshibaAcAuto, ac.getMode()); + EXPECT_EQ(kToshibaAcFanAuto, ac.getFan()); + EXPECT_STATE_EQ(initial_state, ac.getRaw(), kToshibaACBits); } TEST(TestToshibaACClass, Checksums) { - IRToshibaAC toshiba(0); - toshiba.begin(); + IRToshibaAC ac(kGpioUnused); + ac.begin(); uint8_t initial_state[kToshibaACStateLength] = {0xF2, 0x0D, 0x03, 0xFC, 0x01, 0x00, 0x00, 0x00, 0x01}; @@ -319,8 +277,8 @@ TEST(TestToshibaACClass, Checksums) { uint8_t invalid_state[kToshibaACStateLength] = {0xF2, 0x0D, 0x03, 0xFC, 0x01, 0x00, 0x00, 0x00, 0x00}; - EXPECT_EQ(0x01, toshiba.calcChecksum(initial_state)); - EXPECT_EQ(0xC0, toshiba.calcChecksum(modified_state)); + EXPECT_EQ(0x01, ac.calcChecksum(initial_state)); + EXPECT_EQ(0xC0, ac.calcChecksum(modified_state)); // Check we can call it without instantiating the object. EXPECT_EQ(0x01, IRToshibaAC::calcChecksum(initial_state)); // Use different lengths. @@ -344,150 +302,69 @@ TEST(TestToshibaACClass, Checksums) { } TEST(TestToshibaACClass, HumanReadableOutput) { - IRToshibaAC toshiba(0); - toshiba.begin(); + IRToshibaAC ac(kGpioUnused); + ac.begin(); uint8_t initial_state[kToshibaACStateLength] = {0xF2, 0x0D, 0x03, 0xFC, 0x01, 0x00, 0x00, 0x00, 0x01}; uint8_t modified_state[kToshibaACStateLength] = {0xF2, 0x0D, 0x03, 0xFC, 0x01, 0x00, 0xC1, 0x00, 0xC0}; - toshiba.setRaw(initial_state); - EXPECT_EQ("Power: On, Mode: 0 (Auto), Temp: 17C, Fan: 0 (Auto)", - toshiba.toString()); - toshiba.setRaw(modified_state); - EXPECT_EQ("Power: On, Mode: 1 (Cool), Temp: 17C, Fan: 5 (High)", - toshiba.toString()); - toshiba.off(); - toshiba.setTemp(25); - toshiba.setFan(3); - toshiba.setMode(kToshibaAcDry); - EXPECT_EQ("Power: Off, Mode: 2 (Dry), Temp: 25C, Fan: 3 (Medium)", - toshiba.toString()); + ac.setRaw(initial_state); + EXPECT_EQ("Temp: 17C, Power: On, Mode: 0 (Auto), Fan: 0 (Auto), " + "Turbo: Off, Econo: Off", + ac.toString()); + ac.setRaw(modified_state); + EXPECT_EQ("Temp: 17C, Power: On, Mode: 1 (Cool), Fan: 5 (High), " + "Turbo: Off, Econo: Off", + ac.toString()); + ac.setTemp(25); + ac.setFan(3); + ac.setMode(kToshibaAcDry); + EXPECT_EQ("Temp: 25C, Power: On, Mode: 2 (Dry), Fan: 3 (Medium), " + "Turbo: Off, Econo: Off", + ac.toString()); + ac.off(); + EXPECT_EQ("Temp: 25C, Power: Off, Fan: 3 (Medium), Turbo: Off, Econo: Off", + ac.toString()); } TEST(TestToshibaACClass, MessageConstuction) { - IRToshibaAC toshiba(0); - IRsendTest irsend(4); - toshiba.begin(); - irsend.begin(); + IRToshibaAC ac(kGpioUnused); + ac.begin(); - toshiba.setFan(1); - toshiba.setMode(kToshibaAcCool); - toshiba.setTemp(27); - toshiba.on(); + ac.on(); + ac.setFan(1); + ac.setMode(kToshibaAcCool); + ac.setTemp(27); // Check everything for kicks. - EXPECT_EQ(1, toshiba.getFan()); - EXPECT_EQ(kToshibaAcCool, toshiba.getMode()); - EXPECT_EQ(27, toshiba.getTemp()); - EXPECT_TRUE(toshiba.getPower()); - - irsend.reset(); - irsend.sendToshibaAC(toshiba.getRaw()); - EXPECT_EQ( - "f38000d50" - "m4400s4300" - "m543s1623m543s1623m543s1623m543s1623m543s472m543s472m543s1623m543s472" - "m543s472m543s472m543s472m543s472m543s1623m543s1623m543s472m543s1623" - "m543s472m543s472m543s472m543s472m543s472m543s472m543s1623m543s1623" - "m543s1623m543s1623m543s1623m543s1623m543s1623m543s1623m543s472m543s472" - "m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s1623" - "m543s1623m543s472m543s1623m543s472m543s472m543s472m543s472m543s472" - "m543s472m543s1623m543s472m543s472m543s472m543s472m543s472m543s1623" - "m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s472" - "m543s1623m543s1623m543s1623m543s472m543s472m543s472m543s472m543s472" - "m543s7048" - "m4400s4300" - "m543s1623m543s1623m543s1623m543s1623m543s472m543s472m543s1623m543s472" - "m543s472m543s472m543s472m543s472m543s1623m543s1623m543s472m543s1623" - "m543s472m543s472m543s472m543s472m543s472m543s472m543s1623m543s1623" - "m543s1623m543s1623m543s1623m543s1623m543s1623m543s1623m543s472m543s472" - "m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s1623" - "m543s1623m543s472m543s1623m543s472m543s472m543s472m543s472m543s472" - "m543s472m543s1623m543s472m543s472m543s472m543s472m543s472m543s1623" - "m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s472" - "m543s1623m543s1623m543s1623m543s472m543s472m543s472m543s472m543s472" - "m543s7048", - irsend.outputStr()); + EXPECT_EQ(1, ac.getFan()); + EXPECT_EQ(kToshibaAcCool, ac.getMode()); + EXPECT_EQ(27, ac.getTemp()); + EXPECT_TRUE(ac.getPower()); // Turn off the power and re-check. - toshiba.setPower(false); + ac.setPower(false); // Check everything for kicks. - EXPECT_EQ(1, toshiba.getFan()); - EXPECT_EQ(kToshibaAcCool, toshiba.getMode()); - EXPECT_EQ(27, toshiba.getTemp()); - EXPECT_FALSE(toshiba.getPower()); - - irsend.reset(); - irsend.sendToshibaAC(toshiba.getRaw()); - EXPECT_EQ( - "f38000d50" - "m4400s4300" - "m543s1623m543s1623m543s1623m543s1623m543s472m543s472m543s1623m543s472" - "m543s472m543s472m543s472m543s472m543s1623m543s1623m543s472m543s1623" - "m543s472m543s472m543s472m543s472m543s472m543s472m543s1623m543s1623" - "m543s1623m543s1623m543s1623m543s1623m543s1623m543s1623m543s472m543s472" - "m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s1623" - "m543s1623m543s472m543s1623m543s472m543s472m543s472m543s472m543s472" - "m543s472m543s1623m543s472m543s472m543s472m543s1623m543s1623m543s1623" - "m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s472" - "m543s1623m543s1623m543s1623m543s472m543s472m543s1623m543s1623m543s472" - "m543s7048" - "m4400s4300" - "m543s1623m543s1623m543s1623m543s1623m543s472m543s472m543s1623m543s472" - "m543s472m543s472m543s472m543s472m543s1623m543s1623m543s472m543s1623" - "m543s472m543s472m543s472m543s472m543s472m543s472m543s1623m543s1623" - "m543s1623m543s1623m543s1623m543s1623m543s1623m543s1623m543s472m543s472" - "m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s1623" - "m543s1623m543s472m543s1623m543s472m543s472m543s472m543s472m543s472" - "m543s472m543s1623m543s472m543s472m543s472m543s1623m543s1623m543s1623" - "m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s472" - "m543s1623m543s1623m543s1623m543s472m543s472m543s1623m543s1623m543s472" - "m543s7048", - irsend.outputStr()); + EXPECT_EQ(1, ac.getFan()); + EXPECT_EQ(kToshibaAcCool, ac.getMode()); + EXPECT_EQ(27, ac.getTemp()); + EXPECT_FALSE(ac.getPower()); // Turn the power back on, and check nothing changed. - toshiba.on(); + ac.on(); - EXPECT_EQ(1, toshiba.getFan()); - EXPECT_EQ(kToshibaAcCool, toshiba.getMode()); - EXPECT_EQ(27, toshiba.getTemp()); - EXPECT_TRUE(toshiba.getPower()); - - irsend.reset(); - irsend.sendToshibaAC(toshiba.getRaw()); - EXPECT_EQ( - "f38000d50" - "m4400s4300" - "m543s1623m543s1623m543s1623m543s1623m543s472m543s472m543s1623m543s472" - "m543s472m543s472m543s472m543s472m543s1623m543s1623m543s472m543s1623" - "m543s472m543s472m543s472m543s472m543s472m543s472m543s1623m543s1623" - "m543s1623m543s1623m543s1623m543s1623m543s1623m543s1623m543s472m543s472" - "m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s1623" - "m543s1623m543s472m543s1623m543s472m543s472m543s472m543s472m543s472" - "m543s472m543s1623m543s472m543s472m543s472m543s472m543s472m543s1623" - "m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s472" - "m543s1623m543s1623m543s1623m543s472m543s472m543s472m543s472m543s472" - "m543s7048" - "m4400s4300" - "m543s1623m543s1623m543s1623m543s1623m543s472m543s472m543s1623m543s472" - "m543s472m543s472m543s472m543s472m543s1623m543s1623m543s472m543s1623" - "m543s472m543s472m543s472m543s472m543s472m543s472m543s1623m543s1623" - "m543s1623m543s1623m543s1623m543s1623m543s1623m543s1623m543s472m543s472" - "m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s1623" - "m543s1623m543s472m543s1623m543s472m543s472m543s472m543s472m543s472" - "m543s472m543s1623m543s472m543s472m543s472m543s472m543s472m543s1623" - "m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s472" - "m543s1623m543s1623m543s1623m543s472m543s472m543s472m543s472m543s472" - "m543s7048", - irsend.outputStr()); + EXPECT_EQ(1, ac.getFan()); + EXPECT_EQ(kToshibaAcCool, ac.getMode()); + EXPECT_EQ(27, ac.getTemp()); + EXPECT_TRUE(ac.getPower()); } // Decoding a message we entirely constructed based solely on a given state. TEST(TestDecodeToshibaAC, SyntheticExample) { - IRsendTest irsend(4); - IRrecv irrecv(4); + IRsendTest irsend(kGpioUnused); + IRrecv irrecv(kGpioUnused); irsend.begin(); uint8_t expectedState[kToshibaACStateLength] = {0xF2, 0x0D, 0x03, 0xFC, 0x01, @@ -501,7 +378,8 @@ TEST(TestDecodeToshibaAC, SyntheticExample) { ASSERT_EQ(kToshibaACBits, irsend.capture.bits); EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); EXPECT_EQ( - "Power: On, Mode: 0 (Auto), Temp: 17C, Fan: 0 (Auto)", + "Temp: 17C, Power: On, Mode: 0 (Auto), Fan: 0 (Auto), Turbo: Off, " + "Econo: Off", IRAcUtils::resultAcToString(&irsend.capture)); stdAc::state_t r, p; ASSERT_TRUE(IRAcUtils::decodeToState(&irsend.capture, &r, &p)); @@ -510,9 +388,9 @@ TEST(TestDecodeToshibaAC, SyntheticExample) { // Test decoding against captures from a real Toshiba A/C remote. // Recorded by @mwildbolz TEST(TestDecodeToshibaAC, RealExamples) { - IRToshibaAC toshiba(0); - IRsendTest irsend(4); - IRrecv irrecv(4); + IRToshibaAC ac(kGpioUnused); + IRsendTest irsend(kGpioUnused); + IRrecv irrecv(kGpioUnused); irsend.begin(); uint16_t rawData1[295] = { @@ -548,11 +426,11 @@ TEST(TestDecodeToshibaAC, RealExamples) { EXPECT_TRUE(irrecv.decode(&irsend.capture)); ASSERT_EQ(TOSHIBA_AC, irsend.capture.decode_type); ASSERT_EQ(kToshibaACBits, irsend.capture.bits); - toshiba.setRaw(irsend.capture.state); - EXPECT_TRUE(toshiba.getPower()); - EXPECT_EQ(23, toshiba.getTemp()); - EXPECT_EQ(kToshibaAcFanAuto, toshiba.getFan()); - EXPECT_EQ(kToshibaAcAuto, toshiba.getMode()); + ac.setRaw(irsend.capture.state); + EXPECT_TRUE(ac.getPower()); + EXPECT_EQ(23, ac.getTemp()); + EXPECT_EQ(kToshibaAcFanAuto, ac.getFan()); + EXPECT_EQ(kToshibaAcAuto, ac.getMode()); uint16_t rawData2[295] = { 4500, 4236, 636, 1520, 642, 1520, 640, 1520, 664, 1492, 642, 440, @@ -587,11 +465,11 @@ TEST(TestDecodeToshibaAC, RealExamples) { EXPECT_TRUE(irrecv.decode(&irsend.capture)); ASSERT_EQ(TOSHIBA_AC, irsend.capture.decode_type); ASSERT_EQ(kToshibaACBits, irsend.capture.bits); - toshiba.setRaw(irsend.capture.state); - EXPECT_TRUE(toshiba.getPower()); - EXPECT_EQ(17, toshiba.getTemp()); - EXPECT_EQ(3, toshiba.getFan()); - EXPECT_EQ(kToshibaAcCool, toshiba.getMode()); + ac.setRaw(irsend.capture.state); + EXPECT_TRUE(ac.getPower()); + EXPECT_EQ(17, ac.getTemp()); + EXPECT_EQ(3, ac.getFan()); + EXPECT_EQ(kToshibaAcCool, ac.getMode()); uint16_t rawData3[295] = { 4474, 4262, 642, 1514, 642, 1520, 642, 1520, 642, 1514, 642, 438, @@ -626,11 +504,11 @@ TEST(TestDecodeToshibaAC, RealExamples) { EXPECT_TRUE(irrecv.decode(&irsend.capture)); ASSERT_EQ(TOSHIBA_AC, irsend.capture.decode_type); ASSERT_EQ(kToshibaACBits, irsend.capture.bits); - toshiba.setRaw(irsend.capture.state); - EXPECT_TRUE(toshiba.getPower()); - EXPECT_EQ(24, toshiba.getTemp()); - EXPECT_EQ(kToshibaAcFanMax, toshiba.getFan()); - EXPECT_EQ(kToshibaAcHeat, toshiba.getMode()); + ac.setRaw(irsend.capture.state); + EXPECT_TRUE(ac.getPower()); + EXPECT_EQ(24, ac.getTemp()); + EXPECT_EQ(kToshibaAcFanMax, ac.getFan()); + EXPECT_EQ(kToshibaAcHeat, ac.getMode()); uint16_t rawData4[295] = { 4474, 4262, 636, 1520, 640, 1520, 640, 1520, 638, 1518, 642, 438, @@ -665,16 +543,10 @@ TEST(TestDecodeToshibaAC, RealExamples) { EXPECT_TRUE(irrecv.decode(&irsend.capture)); ASSERT_EQ(TOSHIBA_AC, irsend.capture.decode_type); ASSERT_EQ(kToshibaACBits, irsend.capture.bits); - toshiba.setRaw(irsend.capture.state); - EXPECT_FALSE(toshiba.getPower()); - EXPECT_EQ(22, toshiba.getTemp()); - EXPECT_EQ(4, toshiba.getFan()); - - // Confirming the quirky behaviour that the 'Power OFF' signal - // sets the mode to heat. - // The previous state the remote was in was 'AUTO' just prior to - // sending the power off message. - EXPECT_EQ(kToshibaAcHeat, toshiba.getMode()); + ac.setRaw(irsend.capture.state); + EXPECT_FALSE(ac.getPower()); + EXPECT_EQ(22, ac.getTemp()); + EXPECT_EQ(4, ac.getFan()); } TEST(TestToshibaACClass, toCommon) { @@ -704,3 +576,166 @@ TEST(TestToshibaACClass, toCommon) { ASSERT_EQ(-1, ac.toCommon().sleep); ASSERT_EQ(-1, ac.toCommon().clock); } + +// Tests for CarrierAc2 +/// Decode a "real" long example message. +TEST(TestDecodeToshibaAC, RealLongExample) { + IRsendTest irsend(kGpioUnused); + IRrecv irrecv(kGpioUnused); + irsend.begin(); + + irsend.reset(); + // Data from: + // https://github.com/crankyoldgit/IRremoteESP8266/issues/1205#issuecomment-650475434 + const uint16_t high_power_on[327] = { + 4424, 4320, + 582, 1574, 588, 1578, 582, 1574, 586, 1578, 586, 496, 582, 492, 586, 1576, + 586, 492, 586, 492, 588, 496, 584, 496, 584, 496, 584, 1626, 534, 1626, + 534, 494, 586, 1578, 582, 494, 586, 494, 586, 494, 588, 492, 586, 492, + 586, 1576, 586, 494, 588, 492, 588, 1574, 588, 1576, 584, 1578, 584, 1574, + 588, 1574, 588, 492, 588, 1572, 590, 1570, 590, 492, 588, 492, 590, 488, + 590, 494, 584, 1570, 592, 492, 586, 490, 590, 1572, 590, 490, 590, 1570, + 590, 490, 590, 1570, 590, 492, 588, 490, 588, 492, 588, 492, 590, 490, + 590, 494, 586, 490, 590, 490, 588, 490, 590, 490, 588, 492, 590, 490, + 588, 492, 590, 490, 590, 490, 590, 494, 584, 490, 590, 490, 590, 490, + 590, 490, 588, 490, 588, 492, 588, 492, 586, 492, 588, 490, 588, 492, + 588, 490, 590, 1572, 588, 494, 586, 1574, 588, 492, 588, 1572, 590, 1572, + 588, 492, 588, 492, 586, 494, 588, + 7422, + 4424, 4320, + 586, 1572, 588, 1572, 588, 1576, 584, 1574, 588, 494, 586, 492, 588, 1572, + 588, 492, 588, 492, 588, 492, 588, 494, 586, 496, 584, 1574, 586, 1578, + 582, 494, 586, 1578, 584, 494, 586, 492, 588, 492, 586, 496, 584, 494, + 586, 1578, 584, 494, 586, 494, 584, 1574, 588, 1572, 586, 1574, 588, 1574, + 588, 1572, 588, 494, 590, 1572, 588, 1574, 588, 492, 588, 492, 588, 492, + 586, 492, 588, 1572, 588, 498, 582, 492, 588, 1576, 586, 492, 588, 1572, + 588, 494, 588, 1572, 588, 492, 586, 492, 588, 492, 590, 490, 588, 492, + 586, 492, 588, 492, 590, 490, 588, 490, 588, 492, 590, 490, 588, 492, + 590, 490, 588, 490, 590, 490, 590, 490, 592, 488, 592, 494, 584, 494, + 586, 490, 590, 494, 586, 494, 588, 488, 592, 490, 588, 492, 586, 490, + 592, 490, 588, 1576, 584, 494, 586, 1570, 590, 494, 586, 1576, 582, 1572, + 590, 490, 590, 490, 588, 490, 590}; // UNKNOWN 54926187 + + const uint8_t expectedState[kToshibaACStateLengthLong] = { + 0xF2, 0x0D, 0x04, 0xFB, 0x09, 0x50, 0x00, 0x00, 0x01, 0x58}; + irsend.sendRaw(high_power_on, 327, 38000); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(decode_type_t::TOSHIBA_AC, irsend.capture.decode_type); + EXPECT_EQ(kToshibaACBitsLong, irsend.capture.bits); + EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); + EXPECT_EQ( + "Temp: 22C, Power: On, Mode: 0 (Auto), Fan: 0 (Auto), Turbo: On, " + "Econo: Off", + IRAcUtils::resultAcToString(&irsend.capture)); +} + + +/// Decode a synthetic long message. +TEST(TestDecodeToshibaAC, SyntheticLongExample) { + IRsendTest irsend(0); + IRrecv irrecv(0); + + const uint8_t expectedState[kToshibaACStateLengthLong] = { + 0xF2, 0x0D, 0x04, 0xFB, 0x09, 0x50, 0x00, 0x00, 0x01, 0x58}; + irsend.begin(); + irsend.reset(); + irsend.sendToshibaAC(expectedState, kToshibaACStateLengthLong); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(decode_type_t::TOSHIBA_AC, irsend.capture.decode_type); + EXPECT_EQ(kToshibaACBitsLong, irsend.capture.bits); + EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); + EXPECT_EQ( + "f38000d50" + // 4424 4320 + "m4400s4300" + // 582 1574 588 1578 582 1574 586 1578 586 496 582 492 586 1576 586 492 + "m580s1600m580s1600m580s1600m580s1600m580s490m580s490m580s1600m580s490" + // 586 492 588 496 584 496 584 496 584 1626 534 1626 534 494 586 1578 + "m580s490m580s490m580s490m580s490m580s1600m580s1600m580s490m580s1600" + // 582 494 586 494 586 494 588 492 586 492 586 1576 586 494 588 492 + "m580s490m580s490m580s490m580s490m580s490m580s1600m580s490m580s490" + // 588 1574 588 1576 584 1578 584 1574 588 1574 588 492 588 1572 590 1570 + "m580s1600m580s1600m580s1600m580s1600m580s1600m580s490m580s1600m580s1600" + // 590 492 588 492 590 488 590 494 584 1570 592 492 586 490 590 1572 + "m580s490m580s490m580s490m580s490m580s1600m580s490m580s490m580s1600" + // 590 490 590 1570 590 490 590 1570 590 492 588 490 588 492 588 492 + "m580s490m580s1600m580s490m580s1600m580s490m580s490m580s490m580s490" + // 590 490 590 494 586 490 590 490 588 490 590 490 588 492 590 490 + "m580s490m580s490m580s490m580s490m580s490m580s490m580s490m580s490" + // 588 492 590 490 590 490 590 494 584 490 590 490 590 490 590 490 + "m580s490m580s490m580s490m580s490m580s490m580s490m580s490m580s490" + // 588 490 588 492 588 492 586 492 588 490 588 492 588 490 590 1572 + "m580s490m580s490m580s490m580s490m580s490m580s490m580s490m580s1600" + // 588 494 586 1574 588 492 588 1572 590 1572 588 492 588 492 586 494 + "m580s490m580s1600m580s490m580s1600m580s1600m580s490m580s490m580s490" + // 588 7422 + "m580s7400" + "m4400s4300" + "m580s1600m580s1600m580s1600m580s1600m580s490m580s490m580s1600m580s490" + "m580s490m580s490m580s490m580s490m580s1600m580s1600m580s490m580s1600" + "m580s490m580s490m580s490m580s490m580s490m580s1600m580s490m580s490" + "m580s1600m580s1600m580s1600m580s1600m580s1600m580s490m580s1600m580s1600" + "m580s490m580s490m580s490m580s490m580s1600m580s490m580s490m580s1600" + "m580s490m580s1600m580s490m580s1600m580s490m580s490m580s490m580s490" + "m580s490m580s490m580s490m580s490m580s490m580s490m580s490m580s490" + "m580s490m580s490m580s490m580s490m580s490m580s490m580s490m580s490" + "m580s490m580s490m580s490m580s490m580s490m580s490m580s490m580s1600" + "m580s490m580s1600m580s490m580s1600m580s1600m580s490m580s490m580s490" + "m580s7400", + irsend.outputStr()); +} + +/// Decode a "real" short example message. +TEST(TestDecodeToshibaAC, RealShortExample) { + IRsendTest irsend(kGpioUnused); + IRrecv irrecv(kGpioUnused); + irsend.begin(); + + irsend.reset(); + // Data from: + // https://github.com/crankyoldgit/IRremoteESP8266/issues/1205#issuecomment-650475096 + const uint16_t air_direction[115] = { + 4424, 4318, + 588, 1574, 588, 1572, 588, 1574, 586, 1572, 590, 490, 586, 494, 586, 1574, + 588, 492, 586, 494, 586, 494, 586, 496, 584, 492, 588, 1572, 588, 1572, + 590, 492, 588, 1572, 590, 490, 588, 492, 586, 494, 588, 492, 588, 492, + 588, 494, 584, 492, 588, 1572, 588, 1574, 588, 1574, 588, 1572, 588, 1572, + 588, 1572, 588, 1574, 590, 1570, 588, 494, 586, 496, 584, 494, 588, 1572, + 588, 492, 588, 492, 588, 490, 588, 492, 590, 1572, 588, 492, 588, 496, + 586, 492, 588, 492, 586, 492, 588, 492, 588, 490, 590, 490, 588, 492, + 588, 490, 588, 1572, 588, 492, 588, 492, 588, 490, 588, 492, 588, 1572, + 586}; // UNKNOWN DEB8845C + const uint8_t expectedState[kToshibaACStateLengthShort] = { + 0xF2, 0x0D, 0x01, 0xFE, 0x21, 0x00, 0x21}; + irsend.sendRaw(air_direction, 115, 38000); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(decode_type_t::TOSHIBA_AC, irsend.capture.decode_type); + EXPECT_EQ(kToshibaACBitsShort, irsend.capture.bits); + EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); + EXPECT_EQ( + "Temp: 17C, Swing(V): 0 (Step)", + IRAcUtils::resultAcToString(&irsend.capture)); +} + +TEST(TestToshibaACClass, ConstructLongState) { + IRToshibaAC ac(kGpioUnused); + ac.setPower(true); + ac.setMode(kToshibaAcDry); + ac.setTemp(29); + ac.setFan(2); + ac.setSwing(false); + ac.setTurbo(false); + ac.setEcono(true); + EXPECT_EQ( + "Temp: 29C, Power: On, Mode: 2 (Dry), Fan: 2 (UNKNOWN), " + "Turbo: Off, Econo: On", + ac.toString()); + EXPECT_EQ(kToshibaACStateLengthLong, ac.getStateLength()); + const uint8_t expectedState[kToshibaACStateLengthLong] = { + 0xF2, 0x0D, 0x04, 0xFB, 0x09, 0xC0, 0x62, 0x00, 0x03, 0xA8}; + EXPECT_STATE_EQ(expectedState, ac.getRaw(), kToshibaACBitsLong); + EXPECT_EQ(kToshibaACStateLengthLong, ac.getStateLength()); +} diff --git a/lib/IRremoteESP8266-2.7.8/tools/RawToGlobalCache.sh b/lib/IRremoteESP8266-2.7.8/tools/RawToGlobalCache.sh old mode 100644 new mode 100755 diff --git a/lib/IRremoteESP8266-2.7.8/tools/auto_analyse_raw_data.py b/lib/IRremoteESP8266-2.7.8/tools/auto_analyse_raw_data.py old mode 100644 new mode 100755 diff --git a/lib/IRremoteESP8266-2.7.8/tools/auto_analyse_raw_data_test.py b/lib/IRremoteESP8266-2.7.8/tools/auto_analyse_raw_data_test.py old mode 100644 new mode 100755 diff --git a/lib/IRremoteESP8266-2.7.8/tools/generate_irtext_h.sh b/lib/IRremoteESP8266-2.7.8/tools/generate_irtext_h.sh old mode 100644 new mode 100755 diff --git a/lib/IRremoteESP8266-2.7.8/tools/mkkeywords b/lib/IRremoteESP8266-2.7.8/tools/mkkeywords old mode 100644 new mode 100755 diff --git a/lib/IRremoteESP8266-2.7.8/tools/scrape_supported_devices.py b/lib/IRremoteESP8266-2.7.8/tools/scrape_supported_devices.py old mode 100644 new mode 100755 diff --git a/tasmota/CHANGELOG.md b/tasmota/CHANGELOG.md index 6a9520fcc..0f13cfe48 100644 --- a/tasmota/CHANGELOG.md +++ b/tasmota/CHANGELOG.md @@ -13,6 +13,7 @@ - Add command ``SetOption101 0/1`` to add the Zigbee source endpoint as suffix to attributes, ex `Power3` instead of `Power` if sent from endpoint 3 - Add command (``S``)``SerialSend6`` \ (#8937) - Add support for Sonoff Zigbee Bridge as module 75 (#8583) +- Change IRRemoteESP8266 IR lib to pre-2.7.9, fixing Samsung and Pioneer protocols (#8938) ### 8.3.1.6 20200617