diff --git a/src/IRac.cpp b/src/IRac.cpp index 650037de0..de07bf964 100644 --- a/src/IRac.cpp +++ b/src/IRac.cpp @@ -230,6 +230,9 @@ bool IRac::isProtocolSupported(const decode_type_t protocol) { #if SEND_HITACHI_AC264 case decode_type_t::HITACHI_AC264: #endif +#if SEND_HITACHI_AC296 + case decode_type_t::HITACHI_AC296: +#endif #if SEND_HITACHI_AC344 case decode_type_t::HITACHI_AC344: #endif @@ -1341,6 +1344,35 @@ void IRac::hitachi264(IRHitachiAc264 *ac, } #endif // SEND_HITACHI_AC264 +#if SEND_HITACHI_AC296 +/// Send a Hitachi 296-bit A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRHitachiAc296 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::hitachi296(IRHitachiAc296 *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan) { + ac->begin(); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setPower(on); + // No Swing(V) setting available. + // No Swing(H) 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_HITACHI_AC296 + #if SEND_HITACHI_AC344 /// Send a Hitachi 344-bit A/C message with the supplied settings. /// @param[in, out] ac A Ptr to an IRHitachiAc344 object to use. @@ -2946,6 +2978,14 @@ bool IRac::sendAc(const stdAc::state_t desired, const stdAc::state_t *prev) { break; } #endif // SEND_HITACHI_AC264 +#if SEND_HITACHI_AC296 + case HITACHI_AC296: + { + IRHitachiAc296 ac(_pin, _inverted, _modulation); + hitachi296(&ac, send.power, send.mode, degC, send.fanspeed); + break; + } +#endif // SEND_HITACHI_AC296 #if SEND_HITACHI_AC344 case HITACHI_AC344: { @@ -3808,6 +3848,13 @@ namespace IRAcUtils { return ac.toString(); } #endif // DECODE_HITACHI_AC264 +#if DECODE_HITACHI_AC296 + case decode_type_t::HITACHI_AC296: { + IRHitachiAc296 ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_HITACHI_AC296 #if DECODE_HITACHI_AC344 case decode_type_t::HITACHI_AC344: { IRHitachiAc344 ac(kGpioUnused); @@ -4273,6 +4320,14 @@ namespace IRAcUtils { break; } #endif // DECODE_HITACHI_AC264 +#if DECODE_HITACHI_AC296 + case decode_type_t::HITACHI_AC296: { + IRHitachiAc296 ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_HITACHI_AC296 #if DECODE_HITACHI_AC344 case decode_type_t::HITACHI_AC344: { IRHitachiAc344 ac(kGpioUnused); diff --git a/src/IRac.h b/src/IRac.h index 814a3a35a..8e3ef7582 100644 --- a/src/IRac.h +++ b/src/IRac.h @@ -305,6 +305,11 @@ void electra(IRElectraAc *ac, const bool on, const stdAc::opmode_t mode, const float degrees, const stdAc::fanspeed_t fan); #endif // SEND_HITACHI_AC264 +#if SEND_HITACHI_AC296 + void hitachi296(IRHitachiAc296 *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan); +#endif // SEND_HITACHI_AC296 #if SEND_HITACHI_AC344 void hitachi344(IRHitachiAc344 *ac, const bool on, const stdAc::opmode_t mode, diff --git a/src/ir_Hitachi.cpp b/src/ir_Hitachi.cpp index b80430424..e47bac929 100644 --- a/src/ir_Hitachi.cpp +++ b/src/ir_Hitachi.cpp @@ -1723,10 +1723,15 @@ void IRsend::sendHitachiAc296(const unsigned char data[], } #endif // SEND_HITACHIAC296 +// Class constructor for handling detailed Hitachi_AC296 37 byte A/C messages. +/// @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? IRHitachiAc296::IRHitachiAc296(const uint16_t pin, const bool inverted, const bool use_modulation) : _irsend(pin, inverted, use_modulation) { stateReset(); } +/// Reset the internal state to auto fan, heating, & 24° Celsius void IRHitachiAc296::stateReset(void) { // Header _.raw[0] = 0x01; @@ -1785,18 +1790,13 @@ void IRHitachiAc296::send(const uint16_t repeat) { } #endif // SEND_HITACHI_AC296 - /// Get the value of the current power setting. /// @return true, the setting is on. false, the setting is off. -bool IRHitachiAc296::getPower(void) const { - return _.Power; -} +bool IRHitachiAc296::getPower(void) const { return _.Power; } /// Change the power setting. /// @param[in] on true, the setting is on. false, the setting is off. -void IRHitachiAc296::setPower(const bool on) { - _.Power = on; -} +void IRHitachiAc296::setPower(const bool on) { _.Power = on; } /// Change the power setting to On. void IRHitachiAc296::on(void) { setPower(true); } @@ -1806,52 +1806,106 @@ void IRHitachiAc296::off(void) { setPower(false); } /// Get the operating mode setting of the A/C. /// @return The current operating mode setting. -uint8_t IRHitachiAc296::getMode(void) const { - return _.Mode; -} +uint8_t IRHitachiAc296::getMode(void) const { return _.Mode; } /// Set the operating mode of the A/C. /// @param[in] mode The desired operating mode. void IRHitachiAc296::setMode(const uint8_t mode) { - uint8_t newMode = mode; switch (mode) { case kHitachiAc296Heat: case kHitachiAc296Cool: - case kHitachiAc296Auto: break; - default: newMode = kHitachiAc296Auto; + case kHitachiAc296Dehumidify: + case kHitachiAc296AutoDehumidifying: + case kHitachiAc296Auto: + _.Mode = mode; + setTemp(getTemp()); // Reset the temp to handle "Auto"'s special temp. + break; + default: + setMode(kHitachiAc296Auto); } +} - _.Mode = newMode; +/// Convert a stdAc::opmode_t enum into its native mode. +/// @param[in] mode The enum to be converted. +/// @return The native equivalent of the enum. +uint8_t IRHitachiAc296::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: return kHitachiAc296Cool; + case stdAc::opmode_t::kHeat: return kHitachiAc296Heat; + case stdAc::opmode_t::kDry: return kHitachiAc296Dehumidify; + default: return kHitachiAc296Auto; + } +} + +/// Convert a native mode into its stdAc equivalent. +/// @param[in] mode The native setting to be converted. +/// @return The stdAc equivalent of the native setting. +stdAc::opmode_t IRHitachiAc296::toCommonMode(const uint8_t mode) { + switch (mode) { + case kHitachiAc296DryCool: + case kHitachiAc296Cool: return stdAc::opmode_t::kCool; + case kHitachiAc296Heat: return stdAc::opmode_t::kHeat; + case kHitachiAc296AutoDehumidifying: + case kHitachiAc296Dehumidify: return stdAc::opmode_t::kDry; + default: return stdAc::opmode_t::kAuto; + } } /// Get the current temperature setting. /// @return The current setting for temp. in degrees celsius. -uint8_t IRHitachiAc296::getTemp(void) const { - return _.Temp; -} +uint8_t IRHitachiAc296::getTemp(void) const { return _.Temp; } /// Set the temperature. /// @param[in] celsius The temperature in degrees celsius. void IRHitachiAc296::setTemp(const uint8_t celsius) { - uint8_t temp; - temp = std::min(celsius, kHitachiAc296MaxTemp); - _.Temp = std::max(temp, kHitachiAc296MinTemp); + uint8_t temp = celsius; + if (getMode() == kHitachiAc296Auto) { // Special temp for auto mode + temp = kHitachiAc296TempAuto; + } else { // Normal temp setting. + temp = std::min(temp, kHitachiAc296MaxTemp); + temp = std::max(temp, kHitachiAc296MinTemp); + } + _.Temp = temp; } /// Get the current fan speed setting. /// @return The current fan speed. -uint8_t IRHitachiAc296::getFan(void) const { - return _.Fan; -} +uint8_t IRHitachiAc296::getFan(void) const { return _.Fan; } /// Set the speed of the fan. /// @param[in] speed The desired setting. void IRHitachiAc296::setFan(const uint8_t speed) { - uint8_t newSpeed = speed; - newSpeed = std::max(newSpeed, kHitachiAc296FanSilent); + uint8_t newSpeed = std::max(speed, kHitachiAc296FanSilent); _.Fan = std::min(newSpeed, kHitachiAc296FanAuto); } +/// Convert a stdAc::fanspeed_t enum into it's native speed. +/// @param[in] speed The enum to be converted. +/// @return The native equivalent of the enum. +uint8_t IRHitachiAc296::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: return kHitachiAc296FanSilent; + case stdAc::fanspeed_t::kLow: return kHitachiAc296FanLow; + case stdAc::fanspeed_t::kMedium: return kHitachiAc296FanMedium; + case stdAc::fanspeed_t::kHigh: + case stdAc::fanspeed_t::kMax: return kHitachiAc296FanHigh; + default: return kHitachiAc296FanAuto; + } +} + +/// Convert a native fan speed into its stdAc equivalent. +/// @param[in] speed The native setting to be converted. +/// @return The stdAc equivalent of the native setting. +stdAc::fanspeed_t IRHitachiAc296::toCommonFanSpeed(const uint8_t speed) { + switch (speed) { + case kHitachiAc296FanHigh: return stdAc::fanspeed_t::kHigh; + case kHitachiAc296FanMedium: return stdAc::fanspeed_t::kMedium; + case kHitachiAc296FanLow: return stdAc::fanspeed_t::kLow; + case kHitachiAc296FanSilent: return stdAc::fanspeed_t::kMin; + default: return stdAc::fanspeed_t::kAuto; + } +} + /// 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 *IRHitachiAc296::getRaw(void) { @@ -1866,6 +1920,49 @@ void IRHitachiAc296::setRaw(const uint8_t new_code[], const uint16_t length) { memcpy(_.raw, new_code, std::min(length, kHitachiAc296StateLength)); } + +/// Convert the current internal state into its stdAc::state_t equivalent. +/// @return The stdAc equivalent of the native settings. +stdAc::state_t IRHitachiAc296::toCommon(void) const { + stdAc::state_t result{}; + result.protocol = decode_type_t::HITACHI_AC296; + result.model = -1; // No models used. + result.power = getPower(); + result.mode = toCommonMode(_.Mode); + result.celsius = true; + result.degrees = _.Temp; + result.fanspeed = toCommonFanSpeed(_.Fan); + result.quiet = _.Fan == kHitachiAc296FanSilent; + // Not supported. + result.swingv = stdAc::swingv_t::kOff; + result.swingh = stdAc::swingh_t::kOff; + result.turbo = false; + result.clean = false; + result.econo = false; + result.filter = false; + result.light = 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 IRHitachiAc296::toString(void) const { + String result = ""; + result.reserve(70); // Reserve some heap for the string to reduce fragging. + result += addBoolToString(_.Power, kPowerStr, false); + result += addModeToString(_.Mode, kHitachiAc296Auto, kHitachiAc296Cool, + kHitachiAc296Heat, kHitachiAc1Dry, + kHitachiAc296Auto); + result += addTempToString(getTemp()); + result += addFanToString(_.Fan, kHitachiAc296FanHigh, kHitachiAc296FanLow, + kHitachiAc296FanAuto, kHitachiAc296FanSilent, + kHitachiAc296FanMedium); + return result; +} + #if DECODE_HITACHI_AC296 /// Decode the supplied Hitachi 37-byte A/C message. /// Status: STABLE / Working on a real device. @@ -1879,14 +1976,13 @@ void IRHitachiAc296::setRaw(const uint8_t new_code[], const uint16_t length) { bool IRrecv::decodeHitachiAc296(decode_results *results, uint16_t offset, const uint16_t nbits, const bool strict) { - uint16_t used = matchGeneric(results->rawbuf + offset, results->state, - results->rawlen - offset, nbits, - kHitachiAcHdrMark, kHitachiAcHdrSpace, - kHitachiAcBitMark, kHitachiAcOneSpace, - kHitachiAcBitMark, kHitachiAcZeroSpace, - kHitachiAcBitMark, kHitachiAcMinGap, true, - kUseDefTol, 0, false); - if (used == 0) return false; + if (!matchGeneric(results->rawbuf + offset, results->state, + results->rawlen - offset, nbits, + kHitachiAcHdrMark, kHitachiAcHdrSpace, + kHitachiAcBitMark, kHitachiAcOneSpace, + kHitachiAcBitMark, kHitachiAcZeroSpace, + kHitachiAcBitMark, kHitachiAcMinGap, true, + kUseDefTol, 0, false)) return false; // Compliance if (strict && !IRHitachiAc296::hasInvertedStates(results->state, nbits / 8)) diff --git a/src/ir_Hitachi.h b/src/ir_Hitachi.h index 8c58b2974..413ab75e8 100644 --- a/src/ir_Hitachi.h +++ b/src/ir_Hitachi.h @@ -358,9 +358,9 @@ const uint8_t kHitachiAc296FanMedium = 0b011; const uint8_t kHitachiAc296FanHigh = 0b100; const uint8_t kHitachiAc296FanAuto = 0b101; -const uint8_t kHitachiAc296TempSize = 5; +const uint8_t kHitachiAc296TempAuto = 1; // Special value for "Auto" op mode. const uint8_t kHitachiAc296MinTemp = 16; -const uint8_t kHitachiAc296MaxTemp = 32; +const uint8_t kHitachiAc296MaxTemp = 31; // Max value you can store in 5 bits. const uint8_t kHitachiAc296PowerOn = 1; const uint8_t kHitachiAc296PowerOff = 0; @@ -629,7 +629,7 @@ class IRHitachiAc296 { #if SEND_HITACHI_AC296 void send(const uint16_t repeat = kHitachiAcDefaultRepeat); -#endif +#endif // SEND_HITACHI_AC296 void begin(void); void on(void); void off(void); @@ -645,7 +645,12 @@ class IRHitachiAc296 { uint8_t* getRaw(void); void setRaw(const uint8_t new_code[], const uint16_t length = kHitachiAc296StateLength); - + static uint8_t convertMode(const stdAc::opmode_t mode); + static 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) const; + String toString(void) const; #ifndef UNIT_TEST private: diff --git a/test/IRac_test.cpp b/test/IRac_test.cpp index 59e1186a7..1947ea532 100644 --- a/test/IRac_test.cpp +++ b/test/IRac_test.cpp @@ -973,6 +973,34 @@ TEST(TestIRac, Hitachi264) { EXPECT_EQ(25, r.degrees); } +TEST(TestIRac, Hitachi296) { + IRHitachiAc296 ac(kGpioUnused); + IRac irac(kGpioUnused); + IRrecv capture(kGpioUnused); + char expected[] = + "Power: On, Mode: 6 (Heat), Temp: 20C, Fan: 2 (Low)"; + + ac.begin(); + irac.hitachi296(&ac, + true, // Power + stdAc::opmode_t::kHeat, // Mode + 20, // Celsius + stdAc::fanspeed_t::kLow); // Fan speed + + ASSERT_EQ(expected, ac.toString()); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(HITACHI_AC296, ac._irsend.capture.decode_type); + ASSERT_EQ(kHitachiAc296Bits, 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(decode_type_t::HITACHI_AC296, r.protocol); + EXPECT_TRUE(r.power); + EXPECT_EQ(stdAc::opmode_t::kHeat, r.mode); + EXPECT_EQ(20, r.degrees); +} + TEST(TestIRac, Hitachi344) { IRHitachiAc344 ac(kGpioUnused); IRac irac(kGpioUnused); diff --git a/test/ir_Hitachi_test.cpp b/test/ir_Hitachi_test.cpp index 5313fb14b..51664688a 100644 --- a/test/ir_Hitachi_test.cpp +++ b/test/ir_Hitachi_test.cpp @@ -191,7 +191,7 @@ TEST(TestSendHitachiAC, SendUnexpectedSizes) { // Tests for IRHitachiAc class. TEST(TestIRHitachiAcClass, SetAndGetPower) { - IRHitachiAc ac(0); + IRHitachiAc ac(kGpioUnused); ac.on(); EXPECT_TRUE(ac.getPower()); ac.off(); @@ -203,7 +203,7 @@ TEST(TestIRHitachiAcClass, SetAndGetPower) { } TEST(TestIRHitachiAcClass, SetAndGetSwing) { - IRHitachiAc ac(0); + IRHitachiAc ac(kGpioUnused); ac.setSwingVertical(true); ac.setSwingHorizontal(true); EXPECT_TRUE(ac.getSwingVertical()); @@ -222,7 +222,7 @@ TEST(TestIRHitachiAcClass, SetAndGetSwing) { } TEST(TestIRHitachiAcClass, SetAndGetTemp) { - IRHitachiAc ac(0); + IRHitachiAc ac(kGpioUnused); ac.setTemp(25); EXPECT_EQ(25, ac.getTemp()); ac.setTemp(kHitachiAcMinTemp); @@ -238,7 +238,7 @@ TEST(TestIRHitachiAcClass, SetAndGetTemp) { } TEST(TestIRHitachiAcClass, SetAndGetMode) { - IRHitachiAc ac(0); + IRHitachiAc ac(kGpioUnused); ac.setMode(kHitachiAcCool); ac.setFan(kHitachiAcFanAuto); EXPECT_EQ(kHitachiAcCool, ac.getMode()); @@ -261,7 +261,7 @@ TEST(TestIRHitachiAcClass, SetAndGetMode) { } TEST(TestIRHitachiAcClass, SetAndGetFan) { - IRHitachiAc ac(0); + IRHitachiAc ac(kGpioUnused); ac.setMode(kHitachiAcCool); // All fan options are available in this mode. ac.setFan(kHitachiAcFanAuto); EXPECT_EQ(kHitachiAcFanAuto, ac.getFan()); @@ -293,7 +293,7 @@ TEST(TestIRHitachiAcClass, SetAndGetFan) { } TEST(TestIRHitachiAcClass, HumanReadable) { - IRHitachiAc ac(0); + IRHitachiAc ac(kGpioUnused); ac.setMode(kHitachiAcHeat); ac.setTemp(kHitachiAcMaxTemp); @@ -316,7 +316,7 @@ TEST(TestIRHitachiAcClass, HumanReadable) { } TEST(TestIRHitachiAcClass, ChecksumCalculation) { - IRHitachiAc ac(0); + IRHitachiAc ac(kGpioUnused); const uint8_t originalstate[kHitachiAcStateLength] = { 0x80, 0x08, 0x0C, 0x02, 0xFD, 0x80, 0x7F, 0x88, 0x48, 0x80, @@ -426,7 +426,7 @@ TEST(TestDecodeHitachiAC, NormalRealExample1) { EXPECT_EQ(HITACHI_AC, irsend.capture.decode_type); ASSERT_EQ(kHitachiAcBits, irsend.capture.bits); EXPECT_STATE_EQ(hitachi_code, irsend.capture.state, kHitachiAcBits); - IRHitachiAc ac(0); + IRHitachiAc ac(kGpioUnused); ac.setRaw(irsend.capture.state); EXPECT_EQ( "Power: On, Mode: 4 (Cool), Temp: 16C, Fan: 1 (Auto), " @@ -494,7 +494,7 @@ TEST(TestDecodeHitachiAC, NormalRealExample2) { EXPECT_EQ(HITACHI_AC, irsend.capture.decode_type); ASSERT_EQ(kHitachiAcBits, irsend.capture.bits); EXPECT_STATE_EQ(hitachi_code, irsend.capture.state, kHitachiAcBits); - IRHitachiAc ac(0); + IRHitachiAc ac(kGpioUnused); ac.setRaw(irsend.capture.state); EXPECT_EQ( "Power: On, Mode: 3 (Heat), Temp: 32C, Fan: 5 (High), " @@ -777,7 +777,7 @@ TEST(TestDecodeHitachiAC2, NormalRealExample) { } TEST(TestIRHitachiAcClass, toCommon) { - IRHitachiAc ac(0); + IRHitachiAc ac(kGpioUnused); ac.setPower(true); ac.setMode(kHitachiAcCool); ac.setTemp(20); @@ -865,6 +865,15 @@ TEST(TestUtils, Housekeeping) { IRsend::defaultBits(decode_type_t::HITACHI_AC264)); ASSERT_EQ(kNoRepeat, IRsend::minRepeats(decode_type_t::HITACHI_AC264)); + + ASSERT_EQ("HITACHI_AC296", typeToString(decode_type_t::HITACHI_AC296)); + ASSERT_EQ(decode_type_t::HITACHI_AC296, strToDecodeType("HITACHI_AC296")); + ASSERT_TRUE(hasACState(decode_type_t::HITACHI_AC296)); + ASSERT_TRUE(IRac::isProtocolSupported(decode_type_t::HITACHI_AC296)); + ASSERT_EQ(kHitachiAc296Bits, + IRsend::defaultBits(decode_type_t::HITACHI_AC296)); + ASSERT_EQ(kNoRepeat, + IRsend::minRepeats(decode_type_t::HITACHI_AC296)); } // Decode a 'real' HitachiAc424 message. @@ -954,7 +963,7 @@ TEST(TestDecodeHitachiAc424, RealExample) { EXPECT_EQ(HITACHI_AC424, irsend.capture.decode_type); ASSERT_EQ(kHitachiAc424Bits, irsend.capture.bits); EXPECT_STATE_EQ(expected, irsend.capture.state, kHitachiAc424Bits); - IRHitachiAc ac(0); + IRHitachiAc ac(kGpioUnused); ac.setRaw(irsend.capture.state); EXPECT_EQ( "Power: On, Mode: 3 (Cool), Temp: 23C, Fan: 5 (Auto), " @@ -988,7 +997,7 @@ TEST(TestDecodeHitachiAc424, SyntheticExample) { // Tests for IRHitachiAc424 class. TEST(TestIRHitachiAc424Class, SetInvertedStates) { - IRHitachiAc424 ac(0); + IRHitachiAc424 ac(kGpioUnused); uint8_t raw[kHitachiAc424StateLength] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -1010,7 +1019,7 @@ TEST(TestIRHitachiAc424Class, SetInvertedStates) { } TEST(TestIRHitachiAc424Class, SetAndGetPower) { - IRHitachiAc424 ac(0); + IRHitachiAc424 ac(kGpioUnused); ac.on(); EXPECT_TRUE(ac.getPower()); ac.off(); @@ -1024,7 +1033,7 @@ TEST(TestIRHitachiAc424Class, SetAndGetPower) { } TEST(TestIRHitachiAc424Class, SetAndGetTemp) { - IRHitachiAc424 ac(0); + IRHitachiAc424 ac(kGpioUnused); ac.setTemp(25); EXPECT_EQ(25, ac.getTemp()); ac.setTemp(kHitachiAc424MinTemp); @@ -1038,7 +1047,7 @@ TEST(TestIRHitachiAc424Class, SetAndGetTemp) { } TEST(TestIRHitachiAc424Class, SetAndGetMode) { - IRHitachiAc424 ac(0); + IRHitachiAc424 ac(kGpioUnused); ac.setMode(kHitachiAc424Cool); ac.setFan(kHitachiAc424FanAuto); ac.setTemp(25); @@ -1058,7 +1067,7 @@ TEST(TestIRHitachiAc424Class, SetAndGetMode) { } TEST(TestIRHitachiAc424Class, SetAndGetFan) { - IRHitachiAc424 ac(0); + IRHitachiAc424 ac(kGpioUnused); ac.setMode(kHitachiAc424Cool); // All fan options are available in this mode. ac.setFan(kHitachiAc424FanAuto); EXPECT_EQ(kHitachiAc424FanAuto, ac.getFan()); @@ -1103,7 +1112,7 @@ TEST(TestIRHitachiAc424Class, SetAndGetFan) { TEST(TestIRHitachiAc424Class, SetAndGetButton) { - IRHitachiAc424 ac(0); + IRHitachiAc424 ac(kGpioUnused); ac.on(); EXPECT_EQ(ac.getButton(), kHitachiAc424ButtonPowerMode); ac.setButton(kHitachiAc424ButtonTempUp); @@ -1113,7 +1122,7 @@ TEST(TestIRHitachiAc424Class, SetAndGetButton) { } TEST(TestIRHitachiAc424Class, ToggleSwingVertical) { - IRHitachiAc424 ac(0); + IRHitachiAc424 ac(kGpioUnused); ac.on(); EXPECT_EQ(ac.getButton(), kHitachiAc424ButtonPowerMode); ac.setSwingVToggle(true); @@ -1125,7 +1134,7 @@ TEST(TestIRHitachiAc424Class, ToggleSwingVertical) { } TEST(TestIRHitachiAc424Class, HumanReadable) { - IRHitachiAc424 ac(0); + IRHitachiAc424 ac(kGpioUnused); ac.setMode(kHitachiAc424Heat); ac.setTemp(kHitachiAc424MaxTemp); @@ -2231,3 +2240,172 @@ TEST(TestIRHitachiAc264Class, Issue1729_PowerOntoOff) { EXPECT_FALSE(irac.getState().power); EXPECT_FALSE(irac.next.power); } + +// Decode a 'real' HitachiAc296 message. +TEST(TestDecodeHitachiAc296, RealExample) { + IRsendTest irsend(kGpioUnused); + IRrecv irrecv(kGpioUnused); + irsend.begin(); + + const uint8_t expected[kHitachiAc296StateLength] = { + 0x01, 0x10, 0x00, 0x40, 0xBF, 0xFF, 0x00, 0xCC, 0x33, 0x92, + 0x6D, 0x44, 0xBB, 0x04, 0xFB, 0x00, 0xFF, 0x00, 0xFF, 0x00, + 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x57, 0xA8, 0xF1, 0x0E, 0x00, + 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x03, 0xFC}; + + // Ref: https://github.com/crankyoldgit/IRremoteESP8266/pull/1758#issuecomment-1066017940 + // Power: ON, Temp: -- (not used in auto mode), Fan: Auto, Mode: Auto + const uint16_t rawData[595] = { + 3298, 1712, 358, 1278, 362, 516, 356, 518, 358, 514, 358, 518, 358, 516, + 360, 516, 358, 520, 362, 514, 358, 516, 360, 514, 362, 514, 360, 1280, + 358, 518, 358, 514, 362, 518, 360, 516, 358, 516, 358, 516, 360, 516, 358, + 516, 358, 518, 358, 516, 360, 518, 362, 516, 358, 514, 360, 514, 360, 516, + 360, 516, 358, 516, 362, 1280, 356, 520, 360, 1276, 362, 1276, 360, 1282, + 360, 1280, 356, 1280, 358, 1278, 362, 514, 362, 1280, 360, 1280, 360, + 1278, 358, 1280, 358, 1280, 358, 1278, 360, 1280, 358, 1278, 360, 1282, + 360, 514, 360, 514, 362, 516, 356, 516, 362, 514, 360, 516, 358, 516, 358, + 520, 360, 516, 358, 516, 362, 1276, 360, 1278, 360, 514, 358, 516, 360, + 1278, 360, 1282, 360, 1276, 362, 1278, 360, 514, 358, 518, 358, 1278, 360, + 1278, 360, 514, 360, 518, 360, 516, 358, 1280, 358, 516, 360, 516, 360, + 1278, 360, 518, 356, 516, 360, 1282, 360, 1278, 362, 514, 360, 1278, 360, + 1278, 360, 516, 358, 1278, 360, 1280, 358, 520, 358, 516, 358, 516, 360, + 1276, 360, 516, 358, 514, 360, 514, 364, 1276, 358, 522, 358, 1278, 360, + 1280, 358, 516, 360, 1278, 360, 1276, 360, 1278, 362, 514, 358, 1286, 358, + 514, 358, 514, 362, 1278, 360, 514, 360, 518, 358, 516, 358, 518, 358, + 518, 360, 1276, 360, 1278, 362, 514, 360, 1276, 360, 1276, 362, 1280, 358, + 1276, 360, 1284, 358, 516, 358, 516, 360, 516, 360, 514, 360, 516, 358, + 516, 360, 518, 356, 518, 358, 1280, 358, 1278, 362, 1276, 360, 1280, 360, + 1276, 360, 1278, 362, 1278, 358, 1282, 358, 516, 358, 518, 358, 514, 360, + 516, 358, 516, 360, 514, 360, 514, 358, 520, 360, 1278, 360, 1280, 358, + 1280, 360, 1278, 360, 1278, 358, 1278, 360, 1278, 362, 1282, 360, 516, + 360, 516, 358, 516, 360, 518, 356, 516, 358, 516, 362, 514, 358, 520, 358, + 1280, 358, 1278, 362, 1276, 362, 1276, 360, 1276, 360, 1278, 360, 1278, + 360, 1282, 360, 514, 362, 514, 360, 516, 360, 514, 360, 516, 360, 516, + 360, 516, 358, 520, 362, 1278, 358, 1278, 362, 1276, 360, 1280, 358, 1276, + 362, 1276, 362, 1278, 360, 1284, 358, 516, 358, 514, 360, 516, 360, 514, + 360, 516, 360, 512, 362, 514, 360, 518, 360, 1276, 362, 1276, 362, 1278, + 358, 1280, 360, 1278, 360, 1278, 360, 1276, 362, 1282, 360, 1278, 362, + 1278, 362, 1276, 362, 512, 360, 1278, 362, 514, 360, 1278, 358, 520, 358, + 516, 360, 514, 360, 516, 358, 1278, 362, 514, 360, 1278, 358, 516, 362, + 1282, 360, 1276, 360, 516, 360, 516, 358, 518, 358, 1276, 362, 1278, 360, + 1278, 360, 1284, 358, 516, 360, 1278, 362, 1276, 360, 1278, 360, 516, 360, + 512, 362, 512, 362, 518, 362, 514, 362, 514, 358, 514, 360, 514, 360, 518, + 358, 514, 358, 516, 360, 518, 360, 1278, 360, 1278, 360, 1276, 362, 1276, + 362, 1276, 362, 1278, 358, 1278, 362, 1284, 358, 514, 362, 512, 360, 514, + 362, 516, 360, 514, 360, 514, 360, 512, 362, 520, 360, 1276, 362, 1278, + 362, 1276, 364, 1274, 360, 1278, 362, 1278, 360, 1278, 360, 1282, 362, + 512, 360, 514, 362, 512, 362, 514, 360, 516, 362, 512, 362, 516, 360, 518, + 360, 1278, 360, 1276, 362, 1278, 360, 1274, 364, 1276, 362, 1276, 360, + 1278, 360, 1280, 362, 1276, 362, 1276, 364, 512, 360, 514, 362, 512, 362, + 512, 362, 512, 362, 520, 358, 516, 360, 514, 360, 1276, 360, 1276, 362, + 1276, 362, 1278, 360, 1278, 360, 1280, 362}; // HITACHI_AC296 + + irsend.reset(); + irsend.sendRaw(rawData, 595, kHitachiAcFreq); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(HITACHI_AC296, irsend.capture.decode_type); + ASSERT_EQ(kHitachiAc296Bits, irsend.capture.bits); + EXPECT_STATE_EQ(expected, irsend.capture.state, irsend.capture.bits); + EXPECT_EQ( + "Power: On, Mode: 7 (Auto), Temp: 1C, Fan: 5 (Auto)", + IRAcUtils::resultAcToString(&irsend.capture)); + stdAc::state_t r, p; + ASSERT_TRUE(IRAcUtils::decodeToState(&irsend.capture, &r, &p)); +} + +// Decode a 'Synthetic' HitachiAc296 message. +TEST(TestDecodeHitachiAc296, SyntheticExample) { + IRsendTest irsend(kGpioUnused); + IRrecv irrecv(kGpioUnused); + irsend.begin(); + + // Ref: https://github.com/crankyoldgit/IRremoteESP8266/pull/1758#issuecomment-1066017940 + // Power: ON, Temp: 24, Fan: Quiet (1), Mode: Cool + const uint8_t expected[kHitachiAc296StateLength] = { + 0x01, 0x10, 0x00, 0x40, 0xBF, 0xFF, 0x00, 0xCC, 0x33, 0x98, + 0x67, 0x42, 0xBD, 0x60, 0x9F, 0x00, 0xFF, 0x00, 0xFF, 0x00, + 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x13, 0xEC, 0xF1, 0x0E, 0x00, + 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x03, 0xFC}; + + irsend.reset(); + irsend.sendHitachiAc296(expected); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(HITACHI_AC296, irsend.capture.decode_type); + ASSERT_EQ(kHitachiAc296Bits, irsend.capture.bits); + EXPECT_STATE_EQ(expected, irsend.capture.state, kHitachiAc296Bits); + EXPECT_EQ( + "Power: On, Mode: 3 (Cool), Temp: 24C, Fan: 1 (Quiet)", + IRAcUtils::resultAcToString(&irsend.capture)); + stdAc::state_t r, p; + ASSERT_TRUE(IRAcUtils::decodeToState(&irsend.capture, &r, &p)); +} + +TEST(TestIRHitachiAc296Class, SetAndGetPower) { + IRHitachiAc296 ac(kGpioUnused); + 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(TestIRHitachiAc296Class, SetAndGetTemp) { + IRHitachiAc296 ac(kGpioUnused); + ac.setTemp(25); + EXPECT_EQ(25, ac.getTemp()); + ac.setTemp(kHitachiAc296MinTemp); + EXPECT_EQ(kHitachiAc296MinTemp, ac.getTemp()); + ac.setTemp(kHitachiAc296MinTemp - 1); + EXPECT_EQ(kHitachiAc296MinTemp, ac.getTemp()); + ac.setTemp(kHitachiAc296MaxTemp); + EXPECT_EQ(kHitachiAc296MaxTemp, ac.getTemp()); + ac.setTemp(kHitachiAc296MaxTemp + 1); + EXPECT_EQ(kHitachiAc296MaxTemp, ac.getTemp()); + // Check the handling of the special temp value for auto operation mode, + ac.setMode(kHitachiAc296Cool); + ac.setTemp(kHitachiAc296TempAuto); + EXPECT_EQ(kHitachiAc296MinTemp, ac.getTemp()); + ac.setMode(kHitachiAc296Auto); + EXPECT_EQ(kHitachiAc296TempAuto, ac.getTemp()); + ac.setTemp(kHitachiAc296TempAuto); + EXPECT_EQ(kHitachiAc296TempAuto, ac.getTemp()); + ac.setTemp(kHitachiAc296MinTemp); + EXPECT_EQ(kHitachiAc296TempAuto, ac.getTemp()); + ac.setMode(kHitachiAc296Cool); + EXPECT_NE(kHitachiAc296TempAuto, ac.getTemp()); +} + +TEST(TestIRHitachiAc296Class, SetAndGetMode) { + IRHitachiAc296 ac(kGpioUnused); + ac.setMode(kHitachiAc296Cool); + ac.setFan(kHitachiAc296FanAuto); + ac.setTemp(25); + EXPECT_EQ(25, ac.getTemp()); + EXPECT_EQ(kHitachiAc296Cool, ac.getMode()); + EXPECT_EQ(kHitachiAc296FanAuto, ac.getFan()); + ac.setMode(kHitachiAc296Heat); + EXPECT_EQ(25, ac.getTemp()); + EXPECT_EQ(kHitachiAc296Heat, ac.getMode()); + ac.setMode(kHitachiAc296Dehumidify); + EXPECT_EQ(kHitachiAc296Dehumidify, ac.getMode()); +} + +TEST(TestIRHitachiAc296Class, SetAndGetFan) { + IRHitachiAc296 ac(kGpioUnused); + ac.setMode(kHitachiAc296Cool); + ac.setFan(kHitachiAc296FanAuto); + EXPECT_EQ(kHitachiAc296FanAuto, ac.getFan()); + ac.setFan(kHitachiAc296FanLow); + EXPECT_EQ(kHitachiAc296FanLow, ac.getFan()); + ac.setFan(kHitachiAc296FanHigh); + EXPECT_EQ(kHitachiAc296FanHigh, ac.getFan()); + ac.setFan(kHitachiAc296FanAuto + 1); + EXPECT_EQ(kHitachiAc296FanAuto, ac.getFan()); + ac.setFan(kHitachiAc296FanSilent - 1); + EXPECT_EQ(kHitachiAc296FanSilent, ac.getFan()); +}