From d33297376aaa824768a2136a82923a8c5aa42a4c Mon Sep 17 00:00:00 2001 From: David Conran Date: Sun, 6 Sep 2020 23:02:33 +1000 Subject: [PATCH] Technibel: Cleanup and code fixes/improvements. (#1266) * Add Fahrenheit support to `IRac` for Technibel. * Add checksum verification to `decodeTechnibelAc()`. * Add unit tests for `IRac::technibel()` * Fix old horrible typo. * Add more Technibel Unit tests. * Fix poor Technibel Unit test. * [bug] Fix problem with `IRTechnibelAc` initial state not being initalised properly. * [bug] Fix problem where "Auto" being returned for "Cool" mode. (Auto mode doesn't exist!) * Lots of minor code style cleanups. * Refactor some class methods to make them simpler/smaller etc. * Fix/Add some Doxygen comments. Ref #1259 --- src/IRac.cpp | 13 ++-- src/IRac.h | 6 +- src/ir_Technibel.cpp | 149 ++++++++++++++----------------------- src/ir_Technibel.h | 40 +++++----- test/IRac_test.cpp | 109 +++++++++++++++++---------- test/ir_Technibel_test.cpp | 64 +++++++++++++--- 6 files changed, 210 insertions(+), 171 deletions(-) diff --git a/src/IRac.cpp b/src/IRac.cpp index 3c5d9fad..233b7e24 100644 --- a/src/IRac.cpp +++ b/src/IRac.cpp @@ -1755,18 +1755,19 @@ void IRac::tcl112(IRTcl112Ac *ac, /// @param[in, out] ac A Ptr to an IRTechnibelAc 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. /// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. void IRac::technibel(IRTechnibelAc *ac, - const bool on, const stdAc::opmode_t mode, const float degrees, - const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, - const int16_t sleep) { + const bool on, const stdAc::opmode_t mode, const bool celsius, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const int16_t sleep) { ac->begin(); ac->setPower(on); ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); + ac->setTemp(degrees, !celsius); ac->setFan(ac->convertFan(fan)); ac->setSwing(swingv != stdAc::swingv_t::kOff); // No Horizontal swing setting available. @@ -2502,8 +2503,8 @@ bool IRac::sendAc(const stdAc::state_t desired, const stdAc::state_t *prev) { case TECHNIBEL_AC: { IRTechnibelAc ac(_pin, _inverted, _modulation); - technibel(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, - send.sleep); + technibel(&ac, send.power, send.mode, send.celsius, send.degrees, + send.fanspeed, send.swingv, send.sleep); break; } #endif // SEND_TECHNIBEL_AC diff --git a/src/IRac.h b/src/IRac.h index bbcb02f2..75dafc44 100644 --- a/src/IRac.h +++ b/src/IRac.h @@ -384,9 +384,9 @@ void electra(IRElectraAc *ac, #endif // SEND_TCL112AC #if SEND_TECHNIBEL_AC void technibel(IRTechnibelAc *ac, - const bool on, const stdAc::opmode_t mode, const float degrees, - const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, - const int16_t sleep = -1); + const bool on, const stdAc::opmode_t mode, const bool celsius, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const int16_t sleep = -1); #endif // SEND_TECHNIBEL_AC #if SEND_TECO void teco(IRTecoAc *ac, diff --git a/src/ir_Technibel.cpp b/src/ir_Technibel.cpp index 062907cb..b9c955c3 100644 --- a/src/ir_Technibel.cpp +++ b/src/ir_Technibel.cpp @@ -26,7 +26,6 @@ const uint16_t kTechnibelAcOneSpace = 1696;; const uint16_t kTechnibelAcZeroSpace = 564;; const uint32_t kTechnibelAcGap = kDefaultMessageGap; const uint16_t kTechnibelAcFreq = 38000; -const uint16_t kTechnibelAcOverhead = 3; #if SEND_TECHNIBEL_AC @@ -56,9 +55,7 @@ void IRsend::sendTechnibelAc(const uint64_t data, const uint16_t nbits, /// @return A boolean. True if it can decode it, false if it can't. bool IRrecv::decodeTechnibelAc(decode_results *results, uint16_t offset, const uint16_t nbits, const bool strict) { - if (results->rawlen < 2 * nbits + kTechnibelAcOverhead - offset) { - return false; // Too short a message to match. - } + // Compliance if (strict && nbits != kTechnibelAcBits) { return false; } @@ -74,6 +71,9 @@ bool IRrecv::decodeTechnibelAc(decode_results *results, uint16_t offset, kTechnibelAcBitMark, kTechnibelAcGap, true, _tolerance, kMarkExcess, true)) return false; + // Compliance + if (strict && !IRTechnibelAc::validChecksum(data)) return false; + // Success results->decode_type = decode_type_t::TECHNIBEL_AC; results->bits = nbits; @@ -103,19 +103,24 @@ void IRTechnibelAc::send(const uint16_t repeat) { } #endif // SEND_TECHNIBEL_AC -/// Compute the checksum of the internal state. +/// Compute the checksum of the supplied state. /// @param[in] state A valid code for this protocol. -/// @return The checksum byte of the internal state. +/// @return The calculated checksum of the supplied state. uint8_t IRTechnibelAc::calcChecksum(const uint64_t state) { uint8_t sum = 0; // Add up all the 8 bit data chunks. for (uint8_t offset = kTechnibelAcTimerHoursOffset; - offset < kTechnibelAcHeaderOffset; offset += 8) { + offset < kTechnibelAcHeaderOffset; offset += 8) sum += GETBITS64(state, offset, 8); - } - sum = invertBits(sum, 8); - sum += 1; - return sum; + return ~sum + 1; +} + +/// Confirm the checksum of the supplied state is valid. +/// @param[in] state A valid code for this protocol. +/// @return `true` if the checksum is correct, otherwise `false`. +bool IRTechnibelAc::validChecksum(const uint64_t state) { + return calcChecksum(state) == GETBITS64(state, kTechnibelAcChecksumOffset, + kTechnibelAcChecksumSize); } /// Set the checksum of the internal state. @@ -127,23 +132,14 @@ void IRTechnibelAc::checksum(void) { /// Reset the internal state of the emulation. /// @note Mode:Cool, Power:Off, fan:Low, temp:20, swing:Off, sleep:Off void IRTechnibelAc::stateReset(void) { + remote_state = kTechnibelAcResetState; _saved_temp = 20; // DegC (Random reasonable default value) _saved_temp_units = 0; // Celsius - - off(); - setTemp(_saved_temp); - setTempUnit(_saved_temp_units); - setMode(kTechnibelAcCool); - setFan(kTechnibelAcFanLow); - setSwing(false); - setSleep(false); } /// Get a copy of the internal state/code for this protocol. /// @return A code for this protocol based on the current internal state. uint64_t IRTechnibelAc::getRaw(void) { - setBits(&remote_state, kTechnibelAcHeaderOffset, kTechnibelAcHeaderSize, - kTechnibelAcHeader); checksum(); return remote_state; } @@ -175,6 +171,7 @@ bool IRTechnibelAc::getPower(void) { /// Set the temperature unit setting. /// @param[in] fahrenheit true, the unit is °F. false, the unit is °C. void IRTechnibelAc::setTempUnit(const bool fahrenheit) { + _saved_temp_units = fahrenheit; setBit(&remote_state, kTechnibelAcTempUnitBit, fahrenheit); } @@ -188,20 +185,12 @@ bool IRTechnibelAc::getTempUnit(void) { /// @param[in] degrees The temperature in degrees. /// @param[in] fahrenheit The temperature unit: true=°F, false(default)=°C. void IRTechnibelAc::setTemp(const uint8_t degrees, const bool fahrenheit) { - uint8_t temp; - uint8_t temp_min = kTechnibelAcTempMinC; - uint8_t temp_max = kTechnibelAcTempMaxC; setTempUnit(fahrenheit); - if (fahrenheit) { - temp_min = kTechnibelAcTempMinF; - temp_max = kTechnibelAcTempMaxF; - } - temp = std::max(temp_min, degrees); - temp = std::min(temp_max, temp); - _saved_temp = temp; - _saved_temp_units = fahrenheit; - - setBits(&remote_state, kTechnibelAcTempOffset, kTechnibelAcTempSize, temp); + uint8_t temp_min = fahrenheit ? kTechnibelAcTempMinF : kTechnibelAcTempMinC; + uint8_t temp_max = fahrenheit ? kTechnibelAcTempMaxF : kTechnibelAcTempMaxC; + _saved_temp = std::min(temp_max, std::max(temp_min, degrees)); + setBits(&remote_state, kTechnibelAcTempOffset, kTechnibelAcTempSize, + _saved_temp); } /// Get the current temperature setting. @@ -218,13 +207,14 @@ void IRTechnibelAc::setFan(const uint8_t speed) { setFan(kTechnibelAcFanLow); return; } - // Bounds check enforcement - if (speed > kTechnibelAcFanHigh) { - setFan(kTechnibelAcFanHigh); - } else if (speed < kTechnibelAcFanLow) { - setFan(kTechnibelAcFanLow); - } else { - setBits(&remote_state, kTechnibelAcFanOffset, kTechnibelAcFanSize, speed); + switch (speed) { + case kTechnibelAcFanHigh: + case kTechnibelAcFanMedium: + case kTechnibelAcFanLow: + setBits(&remote_state, kTechnibelAcFanOffset, kTechnibelAcFanSize, speed); + break; + default: + setFan(kTechnibelAcFanLow); } } @@ -240,15 +230,11 @@ uint8_t IRTechnibelAc::getFan(void) { uint8_t IRTechnibelAc::convertFan(const stdAc::fanspeed_t speed) { switch (speed) { case stdAc::fanspeed_t::kMin: - case stdAc::fanspeed_t::kLow: - return kTechnibelAcFanLow; - case stdAc::fanspeed_t::kMedium: - return kTechnibelAcFanMedium; + case stdAc::fanspeed_t::kLow: return kTechnibelAcFanLow; + case stdAc::fanspeed_t::kMedium: return kTechnibelAcFanMedium; case stdAc::fanspeed_t::kHigh: - case stdAc::fanspeed_t::kMax: - return kTechnibelAcFanHigh; - default: - return kTechnibelAcFanLow; + case stdAc::fanspeed_t::kMax: return kTechnibelAcFanHigh; + default: return kTechnibelAcFanLow; } } @@ -259,7 +245,6 @@ stdAc::fanspeed_t IRTechnibelAc::toCommonFanSpeed(const uint8_t speed) { switch (speed) { case kTechnibelAcFanHigh: return stdAc::fanspeed_t::kHigh; case kTechnibelAcFanMedium: return stdAc::fanspeed_t::kMedium; - case kTechnibelAcFanLow: return stdAc::fanspeed_t::kLow; default: return stdAc::fanspeed_t::kLow; } } @@ -267,8 +252,7 @@ stdAc::fanspeed_t IRTechnibelAc::toCommonFanSpeed(const uint8_t speed) { /// Get the operating mode setting of the A/C. /// @return The current operating mode setting. uint8_t IRTechnibelAc::getMode(void) { - return GETBITS64(remote_state, kTechnibelAcModeOffset, - kTechnibelAcModeSize); + return GETBITS64(remote_state, kTechnibelAcModeOffset, kTechnibelAcModeSize); } /// Set the operating mode of the A/C. @@ -295,16 +279,10 @@ void IRTechnibelAc::setMode(const uint8_t mode) { /// @return The native equivilant of the enum. uint8_t IRTechnibelAc::convertMode(const stdAc::opmode_t mode) { switch (mode) { - case stdAc::opmode_t::kCool: - return kTechnibelAcCool; - case stdAc::opmode_t::kHeat: - return kTechnibelAcHeat; - case stdAc::opmode_t::kDry: - return kTechnibelAcDry; - case stdAc::opmode_t::kFan: - return kTechnibelAcFan; - default: - return kTechnibelAcCool; + case stdAc::opmode_t::kHeat: return kTechnibelAcHeat; + case stdAc::opmode_t::kDry: return kTechnibelAcDry; + case stdAc::opmode_t::kFan: return kTechnibelAcFan; + default: return kTechnibelAcCool; } } @@ -313,11 +291,10 @@ uint8_t IRTechnibelAc::convertMode(const stdAc::opmode_t mode) { /// @return The stdAc equivilant of the native setting. stdAc::opmode_t IRTechnibelAc::toCommonMode(const uint8_t mode) { switch (mode) { - case kTechnibelAcCool: return stdAc::opmode_t::kCool; case kTechnibelAcHeat: return stdAc::opmode_t::kHeat; case kTechnibelAcDry: return stdAc::opmode_t::kDry; case kTechnibelAcFan: return stdAc::opmode_t::kFan; - default: return stdAc::opmode_t::kAuto; + default: return stdAc::opmode_t::kCool; } } @@ -337,22 +314,14 @@ bool IRTechnibelAc::getSwing(void) { /// @param[in] swing The enum to be converted. /// @return true, the swing is on. false, the swing is off. bool IRTechnibelAc::convertSwing(const stdAc::swingv_t swing) { - switch (swing) { - case stdAc::swingv_t::kOff: - return false; - default: - return true; - } + return swing != stdAc::swingv_t::kOff; } /// Convert a native swing into its stdAc equivilant. /// @param[in] swing true, the swing is on. false, the swing is off. /// @return The stdAc equivilant of the native setting. stdAc::swingv_t IRTechnibelAc::toCommonSwing(const bool swing) { - if (swing) - return stdAc::swingv_t::kAuto; - else - return stdAc::swingv_t::kOff; + return swing ? stdAc::swingv_t::kAuto : stdAc::swingv_t::kOff; } /// Set the Sleep setting of the A/C. @@ -367,8 +336,8 @@ bool IRTechnibelAc::getSleep(void) { return GETBIT64(remote_state, kTechnibelAcSleepBit); } -/// Is the timer function enabled? -/// @return true, the setting is on. false, the setting is off. +/// Set the enable timer setting. +/// @param[in] on true, the setting is on. false, the setting is off. void IRTechnibelAc::setTimerEnabled(const bool on) { setBit(&remote_state, kTechnibelAcTimerEnableBit, on); } @@ -384,23 +353,19 @@ bool IRTechnibelAc::getTimerEnabled(void) { /// `0` will clear the timer. Max is 24 hrs (1440 mins). /// @note Time is stored internaly in hours. void IRTechnibelAc::setTimer(const uint16_t nr_of_mins) { - uint8_t hours = nr_of_mins / 60; - uint8_t value = std::min(kTechnibelAcTimerMax, hours); + const uint8_t hours = nr_of_mins / 60; setBits(&remote_state, kTechnibelAcTimerHoursOffset, kTechnibelAcHoursSize, - value); + std::min(kTechnibelAcTimerMax, hours)); // Enable or not? - setTimerEnabled(value > 0); + setTimerEnabled(hours); } /// Get the timer time for when the A/C unit will switch power state. /// @return The number of minutes left on the timer. `0` means off. uint16_t IRTechnibelAc::getTimer(void) { - uint16_t mins = 0; - if (getTimerEnabled()) { - mins = GETBITS64(remote_state, kTechnibelAcTimerHoursOffset, - kTechnibelAcHoursSize) * 60; - } - return mins; + return getTimerEnabled() ? GETBITS64(remote_state, + kTechnibelAcTimerHoursOffset, + kTechnibelAcHoursSize) * 60 : 0; } /// Convert the current internal state into its stdAc::state_t equivilant. @@ -435,8 +400,8 @@ String IRTechnibelAc::toString(void) { String result = ""; result.reserve(100); // Reserve some heap for the string to reduce fragging. result += addBoolToString(getPower(), kPowerStr, false); - result += addModeToString(getMode(), kTechnibelAcCool, kTechnibelAcCool, - kTechnibelAcHeat, kTechnibelAcDry, + result += addModeToString(getMode(), 255, // No Auto, so use impossible value + kTechnibelAcCool, kTechnibelAcHeat, kTechnibelAcDry, kTechnibelAcFan); result += addFanToString(getFan(), kTechnibelAcFanHigh, kTechnibelAcFanLow, kTechnibelAcFanLow, kTechnibelAcFanLow, @@ -444,10 +409,8 @@ String IRTechnibelAc::toString(void) { result += addTempToString(getTemp(), !getTempUnit()); result += addBoolToString(getSleep(), kSleepStr); result += addBoolToString(getSwing(), kSwingVStr); - if (getTimerEnabled()) - result += addLabeledString(irutils::minsToString(getTimer()), - kTimerStr); - else - result += addBoolToString(false, kTimerStr); + result += addLabeledString(getTimerEnabled() ? minsToString(getTimer()) + : kOffStr, + kTimerStr); return result; } diff --git a/src/ir_Technibel.h b/src/ir_Technibel.h index 73d0b1f8..a71ed19b 100644 --- a/src/ir_Technibel.h +++ b/src/ir_Technibel.h @@ -18,13 +18,8 @@ #endif // Supports: -// Brand: TECHNIBEL, Model: IRO PLUS +// Brand: Technibel, Model: IRO PLUS -// Ref: -// - -// Kudos: -// : For the breakdown and mapping of the bit values. /* State bit map: @@ -50,17 +45,17 @@ const uint8_t kTechnibelAcChecksumOffset = 0; const uint8_t kTechnibelAcChecksumSize = 8; -const uint8_t kTechnibelAcFooterOffset = kTechnibelAcChecksumOffset - + kTechnibelAcChecksumSize; +const uint8_t kTechnibelAcFooterOffset = kTechnibelAcChecksumOffset + + kTechnibelAcChecksumSize; const uint8_t kTechnibelAcFooterSize = 8; -const uint8_t kTechnibelAcTimerHoursOffset = kTechnibelAcFooterOffset - + kTechnibelAcFooterSize; +const uint8_t kTechnibelAcTimerHoursOffset = kTechnibelAcFooterOffset + + kTechnibelAcFooterSize; const uint8_t kTechnibelAcHoursSize = 8; // Max 24 hrs const uint8_t kTechnibelAcTimerMax = 24; -const uint8_t kTechnibelAcTempOffset = kTechnibelAcTimerHoursOffset - + kTechnibelAcHoursSize; +const uint8_t kTechnibelAcTempOffset = kTechnibelAcTimerHoursOffset + + kTechnibelAcHoursSize; const uint8_t kTechnibelAcTempSize = 8; const uint8_t kTechnibelAcTempMinC = 16; // Deg C const uint8_t kTechnibelAcTempMaxC = 31; // Deg C @@ -68,19 +63,19 @@ const uint8_t kTechnibelAcTempMinF = 61; // Deg F const uint8_t kTechnibelAcTempMaxF = 88; // Deg F const uint8_t kTechnibelAcFanOffset = kTechnibelAcTempOffset - + kTechnibelAcTempSize; + + kTechnibelAcTempSize; const uint8_t kTechnibelAcFanSize = 4; const uint8_t kTechnibelAcFanLow = 0b0001; const uint8_t kTechnibelAcFanMedium = 0b0010; const uint8_t kTechnibelAcFanHigh = 0b0100; -const uint8_t kTechnibelAcSleepBit = kTechnibelAcFanOffset - + kTechnibelAcFanSize; +const uint8_t kTechnibelAcSleepBit = kTechnibelAcFanOffset + + kTechnibelAcFanSize; const uint8_t kTechnibelAcSwingBit = kTechnibelAcSleepBit + 1; +// (0 = Celsius, 1 = Fahrenheit) const uint8_t kTechnibelAcTempUnitBit = kTechnibelAcSwingBit + 1; - // (0 = Celsius, 1 = Fahrenheit) const uint8_t kTechnibelAcTimerEnableBit = kTechnibelAcTempUnitBit + 1; @@ -91,8 +86,8 @@ const uint8_t kTechnibelAcDry = 0b0010; const uint8_t kTechnibelAcFan = 0b0100; const uint8_t kTechnibelAcHeat = 0b1000; -const uint8_t kTechnibelAcFanChangeBit = kTechnibelAcModeOffset - + kTechnibelAcModeSize; +const uint8_t kTechnibelAcFanChangeBit = kTechnibelAcModeOffset + + kTechnibelAcModeSize; const uint8_t kTechnibelAcTempChangeBit = kTechnibelAcFanChangeBit + 1; @@ -104,15 +99,22 @@ const uint8_t kTechnibelAcHeaderOffset = kTechnibelAcPowerBit + 1; const uint8_t kTechnibelAcHeaderSize = 8; const uint8_t kTechnibelAcHeader = 0b00011000; +const uint64_t kTechnibelAcResetState = 0x180101140000EA; ///< +///< Mode:Cool, Power:Off, fan:Low, temp:20, swing:Off, sleep:Off + + // Classes class IRTechnibelAc { public: explicit IRTechnibelAc(const uint16_t pin, const bool inverted = false, const bool use_modulation = true); - void stateReset(); #if SEND_TECHNIBEL_AC void send(const uint16_t repeat = kTechnibelAcDefaultRepeat); + /// 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_TECHNIBEL_AC void begin(); diff --git a/test/IRac_test.cpp b/test/IRac_test.cpp index 722163d5..03e952e7 100644 --- a/test/IRac_test.cpp +++ b/test/IRac_test.cpp @@ -97,7 +97,7 @@ TEST(TestIRac, Argo) { stdAc::opmode_t::kHeat, // Mode 21, // Celsius stdAc::fanspeed_t::kHigh, // Fan speed - stdAc::swingv_t::kOff, // Veritcal swing + stdAc::swingv_t::kOff, // Vertical swing false, // Turbo -1); // Sleep EXPECT_TRUE(ac.getPower()); @@ -123,8 +123,8 @@ TEST(TestIRac, Carrier64) { stdAc::opmode_t::kHeat, // Mode 21, // Celsius stdAc::fanspeed_t::kHigh, // Fan speed - stdAc::swingv_t::kAuto, // Veritcal swing - 1); // Sleep + stdAc::swingv_t::kAuto, // Vertical swing + 1); // Sleep EXPECT_TRUE(ac.getPower()); // Power. EXPECT_EQ(kCarrierAc64Heat, ac.getMode()); // Operating mode. EXPECT_EQ(21, ac.getTemp()); // Temperature. @@ -156,7 +156,7 @@ TEST(TestIRac, Coolix) { stdAc::opmode_t::kHeat, // Mode 21, // Celsius stdAc::fanspeed_t::kHigh, // Fan speed - stdAc::swingv_t::kOff, // Veritcal swing + stdAc::swingv_t::kOff, // Vertical swing stdAc::swingh_t::kOff, // Horizontal swing false, // Turbo false, // Light @@ -225,7 +225,7 @@ TEST(TestIRac, Corona) { stdAc::opmode_t::kHeat, // Mode 21, // Celsius stdAc::fanspeed_t::kHigh, // Fan speed - stdAc::swingv_t::kAuto, // Veritcal swing + stdAc::swingv_t::kAuto, // Vertical swing true); // Econo (PowerSave) EXPECT_TRUE(ac.getPower()); // Power. EXPECT_TRUE(ac.getPowerButton()); // Power.button @@ -262,7 +262,7 @@ TEST(TestIRac, Daikin) { stdAc::opmode_t::kCool, // Mode 19, // Celsius stdAc::fanspeed_t::kMax, // Fan speed - stdAc::swingv_t::kOff, // Veritcal swing + stdAc::swingv_t::kOff, // Vertical swing stdAc::swingh_t::kOff, // Horizontal swing false, // Quiet false, // Turbo @@ -294,7 +294,7 @@ TEST(TestIRac, Daikin128) { stdAc::opmode_t::kHeat, // Mode 27, // Celsius stdAc::fanspeed_t::kMin, // Fan speed - stdAc::swingv_t::kAuto, // Veritcal swing + stdAc::swingv_t::kAuto, // Vertical swing true, // Quiet false, // Turbo true, // Light @@ -325,7 +325,7 @@ TEST(TestIRac, Daikin152) { stdAc::opmode_t::kCool, // Mode 27, // Celsius stdAc::fanspeed_t::kMedium, // Fan speed - stdAc::swingv_t::kAuto, // Veritcal swing + stdAc::swingv_t::kAuto, // Vertical swing false, // Quiet false, // Turbo true); // Econo @@ -353,7 +353,7 @@ TEST(TestIRac, Daikin160) { stdAc::opmode_t::kDry, // Mode 23, // Celsius stdAc::fanspeed_t::kMin, // Fan speed - stdAc::swingv_t::kMiddle); // Veritcal swing + stdAc::swingv_t::kMiddle); // Vertical swing ASSERT_EQ(expected, ac.toString()); ac._irsend.makeDecodeResult(); EXPECT_TRUE(capture.decode(&ac._irsend.capture)); @@ -405,7 +405,7 @@ TEST(TestIRac, Daikin2) { stdAc::opmode_t::kCool, // Mode 19, // Celsius stdAc::fanspeed_t::kLow, // Fan speed - stdAc::swingv_t::kOff, // Veritcal swing + stdAc::swingv_t::kOff, // Vertical swing stdAc::swingh_t::kMiddle, // Horizontal swing false, // Quiet false, // Turbo @@ -440,7 +440,7 @@ TEST(TestIRac, Daikin216) { stdAc::opmode_t::kHeat, // Mode 31, // Celsius stdAc::fanspeed_t::kMedium, // Fan speed - stdAc::swingv_t::kAuto, // Veritcal swing + stdAc::swingv_t::kAuto, // Vertical swing stdAc::swingh_t::kLeft, // Horizontal swing true, // Quiet false); // Turbo (Powerful) @@ -469,7 +469,7 @@ TEST(TestIRac, Daikin64) { stdAc::opmode_t::kCool, // Mode 27, // Celsius stdAc::fanspeed_t::kLow, // Fan Speed - stdAc::swingv_t::kAuto, // Veritcal swing + stdAc::swingv_t::kAuto, // Vertical swing false, // Quiet false, // Turbo 360, // Sleep @@ -521,7 +521,7 @@ TEST(TestIRac, Electra) { stdAc::opmode_t::kFan, // Mode 26, // Celsius stdAc::fanspeed_t::kHigh, // Fan speed - stdAc::swingv_t::kAuto, // Veritcal swing + stdAc::swingv_t::kAuto, // Vertical swing stdAc::swingh_t::kLeft, // Horizontal swing true, // Turbo true, // Light (toggle) @@ -555,7 +555,7 @@ TEST(TestIRac, Fujitsu) { stdAc::opmode_t::kCool, // Mode 19, // Celsius stdAc::fanspeed_t::kMedium, // Fan speed - stdAc::swingv_t::kOff, // Veritcal swing + stdAc::swingv_t::kOff, // Vertical swing stdAc::swingh_t::kOff, // Horizontal swing false, // Quiet false, // Turbo (Powerful) @@ -578,7 +578,7 @@ TEST(TestIRac, Fujitsu) { stdAc::opmode_t::kCool, // Mode 19, // Celsius stdAc::fanspeed_t::kMedium, // Fan speed - stdAc::swingv_t::kOff, // Veritcal swing + stdAc::swingv_t::kOff, // Vertical swing stdAc::swingh_t::kOff, // Horizontal swing false, // Quiet false, // Turbo (Powerful) @@ -599,7 +599,7 @@ TEST(TestIRac, Fujitsu) { stdAc::opmode_t::kCool, // Mode 19, // Celsius stdAc::fanspeed_t::kMedium, // Fan speed - stdAc::swingv_t::kOff, // Veritcal swing + stdAc::swingv_t::kOff, // Vertical swing stdAc::swingh_t::kOff, // Horizontal swing false, // Quiet false, // Turbo (Powerful) @@ -629,7 +629,7 @@ TEST(TestIRac, Goodweather) { stdAc::opmode_t::kCool, // Mode 19, // Celsius stdAc::fanspeed_t::kMedium, // Fan speed - stdAc::swingv_t::kHigh, // Veritcal swing + stdAc::swingv_t::kHigh, // Vertical swing true, // Turbo true, // Light 8 * 60 + 0); // Sleep time @@ -661,7 +661,7 @@ TEST(TestIRac, Gree) { false, // Celsius 71, // Degrees (F) stdAc::fanspeed_t::kMedium, // Fan speed - stdAc::swingv_t::kHigh, // Veritcal swing + stdAc::swingv_t::kHigh, // Vertical swing false, // Turbo true, // Light true, // Clean (aka Mold/XFan) @@ -691,7 +691,7 @@ TEST(TestIRac, Haier) { stdAc::opmode_t::kCool, // Mode 24, // Celsius stdAc::fanspeed_t::kMedium, // Fan speed - stdAc::swingv_t::kHigh, // Veritcal swing + stdAc::swingv_t::kHigh, // Vertical swing true, // Filter 8 * 60 + 0, // Sleep time 13 * 60 + 45); // Clock @@ -721,7 +721,7 @@ TEST(TestIRac, HaierYrwo2) { stdAc::opmode_t::kCool, // Mode 23, // Celsius stdAc::fanspeed_t::kMedium, // Fan speed - stdAc::swingv_t::kHigh, // Veritcal swing + stdAc::swingv_t::kHigh, // Vertical swing true, // Turbo true, // Filter 8 * 60 + 0); // Sleep time @@ -749,7 +749,7 @@ TEST(TestIRac, Hitachi) { stdAc::opmode_t::kAuto, // Mode 22, // Celsius stdAc::fanspeed_t::kMedium, // Fan speed - stdAc::swingv_t::kOff, // Veritcal swing + stdAc::swingv_t::kOff, // Vertical swing stdAc::swingh_t::kAuto); // Horizontal swing ASSERT_EQ(expected, ac.toString()); @@ -905,7 +905,7 @@ TEST(TestIRac, Kelvinator) { stdAc::opmode_t::kCool, // Mode 19, // Celsius stdAc::fanspeed_t::kMedium, // Fan speed - stdAc::swingv_t::kOff, // Veritcal swing + stdAc::swingv_t::kOff, // Vertical swing stdAc::swingh_t::kOff, // Horizontal swing false, // Quiet false, // Turbo @@ -993,7 +993,7 @@ TEST(TestIRac, Mitsubishi) { stdAc::opmode_t::kCool, // Mode 20, // Celsius stdAc::fanspeed_t::kMedium, // Fan speed - stdAc::swingv_t::kOff, // Veritcal swing + stdAc::swingv_t::kOff, // Vertical swing stdAc::swingh_t::kOff, // Horizontal swing false, // Silent 14 * 60 + 35); // Clock @@ -1021,7 +1021,7 @@ TEST(TestIRac, Mitsubishi136) { stdAc::opmode_t::kDry, // Mode 22, // Celsius stdAc::fanspeed_t::kMax, // Fan speed - stdAc::swingv_t::kHighest, // Veritcal swing + stdAc::swingv_t::kHighest, // Vertical swing false); // Quiet ASSERT_EQ(expected, ac.toString()); ac._irsend.makeDecodeResult(); @@ -1048,7 +1048,7 @@ TEST(TestIRac, MitsubishiHeavy88) { stdAc::opmode_t::kCool, // Mode 21, // Celsius stdAc::fanspeed_t::kMedium, // Fan speed - stdAc::swingv_t::kAuto, // Veritcal swing + stdAc::swingv_t::kAuto, // Vertical swing stdAc::swingh_t::kOff, // Horizontal swing false, // Turbo false, // Econo @@ -1078,7 +1078,7 @@ TEST(TestIRac, MitsubishiHeavy152) { stdAc::opmode_t::kCool, // Mode 20, // Celsius stdAc::fanspeed_t::kLow, // Fan speed - stdAc::swingv_t::kOff, // Veritcal swing + stdAc::swingv_t::kOff, // Vertical swing stdAc::swingh_t::kAuto, // Horizontal swing true, // Silent false, // Turbo @@ -1112,7 +1112,7 @@ TEST(TestIRac, Neoclima) { stdAc::opmode_t::kCool, // Mode 20, // Celsius stdAc::fanspeed_t::kLow, // Fan speed - stdAc::swingv_t::kOff, // Veritcal swing + stdAc::swingv_t::kOff, // Vertical swing stdAc::swingh_t::kAuto, // Horizontal swing false, // Turbo true, // Light @@ -1144,7 +1144,7 @@ TEST(TestIRac, Panasonic) { stdAc::opmode_t::kHeat, // Mode 28, // Celsius stdAc::fanspeed_t::kMedium, // Fan speed - stdAc::swingv_t::kAuto, // Veritcal swing + stdAc::swingv_t::kAuto, // Vertical swing stdAc::swingh_t::kLeft, // Horizontal swing true, // Quiet false, // Turbo @@ -1171,7 +1171,7 @@ TEST(TestIRac, Panasonic) { stdAc::opmode_t::kCool, // Mode 18, // Celsius stdAc::fanspeed_t::kMax, // Fan speed - stdAc::swingv_t::kHigh, // Veritcal swing + stdAc::swingv_t::kHigh, // Vertical swing stdAc::swingh_t::kMiddle, // Horizontal swing false, // Quiet true, // Turbo @@ -1201,7 +1201,7 @@ TEST(TestIRac, Samsung) { stdAc::opmode_t::kAuto, // Mode 28, // Celsius stdAc::fanspeed_t::kMedium, // Fan speed - stdAc::swingv_t::kAuto, // Veritcal swing + stdAc::swingv_t::kAuto, // Vertical swing true, // Quiet false, // Turbo true, // Light (Display) @@ -1225,7 +1225,7 @@ TEST(TestIRac, Samsung) { stdAc::opmode_t::kAuto, // Mode 28, // Celsius stdAc::fanspeed_t::kMedium, // Fan speed - stdAc::swingv_t::kAuto, // Veritcal swing + stdAc::swingv_t::kAuto, // Vertical swing true, // Quiet false, // Turbo true, // Light (Display) @@ -1292,7 +1292,7 @@ TEST(TestIRac, Sharp) { stdAc::opmode_t::kCool, // Mode 28, // Celsius stdAc::fanspeed_t::kMedium, // Fan speed - stdAc::swingv_t::kAuto, // Veritcal swing + stdAc::swingv_t::kAuto, // Vertical swing false, // Turbo true, // Filter (Ion) false); // Clean @@ -1320,7 +1320,7 @@ TEST(TestIRac, Tcl112) { stdAc::opmode_t::kCool, // Mode 20, // Celsius stdAc::fanspeed_t::kMedium, // Fan speed - stdAc::swingv_t::kOff, // Veritcal swing + stdAc::swingv_t::kOff, // Vertical swing stdAc::swingh_t::kAuto, // Horizontal swing false, // Turbo true, // Light @@ -1336,6 +1336,33 @@ TEST(TestIRac, Tcl112) { ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p)); } +TEST(TestIRac, Technibel) { + IRTechnibelAc ac(kGpioUnused); + IRac irac(kGpioUnused); + IRrecv capture(kGpioUnused); + char expected[] = + "Power: On, Mode: 8 (Heat), Fan: 2 (Medium), Temp: 72F, Sleep: On, " + "Swing(V): On, Timer: Off"; + + ac.begin(); + irac.technibel(&ac, + true, // Power + stdAc::opmode_t::kHeat, // Mode + false, // Celsius + 72, // Degrees + stdAc::fanspeed_t::kMedium, // Fan speed + stdAc::swingv_t::kAuto, // Vertical swing + 8 * 60 + 30); // Sleep + ASSERT_EQ(expected, ac.toString()); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(decode_type_t::TECHNIBEL_AC, ac._irsend.capture.decode_type); + ASSERT_EQ(kTechnibelAcBits, 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, Teco) { IRTecoAc ac(kGpioUnused); IRac irac(kGpioUnused); @@ -1350,7 +1377,7 @@ TEST(TestIRac, Teco) { stdAc::opmode_t::kAuto, // Mode 21, // Celsius stdAc::fanspeed_t::kMedium, // Fan speed - stdAc::swingv_t::kAuto, // Veritcal swing + stdAc::swingv_t::kAuto, // Vertical swing true, // Light 8 * 60 + 30); // Sleep ASSERT_EQ(expected, ac.toString()); @@ -1479,7 +1506,7 @@ TEST(TestIRac, Vestel) { stdAc::opmode_t::kAuto, // Mode 22, // Celsius stdAc::fanspeed_t::kLow, // Fan speed - stdAc::swingv_t::kHigh, // Veritcal swing + stdAc::swingv_t::kHigh, // Vertical swing false, // Turbo true, // Filter 8 * 60 + 0); // Sleep time @@ -1503,7 +1530,7 @@ TEST(TestIRac, Vestel) { stdAc::opmode_t::kAuto, // Mode 22, // Celsius stdAc::fanspeed_t::kLow, // Fan speed - stdAc::swingv_t::kHigh, // Veritcal swing + stdAc::swingv_t::kHigh, // Vertical swing false, // Turbo true, // Filter 8 * 60 + 0, // Sleep time @@ -1527,7 +1554,7 @@ TEST(TestIRac, Vestel) { stdAc::opmode_t::kAuto, // Mode 22, // Celsius stdAc::fanspeed_t::kLow, // Fan speed - stdAc::swingv_t::kHigh, // Veritcal swing + stdAc::swingv_t::kHigh, // Vertical swing false, // Turbo true, // Filter 8 * 60 + 0, // Sleep time @@ -1649,7 +1676,7 @@ TEST(TestIRac, Whirlpool) { stdAc::opmode_t::kAuto, // Mode 21, // Celsius stdAc::fanspeed_t::kMedium, // Fan speed - stdAc::swingv_t::kAuto, // Veritcal swing + stdAc::swingv_t::kAuto, // Vertical swing false, // Turbo true, // Light 8 * 60 + 30, // Sleep @@ -1951,7 +1978,7 @@ TEST(TestIRac, Issue821) { result.mode, // Mode result.degrees, // Celsius result.fanspeed, // Fan speed - result.swingv, // Veritcal swing + result.swingv, // Vertical swing result.swingh, // Horizontal swing result.turbo, // Turbo result.light, // Light @@ -2043,7 +2070,7 @@ TEST(TestIRac, Issue1001) { result.mode, // Mode result.degrees, // Celsius result.fanspeed, // Fan speed - result.swingv, // Veritcal swing + result.swingv, // Vertical swing result.turbo, // Turbo result.light, // Light result.sleep); // Sleep @@ -2074,7 +2101,7 @@ TEST(TestIRac, Issue1001) { result.mode, // Mode result.degrees, // Celsius result.fanspeed, // Fan speed - result.swingv, // Veritcal swing + result.swingv, // Vertical swing result.turbo, // Turbo result.light, // Light result.sleep); // Sleep diff --git a/test/ir_Technibel_test.cpp b/test/ir_Technibel_test.cpp index 16505182..808da1e9 100644 --- a/test/ir_Technibel_test.cpp +++ b/test/ir_Technibel_test.cpp @@ -25,12 +25,12 @@ TEST(TestDecodeTechnibelAc, SyntheticSelfDecode) { irsend.begin(); irsend.reset(); - irsend.sendTechnibelAc(0xD2000048448118); + irsend.sendTechnibelAc(0x1881221200004B); irsend.makeDecodeResult(); ASSERT_TRUE(irrecv.decode(&irsend.capture)); EXPECT_EQ(TECHNIBEL_AC, irsend.capture.decode_type); EXPECT_EQ(kTechnibelAcBits, irsend.capture.bits); - EXPECT_EQ(0xD2000048448118, irsend.capture.value); + EXPECT_EQ(0x1881221200004B, irsend.capture.value); EXPECT_EQ(0, irsend.capture.command); EXPECT_EQ(0, irsend.capture.address); } @@ -62,7 +62,7 @@ TEST(TestDecodeTechnibelAc, RealExample) { EXPECT_EQ(0, irsend.capture.command); EXPECT_EQ(0, irsend.capture.address); EXPECT_EQ( - "Power: On, Mode: 1 (Auto), Fan: 2 (Medium), Temp: 18C, " + "Power: On, Mode: 1 (Cool), Fan: 2 (Medium), Temp: 18C, " "Sleep: Off, Swing(V): On, Timer: Off", IRAcUtils::resultAcToString(&irsend.capture)); stdAc::state_t r, p; @@ -201,12 +201,6 @@ TEST(TestIRTechnibelAcClass, FanSpeed) { ac.begin(); ac.setMode(kTechnibelAcCool); // All fan speeds available in this mode. - ac.setFan(0); - EXPECT_EQ(kTechnibelAcFanLow, ac.getFan()); - - ac.setFan(255); - EXPECT_EQ(kTechnibelAcFanHigh, ac.getFan()); - ac.setFan(kTechnibelAcFanLow); EXPECT_EQ(kTechnibelAcFanLow, ac.getFan()); @@ -215,6 +209,21 @@ TEST(TestIRTechnibelAcClass, FanSpeed) { ac.setFan(kTechnibelAcFanHigh); EXPECT_EQ(kTechnibelAcFanHigh, ac.getFan()); + + ac.setFan(0); + EXPECT_EQ(kTechnibelAcFanLow, ac.getFan()); + + ac.setFan(kTechnibelAcFanMedium); + ac.setFan(255); + EXPECT_EQ(kTechnibelAcFanLow, ac.getFan()); + + // Check fan speed enforcement for Dry mode. + ac.setFan(kTechnibelAcFanMedium); + ac.setMode(kTechnibelAcDry); + EXPECT_EQ(kTechnibelAcFanLow, ac.getFan()); + EXPECT_EQ(kTechnibelAcDry, ac.getMode()); + ac.setFan(kTechnibelAcFanHigh); + EXPECT_EQ(kTechnibelAcFanLow, ac.getFan()); } TEST(TestIRTechnibelAcClass, Swing) { @@ -274,3 +283,40 @@ TEST(TestIRTechnibelAcClass, Timer) { EXPECT_TRUE(ac.getTimerEnabled()); EXPECT_EQ(1440, ac.getTimer()); } + +TEST(TestIRTechnibelAcClass, ConstructKnownState) { + IRTechnibelAc ac(kGpioUnused); + EXPECT_EQ(kTechnibelAcResetState, ac.getRaw()); + ac.on(); + ac.setMode(kTechnibelAcCool); + ac.setFan(kTechnibelAcFanMedium); + ac.setTemp(18); // 18C + ac.setSleep(false); // Off + ac.setSwing(true); // On + EXPECT_EQ(0x1881221200004B, ac.getRaw()); + EXPECT_EQ( + "Power: On, Mode: 1 (Cool), Fan: 2 (Medium), Temp: 18C, " + "Sleep: Off, Swing(V): On, Timer: Off", + ac.toString()); +} + +TEST(TestIRTechnibelAcClass, HumanReadable) { + IRTechnibelAc ac(kGpioUnused); + ac.begin(); + EXPECT_EQ( + "Power: Off, Mode: 1 (Cool), Fan: 1 (Low), Temp: 20C, Sleep: Off, " + "Swing(V): Off, Timer: Off", ac.toString()); + ac.setPower(true); + ac.setTemp(72, true); // 72F + ac.setFan(kTechnibelAcFanMedium); + ac.setSleep(true); + ac.setSwing(true); + EXPECT_EQ( + "Power: On, Mode: 1 (Cool), Fan: 2 (Medium), Temp: 72F, Sleep: On, " + "Swing(V): On, Timer: Off", ac.toString()); + ac.setMode(kTechnibelAcHeat); + ac.setTimer(23 * 60); + EXPECT_EQ( + "Power: On, Mode: 8 (Heat), Fan: 2 (Medium), Temp: 72F, Sleep: On, " + "Swing(V): On, Timer: 23:00", ac.toString()); +}