diff --git a/src/IRac.cpp b/src/IRac.cpp index de07bf964..20499bea3 100644 --- a/src/IRac.cpp +++ b/src/IRac.cpp @@ -4252,7 +4252,7 @@ namespace IRAcUtils { case decode_type_t::FUJITSU_AC: { IRFujitsuAC ac(kGpioUnused); ac.setRaw(decode->state, decode->bits / 8); - *result = ac.toCommon(); + *result = ac.toCommon(prev); break; } #endif // DECODE_FUJITSU_AC diff --git a/src/ir_Fujitsu.cpp b/src/ir_Fujitsu.cpp index eca1adf93..1b0bfab18 100644 --- a/src/ir_Fujitsu.cpp +++ b/src/ir_Fujitsu.cpp @@ -1,5 +1,5 @@ // Copyright 2017 Jonny Graham -// Copyright 2017-2021 David Conran +// Copyright 2017-2022 David Conran // Copyright 2021 siriuslzx /// @file @@ -116,6 +116,7 @@ void IRFujitsuAC::stateReset(void) { _.longcode[1] = 0x63; _.longcode[3] = 0x10; _.longcode[4] = 0x10; + _rawstatemodified = true; } /// Set up hardware to be able to send a message. @@ -142,6 +143,7 @@ bool IRFujitsuAC::updateUseLongOrShort(void) { case kFujitsuAcCmdStepHoriz: // 0x79 case kFujitsuAcCmdToggleSwingHoriz: // 0x7A _.Cmd = _cmd; + _rawstatemodified = true; break; default: switch (_model) { @@ -150,10 +152,12 @@ bool IRFujitsuAC::updateUseLongOrShort(void) { case fujitsu_ac_remote_model_t::ARREB1E: case fujitsu_ac_remote_model_t::ARREW4E: _.Cmd = 0xFE; + _rawstatemodified = true; break; case fujitsu_ac_remote_model_t::ARDB1: case fujitsu_ac_remote_model_t::ARJW2: _.Cmd = 0xFC; + _rawstatemodified = true; break; } fullCmd = true; @@ -164,7 +168,8 @@ bool IRFujitsuAC::updateUseLongOrShort(void) { /// Calculate and set the checksum values for the internal state. void IRFujitsuAC::checkSum(void) { - if (updateUseLongOrShort()) { // Is it a long code? + _rawstatemodified = true; + if (updateUseLongOrShort()) { // Is it going to be a long code? // Nr. of bytes in the message after this byte. _.RestLength = _state_length - 7; _.Protocol = (_model == fujitsu_ac_remote_model_t::ARREW4E) ? 0x31 : 0x30; @@ -235,13 +240,17 @@ uint8_t IRFujitsuAC::getStateLength(void) { return updateUseLongOrShort() ? _state_length : _state_length_short; } +/// Is the current binary state representation a long or a short code? +/// @return true, if long; false, if short. +bool IRFujitsuAC::isLongCode(void) const { + return _.Cmd == 0xFE || _.Cmd == 0xFC; +} + /// Get a PTR to the internal state/code for this protocol. /// @return PTR to a code for this protocol based on the current internal state. uint8_t* IRFujitsuAC::getRaw(void) { checkSum(); - if (_.Cmd == 0xFE || _.Cmd == 0xFC) - return _.longcode; - return _.shortcode; + return isLongCode() ? _.longcode : _.shortcode; } /// Build the internal state/config from the current (raw) A/C message. @@ -312,6 +321,7 @@ bool IRFujitsuAC::setRaw(const uint8_t newState[], const uint16_t length) { _.longcode[i] = 0; } buildFromState(length); + _rawstatemodified = false; return true; } @@ -405,6 +415,7 @@ bool IRFujitsuAC::getPower(void) const { return _cmd != kFujitsuAcCmdTurnOff; } /// @param[in] on true, the setting is on. false, the setting is off. void IRFujitsuAC::setOutsideQuiet(const bool on) { _.OutsideQuiet = on; + _rawstatemodified = true; setCmd(kFujitsuAcCmdStayOn); // No special command involved. } @@ -461,6 +472,7 @@ void IRFujitsuAC::setTemp(const float temp, const bool useCelsius) { } else { _.Temp = _temp - offset; } + _rawstatemodified = true; setCmd(kFujitsuAcCmdStayOn); // No special command involved. } @@ -484,6 +496,7 @@ void IRFujitsuAC::setFanSpeed(const uint8_t fanSpeed) { _.Fan = kFujitsuAcFanHigh; // Set the fan to maximum if out of range. else _.Fan = fanSpeed; + _rawstatemodified = true; setCmd(kFujitsuAcCmdStayOn); // No special command involved. } @@ -498,6 +511,7 @@ void IRFujitsuAC::setMode(const uint8_t mode) { _.Mode = kFujitsuAcModeHeat; // Set the mode to maximum if out of range. else _.Mode = mode; + _rawstatemodified = true; setCmd(kFujitsuAcCmdStayOn); // No special command involved. } @@ -511,6 +525,7 @@ uint8_t IRFujitsuAC::getMode(void) const { return _.Mode; } /// @note Not all models support all possible swing modes. void IRFujitsuAC::setSwing(const uint8_t swingMode) { _.Swing = swingMode; + _rawstatemodified = true; switch (_model) { // No Horizontal support. case fujitsu_ac_remote_model_t::ARDB1: @@ -531,14 +546,13 @@ void IRFujitsuAC::setSwing(const uint8_t swingMode) { /// Get the requested swing operation mode of the A/C unit. /// @return The contents of the swing state/mode. -uint8_t IRFujitsuAC::getSwing(void) const { - return _.Swing; -} +uint8_t IRFujitsuAC::getSwing(void) const { return _.Swing; } /// Set the Clean mode of the A/C. /// @param[in] on true, the setting is on. false, the setting is off. void IRFujitsuAC::setClean(const bool on) { _.Clean = on; + _rawstatemodified = true; setCmd(kFujitsuAcCmdStayOn); // No special command involved. } @@ -555,6 +569,7 @@ bool IRFujitsuAC::getClean(void) const { /// @param[in] on true, the setting is on. false, the setting is off. void IRFujitsuAC::setFilter(const bool on) { _.Filter = on; + _rawstatemodified = true; setCmd(kFujitsuAcCmdStayOn); // No special command involved. } @@ -579,6 +594,7 @@ void IRFujitsuAC::set10CHeat(const bool on) { _.Power = true; _.Fan = kFujitsuAcFanAuto; _.Swing = kFujitsuAcSwingOff; + _rawstatemodified = true; } default: break; @@ -602,9 +618,8 @@ uint8_t IRFujitsuAC::getTimerType(void) const { switch (_model) { // These models seem to have timer support. case fujitsu_ac_remote_model_t::ARRAH2E: - case fujitsu_ac_remote_model_t::ARREB1E: - return _.TimerType; - default: return kFujitsuAcStopTimers; + case fujitsu_ac_remote_model_t::ARREB1E: return _.TimerType; + default: return kFujitsuAcStopTimers; } } @@ -620,6 +635,7 @@ void IRFujitsuAC::setTimerType(const uint8_t timertype) { break; default: _.TimerType = kFujitsuAcStopTimers; } + _rawstatemodified = true; } /// Get the On Timer setting of the A/C. @@ -634,6 +650,7 @@ uint16_t IRFujitsuAC::getOnTimer(void) const { /// @param[in] nr_mins Nr. of minutes to set the timer to. 0 means disabled. void IRFujitsuAC::setOnTimer(const uint16_t nr_mins) { _.OnTimer = std::min(kFujitsuAcTimerMax, nr_mins); // Bounds check. + _rawstatemodified = true; if (_.OnTimer) { _.TimerType = kFujitsuAcOnTimer; } else if (getTimerType() == kFujitsuAcOnTimer) { @@ -646,10 +663,8 @@ void IRFujitsuAC::setOnTimer(const uint16_t nr_mins) { uint16_t IRFujitsuAC::getOffSleepTimer(void) const { switch (getTimerType()) { case kFujitsuAcOffTimer: - case kFujitsuAcSleepTimer: - return _.OffTimer; - default: - return 0; + case kFujitsuAcSleepTimer: return _.OffTimer; + default: return 0; } } @@ -657,12 +672,13 @@ uint16_t IRFujitsuAC::getOffSleepTimer(void) const { /// @param[in] nr_mins Nr. of minutes to set the timer to. 0 means disabled. inline void IRFujitsuAC::setOffSleepTimer(const uint16_t nr_mins) { _.OffTimer = std::min(kFujitsuAcTimerMax, nr_mins); // Bounds check. + _rawstatemodified = true; } /// Set the Off Timer time for the A/C. /// @param[in] nr_mins Nr. of minutes to set the timer to. 0 means disabled. void IRFujitsuAC::setOffTimer(const uint16_t nr_mins) { - setOffSleepTimer(nr_mins); + setOffSleepTimer(nr_mins); // This will also set _rawstatemodified to true. if (nr_mins) _.TimerType = kFujitsuAcOffTimer; else if (getTimerType() != kFujitsuAcOnTimer) @@ -672,7 +688,7 @@ void IRFujitsuAC::setOffTimer(const uint16_t nr_mins) { /// Set the Sleep Timer time for the A/C. /// @param[in] nr_mins Nr. of minutes to set the timer to. 0 means disabled. void IRFujitsuAC::setSleepTimer(const uint16_t nr_mins) { - setOffSleepTimer(nr_mins); + setOffSleepTimer(nr_mins); // This will also set _rawstatemodified to true. if (nr_mins) _.TimerType = kFujitsuAcSleepTimer; else if (getTimerType() != kFujitsuAcOnTimer) @@ -706,7 +722,10 @@ bool IRFujitsuAC::validChecksum(uint8_t state[], const uint16_t length) { /// Set the device's remote ID number. /// @param[in] num The ID for the remote. Valid number range is 0 to 3. -void IRFujitsuAC::setId(const uint8_t num) { _.Id = num; } +void IRFujitsuAC::setId(const uint8_t num) { + _.Id = num; + _rawstatemodified = true; +} /// Get the current device's remote ID number. /// @return The current device's remote ID number. @@ -714,7 +733,10 @@ uint8_t IRFujitsuAC::getId(void) const { return _.Id; } /// Set the Temperature units for the A/C. /// @param[in] on true, use Celsius. false, use Fahrenheit. -void IRFujitsuAC::setCelsius(const bool on) { _.Fahrenheit = !on; } +void IRFujitsuAC::setCelsius(const bool on) { + _.Fahrenheit = !on; + _rawstatemodified = true; +} /// Get the Clean mode status of the A/C. /// @return true, the setting is on. false, the setting is off. @@ -774,35 +796,41 @@ stdAc::fanspeed_t IRFujitsuAC::toCommonFanSpeed(const uint8_t speed) { } /// Convert the current internal state into its stdAc::state_t equivalent. +/// @param[in] prev Ptr to a previous state. /// @return The stdAc equivalent of the native settings. -stdAc::state_t IRFujitsuAC::toCommon(void) const { +stdAc::state_t IRFujitsuAC::toCommon(const stdAc::state_t *prev) { stdAc::state_t result{}; + if (prev != NULL) result = *prev; result.protocol = decode_type_t::FUJITSU_AC; + checkSum(); result.model = _model; result.power = getPower(); - result.mode = toCommonMode(_.Mode); - result.celsius = getCelsius(); - result.degrees = getTemp(); - result.fanspeed = toCommonFanSpeed(_.Fan); - uint8_t swing = _.Swing; - switch (result.model) { - case fujitsu_ac_remote_model_t::ARREB1E: - case fujitsu_ac_remote_model_t::ARRAH2E: - case fujitsu_ac_remote_model_t::ARRY4: - result.clean = _.Clean; - result.filter = _.Filter; - result.swingv = (swing & kFujitsuAcSwingVert) ? stdAc::swingv_t::kAuto - : stdAc::swingv_t::kOff; - result.swingh = (swing & kFujitsuAcSwingHoriz) ? stdAc::swingh_t::kAuto - : stdAc::swingh_t::kOff; - break; - case fujitsu_ac_remote_model_t::ARDB1: - case fujitsu_ac_remote_model_t::ARJW2: - default: - result.swingv = stdAc::swingv_t::kOff; - result.swingh = stdAc::swingh_t::kOff; + // Only update these settings if it is a long message, or we have no previous + // state info for those settings. + if (isLongCode() || prev == NULL) { + result.mode = toCommonMode(_.Mode); + result.celsius = getCelsius(); + result.degrees = getTemp(); + result.fanspeed = toCommonFanSpeed(_.Fan); + uint8_t swing = _.Swing; + switch (result.model) { + case fujitsu_ac_remote_model_t::ARREB1E: + case fujitsu_ac_remote_model_t::ARRAH2E: + case fujitsu_ac_remote_model_t::ARRY4: + result.clean = _.Clean; + result.filter = _.Filter; + result.swingv = (swing & kFujitsuAcSwingVert) ? stdAc::swingv_t::kAuto + : stdAc::swingv_t::kOff; + result.swingh = (swing & kFujitsuAcSwingHoriz) ? stdAc::swingh_t::kAuto + : stdAc::swingh_t::kOff; + break; + case fujitsu_ac_remote_model_t::ARDB1: + case fujitsu_ac_remote_model_t::ARJW2: + default: + result.swingv = stdAc::swingv_t::kOff; + result.swingh = stdAc::swingh_t::kOff; + } } - result.quiet = _.Fan == kFujitsuAcFanQuiet; result.turbo = _cmd == kFujitsuAcCmdPowerful; result.econo = _cmd == kFujitsuAcCmdEcono; @@ -825,49 +853,51 @@ String IRFujitsuAC::toString(void) const { result += addModelToString(decode_type_t::FUJITSU_AC, model, false); result += addIntToString(_.Id, kIdStr); result += addBoolToString(getPower(), kPowerStr); - result += addModeToString(_.Mode, kFujitsuAcModeAuto, kFujitsuAcModeCool, - kFujitsuAcModeHeat, kFujitsuAcModeDry, - kFujitsuAcModeFan); - result += addTempFloatToString(getTemp(), getCelsius()); - result += addFanToString(_.Fan, kFujitsuAcFanHigh, kFujitsuAcFanLow, - kFujitsuAcFanAuto, kFujitsuAcFanQuiet, - kFujitsuAcFanMed); - switch (model) { - // These models have no internal swing, clean. or filter state. - case fujitsu_ac_remote_model_t::ARDB1: - case fujitsu_ac_remote_model_t::ARJW2: - break; - // These models have Clean & Filter, plus Swing (via fall thru) - case fujitsu_ac_remote_model_t::ARRAH2E: - case fujitsu_ac_remote_model_t::ARREB1E: - case fujitsu_ac_remote_model_t::ARRY4: - result += addBoolToString(getClean(), kCleanStr); - result += addBoolToString(getFilter(), kFilterStr); - // FALL THRU - default: // e.g. ARREW4E - if (model == fujitsu_ac_remote_model_t::ARREW4E) - result += addBoolToString(get10CHeat(), k10CHeatStr); - result += addIntToString(_.Swing, kSwingStr); - result += kSpaceLBraceStr; - switch (_.Swing) { - case kFujitsuAcSwingOff: - result += kOffStr; - break; - case kFujitsuAcSwingVert: - result += kSwingVStr; - break; - case kFujitsuAcSwingHoriz: - result += kSwingHStr; - break; - case kFujitsuAcSwingBoth: - result += kSwingVStr; - result += '+'; - result += kSwingHStr; - break; - default: - result += kUnknownStr; - } - result += ')'; + if (_rawstatemodified || isLongCode()) { + result += addModeToString(_.Mode, kFujitsuAcModeAuto, kFujitsuAcModeCool, + kFujitsuAcModeHeat, kFujitsuAcModeDry, + kFujitsuAcModeFan); + result += addTempFloatToString(getTemp(), getCelsius()); + result += addFanToString(_.Fan, kFujitsuAcFanHigh, kFujitsuAcFanLow, + kFujitsuAcFanAuto, kFujitsuAcFanQuiet, + kFujitsuAcFanMed); + switch (model) { + // These models have no internal swing, clean. or filter state. + case fujitsu_ac_remote_model_t::ARDB1: + case fujitsu_ac_remote_model_t::ARJW2: + break; + // These models have Clean & Filter, plus Swing (via fall thru) + case fujitsu_ac_remote_model_t::ARRAH2E: + case fujitsu_ac_remote_model_t::ARREB1E: + case fujitsu_ac_remote_model_t::ARRY4: + result += addBoolToString(getClean(), kCleanStr); + result += addBoolToString(getFilter(), kFilterStr); + // FALL THRU + default: // e.g. ARREW4E + if (model == fujitsu_ac_remote_model_t::ARREW4E) + result += addBoolToString(get10CHeat(), k10CHeatStr); + result += addIntToString(_.Swing, kSwingStr); + result += kSpaceLBraceStr; + switch (_.Swing) { + case kFujitsuAcSwingOff: + result += kOffStr; + break; + case kFujitsuAcSwingVert: + result += kSwingVStr; + break; + case kFujitsuAcSwingHoriz: + result += kSwingHStr; + break; + case kFujitsuAcSwingBoth: + result += kSwingVStr; + result += '+'; + result += kSwingHStr; + break; + default: + result += kUnknownStr; + } + result += ')'; + } } result += kCommaSpaceStr; result += kCommandStr; @@ -902,33 +932,36 @@ String IRFujitsuAC::toString(void) const { default: result += kNAStr; } - uint16_t mins = 0; - String type_str = kTimerStr; - switch (model) { - case fujitsu_ac_remote_model_t::ARREB1E: - case fujitsu_ac_remote_model_t::ARREW4E: - result += addBoolToString(getOutsideQuiet(), kOutsideQuietStr); - // FALL THRU - // These models seem to have timer support. - case fujitsu_ac_remote_model_t::ARRAH2E: - switch (getTimerType()) { - case kFujitsuAcOnTimer: - type_str = kOnTimerStr; - mins = getOnTimer(); - break; - case kFujitsuAcOffTimer: - type_str = kOffTimerStr; - mins = getOffSleepTimer(); - break; - case kFujitsuAcSleepTimer: - type_str = kSleepTimerStr; - mins = getOffSleepTimer(); - break; - } - result += addLabeledString(mins ? minsToString(mins) : kOffStr, type_str); - break; - default: - break; + if (_rawstatemodified || isLongCode()) { + uint16_t mins = 0; + String type_str = kTimerStr; + switch (model) { + case fujitsu_ac_remote_model_t::ARREB1E: + case fujitsu_ac_remote_model_t::ARREW4E: + result += addBoolToString(getOutsideQuiet(), kOutsideQuietStr); + // FALL THRU + // These models seem to have timer support. + case fujitsu_ac_remote_model_t::ARRAH2E: + switch (getTimerType()) { + case kFujitsuAcOnTimer: + type_str = kOnTimerStr; + mins = getOnTimer(); + break; + case kFujitsuAcOffTimer: + type_str = kOffTimerStr; + mins = getOffSleepTimer(); + break; + case kFujitsuAcSleepTimer: + type_str = kSleepTimerStr; + mins = getOffSleepTimer(); + break; + } + result += addLabeledString(mins ? minsToString(mins) : kOffStr, + type_str); + break; + default: + break; + } } return result; } diff --git a/src/ir_Fujitsu.h b/src/ir_Fujitsu.h index 70c0a4cf0..09cb9ee83 100644 --- a/src/ir_Fujitsu.h +++ b/src/ir_Fujitsu.h @@ -1,5 +1,5 @@ // Copyright 2017 Jonny Graham -// Copyright 2018-2021 David Conran +// Copyright 2018-2022 David Conran // Copyright 2021 siriuslzx /// @file @@ -206,6 +206,7 @@ class IRFujitsuAC { bool setRaw(const uint8_t newState[], const uint16_t length); uint8_t getStateLength(void); static bool validChecksum(uint8_t* state, const uint16_t length); + bool isLongCode(void) const; void setPower(const bool on); void off(void); void on(void); @@ -233,7 +234,7 @@ class IRFujitsuAC { static uint8_t convertFan(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) const; + stdAc::state_t toCommon(const stdAc::state_t *prev = NULL); String toString(void) const; #ifndef UNIT_TEST @@ -249,6 +250,7 @@ class IRFujitsuAC { fujitsu_ac_remote_model_t _model; uint8_t _state_length; uint8_t _state_length_short; + bool _rawstatemodified; void checkSum(void); bool updateUseLongOrShort(void); void buildFromState(const uint16_t length); diff --git a/test/ir_Fujitsu_test.cpp b/test/ir_Fujitsu_test.cpp index b3d7cff34..638ec6ee4 100644 --- a/test/ir_Fujitsu_test.cpp +++ b/test/ir_Fujitsu_test.cpp @@ -318,9 +318,7 @@ TEST(TestDecodeFujitsuAC, SyntheticShortMessages) { uint8_t expected_arrah2e[7] = {0x14, 0x63, 0x0, 0x10, 0x10, 0x02, 0xFD}; EXPECT_STATE_EQ(expected_arrah2e, irsend.capture.state, irsend.capture.bits); EXPECT_EQ( - "Model: 1 (ARRAH2E), Id: 0, Power: Off, Mode: 0 (Auto), Temp: 16C, " - "Fan: 0 (Auto), Clean: Off, Filter: Off, Swing: 0 (Off), Command: N/A, " - "Timer: Off", + "Model: 1 (ARRAH2E), Id: 0, Power: Off, Command: N/A", IRAcUtils::resultAcToString(&irsend.capture)); stdAc::state_t r, p; ASSERT_TRUE(IRAcUtils::decodeToState(&irsend.capture, &r, &p)); @@ -617,9 +615,7 @@ TEST(TestIRFujitsuACClass, toCommon) { // Now test it. EXPECT_EQ( // Off mode technically has no temp, mode, fan, etc. - "Model: 1 (ARRAH2E), Id: 0, Power: Off, Mode: 0 (Auto), Temp: 16C, " - "Fan: 0 (Auto), Clean: Off, Filter: Off, Swing: 0 (Off), Command: N/A, " - "Timer: Off", + "Model: 1 (ARRAH2E), Id: 0, Power: Off, Command: N/A", ac.toString()); ASSERT_EQ(decode_type_t::FUJITSU_AC, ac.toCommon().protocol); ASSERT_EQ(fujitsu_ac_remote_model_t::ARRAH2E, ac.toCommon().model); @@ -1361,3 +1357,54 @@ TEST(TestIRFujitsuACClass, Discussion1701) { EXPECT_EQ(expected_raw_output, ac._irsend.outputStr()); // Success. } + +TEST(TestIRFujitsuACClass, toCommon_Issue1780HandlePrev) { + IRFujitsuAC ac(kGpioUnused); + ac.setMode(kFujitsuAcModeCool); + ac.setTemp(20); + ac.setFanSpeed(kFujitsuAcFanQuiet); + ac.setSwing(kFujitsuAcSwingBoth); + ac.on(); + ASSERT_TRUE(ac.toCommon().power); + stdAc::state_t prev = ac.toCommon(); // Copy in the state. + ac.off(); + ASSERT_FALSE(ac.toCommon().power); + ac.send(); // This should send a short code. + prev.degrees = 27; + ac.stateReset(); + IRrecv irrecv(kGpioUnused); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&ac._irsend.capture)); + ASSERT_EQ(FUJITSU_AC, ac._irsend.capture.decode_type); + ac.setRaw(ac._irsend.capture.state, ac._irsend.capture.bits / 8); + ASSERT_EQ(decode_type_t::FUJITSU_AC, ac.toCommon().protocol); + ASSERT_EQ(fujitsu_ac_remote_model_t::ARRAH2E, ac.toCommon().model); + ASSERT_FALSE(ac.toCommon().power); + ASSERT_TRUE(ac.toCommon().celsius); + ASSERT_EQ(16, ac.toCommon().degrees); + ASSERT_EQ(27, ac.toCommon(&prev).degrees); + ASSERT_FALSE(ac.toCommon().quiet); + + ASSERT_EQ(stdAc::opmode_t::kAuto, ac.toCommon().mode); + ASSERT_EQ(stdAc::opmode_t::kCool, ac.toCommon(&prev).mode); + ASSERT_EQ(stdAc::fanspeed_t::kAuto, ac.toCommon().fanspeed); + ASSERT_EQ(stdAc::fanspeed_t::kMin, ac.toCommon(&prev).fanspeed); + ASSERT_EQ(stdAc::swingv_t::kOff, ac.toCommon().swingv); + ASSERT_EQ(stdAc::swingh_t::kOff, ac.toCommon().swingh); + // Unsupported. + ASSERT_FALSE(ac.toCommon().filter); + ASSERT_FALSE(ac.toCommon().clean); + ASSERT_FALSE(ac.toCommon().turbo); + ASSERT_FALSE(ac.toCommon().light); + ASSERT_FALSE(ac.toCommon().econo); + ASSERT_FALSE(ac.toCommon().beep); + ASSERT_EQ(-1, ac.toCommon().sleep); + ASSERT_EQ(-1, ac.toCommon().clock); + + stdAc::state_t result_inc_prev; + ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &result_inc_prev, + &prev)); + ASSERT_EQ(27, result_inc_prev.degrees); + ASSERT_EQ(stdAc::opmode_t::kCool, result_inc_prev.mode); + ASSERT_EQ(stdAc::fanspeed_t::kMin, result_inc_prev.fanspeed); +}