diff --git a/src/IRsend.h b/src/IRsend.h index 38491372a..1af988cc9 100644 --- a/src/IRsend.h +++ b/src/IRsend.h @@ -228,6 +228,13 @@ enum argo_ac_remote_model_t { SAC_WREM3 // (2) ARGO WREM3 remote (touch buttons), bit-len vary by cmd }; +/// Toshiba A/C model numbers +enum toshiba_ac_remote_model_t { + kToshibaGenericRemote_A = 0, // Default from existing codebase + kToshibaGenericRemote_B = 1, // Newly discovered remote control b, applies to + // many remote models such as WA-TH03A, WA-TH04A etc. +}; + // Classes /// Class for sending all basic IR protocols. diff --git a/src/IRtext.cpp b/src/IRtext.cpp index 9cb39b772..8fd27b955 100644 --- a/src/IRtext.cpp +++ b/src/IRtext.cpp @@ -295,6 +295,10 @@ IRTEXT_CONST_STRING(kDg11j104Str, D_STR_DG11J104); ///< "DG11J104" IRTEXT_CONST_STRING(kDg11j191Str, D_STR_DG11J191); ///< "DG11J191" IRTEXT_CONST_STRING(kArgoWrem2Str, D_STR_ARGO_WREM2); ///< "WREM3" IRTEXT_CONST_STRING(kArgoWrem3Str, D_STR_ARGO_WREM3); ///< "WREM3" +IRTEXT_CONST_STRING(kToshibaGenericRemoteAStr, D_STR_TOSHIBAGENERICREMOTEA); +// "TOSHIBA REMOTE A" +IRTEXT_CONST_STRING(kToshibaGenericRemoteBStr, D_STR_TOSHIBAGENERICREMOTEB); +// "TOSHIBA REMOTE B" #define D_STR_UNSUPPORTED "?" // Unsupported protocols will be showing as // a question mark, check for length > 1 diff --git a/src/IRtext.h b/src/IRtext.h index 15d2690b7..cc7e900f6 100644 --- a/src/IRtext.h +++ b/src/IRtext.h @@ -229,6 +229,8 @@ extern IRTEXT_CONST_PTR(kSetTimerCommandStr); extern IRTEXT_CONST_PTR(kTimerStr); extern IRTEXT_CONST_PTR(kToggleStr); extern IRTEXT_CONST_PTR(kTopStr); +extern IRTEXT_CONST_PTR(kToshibaGenericRemoteAStr); +extern IRTEXT_CONST_PTR(kToshibaGenericRemoteBStr); extern IRTEXT_CONST_PTR(kTrueStr); extern IRTEXT_CONST_PTR(kTurboStr); extern IRTEXT_CONST_PTR(kTurboToggleStr); diff --git a/src/IRutils.cpp b/src/IRutils.cpp index e9af0b28f..ab4a08dbf 100644 --- a/src/IRutils.cpp +++ b/src/IRutils.cpp @@ -701,6 +701,15 @@ namespace irutils { default: return kUnknownStr; } break; + case decode_type_t::TOSHIBA_AC: + switch (model) { + case toshiba_ac_remote_model_t::kToshibaGenericRemote_A: + return kToshibaGenericRemoteAStr; + case toshiba_ac_remote_model_t::kToshibaGenericRemote_B: + return kToshibaGenericRemoteBStr; + default: + return kUnknownStr; + } default: return kUnknownStr; } } diff --git a/src/ir_Toshiba.cpp b/src/ir_Toshiba.cpp index c9672bcab..68ca47d74 100644 --- a/src/ir_Toshiba.cpp +++ b/src/ir_Toshiba.cpp @@ -40,6 +40,7 @@ using irutils::addModeToString; using irutils::addTempToString; using irutils::checkInvertedBytePairs; using irutils::invertBytePairs; +using irutils::addModelToString; #if SEND_TOSHIBA_AC /// Send a Toshiba A/C message. @@ -104,8 +105,9 @@ void IRToshibaAC::send(const uint16_t repeat) { uint16_t IRToshibaAC::getInternalStateLength(const uint8_t state[], const uint16_t size) { if (size < kToshibaAcLengthByte) return 0; - return std::min((uint16_t)(state[kToshibaAcLengthByte] + kToshibaAcMinLength), - kToshibaACStateLengthLong); + // Fix: Extract the last 4 bits instead + return std::min((uint16_t)((state[kToshibaAcLengthByte] & 0xF) + + kToshibaAcMinLength), kToshibaACStateLengthLong); } /// Get the length of the current internal state per the protocol structure. @@ -462,7 +464,8 @@ stdAc::state_t IRToshibaAC::toCommon(const stdAc::state_t *prev) const { String IRToshibaAC::toString(void) const { String result = ""; result.reserve(95); - result += addTempToString(getTemp(), true, false); + result += addModelToString(decode_type_t::TOSHIBA_AC, getModel(), false); + result += addTempToString(getTemp(), true); switch (getStateLength()) { case kToshibaACStateLengthShort: result += addIntToString(getSwing(true), kSwingVStr); @@ -493,6 +496,30 @@ String IRToshibaAC::toString(void) const { return result; } +/// Get the model information currently known. +/// @return The known model number. +toshiba_ac_remote_model_t IRToshibaAC::getModel(void) const { + switch (_.Model) { + case kToshibaAcRemoteB: + return toshiba_ac_remote_model_t::kToshibaGenericRemote_B; + default: + return toshiba_ac_remote_model_t::kToshibaGenericRemote_A; + } +} + +/// Set the current model for the remote. +/// @param[in] model The model number. +void IRToshibaAC::setModel(const toshiba_ac_remote_model_t model) { + switch (model) { + case toshiba_ac_remote_model_t::kToshibaGenericRemote_B: + _.Model = kToshibaAcRemoteB; + break; + default: + _.Model = kToshibaAcRemoteA; + break; + } +} + #if DECODE_TOSHIBA_AC /// Decode the supplied Toshiba A/C message. /// Status: STABLE / Working. diff --git a/src/ir_Toshiba.h b/src/ir_Toshiba.h index 1314cf54d..163847ad1 100644 --- a/src/ir_Toshiba.h +++ b/src/ir_Toshiba.h @@ -53,7 +53,11 @@ union ToshibaProtocol{ ///< 1 (56 bit message) ///< 3 (72 bit message) ///< 4 (80 bit message) - uint8_t Length :8; + uint8_t Length :4; + // Toshiba remote type + // 0 - Remote control A + // 1 - Remote control B + uint8_t Model :4; // Byte[3] - The bit-inverted value of the "length" byte. uint8_t :8; // Byte[4] @@ -111,6 +115,9 @@ const uint8_t kToshibaAcFanMax = 5; // 0b101 const uint8_t kToshibaAcTurboOn = 1; // 0b01 const uint8_t kToshibaAcEconoOn = 3; // 0b11 +const uint8_t kToshibaAcRemoteA = 0; // 0b0000 +const uint8_t kToshibaAcRemoteB = 1; // 0b0001 + // Legacy defines. (Deprecated) #define TOSHIBA_AC_AUTO kToshibaAcAuto #define TOSHIBA_AC_COOL kToshibaAcCool @@ -140,6 +147,8 @@ class IRToshibaAC { void begin(void); void on(void); void off(void); + void setModel(const toshiba_ac_remote_model_t model); + toshiba_ac_remote_model_t getModel() const; void setPower(const bool on); bool getPower(void) const; void setTemp(const uint8_t degrees); diff --git a/src/locale/defaults.h b/src/locale/defaults.h index 438cc5da3..ea20686cf 100644 --- a/src/locale/defaults.h +++ b/src/locale/defaults.h @@ -1120,6 +1120,12 @@ D_STR_INDIRECT " " D_STR_MODE #ifndef D_STR_ZEPEAL #define D_STR_ZEPEAL "ZEPEAL" #endif // D_STR_ZEPEAL +#ifndef D_STR_TOSHIBAGENERICREMOTEA +#define D_STR_TOSHIBAGENERICREMOTEA "TOSHIBA REMOTE A" +#endif // D_STR_TOSHIBAGENERICREMOTEA +#ifndef D_STR_TOSHIBAGENERICREMOTEB +#define D_STR_TOSHIBAGENERICREMOTEB "TOSHIBA REMOTE B" +#endif // D_STR_TOSHIBAGENERICREMOTEB // IRrecvDumpV2+ #ifndef D_STR_TIMESTAMP diff --git a/test/IRac_test.cpp b/test/IRac_test.cpp index 5d9084ecb..77d6c1081 100644 --- a/test/IRac_test.cpp +++ b/test/IRac_test.cpp @@ -2158,8 +2158,8 @@ TEST(TestIRac, Toshiba) { IRac irac(kGpioUnused); IRrecv capture(kGpioUnused); char expected[] = - "Temp: 29C, Power: On, Mode: 2 (Dry), Fan: 2 (UNKNOWN), " - "Turbo: Off, Econo: On, Filter: Off"; + "Model: 0 (TOSHIBA REMOTE A), Temp: 29C, Power: On, Mode: 2 (Dry), " + "Fan: 2 (UNKNOWN), Turbo: Off, Econo: On, Filter: Off"; ac.begin(); irac.toshiba(&ac, @@ -3145,8 +3145,8 @@ 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, Filter: Off"; + "Model: 0 (TOSHIBA REMOTE A), Temp: 19C, Power: On, Mode: 4 (Fan), " + "Fan: 0 (Auto), Turbo: Off, Econo: Off, Filter: Off"; ac._irsend.reset(); irac.toshiba(&ac, irac.next.power, // Power @@ -3172,8 +3172,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, " - "Filter: Off"; + "Model: 0 (TOSHIBA REMOTE A), Temp: 19C, Power: Off, Fan: 0 (Auto), " + "Turbo: Off, Econo: Off, Filter: Off"; ac._irsend.reset(); irac.toshiba(&ac, irac.next.power, // Power diff --git a/test/ir_Toshiba_test.cpp b/test/ir_Toshiba_test.cpp index 4a63780d5..37ab2db2c 100644 --- a/test/ir_Toshiba_test.cpp +++ b/test/ir_Toshiba_test.cpp @@ -311,22 +311,25 @@ TEST(TestToshibaACClass, HumanReadableOutput) { 0x00, 0xC1, 0x00, 0xC0}; ac.setRaw(initial_state); - EXPECT_EQ("Temp: 17C, Power: On, Mode: 0 (Auto), Fan: 0 (Auto), " - "Turbo: Off, Econo: Off, Filter: Off", + EXPECT_EQ("Model: 0 (TOSHIBA REMOTE A), Temp: 17C, Power: On, " + "Mode: 0 (Auto), Fan: 0 (Auto), 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, Filter: Off", + EXPECT_EQ("Model: 0 (TOSHIBA REMOTE A), Temp: 17C, Power: On, " + "Mode: 1 (Cool), Fan: 5 (High), 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, Filter: Off", + EXPECT_EQ("Model: 0 (TOSHIBA REMOTE A), Temp: 25C, Power: On, " + "Mode: 2 (Dry), Fan: 3 (Medium), Turbo: Off, Econo: Off, " + "Filter: Off", ac.toString()); ac.off(); - EXPECT_EQ("Temp: 25C, Power: Off, Fan: 3 (Medium), Turbo: Off, Econo: Off, " - "Filter: Off", + EXPECT_EQ("Model: 0 (TOSHIBA REMOTE A), Temp: 25C, Power: Off, " + "Fan: 3 (Medium), Turbo: Off, Econo: Off, Filter: Off", ac.toString()); } @@ -379,8 +382,8 @@ TEST(TestDecodeToshibaAC, SyntheticExample) { ASSERT_EQ(kToshibaACBits, irsend.capture.bits); 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, Filter: Off", + "Model: 0 (TOSHIBA REMOTE A), Temp: 17C, Power: On, Mode: 0 (Auto), " + "Fan: 0 (Auto), Turbo: Off, Econo: Off, Filter: Off", IRAcUtils::resultAcToString(&irsend.capture)); stdAc::state_t r, p; ASSERT_TRUE(IRAcUtils::decodeToState(&irsend.capture, &r, &p)); @@ -627,8 +630,8 @@ TEST(TestDecodeToshibaAC, RealLongExample) { EXPECT_EQ(kToshibaACBitsLong, irsend.capture.bits); 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, Filter: Off", + "Model: 0 (TOSHIBA REMOTE A), Temp: 22C, Power: On, Mode: 0 (Auto), " + "Fan: 0 (Auto), Turbo: On, Econo: Off, Filter: Off", IRAcUtils::resultAcToString(&irsend.capture)); } @@ -718,7 +721,7 @@ TEST(TestDecodeToshibaAC, RealShortExample) { EXPECT_EQ(kToshibaACBitsShort, irsend.capture.bits); EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); EXPECT_EQ( - "Temp: 17C, Swing(V): 0 (Step)", + "Model: 0 (TOSHIBA REMOTE A), Temp: 17C, Swing(V): 0 (Step)", IRAcUtils::resultAcToString(&irsend.capture)); } @@ -732,8 +735,8 @@ TEST(TestToshibaACClass, ConstructLongState) { ac.setTurbo(false); ac.setEcono(true); EXPECT_EQ( - "Temp: 29C, Power: On, Mode: 2 (Dry), Fan: 2 (UNKNOWN), " - "Turbo: Off, Econo: On, Filter: Off", + "Model: 0 (TOSHIBA REMOTE A), Temp: 29C, Power: On, Mode: 2 (Dry), " + "Fan: 2 (UNKNOWN), Turbo: Off, Econo: On, Filter: Off", ac.toString()); EXPECT_EQ(kToshibaACStateLengthLong, ac.getStateLength()); const uint8_t expectedState[kToshibaACStateLengthLong] = { @@ -783,8 +786,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, " - "Filter: Off", + "Model: 0 (TOSHIBA REMOTE A), Temp: 20C, Power: Off, Fan: 0 (Auto), " + "Turbo: Off, Econo: Off, Filter: Off", IRAcUtils::resultAcToString(&irsend.capture)); } @@ -805,7 +808,7 @@ TEST(TestToshibaACClass, SwingCodes) { ac.setSwing(kToshibaAcSwingOn); EXPECT_EQ( - "Temp: 17C, Swing(V): 1 (On)", + "Model: 0 (TOSHIBA REMOTE A), Temp: 17C, Swing(V): 1 (On)", ac.toString()); EXPECT_EQ(kToshibaACStateLengthShort, ac.getStateLength()); const uint8_t swingOnState[kToshibaACStateLengthShort] = { @@ -815,7 +818,7 @@ TEST(TestToshibaACClass, SwingCodes) { ac.setSwing(kToshibaAcSwingOff); EXPECT_EQ( - "Temp: 17C, Swing(V): 2 (Off)", + "Model: 0 (TOSHIBA REMOTE A), Temp: 17C, Swing(V): 2 (Off)", ac.toString()); EXPECT_EQ(kToshibaACStateLengthShort, ac.getStateLength()); const uint8_t swingOffState[kToshibaACStateLengthShort] = { @@ -828,7 +831,7 @@ TEST(TestToshibaACClass, SwingCodes) { ac.setRaw(swingToggleState, kToshibaACStateLengthShort); EXPECT_EQ(kToshibaAcSwingToggle, ac.getSwing()); EXPECT_EQ( - "Temp: 17C, Swing(V): 4 (Toggle)", + "Model: 0 (TOSHIBA REMOTE A), Temp: 17C, Swing(V): 4 (Toggle)", ac.toString()); }