Skip to content

Commit

Permalink
TOSHIBA_AC: Add Filter setting support. aka. Pure. (#1693)
Browse files Browse the repository at this point in the history
* Add `setFilter()` & `getFilter()`.
* Add filter support for Toshiba in `IRAc` class.
* Minor code style/formating cleanup.
* Update & add unit tests for code coverage and verification.

Fixes #1692
  • Loading branch information
crankyoldgit authored Nov 29, 2021
1 parent 94080aa commit 187c533
Show file tree
Hide file tree
Showing 6 changed files with 98 additions and 39 deletions.
7 changes: 4 additions & 3 deletions src/IRac.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2158,11 +2158,12 @@ void IRac::teco(IRTecoAc *ac,
/// @param[in] swingv The vertical swing setting.
/// @param[in] turbo Run the device in turbo/powerful mode.
/// @param[in] econo Run the device in economical mode.
/// @param[in] filter Turn on the (Pure/ion/pollen/etc) filter mode.
void IRac::toshiba(IRToshibaAC *ac,
const bool on, const stdAc::opmode_t mode,
const float degrees, const stdAc::fanspeed_t fan,
const stdAc::swingv_t swingv,
const bool turbo, const bool econo) {
const bool turbo, const bool econo, const bool filter) {
ac->begin();
ac->setMode(ac->convertMode(mode));
ac->setTemp(degrees);
Expand All @@ -2175,7 +2176,7 @@ void IRac::toshiba(IRToshibaAC *ac,
ac->setTurbo(turbo);
ac->setEcono(econo);
// No Light setting available.
// No Filter setting available.
ac->setFilter(filter);
// No Clean setting available.
// No Beep setting available.
// No Sleep setting available.
Expand Down Expand Up @@ -3114,7 +3115,7 @@ bool IRac::sendAc(const stdAc::state_t desired, const stdAc::state_t *prev) {
{
IRToshibaAC ac(_pin, _inverted, _modulation);
toshiba(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv,
send.turbo, send.econo);
send.turbo, send.econo, send.filter);
break;
}
#endif // SEND_TOSHIBA_AC
Expand Down
2 changes: 1 addition & 1 deletion src/IRac.h
Original file line number Diff line number Diff line change
Expand Up @@ -466,7 +466,7 @@ void electra(IRElectraAc *ac,
void toshiba(IRToshibaAC *ac,
const bool on, const stdAc::opmode_t mode, const float degrees,
const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv,
const bool turbo, const bool econo);
const bool turbo, const bool econo, const bool filter);
#endif // SEND_TOSHIBA_AC
#if SEND_TROTEC
void trotec(IRTrotecESP *ac,
Expand Down
22 changes: 17 additions & 5 deletions src/ir_Toshiba.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -216,9 +216,7 @@ void IRToshibaAC::setTemp(const uint8_t degrees) {

/// Get the current temperature setting.
/// @return The current setting for temp. in degrees celsius.
uint8_t IRToshibaAC::getTemp(void) const {
return _.Temp + kToshibaAcMinTemp;
}
uint8_t IRToshibaAC::getTemp(void) const { return _.Temp + kToshibaAcMinTemp; }

/// Set the speed of the fan.
/// @param[in] speed The desired setting (0 is Auto, 1-5 is the speed, 5 is Max)
Expand Down Expand Up @@ -339,6 +337,19 @@ void IRToshibaAC::setEcono(const bool on) {
}
}

/// Get the filter (Pure/Ion Filter) setting of the A/C.
/// @return true, if the current setting is on. Otherwise, false.
bool IRToshibaAC::getFilter(void) const {
return (getStateLength() >= kToshibaACStateLength) ? _.Filter : false;
}

/// Set the filter (Pure/Ion Filter) setting of the A/C.
/// @param[in] on true, the setting is on. false, the setting is off.
void IRToshibaAC::setFilter(const bool on) {
_.Filter = on;
if (on) setStateLength(std::min(kToshibaACStateLength, getStateLength()));
}

/// 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.
Expand Down Expand Up @@ -421,6 +432,7 @@ stdAc::state_t IRToshibaAC::toCommon(const stdAc::state_t *prev) const {
result.fanspeed = toCommonFanSpeed(getFan());
result.turbo = getTurbo();
result.econo = getEcono();
result.filter = getFilter();
}
switch (getSwing()) {
case kToshibaAcSwingOn:
Expand All @@ -436,7 +448,6 @@ stdAc::state_t IRToshibaAC::toCommon(const stdAc::state_t *prev) const {
}
// Not supported.
result.light = false;
result.filter = false;
result.swingh = stdAc::swingh_t::kOff;
result.quiet = false;
result.clean = false;
Expand All @@ -450,7 +461,7 @@ stdAc::state_t IRToshibaAC::toCommon(const stdAc::state_t *prev) const {
/// @return A human readable string.
String IRToshibaAC::toString(void) const {
String result = "";
result.reserve(80);
result.reserve(95);
result += addTempToString(getTemp(), true, false);
switch (getStateLength()) {
case kToshibaACStateLengthShort:
Expand All @@ -477,6 +488,7 @@ String IRToshibaAC::toString(void) const {
kToshibaAcFanMed);
result += addBoolToString(getTurbo(), kTurboStr);
result += addBoolToString(getEcono(), kEconoStr);
result += addBoolToString(getFilter(), kFilterStr);
}
return result;
}
Expand Down
38 changes: 23 additions & 15 deletions src/ir_Toshiba.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
/// @see https://docs.google.com/spreadsheets/d/1yidE2fvaO9kpCHfKafIdH31q4uaskYR1OwwrkyOxbp0/edit?usp=drivesdk
/// @see https://www.toshiba-carrier.co.jp/global/about/index.htm
/// @see http://www.toshiba-carrier.co.th/AboutUs/Pages/CompanyProfile.aspx
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1692

// Supports:
// Brand: Toshiba, Model: RAS-B13N3KV2
Expand All @@ -18,6 +19,8 @@
// Brand: Toshiba, Model: WC-L03SE
// Brand: Toshiba, Model: WH-UB03NJ remote
// Brand: Toshiba, Model: RAS-2558V A/C
// Brand: Toshiba, Model: WH-TA01JE remote
// Brand: Toshiba, Model: RAS-25SKVP2-ND A/C
// Brand: Carrier, Model: 42NQV060M2 / 38NYV060M2 A/C
// Brand: Carrier, Model: 42NQV050M2 / 38NYV050M2 A/C
// Brand: Carrier, Model: 42NQV035M2 / 38NYV035M2 A/C
Expand Down Expand Up @@ -50,28 +53,31 @@ union ToshibaProtocol{
///< 1 (56 bit message)
///< 3 (72 bit message)
///< 4 (80 bit message)
uint8_t Length :8;
uint8_t Length :8;
// Byte[3] - The bit-inverted value of the "length" byte.
uint8_t :8;
uint8_t :8;
// Byte[4]
uint8_t :3;
uint8_t LongMsg :1;
uint8_t :1;
uint8_t ShortMsg:1;
uint8_t :2;
uint8_t :3;
uint8_t LongMsg :1;
uint8_t :1;
uint8_t ShortMsg :1;
uint8_t :2;
// Byte[5]
uint8_t Swing :3;
uint8_t :1;
uint8_t Temp :4;
uint8_t Swing :3;
uint8_t :1;
uint8_t Temp :4;
// Byte[6]
uint8_t Mode :3;
uint8_t :2;
uint8_t Fan :3;
uint8_t Mode :3;
uint8_t :2;
uint8_t Fan :3;
// Byte[7]
uint8_t :8;
uint8_t :4;
uint8_t Filter :1;
uint8_t :3;

// Byte[8]
// (Checksum for 72 bit messages, Eco/Turbo for long 80 bit messages)
uint8_t EcoTurbo :8;
uint8_t EcoTurbo :8;
};
};

Expand Down Expand Up @@ -144,6 +150,8 @@ class IRToshibaAC {
bool getTurbo(void) const;
void setEcono(const bool on);
bool getEcono(void) const;
void setFilter(const bool on);
bool getFilter(void) const;
void setMode(const uint8_t mode);
uint8_t getMode(const bool raw = false) const;
void setRaw(const uint8_t newState[],
Expand Down
16 changes: 10 additions & 6 deletions test/IRac_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1989,7 +1989,7 @@ TEST(TestIRac, Toshiba) {
IRrecv capture(kGpioUnused);
char expected[] =
"Temp: 29C, Power: On, Mode: 2 (Dry), Fan: 2 (UNKNOWN), "
"Turbo: Off, Econo: On";
"Turbo: Off, Econo: On, Filter: Off";

ac.begin();
irac.toshiba(&ac,
Expand All @@ -1999,7 +1999,8 @@ TEST(TestIRac, Toshiba) {
stdAc::fanspeed_t::kLow, // Fan speed
stdAc::swingv_t::kOff, // Vertical Swing
false, // Turbo
true); // Econo
true, // Econo
false); // Filter
ASSERT_EQ(expected, ac.toString());
ASSERT_EQ(kToshibaACStateLengthLong, ac.getStateLength());
ac._irsend.makeDecodeResult();
Expand Down Expand Up @@ -2904,7 +2905,7 @@ TEST(TestIRac, Issue1250) {
// Now send the state so we can actually decode/capture what we sent.
char expected_on[] =
"Temp: 19C, Power: On, Mode: 4 (Fan), Fan: 0 (Auto), "
"Turbo: Off, Econo: Off";
"Turbo: Off, Econo: Off, Filter: Off";
ac._irsend.reset();
irac.toshiba(&ac,
irac.next.power, // Power
Expand All @@ -2913,7 +2914,8 @@ TEST(TestIRac, Issue1250) {
irac.next.fanspeed, // Fan speed
irac.next.swingv, // Vertical Swing
irac.next.turbo, // Turbo
irac.next.econo); // Econo
irac.next.econo, // Econo
irac.next.filter); // Filter
ASSERT_EQ(expected_on, ac.toString());
ASSERT_EQ(kToshibaACStateLength, ac.getStateLength());
ac._irsend.makeDecodeResult();
Expand All @@ -2929,7 +2931,8 @@ TEST(TestIRac, Issue1250) {
irac.sendAc();
// Now send the state so we can actually decode/capture what we sent.
char expected_off[] =
"Temp: 19C, Power: Off, Fan: 0 (Auto), Turbo: Off, Econo: Off";
"Temp: 19C, Power: Off, Fan: 0 (Auto), Turbo: Off, Econo: Off, "
"Filter: Off";
ac._irsend.reset();
irac.toshiba(&ac,
irac.next.power, // Power
Expand All @@ -2938,7 +2941,8 @@ TEST(TestIRac, Issue1250) {
irac.next.fanspeed, // Fan speed
irac.next.swingv, // Vertical Swing
irac.next.turbo, // Turbo
irac.next.econo); // Econo
irac.next.econo, // Econo
irac.next.filter); // Filter
ASSERT_EQ(expected_off, ac.toString());
ASSERT_EQ(kToshibaACStateLength, ac.getStateLength());
ac._irsend.makeDecodeResult();
Expand Down
52 changes: 43 additions & 9 deletions test/ir_Toshiba_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -312,20 +312,21 @@ TEST(TestToshibaACClass, HumanReadableOutput) {

ac.setRaw(initial_state);
EXPECT_EQ("Temp: 17C, Power: On, Mode: 0 (Auto), Fan: 0 (Auto), "
"Turbo: Off, Econo: Off",
"Turbo: Off, Econo: Off, Filter: Off",
ac.toString());
ac.setRaw(modified_state);
EXPECT_EQ("Temp: 17C, Power: On, Mode: 1 (Cool), Fan: 5 (High), "
"Turbo: Off, Econo: Off",
"Turbo: Off, Econo: Off, Filter: Off",
ac.toString());
ac.setTemp(25);
ac.setFan(3);
ac.setMode(kToshibaAcDry);
EXPECT_EQ("Temp: 25C, Power: On, Mode: 2 (Dry), Fan: 3 (Medium), "
"Turbo: Off, Econo: Off",
"Turbo: Off, Econo: Off, Filter: Off",
ac.toString());
ac.off();
EXPECT_EQ("Temp: 25C, Power: Off, Fan: 3 (Medium), Turbo: Off, Econo: Off",
EXPECT_EQ("Temp: 25C, Power: Off, Fan: 3 (Medium), Turbo: Off, Econo: Off, "
"Filter: Off",
ac.toString());
}

Expand Down Expand Up @@ -379,7 +380,7 @@ TEST(TestDecodeToshibaAC, SyntheticExample) {
EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits);
EXPECT_EQ(
"Temp: 17C, Power: On, Mode: 0 (Auto), Fan: 0 (Auto), Turbo: Off, "
"Econo: Off",
"Econo: Off, Filter: Off",
IRAcUtils::resultAcToString(&irsend.capture));
stdAc::state_t r, p;
ASSERT_TRUE(IRAcUtils::decodeToState(&irsend.capture, &r, &p));
Expand Down Expand Up @@ -555,6 +556,7 @@ TEST(TestToshibaACClass, toCommon) {
ac.setMode(kToshibaAcCool);
ac.setTemp(20);
ac.setFan(kToshibaAcFanMax);
ac.setFilter(true);
// Now test it.
ASSERT_EQ(decode_type_t::TOSHIBA_AC, ac.toCommon().protocol);
ASSERT_EQ(-1, ac.toCommon().model);
Expand All @@ -563,13 +565,13 @@ TEST(TestToshibaACClass, toCommon) {
ASSERT_EQ(20, ac.toCommon().degrees);
ASSERT_EQ(stdAc::opmode_t::kCool, ac.toCommon().mode);
ASSERT_EQ(stdAc::fanspeed_t::kMax, ac.toCommon().fanspeed);
ASSERT_TRUE(ac.toCommon().filter);
// Unsupported.
ASSERT_EQ(stdAc::swingv_t::kOff, ac.toCommon().swingv);
ASSERT_EQ(stdAc::swingh_t::kOff, ac.toCommon().swingh);
ASSERT_FALSE(ac.toCommon().turbo);
ASSERT_FALSE(ac.toCommon().econo);
ASSERT_FALSE(ac.toCommon().light);
ASSERT_FALSE(ac.toCommon().filter);
ASSERT_FALSE(ac.toCommon().clean);
ASSERT_FALSE(ac.toCommon().beep);
ASSERT_FALSE(ac.toCommon().quiet);
Expand Down Expand Up @@ -626,7 +628,7 @@ TEST(TestDecodeToshibaAC, RealLongExample) {
EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits);
EXPECT_EQ(
"Temp: 22C, Power: On, Mode: 0 (Auto), Fan: 0 (Auto), Turbo: On, "
"Econo: Off",
"Econo: Off, Filter: Off",
IRAcUtils::resultAcToString(&irsend.capture));
}

Expand Down Expand Up @@ -731,7 +733,7 @@ TEST(TestToshibaACClass, ConstructLongState) {
ac.setEcono(true);
EXPECT_EQ(
"Temp: 29C, Power: On, Mode: 2 (Dry), Fan: 2 (UNKNOWN), "
"Turbo: Off, Econo: On",
"Turbo: Off, Econo: On, Filter: Off",
ac.toString());
EXPECT_EQ(kToshibaACStateLengthLong, ac.getStateLength());
const uint8_t expectedState[kToshibaACStateLengthLong] = {
Expand Down Expand Up @@ -781,7 +783,8 @@ TEST(TestDecodeToshibaAC, RealExample_WHUB03NJ) {
EXPECT_EQ(kToshibaACBits, irsend.capture.bits);
EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits);
EXPECT_EQ(
"Temp: 20C, Power: Off, Fan: 0 (Auto), Turbo: Off, Econo: Off",
"Temp: 20C, Power: Off, Fan: 0 (Auto), Turbo: Off, Econo: Off, "
"Filter: Off",
IRAcUtils::resultAcToString(&irsend.capture));
}

Expand Down Expand Up @@ -828,3 +831,34 @@ TEST(TestToshibaACClass, SwingCodes) {
"Temp: 17C, Swing(V): 4 (Toggle)",
ac.toString());
}

// For https://github.com/crankyoldgit/IRremoteESP8266/issues/1692
TEST(TestToshibaACClass, Filter) {
IRToshibaAC ac(kGpioUnused);
ac.begin();
EXPECT_FALSE(ac.getFilter());

ac.setFilter(true);
EXPECT_TRUE(ac.getFilter());


ac.setFilter(false);
EXPECT_FALSE(ac.getFilter());

ac.setFilter(true);
EXPECT_TRUE(ac.getFilter());

const uint8_t pure_off[kToshibaACStateLength] = {
0xF2, 0x0D, 0x03, 0xFC, 0x01, 0x40, 0x03, 0x00, 0x42};
ac.setRaw(pure_off);
EXPECT_FALSE(ac.getFilter());

const uint8_t pure_on[kToshibaACStateLength] = {
0xF2, 0x0D, 0x03, 0xFC, 0x01, 0x40, 0x03, 0x10, 0x52};
ac.setRaw(pure_on);
EXPECT_TRUE(ac.getFilter());

// Convert a known filter/pure on state to a known off filter/pure state.
ac.setFilter(false);
EXPECT_STATE_EQ(pure_off, ac.getRaw(), ac.getStateLength() * 8);
}

0 comments on commit 187c533

Please sign in to comment.