Skip to content

Commit

Permalink
Add support for Quiet, Clean & Freeze Protect controls.
Browse files Browse the repository at this point in the history
* Add support for Quiet setting controls.
* Add support for Clean and Freeze Protect toggle controls/settings/logic.
* Add & update unit test coverage.
* Minor code and output cleanup.
* Update supported model info.

Fixes #1733
  • Loading branch information
crankyoldgit committed Jan 11, 2022
1 parent 93af543 commit ffa07de
Show file tree
Hide file tree
Showing 6 changed files with 310 additions and 96 deletions.
21 changes: 15 additions & 6 deletions src/IRac.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1502,16 +1502,21 @@ void IRac::lg(IRLgAc *ac, const lg_ac_remote_model_t model,
/// @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] quiet Run the device in quiet/silent mode.
/// @param[in] prev_quiet The device's previous quiet/silent mode.
/// @param[in] turbo Toggle the device's turbo/powerful mode.
/// @param[in] econo Toggle the device's economical mode.
/// @param[in] light Toggle the LED/Display mode.
/// @param[in] clean Turn on the self-cleaning mode. e.g. XFan, dry filters etc
/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on.
/// @note On Danby A/C units, swingv controls the Ion Filter instead.
void IRac::midea(IRMideaAC *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, const bool turbo,
const bool econo, const bool light, const int16_t sleep) {
const stdAc::swingv_t swingv,
const bool quiet, const bool quiet_prev,
const bool turbo, const bool econo, const bool light,
const bool clean, const int16_t sleep) {
ac->begin();
ac->setPower(on);
ac->setMode(ac->convertMode(mode));
Expand All @@ -1520,12 +1525,12 @@ void IRac::midea(IRMideaAC *ac,
ac->setFan(ac->convertFan(fan));
ac->setSwingVToggle(swingv != stdAc::swingv_t::kOff);
// No Horizontal swing setting available.
// No Quiet setting available.
ac->setQuiet(quiet, quiet_prev);
ac->setTurboToggle(turbo);
ac->setEconoToggle(econo);
ac->setLightToggle(light);
// No Filter setting available.
// No Clean setting available.
ac->setCleanToggle(clean);
// No Beep setting available.
ac->setSleep(sleep >= 0); // Sleep on this A/C is either on or off.
// No Clock setting available.
Expand Down Expand Up @@ -2530,6 +2535,7 @@ stdAc::state_t IRac::handleToggles(const stdAc::state_t desired,
result.turbo = desired.turbo ^ prev->turbo;
result.econo = desired.econo ^ prev->econo;
result.light = desired.light ^ prev->light;
result.clean = desired.clean ^ prev->clean;
// FALL THRU
case decode_type_t::CORONA_AC:
case decode_type_t::HITACHI_AC344:
Expand Down Expand Up @@ -2645,6 +2651,9 @@ bool IRac::sendAc(const stdAc::state_t desired, const stdAc::state_t *prev) {
const stdAc::swingv_t prev_swingv = (prev != NULL) ? prev->swingv
: stdAc::swingv_t::kOff;
#endif // (SEND_LG || SEND_SHARP_AC)
#if SEND_MIDEA
const bool prev_quiet = (prev != NULL) ? prev->quiet : !send.quiet;
#endif // SEND_MIDEA
// Per vendor settings & setup.
switch (send.protocol) {
#if SEND_AIRTON
Expand Down Expand Up @@ -2945,8 +2954,8 @@ bool IRac::sendAc(const stdAc::state_t desired, const stdAc::state_t *prev) {
{
IRMideaAC ac(_pin, _inverted, _modulation);
midea(&ac, send.power, send.mode, send.celsius, send.degrees,
send.fanspeed, send.swingv, send.turbo, send.econo, send.light,
send.sleep);
send.fanspeed, send.swingv, send.quiet, prev_quiet, send.turbo,
send.econo, send.light, send.sleep);
break;
}
#endif // SEND_MIDEA
Expand Down
6 changes: 4 additions & 2 deletions src/IRac.h
Original file line number Diff line number Diff line change
Expand Up @@ -338,8 +338,10 @@ void electra(IRElectraAc *ac,
void midea(IRMideaAC *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, const bool turbo, const bool econo,
const bool light, const int16_t sleep = -1);
const stdAc::swingv_t swingv,
const bool quiet, const bool quiet_prev, const bool turbo,
const bool econo, const bool light, const bool clean,
const int16_t sleep = -1);
#endif // SEND_MIDEA
#if SEND_MIRAGE
void mirage(IRMirageAc *ac, const stdAc::state_t state);
Expand Down
107 changes: 98 additions & 9 deletions src/ir_Midea.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ using irutils::addIntToString;
using irutils::addLabeledString;
using irutils::addModeToString;
using irutils::addTempToString;
using irutils::addToggleToString;
using irutils::minsToString;

#if SEND_MIDEA
Expand Down Expand Up @@ -100,10 +101,13 @@ IRMideaAC::IRMideaAC(const uint16_t pin, const bool inverted,
void IRMideaAC::stateReset(void) {
// Power On, Mode Auto, Fan Auto, Temp = 25C/77F
_.remote_state = 0xA1826FFFFF62;
_SwingVToggle = false;
_CleanToggle = false;
_EconoToggle = false;
_TurboToggle = false;
_8CHeatToggle = false;
_LightToggle = false;
_Quiet = _Quiet_prev = false;
_SwingVToggle = false;
_TurboToggle = false;
#if KAYSUN_AC
_SwingVStep = false;
#endif // KAYSUN_AC
Expand Down Expand Up @@ -135,6 +139,19 @@ void IRMideaAC::send(const uint16_t repeat) {
if (_LightToggle && !isLightToggle())
_irsend.sendMidea(kMideaACToggleLight, kMideaBits, repeat);
_LightToggle = false;
if (getMode() <= kMideaACAuto) { // Only available in Cool, Dry, or Auto mode
if (_CleanToggle && !isCleanToggle())
_irsend.sendMidea(kMideaACToggleSelfClean, kMideaBits, repeat);
_CleanToggle = false;
} else if (getMode() == kMideaACHeat) { // Only available in Heat mode
if (_8CHeatToggle && !is8CHeatToggle())
_irsend.sendMidea(kMideaACToggle8CHeat, kMideaBits, repeat);
_8CHeatToggle = false;
}
if (_Quiet != _Quiet_prev)
_irsend.sendMidea(_Quiet ? kMideaACQuietOn : kMideaACQuietOff,
kMideaBits, repeat);
_Quiet_prev = _Quiet;
}
#endif // SEND_MIDEA

Expand Down Expand Up @@ -410,6 +427,75 @@ bool IRMideaAC::getLightToggle(void) {
return _LightToggle;
}

/// Is the current state a Self-Clean toggle message?
/// @return true, it is. false, it isn't.
bool IRMideaAC::isCleanToggle(void) const {
return _.remote_state == kMideaACToggleSelfClean;
}

/// Set the A/C to toggle the Self Clean mode for the next send.
/// @note Only works in Cool, Dry, or Auto modes.
/// @param[in] on true, the setting is on. false, the setting is off.
void IRMideaAC::setCleanToggle(const bool on) {
_CleanToggle = on && getMode() <= kMideaACAuto;
}

// Get the Self-Clean toggle state of the A/C.
/// @return true, the setting is on. false, the setting is off.
bool IRMideaAC::getCleanToggle(void) {
_CleanToggle |= isCleanToggle();
return _CleanToggle;
}

/// Is the current state a 8C Heat (Freeze Protect) toggle message?
/// @note Only works in Heat mode.
/// @return true, it is. false, it isn't.
bool IRMideaAC::is8CHeatToggle(void) const {
return _.remote_state == kMideaACToggle8CHeat;
}

/// Set the A/C to toggle the 8C Heat (Freeze Protect) mode for the next send.
/// @note Only works in Heat mode.
/// @param[in] on true, the setting is on. false, the setting is off.
void IRMideaAC::set8CHeatToggle(const bool on) {
_8CHeatToggle = on && getMode() == kMideaACHeat;
}

// Get the 8C Heat (Freeze Protect) toggle state of the A/C.
/// @return true, the setting is on. false, the setting is off.
bool IRMideaAC::get8CHeatToggle(void) {
_8CHeatToggle |= is8CHeatToggle();
return _8CHeatToggle;
}

/// Is the current state a Quiet(Silent) message?
/// @return true, it is. false, it isn't.
bool IRMideaAC::isQuiet(void) const {
return (_.remote_state == kMideaACQuietOff ||
_.remote_state == kMideaACQuietOn);
}

/// Set the Quiet (Silent) mode for the next send.
/// @param[in] on true, the setting is on. false, the setting is off.
void IRMideaAC::setQuiet(const bool on) { _Quiet = on; }

/// Set the Quiet (Silent) mode for the next send.
/// @param[in] on true, the setting is on. false, the setting is off.
/// @param[in] prev true, previously the setting was on. false, setting was off.
void IRMideaAC::setQuiet(const bool on, const bool prev) {
setQuiet(on);
_Quiet_prev = prev;
}

// Get the Quiet (Silent) mode state of the A/C.
/// @return true, the setting is on. false, the setting is off.
bool IRMideaAC::getQuiet(void) const {
if (isQuiet())
return _.remote_state == kMideaACQuietOn;
else
return _Quiet;
}

/// Calculate the checksum for a given state.
/// @param[in] state The value to calc the checksum of.
/// @return The calculated checksum value.
Expand Down Expand Up @@ -566,7 +652,7 @@ stdAc::fanspeed_t IRMideaAC::toCommonFanSpeed(const uint8_t speed) {
/// @param[in] prev A Ptr to the previous state.
/// @return The stdAc equivalent of the native settings.
stdAc::state_t IRMideaAC::toCommon(const stdAc::state_t *prev) {
stdAc::state_t result;
stdAc::state_t result{};
if (prev != NULL) {
result = *prev;
} else {
Expand All @@ -577,7 +663,6 @@ stdAc::state_t IRMideaAC::toCommon(const stdAc::state_t *prev) {
result.swingv = stdAc::swingv_t::kOff;
result.quiet = false;
result.turbo = false;
result.clean = false;
result.econo = false;
result.filter = false;
result.light = false;
Expand All @@ -597,6 +682,7 @@ stdAc::state_t IRMideaAC::toCommon(const stdAc::state_t *prev) {
result.fanspeed = toCommonFanSpeed(_.Fan);
result.sleep = _.Sleep ? 0 : -1;
result.econo = getEconoToggle();
result.clean ^= getCleanToggle();
return result;
}

Expand All @@ -612,7 +698,7 @@ String IRMideaAC::toString(void) {
case kMideaACTypeCommand: result += kCommandStr; break;
case kMideaACTypeSpecial: result += kSpecialStr; break;
case kMideaACTypeFollow: result += kFollowStr; break;
default: result += kUnknownStr;
default: result += kUnknownStr;
}
result += ')';
if (message_type != kMideaACTypeSpecial) {
Expand Down Expand Up @@ -643,13 +729,16 @@ String IRMideaAC::toString(void) {
kMideaACFanAuto, kMideaACFanAuto, kMideaACFanMed);
result += addBoolToString(_.Sleep, kSleepStr);
}
result += addBoolToString(getSwingVToggle(), kSwingVToggleStr);
result += addToggleToString(getSwingVToggle(), kSwingVStr);
#if KAYSUN_AC
result += addBoolToString(getSwingVStep(), kStepStr);
#endif // KAYSUN_AC
result += addBoolToString(getEconoToggle(), kEconoToggleStr);
result += addBoolToString(getTurboToggle(), kTurboToggleStr);
result += addBoolToString(getLightToggle(), kLightToggleStr);
result += addToggleToString(getEconoToggle(), kEconoStr);
result += addToggleToString(getTurboToggle(), kTurboStr);
result += addBoolToString(getQuiet(), kQuietStr);
result += addToggleToString(getLightToggle(), kLightStr);
result += addToggleToString(getCleanToggle(), kCleanStr);
result += addToggleToString(get8CHeatToggle(), k8CHeatStr);
return result;
}

Expand Down
32 changes: 27 additions & 5 deletions src/ir_Midea.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
// Brand: Danby, Model: R09C/BCGE remote (MIDEA)
// Brand: Trotec, Model: TROTEC PAC 3900 X (MIDEA)
// Brand: Trotec, Model: RG57H(B)/BGE remote (MIDEA)
// Brand: Lennox, Model: M0STAT60Q-1 remote (MIDEA)

#ifndef IR_MIDEA_H_
#define IR_MIDEA_H_
Expand Down Expand Up @@ -123,9 +124,16 @@ const uint8_t kMideaACFanHigh = 3; // 0b11
// const uint64_t kMideaACToggleIonizer = 0xA201FFFFFF7C;
kSwingVToggleStr = kIonStr;
#endif // DANBY_DAC
const uint64_t kMideaACToggleEcono = 0xA202FFFFFF7E;
const uint64_t kMideaACToggleLight = 0xA208FFFFFF75;
const uint64_t kMideaACToggleTurbo = 0xA209FFFFFF74;
const uint64_t kMideaACToggleEcono = 0xA202FFFFFF7E;
const uint64_t kMideaACToggleLight = 0xA208FFFFFF75;
const uint64_t kMideaACToggleTurbo = 0xA209FFFFFF74;
// Mode must be Auto, Cool, or Dry
const uint64_t kMideaACToggleSelfClean = 0xA20DFFFFFF70;
// 8C Heat AKA Freeze Protection
const uint64_t kMideaACToggle8CHeat = 0xA20FFFFFFF73; // Only in Heat
const uint64_t kMideaACQuietOn = 0xA212FFFFFF6E;
const uint64_t kMideaACQuietOff = 0xA213FFFFFF6F;

const uint8_t kMideaACTypeCommand = 0b001; ///< Message type
const uint8_t kMideaACTypeSpecial = 0b010; ///< Message type
const uint8_t kMideaACTypeFollow = 0b100; ///< Message type
Expand Down Expand Up @@ -202,6 +210,16 @@ class IRMideaAC {
bool isLightToggle(void) const;
void setLightToggle(const bool on);
bool getLightToggle(void);
bool isCleanToggle(void) const;
void setCleanToggle(const bool on);
bool getCleanToggle(void);
bool is8CHeatToggle(void) const;
void set8CHeatToggle(const bool on);
bool get8CHeatToggle(void);
bool isQuiet(void) const;
void setQuiet(const bool on);
void setQuiet(const bool on, const bool prev);
bool getQuiet(void) const;
uint8_t getType(void) const;
bool isOnTimerEnabled(void) const;
uint16_t getOnTimer(void) const;
Expand All @@ -225,13 +243,17 @@ class IRMideaAC {
/// @endcond
#endif // UNIT_TEST
MideaProtocol _;
bool _CleanToggle;
bool _EconoToggle;
bool _8CHeatToggle;
bool _LightToggle;
bool _Quiet;
bool _Quiet_prev;
bool _SwingVToggle;
#if KAYSUN_AC
bool _SwingVStep;
#endif // KAYSUN_AC
bool _EconoToggle;
bool _TurboToggle;
bool _LightToggle;
void checksum(void);
static uint8_t calcChecksum(const uint64_t state);
void setType(const uint8_t type);
Expand Down
7 changes: 5 additions & 2 deletions test/IRac_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1359,8 +1359,8 @@ TEST(TestIRac, Midea) {
char expected[] =
"Type: 1 (Command), Power: On, Mode: 1 (Dry), Celsius: On, "
"Temp: 27C/80F, On Timer: Off, Off Timer: Off, Fan: 2 (Medium), "
"Sleep: On, Swing(V) Toggle: Off, Econo Toggle: Off, "
"Turbo Toggle: Off, Light Toggle: Off";
"Sleep: On, Swing(V): -, Econo: -, "
"Turbo: -, Quiet: Off, Light: -, Clean: -, 8C Heat: -";

ac.begin();
irac.midea(&ac,
Expand All @@ -1370,9 +1370,12 @@ TEST(TestIRac, Midea) {
27, // Degrees
stdAc::fanspeed_t::kMedium, // Fan speed
stdAc::swingv_t::kOff, // Swing(V)
false, // Silent/Quiet
false, // Previous Silent/Quiet setting
false, // Turbo
false, // Econo
false, // Light
false, // Clean
8 * 60 + 0); // Sleep time

ASSERT_EQ(expected, ac.toString());
Expand Down
Loading

0 comments on commit ffa07de

Please sign in to comment.