diff --git a/src/IRac.cpp b/src/IRac.cpp index 35ff4e535..92a52a7dd 100644 --- a/src/IRac.cpp +++ b/src/IRac.cpp @@ -231,6 +231,9 @@ bool IRac::isProtocolSupported(const decode_type_t protocol) { #if SEND_HITACHI_AC1 case decode_type_t::HITACHI_AC1: #endif +#if SEND_HITACHI_AC264 + case decode_type_t::HITACHI_AC264: +#endif #if SEND_HITACHI_AC344 case decode_type_t::HITACHI_AC344: #endif @@ -1313,6 +1316,35 @@ void IRac::hitachi1(IRHitachiAc1 *ac, const hitachi_ac1_remote_model_t model, } #endif // SEND_HITACHI_AC1 +#if SEND_HITACHI_AC264 +/// Send a Hitachi 264-bit A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRHitachiAc264 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::hitachi264(IRHitachiAc264 *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_AC264 + #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. @@ -2903,6 +2935,14 @@ bool IRac::sendAc(const stdAc::state_t desired, const stdAc::state_t *prev) { break; } #endif // SEND_HITACHI_AC1 +#if SEND_HITACHI_AC264 + case HITACHI_AC264: + { + IRHitachiAc264 ac(_pin, _inverted, _modulation); + hitachi264(&ac, send.power, send.mode, degC, send.fanspeed); + break; + } +#endif // SEND_HITACHI_AC264 #if SEND_HITACHI_AC344 case HITACHI_AC344: { @@ -3756,6 +3796,13 @@ namespace IRAcUtils { return ac.toString(); } #endif // DECODE_HITACHI_AC1 +#if DECODE_HITACHI_AC264 + case decode_type_t::HITACHI_AC264: { + IRHitachiAc264 ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_HITACHI_AC264 #if DECODE_HITACHI_AC344 case decode_type_t::HITACHI_AC344: { IRHitachiAc344 ac(kGpioUnused); @@ -4213,6 +4260,14 @@ namespace IRAcUtils { break; } #endif // DECODE_HITACHI_AC1 +#if DECODE_HITACHI_AC264 + case decode_type_t::HITACHI_AC264: { + IRHitachiAc264 ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_HITACHI_AC264 #if DECODE_HITACHI_AC344 case decode_type_t::HITACHI_AC344: { IRHitachiAc344 ac(kGpioUnused); diff --git a/src/IRac.h b/src/IRac.h index 7a2ebd291..814a3a35a 100644 --- a/src/IRac.h +++ b/src/IRac.h @@ -300,6 +300,11 @@ void electra(IRElectraAc *ac, const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, const bool swing_toggle, const int16_t sleep = -1); #endif // SEND_HITACHI_AC1 +#if SEND_HITACHI_AC264 + void hitachi264(IRHitachiAc264 *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan); +#endif // SEND_HITACHI_AC264 #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 ca2a7fcf9..9d5232516 100644 --- a/src/ir_Hitachi.cpp +++ b/src/ir_Hitachi.cpp @@ -1025,6 +1025,7 @@ void IRHitachiAc424::stateReset(void) { _.raw[3] = 0x40; _.raw[5] = 0xFF; _.raw[7] = 0xCC; + _.raw[27] = 0xE1; _.raw[33] = 0x80; _.raw[35] = 0x03; _.raw[37] = 0x01; @@ -1072,15 +1073,13 @@ void IRHitachiAc424::send(const uint16_t repeat) { /// Get the value of the current power setting. /// @return true, the setting is on. false, the setting is off. -bool IRHitachiAc424::getPower(void) const { - return _.Power == kHitachiAc424PowerOn; -} +bool IRHitachiAc424::getPower(void) const { return _.Power; } /// Change the power setting. /// @param[in] on true, the setting is on. false, the setting is off. void IRHitachiAc424::setPower(const bool on) { + _.Power = on; setButton(kHitachiAc424ButtonPowerMode); - _.Power = (on ? kHitachiAc424PowerOn : kHitachiAc424PowerOff); } /// Change the power setting to On. @@ -1220,7 +1219,7 @@ uint8_t IRHitachiAc424::convertMode(const stdAc::opmode_t mode) { /// 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 IRHitachiAc424::convertFan(const stdAc::fanspeed_t speed) { +uint8_t IRHitachiAc424::convertFan(const stdAc::fanspeed_t speed) const { switch (speed) { case stdAc::fanspeed_t::kMin: return kHitachiAc424FanMin; case stdAc::fanspeed_t::kLow: return kHitachiAc424FanLow; @@ -1247,7 +1246,7 @@ stdAc::opmode_t IRHitachiAc424::toCommonMode(const uint8_t mode) { /// 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 IRHitachiAc424::toCommonFanSpeed(const uint8_t speed) { +stdAc::fanspeed_t IRHitachiAc424::toCommonFanSpeed(const uint8_t speed) const { switch (speed) { case kHitachiAc424FanMax: return stdAc::fanspeed_t::kMax; case kHitachiAc424FanHigh: return stdAc::fanspeed_t::kHigh; @@ -1613,6 +1612,95 @@ void IRsend::sendHitachiAc264(const unsigned char data[], const uint16_t nbytes, } #endif // SEND_HITACHI_AC264 +// Class constructor for handling detailed Hitachi_AC344 43 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? +IRHitachiAc264::IRHitachiAc264(const uint16_t pin, const bool inverted, + const bool use_modulation) + : IRHitachiAc424(pin, inverted, use_modulation) { stateReset(); } + +/// Reset the internal state to auto fan, cooling, 23° Celsius +void IRHitachiAc264::stateReset(void) { + IRHitachiAc424::stateReset(); + _.raw[9] = 0x92; + _.raw[27] = 0xC1; +} + +#if SEND_HITACHI_AC264 +/// Create and send the IR message to the A/C. +/// @param[in] repeat Nr. of times to repeat the message. +void IRHitachiAc264::send(const uint16_t repeat) { + _irsend.sendHitachiAc264(getRaw(), kHitachiAc264StateLength, repeat); +} +#endif // SEND_HITACHI_AC264 + +/// Set the internal state from a valid code for this protocol. +/// @param[in] new_code A valid code for this protocol. +/// @param[in] length Size (in bytes) of the code for this protocol. +void IRHitachiAc264::setRaw(const uint8_t new_code[], const uint16_t length) { + memcpy(_.raw, new_code, std::min(length, kHitachiAc264StateLength)); +} + +/// Set the speed of the fan. +/// @param[in] speed The desired setting. +void IRHitachiAc264::setFan(const uint8_t speed) { + switch (speed) { + case kHitachiAc264FanMin: + case kHitachiAc264FanMedium: + case kHitachiAc264FanHigh: + case kHitachiAc264FanAuto: + _.Fan = speed; + break; + default: + setFan(kHitachiAc264FanAuto); + } +} + +/// 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 IRHitachiAc264::convertFan(const stdAc::fanspeed_t speed) const { + switch (speed) { + case stdAc::fanspeed_t::kMin: + case stdAc::fanspeed_t::kLow: return kHitachiAc264FanMin; + case stdAc::fanspeed_t::kMedium: return kHitachiAc264FanMedium; + case stdAc::fanspeed_t::kHigh: + case stdAc::fanspeed_t::kMax: return kHitachiAc424FanHigh; + default: return kHitachiAc424FanAuto; + } +} + +/// 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 IRHitachiAc264::toCommonFanSpeed(const uint8_t speed) const { + switch (speed) { + case kHitachiAc264FanHigh: return stdAc::fanspeed_t::kHigh; + case kHitachiAc264FanMedium: return stdAc::fanspeed_t::kMedium; + case kHitachiAc264FanMin: return stdAc::fanspeed_t::kMin; + default: return stdAc::fanspeed_t::kAuto; + } +} + +/// Convert the current internal state into its stdAc::state_t equivalent. +/// @return The stdAc equivalent of the native settings. +stdAc::state_t IRHitachiAc264::toCommon(void) const { + stdAc::state_t result = IRHitachiAc424::toCommon(); + result.protocol = decode_type_t::HITACHI_AC264; + result.swingv = stdAc::swingv_t::kOff; + return result; +} + +/// Convert the internal state into a human readable string. +/// @return A string containing the settings in human-readable form. +String IRHitachiAc264::toString(void) const { + String result; + result.reserve(120); // Reserve some heap for the string to reduce fragging. + result += _toString(); + return result; +} + #if DECODE_HITACHI_AC264 // For Decoding HITACHI_AC264, see `decodeHitachiAC` #endif // DECODE_HITACHI_AC264 diff --git a/src/ir_Hitachi.h b/src/ir_Hitachi.h index 316614dc0..5b9e93f14 100644 --- a/src/ir_Hitachi.h +++ b/src/ir_Hitachi.h @@ -105,7 +105,9 @@ union Hitachi424Protocol{ // Byte 26 uint8_t :8; // Byte 27 - uint8_t Power :8; + uint8_t :4; + uint8_t Power :1; + uint8_t :3; // Byte 28~34 uint8_t pad2[7]; // Byte 35 @@ -163,9 +165,6 @@ const uint8_t kHitachiAc344FanHigh = kHitachiAc424FanHigh; const uint8_t kHitachiAc344FanAuto = kHitachiAc424FanAuto; const uint8_t kHitachiAc344FanMax = kHitachiAc424FanMax; -const uint8_t kHitachiAc424PowerOn = 0xF1; -const uint8_t kHitachiAc424PowerOff = 0xE1; - const uint8_t kHitachiAc344SwingHAuto = 0; // 0b000 const uint8_t kHitachiAc344SwingHRightMax = 1; // 0b001 const uint8_t kHitachiAc344SwingHRight = 2; // 0b010 @@ -243,6 +242,57 @@ const uint8_t kHitachiAc1Sleep4 = 0b100; const uint8_t kHitachiAc1ChecksumStartByte = 5; +/// Native representation of a Hitachi 164-bit A/C message. +union HitachiAC264Protocol{ + uint8_t raw[kHitachiAc264StateLength]; ///< The state in native code. + struct { + // Bytes 0~10 + uint8_t pad0[11]; + // Byte 11 + uint8_t Button :8; + // Byte 12 + uint8_t :8; + // Byte 13 + uint8_t :2; + uint8_t Temp :6; + // Byte 14 + uint8_t :8; + // Bytes 14~24 + uint8_t pad1[10]; + // Byte 25 + uint8_t Mode :4; + uint8_t Fan :4; + // Byte 26 + uint8_t :8; + // Byte 27 + uint8_t :4; + uint8_t Power :1; + uint8_t :3; + // Byte 28 + uint8_t :8; + // Bytes 29~32 + uint8_t pad2[4]; + }; +}; + +// HitachiAc264 +const uint8_t kHitachiAc264ButtonPowerMode = kHitachiAc424ButtonPowerMode; +const uint8_t kHitachiAc264ButtonFan = kHitachiAc424ButtonFan; +const uint8_t kHitachiAc264ButtonTempDown = kHitachiAc424ButtonTempDown; +const uint8_t kHitachiAc264ButtonTempUp = kHitachiAc424ButtonTempUp; +const uint8_t kHitachiAc264ButtonSwingV = kHitachiAc424ButtonSwingV; +const uint8_t kHitachiAc264MinTemp = kHitachiAc424MinTemp; // 16C +const uint8_t kHitachiAc264MaxTemp = kHitachiAc424MaxTemp; // 32C +const uint8_t kHitachiAc264Fan = kHitachiAc424Fan; +const uint8_t kHitachiAc264Cool = kHitachiAc424Cool; +const uint8_t kHitachiAc264Dry = kHitachiAc424Dry; +const uint8_t kHitachiAc264Heat = kHitachiAc424Heat; +const uint8_t kHitachiAc264FanMin = kHitachiAc424FanMin; +const uint8_t kHitachiAc264FanLow = kHitachiAc424FanMin; +const uint8_t kHitachiAc264FanMedium = kHitachiAc424FanMedium; +const uint8_t kHitachiAc264FanHigh = kHitachiAc424FanHigh; +const uint8_t kHitachiAc264FanAuto = kHitachiAc424FanAuto; + // Classes /// Class for handling detailed Hitachi 224-bit A/C messages. /// @see https://github.com/ToniA/arduino-heatpumpir/blob/master/HitachiHeatpumpIR.cpp @@ -372,6 +422,7 @@ class IRHitachiAc1 { /// Class for handling detailed Hitachi 53-byte/424-bit A/C messages. class IRHitachiAc424 { + friend class IRHitachiAc264; friend class IRHitachiAc344; public: explicit IRHitachiAc424(const uint16_t pin, const bool inverted = false, @@ -392,7 +443,7 @@ class IRHitachiAc424 { bool getPower(void) const; void setTemp(const uint8_t temp, bool setPrevious = true); uint8_t getTemp(void) const; - void setFan(const uint8_t speed); + virtual void setFan(const uint8_t speed); uint8_t getFan(void) const; uint8_t getButton(void) const; void setButton(const uint8_t button); @@ -404,9 +455,9 @@ class IRHitachiAc424 { virtual void setRaw(const uint8_t new_code[], const uint16_t length = kHitachiAc424StateLength); static uint8_t convertMode(const stdAc::opmode_t mode); - static uint8_t convertFan(const stdAc::fanspeed_t speed); + virtual uint8_t convertFan(const stdAc::fanspeed_t speed) const; static stdAc::opmode_t toCommonMode(const uint8_t mode); - static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed); + virtual stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed) const; virtual stdAc::state_t toCommon(void) const; virtual String toString(void) const; #ifndef UNIT_TEST @@ -478,4 +529,22 @@ class IRHitachiAc344: public IRHitachiAc424 { static stdAc::swingh_t toCommonSwingH(const uint8_t pos); String toString(void) const override; }; + +/// Class for handling detailed Hitachi 264-bit A/C messages. +class IRHitachiAc264: public IRHitachiAc424 { + public: + explicit IRHitachiAc264(const uint16_t pin, const bool inverted = false, + const bool use_modulation = true); + void stateReset(void) override; + void setRaw(const uint8_t new_code[], + const uint16_t length = kHitachiAc264StateLength) override; + void setFan(const uint8_t speed) override; + uint8_t convertFan(const stdAc::fanspeed_t speed) const override; + stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed) const override; + stdAc::state_t toCommon(void) const override; +#if SEND_HITACHI_AC264 + void send(const uint16_t repeat = kHitachiAcDefaultRepeat) override; +#endif // SEND_HITACHI_AC264 + String toString(void) const override; +}; #endif // IR_HITACHI_H_ diff --git a/test/IRac_test.cpp b/test/IRac_test.cpp index 2990f400b..7d19b6f76 100644 --- a/test/IRac_test.cpp +++ b/test/IRac_test.cpp @@ -944,6 +944,35 @@ TEST(TestIRac, Hitachi1) { ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p)); } +TEST(TestIRac, Hitachi264) { + IRHitachiAc264 ac(kGpioUnused); + IRac irac(kGpioUnused); + IRrecv capture(kGpioUnused); + char expected_swingon[] = + "Power: On, Mode: 6 (Heat), Temp: 25C, Fan: 4 (High), " + "Button: 19 (Power/Mode)"; + + ac.begin(); + irac.hitachi264(&ac, + true, // Power + stdAc::opmode_t::kHeat, // Mode + 25, // Celsius + stdAc::fanspeed_t::kMax); // Fan speed + + ASSERT_EQ(expected_swingon, ac.toString()); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(HITACHI_AC264, ac._irsend.capture.decode_type); + ASSERT_EQ(kHitachiAc264Bits, ac._irsend.capture.bits); + ASSERT_EQ(expected_swingon, 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_AC264, r.protocol); + EXPECT_TRUE(r.power); + EXPECT_EQ(stdAc::opmode_t::kHeat, r.mode); + EXPECT_EQ(25, 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 10fb0b6ea..5313fb14b 100644 --- a/test/ir_Hitachi_test.cpp +++ b/test/ir_Hitachi_test.cpp @@ -860,7 +860,7 @@ TEST(TestUtils, Housekeeping) { ASSERT_EQ("HITACHI_AC264", typeToString(decode_type_t::HITACHI_AC264)); ASSERT_EQ(decode_type_t::HITACHI_AC264, strToDecodeType("HITACHI_AC264")); ASSERT_TRUE(hasACState(decode_type_t::HITACHI_AC264)); - ASSERT_FALSE(IRac::isProtocolSupported(decode_type_t::HITACHI_AC264)); + ASSERT_TRUE(IRac::isProtocolSupported(decode_type_t::HITACHI_AC264)); ASSERT_EQ(kHitachiAc264Bits, IRsend::defaultBits(decode_type_t::HITACHI_AC264)); ASSERT_EQ(kNoRepeat, @@ -2056,10 +2056,11 @@ TEST(TestDecodeHitachiAc264, RealExample) { ASSERT_EQ(kHitachiAc264Bits, irsend.capture.bits); EXPECT_STATE_EQ(expected, irsend.capture.state, kHitachiAc264Bits); EXPECT_EQ( - "", + "Power: Off, Mode: 6 (Heat), Temp: 27C, Fan: 1 (Min), " + "Button: 19 (Power/Mode)", IRAcUtils::resultAcToString(&irsend.capture)); stdAc::state_t r, p; - ASSERT_FALSE(IRAcUtils::decodeToState(&irsend.capture, &r, &p)); + ASSERT_TRUE(IRAcUtils::decodeToState(&irsend.capture, &r, &p)); } // Decode a 'Synthetic' HitachiAc264 message. @@ -2083,8 +2084,150 @@ TEST(TestDecodeHitachiAc264, SyntheticExample) { ASSERT_EQ(kHitachiAc264Bits, irsend.capture.bits); EXPECT_STATE_EQ(expected, irsend.capture.state, kHitachiAc264Bits); EXPECT_EQ( - "", + "Power: Off, Mode: 6 (Heat), Temp: 27C, Fan: 1 (Min), " + "Button: 19 (Power/Mode)", IRAcUtils::resultAcToString(&irsend.capture)); stdAc::state_t r, p; - ASSERT_FALSE(IRAcUtils::decodeToState(&irsend.capture, &r, &p)); + ASSERT_TRUE(IRAcUtils::decodeToState(&irsend.capture, &r, &p)); +} + +TEST(TestIRHitachiAc264Class, ConstructKnownState) { + IRHitachiAc264 ac(kGpioUnused); + // hot/power off/ fan 1 /27°C + // Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/1729#issuecomment-1006938712 + const uint8_t expected[kHitachiAc264StateLength] = { + 0x01, 0x10, 0x00, 0x40, 0xBF, 0xFF, 0x00, 0xCC, 0x33, 0x92, + 0x6D, 0x13, 0xEC, 0x6C, 0x93, 0x00, 0xFF, 0x00, 0xFF, 0x00, + 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x16, 0xE9, 0xC1, 0x3E, 0x00, + 0xFF, 0x00, 0xFF}; + ac.stateReset(); + ac.setMode(kHitachiAc264Heat); + ac.setFan(kHitachiAc264FanMin); + ac.setTemp(27); + ac.setPower(false); + EXPECT_EQ( + "Power: Off, Mode: 6 (Heat), Temp: 27C, Fan: 1 (Min), " + "Button: 19 (Power/Mode)", + ac.toString()); + EXPECT_STATE_EQ(expected, ac.getRaw(), kHitachiAc264Bits); +} + +TEST(TestIRHitachiAc264Class, Issue1729_PowerOntoOff) { + IRHitachiAc264 ac(kGpioUnused); + + const String on_str = "Power: On, Mode: 6 (Heat), Temp: 25C, Fan: 5 (Auto), " + "Button: 19 (Power/Mode)"; + const String off_str = "Power: Off, Mode: 6 (Heat), Temp: 25C, " + "Fan: 5 (Auto), Button: 19 (Power/Mode)"; + const uint8_t off_state[kHitachiAc264StateLength] = { + 0x01, 0x10, 0x00, 0x40, 0xBF, 0xFF, 0x00, 0xCC, 0x33, 0x92, + 0x6D, 0x13, 0xEC, 0x64, 0x9B, 0x00, 0xFF, 0x00, 0xFF, 0x00, + 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x56, 0xA9, 0xC1, 0x3E, 0x00, + 0xFF, 0x00, 0xFF}; + // Simulate at the `IRHitachiAc264` class level. + ac.stateReset(); + // Create an On state. + ac.setMode(kHitachiAc264Heat); + ac.setTemp(25); + ac.setFan(kHitachiAc264FanAuto); + ac.setPower(true); + EXPECT_EQ(on_str, ac.toString()); + ac.send(); // Send it + EXPECT_EQ(on_str, ac.toString()); // Check it is still the same. + + // Now construct the "Off" state. + ac.setMode(kHitachiAc264Heat); + ac.setTemp(25); + ac.setFan(kHitachiAc264FanAuto); + ac.setPower(false); + + EXPECT_EQ(off_str, ac.toString()); + EXPECT_STATE_EQ(off_state, ac.getRaw(), kHitachiAc264Bits); + ac.send(); // Send it + EXPECT_EQ(off_str, ac.toString()); // Check it is still the same "off" state. + EXPECT_STATE_EQ(off_state, ac.getRaw(), kHitachiAc264Bits); + + // Simulate at the `IRac` class level. + IRac irac(kGpioUnused); + IRrecv capture(kGpioUnused); + + ac.stateReset(); + ac._irsend.reset(); + + // Create a power on state & send it. + irac.hitachi264(&ac, + true, // Power + stdAc::opmode_t::kHeat, // Mode + 25, // Celsius + stdAc::fanspeed_t::kAuto); // Fan speed + + ASSERT_EQ(on_str, ac.toString()); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(HITACHI_AC264, ac._irsend.capture.decode_type); + ASSERT_EQ(kHitachiAc264Bits, ac._irsend.capture.bits); + ASSERT_EQ(on_str, IRAcUtils::resultAcToString(&ac._irsend.capture)); + stdAc::state_t on, prev; + ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &on, &prev)); + EXPECT_EQ(decode_type_t::HITACHI_AC264, on.protocol); + EXPECT_TRUE(on.power); + EXPECT_EQ(stdAc::opmode_t::kHeat, on.mode); + EXPECT_EQ(25, on.degrees); + + ac.stateReset(); + ac._irsend.reset(); + // Create a power off state & send it. + prev = on; + irac.hitachi264(&ac, + false, // Power + stdAc::opmode_t::kHeat, // Mode + 25, // Celsius + stdAc::fanspeed_t::kAuto); // Fan speed + + ASSERT_EQ(off_str, ac.toString()); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(HITACHI_AC264, ac._irsend.capture.decode_type); + ASSERT_EQ(kHitachiAc264Bits, ac._irsend.capture.bits); + ASSERT_EQ(off_str, IRAcUtils::resultAcToString(&ac._irsend.capture)); + EXPECT_STATE_EQ(off_state, ac._irsend.capture.state, ac._irsend.capture.bits); + stdAc::state_t off; + ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &off, &prev)); + EXPECT_EQ(decode_type_t::HITACHI_AC264, off.protocol); + EXPECT_FALSE(off.power); + EXPECT_EQ(stdAc::opmode_t::kHeat, off.mode); + EXPECT_EQ(25, off.degrees); + + // Verify that handleToggles() isn't screwing anything up. + stdAc::state_t toggle_state; + toggle_state = irac.handleToggles(on, &prev); + EXPECT_FALSE(irac.cmpStates(on, prev)); // They are the same. + EXPECT_FALSE(irac.cmpStates(on, toggle_state)); // They are the same. + toggle_state = irac.handleToggles(off, &on); + EXPECT_TRUE(irac.cmpStates(off, on)); // They are different. + EXPECT_FALSE(irac.cmpStates(off, toggle_state)); // They are the same. + EXPECT_EQ(off.protocol, decode_type_t::HITACHI_AC264); + EXPECT_FALSE(off.power); + // That confirms handleToggles() isn't causing any grief. + + // Check that IRac copies & modifies the states as we expect. + irac.next = on; // Copy in the "on" state we constructed earlier. + irac.sendAc(); // Send it. + irac.markAsSent(); // Confirm it was sent. + EXPECT_FALSE(irac.cmpStates(on, irac.getStatePrev())); // They are the same. + EXPECT_FALSE(irac.cmpStates(on, irac.getState())); // They are the same. + + irac.next.power = false; // Turn off the power. + EXPECT_TRUE(irac.cmpStates(on, irac.getState())); // They are the different. + EXPECT_FALSE(off.power); + EXPECT_FALSE(irac.cmpStates(off, irac.getState())); // They are the same. + EXPECT_FALSE(irac.cmpStates(off, irac.next)); // They are the same. + irac.sendAc(); // Send it. + irac.markAsSent(); // Confirm it was sent. + EXPECT_FALSE(irac.cmpStates(off, irac.getStatePrev())); // They are the same. + EXPECT_FALSE(irac.cmpStates(off, irac.getState())); // They are the same. + EXPECT_FALSE(irac.cmpStates(off, irac.next)); // They are the same. + EXPECT_FALSE(irac.getStatePrev().power); + EXPECT_FALSE(irac.getState().power); + EXPECT_FALSE(irac.next.power); }