HITACHI_AC296: Add IRac class support & tests. (#1776)

* Fix max temp issue.
* Set special temp for auto operation mode.
* Merge into the `IRac` class so it is supported fully.
* Add `.toString()` output.
* Add real & synthetic decoding examples.
* General code style cleanup(s).
* Add supporting Unit Tests.

Ref: #1758
Fixes #1757
This commit is contained in:
David Conran
2022-03-14 08:36:44 +10:00
committed by GitHub
parent 434f65e996
commit f63b9be67b
6 changed files with 423 additions and 56 deletions

View File

@@ -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);

View File

@@ -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,

View File

@@ -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))

View File

@@ -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:

View File

@@ -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);

View File

@@ -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());
}