TCL112AC/TEKNOPOINT: Add support for GZ055BE1 model (#1602)

* Add `model` support for `TCL112AC` protocol.
* Hack `TEKNOPOINT` decoding to use `TCL112AC` model `GZ055BE1`
* Change Vertical Swing to have multiple positions.
* Add On & Off timer support.
* Add "Night" (aka. Quiet/min fan speed)
* Plenty of Unit Test additions, updates, & improvements.

Fixes #1486
This commit is contained in:
David Conran
2021-09-13 06:31:08 +10:00
committed by GitHub
parent ece6bd5729
commit 5cd13df8e6
10 changed files with 481 additions and 155 deletions

View File

@@ -277,6 +277,9 @@ bool IRac::isProtocolSupported(const decode_type_t protocol) {
#if SEND_TECO
case decode_type_t::TECO:
#endif
#if SEND_TEKNOPOINT
case decode_type_t::TEKNOPOINT:
#endif // SEND_TEKNOPOINT
#if SEND_TOSHIBA_AC
case decode_type_t::TOSHIBA_AC:
#endif
@@ -1942,6 +1945,7 @@ void IRac::sharp(IRSharpAc *ac, const sharp_ac_remote_model_t model,
#if SEND_TCL112AC
/// Send a TCL 112-bit A/C message with the supplied settings.
/// @param[in, out] ac A Ptr to an IRTcl112Ac object to use.
/// @param[in] model The A/C model to use.
/// @param[in] on The power setting.
/// @param[in] mode The operation mode setting.
/// @param[in] degrees The temperature setting in degrees.
@@ -1953,18 +1957,19 @@ void IRac::sharp(IRSharpAc *ac, const sharp_ac_remote_model_t model,
/// @param[in] light Turn on the LED/Display mode.
/// @param[in] econo Run the device in economical mode.
/// @param[in] filter Turn on the (ion/pollen/etc) filter mode.
void IRac::tcl112(IRTcl112Ac *ac,
void IRac::tcl112(IRTcl112Ac *ac, const tcl_ac_remote_model_t model,
const bool on, const stdAc::opmode_t mode,
const float degrees, const stdAc::fanspeed_t fan,
const stdAc::swingv_t swingv, const stdAc::swingh_t swingh,
const bool quiet, const bool turbo, const bool light,
const bool econo, const bool filter) {
ac->begin();
ac->setModel(model);
ac->setPower(on);
ac->setMode(ac->convertMode(mode));
ac->setTemp(degrees);
ac->setFan(ac->convertFan(fan));
ac->setSwingVertical(swingv != stdAc::swingv_t::kOff);
ac->setSwingVertical(ac->convertSwingV(swingv));
ac->setSwingHorizontal(swingh != stdAc::swingh_t::kOff);
ac->setQuiet(quiet);
ac->setTurbo(turbo);
@@ -2906,16 +2911,20 @@ bool IRac::sendAc(const stdAc::state_t desired, const stdAc::state_t *prev) {
break;
}
#endif // SEND_SHARP_AC
#if SEND_TCL112AC
#if (SEND_TCL112AC || SEND_TEKNOPOINT)
case TCL112AC:
case TEKNOPOINT:
{
IRTcl112Ac ac(_pin, _inverted, _modulation);
tcl112(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv,
send.swingh, send.quiet, send.turbo, send.light, send.econo,
send.filter);
tcl_ac_remote_model_t model = (tcl_ac_remote_model_t)send.model;
if (send.protocol == decode_type_t::TEKNOPOINT)
model = tcl_ac_remote_model_t::GZ055BE1;
tcl112(&ac, model, send.power, send.mode,
degC, send.fanspeed, send.swingv, send.swingh, send.quiet,
send.turbo, send.light, send.econo, send.filter);
break;
}
#endif // SEND_TCL112AC
#endif // (SEND_TCL112AC || SEND_TEKNOPOINT)
#if SEND_TECHNIBEL_AC
case TECHNIBEL_AC:
{
@@ -3235,6 +3244,18 @@ int16_t IRac::strToModel(const char *str, const int16_t def) {
return panasonic_ac_remote_model_t::kPanasonicCkp;
} else if (!strcasecmp(str, "RKR") || !strcasecmp(str, "PANASONICRKR")) {
return panasonic_ac_remote_model_t::kPanasonicRkr;
// Sharp A/C Models
} else if (!strcasecmp(str, "A907")) {
return sharp_ac_remote_model_t::A907;
} else if (!strcasecmp(str, "A705")) {
return sharp_ac_remote_model_t::A705;
} else if (!strcasecmp(str, "A903")) {
return sharp_ac_remote_model_t::A903;
// TCL A/C Models
} else if (!strcasecmp(str, "TAC09CHSD")) {
return tcl_ac_remote_model_t::TAC09CHSD;
} else if (!strcasecmp(str, "GZ055BE1")) {
return tcl_ac_remote_model_t::GZ055BE1;
// Voltas A/C models
} else if (!strcasecmp(str, "122LZF")) {
return voltas_ac_remote_model_t::kVoltas122LZF;
@@ -3746,13 +3767,14 @@ namespace IRAcUtils {
return ac.toString();
}
#endif // DECODE_TECO
#if DECODE_TCL112AC
case decode_type_t::TCL112AC: {
#if (DECODE_TCL112AC || DECODE_TEKNOPOINT)
case decode_type_t::TCL112AC:
case decode_type_t::TEKNOPOINT: {
IRTcl112Ac ac(kGpioUnused);
ac.setRaw(result->state);
return ac.toString();
}
#endif // DECODE_TCL112AC
#endif // (DECODE_TCL112AC || DECODE_TEKNOPOINT)
#if DECODE_LG
case decode_type_t::LG:
case decode_type_t::LG2: {
@@ -4142,14 +4164,18 @@ namespace IRAcUtils {
break;
}
#endif // DECODE_SHARP_AC
#if DECODE_TCL112AC
case decode_type_t::TCL112AC: {
#if (DECODE_TCL112AC || DECODE_TEKNOPOINT)
case decode_type_t::TCL112AC:
case decode_type_t::TEKNOPOINT: {
IRTcl112Ac ac(kGpioUnused);
ac.setRaw(decode->state);
*result = ac.toCommon(prev);
// Teknopoint uses the TCL protocol, but with a different model number.
// Just keep the original protocol type ... for now.
result->protocol = decode->decode_type;
break;
}
#endif // DECODE_TCL112AC
#endif // (DECODE_TCL112AC || DECODE_TEKNOPOINT)
#if DECODE_TECHNIBEL_AC
case decode_type_t::TECHNIBEL_AC: {
IRTechnibelAc ac(kGpioUnused);

View File

@@ -418,7 +418,7 @@ void electra(IRElectraAc *ac,
const bool filter, const bool clean);
#endif // SEND_SHARP_AC
#if SEND_TCL112AC
void tcl112(IRTcl112Ac *ac,
void tcl112(IRTcl112Ac *ac, const tcl_ac_remote_model_t model,
const bool on, const stdAc::opmode_t mode, const float degrees,
const stdAc::fanspeed_t fan,
const stdAc::swingv_t swingv, const stdAc::swingh_t swingh,

View File

@@ -160,6 +160,12 @@ enum sharp_ac_remote_model_t {
A903 = 3, // 820 too
};
/// TCL A/C model numbers
enum tcl_ac_remote_model_t {
TAC09CHSD = 1,
GZ055BE1 = 2,
};
/// Voltas A/C model numbers
enum voltas_ac_remote_model_t {
kVoltasUnknown = 0, // Full Function

View File

@@ -586,14 +586,6 @@ namespace irutils {
default: return kUnknownStr;
}
break;
case decode_type_t::SHARP_AC:
switch (model) {
case sharp_ac_remote_model_t::A907: return F("A907");
case sharp_ac_remote_model_t::A705: return F("A705");
case sharp_ac_remote_model_t::A903: return F("A903");
default: return kUnknownStr;
}
break;
case decode_type_t::PANASONIC_AC:
switch (model) {
case panasonic_ac_remote_model_t::kPanasonicLke: return F("LKE");
@@ -605,6 +597,21 @@ namespace irutils {
default: return kUnknownStr;
}
break;
case decode_type_t::SHARP_AC:
switch (model) {
case sharp_ac_remote_model_t::A907: return F("A907");
case sharp_ac_remote_model_t::A705: return F("A705");
case sharp_ac_remote_model_t::A903: return F("A903");
default: return kUnknownStr;
}
break;
case decode_type_t::TCL112AC:
switch (model) {
case tcl_ac_remote_model_t::TAC09CHSD: return F("TAC09CHSD");
case tcl_ac_remote_model_t::GZ055BE1: return F("GZ055BE1");
default: return kUnknownStr;
}
break;
case decode_type_t::VOLTAS:
switch (model) {
case voltas_ac_remote_model_t::kVoltas122LZF: return F("122LZF");

View File

@@ -1166,7 +1166,7 @@ void IRsend::sendMitsubishi112(const unsigned char data[],
}
#endif // SEND_MITSUBISHI112
#if DECODE_MITSUBISHI112 || DECODE_TCL112AC
#if (DECODE_MITSUBISHI112 || DECODE_TCL112AC)
/// Decode the supplied Mitsubishi/TCL 112-bit A/C message.
/// (MITSUBISHI112, TCL112AC)
/// Status: STABLE / Reported as working.
@@ -1212,7 +1212,7 @@ bool IRrecv::decodeMitsubishi112(decode_results *results, uint16_t offset,
gap = kMitsubishi112Gap;
}
#endif // DECODE_MITSUBISHI112
#if DECODE_TCL112AC
#if (DECODE_TCL112AC || DECODE_TEKNOPOINT)
if (typeguess == decode_type_t::UNKNOWN && // We didn't match Mitsubishi112
matchMark(results->rawbuf[offset], kTcl112AcHdrMark,
kTcl112AcHdrMarkTolerance, 0)) {
@@ -1224,7 +1224,7 @@ bool IRrecv::decodeMitsubishi112(decode_results *results, uint16_t offset,
gap = kTcl112AcGap;
tolerance += kTcl112AcTolerance;
}
#endif // DECODE_TCL112AC
#endif // (DECODE_TCL112AC || DECODE_TEKNOPOINT)
if (typeguess == decode_type_t::UNKNOWN) return false; // No header matched.
offset++;

View File

@@ -15,12 +15,18 @@
// Constants
const uint8_t kTcl112AcTimerResolution = 20; // Minutes
const uint16_t kTcl112AcTimerMax = 720; // Minutes (12 hrs)
using irutils::addBoolToString;
using irutils::addFanToString;
using irutils::addIntToString;
using irutils::addLabeledString;
using irutils::addModeToString;
using irutils::addModelToString;
using irutils::addSwingVToString;
using irutils::addTempFloatToString;
using irutils::minsToString;
#if SEND_TCL112AC
/// Send a TCL 112-bit A/C message.
@@ -71,6 +77,8 @@ void IRTcl112Ac::send(const uint16_t repeat) {
_quiet_prev = _quiet;
// Restore the old state.
setRaw(save);
// Make sure it looks like a normal TCL mesg if needed.
if (_.MsgType == kTcl112AcNormal) _.isTcl = true;
}
// Send the normal (type 1) state.
_irsend.sendTcl112Ac(getRaw(), kTcl112AcStateLength, repeat);
@@ -109,6 +117,17 @@ bool IRTcl112Ac::validChecksum(uint8_t state[], const uint16_t length) {
return (length > 1 && state[length - 1] == calcChecksum(state, length));
}
/// Check the supplied state looks like a TCL112AC message.
/// @param[in] state The array to verify the checksum of.
/// @note Assumes the state is the correct size.
/// @return true, if the state looks like a TCL112AC message. Otherwise, false.
/// @warning This is just a guess.
bool IRTcl112Ac::isTcl(const uint8_t state[]) {
Tcl112Protocol mesg;
std::memcpy(mesg.raw, state, kTcl112AcStateLength);
return (mesg.MsgType != kTcl112AcNormal) || mesg.isTcl;
}
/// Reset the internal state of the emulation. (On, Cool, 24C)
void IRTcl112Ac::stateReset(void) {
// A known good state. (On, Cool, 24C)
@@ -121,6 +140,19 @@ void IRTcl112Ac::stateReset(void) {
_quiet_explictly_set = false;
}
/// Get/Detect the model of the A/C.
/// @return The enum of the compatible model.
tcl_ac_remote_model_t IRTcl112Ac::getModel(void) const {
return isTcl(_.raw) ? tcl_ac_remote_model_t::TAC09CHSD
: tcl_ac_remote_model_t::GZ055BE1;
}
/// Set the model of the A/C to emulate.
/// @param[in] model The enum of the appropriate model.
void IRTcl112Ac::setModel(const tcl_ac_remote_model_t model) {
_.isTcl = (model != tcl_ac_remote_model_t::GZ055BE1);
}
/// 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* IRTcl112Ac::getRaw(void) {
@@ -203,6 +235,7 @@ float IRTcl112Ac::getTemp(void) const {
void IRTcl112Ac::setFan(const uint8_t speed) {
switch (speed) {
case kTcl112AcFanAuto:
case kTcl112AcFanMin:
case kTcl112AcFanLow:
case kTcl112AcFanMed:
case kTcl112AcFanHigh:
@@ -250,14 +283,23 @@ void IRTcl112Ac::setSwingHorizontal(const bool on) { _.SwingH = on; }
bool IRTcl112Ac::getSwingHorizontal(void) const { return _.SwingH; }
/// Set the vertical swing setting of the A/C.
/// @param[in] on true, the setting is on. false, the setting is off.
void IRTcl112Ac::setSwingVertical(const bool on) {
_.SwingV = (on ? kTcl112AcSwingVOn : kTcl112AcSwingVOff);
/// @param[in] setting The value of the desired setting.
void IRTcl112Ac::setSwingVertical(const uint8_t setting) {
switch (setting) {
case kTcl112AcSwingVOff:
case kTcl112AcSwingVHighest:
case kTcl112AcSwingVHigh:
case kTcl112AcSwingVMiddle:
case kTcl112AcSwingVLow:
case kTcl112AcSwingVLowest:
case kTcl112AcSwingVOn:
_.SwingV = setting;
}
}
/// Get the vertical swing setting of the A/C.
/// @return true, the setting is on. false, the setting is off.
bool IRTcl112Ac::getSwingVertical(void) const { return _.SwingV; }
/// @return The current setting.
uint8_t IRTcl112Ac::getSwingVertical(void) const { return _.SwingV; }
/// Set the Turbo setting of the A/C.
/// @param[in] on true, the setting is on. false, the setting is off.
@@ -291,6 +333,36 @@ bool IRTcl112Ac::getQuiet(const bool def) const {
return _quiet_explictly_set ? _quiet : def;
}
/// Get how long the On Timer is set for, in minutes.
/// @return The time in nr of minutes.
uint16_t IRTcl112Ac::getOnTimer(void) const {
return _.OnTimer * kTcl112AcTimerResolution;
}
/// Set or cancel the On Timer function.
/// @param[in] mins Nr. of minutes the timer is to be set to.
/// @note Rounds down to 20 min increments. (max: 720 mins (12h), 0 is Off)
void IRTcl112Ac::setOnTimer(const uint16_t mins) {
_.OnTimer = std::min(mins, kTcl112AcTimerMax) / kTcl112AcTimerResolution;
_.OnTimerEnabled = _.OnTimer > 0;
_.TimerIndicator = _.OnTimerEnabled || _.OffTimerEnabled;
}
/// Get how long the Off Timer is set for, in minutes.
/// @return The time in nr of minutes.
uint16_t IRTcl112Ac::getOffTimer(void) const {
return _.OffTimer * kTcl112AcTimerResolution;
}
/// Set or cancel the Off Timer function.
/// @param[in] mins Nr. of minutes the timer is to be set to.
/// @note Rounds down to 20 min increments. (max: 720 mins (12h), 0 is Off)
void IRTcl112Ac::setOffTimer(const uint16_t mins) {
_.OffTimer = std::min(mins, kTcl112AcTimerMax) / kTcl112AcTimerResolution;
_.OffTimerEnabled = _.OffTimer > 0;
_.TimerIndicator = _.OnTimerEnabled || _.OffTimerEnabled;
}
/// 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.
@@ -309,7 +381,7 @@ uint8_t IRTcl112Ac::convertMode(const stdAc::opmode_t mode) {
/// @return The native equivalent of the enum.
uint8_t IRTcl112Ac::convertFan(const stdAc::fanspeed_t speed) {
switch (speed) {
case stdAc::fanspeed_t::kMin:
case stdAc::fanspeed_t::kMin: return kTcl112AcFanMin;
case stdAc::fanspeed_t::kLow: return kTcl112AcFanLow;
case stdAc::fanspeed_t::kMedium: return kTcl112AcFanMed;
case stdAc::fanspeed_t::kHigh:
@@ -331,6 +403,21 @@ stdAc::opmode_t IRTcl112Ac::toCommonMode(const uint8_t mode) {
}
}
/// Convert a stdAc::swingv_t enum into it's native setting.
/// @param[in] position The enum to be converted.
/// @return The native equivalent of the enum.
uint8_t IRTcl112Ac::convertSwingV(const stdAc::swingv_t position) {
switch (position) {
case stdAc::swingv_t::kOff: return kTcl112AcSwingVOff;
case stdAc::swingv_t::kHighest: return kTcl112AcSwingVHighest;
case stdAc::swingv_t::kHigh: return kTcl112AcSwingVHigh;
case stdAc::swingv_t::kMiddle: return kTcl112AcSwingVMiddle;
case stdAc::swingv_t::kLow: return kTcl112AcSwingVLow;
case stdAc::swingv_t::kLowest: return kTcl112AcSwingVLowest;
default: return kTcl112AcSwingVOn;
}
}
/// Convert a native fan speed into its stdAc equivalent.
/// @param[in] spd The native setting to be converted.
/// @return The stdAc equivalent of the native setting.
@@ -338,11 +425,21 @@ stdAc::fanspeed_t IRTcl112Ac::toCommonFanSpeed(const uint8_t spd) {
switch (spd) {
case kTcl112AcFanHigh: return stdAc::fanspeed_t::kMax;
case kTcl112AcFanMed: return stdAc::fanspeed_t::kMedium;
case kTcl112AcFanLow: return stdAc::fanspeed_t::kMin;
case kTcl112AcFanLow: return stdAc::fanspeed_t::kLow;
case kTcl112AcFanMin: return stdAc::fanspeed_t::kMin;
default: return stdAc::fanspeed_t::kAuto;
}
}
/// Convert a native vertical swing postion to it's common equivalent.
/// @param[in] setting A native position to convert.
/// @return The common vertical swing position.
stdAc::swingv_t IRTcl112Ac::toCommonSwingV(const uint8_t setting) {
switch (setting) {
case kTcl112AcSwingVOff: return stdAc::swingv_t::kOff;
default: return stdAc::swingv_t::kAuto;
}
}
/// Convert the current internal state into its stdAc::state_t equivalent.
/// @param[in] prev Ptr to the previous state if required.
/// @return The stdAc equivalent of the native settings.
@@ -351,7 +448,7 @@ stdAc::state_t IRTcl112Ac::toCommon(const stdAc::state_t *prev) const {
// Start with the previous state if given it.
if (prev != NULL) result = *prev;
result.protocol = decode_type_t::TCL112AC;
result.model = -1; // Not supported.
result.model = getModel();
result.quiet = getQuiet(result.quiet);
// The rest only get updated if it is a "normal" message.
if (_.MsgType == kTcl112AcNormal) {
@@ -360,7 +457,7 @@ stdAc::state_t IRTcl112Ac::toCommon(const stdAc::state_t *prev) const {
result.celsius = true;
result.degrees = getTemp();
result.fanspeed = toCommonFanSpeed(_.Fan);
result.swingv = _.SwingV ? stdAc::swingv_t::kAuto : stdAc::swingv_t::kOff;
result.swingv = toCommonSwingV(_.SwingV);
result.swingh = _.SwingH ? stdAc::swingh_t::kAuto : stdAc::swingh_t::kOff;
result.turbo = _.Turbo;
result.filter = _.Health;
@@ -379,8 +476,10 @@ stdAc::state_t IRTcl112Ac::toCommon(const stdAc::state_t *prev) const {
/// @return A human readable string.
String IRTcl112Ac::toString(void) const {
String result = "";
result.reserve(150); // Reserve some heap for the string to reduce fragging.
result += addIntToString(_.MsgType, D_STR_TYPE, false);
result.reserve(220); // Reserve some heap for the string to reduce fragging.
tcl_ac_remote_model_t model = getModel();
result += addModelToString(decode_type_t::TCL112AC, model, false);
result += addIntToString(_.MsgType, D_STR_TYPE);
switch (_.MsgType) {
case kTcl112AcNormal:
result += addBoolToString(_.Power, kPowerStr);
@@ -388,14 +487,32 @@ String IRTcl112Ac::toString(void) const {
kTcl112AcHeat, kTcl112AcDry, kTcl112AcFan);
result += addTempFloatToString(getTemp());
result += addFanToString(_.Fan, kTcl112AcFanHigh, kTcl112AcFanLow,
kTcl112AcFanAuto, kTcl112AcFanAuto,
kTcl112AcFanAuto, kTcl112AcFanMin,
kTcl112AcFanMed);
result += addBoolToString(_.Econo, kEconoStr);
result += addBoolToString(_.Health, kHealthStr);
result += addBoolToString(_.Turbo, kTurboStr);
result += addBoolToString(_.SwingH, kSwingHStr);
result += addBoolToString(_.SwingV, kSwingVStr);
result += addBoolToString(getLight(), kLightStr);
result += addSwingVToString(_.SwingV, kTcl112AcSwingVOff,
kTcl112AcSwingVHighest,
kTcl112AcSwingVHigh,
0xFF, // unused
kTcl112AcSwingVMiddle,
0xFF, // unused
kTcl112AcSwingVLow,
kTcl112AcSwingVLowest,
kTcl112AcSwingVOff,
kTcl112AcSwingVOn, // Swing
0xFF, 0xFF); // Both Unused
if (model != tcl_ac_remote_model_t::GZ055BE1) {
result += addBoolToString(_.SwingH, kSwingHStr);
result += addBoolToString(_.Econo, kEconoStr);
result += addBoolToString(_.Health, kHealthStr);
result += addBoolToString(_.Turbo, kTurboStr);
result += addBoolToString(getLight(), kLightStr);
}
result += addLabeledString(
_.OnTimerEnabled ? minsToString(getOnTimer()) : kOffStr,
kOnTimerStr);
result += addLabeledString(
_.OffTimerEnabled ? minsToString(getOffTimer()) : kOffStr,
kOffTimerStr);
break;
case kTcl112AcSpecial:
result += addBoolToString(_.Quiet, kQuietStr);

View File

@@ -4,8 +4,10 @@
/// @brief Support for TCL protocols.
// Supports:
// Brand: Leberg, Model: LBS-TOR07 A/C
// Brand: TCL, Model: TAC-09CHSD/XA31I A/C
// Brand: Leberg, Model: LBS-TOR07 A/C (TAC09CHSD)
// Brand: TCL, Model: TAC-09CHSD/XA31I A/C (TAC09CHSD)
// Brand: Teknopoint, Model: Allegro SSA-09H A/C (GZ055BE1)
// Brand: Teknopoint, Model: GZ-055B-E1 remote (GZ055BE1)
#ifndef IR_TCL_H_
#define IR_TCL_H_
@@ -25,41 +27,54 @@ union Tcl112Protocol{
uint8_t raw[kTcl112AcStateLength]; ///< The State in IR code form.
struct {
// Byte 0~2
uint8_t pad0[3];
uint8_t :8;
uint8_t :8;
uint8_t :8;
// Byte 3
uint8_t MsgType :2;
uint8_t :6;
uint8_t MsgType :2;
uint8_t :6;
// Byte 4
uint8_t :8;
uint8_t :8;
// Byte 5
uint8_t :2;
uint8_t Power :1;
uint8_t :2;
uint8_t Quiet :1;
uint8_t Light :1;
uint8_t Econo :1;
uint8_t :2;
uint8_t Power :1;
uint8_t OffTimerEnabled :1;
uint8_t OnTimerEnabled :1;
uint8_t Quiet :1;
uint8_t Light :1;
uint8_t Econo :1;
// Byte 6
uint8_t Mode :4;
uint8_t Health :1;
uint8_t Turbo :1;
uint8_t :2;
uint8_t Mode :4;
uint8_t Health :1;
uint8_t Turbo :1;
uint8_t :2;
// Byte 7
uint8_t Temp :4;
uint8_t :4;
uint8_t Temp :4;
uint8_t :4;
// Byte 8
uint8_t Fan :3;
uint8_t SwingV :3;
uint8_t :2;
// Byte 9~11
uint8_t pad1[3];
uint8_t Fan :3;
uint8_t SwingV :3;
uint8_t TimerIndicator :1;
uint8_t :1;
// Byte 9
uint8_t :1; // 0
uint8_t OffTimer :6;
uint8_t :1; // 0
// Byte 10
uint8_t :1; // 0
uint8_t OnTimer :6;
uint8_t :1; // 0
// Byte 11
uint8_t :8; // 00000000
// Byte 12
uint8_t :3;
uint8_t SwingH :1;
uint8_t :1;
uint8_t HalfDegree :1;
uint8_t :2;
uint8_t :3;
uint8_t SwingH :1;
uint8_t :1;
uint8_t HalfDegree :1;
uint8_t :1;
uint8_t isTcl :1;
// Byte 13
uint8_t Sum :8;
uint8_t Sum :8;
};
};
@@ -81,15 +96,23 @@ const uint8_t kTcl112AcFan = 7;
const uint8_t kTcl112AcAuto = 8;
const uint8_t kTcl112AcFanAuto = 0b000;
const uint8_t kTcl112AcFanMin = 0b001; // Aka. "Night"
const uint8_t kTcl112AcFanLow = 0b010;
const uint8_t kTcl112AcFanMed = 0b011;
const uint8_t kTcl112AcFanHigh = 0b101;
const uint8_t kTcl112AcFanNight = kTcl112AcFanMin;
const uint8_t kTcl112AcFanQuiet = kTcl112AcFanMin;
const float kTcl112AcTempMax = 31.0;
const float kTcl112AcTempMin = 16.0;
const uint8_t kTcl112AcSwingVOn = 0b111;
const uint8_t kTcl112AcSwingVOff = 0b000;
const uint8_t kTcl112AcSwingVOff = 0b000;
const uint8_t kTcl112AcSwingVHighest = 0b001;
const uint8_t kTcl112AcSwingVHigh = 0b010;
const uint8_t kTcl112AcSwingVMiddle = 0b011;
const uint8_t kTcl112AcSwingVLow = 0b100;
const uint8_t kTcl112AcSwingVLowest = 0b101;
const uint8_t kTcl112AcSwingVOn = 0b111;
// MsgType
const uint8_t kTcl112AcNormal = 0b01;
const uint8_t kTcl112AcSpecial = 0b10;
@@ -113,6 +136,8 @@ class IRTcl112Ac {
uint8_t* getRaw(void);
void setRaw(const uint8_t new_code[],
const uint16_t length = kTcl112AcStateLength);
tcl_ac_remote_model_t getModel(void) const;
void setModel(const tcl_ac_remote_model_t model);
void on(void);
void off(void);
void setPower(const bool on);
@@ -135,16 +160,23 @@ class IRTcl112Ac {
bool getLight(void) const;
void setSwingHorizontal(const bool on);
bool getSwingHorizontal(void) const;
void setSwingVertical(const bool on);
bool getSwingVertical(void) const;
void setSwingVertical(const uint8_t setting);
uint8_t getSwingVertical(void) const;
void setTurbo(const bool on);
bool getTurbo(void) const;
void setQuiet(const bool on);
bool getQuiet(const bool def = false) const;
uint16_t getOnTimer(void) const;
void setOnTimer(const uint16_t mins);
uint16_t getOffTimer(void) const;
void setOffTimer(const uint16_t mins);
static bool isTcl(const uint8_t state[]);
static uint8_t convertMode(const stdAc::opmode_t mode);
static uint8_t convertFan(const stdAc::fanspeed_t speed);
static uint8_t convertSwingV(const stdAc::swingv_t position);
static stdAc::opmode_t toCommonMode(const uint8_t mode);
static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed);
static stdAc::swingv_t toCommonSwingV(const uint8_t setting);
stdAc::state_t toCommon(const stdAc::state_t *prev = NULL) const;
String toString(void) const;
#ifndef UNIT_TEST

View File

@@ -1641,23 +1641,25 @@ TEST(TestIRac, Tcl112) {
IRac irac(kGpioUnused);
IRrecv capture(kGpioUnused);
char expected[] =
"Type: 1, Power: On, Mode: 3 (Cool), Temp: 20C, Fan: 3 (Medium), "
"Econo: On, Health: On, Turbo: Off, Swing(H): On, Swing(V): Off, "
"Light: On";
"Model: 1 (TAC09CHSD), Type: 1, Power: On, Mode: 3 (Cool), Temp: 20C, "
"Fan: 3 (Medium), Swing(V): 0 (Auto), Swing(H): On, "
"Econo: On, Health: On, Turbo: Off, Light: On, "
"On Timer: Off, Off Timer: Off";
ac.begin();
irac.tcl112(&ac,
true, // Power
stdAc::opmode_t::kCool, // Mode
20, // Celsius
stdAc::fanspeed_t::kMedium, // Fan speed
stdAc::swingv_t::kOff, // Vertical swing
stdAc::swingh_t::kAuto, // Horizontal swing
false, // Quiet (aka. Mute)
false, // Turbo
true, // Light
true, // Econo
true); // Filter (aka. Health)
tcl_ac_remote_model_t::TAC09CHSD, // Model
true, // Power
stdAc::opmode_t::kCool, // Mode
20, // Celsius
stdAc::fanspeed_t::kMedium, // Fan speed
stdAc::swingv_t::kOff, // Vertical swing
stdAc::swingh_t::kAuto, // Horizontal swing
false, // Quiet (aka. Mute)
false, // Turbo
true, // Light
true, // Econo
true); // Filter (aka. Health)
ASSERT_EQ(expected, ac.toString());
ac._irsend.makeDecodeResult();
EXPECT_TRUE(capture.decode(&ac._irsend.capture));
@@ -1669,23 +1671,24 @@ TEST(TestIRac, Tcl112) {
// Test the quiet mode, which should generate two messages.
ac._irsend.reset();
irac.tcl112(&ac,
true, // Power
stdAc::opmode_t::kCool, // Mode
20, // Celsius
stdAc::fanspeed_t::kMedium, // Fan speed
stdAc::swingv_t::kOff, // Vertical swing
stdAc::swingh_t::kAuto, // Horizontal swing
true, // Quiet (aka. Mute)
false, // Turbo
true, // Light
true, // Econo
true); // Filter (aka. Health)
tcl_ac_remote_model_t::TAC09CHSD, // Model
true, // Power
stdAc::opmode_t::kCool, // Mode
20, // Celsius
stdAc::fanspeed_t::kMedium, // Fan speed
stdAc::swingv_t::kOff, // Vertical swing
stdAc::swingh_t::kAuto, // Horizontal swing
true, // Quiet (aka. Mute)
false, // Turbo
true, // Light
true, // Econo
true); // Filter (aka. Health)
ac._irsend.makeDecodeResult();
EXPECT_TRUE(capture.decode(&ac._irsend.capture));
ASSERT_EQ(TCL112AC, ac._irsend.capture.decode_type);
ASSERT_EQ(kTcl112AcBits, ac._irsend.capture.bits);
ASSERT_EQ(
"Type: 2, Quiet: On",
"Model: 1 (TAC09CHSD), Type: 2, Quiet: On",
IRAcUtils::resultAcToString(&ac._irsend.capture));
// TCL112 uses the Mitsubishi112 decoder.
// Skip first message.

View File

@@ -10,8 +10,18 @@
// General housekeeping
TEST(TestTcl112Ac, Housekeeping) {
ASSERT_EQ("TCL112AC", typeToString(TCL112AC));
ASSERT_TRUE(hasACState(TCL112AC));
ASSERT_EQ("TCL112AC", typeToString(decode_type_t::TCL112AC));
ASSERT_EQ(decode_type_t::TCL112AC, strToDecodeType("TCL112AC"));
ASSERT_TRUE(hasACState(decode_type_t::TCL112AC));
ASSERT_TRUE(IRac::isProtocolSupported(decode_type_t::TCL112AC));
ASSERT_EQ(kTcl112AcBits, IRsend::defaultBits(decode_type_t::TCL112AC));
ASSERT_EQ(kNoRepeat, IRsend::minRepeats(decode_type_t::TCL112AC));
ASSERT_EQ(tcl_ac_remote_model_t::TAC09CHSD, IRac::strToModel("TAC09CHSD"));
ASSERT_EQ(irutils::modelToStr(decode_type_t::TCL112AC,
tcl_ac_remote_model_t::TAC09CHSD), "TAC09CHSD");
ASSERT_EQ(tcl_ac_remote_model_t::GZ055BE1, IRac::strToModel("GZ055BE1"));
ASSERT_EQ(irutils::modelToStr(decode_type_t::TCL112AC,
tcl_ac_remote_model_t::GZ055BE1), "GZ055BE1");
}
// Tests for decodeTcl112Ac().
@@ -63,9 +73,10 @@ TEST(TestDecodeTcl112Ac, DecodeRealExample) {
EXPECT_EQ(kTcl112AcBits, irsend.capture.bits);
EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits);
EXPECT_EQ(
"Type: 1, Power: On, Mode: 3 (Cool), Temp: 24C, Fan: 0 (Auto), "
"Econo: Off, Health: Off, Turbo: Off, Swing(H): Off, Swing(V): Off, "
"Light: On",
"Model: 1 (TAC09CHSD), Type: 1, Power: On, Mode: 3 (Cool), Temp: 24C, "
"Fan: 0 (Auto), Swing(V): 0 (Auto), Swing(H): Off, "
"Econo: Off, Health: Off, Turbo: Off, Light: On, "
"On Timer: Off, Off Timer: Off",
IRAcUtils::resultAcToString(&irsend.capture));
}
@@ -106,27 +117,31 @@ TEST(TestTcl112AcClass, Temperature) {
IRTcl112Ac ac(kGpioUnused);
ac.setRaw(temp16C);
EXPECT_EQ(
"Type: 1, Power: On, Mode: 3 (Cool), Temp: 16C, Fan: 0 (Auto), "
"Econo: Off, Health: Off, Turbo: Off, Swing(H): Off, Swing(V): Off, "
"Light: On",
"Model: 1 (TAC09CHSD), Type: 1, Power: On, Mode: 3 (Cool), Temp: 16C, "
"Fan: 0 (Auto), Swing(V): 0 (Auto), Swing(H): Off, "
"Econo: Off, Health: Off, Turbo: Off, Light: On, "
"On Timer: Off, Off Timer: Off",
ac.toString());
ac.setRaw(temp16point5C);
EXPECT_EQ(
"Type: 1, Power: On, Mode: 3 (Cool), Temp: 16.5C, Fan: 0 (Auto), "
"Econo: Off, Health: Off, Turbo: Off, Swing(H): Off, Swing(V): Off, "
"Light: On",
"Model: 1 (TAC09CHSD), Type: 1, Power: On, Mode: 3 (Cool), Temp: 16.5C, "
"Fan: 0 (Auto), Swing(V): 0 (Auto), Swing(H): Off, "
"Econo: Off, Health: Off, Turbo: Off, Light: On, "
"On Timer: Off, Off Timer: Off",
ac.toString());
ac.setRaw(temp19point5C);
EXPECT_EQ(
"Type: 1, Power: On, Mode: 3 (Cool), Temp: 19.5C, Fan: 0 (Auto), "
"Econo: Off, Health: Off, Turbo: Off, Swing(H): Off, Swing(V): Off, "
"Light: On",
"Model: 1 (TAC09CHSD), Type: 1, Power: On, Mode: 3 (Cool), Temp: 19.5C, "
"Fan: 0 (Auto), Swing(V): 0 (Auto), Swing(H): Off, "
"Econo: Off, Health: Off, Turbo: Off, Light: On, "
"On Timer: Off, Off Timer: Off",
ac.toString());
ac.setRaw(temp31C);
EXPECT_EQ(
"Type: 1, Power: On, Mode: 3 (Cool), Temp: 31C, Fan: 0 (Auto), "
"Econo: Off, Health: Off, Turbo: Off, Swing(H): Off, Swing(V): Off, "
"Light: On",
"Model: 1 (TAC09CHSD), Type: 1, Power: On, Mode: 3 (Cool), Temp: 31C, "
"Fan: 0 (Auto), Swing(V): 0 (Auto), Swing(H): Off, "
"Econo: Off, Health: Off, Turbo: Off, Light: On, "
"On Timer: Off, Off Timer: Off",
ac.toString());
ac.setTemp(kTcl112AcTempMin);
@@ -206,9 +221,10 @@ TEST(TestTcl112AcClass, OperatingMode) {
0x07, 0x00, 0x00, 0x00, 0x00, 0x80, 0x48};
ac.setRaw(automode);
EXPECT_EQ(
"Type: 1, Power: On, Mode: 8 (Auto), Temp: 24C, Fan: 0 (Auto), "
"Econo: Off, Health: Off, Turbo: Off, Swing(H): Off, Swing(V): Off, "
"Light: On",
"Model: 1 (TAC09CHSD), Type: 1, Power: On, Mode: 8 (Auto), Temp: 24C, "
"Fan: 0 (Auto), Swing(V): 0 (Auto), Swing(H): Off, "
"Econo: Off, Health: Off, Turbo: Off, Light: On, "
"On Timer: Off, Off Timer: Off",
ac.toString());
}
@@ -236,9 +252,10 @@ TEST(TestTcl112AcClass, Power) {
0x0F, 0x00, 0x00, 0x00, 0x00, 0x80, 0xCB};
ac.setRaw(on);
EXPECT_EQ(
"Type: 1, Power: On, Mode: 3 (Cool), Temp: 16C, Fan: 0 (Auto), "
"Econo: Off, Health: Off, Turbo: Off, Swing(H): Off, Swing(V): Off, "
"Light: On",
"Model: 1 (TAC09CHSD), Type: 1, Power: On, Mode: 3 (Cool), Temp: 16C, "
"Fan: 0 (Auto), Swing(V): 0 (Auto), Swing(H): Off, "
"Econo: Off, Health: Off, Turbo: Off, Light: On, "
"On Timer: Off, Off Timer: Off",
ac.toString());
const uint8_t off[kTcl112AcStateLength] = {
@@ -246,9 +263,10 @@ TEST(TestTcl112AcClass, Power) {
0x07, 0x40, 0x00, 0x00, 0x00, 0x80, 0xCB};
ac.setRaw(off);
EXPECT_EQ(
"Type: 1, Power: Off, Mode: 3 (Cool), Temp: 24C, Fan: 0 (Auto), "
"Econo: Off, Health: Off, Turbo: Off, Swing(H): Off, Swing(V): Off, "
"Light: On",
"Model: 1 (TAC09CHSD), Type: 1, Power: Off, Mode: 3 (Cool), Temp: 24C, "
"Fan: 0 (Auto), Swing(V): 0 (Auto), Swing(H): Off, "
"Econo: Off, Health: Off, Turbo: Off, Light: On, "
"On Timer: Off, Off Timer: Off",
ac.toString());
}
@@ -263,15 +281,17 @@ TEST(TestTcl112AcClass, Checksum) {
EXPECT_EQ(0xCB, ac.calcChecksum(temp16C));
ac.setRaw(temp16C);
EXPECT_EQ(
"Type: 1, Power: On, Mode: 3 (Cool), Temp: 16C, Fan: 0 (Auto), "
"Econo: Off, Health: Off, Turbo: Off, Swing(H): Off, Swing(V): Off, "
"Light: On",
"Model: 1 (TAC09CHSD), Type: 1, Power: On, Mode: 3 (Cool), Temp: 16C, "
"Fan: 0 (Auto), Swing(V): 0 (Auto), Swing(H): Off, "
"Econo: Off, Health: Off, Turbo: Off, Light: On, "
"On Timer: Off, Off Timer: Off",
ac.toString());
ac.setRaw(temp31C);
EXPECT_EQ(
"Type: 1, Power: On, Mode: 3 (Cool), Temp: 31C, Fan: 0 (Auto), "
"Econo: Off, Health: Off, Turbo: Off, Swing(H): Off, Swing(V): Off, "
"Light: On",
"Model: 1 (TAC09CHSD), Type: 1, Power: On, Mode: 3 (Cool), Temp: 31C, "
"Fan: 0 (Auto), Swing(V): 0 (Auto), Swing(H): Off, "
"Econo: Off, Health: Off, Turbo: Off, Light: On, "
"On Timer: Off, Off Timer: Off",
ac.toString());
EXPECT_EQ(0xBC, ac.calcChecksum(temp31C));
@@ -337,12 +357,22 @@ TEST(TestTcl112AcClass, SwingVertical) {
IRTcl112Ac ac(kGpioUnused);
ac.begin();
ac.setSwingVertical(true);
EXPECT_TRUE(ac.getSwingVertical());
ac.setSwingVertical(false);
EXPECT_EQ(false, ac.getSwingVertical());
ac.setSwingVertical(true);
EXPECT_TRUE(ac.getSwingVertical());
ac.setSwingVertical(kTcl112AcSwingVOff);
EXPECT_EQ(kTcl112AcSwingVOff, ac.getSwingVertical());
ac.setSwingVertical(kTcl112AcSwingVOn);
EXPECT_EQ(kTcl112AcSwingVOn, ac.getSwingVertical());
ac.setSwingVertical(kTcl112AcSwingVHigh);
EXPECT_EQ(kTcl112AcSwingVHigh, ac.getSwingVertical());
ac.setSwingVertical(kTcl112AcSwingVOff);
EXPECT_EQ(kTcl112AcSwingVOff, ac.getSwingVertical());
ac.setSwingVertical(0xFF); // Unused value so shouldn't change from previous.
EXPECT_EQ(kTcl112AcSwingVOff, ac.getSwingVertical());
const uint8_t highest[kTcl112AcStateLength] = {
0x23, 0xCB, 0x26, 0x01, 0x00, 0x24, 0x03,
0x0F, 0x08, 0x00, 0x00, 0x00, 0x00, 0x53};
ac.setRaw(highest);
EXPECT_EQ(kTcl112AcSwingVHighest, ac.getSwingVertical());
}
TEST(TestTcl112AcClass, Turbo) {
@@ -405,6 +435,7 @@ TEST(TestTcl112AcClass, Quiet_Mute) {
TEST(TestTcl112AcClass, toCommon) {
IRTcl112Ac ac(kGpioUnused);
ac.setModel(tcl_ac_remote_model_t::TAC09CHSD);
ac.setPower(true);
ac.setMode(kTcl112AcCool);
ac.setTemp(20);
@@ -418,7 +449,7 @@ TEST(TestTcl112AcClass, toCommon) {
ac.setQuiet(false);
// Now test it.
ASSERT_EQ(decode_type_t::TCL112AC, ac.toCommon().protocol);
ASSERT_EQ(-1, ac.toCommon().model);
ASSERT_EQ(1, ac.toCommon().model);
ASSERT_TRUE(ac.toCommon().power);
ASSERT_TRUE(ac.toCommon().celsius);
ASSERT_EQ(20, ac.toCommon().degrees);
@@ -485,9 +516,10 @@ TEST(TestDecodeTcl112Ac, Issue744) {
IRTcl112Ac ac(kGpioUnused);
ac.setRaw(irsend.capture.state);
EXPECT_EQ(
"Type: 1, Power: On, Mode: 3 (Cool), Temp: 23C, Fan: 0 (Auto), "
"Econo: Off, Health: Off, Turbo: Off, Swing(H): Off, Swing(V): Off, "
"Light: On",
"Model: 1 (TAC09CHSD), Type: 1, Power: On, Mode: 3 (Cool), Temp: 23C, "
"Fan: 0 (Auto), Swing(V): 0 (Auto), Swing(H): Off, "
"Econo: Off, Health: Off, Turbo: Off, Light: On, "
"On Timer: Off, Off Timer: Off",
ac.toString());
}
@@ -527,7 +559,7 @@ TEST(TestDecodeTcl112Ac, Issue1528) {
EXPECT_EQ(kTcl112AcBits, irsend.capture.bits);
EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits);
EXPECT_EQ(
"Type: 2, Quiet: On",
"Model: 1 (TAC09CHSD), Type: 2, Quiet: On",
IRAcUtils::resultAcToString(&irsend.capture));
}
@@ -536,7 +568,6 @@ TEST(TestTcl112AcClass, SendingQuiet) {
IRTcl112Ac ac(kGpioUnused);
IRrecv capture(kGpioUnused);
ac.begin();
ac.on();
ac.setTemp(24);
@@ -559,16 +590,116 @@ TEST(TestTcl112AcClass, SendingQuiet) {
ASSERT_EQ(TCL112AC, ac._irsend.capture.decode_type);
ASSERT_EQ(kTcl112AcBits, ac._irsend.capture.bits);
ASSERT_EQ(
"Type: 2, Quiet: On",
"Model: 1 (TAC09CHSD), Type: 2, Quiet: On",
IRAcUtils::resultAcToString(&ac._irsend.capture));
// Second message.
// TCL112 uses the Mitsubishi112 decoder.
// Skip first message.
EXPECT_TRUE(capture.decodeMitsubishi112(&ac._irsend.capture, 229));
ASSERT_EQ(TCL112AC, ac._irsend.capture.decode_type);
ASSERT_EQ(kTcl112AcBits, ac._irsend.capture.bits);
EXPECT_EQ(TCL112AC, ac._irsend.capture.decode_type);
EXPECT_EQ(kTcl112AcBits, ac._irsend.capture.bits);
ASSERT_EQ(
"Type: 1, Power: On, Mode: 3 (Cool), Temp: 24C, Fan: 0 (Auto), "
"Econo: Off, Health: Off, Turbo: Off, Swing(H): Off, Swing(V): Off, "
"Light: Off", IRAcUtils::resultAcToString(&ac._irsend.capture));
"Model: 1 (TAC09CHSD), Type: 1, Power: On, Mode: 3 (Cool), Temp: 24C, "
"Fan: 0 (Auto), Swing(V): 0 (Auto), Swing(H): Off, "
"Econo: Off, Health: Off, Turbo: Off, Light: Off, "
"On Timer: Off, Off Timer: Off",
IRAcUtils::resultAcToString(&ac._irsend.capture));
}
TEST(TestTcl112AcClass, isTcl) {
const uint8_t tcl_temp16C[kTcl112AcStateLength] = {
0x23, 0xCB, 0x26, 0x01, 0x00, 0x24, 0x03,
0x0F, 0x00, 0x00, 0x00, 0x00, 0x80, 0xCB};
EXPECT_TRUE(IRTcl112Ac::isTcl(tcl_temp16C));
const uint8_t tcl_temp31C[kTcl112AcStateLength] = {
0x23, 0xCB, 0x26, 0x01, 0x00, 0x24, 0x03,
0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xBC};
EXPECT_TRUE(IRTcl112Ac::isTcl(tcl_temp31C));
const uint8_t issue1528[kTcl112AcStateLength] = {
0x23, 0xCB, 0x26, 0x02, 0x00, 0x60, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x85};
EXPECT_TRUE(IRTcl112Ac::isTcl(issue1528));
// Ref: https://cociweb.info/container/hvac_ir_recapture_2719.log
const uint8_t teknopoint[14] = {
0x23, 0xCB, 0x26, 0x01, 0x00, 0x24, 0x03,
0x0F, 0x38, 0x00, 0x00, 0x00, 0x00, 0x83};
EXPECT_FALSE(IRTcl112Ac::isTcl(teknopoint));
}
TEST(TestTcl112AcClass, Timers) {
IRTcl112Ac ac(kGpioUnused);
ac.stateReset();
ac.setOnTimer(0);
ac.setOffTimer(0);
EXPECT_EQ(0, ac.getOnTimer());
EXPECT_EQ(0, ac.getOffTimer());
EXPECT_FALSE(ac._.TimerIndicator);
EXPECT_FALSE(ac._.OnTimerEnabled);
EXPECT_FALSE(ac._.OffTimerEnabled);
ac.setOnTimer(7 * 60);
EXPECT_EQ(7 * 60, ac.getOnTimer());
EXPECT_TRUE(ac._.TimerIndicator);
EXPECT_TRUE(ac._.OnTimerEnabled);
EXPECT_FALSE(ac._.OffTimerEnabled);
ac.setOnTimer(0);
EXPECT_EQ(0, ac.getOnTimer());
EXPECT_FALSE(ac._.TimerIndicator);
EXPECT_FALSE(ac._.OnTimerEnabled);
EXPECT_FALSE(ac._.OffTimerEnabled);
ac.setOffTimer(13 * 60); // Beyond max.
EXPECT_EQ(12 * 60, ac.getOffTimer());
EXPECT_TRUE(ac._.TimerIndicator);
EXPECT_FALSE(ac._.OnTimerEnabled);
EXPECT_TRUE(ac._.OffTimerEnabled);
ac.setOffTimer(0);
EXPECT_EQ(0, ac.getOffTimer());
EXPECT_FALSE(ac._.TimerIndicator);
EXPECT_FALSE(ac._.OnTimerEnabled);
EXPECT_FALSE(ac._.OffTimerEnabled);
// Real messages/states
// Per https://github.com/crankyoldgit/IRremoteESP8266/issues/1486#issuecomment-917545485
const uint8_t ontimer_1h[14] = {
0x23, 0xCB, 0x26, 0x01, 0x00, 0x34, 0x03,
0x00, 0x78, 0x00, 0x06, 0x00, 0x00, 0xCA};
ac.setRaw(ontimer_1h);
EXPECT_EQ(60, ac.getOnTimer());
EXPECT_TRUE(ac._.TimerIndicator);
EXPECT_TRUE(ac._.OnTimerEnabled);
EXPECT_FALSE(ac._.OffTimerEnabled);
const uint8_t ontimer_4h[14] = {
0x23, 0xCB, 0x26, 0x01, 0x00, 0x34, 0x03,
0x00, 0x78, 0x00, 0x18, 0x00, 0x00, 0xDC};
ac.setRaw(ontimer_4h);
EXPECT_EQ(240, ac.getOnTimer());
EXPECT_TRUE(ac._.TimerIndicator);
EXPECT_TRUE(ac._.OnTimerEnabled);
EXPECT_FALSE(ac._.OffTimerEnabled);
EXPECT_EQ(
"Model: 2 (GZ055BE1), Type: 1, Power: On, Mode: 3 (Cool), Temp: 31C, "
"Fan: 0 (Auto), Swing(V): 7 (Swing), "
"On Timer: 04:00, Off Timer: Off",
ac.toString());
const uint8_t offtimer_2h[14] = {
0x23, 0xCB, 0x26, 0x01, 0x00, 0x2C, 0x08,
0x07, 0x78, 0x0C, 0x00, 0x00, 0x00, 0xD4};
ac.setRaw(offtimer_2h);
EXPECT_EQ(120, ac.getOffTimer());
EXPECT_TRUE(ac._.TimerIndicator);
EXPECT_FALSE(ac._.OnTimerEnabled);
EXPECT_TRUE(ac._.OffTimerEnabled);
EXPECT_EQ(
"Model: 2 (GZ055BE1), Type: 1, Power: On, Mode: 8 (Auto), Temp: 24C, "
"Fan: 0 (Auto), Swing(V): 7 (Swing), "
"On Timer: Off, Off Timer: 02:00",
ac.toString());
}

View File

@@ -44,7 +44,9 @@ TEST(TestDecodeTeknopoint, RealExample) {
ASSERT_EQ(kTeknopointBits, irsend.capture.bits);
EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits);
EXPECT_EQ(
"",
"Model: 2 (GZ055BE1), Type: 1, Power: On, Mode: 3 (Cool), Temp: 16C, "
"Fan: 0 (Auto), Swing(V): 1 (Highest), "
"On Timer: Off, Off Timer: Off",
IRAcUtils::resultAcToString(&irsend.capture));
}
@@ -67,7 +69,9 @@ TEST(TestDecodeTeknopoint, SyntheticExample) {
ASSERT_EQ(kTeknopointBits, irsend.capture.bits);
EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits);
EXPECT_EQ(
"",
"Model: 2 (GZ055BE1), Type: 1, Power: On, Mode: 3 (Cool), Temp: 16C, "
"Fan: 0 (Auto), Swing(V): 1 (Highest), "
"On Timer: Off, Off Timer: Off",
IRAcUtils::resultAcToString(&irsend.capture));
EXPECT_EQ(
@@ -95,7 +99,7 @@ TEST(TestUtils, Housekeeping) {
ASSERT_EQ("TEKNOPOINT", typeToString(decode_type_t::TEKNOPOINT));
ASSERT_EQ(decode_type_t::TEKNOPOINT, strToDecodeType("TEKNOPOINT"));
ASSERT_TRUE(hasACState(decode_type_t::TEKNOPOINT));
ASSERT_FALSE(IRac::isProtocolSupported(decode_type_t::TEKNOPOINT));
ASSERT_TRUE(IRac::isProtocolSupported(decode_type_t::TEKNOPOINT));
ASSERT_EQ(kTeknopointBits, IRsend::defaultBits(decode_type_t::TEKNOPOINT));
ASSERT_EQ(kNoRepeat, IRsend::minRepeats(decode_type_t::TEKNOPOINT));
}