Skip to content

Commit

Permalink
Add support of Toshiba Remote Control B (#2094)
Browse files Browse the repository at this point in the history
Add support of Toshiba Remote Control B
  • Loading branch information
jackysze authored Jul 25, 2024
1 parent aae4528 commit c00a919
Show file tree
Hide file tree
Showing 9 changed files with 97 additions and 30 deletions.
7 changes: 7 additions & 0 deletions src/IRsend.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
4 changes: 4 additions & 0 deletions src/IRtext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions src/IRtext.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
9 changes: 9 additions & 0 deletions src/IRutils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
Expand Down
33 changes: 30 additions & 3 deletions src/ir_Toshiba.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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.
Expand Down
11 changes: 10 additions & 1 deletion src/ir_Toshiba.h
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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);
Expand Down
6 changes: 6 additions & 0 deletions src/locale/defaults.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
12 changes: 6 additions & 6 deletions test/IRac_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2188,8 +2188,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,
Expand Down Expand Up @@ -3175,8 +3175,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
Expand All @@ -3202,8 +3202,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
Expand Down
43 changes: 23 additions & 20 deletions test/ir_Toshiba_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}

Expand Down Expand Up @@ -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));
Expand Down Expand Up @@ -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));
}

Expand Down Expand Up @@ -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));
}

Expand All @@ -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] = {
Expand Down Expand Up @@ -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));
}

Expand All @@ -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] = {
Expand All @@ -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] = {
Expand All @@ -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());
}

Expand Down

0 comments on commit c00a919

Please sign in to comment.