mirror of
https://github.com/crankyoldgit/IRremoteESP8266.git
synced 2026-01-12 00:05:10 +08:00
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:
52
src/IRac.cpp
52
src/IRac.cpp
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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++;
|
||||
|
||||
|
||||
153
src/ir_Tcl.cpp
153
src/ir_Tcl.cpp
@@ -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);
|
||||
|
||||
98
src/ir_Tcl.h
98
src/ir_Tcl.h
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user