mirror of
https://github.com/crankyoldgit/IRremoteESP8266.git
synced 2026-01-12 00:05:10 +08:00
Add support for new TROTEC_3550 A/C protocol (#1566)
* Add `sendTrotec3550()` and `decodeTrotec3550()` routines. * Add detailed & Common A/C API support for: - Power - Temp (Celsius & Fahrenheit) - SwingV - Mode - Fan Speed - Timer - Checksums * Add and update Unit tests. Ref #1507 Fixes #1563
This commit is contained in:
60
src/IRac.cpp
60
src/IRac.cpp
@@ -280,6 +280,9 @@ bool IRac::isProtocolSupported(const decode_type_t protocol) {
|
||||
#if SEND_TROTEC
|
||||
case decode_type_t::TROTEC:
|
||||
#endif
|
||||
#if SEND_TROTEC_3550
|
||||
case decode_type_t::TROTEC_3550:
|
||||
#endif // SEND_TROTEC_3550
|
||||
#if SEND_TRUMA
|
||||
case decode_type_t::TRUMA:
|
||||
#endif // SEND_TRUMA
|
||||
@@ -2031,6 +2034,39 @@ void IRac::trotec(IRTrotecESP *ac,
|
||||
}
|
||||
#endif // SEND_TROTEC
|
||||
|
||||
#if SEND_TROTEC_3550
|
||||
/// Send a Trotec 3550 A/C message with the supplied settings.
|
||||
/// @param[in, out] ac A Ptr to an IRTrotecESP object to use.
|
||||
/// @param[in] on The power setting.
|
||||
/// @param[in] mode The operation mode setting.
|
||||
/// @param[in] celsius Temperature units. True is Celsius, False is Fahrenheit.
|
||||
/// @param[in] degrees The temperature setting in degrees.
|
||||
/// @param[in] fan The speed setting for the fan.
|
||||
/// @param[in] swingv The vertical swing setting.
|
||||
void IRac::trotec3550(IRTrotec3550 *ac,
|
||||
const bool on, const stdAc::opmode_t mode,
|
||||
const bool celsius, const float degrees,
|
||||
const stdAc::fanspeed_t fan,
|
||||
const stdAc::swingv_t swingv) {
|
||||
ac->begin();
|
||||
ac->setPower(on);
|
||||
ac->setMode(ac->convertMode(mode));
|
||||
ac->setTemp(degrees, celsius);
|
||||
ac->setFan(ac->convertFan(fan));
|
||||
ac->setSwingV(swingv != stdAc::swingv_t::kOff);
|
||||
// No Horizontal swing setting available.
|
||||
// No Quiet setting available.
|
||||
// No Turbo setting available.
|
||||
// No Light setting available.
|
||||
// No Filter setting available.
|
||||
// No Clean setting available.
|
||||
// No Beep setting available.
|
||||
// No Sleep setting available.
|
||||
// No Clock setting available.
|
||||
ac->send();
|
||||
}
|
||||
#endif // SEND_TROTEC_3550
|
||||
|
||||
#if SEND_TRUMA
|
||||
/// Send a Truma A/C message with the supplied settings.
|
||||
/// @param[in, out] ac A Ptr to an IRTrumaAc object to use.
|
||||
@@ -2817,6 +2853,15 @@ bool IRac::sendAc(const stdAc::state_t desired, const stdAc::state_t *prev) {
|
||||
break;
|
||||
}
|
||||
#endif // SEND_TROTEC
|
||||
#if SEND_TROTEC_3550
|
||||
case TROTEC_3550:
|
||||
{
|
||||
IRTrotec3550 ac(_pin, _inverted, _modulation);
|
||||
trotec3550(&ac, send.power, send.mode, send.celsius, send.degrees,
|
||||
send.fanspeed, send.swingv);
|
||||
break;
|
||||
}
|
||||
#endif // SEND_TROTEC_3550
|
||||
#if SEND_TRUMA
|
||||
case TRUMA:
|
||||
{
|
||||
@@ -3421,6 +3466,13 @@ namespace IRAcUtils {
|
||||
return ac.toString();
|
||||
}
|
||||
#endif // DECODE_TROTEC
|
||||
#if DECODE_TROTEC_3550
|
||||
case decode_type_t::TROTEC_3550: {
|
||||
IRTrotec3550 ac(kGpioUnused);
|
||||
ac.setRaw(result->state);
|
||||
return ac.toString();
|
||||
}
|
||||
#endif // DECODE_TROTEC_3550
|
||||
#if DECODE_TRUMA
|
||||
case decode_type_t::TRUMA: {
|
||||
IRTrumaAc ac(kGpioUnused);
|
||||
@@ -4002,6 +4054,14 @@ namespace IRAcUtils {
|
||||
break;
|
||||
}
|
||||
#endif // DECODE_TROTEC
|
||||
#if DECODE_TROTEC_3550
|
||||
case decode_type_t::TROTEC_3550: {
|
||||
IRTrotec3550 ac(kGpioUnused);
|
||||
ac.setRaw(decode->state);
|
||||
*result = ac.toCommon();
|
||||
break;
|
||||
}
|
||||
#endif // DECODE_TROTEC_3550
|
||||
#if DECODE_TRUMA
|
||||
case decode_type_t::TRUMA: {
|
||||
IRTrumaAc ac(kGpioUnused);
|
||||
|
||||
@@ -431,6 +431,12 @@ void electra(IRElectraAc *ac,
|
||||
const bool on, const stdAc::opmode_t mode, const float degrees,
|
||||
const stdAc::fanspeed_t fan, const int16_t sleep = -1);
|
||||
#endif // SEND_TROTEC
|
||||
#if SEND_TROTEC_3550
|
||||
void trotec3550(IRTrotec3550 *ac,
|
||||
const bool on, const stdAc::opmode_t mode,
|
||||
const bool celsius, const float degrees,
|
||||
const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv);
|
||||
#endif // SEND_TROTEC_3550
|
||||
#if SEND_TRUMA
|
||||
void truma(IRTrumaAc *ac,
|
||||
const bool on, const stdAc::opmode_t mode, const float degrees,
|
||||
|
||||
@@ -899,6 +899,10 @@ bool IRrecv::decode(decode_results *results, irparams_t *save,
|
||||
DPRINTLN("Attempting Trotec decode");
|
||||
if (decodeTrotec(results, offset)) return true;
|
||||
#endif // DECODE_TROTEC
|
||||
#if DECODE_TROTEC_3550
|
||||
DPRINTLN("Attempting Trotec 3550 decode");
|
||||
if (decodeTrotec3550(results, offset)) return true;
|
||||
#endif // DECODE_TROTEC_3550
|
||||
#if DECODE_DAIKIN160
|
||||
DPRINTLN("Attempting Daikin160 decode");
|
||||
if (decodeDaikin160(results, offset)) return true;
|
||||
|
||||
@@ -500,6 +500,11 @@ class IRrecv {
|
||||
const uint16_t nbits = kTrotecBits,
|
||||
const bool strict = true);
|
||||
#endif // DECODE_TROTEC
|
||||
#if DECODE_TROTEC_3550
|
||||
bool decodeTrotec3550(decode_results *results, uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kTrotecBits,
|
||||
const bool strict = true);
|
||||
#endif // DECODE_TROTEC_3550
|
||||
#if DECODE_MIDEA
|
||||
bool decodeMidea(decode_results *results, uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kMideaBits,
|
||||
|
||||
@@ -349,6 +349,13 @@
|
||||
#define SEND_TROTEC _IR_ENABLE_DEFAULT_
|
||||
#endif // SEND_TROTEC
|
||||
|
||||
#ifndef DECODE_TROTEC_3550
|
||||
#define DECODE_TROTEC_3550 _IR_ENABLE_DEFAULT_
|
||||
#endif // DECODE_TROTEC_3550
|
||||
#ifndef SEND_TROTEC_3550
|
||||
#define SEND_TROTEC_3550 _IR_ENABLE_DEFAULT_
|
||||
#endif // SEND_TROTEC_3550
|
||||
|
||||
#ifndef DECODE_NIKAI
|
||||
#define DECODE_NIKAI _IR_ENABLE_DEFAULT_
|
||||
#endif // DECODE_NIKAI
|
||||
@@ -782,7 +789,7 @@
|
||||
DECODE_MITSUBISHI112 || DECODE_HITACHI_AC424 || DECODE_HITACHI_AC3 || \
|
||||
DECODE_HITACHI_AC344 || DECODE_CORONA_AC || DECODE_SANYO_AC || \
|
||||
DECODE_VOLTAS || DECODE_MIRAGE || DECODE_HAIER_AC176 || \
|
||||
DECODE_TEKNOPOINT || DECODE_KELON || \
|
||||
DECODE_TEKNOPOINT || DECODE_KELON || DECODE_TROTEC_3550 || \
|
||||
false)
|
||||
// Add any DECODE to the above if it uses result->state (see kStateSizeMax)
|
||||
// you might also want to add the protocol to hasACState function
|
||||
@@ -926,8 +933,9 @@ enum decode_type_t {
|
||||
HAIER_AC176,
|
||||
TEKNOPOINT,
|
||||
KELON,
|
||||
TROTEC_3550,
|
||||
// Add new entries before this one, and update it to point to the last entry.
|
||||
kLastDecodeType = KELON,
|
||||
kLastDecodeType = TROTEC_3550,
|
||||
};
|
||||
|
||||
// Message lengths & required repeat values
|
||||
|
||||
@@ -748,6 +748,7 @@ uint16_t IRsend::defaultBits(const decode_type_t protocol) {
|
||||
case TOSHIBA_AC:
|
||||
return kToshibaACBits;
|
||||
case TROTEC:
|
||||
case TROTEC_3550:
|
||||
return kTrotecBits;
|
||||
case VOLTAS:
|
||||
return kVoltasBits;
|
||||
@@ -1271,6 +1272,11 @@ bool IRsend::send(const decode_type_t type, const uint8_t *state,
|
||||
sendTrotec(state, nbytes);
|
||||
break;
|
||||
#endif // SEND_TROTEC
|
||||
#if SEND_TROTEC_3550
|
||||
case TROTEC_3550:
|
||||
sendTrotec3550(state, nbytes);
|
||||
break;
|
||||
#endif // SEND_TROTEC_3550
|
||||
#if SEND_WHIRLPOOL_AC
|
||||
case WHIRLPOOL_AC:
|
||||
sendWhirlpoolAC(state, nbytes);
|
||||
|
||||
@@ -485,7 +485,12 @@ class IRsend {
|
||||
void sendTrotec(const unsigned char data[],
|
||||
const uint16_t nbytes = kTrotecStateLength,
|
||||
const uint16_t repeat = kTrotecDefaultRepeat);
|
||||
#endif
|
||||
#endif // SEND_TROTEC
|
||||
#if SEND_TROTEC_3550
|
||||
void sendTrotec3550(const unsigned char data[],
|
||||
const uint16_t nbytes = kTrotecStateLength,
|
||||
const uint16_t repeat = kTrotecDefaultRepeat);
|
||||
#endif // SEND_TROTEC_3550
|
||||
#if SEND_NIKAI
|
||||
void sendNikai(uint64_t data, uint16_t nbits = kNikaiBits,
|
||||
uint16_t repeat = kNoRepeat);
|
||||
|
||||
@@ -289,5 +289,6 @@ const PROGMEM char *kAllProtocolNamesStr =
|
||||
D_STR_HAIER_AC176 "\x0"
|
||||
D_STR_TEKNOPOINT "\x0"
|
||||
D_STR_KELON "\x0"
|
||||
D_STR_TROTEC_3550 "\x0"
|
||||
///< New protocol strings should be added just above this line.
|
||||
"\x0"; ///< This string requires double null termination.
|
||||
|
||||
@@ -182,6 +182,7 @@ bool hasACState(const decode_type_t protocol) {
|
||||
case TEKNOPOINT:
|
||||
case TOSHIBA_AC:
|
||||
case TROTEC:
|
||||
case TROTEC_3550:
|
||||
case VOLTAS:
|
||||
case WHIRLPOOL_AC:
|
||||
return true;
|
||||
|
||||
@@ -25,12 +25,21 @@ const uint16_t kTrotecZeroSpace = 592;
|
||||
const uint16_t kTrotecGap = 6184;
|
||||
const uint16_t kTrotecGapEnd = 1500; // made up value
|
||||
|
||||
const uint16_t kTrotec3550HdrMark = 12000;
|
||||
const uint16_t kTrotec3550HdrSpace = 5130;
|
||||
const uint16_t kTrotec3550BitMark = 550;
|
||||
const uint16_t kTrotec3550OneSpace = 1950;
|
||||
const uint16_t kTrotec3550ZeroSpace = 500;
|
||||
|
||||
const uint16_t kTrotec3550TimerMax = 8 * 60; ///< 8 hours in Minutes.
|
||||
|
||||
using irutils::addBoolToString;
|
||||
using irutils::addFanToString;
|
||||
using irutils::addIntToString;
|
||||
using irutils::addLabeledString;
|
||||
using irutils::addModeToString;
|
||||
using irutils::addTempToString;
|
||||
using irutils::minsToString;
|
||||
|
||||
#if SEND_TROTEC
|
||||
/// Send a Trotec message.
|
||||
@@ -338,3 +347,296 @@ bool IRrecv::decodeTrotec(decode_results *results, uint16_t offset,
|
||||
return true;
|
||||
}
|
||||
#endif // DECODE_TROTEC
|
||||
|
||||
#if SEND_TROTEC_3550
|
||||
/// Send a Trotec 3550 message.
|
||||
/// Status: STABLE / Known to be working.
|
||||
/// @param[in] data The message to be sent.
|
||||
/// @param[in] nbytes The number of bytes of message to be sent.
|
||||
/// @param[in] repeat The number of times the command is to be repeated.
|
||||
void IRsend::sendTrotec3550(const unsigned char data[], const uint16_t nbytes,
|
||||
const uint16_t repeat) {
|
||||
sendGeneric(kTrotec3550HdrMark, kTrotec3550HdrSpace,
|
||||
kTrotec3550BitMark, kTrotec3550OneSpace,
|
||||
kTrotec3550BitMark, kTrotec3550ZeroSpace,
|
||||
kTrotec3550BitMark, kDefaultMessageGap,
|
||||
data, nbytes, 38, true, repeat, kDutyDefault);
|
||||
}
|
||||
#endif // SEND_TROTEC_3550
|
||||
|
||||
#if DECODE_TROTEC_3550
|
||||
/// Decode the supplied Trotec 3550 message.
|
||||
/// Status: STABLE / Known to be working.
|
||||
/// @param[in,out] results Ptr to the data to decode & where to store the result
|
||||
/// @param[in] offset The starting index to use when attempting to decode the
|
||||
/// raw data. Typically/Defaults to kStartOffset.
|
||||
/// @param[in] nbits The number of data bits to expect.
|
||||
/// @param[in] strict Flag indicating if we should perform strict matching.
|
||||
/// @return True if it can decode it, false if it can't.
|
||||
bool IRrecv::decodeTrotec3550(decode_results *results, uint16_t offset,
|
||||
const uint16_t nbits, const bool strict) {
|
||||
if (strict && nbits != kTrotecBits) return false;
|
||||
|
||||
// Header + Data + Footer
|
||||
if (!matchGeneric(results->rawbuf + offset, results->state,
|
||||
results->rawlen - offset, nbits,
|
||||
kTrotec3550HdrMark, kTrotec3550HdrSpace,
|
||||
kTrotec3550BitMark, kTrotec3550OneSpace,
|
||||
kTrotec3550BitMark, kTrotec3550ZeroSpace,
|
||||
kTrotec3550BitMark, kDefaultMessageGap)) return false;
|
||||
// Compliance
|
||||
if (strict && !IRTrotec3550::validChecksum(results->state, nbits / 8))
|
||||
return false;
|
||||
// Success
|
||||
results->decode_type = TROTEC_3550;
|
||||
results->bits = nbits;
|
||||
// No need to record the state as we stored it as we decoded it.
|
||||
// As we use result->state, we don't record value, address, or command as it
|
||||
// is a union data type.
|
||||
return true;
|
||||
}
|
||||
#endif // DECODE_TROTEC_3550
|
||||
|
||||
/// Class constructor
|
||||
/// @param[in] pin GPIO to be used when sending.
|
||||
/// @param[in] inverted Is the output signal to be inverted?
|
||||
/// @param[in] use_modulation Is frequency modulation to be used?
|
||||
IRTrotec3550::IRTrotec3550(const uint16_t pin, const bool inverted,
|
||||
const bool use_modulation)
|
||||
: _irsend(pin, inverted, use_modulation) { stateReset(); }
|
||||
|
||||
/// Set up hardware to be able to send a message.
|
||||
void IRTrotec3550::begin(void) { _irsend.begin(); }
|
||||
|
||||
#if SEND_TROTEC_3550
|
||||
/// Send the current internal state as an IR message.
|
||||
/// @param[in] repeat Nr. of times the message will be repeated.
|
||||
void IRTrotec3550::send(const uint16_t repeat) {
|
||||
_irsend.sendTrotec3550(getRaw(), kTrotecStateLength, repeat);
|
||||
}
|
||||
#endif // SEND_TROTEC_3550
|
||||
|
||||
/// Calculate the checksum for a given state.
|
||||
/// @param[in] state The array to calc the checksum of.
|
||||
/// @param[in] length The length/size of the array.
|
||||
/// @return The calculated checksum value.
|
||||
uint8_t IRTrotec3550::calcChecksum(const uint8_t state[],
|
||||
const uint16_t length) {
|
||||
return length ? sumBytes(state, length - 1) : 0;
|
||||
}
|
||||
|
||||
/// Verify the checksum is valid for a given state.
|
||||
/// @param[in] state The array to verify the checksum of.
|
||||
/// @param[in] length The length/size of the array.
|
||||
/// @return true, if the state has a valid checksum. Otherwise, false.
|
||||
bool IRTrotec3550::validChecksum(const uint8_t state[], const uint16_t length) {
|
||||
return state[length - 1] == calcChecksum(state, length);
|
||||
}
|
||||
|
||||
/// Calculate & set the checksum for the current internal state of the remote.
|
||||
void IRTrotec3550::checksum(void) { _.Sum = calcChecksum(_.raw); }
|
||||
|
||||
/// Reset the state of the remote to a known good state/sequence.
|
||||
void IRTrotec3550::stateReset(void) {
|
||||
static const uint8_t kReset[kTrotecStateLength] = {
|
||||
0x55, 0x60, 0x00, 0x0D, 0x00, 0x00, 0x10, 0x88, 0x5A};
|
||||
std::memcpy(_.raw, kReset, kTrotecStateLength);
|
||||
}
|
||||
|
||||
/// 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* IRTrotec3550::getRaw(void) {
|
||||
checksum();
|
||||
return _.raw;
|
||||
}
|
||||
|
||||
/// Set the internal state from a valid code for this protocol.
|
||||
/// @param[in] state A valid code for this protocol.
|
||||
void IRTrotec3550::setRaw(const uint8_t state[]) {
|
||||
memcpy(_.raw, state, kTrotecStateLength);
|
||||
}
|
||||
|
||||
/// Set the requested power state of the A/C to on.
|
||||
void IRTrotec3550::on(void) { setPower(true); }
|
||||
|
||||
/// Set the requested power state of the A/C to off.
|
||||
void IRTrotec3550::off(void) { setPower(false); }
|
||||
|
||||
/// Change the power setting.
|
||||
/// @param[in] on true, the setting is on. false, the setting is off.
|
||||
void IRTrotec3550::setPower(const bool on) { _.Power = on; }
|
||||
|
||||
/// Get the value of the current power setting.
|
||||
/// @return true, the setting is on. false, the setting is off.
|
||||
bool IRTrotec3550::getPower(void) const { return _.Power; }
|
||||
|
||||
/// Set the speed of the fan.
|
||||
/// @param[in] fan The desired setting.
|
||||
void IRTrotec3550::setFan(const uint8_t fan) {
|
||||
uint8_t speed = std::min(fan, kTrotecFanHigh);
|
||||
_.Fan = speed;
|
||||
}
|
||||
|
||||
/// Get the current fan speed setting.
|
||||
/// @return The current fan speed/mode.
|
||||
uint8_t IRTrotec3550::getFan(void) const { return _.Fan; }
|
||||
|
||||
/// Set the operating mode of the A/C.
|
||||
/// @param[in] mode The desired operating mode.
|
||||
void IRTrotec3550::setMode(const uint8_t mode) {
|
||||
_.Mode = (mode > kTrotecFan) ? kTrotecAuto : mode;
|
||||
}
|
||||
|
||||
/// Get the operating mode setting of the A/C.
|
||||
/// @return The current operating mode setting.
|
||||
uint8_t IRTrotec3550::getMode(void) const { return _.Mode; }
|
||||
|
||||
/// Set the temperature.
|
||||
/// @param[in] degrees The temperature in degrees.
|
||||
/// @param[in] celsius Use celsius units. True, Celsius; False Fahrenheit.
|
||||
void IRTrotec3550::setTemp(const uint8_t degrees, const bool celsius) {
|
||||
setTempUnit(celsius);
|
||||
uint8_t minTemp = kTrotec3550MinTempC;
|
||||
uint8_t maxTemp = kTrotec3550MaxTempC;
|
||||
if (!celsius) { // Fahrenheit?
|
||||
minTemp = kTrotec3550MinTempF;
|
||||
maxTemp = kTrotec3550MaxTempF;
|
||||
}
|
||||
uint8_t temp = std::max(degrees, minTemp);
|
||||
temp = std::min(temp, maxTemp);
|
||||
if (celsius) {
|
||||
_.TempC = temp - minTemp;
|
||||
_.TempF = celsiusToFahrenheit(temp) - kTrotec3550MinTempF;
|
||||
} else {
|
||||
_.TempF = temp - minTemp;
|
||||
_.TempC = fahrenheitToCelsius(temp) - kTrotec3550MinTempC;
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the current temperature setting.
|
||||
/// @return The current setting for temp. in degrees.
|
||||
uint8_t IRTrotec3550::getTemp(void) const {
|
||||
return getTempUnit() ? _.TempC + kTrotec3550MinTempC
|
||||
: _.TempF + kTrotec3550MinTempF;
|
||||
}
|
||||
|
||||
/// Set the temperature unit that the A/C will use..
|
||||
/// @param[in] celsius Use celsius units. True, Celsius; False Fahrenheit.
|
||||
void IRTrotec3550::setTempUnit(const bool celsius) { _.Celsius = celsius; }
|
||||
|
||||
/// Get the current temperature unit setting.
|
||||
/// @return True, Celsius; False Fahrenheit.
|
||||
bool IRTrotec3550::getTempUnit(void) const { return _.Celsius; }
|
||||
|
||||
/// Change the Vertical Swing setting.
|
||||
/// @param[in] on true, the setting is on. false, the setting is off.
|
||||
void IRTrotec3550::setSwingV(const bool on) { _.SwingV = on; }
|
||||
|
||||
/// Get the value of the current Vertical Swing setting.
|
||||
/// @return true, the setting is on. false, the setting is off.
|
||||
bool IRTrotec3550::getSwingV(void) const { return _.SwingV; }
|
||||
|
||||
/// Get the number of minutes of the Timer setting.
|
||||
/// @return Nr of minutes.
|
||||
uint16_t IRTrotec3550::getTimer(void) const { return _.TimerHrs * 60; }
|
||||
|
||||
/// Set the number of minutes of the Timer setting.
|
||||
/// @param[in] mins Nr. of Minutes for the Timer. `0` means disable the timer.
|
||||
void IRTrotec3550::setTimer(const uint16_t mins) {
|
||||
_.TimerSet = mins > 0;
|
||||
_.TimerHrs = (std::min(mins, kTrotec3550TimerMax) / 60);
|
||||
}
|
||||
|
||||
/// 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.
|
||||
uint8_t IRTrotec3550::convertMode(const stdAc::opmode_t mode) {
|
||||
switch (mode) {
|
||||
case stdAc::opmode_t::kCool: return kTrotecCool;
|
||||
case stdAc::opmode_t::kDry: return kTrotecDry;
|
||||
case stdAc::opmode_t::kFan: return kTrotecFan;
|
||||
// Note: No Heat mode.
|
||||
default: return kTrotecAuto;
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a stdAc::fanspeed_t enum into it's native speed.
|
||||
/// @param[in] speed The enum to be converted.
|
||||
/// @return The native equivalent of the enum.
|
||||
uint8_t IRTrotec3550::convertFan(const stdAc::fanspeed_t speed) {
|
||||
switch (speed) {
|
||||
case stdAc::fanspeed_t::kMin:
|
||||
case stdAc::fanspeed_t::kLow: return kTrotecFanLow;
|
||||
case stdAc::fanspeed_t::kMedium: return kTrotecFanMed;
|
||||
case stdAc::fanspeed_t::kHigh:
|
||||
case stdAc::fanspeed_t::kMax: return kTrotecFanHigh;
|
||||
default: return kTrotecFanMed;
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a native mode into its stdAc equivalent.
|
||||
/// @param[in] mode The native setting to be converted.
|
||||
/// @return The stdAc equivalent of the native setting.
|
||||
stdAc::opmode_t IRTrotec3550::toCommonMode(const uint8_t mode) {
|
||||
switch (mode) {
|
||||
case kTrotecCool: return stdAc::opmode_t::kCool;
|
||||
case kTrotecDry: return stdAc::opmode_t::kDry;
|
||||
case kTrotecFan: return stdAc::opmode_t::kFan;
|
||||
default: return stdAc::opmode_t::kAuto;
|
||||
}
|
||||
}
|
||||
|
||||
/// 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.
|
||||
stdAc::fanspeed_t IRTrotec3550::toCommonFanSpeed(const uint8_t spd) {
|
||||
switch (spd) {
|
||||
case kTrotecFanHigh: return stdAc::fanspeed_t::kMax;
|
||||
case kTrotecFanMed: return stdAc::fanspeed_t::kMedium;
|
||||
case kTrotecFanLow: return stdAc::fanspeed_t::kMin;
|
||||
default: return stdAc::fanspeed_t::kAuto;
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert the current internal state into its stdAc::state_t equivalent.
|
||||
/// @return The stdAc equivalent of the native settings.
|
||||
stdAc::state_t IRTrotec3550::toCommon(void) const {
|
||||
stdAc::state_t result;
|
||||
result.protocol = decode_type_t::TROTEC_3550;
|
||||
result.power = _.Power;
|
||||
result.mode = toCommonMode(_.Mode);
|
||||
result.celsius = getTempUnit();
|
||||
result.degrees = getTemp();
|
||||
result.fanspeed = toCommonFanSpeed(_.Fan);
|
||||
result.swingv = _.SwingV ? stdAc::swingv_t::kAuto : stdAc::swingv_t::kOff;
|
||||
// Not supported.
|
||||
result.model = -1;
|
||||
result.swingh = stdAc::swingh_t::kOff;
|
||||
result.turbo = false;
|
||||
result.light = false;
|
||||
result.filter = false;
|
||||
result.econo = false;
|
||||
result.quiet = false;
|
||||
result.clean = false;
|
||||
result.beep = false;
|
||||
result.sleep = -1;
|
||||
result.clock = -1;
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Convert the current internal state into a human readable string.
|
||||
/// @return A human readable string.
|
||||
String IRTrotec3550::toString(void) const {
|
||||
String result = "";
|
||||
result.reserve(80); // Reserve some heap for the string to reduce fragging.
|
||||
result += addBoolToString(_.Power, kPowerStr, false);
|
||||
result += addModeToString(_.Mode, kTrotecAuto, kTrotecCool, kTrotecAuto,
|
||||
kTrotecDry, kTrotecFan);
|
||||
result += addTempToString(getTemp(), _.Celsius);
|
||||
result += addFanToString(_.Fan, kTrotecFanHigh, kTrotecFanLow,
|
||||
kTrotecFanHigh, kTrotecFanHigh, kTrotecFanMed);
|
||||
result += addBoolToString(_.SwingV, kSwingVStr);
|
||||
result += addLabeledString(_.TimerSet ? minsToString(getTimer()) : kOffStr,
|
||||
kTimerStr);
|
||||
return result;
|
||||
}
|
||||
|
||||
105
src/ir_Trotec.h
105
src/ir_Trotec.h
@@ -7,8 +7,9 @@
|
||||
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1176
|
||||
|
||||
// Supports:
|
||||
// Brand: Trotec, Model: PAC 3200 A/C
|
||||
// Brand: Duux, Model: Blizzard Smart 10K / DXMA04 A/C
|
||||
// Brand: Trotec, Model: PAC 3200 A/C (TROTEC)
|
||||
// Brand: Trotec, Model: PAC 3550 Pro A/C (TROTEC_3550)
|
||||
// Brand: Duux, Model: Blizzard Smart 10K / DXMA04 A/C (TROTEC)
|
||||
|
||||
#ifndef IR_TROTEC_H_
|
||||
#define IR_TROTEC_H_
|
||||
@@ -74,6 +75,46 @@ const uint8_t kTrotecMaxTemp = 32;
|
||||
|
||||
const uint8_t kTrotecMaxTimer = 23;
|
||||
|
||||
/// Native representation of a Trotec 3550 A/C message.
|
||||
union Trotec3550Protocol{
|
||||
uint8_t raw[kTrotecStateLength]; ///< Remote state in IR code form.
|
||||
struct {
|
||||
// Byte 0
|
||||
uint8_t Intro: 8; // fixed value (0x55)
|
||||
// Byte 1
|
||||
uint8_t SwingV :1;
|
||||
uint8_t Power :1;
|
||||
uint8_t :1; // Unknown
|
||||
uint8_t TimerSet :1;
|
||||
uint8_t TempC :4; // Temp + kTrotec3550MinTempC for degC)
|
||||
// Byte 2
|
||||
uint8_t TimerHrs :4;
|
||||
uint8_t :4; // Unknown
|
||||
// Byte 3
|
||||
uint8_t TempF :5; // Temp + kTrotec3550MinTempF for degF)
|
||||
uint8_t :3; // Unknown
|
||||
// Byte 4
|
||||
uint8_t :8; // Unknown
|
||||
// Byte 5
|
||||
uint8_t :8; // Unknown
|
||||
// Byte 6
|
||||
uint8_t Mode :2;
|
||||
uint8_t :2; // Unknown
|
||||
uint8_t Fan :2;
|
||||
uint8_t :2; // Unknown
|
||||
// Byte 7
|
||||
uint8_t :7; // Unknown
|
||||
uint8_t Celsius :1; // DegC or DegF
|
||||
// Byte 8
|
||||
uint8_t Sum :8;
|
||||
};
|
||||
};
|
||||
|
||||
const uint8_t kTrotec3550MinTempC = 16;
|
||||
const uint8_t kTrotec3550MaxTempC = 30;
|
||||
const uint8_t kTrotec3550MinTempF = 59;
|
||||
const uint8_t kTrotec3550MaxTempF = 86;
|
||||
|
||||
// Legacy defines. (Deprecated)
|
||||
#define TROTEC_AUTO kTrotecAuto
|
||||
#define TROTEC_COOL kTrotecCool
|
||||
@@ -130,6 +171,8 @@ class IRTrotecESP {
|
||||
void setRaw(const uint8_t state[]);
|
||||
static bool validChecksum(const uint8_t state[],
|
||||
const uint16_t length = kTrotecStateLength);
|
||||
static uint8_t calcChecksum(const uint8_t state[],
|
||||
const uint16_t length = kTrotecStateLength);
|
||||
static uint8_t convertMode(const stdAc::opmode_t mode);
|
||||
static uint8_t convertFan(const stdAc::fanspeed_t speed);
|
||||
static stdAc::opmode_t toCommonMode(const uint8_t mode);
|
||||
@@ -146,9 +189,63 @@ class IRTrotecESP {
|
||||
/// @endcond
|
||||
#endif // UNIT_TEST
|
||||
TrotecProtocol _;
|
||||
static uint8_t calcChecksum(const uint8_t state[],
|
||||
const uint16_t length = kTrotecStateLength);
|
||||
void checksum(void);
|
||||
};
|
||||
|
||||
// Class
|
||||
/// Class for handling detailed Trotec 3550 A/C messages.
|
||||
class IRTrotec3550 {
|
||||
public:
|
||||
explicit IRTrotec3550(const uint16_t pin, const bool inverted = false,
|
||||
const bool use_modulation = true);
|
||||
#if SEND_TROTEC_3550
|
||||
void send(const uint16_t repeat = kTrotecDefaultRepeat);
|
||||
/// Run the calibration to calculate uSec timing offsets for this platform.
|
||||
/// @return The uSec timing offset needed per modulation of the IR Led.
|
||||
/// @note This will produce a 65ms IR signal pulse at 38kHz.
|
||||
/// Only ever needs to be run once per object instantiation, if at all.
|
||||
int8_t calibrate(void) { return _irsend.calibrate(); }
|
||||
#endif // SEND_TROTEC_3550
|
||||
void begin(void);
|
||||
void stateReset(void);
|
||||
void on(void);
|
||||
void off(void);
|
||||
void setPower(const bool state);
|
||||
bool getPower(void) const;
|
||||
void setTemp(const uint8_t degrees, const bool celsius = true);
|
||||
uint8_t getTemp(void) const;
|
||||
void setTempUnit(const bool celsius);
|
||||
bool getTempUnit(void) const;
|
||||
void setFan(const uint8_t fan);
|
||||
uint8_t getFan(void) const;
|
||||
uint8_t getMode(void) const;
|
||||
void setMode(const uint8_t mode);
|
||||
bool getSwingV(void) const;
|
||||
void setSwingV(const bool on);
|
||||
uint16_t getTimer(void) const;
|
||||
void setTimer(const uint16_t mins);
|
||||
uint8_t* getRaw(void);
|
||||
void setRaw(const uint8_t state[]);
|
||||
static bool validChecksum(const uint8_t state[],
|
||||
const uint16_t length = kTrotecStateLength);
|
||||
static uint8_t calcChecksum(const uint8_t state[],
|
||||
const uint16_t length = kTrotecStateLength);
|
||||
static uint8_t convertMode(const stdAc::opmode_t mode);
|
||||
static uint8_t convertFan(const stdAc::fanspeed_t speed);
|
||||
static stdAc::opmode_t toCommonMode(const uint8_t mode);
|
||||
static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed);
|
||||
stdAc::state_t toCommon(void) const;
|
||||
String toString(void) const;
|
||||
#ifndef UNIT_TEST
|
||||
|
||||
private:
|
||||
IRsend _irsend; ///< Instance of the IR send class
|
||||
#else // UNIT_TEST
|
||||
/// @cond IGNORE
|
||||
IRsendTest _irsend; ///< Instance of the testing IR send class
|
||||
/// @endcond
|
||||
#endif // UNIT_TEST
|
||||
Trotec3550Protocol _;
|
||||
void checksum(void);
|
||||
};
|
||||
#endif // IR_TROTEC_H_
|
||||
|
||||
@@ -784,6 +784,9 @@
|
||||
#ifndef D_STR_TROTEC
|
||||
#define D_STR_TROTEC "TROTEC"
|
||||
#endif // D_STR_TROTEC
|
||||
#ifndef D_STR_TROTEC_3550
|
||||
#define D_STR_TROTEC_3550 D_STR_TROTEC "_3550"
|
||||
#endif // D_STR_TROTEC_3550
|
||||
#ifndef D_STR_TRUMA
|
||||
#define D_STR_TRUMA "TRUMA"
|
||||
#endif // D_STR_TRUMA
|
||||
|
||||
@@ -1824,6 +1824,36 @@ TEST(TestIRac, Trotec) {
|
||||
ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p));
|
||||
}
|
||||
|
||||
TEST(TestIRac, Trotec3550) {
|
||||
IRTrotec3550 ac(kGpioUnused);
|
||||
IRac irac(kGpioUnused);
|
||||
IRrecv capture(kGpioUnused);
|
||||
char expected[] =
|
||||
"Power: On, Mode: 1 (Cool), Temp: 18C, Fan: 3 (High), Swing(V): On, "
|
||||
"Timer: Off";
|
||||
ac.begin();
|
||||
irac.trotec3550(&ac,
|
||||
true, // Power
|
||||
stdAc::opmode_t::kCool, // Mode
|
||||
true, // Celsius
|
||||
18, // Degrees
|
||||
stdAc::fanspeed_t::kHigh, // Fan speed
|
||||
stdAc::swingv_t::kAuto); // Vertical Swing
|
||||
EXPECT_TRUE(ac.getPower());
|
||||
EXPECT_EQ(kTrotecCool, ac.getMode());
|
||||
EXPECT_EQ(18, ac.getTemp());
|
||||
EXPECT_EQ(kTrotecFanHigh, ac.getFan());
|
||||
EXPECT_TRUE(ac.getSwingV());
|
||||
ASSERT_EQ(expected, ac.toString());
|
||||
ac._irsend.makeDecodeResult();
|
||||
EXPECT_TRUE(capture.decode(&ac._irsend.capture));
|
||||
ASSERT_EQ(TROTEC_3550, ac._irsend.capture.decode_type);
|
||||
ASSERT_EQ(kTrotecBits, ac._irsend.capture.bits);
|
||||
ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture));
|
||||
stdAc::state_t r, p;
|
||||
ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p));
|
||||
}
|
||||
|
||||
TEST(TestIRac, Truma) {
|
||||
IRTrumaAc ac(kGpioUnused);
|
||||
IRac irac(kGpioUnused);
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
|
||||
TEST(TestTrotecESPClass, toCommon) {
|
||||
IRTrotecESP ac(0);
|
||||
IRTrotecESP ac(kGpioUnused);
|
||||
ac.setPower(true);
|
||||
ac.setMode(kTrotecCool);
|
||||
ac.setTemp(20);
|
||||
@@ -38,7 +38,7 @@ TEST(TestTrotecESPClass, toCommon) {
|
||||
}
|
||||
|
||||
TEST(TestTrotecESPClass, MessageConstructon) {
|
||||
IRTrotecESP ac(0);
|
||||
IRTrotecESP ac(kGpioUnused);
|
||||
ac.setPower(true);
|
||||
ac.setTemp(20);
|
||||
ac.setMode(kTrotecCool);
|
||||
@@ -57,7 +57,7 @@ TEST(TestTrotecESPClass, MessageConstructon) {
|
||||
|
||||
// Test sending typical data only.
|
||||
TEST(TestSendTrotec, SendDataOnly) {
|
||||
IRsendTest irsend(0);
|
||||
IRsendTest irsend(kGpioUnused);
|
||||
irsend.begin();
|
||||
uint8_t data[kTrotecStateLength] = {
|
||||
0x12, 0x34, 0x29, 0x82, 0x00, 0x00, 0x00, 0x00, 0xAB};
|
||||
@@ -84,8 +84,8 @@ TEST(TestSendTrotec, SendDataOnly) {
|
||||
// Decode normal Trotec messages.
|
||||
|
||||
TEST(TestDecodeTrotec, SyntheticDecode) {
|
||||
IRsendTest irsend(0);
|
||||
IRrecv irrecv(0);
|
||||
IRsendTest irsend(kGpioUnused);
|
||||
IRrecv irrecv(kGpioUnused);
|
||||
irsend.begin();
|
||||
|
||||
// Synthesised Normal Trotec message.
|
||||
@@ -105,9 +105,8 @@ TEST(TestDecodeTrotec, SyntheticDecode) {
|
||||
ASSERT_TRUE(IRAcUtils::decodeToState(&irsend.capture, &r, &p));
|
||||
}
|
||||
|
||||
|
||||
TEST(TestTrotecESPClass, SetAndGetTemp) {
|
||||
IRTrotecESP ac(0);
|
||||
IRTrotecESP ac(kGpioUnused);
|
||||
|
||||
ac.setTemp(25);
|
||||
EXPECT_EQ(25, ac.getTemp());
|
||||
@@ -122,7 +121,7 @@ TEST(TestTrotecESPClass, SetAndGetTemp) {
|
||||
}
|
||||
|
||||
TEST(TestTrotecESPClass, SetAndGetMode) {
|
||||
IRTrotecESP ac(0);
|
||||
IRTrotecESP ac(kGpioUnused);
|
||||
|
||||
ac.setMode(kTrotecFan);
|
||||
EXPECT_EQ(kTrotecFan, ac.getMode());
|
||||
@@ -137,7 +136,7 @@ TEST(TestTrotecESPClass, SetAndGetMode) {
|
||||
}
|
||||
|
||||
TEST(TestTrotecESPClass, SetAndGetFan) {
|
||||
IRTrotecESP ac(0);
|
||||
IRTrotecESP ac(kGpioUnused);
|
||||
|
||||
ac.setSpeed(kTrotecFanHigh);
|
||||
EXPECT_EQ(kTrotecFanHigh, ac.getSpeed());
|
||||
@@ -154,7 +153,7 @@ TEST(TestTrotecESPClass, SetAndGetFan) {
|
||||
}
|
||||
|
||||
TEST(TestTrotecESPClass, Sleep) {
|
||||
IRTrotecESP ac(0);
|
||||
IRTrotecESP ac(kGpioUnused);
|
||||
ac.setSleep(false);
|
||||
ASSERT_FALSE(ac.getSleep());
|
||||
ac.setSleep(true);
|
||||
@@ -164,7 +163,7 @@ TEST(TestTrotecESPClass, Sleep) {
|
||||
}
|
||||
|
||||
TEST(TestTrotecESPClass, Power) {
|
||||
IRTrotecESP ac(0);
|
||||
IRTrotecESP ac(kGpioUnused);
|
||||
ac.setPower(false);
|
||||
ASSERT_FALSE(ac.getPower());
|
||||
ac.setPower(true);
|
||||
@@ -178,4 +177,275 @@ TEST(TestUtils, Housekeeping) {
|
||||
ASSERT_EQ(decode_type_t::TROTEC, strToDecodeType("TROTEC"));
|
||||
ASSERT_TRUE(hasACState(decode_type_t::TROTEC));
|
||||
ASSERT_TRUE(IRac::isProtocolSupported(decode_type_t::TROTEC));
|
||||
|
||||
ASSERT_EQ("TROTEC_3550", typeToString(decode_type_t::TROTEC_3550));
|
||||
ASSERT_EQ(decode_type_t::TROTEC_3550, strToDecodeType("TROTEC_3550"));
|
||||
ASSERT_TRUE(hasACState(decode_type_t::TROTEC_3550));
|
||||
ASSERT_TRUE(IRac::isProtocolSupported(decode_type_t::TROTEC_3550));
|
||||
}
|
||||
|
||||
TEST(TestDecodeTrotec3550, RealDecode) {
|
||||
IRsendTest irsend(kGpioUnused);
|
||||
IRrecv irrecv(kGpioUnused);
|
||||
irsend.begin();
|
||||
|
||||
// Data from:
|
||||
// https://docs.google.com/spreadsheets/d/1j4H1TDJ2QRFLPnls_sxkTZ3c9922TWxqIxwyGWwBIn0/edit#gid=2015400244&range=F8:EV8
|
||||
const uint16_t rawData[147] = {
|
||||
12005, 5130,
|
||||
545, 500, 600, 1950, 540, 500, 595, 1950, 545, 500, 595, 1950, 545, 500,
|
||||
595, 1950, 550, 495, 545, 495, 595, 1950, 545, 500, 545, 500, 540, 500,
|
||||
600, 1950, 590, 1950, 545, 500, 540, 500, 545, 500, 545, 495, 545, 500,
|
||||
545, 500, 545, 500, 540, 500, 545, 495, 545, 500, 545, 500, 545, 500,
|
||||
540, 500, 595, 1950, 520, 520, 595, 1950, 545, 500, 540, 500, 545, 525,
|
||||
515, 500, 545, 520, 520, 525, 520, 500, 540, 525, 520, 500, 540, 500,
|
||||
545, 500, 540, 500, 545, 520, 525, 495, 520, 520, 545, 500, 570, 475,
|
||||
540, 505, 590, 1950, 595, 1950, 545, 500, 540, 500, 570, 470, 595, 1950,
|
||||
570, 1975, 545, 500, 545, 500, 545, 520, 570, 1950, 550, 495, 545, 500,
|
||||
545, 495, 570, 470, 550, 495, 595, 1950, 570, 1975, 545, 525, 570, 1950,
|
||||
595, 1950, 565, 475, 545};
|
||||
const uint8_t expectedState[kTrotecStateLength] = {
|
||||
0x55, 0x23, 0x00, 0x05, 0x00, 0x00, 0x31, 0x88, 0x36};
|
||||
irsend.reset();
|
||||
irsend.sendRaw(rawData, 147, 38000);
|
||||
irsend.makeDecodeResult();
|
||||
EXPECT_TRUE(irrecv.decode(&irsend.capture));
|
||||
EXPECT_EQ(decode_type_t::TROTEC_3550, irsend.capture.decode_type);
|
||||
EXPECT_EQ(kTrotecBits, irsend.capture.bits);
|
||||
EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits);
|
||||
EXPECT_EQ(
|
||||
"Power: On, Mode: 1 (Cool), Temp: 18C, Fan: 3 (High), Swing(V): On, "
|
||||
"Timer: Off",
|
||||
IRAcUtils::resultAcToString(&irsend.capture));
|
||||
stdAc::state_t r, p;
|
||||
ASSERT_TRUE(IRAcUtils::decodeToState(&irsend.capture, &r, &p));
|
||||
}
|
||||
|
||||
TEST(TestDecodeTrotec3550, SyntheticDecode) {
|
||||
IRsendTest irsend(kGpioUnused);
|
||||
IRrecv irrecv(kGpioUnused);
|
||||
irsend.begin();
|
||||
|
||||
// Synthesised Normal Trotec message.
|
||||
irsend.reset();
|
||||
const uint8_t expectedState[kTrotecStateLength] = {
|
||||
0x55, 0x23, 0x00, 0x05, 0x00, 0x00, 0x31, 0x88, 0x36};
|
||||
irsend.sendTrotec3550(expectedState);
|
||||
irsend.makeDecodeResult();
|
||||
EXPECT_TRUE(irrecv.decode(&irsend.capture));
|
||||
EXPECT_EQ(decode_type_t::TROTEC_3550, irsend.capture.decode_type);
|
||||
EXPECT_EQ(kTrotecBits, irsend.capture.bits);
|
||||
EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits);
|
||||
EXPECT_EQ(
|
||||
"Power: On, Mode: 1 (Cool), Temp: 18C, Fan: 3 (High), Swing(V): On, "
|
||||
"Timer: Off",
|
||||
IRAcUtils::resultAcToString(&irsend.capture));
|
||||
stdAc::state_t r, p;
|
||||
ASSERT_TRUE(IRAcUtils::decodeToState(&irsend.capture, &r, &p));
|
||||
}
|
||||
|
||||
TEST(TestTrotec3550Class, SetAndGetMode) {
|
||||
IRTrotec3550 ac(kGpioUnused);
|
||||
|
||||
ac.setMode(kTrotecFan);
|
||||
EXPECT_EQ(kTrotecFan, ac.getMode());
|
||||
ac.setMode(kTrotecCool);
|
||||
EXPECT_EQ(kTrotecCool, ac.getMode());
|
||||
ac.setMode(kTrotecAuto);
|
||||
EXPECT_EQ(kTrotecAuto, ac.getMode());
|
||||
ac.setMode(kTrotecDry);
|
||||
EXPECT_EQ(kTrotecDry, ac.getMode());
|
||||
ac.setMode(255);
|
||||
EXPECT_EQ(kTrotecAuto, ac.getMode());
|
||||
const uint8_t fan[9] = {0x55, 0x23, 0x00, 0x05, 0x00, 0x00, 0x23, 0x88, 0x28};
|
||||
ac.setRaw(fan);
|
||||
EXPECT_EQ(kTrotecFan, ac.getMode());
|
||||
}
|
||||
|
||||
TEST(TestTrotec3550Class, SetAndGetFan) {
|
||||
IRTrotec3550 ac(kGpioUnused);
|
||||
|
||||
ac.setFan(kTrotecFanHigh);
|
||||
EXPECT_EQ(kTrotecFanHigh, ac.getFan());
|
||||
ac.setFan(kTrotecFanLow);
|
||||
EXPECT_EQ(kTrotecFanLow, ac.getFan());
|
||||
ac.setFan(kTrotecFanMed);
|
||||
EXPECT_EQ(kTrotecFanMed, ac.getFan());
|
||||
ac.setFan(kTrotecFanHigh);
|
||||
EXPECT_EQ(kTrotecFanHigh, ac.getFan());
|
||||
ASSERT_NE(7, kTrotecFanHigh);
|
||||
// Now try some unexpected value.
|
||||
ac.setFan(7);
|
||||
EXPECT_EQ(kTrotecFanHigh, ac.getFan());
|
||||
}
|
||||
|
||||
TEST(TestTrotec3550Class, SetAndGetTemp) {
|
||||
IRTrotec3550 ac(kGpioUnused);
|
||||
|
||||
// Celsius
|
||||
ac.setTemp(25);
|
||||
EXPECT_EQ(25, ac.getTemp());
|
||||
EXPECT_TRUE(ac.getTempUnit());
|
||||
ac.setTemp(kTrotec3550MinTempC);
|
||||
EXPECT_EQ(kTrotec3550MinTempC, ac.getTemp());
|
||||
ac.setTemp(kTrotec3550MaxTempC);
|
||||
EXPECT_EQ(kTrotec3550MaxTempC, ac.getTemp());
|
||||
ac.setTemp(kTrotec3550MinTempC - 1);
|
||||
EXPECT_EQ(kTrotec3550MinTempC, ac.getTemp());
|
||||
ac.setTemp(kTrotec3550MaxTempC + 1);
|
||||
EXPECT_EQ(kTrotec3550MaxTempC, ac.getTemp());
|
||||
// Fahrenheit
|
||||
ac.setTemp(72, false);
|
||||
EXPECT_EQ(72, ac.getTemp());
|
||||
EXPECT_FALSE(ac.getTempUnit());
|
||||
ac.setTemp(kTrotec3550MinTempF, false);
|
||||
EXPECT_EQ(kTrotec3550MinTempF, ac.getTemp());
|
||||
ac.setTemp(kTrotec3550MaxTempF, false);
|
||||
EXPECT_EQ(kTrotec3550MaxTempF, ac.getTemp());
|
||||
ac.setTemp(kTrotec3550MinTempF - 1, false);
|
||||
EXPECT_EQ(kTrotec3550MinTempF, ac.getTemp());
|
||||
ac.setTemp(kTrotec3550MaxTempF + 1, false);
|
||||
EXPECT_EQ(kTrotec3550MaxTempF, ac.getTemp());
|
||||
// Celsius
|
||||
ac.setTemp(25, true);
|
||||
EXPECT_EQ(25, ac.getTemp());
|
||||
EXPECT_TRUE(ac.getTempUnit());
|
||||
uint8_t deg79F[9] = {0x55, 0xA3, 0x00, 0x14, 0x00, 0x00, 0x31, 0x40, 0x7D};
|
||||
ac.setRaw(deg79F);
|
||||
EXPECT_FALSE(ac.getTempUnit()); // Fahrenheit
|
||||
EXPECT_EQ(79, ac.getTemp());
|
||||
}
|
||||
|
||||
TEST(TestTrotec3550Class, SwingV) {
|
||||
IRTrotec3550 ac(kGpioUnused);
|
||||
ac.setSwingV(false);
|
||||
ASSERT_FALSE(ac.getSwingV());
|
||||
ac.setSwingV(true);
|
||||
ASSERT_TRUE(ac.getSwingV());
|
||||
ac.setSwingV(false);
|
||||
ASSERT_FALSE(ac.getSwingV());
|
||||
}
|
||||
|
||||
TEST(TestTrotec3550Class, Power) {
|
||||
IRTrotec3550 ac(kGpioUnused);
|
||||
ac.setPower(false);
|
||||
ASSERT_FALSE(ac.getPower());
|
||||
ac.setPower(true);
|
||||
ASSERT_TRUE(ac.getPower());
|
||||
ac.setPower(false);
|
||||
ASSERT_FALSE(ac.getPower());
|
||||
}
|
||||
|
||||
TEST(TestTrotec3550Class, Timer) {
|
||||
IRTrotec3550 ac(kGpioUnused);
|
||||
EXPECT_FALSE(ac._.TimerSet);
|
||||
EXPECT_EQ(0, ac.getTimer());
|
||||
|
||||
ac.setTimer(1);
|
||||
EXPECT_TRUE(ac._.TimerSet);
|
||||
EXPECT_EQ(0, ac.getTimer());
|
||||
|
||||
ac.setTimer(0);
|
||||
EXPECT_FALSE(ac._.TimerSet);
|
||||
EXPECT_EQ(0, ac.getTimer());
|
||||
|
||||
ac.setTimer(1 * 60);
|
||||
EXPECT_TRUE(ac._.TimerSet);
|
||||
EXPECT_EQ(1 * 60, ac.getTimer());
|
||||
|
||||
ac.setTimer(2 * 60 + 37);
|
||||
EXPECT_TRUE(ac._.TimerSet);
|
||||
EXPECT_EQ(2 * 60, ac.getTimer());
|
||||
|
||||
ac.setTimer(7 * 60 + 59);
|
||||
EXPECT_TRUE(ac._.TimerSet);
|
||||
EXPECT_EQ(7 * 60, ac.getTimer());
|
||||
|
||||
ac.setTimer(8 * 60); // Max
|
||||
EXPECT_TRUE(ac._.TimerSet);
|
||||
EXPECT_EQ(8 * 60, ac.getTimer()); // Max
|
||||
|
||||
ac.setTimer(9 * 60); // Beyond max
|
||||
EXPECT_TRUE(ac._.TimerSet);
|
||||
EXPECT_EQ(8 * 60, ac.getTimer()); // Max
|
||||
|
||||
ac.setTimer(0);
|
||||
EXPECT_FALSE(ac._.TimerSet);
|
||||
EXPECT_EQ(0, ac.getTimer());
|
||||
|
||||
// Real data.
|
||||
uint8_t onehour[9] = {0x55, 0xBB, 0x01, 0x15, 0x00, 0x00, 0x31, 0x02, 0x59};
|
||||
ac.setRaw(onehour);
|
||||
EXPECT_TRUE(ac._.TimerSet);
|
||||
EXPECT_EQ(1 * 60, ac.getTimer());
|
||||
EXPECT_EQ(
|
||||
"Power: On, Mode: 1 (Cool), Temp: 80F, Fan: 3 (High), Swing(V): On, "
|
||||
"Timer: 01:00", ac.toString());
|
||||
}
|
||||
|
||||
TEST(TestTrotec3550Class, HumanReadable) {
|
||||
IRTrotec3550 ac(kGpioUnused);
|
||||
EXPECT_EQ(
|
||||
"Power: Off, Mode: 0 (Auto), Temp: 22C, Fan: 1 (Low), Swing(V): Off, "
|
||||
"Timer: Off",
|
||||
ac.toString());
|
||||
// Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/1563#issuecomment-898318429
|
||||
const uint8_t oncool18f3swing[9] = {
|
||||
0x55, 0x23, 0x00, 0x05, 0x00, 0x00, 0x31, 0x88, 0x36};
|
||||
ac.setRaw(oncool18f3swing);
|
||||
EXPECT_EQ(
|
||||
"Power: On, Mode: 1 (Cool), Temp: 18C, Fan: 3 (High), Swing(V): On, "
|
||||
"Timer: Off",
|
||||
ac.toString());
|
||||
const uint8_t offcool18f3swing[9] = {
|
||||
0x55, 0x21, 0x00, 0x05, 0x00, 0x00, 0x31, 0x88, 0x34};
|
||||
ac.setRaw(offcool18f3swing);
|
||||
EXPECT_EQ(
|
||||
"Power: Off, Mode: 1 (Cool), Temp: 18C, Fan: 3 (High), Swing(V): On, "
|
||||
"Timer: Off",
|
||||
ac.toString());
|
||||
const uint8_t modeauto[9] = {
|
||||
0x55, 0x60, 0x00, 0x0D, 0x00, 0x00, 0x10, 0x88, 0x5A};
|
||||
ac.setRaw(modeauto);
|
||||
EXPECT_EQ(
|
||||
"Power: Off, Mode: 0 (Auto), Temp: 22C, Fan: 1 (Low), Swing(V): Off, "
|
||||
"Timer: Off",
|
||||
ac.toString());
|
||||
}
|
||||
|
||||
TEST(TestTrotec3550Class, Checksums) {
|
||||
// Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/1563#issuecomment-898318429
|
||||
const uint8_t oncool18f3swing[9] = {
|
||||
0x55, 0x23, 0x00, 0x05, 0x00, 0x00, 0x31, 0x88, 0x36};
|
||||
EXPECT_EQ(0x36, IRTrotec3550::calcChecksum(oncool18f3swing));
|
||||
ASSERT_TRUE(IRTrotec3550::validChecksum(oncool18f3swing));
|
||||
}
|
||||
|
||||
TEST(TestTrotec3550Class, toCommon) {
|
||||
IRTrotec3550 ac(kGpioUnused);
|
||||
ac.setPower(true);
|
||||
ac.setMode(kTrotecCool);
|
||||
ac.setTemp(20);
|
||||
ac.setFan(kTrotecFanHigh);
|
||||
ac.setSwingV(true);
|
||||
// Now test it.
|
||||
ASSERT_EQ(decode_type_t::TROTEC_3550, ac.toCommon().protocol);
|
||||
ASSERT_TRUE(ac.toCommon().power);
|
||||
ASSERT_TRUE(ac.toCommon().celsius);
|
||||
ASSERT_EQ(20, ac.toCommon().degrees);
|
||||
ASSERT_EQ(stdAc::opmode_t::kCool, ac.toCommon().mode);
|
||||
ASSERT_EQ(stdAc::fanspeed_t::kMax, ac.toCommon().fanspeed);
|
||||
ASSERT_EQ(stdAc::swingv_t::kAuto, ac.toCommon().swingv);
|
||||
// Unsupported.
|
||||
ASSERT_EQ(-1, ac.toCommon().model);
|
||||
ASSERT_EQ(stdAc::swingh_t::kOff, ac.toCommon().swingh);
|
||||
ASSERT_FALSE(ac.toCommon().turbo);
|
||||
ASSERT_FALSE(ac.toCommon().econo);
|
||||
ASSERT_FALSE(ac.toCommon().light);
|
||||
ASSERT_FALSE(ac.toCommon().filter);
|
||||
ASSERT_FALSE(ac.toCommon().clean);
|
||||
ASSERT_FALSE(ac.toCommon().beep);
|
||||
ASSERT_FALSE(ac.toCommon().quiet);
|
||||
ASSERT_EQ(-1, ac.toCommon().sleep);
|
||||
ASSERT_EQ(-1, ac.toCommon().clock);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user