From ad399c5f362f0f2581b648f502f94e0bccab8052 Mon Sep 17 00:00:00 2001 From: David Conran Date: Mon, 6 Jan 2020 09:53:52 +1000 Subject: [PATCH] Experimental detailed support for LG a/c. (#1009) * Initial detailed support for LG a/c. * Support LG2 in IRLgAc class. * Add different model support to handle LG & LG2 protocols. * Power, Mode, Temp, & Fan speed control. * Common a/c support. * Unit tests coverage. * Update supported devices list. * Add unit test to check raw LG2 messages work. * Ensure the Off command is always 0x88C0051 Fixes #1008 --- SupportedProtocols.md | 6 +- src/IRac.cpp | 71 ++++++ src/IRac.h | 22 +- src/IRremoteESP8266.h | 1 + src/IRsend.h | 5 + src/IRutils.cpp | 20 +- src/ir_LG.cpp | 261 +++++++++++++++++++ src/ir_LG.h | 95 ++++++- test/IRac_test.cpp | 25 ++ test/Makefile | 2 +- test/ir_LG_test.cpp | 400 +++++++++++++++++++++++++++++- tools/Makefile | 2 +- tools/scrape_supported_devices.py | 2 +- 13 files changed, 891 insertions(+), 21 deletions(-) diff --git a/SupportedProtocols.md b/SupportedProtocols.md index d51cab924..440fe8e20 100644 --- a/SupportedProtocols.md +++ b/SupportedProtocols.md @@ -1,6 +1,6 @@ + Last generated: Sun Jan 5 15:53:26 2020 ---> # IR Protocols supported by this library | Protocol | Brand | Model | A/C Model | Detailed A/C Support | @@ -31,7 +31,8 @@ | [JVC](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_JVC.cpp) | **Unknown** | | | - | | [Kelvinator](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Kelvinator.cpp) | **[Green](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Kelvinator.h)** | YAPOF3 remote | | Yes | | [Kelvinator](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Kelvinator.cpp) | **[Kelvinator](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Kelvinator.h)** | KSV26CRC A/C
KSV26HRC A/C
KSV35CRC A/C
KSV35HRC A/C
KSV53HRC A/C
KSV62HRC A/C
KSV70CRC A/C
KSV70HRC A/C
KSV80HRC A/C
YALIF Remote | | Yes | -| [LG](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_LG.cpp) | **[LG](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_LG.h)** | 6711A20083V remote
6711A20083V remote
AKB74395308 remote
AKB74395308 remote | | - | +| [LG](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_LG.cpp) | **[General Electric](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_LG.h)** | 6711AR2853M A/C Remote
AG1BH09AW101 Split A/C | | Yes | +| [LG](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_LG.cpp) | **[LG](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_LG.h)** | 6711A20083V remote
6711A20083V remote
AKB74395308 remote
AKB74395308 remote
AKB75215403 remote (LG2)
S4-W12JA3AA A/C (LG2) | | Yes | | [Lasertag](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Lasertag.cpp) | **Unknown** | | | - | | [Lego](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Lego.cpp) | **LEGO Power Functions** | IR Receiver | | - | | [Lutron](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Lutron.cpp) | **Unknown** | | | - | @@ -43,6 +44,7 @@ | [Mitsubishi](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Mitsubishi.cpp) | **[Mitsubishi](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Mitsubishi.h)** | HC3000 Projector
KM14A 0179213 remote
MS-GK24VA A/C
TV | | Yes | | [Mitsubishi](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Mitsubishi.cpp) | **[Mitsubishi Electric](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Mitsubishi.h)** | 001CP T7WE10714 remote
KPOA remote
MSH-A24WV / MUH-A24WV A/C
PEAD-RP71JAA Ducted A/C | | Yes | | [MitsubishiHeavy](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_MitsubishiHeavy.cpp) | **[Mitsubishi Heavy Industries](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_MitsubishiHeavy.h)** | RKX502A001C remote
RLA502A700B remote
SRKxxZJ-S A/C
SRKxxZM-S A/C
SRKxxZMXA-S A/C | | Yes | +| [NEC](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_NEC.cpp) | **[Aloka](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_NEC.h)** | SleepyLights LED Lamp | | - | | [NEC](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_NEC.cpp) | **[Yamaha](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_NEC.h)** | RAV561 remote
RXV585B A/V Receiver | | - | | [Neoclima](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Neoclima.cpp) | **[Neoclima](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Neoclima.h)** | NS-09AHTI A/C
NS-09AHTI A/C
ZH/TY-01 remote
ZH/TY-01 remote | | Yes | | [Nikai](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Nikai.cpp) | **Unknown** | | | - | diff --git a/src/IRac.cpp b/src/IRac.cpp index ee1ce9dd5..5cb56b974 100644 --- a/src/IRac.cpp +++ b/src/IRac.cpp @@ -25,6 +25,7 @@ #include "ir_Haier.h" #include "ir_Hitachi.h" #include "ir_Kelvinator.h" +#include "ir_LG.h" #include "ir_Midea.h" #include "ir_Mitsubishi.h" #include "ir_MitsubishiHeavy.h" @@ -149,6 +150,10 @@ bool IRac::isProtocolSupported(const decode_type_t protocol) { #if SEND_KELVINATOR case decode_type_t::KELVINATOR: #endif +#if SEND_LG + case decode_type_t::LG: + case decode_type_t::LG2: +#endif #if SEND_MIDEA case decode_type_t::MIDEA: #endif @@ -719,6 +724,30 @@ void IRac::kelvinator(IRKelvinatorAC *ac, } #endif // SEND_KELVINATOR +#if SEND_LG +void IRac::lg(IRLgAc *ac, const lg_ac_remote_model_t model, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan) { + ac->begin(); + ac->setModel(model); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + // No Vertical swing setting available. + // No Horizontal swing setting available. + // No Quiet setting available. + // No Turbo setting available. + // No Light setting available. + // No Filter setting available. + // No Clean setting available. + // No Beep setting available. + // No Sleep setting available. + // No Clock setting available. + ac->send(); +} +#endif // SEND_LG + #if SEND_MIDEA void IRac::midea(IRMideaAC *ac, const bool on, const stdAc::opmode_t mode, const bool celsius, @@ -1439,6 +1468,16 @@ bool IRac::sendAc(const stdAc::state_t desired, const stdAc::state_t *prev) { break; } #endif // SEND_KELVINATOR +#if SEND_LG + case LG: + case LG2: + { + IRLgAc ac(_pin, _inverted, _modulation); + lg(&ac, (lg_ac_remote_model_t)send.model, send.power, send.mode, + send.degrees, send.fanspeed); + break; + } +#endif // SEND_LG #if SEND_MIDEA case MIDEA: { @@ -2139,6 +2178,21 @@ namespace IRAcUtils { return ac.toString(); } #endif // DECODE_TCL112AC +#if DECODE_LG + case decode_type_t::LG: + case decode_type_t::LG2: { + IRLgAc ac(0); + ac.setRaw(result->value); // Like Coolix, use value instead of state. + switch (result->decode_type) { + case decode_type_t::LG2: + ac.setModel(lg_ac_remote_model_t::AKB75215403); + break; + default: + ac.setModel(lg_ac_remote_model_t::GE6711AR2853M); + } + return ac.isValidLgAc() ? ac.toString() : ""; + } +#endif // DECODE_LG default: return ""; } @@ -2309,6 +2363,23 @@ namespace IRAcUtils { break; } #endif // DECODE_KELVINATOR +#if DECODE_LG + case decode_type_t::LG: + case decode_type_t::LG2: { + IRLgAc ac(kGpioUnused); + ac.setRaw(decode->value); // Uses value instead of state. + if (!ac.isValidLgAc()) return false; + switch (decode->decode_type) { + case decode_type_t::LG2: + ac.setModel(lg_ac_remote_model_t::AKB75215403); + break; + default: + ac.setModel(lg_ac_remote_model_t::GE6711AR2853M); + } + *result = ac.toCommon(); + break; + } +#endif // DECODE_LG #if DECODE_MIDEA case decode_type_t::MIDEA: { IRMideaAC ac(kGpioUnused); diff --git a/src/IRac.h b/src/IRac.h index 1ee931986..0f49943e4 100644 --- a/src/IRac.h +++ b/src/IRac.h @@ -18,6 +18,7 @@ #include "ir_Haier.h" #include "ir_Hitachi.h" #include "ir_Kelvinator.h" +#include "ir_LG.h" #include "ir_Midea.h" #include "ir_Mitsubishi.h" #include "ir_MitsubishiHeavy.h" @@ -136,16 +137,16 @@ class IRac { const bool quiet, const bool turbo, const bool econo); #endif // SEND_DAIKIN152 #if SEND_DAIKIN160 -void daikin160(IRDaikin160 *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv); + void daikin160(IRDaikin160 *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv); #endif // SEND_DAIKIN160 #if SEND_DAIKIN176 -void daikin176(IRDaikin176 *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingh_t swingh); + void daikin176(IRDaikin176 *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingh_t swingh); #endif // SEND_DAIKIN176 #if SEND_DAIKIN2 void daikin2(IRDaikin2 *ac, @@ -230,6 +231,11 @@ void electra(IRElectraAc *ac, const bool quiet, const bool turbo, const bool light, const bool filter, const bool clean); #endif // SEND_KELVINATOR +#if SEND_LG + void lg(IRLgAc *ac, const lg_ac_remote_model_t model, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan); +#endif // SEND_LG #if SEND_MIDEA void midea(IRMideaAC *ac, const bool on, const stdAc::opmode_t mode, const bool celsius, diff --git a/src/IRremoteESP8266.h b/src/IRremoteESP8266.h index 1cc5f3c19..ac6d5a14f 100644 --- a/src/IRremoteESP8266.h +++ b/src/IRremoteESP8266.h @@ -463,6 +463,7 @@ const uint16_t kLegoPfBits = 16; const uint16_t kLegoPfMinRepeat = kNoRepeat; const uint16_t kLgBits = 28; const uint16_t kLg32Bits = 32; +const uint16_t kLgDefaultRepeat = kNoRepeat; const uint16_t kLutronBits = 35; const uint16_t kMagiquestBits = 56; const uint16_t kMideaBits = 48; diff --git a/src/IRsend.h b/src/IRsend.h index b0956e77c..e86f4e7de 100644 --- a/src/IRsend.h +++ b/src/IRsend.h @@ -141,6 +141,11 @@ enum whirlpool_ac_remote_model_t { DG11J191, }; +enum lg_ac_remote_model_t { + GE6711AR2853M = 1, // (1) LG 28-bit Protocol (default) + AKB75215403, // (2) LG2 28-bit Protocol +}; + // Classes class IRsend { diff --git a/src/IRutils.cpp b/src/IRutils.cpp index 0aa19e821..e785f625d 100644 --- a/src/IRutils.cpp +++ b/src/IRutils.cpp @@ -824,6 +824,16 @@ namespace irutils { String modelToStr(const decode_type_t protocol, const int16_t model) { switch (protocol) { + case decode_type_t::FUJITSU_AC: + switch (model) { + case fujitsu_ac_remote_model_t::ARRAH2E: return F("ARRAH2E"); + case fujitsu_ac_remote_model_t::ARDB1: return F("ARDB1"); + case fujitsu_ac_remote_model_t::ARREB1E: return F("ARREB1E"); + case fujitsu_ac_remote_model_t::ARJW2: return F("ARJW2"); + case fujitsu_ac_remote_model_t::ARRY4: return F("ARRY4"); + default: return kUnknownStr; + } + break; case decode_type_t::GREE: switch (model) { case gree_ac_remote_model_t::YAW1F: return F("YAW1F"); @@ -831,13 +841,11 @@ namespace irutils { default: return kUnknownStr; } break; - case decode_type_t::FUJITSU_AC: + case decode_type_t::LG: + case decode_type_t::LG2: switch (model) { - case fujitsu_ac_remote_model_t::ARRAH2E: return F("ARRAH2E"); - case fujitsu_ac_remote_model_t::ARDB1: return F("ARDB1"); - case fujitsu_ac_remote_model_t::ARREB1E: return F("ARREB1E"); - case fujitsu_ac_remote_model_t::ARJW2: return F("ARJW2"); - case fujitsu_ac_remote_model_t::ARRY4: return F("ARRY4"); + case lg_ac_remote_model_t::GE6711AR2853M: return F("GE6711AR2853M"); + case lg_ac_remote_model_t::AKB75215403: return F("AKB75215403"); default: return kUnknownStr; } break; diff --git a/src/ir_LG.cpp b/src/ir_LG.cpp index 124256e9f..ce0a36d3d 100644 --- a/src/ir_LG.cpp +++ b/src/ir_LG.cpp @@ -8,10 +8,20 @@ #include "ir_LG.h" #include +#include "IRac.h" #include "IRrecv.h" #include "IRsend.h" +#include "IRtext.h" #include "IRutils.h" +using irutils::addBoolToString; +using irutils::addModeToString; +using irutils::addModelToString; +using irutils::addFanToString; +using irutils::addTempToString; +using irutils::setBit; +using irutils::setBits; + // LG decode originally added by Darryl Smith (based on the JVC protocol) // LG send originally added by https://github.com/chaeplin @@ -283,3 +293,254 @@ bool IRrecv::decodeLG(decode_results *results, uint16_t nbits, bool strict) { return true; } #endif + +// LG A/C Class +// Support for LG-type A/C units. +// Ref: +// https://github.com/arendst/Tasmota/blob/54c2eb283a02e4287640a4595e506bc6eadbd7f2/sonoff/xdrv_05_irremote.ino#L327-438 +IRLgAc::IRLgAc(const uint16_t pin, const bool inverted, + const bool use_modulation) + : _irsend(pin, inverted, use_modulation) { this->stateReset(); } + +void IRLgAc::stateReset(void) { + setRaw(kLgAcOffCommand); + setModel(lg_ac_remote_model_t::GE6711AR2853M); +} + +void IRLgAc::begin(void) { _irsend.begin(); } + +#if SEND_LG +void IRLgAc::send(const uint16_t repeat) { + if (this->getPower()) + _irsend.send(this->_protocol, this->getRaw(), kLgBits, repeat); + else + // Always send the special Off command if the power is set to off. + // Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/1008#issuecomment-570763580 + _irsend.send(this->_protocol, kLgAcOffCommand, kLgBits, repeat); +} +#endif // SEND_LG + +void IRLgAc::setModel(const lg_ac_remote_model_t model) { + switch (model) { + case lg_ac_remote_model_t::AKB75215403: + _protocol = decode_type_t::LG2; + break; + case lg_ac_remote_model_t::GE6711AR2853M: + // FALL THRU + default: + _protocol = decode_type_t::LG; + } +} + +lg_ac_remote_model_t IRLgAc::getModel(void) { + switch (_protocol) { + case LG2: + return lg_ac_remote_model_t::AKB75215403; + case LG: + // FALL THRU + default: + return lg_ac_remote_model_t::GE6711AR2853M; + } +} + +uint32_t IRLgAc::getRaw(void) { + checksum(); + return remote_state; +} + +void IRLgAc::setRaw(const uint32_t new_code) { + remote_state = new_code; + _temp = 15; // Ensure there is a "sane" previous temp. + _temp = getTemp(); +} + +// Calculate the checksum for a given state. +// Args: +// state: The value to calculate the checksum of. +// Returns: +// A uint8_t of the checksum. +uint8_t IRLgAc::calcChecksum(const uint32_t state) { + return calcLGChecksum(state >> 4); +} + +// Verify the checksum is valid for a given state. +// Args: +// state: The value to verify the checksum of. +// Returns: +// A boolean. +bool IRLgAc::validChecksum(const uint32_t state) { + return calcChecksum(state) == GETBITS32(state, kLgAcChecksumOffset, + kLgAcChecksumSize); +} + +void IRLgAc::checksum(void) { + setBits(&remote_state, kLgAcChecksumOffset, kLgAcChecksumSize, + calcChecksum(remote_state)); +} + +void IRLgAc::on(void) { setPower(true); } + +void IRLgAc::off(void) { setPower(false); } + +void IRLgAc::setPower(const bool on) { + setBits(&remote_state, kLgAcPowerOffset, kLgAcPowerSize, + on ? kLgAcPowerOn : kLgAcPowerOff); + if (on) + setTemp(_temp); // Reset the temp if we are on. + else + _setTemp(0); // Off clears the temp. +} + +bool IRLgAc::getPower(void) { + return GETBITS32(remote_state, kLgAcPowerOffset, kLgAcPowerSize) == + kLgAcPowerOn; +} + +// Set the temp. (Internal use only) +void IRLgAc::_setTemp(const uint8_t value) { + setBits(&remote_state, kLgAcTempOffset, kLgAcTempSize, value); +} + +// Set the temp. in deg C +void IRLgAc::setTemp(const uint8_t degrees) { + uint8_t temp = std::max(kLgAcMinTemp, degrees); + temp = std::min(kLgAcMaxTemp, temp); + _temp = temp; + _setTemp(temp - kLgAcTempAdjust); +} + +// Return the set temp. in deg C +uint8_t IRLgAc::getTemp(void) { + if (getPower()) + return GETBITS32(remote_state, kLgAcTempOffset, kLgAcTempSize) + + kLgAcTempAdjust; + else + return _temp; +} + +// Set the speed of the fan. +void IRLgAc::setFan(const uint8_t speed) { + switch (speed) { + case kLgAcFanAuto: + case kLgAcFanLow: + case kLgAcFanMedium: + case kLgAcFanHigh: + setBits(&remote_state, kLgAcFanOffset, kLgAcFanSize, speed); + break; + default: + setFan(kLgAcFanAuto); + } +} + +uint8_t IRLgAc::getFan(void) { + return GETBITS32(remote_state, kLgAcFanOffset, kLgAcFanSize); +} + +uint8_t IRLgAc::getMode(void) { + return GETBITS32(remote_state, kLgAcModeOffset, kLgAcModeSize); +} + +void IRLgAc::setMode(const uint8_t mode) { + switch (mode) { + case kLgAcAuto: + case kLgAcDry: + case kLgAcHeat: + case kLgAcCool: + case kLgAcFan: + setBits(&remote_state, kLgAcModeOffset, kLgAcModeSize, mode); + break; + default: // If we get an unexpected mode, default to AUTO. + this->setMode(kLgAcAuto); + } +} + +// Convert a standard A/C mode into its native mode. +uint8_t IRLgAc::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: return kLgAcCool; + case stdAc::opmode_t::kHeat: return kLgAcHeat; + case stdAc::opmode_t::kFan: return kLgAcFan; + case stdAc::opmode_t::kDry: return kLgAcDry; + default: return kLgAcAuto; + } +} + +// Convert a native mode to it's common equivalent. +stdAc::opmode_t IRLgAc::toCommonMode(const uint8_t mode) { + switch (mode) { + case kLgAcCool: return stdAc::opmode_t::kCool; + case kLgAcHeat: return stdAc::opmode_t::kHeat; + case kLgAcDry: return stdAc::opmode_t::kDry; + case kLgAcFan: return stdAc::opmode_t::kFan; + default: return stdAc::opmode_t::kAuto; + } +} + +// Convert a standard A/C Fan speed into its native fan speed. +uint8_t IRLgAc::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: + 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; + } +} + +// Convert a native fan speed to it's common equivalent. +stdAc::fanspeed_t IRLgAc::toCommonFanSpeed(const uint8_t speed) { + switch (speed) { + case kLgAcFanHigh: return stdAc::fanspeed_t::kMax; + case kLgAcFanMedium: return stdAc::fanspeed_t::kMedium; + case kLgAcFanLow: return stdAc::fanspeed_t::kLow; + default: return stdAc::fanspeed_t::kAuto; + } +} + +// Convert the A/C state to it's common equivalent. +stdAc::state_t IRLgAc::toCommon(void) { + stdAc::state_t result; + result.protocol = decode_type_t::LG; + result.model = this->getModel(); + result.power = this->getPower(); + result.mode = this->toCommonMode(this->getMode()); + result.celsius = true; + result.degrees = this->getTemp(); + result.fanspeed = this->toCommonFanSpeed(this->getFan()); + // Not supported. + result.swingv = stdAc::swingv_t::kOff; + result.swingh = stdAc::swingh_t::kOff; + result.quiet = false; + result.turbo = false; + result.light = false; + result.filter = false; + result.clean = false; + result.econo = false; + result.beep = false; + result.sleep = -1; + result.clock = -1; + return result; +} + +// Convert the internal state into a human readable string. +String IRLgAc::toString(void) { + String result = ""; + result.reserve(80); // Reserve some heap for the string to reduce fragging. + result += addModelToString(_protocol, getModel(), false); + result += addBoolToString(getPower(), kPowerStr); + if (getPower()) { // Only display the rest if is in power on state. + result += addModeToString(getMode(), kLgAcAuto, kLgAcCool, + kLgAcHeat, kLgAcDry, kLgAcFan); + result += addTempToString(getTemp()); + result += addFanToString(getFan(), kLgAcFanHigh, kLgAcFanLow, + kLgAcFanAuto, kLgAcFanAuto, kLgAcFanMedium); + } + return result; +} + +bool IRLgAc::isValidLgAc(void) { + return validChecksum(remote_state) && + (GETBITS32(remote_state, kLgAcSignatureOffset, kLgAcSignatureSize) == + kLgAcSignature); +} diff --git a/src/ir_LG.h b/src/ir_LG.h index 01f81e2c1..1147b30b7 100644 --- a/src/ir_LG.h +++ b/src/ir_LG.h @@ -1,15 +1,108 @@ -// Copyright 2017 David Conran +// Copyright 2017, 2019 David Conran // Supports: // Brand: LG, Model: 6711A20083V remote // Brand: LG, Model: AKB74395308 remote +// Brand: LG, Model: S4-W12JA3AA A/C (LG2) +// Brand: LG, Model: AKB75215403 remote (LG2) +// Brand: General Electric, Model: AG1BH09AW101 Split A/C +// Brand: General Electric, Model: 6711AR2853M A/C Remote #ifndef IR_LG_H_ #define IR_LG_H_ #define __STDC_LIMIT_MACROS #include +#ifndef UNIT_TEST +#include +#endif +#include "IRremoteESP8266.h" +#include "IRsend.h" +#include "IRutils.h" +#ifdef UNIT_TEST +#include "IRsend_test.h" +#endif + +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 kLgAcFanMedium = 2; // 0b010 +const uint8_t kLgAcFanHigh = 4; // 0b100 +const uint8_t kLgAcFanAuto = 5; // 0b101 +const uint8_t kLgAcTempOffset = 8; // Nr. of bits +const uint8_t kLgAcTempSize = 4; // Nr. of bits +const uint8_t kLgAcTempAdjust = 15; +const uint8_t kLgAcMinTemp = 16; // Celsius +const uint8_t kLgAcMaxTemp = 30; // Celsius +const uint8_t kLgAcModeOffset = 12; // Nr. of bits +const uint8_t kLgAcModeSize = 3; // Nr. of bits +const uint8_t kLgAcCool = 0; // 0b000 +const uint8_t kLgAcDry = 1; // 0b001 +const uint8_t kLgAcFan = 2; // 0b010 +const uint8_t kLgAcAuto = 3; // 0b011 +const uint8_t kLgAcHeat = 4; // 0b100 +const uint8_t kLgAcPowerOffset = 18; // Nr. of bits +const uint8_t kLgAcPowerSize = 2; // Nr. of bits +const uint8_t kLgAcPowerOff = 3; // 0b11 +const uint8_t kLgAcPowerOn = 0; // 0b00 +const uint8_t kLgAcSignatureOffset = 20; // Nr. of bits +const uint8_t kLgAcSignatureSize = 8; // Nr. of bits +const uint8_t kLgAcSignature = 0x88; + +const uint32_t kLgAcOffCommand = 0x88C0051; uint8_t calcLGChecksum(uint16_t data); +// Classes +class IRLgAc { + public: + explicit IRLgAc(const uint16_t pin, const bool inverted = false, + const bool use_modulation = true); + + void stateReset(void); + static uint8_t calcChecksum(const uint32_t state); + static bool validChecksum(const uint32_t state); + bool isValidLgAc(void); +#if SEND_LG + void send(const uint16_t repeat = kLgDefaultRepeat); + uint8_t calibrate(void) { return _irsend.calibrate(); } +#endif // SEND_LG + 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 setFan(const uint8_t speed); + uint8_t getFan(void); + void setMode(const uint8_t mode); + uint8_t getMode(void); + uint32_t getRaw(void); + void setRaw(const uint32_t new_code); + uint8_t convertMode(const stdAc::opmode_t mode); + static stdAc::opmode_t toCommonMode(const uint8_t mode); + static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed); + static uint8_t convertFan(const stdAc::fanspeed_t speed); + stdAc::state_t toCommon(void); + String toString(void); + void setModel(const lg_ac_remote_model_t model); + lg_ac_remote_model_t getModel(void); +#ifndef UNIT_TEST + + private: + IRsend _irsend; +#else + IRsendTest _irsend; +#endif + // The state of the IR remote in IR code form. + uint32_t remote_state; + uint8_t _temp; + decode_type_t _protocol; + void checksum(void); + void _setTemp(const uint8_t value); +}; + #endif // IR_LG_H_ diff --git a/test/IRac_test.cpp b/test/IRac_test.cpp index 63ca53e15..2c17e2a92 100644 --- a/test/IRac_test.cpp +++ b/test/IRac_test.cpp @@ -11,6 +11,7 @@ #include "ir_Haier.h" #include "ir_Hitachi.h" #include "ir_Kelvinator.h" +#include "ir_LG.h" #include "ir_Midea.h" #include "ir_Mitsubishi.h" #include "ir_MitsubishiHeavy.h" @@ -640,6 +641,30 @@ TEST(TestIRac, Kelvinator) { ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture)); } +TEST(TestIRac, LG) { + IRLgAc ac(0); + IRac irac(0); + IRrecv capture(0); + char expected[] = + "Model: 1 (GE6711AR2853M), " + "Power: On, Mode: 1 (Dry), Temp: 27C, Fan: 2 (Medium)"; + + ac.begin(); + irac.lg(&ac, + lg_ac_remote_model_t::GE6711AR2853M, // Model + true, // Power + stdAc::opmode_t::kDry, // Mode + 27, // Degrees C + stdAc::fanspeed_t::kMedium); // Fan speed + + ASSERT_EQ(expected, ac.toString()); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(LG, ac._irsend.capture.decode_type); + ASSERT_EQ(kLgBits, ac._irsend.capture.bits); + ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture)); +} + TEST(TestIRac, Midea) { IRMideaAC ac(0); IRac irac(0); diff --git a/test/Makefile b/test/Makefile index ad75937b8..cdf133913 100644 --- a/test/Makefile +++ b/test/Makefile @@ -265,7 +265,7 @@ ir_RCMM_test : $(COMMON_OBJ) ir_RCMM_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ ir_LG.o : $(USER_DIR)/ir_LG.h $(USER_DIR)/ir_LG.cpp $(COMMON_DEPS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_LG.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_LG.cpp ir_LG_test.o : ir_LG_test.cpp $(USER_DIR)/ir_LG.h $(COMMON_TEST_DEPS) $(GTEST_HEADERS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_LG_test.cpp diff --git a/test/ir_LG_test.cpp b/test/ir_LG_test.cpp index d1e1b8659..1c2b2a856 100644 --- a/test/ir_LG_test.cpp +++ b/test/ir_LG_test.cpp @@ -1,6 +1,7 @@ -// Copyright 2017 David Conran +// Copyright 2017, 2019 David Conran #include "ir_LG.h" +#include "IRac.h" #include "IRsend.h" #include "IRsend_test.h" #include "gtest/gtest.h" @@ -18,6 +19,10 @@ TEST(TestCalcLGChecksum, General) { EXPECT_EQ(0xE, calcLGChecksum(0xABCD)); EXPECT_EQ(0x1, calcLGChecksum(0x4AE5)); EXPECT_EQ(0xC, calcLGChecksum(0xFFFF)); + EXPECT_EQ(0x1, calcLGChecksum(0xC005)); + EXPECT_EQ(0x1, IRLgAc::calcChecksum(0x88C0051)); + EXPECT_EQ(0x4, calcLGChecksum(0xC035)); + EXPECT_EQ(0x4, IRLgAc::calcChecksum(0x88C0354)); } // Tests for sendLG(). @@ -457,6 +462,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); irsend.sendLG(0x8808721); irsend.makeDecodeResult(); ASSERT_TRUE(irrecv.decode(&irsend.capture)); @@ -465,6 +471,11 @@ TEST(TestDecodeLG, Issue620) { EXPECT_EQ(0x8808721, irsend.capture.value); EXPECT_EQ(0x88, irsend.capture.address); EXPECT_EQ(0x872, irsend.capture.command); + ac.setRaw(irsend.capture.value); + ASSERT_TRUE(ac.isValidLgAc()); + EXPECT_EQ("Model: 1 (GE6711AR2853M), " + "Power: On, Mode: 0 (Cool), Temp: 22C, Fan: 2 (Medium)", + ac.toString()); // The following seems to match the rawData above. EXPECT_EQ( "f38000d50" @@ -478,3 +489,390 @@ TEST(TestDecodeLG, Issue620) { "s55550", irsend.outputStr()); } + +TEST(TestIRLgAcClass, SetAndGetPower) { + IRLgAc ac(0); + 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(TestIRLgAcClass, SetAndGetTemp) { + IRLgAc ac(0); + ac.setTemp(25); + EXPECT_EQ(25, ac.getTemp()); + ac.setTemp(kLgAcMinTemp); + EXPECT_EQ(kLgAcMinTemp, ac.getTemp()); + ac.setTemp(kLgAcMinTemp - 1); + EXPECT_EQ(kLgAcMinTemp, ac.getTemp()); + ac.setTemp(kLgAcMaxTemp); + EXPECT_EQ(kLgAcMaxTemp, ac.getTemp()); + ac.setTemp(kLgAcMaxTemp + 1); + EXPECT_EQ(kLgAcMaxTemp, ac.getTemp()); +} + +TEST(TestIRLgAcClass, SetAndGetMode) { + IRLgAc ac(0); + ac.setMode(kLgAcCool); + ac.setFan(kLgAcFanAuto); + ac.setTemp(25); + EXPECT_EQ(25, ac.getTemp()); + EXPECT_EQ(kLgAcCool, ac.getMode()); + EXPECT_EQ(kLgAcFanAuto, ac.getFan()); + ac.setMode(kLgAcHeat); + EXPECT_EQ(kLgAcHeat, ac.getMode()); + ac.setMode(kLgAcDry); + EXPECT_EQ(kLgAcDry, ac.getMode()); +} + +TEST(TestIRLgAcClass, SetAndGetFan) { + IRLgAc ac(0); + ac.setMode(kLgAcCool); + ac.setFan(kLgAcFanAuto); + EXPECT_EQ(kLgAcFanAuto, ac.getFan()); + ac.setFan(kLgAcFanLow); + EXPECT_EQ(kLgAcFanLow, ac.getFan()); + ac.setFan(kLgAcFanHigh); + EXPECT_EQ(kLgAcFanHigh, ac.getFan()); + ac.setFan(kLgAcFanAuto + 1); + EXPECT_EQ(kLgAcFanAuto, ac.getFan()); + ac.setFan(kLgAcFanLow - 1); + EXPECT_EQ(kLgAcFanAuto, ac.getFan()); +} + +TEST(TestIRLgAcClass, toCommon) { + IRLgAc ac(0); + ac.setPower(true); + ac.setMode(kLgAcCool); + ac.setTemp(20); + ac.setFan(kLgAcFanHigh); + // Now test it. + ASSERT_EQ(decode_type_t::LG, ac.toCommon().protocol); + ASSERT_EQ(lg_ac_remote_model_t::GE6711AR2853M, ac.toCommon().model); + ASSERT_TRUE(ac.toCommon().power); + ASSERT_TRUE(ac.toCommon().celsius); + ASSERT_EQ(20, ac.toCommon().degrees); + ASSERT_EQ(stdAc::opmode_t::kCool, ac.toCommon().mode); + ASSERT_EQ(stdAc::fanspeed_t::kMax, ac.toCommon().fanspeed); + // Unsupported. + ASSERT_EQ(stdAc::swingv_t::kOff, ac.toCommon().swingv); + ASSERT_EQ(stdAc::swingh_t::kOff, ac.toCommon().swingh); + ASSERT_FALSE(ac.toCommon().turbo); + ASSERT_FALSE(ac.toCommon().clean); + ASSERT_FALSE(ac.toCommon().light); + ASSERT_FALSE(ac.toCommon().quiet); + ASSERT_FALSE(ac.toCommon().econo); + ASSERT_FALSE(ac.toCommon().filter); + ASSERT_FALSE(ac.toCommon().beep); + ASSERT_EQ(-1, ac.toCommon().sleep); + ASSERT_EQ(-1, ac.toCommon().clock); + + // Change models + ac.setModel(AKB75215403); + ASSERT_EQ(lg_ac_remote_model_t::AKB75215403, ac.toCommon().model); +} + +TEST(TestIRLgAcClass, HumanReadable) { + IRLgAc ac(0); + + EXPECT_EQ( + "Model: 1 (GE6711AR2853M), " + "Power: Off", + ac.toString()); + ac.setMode(kLgAcHeat); + ac.setTemp(kLgAcMaxTemp); + ac.on(); + ac.setFan(kLgAcFanHigh); + EXPECT_EQ( + "Model: 1 (GE6711AR2853M), " + "Power: On, Mode: 4 (Heat), Temp: 30C, Fan: 4 (High)", + ac.toString()); + ac.setMode(kLgAcCool); + ac.setFan(kLgAcFanLow); + ac.setTemp(kLgAcMinTemp); + EXPECT_EQ( + "Model: 1 (GE6711AR2853M), " + "Power: On, Mode: 0 (Cool), Temp: 16C, Fan: 0 (Low)", + ac.toString()); + ac.setTemp(ac.getTemp() + 1); + EXPECT_EQ( + "Model: 1 (GE6711AR2853M), " + "Power: On, Mode: 0 (Cool), Temp: 17C, Fan: 0 (Low)", + ac.toString()); + ac.setTemp(ac.getTemp() - 1); + EXPECT_EQ( + "Model: 1 (GE6711AR2853M), " + "Power: On, Mode: 0 (Cool), Temp: 16C, Fan: 0 (Low)", + ac.toString()); + ac.setPower(false); + EXPECT_EQ( + "Model: 1 (GE6711AR2853M), " + "Power: Off", + ac.toString()); +} + +TEST(TestIRLgAcClass, SetAndGetRaw) { + IRLgAc ac(0); + + ac.setRaw(0x8800A4E); + ASSERT_EQ(0x8800A4E, ac.getRaw()); + EXPECT_EQ( + "Model: 1 (GE6711AR2853M), " + "Power: On, Mode: 0 (Cool), Temp: 25C, Fan: 4 (High)", + ac.toString()); + + ac.setRaw(0x88C0051); + ASSERT_EQ(0x88C0051, ac.getRaw()); + EXPECT_EQ( + "Model: 1 (GE6711AR2853M), " + "Power: Off", + ac.toString()); +} + +TEST(TestIRLgAcClass, MessageConstruction) { + IRLgAc ac(0); + + ac.on(); + ac.setMode(kLgAcCool); + ac.setTemp(25); + ac.setFan(kLgAcFanHigh); + ASSERT_EQ(0x8800A4E, ac.getRaw()); + EXPECT_EQ( + "Model: 1 (GE6711AR2853M), " + "Power: On, Mode: 0 (Cool), Temp: 25C, Fan: 4 (High)", + ac.toString()); +} + +TEST(TestIRLgAcClass, isValidLgAc) { + IRLgAc ac(0); + + ac.setRaw(0x8800A4E); + ASSERT_TRUE(ac.isValidLgAc()); + + // Make the checksum wrong. + ac.setRaw(0x8800A4F); + ASSERT_FALSE(ac.isValidLgAc()); + + ac.setRaw(0x88C0051); + ASSERT_TRUE(ac.isValidLgAc()); + + // Use a wrong signature. + ac.setRaw(0x8000A4E); + ASSERT_FALSE(ac.isValidLgAc()); +} + +TEST(TestUtils, Housekeeping) { + ASSERT_EQ("LG", typeToString(decode_type_t::LG)); + ASSERT_EQ(decode_type_t::LG, strToDecodeType("LG")); + ASSERT_FALSE(hasACState(decode_type_t::LG)); + ASSERT_TRUE(IRac::isProtocolSupported(decode_type_t::LG)); + + ASSERT_EQ("LG2", typeToString(decode_type_t::LG2)); + ASSERT_EQ(decode_type_t::LG2, strToDecodeType("LG2")); + ASSERT_FALSE(hasACState(decode_type_t::LG2)); + ASSERT_TRUE(IRac::isProtocolSupported(decode_type_t::LG2)); +} + +TEST(TestIRLgAcClass, KnownExamples) { + IRLgAc ac(0); + // Ref: + // https://github.com/crankyoldgit/IRremoteESP8266/issues/1008#issuecomment-570646648 + + // Temp + ac.setRaw(0x880C152); + ASSERT_TRUE(ac.isValidLgAc()); + EXPECT_EQ( + "Model: 1 (GE6711AR2853M), " + "Power: On, Mode: 4 (Heat), Temp: 16C, Fan: 5 (Auto)", + ac.toString()); + + ac.setRaw(0x880CF50); + ASSERT_TRUE(ac.isValidLgAc()); + EXPECT_EQ( + "Model: 1 (GE6711AR2853M), " + "Power: On, Mode: 4 (Heat), Temp: 30C, Fan: 5 (Auto)", + ac.toString()); + + // Modes + ac.setRaw(0x880960F); + ASSERT_TRUE(ac.isValidLgAc()); + EXPECT_EQ( + "Model: 1 (GE6711AR2853M), " + "Power: On, Mode: 1 (Dry), Temp: 21C, Fan: 0 (Low)", + ac.toString()); + + ac.setRaw(0x880C758); + ASSERT_TRUE(ac.isValidLgAc()); + EXPECT_EQ( + "Model: 1 (GE6711AR2853M), " + "Power: On, Mode: 4 (Heat), Temp: 22C, Fan: 5 (Auto)", + ac.toString()); + + ac.setRaw(0x8808855); + ASSERT_TRUE(ac.isValidLgAc()); + EXPECT_EQ( + "Model: 1 (GE6711AR2853M), " + "Power: On, Mode: 0 (Cool), Temp: 23C, Fan: 5 (Auto)", + ac.toString()); + + // Fan speeds + ac.setRaw(0x880870F); + ASSERT_TRUE(ac.isValidLgAc()); + EXPECT_EQ( + "Model: 1 (GE6711AR2853M), " + "Power: On, Mode: 0 (Cool), Temp: 22C, Fan: 0 (Low)", + ac.toString()); + + ac.setRaw(0x8808721); + ASSERT_TRUE(ac.isValidLgAc()); + EXPECT_EQ( + "Model: 1 (GE6711AR2853M), " + "Power: On, Mode: 0 (Cool), Temp: 22C, Fan: 2 (Medium)", + ac.toString()); + + ac.setRaw(0x8808743); + ASSERT_TRUE(ac.isValidLgAc()); + EXPECT_EQ( + "Model: 1 (GE6711AR2853M), " + "Power: On, Mode: 0 (Cool), Temp: 22C, Fan: 4 (High)", + ac.toString()); + + ac.setRaw(0x8808754); + ASSERT_TRUE(ac.isValidLgAc()); + EXPECT_EQ( + "Model: 1 (GE6711AR2853M), " + "Power: On, Mode: 0 (Cool), Temp: 22C, Fan: 5 (Auto)", + ac.toString()); + + ac.setRaw(0x880A745); + ASSERT_TRUE(ac.isValidLgAc()); + EXPECT_EQ( + "Model: 1 (GE6711AR2853M), " + "Power: On, Mode: 2 (Fan), Temp: 22C, Fan: 4 (High)", + ac.toString()); + + // https://github.com/crankyoldgit/IRremoteESP8266/issues/1008#issuecomment-570794029 + ac.setRaw(0x8800347); + ASSERT_TRUE(ac.isValidLgAc()); + EXPECT_EQ( + "Model: 1 (GE6711AR2853M), " + "Power: On, Mode: 0 (Cool), Temp: 18C, Fan: 4 (High)", + ac.toString()); + ac.setRaw(0x8808440); + ASSERT_TRUE(ac.isValidLgAc()); + EXPECT_EQ( + "Model: 1 (GE6711AR2853M), " + "Power: On, Mode: 0 (Cool), Temp: 19C, Fan: 4 (High)", + ac.toString()); + ac.setRaw(0x8800459); + ASSERT_TRUE(ac.isValidLgAc()); + EXPECT_EQ( + "Model: 1 (GE6711AR2853M), " + "Power: On, Mode: 0 (Cool), Temp: 19C, Fan: 5 (Auto)", + ac.toString()); + ac.setRaw(0x8809946); + ASSERT_TRUE(ac.isValidLgAc()); + EXPECT_EQ( + "Model: 1 (GE6711AR2853M), " + "Power: On, Mode: 1 (Dry), Temp: 24C, Fan: 4 (High)", + ac.toString()); + ac.setRaw(0x880A341); + ASSERT_TRUE(ac.isValidLgAc()); + EXPECT_EQ( + "Model: 1 (GE6711AR2853M), " + "Power: On, Mode: 2 (Fan), Temp: 18C, Fan: 4 (High)", + ac.toString()); + ac.setRaw(0x8810045); + ASSERT_TRUE(ac.isValidLgAc()); + EXPECT_EQ( + "Model: 1 (GE6711AR2853M), " + "Power: On, Mode: 0 (Cool), Temp: 15C, Fan: 4 (High)", + ac.toString()); + ac.setRaw(0x8810056); + ASSERT_TRUE(ac.isValidLgAc()); + EXPECT_EQ( + "Model: 1 (GE6711AR2853M), " + "Power: On, Mode: 0 (Cool), Temp: 15C, Fan: 5 (Auto)", + ac.toString()); +} + +// Verify decoding of LG2 message. +TEST(TestDecodeLG2, Issue1008) { + IRsendTest irsend(0); + IRrecv capture(0); + irsend.begin(); + + irsend.reset(); + // From https://github.com/crankyoldgit/IRremoteESP8266/issues/1008#issuecomment-570794029 + // First entry. + uint16_t rawData[59] = { + 3272, 9844, 506, 1588, 536, 498, 534, 498, 536, 498, 534, 1540, 534, 506, + 534, 498, 534, 500, 532, 500, 534, 498, 534, 498, 534, 506, 534, 500, 534, + 498, 534, 498, 534, 498, 534, 500, 534, 498, 534, 1566, 508, 1566, 508, + 500, 534, 1540, 534, 506, 534, 500, 534, 500, 534, 1560, 508, 1540, 534, + 1558, 508}; // UNKNOWN AFC3034C + irsend.sendRaw(rawData, 59, 38000); + irsend.makeDecodeResult(); + + ASSERT_TRUE(capture.decode(&irsend.capture)); + ASSERT_EQ(LG2, irsend.capture.decode_type); + EXPECT_EQ(kLgBits, irsend.capture.bits); + EXPECT_EQ(0x8800347, irsend.capture.value); + + irsend.reset(); + IRLgAc ac(0); + ac.setRaw(0x8800347); + ac.setModel(lg_ac_remote_model_t::AKB75215403); // aka. 2 + ac.send(); + + char expected[] = + "Model: 2 (AKB75215403), " + "Power: On, Mode: 0 (Cool), Temp: 18C, Fan: 4 (High)"; + ASSERT_EQ(expected, ac.toString()); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(LG2, ac._irsend.capture.decode_type); + ASSERT_EQ(kLgBits, ac._irsend.capture.bits); + ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture)); +} + +TEST(TestIRLgAcClass, DifferentModels) { + IRLgAc ac(0); + IRrecv capture(0); + + ac.setRaw(0x8800347); + + ac.setModel(lg_ac_remote_model_t::GE6711AR2853M); // aka. 1 + ac._irsend.reset(); + ac.send(); + + char expected1[] = + "Model: 1 (GE6711AR2853M), " + "Power: On, Mode: 0 (Cool), Temp: 18C, Fan: 4 (High)"; + ASSERT_EQ(expected1, ac.toString()); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(LG, ac._irsend.capture.decode_type); // Not "LG2" + ASSERT_EQ(kLgBits, ac._irsend.capture.bits); + ASSERT_EQ(expected1, IRAcUtils::resultAcToString(&ac._irsend.capture)); + + + ac.setModel(lg_ac_remote_model_t::AKB75215403); // aka. 2 + ac._irsend.reset(); + ac.send(); + + char expected2[] = + "Model: 2 (AKB75215403), " + "Power: On, Mode: 0 (Cool), Temp: 18C, Fan: 4 (High)"; + ASSERT_EQ(expected2, ac.toString()); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(LG2, ac._irsend.capture.decode_type); // Not "LG" + ASSERT_EQ(kLgBits, ac._irsend.capture.bits); + ASSERT_EQ(expected2, IRAcUtils::resultAcToString(&ac._irsend.capture)); +} diff --git a/tools/Makefile b/tools/Makefile index bf23fbce6..6868f97db 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -118,7 +118,7 @@ ir_RCMM.o : $(USER_DIR)/ir_RCMM.cpp $(COMMON_DEPS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_RCMM.cpp ir_LG.o : $(USER_DIR)/ir_LG.h $(USER_DIR)/ir_LG.cpp $(COMMON_DEPS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_LG.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_LG.cpp ir_Mitsubishi.o : $(USER_DIR)/ir_Mitsubishi.h $(USER_DIR)/ir_Mitsubishi.cpp $(COMMON_DEPS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Mitsubishi.cpp diff --git a/tools/scrape_supported_devices.py b/tools/scrape_supported_devices.py index 7ab2c3771..a0efd0774 100755 --- a/tools/scrape_supported_devices.py +++ b/tools/scrape_supported_devices.py @@ -17,7 +17,7 @@ ALL_FN = re.compile(r"ir_(.+)\.(h|cpp)") EXCLUDED_PROTOCOLS = ["UNKNOWN", "UNUSED", "kLastDecodeType"] -EXCLUDED_ACS = ["LG", "Magiquest", "NEC"] +EXCLUDED_ACS = ["Magiquest", "NEC"] MARKDOWN_HEADER = """