mirror of
https://github.com/crankyoldgit/IRremoteESP8266.git
synced 2026-01-12 00:05:10 +08:00
[MIRAGE] Experimental detailed support for KKG29A-C1 remote. (#1660)
* Add support for KKG29A-C1 model. - IFeel - Sensor Temp - Quiet - Filter (UVC) - Clean - On & Off Timers - SwingH * Add model detection. * Add support to `IRac` class. * Add & Update existing unit tests. * Update supported models. For #1573
This commit is contained in:
44
src/IRac.cpp
44
src/IRac.cpp
@@ -1488,39 +1488,10 @@ void IRac::midea(IRMideaAC *ac,
|
||||
#if SEND_MIRAGE
|
||||
/// Send a Mirage 120-bit A/C message with the supplied settings.
|
||||
/// @param[in, out] ac A Ptr to an IRMitsubishiAC object to use.
|
||||
/// @param[in] on The power setting.
|
||||
/// @param[in] mode The operation mode setting.
|
||||
/// @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] turbo Run the device in turbo mode.
|
||||
/// @param[in] light Turn on the Light/Display.
|
||||
/// @param[in] sleep The time in Nr. of mins to sleep for. < 0 is ignore.
|
||||
/// @note Sleep is either on or off. The time is useless.
|
||||
/// @param[in] clock The time in Nr. of mins since midnight. < 0 is ignore.
|
||||
void IRac::mirage(IRMirageAc *ac,
|
||||
const bool on,
|
||||
const stdAc::opmode_t mode,
|
||||
const float degrees,
|
||||
const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv,
|
||||
const bool turbo, const bool light,
|
||||
const int16_t sleep, const int16_t clock) {
|
||||
/// @param[in] state The desired state to send.
|
||||
void IRac::mirage(IRMirageAc *ac, const stdAc::state_t state) {
|
||||
ac->begin();
|
||||
|
||||
ac->setPower(on);
|
||||
ac->setMode(ac->convertMode(mode));
|
||||
ac->setTemp(degrees);
|
||||
ac->setFan(ac->convertFan(fan));
|
||||
ac->setSwingV(ac->convertSwingV(swingv));
|
||||
// No SwingH setting available
|
||||
ac->setTurbo(turbo);
|
||||
// No Quiet setting available.
|
||||
ac->setLight(light);
|
||||
// No Filter setting available.
|
||||
// No Clean setting available.
|
||||
// No Beep setting available.
|
||||
ac->setSleep(sleep >= 0);
|
||||
if (clock >= 0) ac->setClock(clock * 60); // Clock is in seconds.
|
||||
ac->fromCommon(state);
|
||||
ac->send();
|
||||
}
|
||||
#endif // SEND_MIRAGE
|
||||
@@ -2536,6 +2507,11 @@ stdAc::state_t IRac::handleToggles(const stdAc::state_t desired,
|
||||
case decode_type_t::WHIRLPOOL_AC:
|
||||
result.power = desired.power ^ prev->power;
|
||||
break;
|
||||
case decode_type_t::MIRAGE:
|
||||
if (desired.model == mirage_ac_remote_model_t::KKG29AC1)
|
||||
result.light = desired.light ^ prev->light;
|
||||
result.clean = desired.clean ^ prev->clean;
|
||||
break;
|
||||
case decode_type_t::PANASONIC_AC:
|
||||
// CKP models use a power mode toggle.
|
||||
if (desired.model == panasonic_ac_remote_model_t::kPanasonicCkp)
|
||||
@@ -2910,9 +2886,7 @@ bool IRac::sendAc(const stdAc::state_t desired, const stdAc::state_t *prev) {
|
||||
case MIRAGE:
|
||||
{
|
||||
IRMirageAc ac(_pin, _inverted, _modulation);
|
||||
mirage(&ac, send.power, send.mode, degC,
|
||||
send.fanspeed, send.swingv, send.turbo, send.light,
|
||||
send.sleep, send.clock);
|
||||
mirage(&ac, send);
|
||||
break;
|
||||
}
|
||||
#endif // SEND_MIRAGE
|
||||
|
||||
@@ -332,11 +332,7 @@ void electra(IRElectraAc *ac,
|
||||
const bool light, const int16_t sleep = -1);
|
||||
#endif // SEND_MIDEA
|
||||
#if SEND_MIRAGE
|
||||
void mirage(IRMirageAc *ac,
|
||||
const bool on, const stdAc::opmode_t mode,
|
||||
const float degrees, const stdAc::fanspeed_t fan,
|
||||
const stdAc::swingv_t swingv, const bool turbo, const bool light,
|
||||
const int16_t sleep = -1, const int16_t clock = -1);
|
||||
void mirage(IRMirageAc *ac, const stdAc::state_t state);
|
||||
#endif // SEND_MIRAGE
|
||||
#if SEND_MITSUBISHI_AC
|
||||
void mitsubishi(IRMitsubishiAC *ac,
|
||||
|
||||
@@ -142,6 +142,12 @@ enum hitachi_ac1_remote_model_t {
|
||||
R_LT0541_HTA_B, // (2) R-LT0541-HTA Remote in "B" setting.
|
||||
};
|
||||
|
||||
/// MIRAGE A/C model numbers
|
||||
enum mirage_ac_remote_model_t {
|
||||
KKG9AC1 = 1, // (1) KKG9A-C1 Remote. (Default)
|
||||
KKG29AC1, // (2) KKG29A-C1 Remote.
|
||||
};
|
||||
|
||||
/// Panasonic A/C model numbers
|
||||
enum panasonic_ac_remote_model_t {
|
||||
kPanasonicUnknown = 0,
|
||||
|
||||
@@ -241,6 +241,8 @@ IRTEXT_CONST_STRING(kGe6711ar2853mStr, D_STR_GE6711AR2853M); ///<
|
||||
IRTEXT_CONST_STRING(kAkb75215403Str, D_STR_AKB75215403); ///< "AKB75215403"
|
||||
IRTEXT_CONST_STRING(kAkb74955603Str, D_STR_AKB74955603); ///< "AKB74955603"
|
||||
IRTEXT_CONST_STRING(kAkb73757604Str, D_STR_AKB73757604); ///< "AKB73757604"
|
||||
IRTEXT_CONST_STRING(kKkg9ac1Str, D_STR_KKG9AC1); ///< "KKG9AC1"
|
||||
IRTEXT_CONST_STRING(kKkg29ac1Str, D_STR_KKG29AC1); ///< "KKG29AC1"
|
||||
IRTEXT_CONST_STRING(kLkeStr, D_STR_LKE); ///< "LKE"
|
||||
IRTEXT_CONST_STRING(kNkeStr, D_STR_NKE); ///< "NKE"
|
||||
IRTEXT_CONST_STRING(kDkeStr, D_STR_DKE); ///< "DKE"
|
||||
|
||||
@@ -111,6 +111,8 @@ extern IRTEXT_CONST_PTR(kIFeelStr);
|
||||
extern IRTEXT_CONST_PTR(kInsideStr);
|
||||
extern IRTEXT_CONST_PTR(kIonStr);
|
||||
extern IRTEXT_CONST_PTR(kJkeStr);
|
||||
extern IRTEXT_CONST_PTR(kKkg29ac1Str);
|
||||
extern IRTEXT_CONST_PTR(kKkg9ac1Str);
|
||||
extern IRTEXT_CONST_PTR(kLastStr);
|
||||
extern IRTEXT_CONST_PTR(kLeftMaxNoSpaceStr);
|
||||
extern IRTEXT_CONST_PTR(kLeftMaxStr);
|
||||
|
||||
@@ -626,6 +626,13 @@ namespace irutils {
|
||||
default: return kUnknownStr;
|
||||
}
|
||||
break;
|
||||
case decode_type_t::MIRAGE:
|
||||
switch (model) {
|
||||
case mirage_ac_remote_model_t::KKG9AC1: return kKkg9ac1Str;
|
||||
case mirage_ac_remote_model_t::KKG29AC1: return kKkg29ac1Str;
|
||||
default: return kUnknownStr;
|
||||
}
|
||||
break;
|
||||
case decode_type_t::PANASONIC_AC:
|
||||
switch (model) {
|
||||
case panasonic_ac_remote_model_t::kPanasonicLke: return kLkeStr;
|
||||
|
||||
@@ -21,9 +21,11 @@ using irutils::addFanToString;
|
||||
using irutils::addIntToString;
|
||||
using irutils::addLabeledString;
|
||||
using irutils::addModeToString;
|
||||
using irutils::addModelToString;
|
||||
using irutils::addSwingHToString;
|
||||
using irutils::addSwingVToString;
|
||||
using irutils::addTempToString;
|
||||
using irutils::addToggleToString;
|
||||
using irutils::minsToString;
|
||||
using irutils::bcdToUint8;
|
||||
using irutils::uint8ToBcd;
|
||||
@@ -38,6 +40,9 @@ const uint16_t kMirageZeroSpace = 545; ///< uSeconds
|
||||
const uint32_t kMirageGap = kDefaultMessageGap; ///< uSeconds (just a guess)
|
||||
const uint16_t kMirageFreq = 38000; ///< Hz. (Just a guess)
|
||||
|
||||
const uint8_t kMirageAcKKG29AC1PowerOn = 0b00; // 0
|
||||
const uint8_t kMirageAcKKG29AC1PowerOff = 0b11; // 3
|
||||
|
||||
|
||||
#if SEND_MIRAGE
|
||||
/// Send a Mirage formatted message.
|
||||
@@ -105,6 +110,7 @@ void IRMirageAc::stateReset(void) {
|
||||
0x56, 0x6C, 0x00, 0x00, 0x20, 0x1A, 0x00, 0x00,
|
||||
0x0C, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x42};
|
||||
setRaw(kReset);
|
||||
_model = mirage_ac_remote_model_t::KKG9AC1;
|
||||
}
|
||||
|
||||
/// Set up hardware to be able to send a message.
|
||||
@@ -115,6 +121,15 @@ void IRMirageAc::begin(void) { _irsend.begin(); }
|
||||
/// @param[in] repeat Nr. of times the message will be repeated.
|
||||
void IRMirageAc::send(const uint16_t repeat) {
|
||||
_irsend.sendMirage(getRaw(), kMirageStateLength, repeat);
|
||||
// Reset any toggles after a send.
|
||||
switch (_model) {
|
||||
case mirage_ac_remote_model_t::KKG29AC1:
|
||||
setCleanToggle(false);
|
||||
setLight(false); // For this model (only), Light is a toggle.
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif // SEND_MITSUBISHI_AC
|
||||
|
||||
@@ -129,6 +144,59 @@ uint8_t *IRMirageAc::getRaw(void) {
|
||||
/// @param[in] data A valid code for this protocol.
|
||||
void IRMirageAc::setRaw(const uint8_t *data) {
|
||||
std::memcpy(_.raw, data, kMirageStateLength);
|
||||
_model = getModel(true);
|
||||
}
|
||||
|
||||
/// Guess the Mirage remote model from the supplied state code.
|
||||
/// @param[in] state A valid state code for this protocol.
|
||||
/// @return The model code.
|
||||
/// @note This result isn't perfect. Both protocols can look the same but have
|
||||
/// wildly different settings.
|
||||
mirage_ac_remote_model_t IRMirageAc::getModel(const uint8_t *state) {
|
||||
Mirage120Protocol p;
|
||||
std::memcpy(p.raw, state, kMirageStateLength);
|
||||
// Check for KKG29AC1 specific settings.
|
||||
if (p.RecycleHeat || p.Filter || p.Sleep_Kkg29ac1 || p.CleanToggle ||
|
||||
p.IFeel || p.OffTimerEnable || p.OnTimerEnable)
|
||||
return mirage_ac_remote_model_t::KKG29AC1;
|
||||
// Check for things specific to KKG9AC1
|
||||
if ((p.Minutes || p.Seconds) || // Is part of the clock set?
|
||||
// Are the timer times set, but not enabled? (enable check filtered above)
|
||||
(p.OffTimerHours || p.OffTimerMins) ||
|
||||
(p.OnTimerHours || p.OnTimerMins))
|
||||
return mirage_ac_remote_model_t::KKG9AC1;
|
||||
// As the above test has a 1 in 3600+ (for 1 second an hour) chance of a false
|
||||
// negative in theory, we are going assume that anything left should be a
|
||||
// KKG29AC1 model.
|
||||
return mirage_ac_remote_model_t::KKG29AC1; // Default.
|
||||
}
|
||||
|
||||
/// Get the model code of the interal message state.
|
||||
/// @param[in] useRaw If set, we try to get the model info from just the state.
|
||||
/// @return The model code.
|
||||
mirage_ac_remote_model_t IRMirageAc::getModel(const bool useRaw) const {
|
||||
return useRaw ? getModel(_.raw) : _model;
|
||||
}
|
||||
|
||||
/// Set the model code of the interal message state.
|
||||
/// @param[in] model The desired model to use for the settings.
|
||||
void IRMirageAc::setModel(const mirage_ac_remote_model_t model) {
|
||||
if (model != _model) { // Only change things if we need to.
|
||||
// Save the old settings.
|
||||
stdAc::state_t state = toCommon();
|
||||
const uint16_t ontimer = getOnTimer();
|
||||
const uint16_t offtimer = getOffTimer();
|
||||
const bool ifeel = getIFeel();
|
||||
const uint8_t sensor = getSensorTemp();
|
||||
// Change the model.
|
||||
state.model = model;
|
||||
// Restore/Convert the settings.
|
||||
fromCommon(state);
|
||||
setOnTimer(ontimer);
|
||||
setOffTimer(offtimer);
|
||||
setIFeel(ifeel);
|
||||
setSensorTemp(sensor);
|
||||
}
|
||||
}
|
||||
|
||||
/// Calculate and set the checksum values for the internal state.
|
||||
@@ -157,30 +225,40 @@ void IRMirageAc::off(void) { setPower(false); }
|
||||
/// Change the power setting.
|
||||
/// @param[in] on true, the setting is on. false, the setting is off.
|
||||
void IRMirageAc::setPower(bool on) {
|
||||
// In order to change the power setting, it seems must be less than
|
||||
// kMirageAcPowerOff. kMirageAcPowerOff is larger than half of the possible
|
||||
// value stored in the allocated bit space.
|
||||
// Thus if the value is larger than kMirageAcPowerOff the power is off.
|
||||
// Less than, then power is on.
|
||||
// We can't just aribitarily add or subtract the value (which analysis
|
||||
// indicates is how the power status changes. Very weird, I know!) as that is
|
||||
// not an idempotent action, we must check if the addition or substraction is
|
||||
// needed first. e.g. via getPower()
|
||||
// i.e. If we added or subtracted twice, we would cause a wrap of the integer
|
||||
// and not get the desired result.
|
||||
if (on)
|
||||
_.SwingAndPower -= getPower() ? 0 : kMirageAcPowerOff;
|
||||
else
|
||||
_.SwingAndPower += getPower() ? kMirageAcPowerOff : 0;
|
||||
switch (_model) {
|
||||
case mirage_ac_remote_model_t::KKG29AC1:
|
||||
_.Power = on ? kMirageAcKKG29AC1PowerOn : kMirageAcKKG29AC1PowerOff;
|
||||
break;
|
||||
default:
|
||||
// In order to change the power setting, it seems must be less than
|
||||
// kMirageAcPowerOff. kMirageAcPowerOff is larger than half of the
|
||||
// possible value stored in the allocated bit space.
|
||||
// Thus if the value is larger than kMirageAcPowerOff the power is off.
|
||||
// Less than, then power is on.
|
||||
// We can't just aribitarily add or subtract the value (which analysis
|
||||
// indicates is how the power status changes. Very weird, I know!) as that
|
||||
// is not an idempotent action, we must check if the addition or
|
||||
// substraction is needed first. e.g. via getPower()
|
||||
// i.e. If we added or subtracted twice, we would cause a wrap of the
|
||||
// integer and not get the desired result.
|
||||
if (on)
|
||||
_.SwingAndPower -= getPower() ? 0 : kMirageAcPowerOff;
|
||||
else
|
||||
_.SwingAndPower += getPower() ? kMirageAcPowerOff : 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the value of the current power setting.
|
||||
/// @return true, the setting is on. false, the setting is off.
|
||||
bool IRMirageAc::getPower(void) const {
|
||||
return _.SwingAndPower < kMirageAcPowerOff;
|
||||
switch (_model) {
|
||||
case mirage_ac_remote_model_t::KKG29AC1:
|
||||
return _.Power == kMirageAcKKG29AC1PowerOn;
|
||||
default:
|
||||
return _.SwingAndPower < kMirageAcPowerOff;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Get the operating mode setting of the A/C.
|
||||
/// @return The current operating mode setting.
|
||||
uint8_t IRMirageAc::getMode(void) const { return _.Mode; }
|
||||
@@ -228,62 +306,120 @@ uint8_t IRMirageAc::getFan(void) const { return _.Fan; }
|
||||
/// Change the Turbo setting.
|
||||
/// @param[in] on true, the setting is on. false, the setting is off.
|
||||
void IRMirageAc::setTurbo(bool on) {
|
||||
_.Turbo = (on && (getMode() == kMirageAcCool));
|
||||
const bool value = (on && (getMode() == kMirageAcCool));
|
||||
switch (_model) {
|
||||
case mirage_ac_remote_model_t::KKG29AC1:
|
||||
_.Turbo_Kkg29ac1 = value;
|
||||
break;
|
||||
default:
|
||||
_.Turbo_Kkg9ac1 = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the value of the current Turbo setting.
|
||||
/// @return true, the setting is on. false, the setting is off.
|
||||
bool IRMirageAc::getTurbo(void) const { return _.Turbo; }
|
||||
bool IRMirageAc::getTurbo(void) const {
|
||||
switch (_model) {
|
||||
case mirage_ac_remote_model_t::KKG29AC1: return _.Turbo_Kkg29ac1;
|
||||
default: return _.Turbo_Kkg9ac1;
|
||||
}
|
||||
}
|
||||
|
||||
/// Change the Sleep setting.
|
||||
/// @param[in] on true, the setting is on. false, the setting is off.
|
||||
void IRMirageAc::setSleep(bool on) { _.Sleep = on; }
|
||||
void IRMirageAc::setSleep(bool on) {
|
||||
switch (_model) {
|
||||
case mirage_ac_remote_model_t::KKG29AC1:
|
||||
_.Sleep_Kkg29ac1 = on;
|
||||
break;
|
||||
default:
|
||||
_.Sleep_Kkg9ac1 = on;
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the value of the current Sleep setting.
|
||||
/// @return true, the setting is on. false, the setting is off.
|
||||
bool IRMirageAc::getSleep(void) const { return _.Sleep; }
|
||||
bool IRMirageAc::getSleep(void) const {
|
||||
switch (_model) {
|
||||
case mirage_ac_remote_model_t::KKG29AC1: return _.Sleep_Kkg29ac1;
|
||||
default: return _.Sleep_Kkg9ac1;
|
||||
}
|
||||
}
|
||||
|
||||
/// Change the Light/Display setting.
|
||||
/// @param[in] on true, the setting is on. false, the setting is off.
|
||||
void IRMirageAc::setLight(bool on) { _.Light = on; }
|
||||
/// @note Light is a toggle on the KKG29AC1 model.
|
||||
void IRMirageAc::setLight(bool on) {
|
||||
switch (_model) {
|
||||
case mirage_ac_remote_model_t::KKG29AC1:
|
||||
_.LightToggle_Kkg29ac1 = on;
|
||||
break;
|
||||
default:
|
||||
_.Light_Kkg9ac1 = on;
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the value of the current Light/Display setting.
|
||||
/// @return true, the setting is on. false, the setting is off.
|
||||
bool IRMirageAc::getLight(void) const { return _.Light; }
|
||||
/// @note Light is a toggle on the KKG29AC1 model.
|
||||
bool IRMirageAc::getLight(void) const {
|
||||
switch (_model) {
|
||||
case mirage_ac_remote_model_t::KKG29AC1: return _.LightToggle_Kkg29ac1;
|
||||
default: return _.Light_Kkg9ac1;
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the clock time of the A/C unit.
|
||||
/// @return Nr. of seconds past midnight.
|
||||
uint32_t IRMirageAc::getClock(void) const {
|
||||
return ((bcdToUint8(_.Hours) * 60) + bcdToUint8(_.Minutes)) * 60 +
|
||||
bcdToUint8(_.Seconds);
|
||||
switch (_model) {
|
||||
case mirage_ac_remote_model_t::KKG29AC1:
|
||||
return 0;
|
||||
default:
|
||||
return ((bcdToUint8(_.Hours) * 60) + bcdToUint8(_.Minutes)) * 60 +
|
||||
bcdToUint8(_.Seconds);
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the clock time on the A/C unit.
|
||||
/// @param[in] nr_of_seconds Nr. of seconds past midnight.
|
||||
void IRMirageAc::setClock(const uint32_t nr_of_seconds) {
|
||||
uint32_t remaining = std::min(
|
||||
nr_of_seconds, (uint32_t)(24 * 60 * 60 - 1)); // Limit to 23:59:59.
|
||||
_.Seconds = uint8ToBcd(remaining % 60);
|
||||
remaining /= 60;
|
||||
_.Minutes = uint8ToBcd(remaining % 60);
|
||||
remaining /= 60;
|
||||
_.Hours = uint8ToBcd(remaining);
|
||||
switch (_model) {
|
||||
case mirage_ac_remote_model_t::KKG29AC1:
|
||||
_.Minutes = _.Seconds = 0; // No clock setting. Clear it just in case.
|
||||
break;
|
||||
default:
|
||||
uint32_t remaining = std::min(
|
||||
nr_of_seconds, (uint32_t)(24 * 60 * 60 - 1)); // Limit to 23:59:59.
|
||||
_.Seconds = uint8ToBcd(remaining % 60);
|
||||
remaining /= 60;
|
||||
_.Minutes = uint8ToBcd(remaining % 60);
|
||||
remaining /= 60;
|
||||
_.Hours = uint8ToBcd(remaining);
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the Vertical Swing setting/position of the A/C.
|
||||
/// @param[in] position The desired swing setting.
|
||||
void IRMirageAc::setSwingV(const uint8_t position) {
|
||||
const bool power = getPower();
|
||||
switch (position) {
|
||||
case kMirageAcSwingVOff:
|
||||
case kMirageAcSwingVLowest:
|
||||
case kMirageAcSwingVLow:
|
||||
case kMirageAcSwingVMiddle:
|
||||
case kMirageAcSwingVHigh:
|
||||
case kMirageAcSwingVHighest:
|
||||
case kMirageAcSwingVAuto:
|
||||
_.SwingAndPower = position;
|
||||
// Power needs to be reapplied after overwriting SwingAndPower
|
||||
setPower(power);
|
||||
switch (_model) {
|
||||
case mirage_ac_remote_model_t::KKG29AC1:
|
||||
_.SwingV = (position != kMirageAcSwingVOff);
|
||||
break;
|
||||
default:
|
||||
const bool power = getPower();
|
||||
_.SwingAndPower = position;
|
||||
// Power needs to be reapplied after overwriting SwingAndPower
|
||||
setPower(power);
|
||||
}
|
||||
break;
|
||||
default: // Default to Auto for anything else.
|
||||
setSwingV(kMirageAcSwingVAuto);
|
||||
@@ -293,7 +429,199 @@ void IRMirageAc::setSwingV(const uint8_t position) {
|
||||
/// Get the Vertical Swing setting/position of the A/C.
|
||||
/// @return The desired Vertical Swing setting/position.
|
||||
uint8_t IRMirageAc::getSwingV(void) const {
|
||||
return _.SwingAndPower - (getPower() ? 0 : kMirageAcPowerOff);
|
||||
switch (_model) {
|
||||
case mirage_ac_remote_model_t::KKG29AC1:
|
||||
return _.SwingV ? kMirageAcSwingVAuto : kMirageAcSwingVOff;
|
||||
default:
|
||||
return _.SwingAndPower - (getPower() ? 0 : kMirageAcPowerOff);
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the Horizontal Swing setting of the A/C.
|
||||
/// @param[in] on true, the setting is on. false, the setting is off.
|
||||
void IRMirageAc::setSwingH(const bool on) {
|
||||
switch (_model) {
|
||||
case mirage_ac_remote_model_t::KKG29AC1:
|
||||
_.SwingH = on;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the Horizontal Swing setting of the A/C.
|
||||
/// @return on true, the setting is on. false, the setting is off.
|
||||
bool IRMirageAc::getSwingH(void) const {
|
||||
switch (_model) {
|
||||
case mirage_ac_remote_model_t::KKG29AC1: return _.SwingH;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the Quiet setting of the A/C.
|
||||
/// @param[in] on true, the setting is on. false, the setting is off.
|
||||
void IRMirageAc::setQuiet(const bool on) {
|
||||
switch (_model) {
|
||||
case mirage_ac_remote_model_t::KKG29AC1:
|
||||
_.Quiet = on;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the Quiet setting of the A/C.
|
||||
/// @return on true, the setting is on. false, the setting is off.
|
||||
bool IRMirageAc::getQuiet(void) const {
|
||||
switch (_model) {
|
||||
case mirage_ac_remote_model_t::KKG29AC1: return _.Quiet;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the CleanToggle setting of the A/C.
|
||||
/// @param[in] on true, the setting is on. false, the setting is off.
|
||||
void IRMirageAc::setCleanToggle(const bool on) {
|
||||
switch (_model) {
|
||||
case mirage_ac_remote_model_t::KKG29AC1:
|
||||
_.CleanToggle = on;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the Clean Toggle setting of the A/C.
|
||||
/// @return on true, the setting is on. false, the setting is off.
|
||||
bool IRMirageAc::getCleanToggle(void) const {
|
||||
switch (_model) {
|
||||
case mirage_ac_remote_model_t::KKG29AC1: return _.CleanToggle;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the Filter setting of the A/C.
|
||||
/// @param[in] on true, the setting is on. false, the setting is off.
|
||||
void IRMirageAc::setFilter(const bool on) {
|
||||
switch (_model) {
|
||||
case mirage_ac_remote_model_t::KKG29AC1:
|
||||
_.Filter = on;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the Filter setting of the A/C.
|
||||
/// @return on true, the setting is on. false, the setting is off.
|
||||
bool IRMirageAc::getFilter(void) const {
|
||||
switch (_model) {
|
||||
case mirage_ac_remote_model_t::KKG29AC1: return _.Filter;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the IFeel setting of the A/C.
|
||||
/// @param[in] on true, the setting is on. false, the setting is off.
|
||||
void IRMirageAc::setIFeel(const bool on) {
|
||||
switch (_model) {
|
||||
case mirage_ac_remote_model_t::KKG29AC1:
|
||||
_.IFeel = on;
|
||||
if (on) {
|
||||
// If no previous sensor temp, default to currently desired temp.
|
||||
if (!_.SensorTemp) _.SensorTemp = getTemp();
|
||||
} else {
|
||||
_.SensorTemp = 0; // When turning it off, clear the Sensor Temp.
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the IFeel setting of the A/C.
|
||||
/// @return on true, the setting is on. false, the setting is off.
|
||||
bool IRMirageAc::getIFeel(void) const {
|
||||
switch (_model) {
|
||||
case mirage_ac_remote_model_t::KKG29AC1: return _.IFeel;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the Sensor Temp setting of the A/C's remote.
|
||||
/// @param[in] degrees The desired sensor temp. in degrees celsius.
|
||||
void IRMirageAc::setSensorTemp(const uint8_t degrees) {
|
||||
switch (_model) {
|
||||
case mirage_ac_remote_model_t::KKG29AC1:
|
||||
_.SensorTemp = std::min(kMirageAcSensorTempMax, degrees) +
|
||||
kMirageAcSensorTempOffset;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the Sensor Temp setting of the A/C's remote.
|
||||
/// @return The current setting for the sensor temp. in degrees celsius.
|
||||
uint16_t IRMirageAc::getSensorTemp(void) const {
|
||||
switch (_model) {
|
||||
case mirage_ac_remote_model_t::KKG29AC1:
|
||||
return _.SensorTemp - kMirageAcSensorTempOffset;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the number of minutes the On Timer is currently set for.
|
||||
/// @return Nr. of Minutes the timer is set for. 0, is the timer is not in use.
|
||||
uint16_t IRMirageAc::getOnTimer(void) const {
|
||||
switch (_model) {
|
||||
case mirage_ac_remote_model_t::KKG29AC1:
|
||||
return _.OnTimerEnable ? _.OnTimerHours * 60 + _.OnTimerMins : 0;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the number of minutes for the On Timer.
|
||||
/// @param[in] nr_of_mins How long to set the timer for. 0 disables the timer.
|
||||
void IRMirageAc::setOnTimer(const uint16_t nr_of_mins) {
|
||||
uint16_t mins = std::min(nr_of_mins, (uint16_t)(24 * 60));
|
||||
switch (_model) {
|
||||
case mirage_ac_remote_model_t::KKG29AC1:
|
||||
_.OnTimerEnable = (mins > 0);
|
||||
_.OnTimerHours = mins / 60;
|
||||
_.OnTimerMins = mins % 60;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the number of minutes the Off Timer is currently set for.
|
||||
/// @return Nr. of Minutes the timer is set for. 0, is the timer is not in use.
|
||||
uint16_t IRMirageAc::getOffTimer(void) const {
|
||||
switch (_model) {
|
||||
case mirage_ac_remote_model_t::KKG29AC1:
|
||||
return _.OffTimerEnable ? _.OffTimerHours * 60 + _.OffTimerMins : 0;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the number of minutes for the Off Timer.
|
||||
/// @param[in] nr_of_mins How long to set the timer for. 0 disables the timer.
|
||||
void IRMirageAc::setOffTimer(const uint16_t nr_of_mins) {
|
||||
uint16_t mins = std::min(nr_of_mins, (uint16_t)(24 * 60));
|
||||
switch (_model) {
|
||||
case mirage_ac_remote_model_t::KKG29AC1:
|
||||
_.OffTimerEnable = (mins > 0);
|
||||
_.OffTimerHours = mins / 60;
|
||||
_.OffTimerMins = mins % 60;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a native mode into its stdAc equivalent.
|
||||
@@ -310,13 +638,26 @@ stdAc::opmode_t IRMirageAc::toCommonMode(const uint8_t mode) {
|
||||
|
||||
/// Convert a native fan speed into its stdAc equivalent.
|
||||
/// @param[in] speed The native setting to be converted.
|
||||
/// @param[in] model The model type to use to influence the conversion.
|
||||
/// @return The stdAc equivalent of the native setting.
|
||||
stdAc::fanspeed_t IRMirageAc::toCommonFanSpeed(const uint8_t speed) {
|
||||
switch (speed) {
|
||||
case kMirageAcFanHigh: return stdAc::fanspeed_t::kHigh;
|
||||
case kMirageAcFanMed: return stdAc::fanspeed_t::kMedium;
|
||||
case kMirageAcFanLow: return stdAc::fanspeed_t::kLow;
|
||||
default: return stdAc::fanspeed_t::kAuto;
|
||||
stdAc::fanspeed_t IRMirageAc::toCommonFanSpeed(const uint8_t speed,
|
||||
const mirage_ac_remote_model_t model) {
|
||||
switch (model) {
|
||||
case mirage_ac_remote_model_t::KKG29AC1:
|
||||
switch (speed) {
|
||||
case kMirageAcKKG29AC1FanHigh: return stdAc::fanspeed_t::kHigh;
|
||||
case kMirageAcKKG29AC1FanMed: return stdAc::fanspeed_t::kMedium;
|
||||
case kMirageAcKKG29AC1FanLow: return stdAc::fanspeed_t::kLow;
|
||||
default: return stdAc::fanspeed_t::kAuto;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
switch (speed) {
|
||||
case kMirageAcFanHigh: return stdAc::fanspeed_t::kHigh;
|
||||
case kMirageAcFanMed: return stdAc::fanspeed_t::kMedium;
|
||||
case kMirageAcFanLow: return stdAc::fanspeed_t::kLow;
|
||||
default: return stdAc::fanspeed_t::kAuto;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -334,12 +675,25 @@ uint8_t IRMirageAc::convertMode(const stdAc::opmode_t mode) {
|
||||
|
||||
/// Convert a stdAc::fanspeed_t enum into it's native speed.
|
||||
/// @param[in] speed The enum to be converted.
|
||||
/// @param[in] model The model type to use to influence the conversion.
|
||||
/// @return The native equivalent of the enum.
|
||||
uint8_t IRMirageAc::convertFan(const stdAc::fanspeed_t speed) {
|
||||
uint8_t IRMirageAc::convertFan(const stdAc::fanspeed_t speed,
|
||||
const mirage_ac_remote_model_t model) {
|
||||
uint8_t low;
|
||||
uint8_t med;
|
||||
switch (model) {
|
||||
case mirage_ac_remote_model_t::KKG29AC1:
|
||||
low = kMirageAcKKG29AC1FanLow;
|
||||
med = kMirageAcKKG29AC1FanMed;
|
||||
break;
|
||||
default:
|
||||
low = kMirageAcFanLow;
|
||||
med = kMirageAcFanMed;
|
||||
}
|
||||
switch (speed) {
|
||||
case stdAc::fanspeed_t::kMin:
|
||||
case stdAc::fanspeed_t::kLow: return kMirageAcFanLow;
|
||||
case stdAc::fanspeed_t::kMedium: return kMirageAcFanMed;
|
||||
case stdAc::fanspeed_t::kLow: return low;
|
||||
case stdAc::fanspeed_t::kMedium: return med;
|
||||
case stdAc::fanspeed_t::kHigh:
|
||||
case stdAc::fanspeed_t::kMax: return kMirageAcFanHigh;
|
||||
default: return kMirageAcFanAuto;
|
||||
@@ -356,6 +710,7 @@ uint8_t IRMirageAc::convertSwingV(const stdAc::swingv_t position) {
|
||||
case stdAc::swingv_t::kMiddle: return kMirageAcSwingVMiddle;
|
||||
case stdAc::swingv_t::kLow: return kMirageAcSwingVLow;
|
||||
case stdAc::swingv_t::kLowest: return kMirageAcSwingVLowest;
|
||||
case stdAc::swingv_t::kOff: return kMirageAcSwingVOff;
|
||||
default: return kMirageAcSwingVAuto;
|
||||
}
|
||||
}
|
||||
@@ -370,7 +725,8 @@ stdAc::swingv_t IRMirageAc::toCommonSwingV(const uint8_t pos) {
|
||||
case kMirageAcSwingVMiddle: return stdAc::swingv_t::kMiddle;
|
||||
case kMirageAcSwingVLow: return stdAc::swingv_t::kLow;
|
||||
case kMirageAcSwingVLowest: return stdAc::swingv_t::kLowest;
|
||||
default: return stdAc::swingv_t::kAuto;
|
||||
case kMirageAcSwingVAuto: return stdAc::swingv_t::kAuto;
|
||||
default: return stdAc::swingv_t::kOff;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -379,55 +735,113 @@ stdAc::swingv_t IRMirageAc::toCommonSwingV(const uint8_t pos) {
|
||||
stdAc::state_t IRMirageAc::toCommon(void) const {
|
||||
stdAc::state_t result;
|
||||
result.protocol = decode_type_t::MIRAGE;
|
||||
result.model = -1; // No models used.
|
||||
result.model = _model;
|
||||
result.power = getPower();
|
||||
result.mode = toCommonMode(_.Mode);
|
||||
result.celsius = true;
|
||||
result.degrees = getTemp();
|
||||
result.fanspeed = toCommonFanSpeed(getFan());
|
||||
result.fanspeed = toCommonFanSpeed(getFan(), _model);
|
||||
result.swingv = toCommonSwingV(getSwingV());
|
||||
result.swingh = getSwingH() ? stdAc::swingh_t::kAuto : stdAc::swingh_t::kOff;
|
||||
result.turbo = getTurbo();
|
||||
result.light = getLight();
|
||||
result.clean = getCleanToggle();
|
||||
result.filter = getFilter();
|
||||
result.sleep = getSleep() ? 0 : -1;
|
||||
result.quiet = getQuiet();
|
||||
result.clock = getClock() / 60;
|
||||
// Not supported.
|
||||
result.swingh = stdAc::swingh_t::kOff;
|
||||
result.quiet = false;
|
||||
result.clean = false;
|
||||
result.econo = false;
|
||||
result.filter = false;
|
||||
result.beep = false;
|
||||
result.clock = -1;
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Convert & set a stdAc::state_t to its equivalent internal settings.
|
||||
/// @param[in] state The desired state in stdAc::state_t form.
|
||||
void IRMirageAc::fromCommon(const stdAc::state_t state) {
|
||||
stateReset();
|
||||
_model = (mirage_ac_remote_model_t)state.model; // Set directly to avoid loop
|
||||
setPower(state.power);
|
||||
setTemp(state.celsius ? state.degrees : fahrenheitToCelsius(state.degrees));
|
||||
setMode(convertMode(state.mode));
|
||||
setFan(convertFan(state.fanspeed, _model));
|
||||
setTurbo(state.turbo);
|
||||
setSleep(state.sleep >= 0);
|
||||
setLight(state.light);
|
||||
setSwingV(convertSwingV(state.swingv));
|
||||
setSwingH(state.swingh != stdAc::swingh_t::kOff);
|
||||
setQuiet(state.quiet);
|
||||
setCleanToggle(state.clean);
|
||||
setFilter(state.filter);
|
||||
// setClock() expects seconds, not minutes.
|
||||
setClock((state.clock > 0) ? state.clock * 60 : 0);
|
||||
// Non-common settings.
|
||||
setOnTimer(0);
|
||||
setOffTimer(0);
|
||||
setIFeel(false);
|
||||
}
|
||||
|
||||
/// Convert the internal state into a human readable string.
|
||||
/// @return A string containing the settings in human-readable form.
|
||||
String IRMirageAc::toString(void) const {
|
||||
String result = "";
|
||||
result.reserve(110); // Reserve some heap for the string to reduce fragging.
|
||||
result += addBoolToString(getPower(), kPowerStr, false);
|
||||
result.reserve(240); // Reserve some heap for the string to reduce fragging.
|
||||
result += addModelToString(decode_type_t::MIRAGE, _model, false);
|
||||
result += addBoolToString(getPower(), kPowerStr);
|
||||
result += addModeToString(_.Mode, 0xFF, kMirageAcCool,
|
||||
kMirageAcHeat, kMirageAcDry,
|
||||
kMirageAcFan);
|
||||
result += addTempToString(getTemp());
|
||||
result += addFanToString(_.Fan, kMirageAcFanHigh,
|
||||
kMirageAcFanLow,
|
||||
kMirageAcFanAuto, kMirageAcFanAuto,
|
||||
kMirageAcFanMed);
|
||||
result += addSwingVToString(getSwingV(),
|
||||
kMirageAcSwingVAuto,
|
||||
kMirageAcSwingVHighest,
|
||||
kMirageAcSwingVHigh,
|
||||
0xFF, // Unused.
|
||||
kMirageAcSwingVMiddle,
|
||||
0xFF, // Unused.
|
||||
kMirageAcSwingVLow,
|
||||
kMirageAcSwingVLowest,
|
||||
0xFF, 0xFF, 0xFF, 0xFF); // Unused.
|
||||
result += addBoolToString(_.Turbo, kTurboStr);
|
||||
result += addBoolToString(_.Light, kLightStr);
|
||||
result += addBoolToString(_.Sleep, kSleepStr);
|
||||
result += addLabeledString(minsToString(getClock() / 60), kClockStr);
|
||||
uint8_t fanlow;
|
||||
uint8_t fanmed;
|
||||
switch (_model) {
|
||||
case mirage_ac_remote_model_t::KKG29AC1:
|
||||
fanlow = kMirageAcKKG29AC1FanLow;
|
||||
fanmed = kMirageAcKKG29AC1FanMed;
|
||||
break;
|
||||
default: // e.g. Model KKG9AC1
|
||||
fanlow = kMirageAcFanLow;
|
||||
fanmed = kMirageAcFanMed;
|
||||
}
|
||||
result += addFanToString(_.Fan, kMirageAcFanHigh, fanlow, kMirageAcFanAuto,
|
||||
kMirageAcFanAuto, fanmed);
|
||||
result += addBoolToString(getTurbo(), kTurboStr);
|
||||
result += addBoolToString(getSleep(), kSleepStr);
|
||||
switch (_model) {
|
||||
case mirage_ac_remote_model_t::KKG29AC1:
|
||||
result += addBoolToString(_.Quiet, kQuietStr);
|
||||
result += addToggleToString(getLight(), kLightStr);
|
||||
result += addBoolToString(_.SwingV, kSwingVStr);
|
||||
result += addBoolToString(_.SwingH, kSwingHStr);
|
||||
result += addBoolToString(_.Filter, kFilterStr);
|
||||
result += addToggleToString(_.CleanToggle, kCleanStr);
|
||||
result += addLabeledString(getOnTimer() ? minsToString(getOnTimer())
|
||||
: kOffStr,
|
||||
kOnTimerStr);
|
||||
result += addLabeledString(getOffTimer() ? minsToString(getOffTimer())
|
||||
: kOffStr,
|
||||
kOffTimerStr);
|
||||
result += addBoolToString(_.IFeel, kIFeelStr);
|
||||
if (_.IFeel) {
|
||||
result += addIntToString(getSensorTemp(), kSensorTempStr);
|
||||
result += 'C';
|
||||
}
|
||||
break;
|
||||
default: // e.g. Model KKG9AC1
|
||||
result += addBoolToString(getLight(), kLightStr);
|
||||
result += addSwingVToString(getSwingV(),
|
||||
kMirageAcSwingVAuto,
|
||||
kMirageAcSwingVHighest,
|
||||
kMirageAcSwingVHigh,
|
||||
0xFF, // Unused.
|
||||
kMirageAcSwingVMiddle,
|
||||
0xFF, // Unused.
|
||||
kMirageAcSwingVLow,
|
||||
kMirageAcSwingVLowest,
|
||||
kMirageAcSwingVOff,
|
||||
0xFF, 0xFF, 0xFF); // Unused.
|
||||
result += addLabeledString(minsToString(getClock() / 60), kClockStr);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
#endif // DECODE_MIRAGE
|
||||
|
||||
153
src/ir_Mirage.h
153
src/ir_Mirage.h
@@ -9,6 +9,8 @@
|
||||
// Brand: Mirage, Model: VLU series A/C
|
||||
// Brand: Maxell, Model: MX-CH18CF A/C
|
||||
// Brand: Maxell, Model: KKG9A-C1 remote
|
||||
// Brand: Tronitechnik, Model: Reykir 9000 A/C
|
||||
// Brand: Tronitechnik, Model: KKG29A-C1 remote
|
||||
|
||||
#ifndef IR_MIRAGE_H_
|
||||
#define IR_MIRAGE_H_
|
||||
@@ -28,35 +30,67 @@
|
||||
/// @see https://docs.google.com/spreadsheets/d/1Ucu9mOOIIJoWQjUJq_VCvwgV3EwKaRk8K2AuZgccYEk/edit#gid=0
|
||||
union Mirage120Protocol{
|
||||
uint8_t raw[kMirageStateLength]; ///< The state in code form.
|
||||
struct {
|
||||
struct { // Common
|
||||
// Byte 0
|
||||
uint8_t :8; // Header. (0x56)
|
||||
uint8_t Header :8; // Header. (0x56)
|
||||
// Byte 1
|
||||
uint8_t Temp :8; // Celsius minus 0x5C.
|
||||
// Byte 2
|
||||
uint8_t :8; // Unknown / Unused. Typically 0x00
|
||||
uint8_t :8; // Unknown / Unused.
|
||||
// Byte 3
|
||||
uint8_t :3; // Unknown / Unused. Typically 0x0
|
||||
uint8_t Light :1; // Aka. Display. Seems linked to Sleep mode.
|
||||
uint8_t :4; // Unknown / Unused. Typically 0x0
|
||||
uint8_t :8; // Unknown / Unused.
|
||||
// Byte 4
|
||||
uint8_t Fan :2; // Fan Speed.
|
||||
uint8_t :2; // Unknown / Unused. Typically 0x0
|
||||
uint8_t :2; // Unknown / Unused.
|
||||
uint8_t Mode :4; // Cool, Heat, Dry, Fan, Recycle
|
||||
// Byte 5
|
||||
uint8_t :8;
|
||||
// Byte 6
|
||||
uint8_t :8;
|
||||
// Byte 7
|
||||
uint8_t :8;
|
||||
// Byte 8
|
||||
uint8_t :8;
|
||||
// Byte 9
|
||||
uint8_t :8;
|
||||
// Byte 10
|
||||
uint8_t :8;
|
||||
// Byte 11
|
||||
uint8_t :8;
|
||||
// Byte 12
|
||||
uint8_t :8;
|
||||
// Byte 13
|
||||
uint8_t :8;
|
||||
// Byte 14
|
||||
uint8_t Sum :8; // Sum of all the previous nibbles.
|
||||
};
|
||||
struct { // KKG9AC1 remote
|
||||
// Byte 0
|
||||
uint8_t :8; // Header
|
||||
// Byte 1
|
||||
uint8_t :8; // Temp
|
||||
// Byte 2
|
||||
uint8_t :8; // Unknown / Unused.
|
||||
// Byte 3
|
||||
uint8_t :3; // Unknown / Unused.
|
||||
uint8_t Light_Kkg9ac1 :1; // Aka. Display. Seems linked to Sleep mode.
|
||||
uint8_t :4; // Unknown / Unused.
|
||||
// Byte 4
|
||||
uint8_t :8; // Fan & Mode
|
||||
// Byte 5
|
||||
uint8_t :1; // Unknown
|
||||
uint8_t SwingAndPower :7;
|
||||
// Byte 6
|
||||
uint8_t :7; // Unknown / Unused. Typically 0x00
|
||||
uint8_t Sleep :1; // Sleep mode on or off.
|
||||
uint8_t :7; // Unknown / Unused.
|
||||
uint8_t Sleep_Kkg9ac1 :1; // Sleep mode on or off.
|
||||
// Byte 7
|
||||
uint8_t :3; // Unknown / Unused. Typically 0x0
|
||||
uint8_t Turbo :1; // Sleep mode on or off. Only works in Cool mode.
|
||||
uint8_t :4; // Unknown / Unused. Typically 0x0
|
||||
uint8_t :3; // Unknown / Unused.
|
||||
uint8_t Turbo_Kkg9ac1 :1; // Turbo mode on or off. Only works in Cool mode.
|
||||
uint8_t :4; // Unknown / Unused.
|
||||
// Byte 8
|
||||
uint8_t :8; // Unknown / Unused. Typically 0xC0
|
||||
uint8_t :8; // Unknown / Unused.
|
||||
// Byte 9
|
||||
uint8_t :8; // Unknown / Unused. Typically 0x00
|
||||
uint8_t :8; // Unknown / Unused.
|
||||
// Byte 10
|
||||
uint8_t :8; // Unknown / Unused.
|
||||
// Byte 11
|
||||
@@ -66,7 +100,61 @@ union Mirage120Protocol{
|
||||
// Byte 13
|
||||
uint8_t Hours :8; // Nr. of Hours in BCD.
|
||||
// Byte 14
|
||||
uint8_t Sum :8; // Sum of all the previous nibbles.
|
||||
uint8_t :8; // Sum
|
||||
};
|
||||
struct { // KKG29A-C1 remote
|
||||
// Byte 0
|
||||
uint8_t :8; // Header
|
||||
// Byte 1
|
||||
uint8_t :8; // Temp
|
||||
// Byte 2
|
||||
uint8_t :8;
|
||||
// Byte 3
|
||||
uint8_t Quiet :1;
|
||||
uint8_t :7;
|
||||
// Byte 4
|
||||
uint8_t :2; // Fan
|
||||
uint8_t OffTimerEnable :1;
|
||||
uint8_t OnTimerEnable :1;
|
||||
uint8_t :3; // Mode
|
||||
uint8_t :1;
|
||||
// Byte 5
|
||||
uint8_t SwingH :1;
|
||||
uint8_t SwingV :1;
|
||||
uint8_t LightToggle_Kkg29ac1 :1; // Aka. Display Toggle.
|
||||
uint8_t :3;
|
||||
uint8_t Power :2;
|
||||
// Byte 6
|
||||
uint8_t :1;
|
||||
uint8_t Filter :1; // Aka. UVC
|
||||
uint8_t :1;
|
||||
uint8_t Sleep_Kkg29ac1 :1; // Sleep mode on or off.
|
||||
uint8_t :2;
|
||||
uint8_t RecycleHeat :1;
|
||||
uint8_t :1;
|
||||
// Byte 7
|
||||
uint8_t SensorTemp :6; // Temperature at the remote
|
||||
uint8_t CleanToggle :1;
|
||||
uint8_t IFeel :1;
|
||||
// Byte 8
|
||||
uint8_t OnTimerHours :5;
|
||||
uint8_t :2;
|
||||
uint8_t Turbo_Kkg29ac1 :1; // Turbo mode on or off.
|
||||
// Byte 9
|
||||
uint8_t OnTimerMins :6;
|
||||
uint8_t :2;
|
||||
// Byte 10
|
||||
uint8_t OffTimerHours :5;
|
||||
uint8_t :3;
|
||||
// Byte 11
|
||||
uint8_t OffTimerMins :6;
|
||||
uint8_t :2;
|
||||
// Byte 12
|
||||
uint8_t :8;
|
||||
// Byte 13
|
||||
uint8_t :8;
|
||||
// Byte 14
|
||||
uint8_t :8; // Sum
|
||||
};
|
||||
};
|
||||
|
||||
@@ -81,12 +169,19 @@ const uint8_t kMirageAcFanAuto = 0b00; // 0
|
||||
const uint8_t kMirageAcFanHigh = 0b01; // 1
|
||||
const uint8_t kMirageAcFanMed = 0b10; // 2
|
||||
const uint8_t kMirageAcFanLow = 0b11; // 3
|
||||
const uint8_t kMirageAcKKG29AC1FanAuto = 0b00; // 0
|
||||
const uint8_t kMirageAcKKG29AC1FanHigh = 0b01; // 1
|
||||
const uint8_t kMirageAcKKG29AC1FanLow = 0b10; // 2
|
||||
const uint8_t kMirageAcKKG29AC1FanMed = 0b11; // 3
|
||||
|
||||
const uint8_t kMirageAcMinTemp = 16; // 16C
|
||||
const uint8_t kMirageAcMaxTemp = 32; // 32C
|
||||
const uint8_t kMirageAcTempOffset = 0x5C;
|
||||
const uint8_t kMirageAcSensorTempOffset = 20;
|
||||
const uint8_t kMirageAcSensorTempMax = 43; // Celsius
|
||||
|
||||
const uint8_t kMirageAcPowerOff = 0x5F;
|
||||
const uint8_t kMirageAcSwingVOff = 0b0000; // 0
|
||||
const uint8_t kMirageAcSwingVLowest = 0b0011; // 3
|
||||
const uint8_t kMirageAcSwingVLow = 0b0101; // 5
|
||||
const uint8_t kMirageAcSwingVMiddle = 0b0111; // 7
|
||||
@@ -134,16 +229,37 @@ class IRMirageAc {
|
||||
bool getSleep(void) const;
|
||||
void setSwingV(const uint8_t position);
|
||||
uint8_t getSwingV(void) const;
|
||||
|
||||
void setSwingH(const bool on);
|
||||
bool getSwingH(void) const;
|
||||
void setQuiet(const bool on);
|
||||
bool getQuiet(void) const;
|
||||
void setCleanToggle(const bool on);
|
||||
bool getCleanToggle(void) const;
|
||||
void setFilter(const bool on);
|
||||
bool getFilter(void) const;
|
||||
void setIFeel(const bool on);
|
||||
bool getIFeel(void) const;
|
||||
void setSensorTemp(const uint8_t degrees);
|
||||
uint16_t getSensorTemp(void) const;
|
||||
uint16_t getOnTimer(void) const;
|
||||
uint16_t getOffTimer(void) const;
|
||||
void setOnTimer(const uint16_t nr_of_mins);
|
||||
void setOffTimer(const uint16_t nr_of_mins);
|
||||
mirage_ac_remote_model_t getModel(const bool useRaw = false) const;
|
||||
void setModel(const mirage_ac_remote_model_t model);
|
||||
static mirage_ac_remote_model_t getModel(const uint8_t *state);
|
||||
static bool validChecksum(const uint8_t* data);
|
||||
static uint8_t calculateChecksum(const uint8_t* data);
|
||||
static uint8_t convertMode(const stdAc::opmode_t mode);
|
||||
static uint8_t convertFan(const stdAc::fanspeed_t speed);
|
||||
static uint8_t convertFan(const stdAc::fanspeed_t speed,
|
||||
const mirage_ac_remote_model_t model = mirage_ac_remote_model_t::KKG9AC1);
|
||||
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::fanspeed_t toCommonFanSpeed(const uint8_t speed,
|
||||
const mirage_ac_remote_model_t model = mirage_ac_remote_model_t::KKG9AC1);
|
||||
static stdAc::swingv_t toCommonSwingV(const uint8_t pos);
|
||||
stdAc::state_t toCommon(void) const;
|
||||
void fromCommon(const stdAc::state_t state);
|
||||
String toString(void) const;
|
||||
#ifndef UNIT_TEST
|
||||
|
||||
@@ -155,6 +271,7 @@ class IRMirageAc {
|
||||
/// @endcond
|
||||
#endif // UNIT_TEST
|
||||
Mirage120Protocol _;
|
||||
mirage_ac_remote_model_t _model;
|
||||
void checksum(void);
|
||||
};
|
||||
#endif // IR_MIRAGE_H_
|
||||
|
||||
@@ -586,6 +586,12 @@
|
||||
#ifndef D_STR_AKB73757604
|
||||
#define D_STR_AKB73757604 "AKB73757604"
|
||||
#endif // D_STR_AKB73757604
|
||||
#ifndef D_STR_KKG9AC1
|
||||
#define D_STR_KKG9AC1 "KKG9AC1"
|
||||
#endif // D_STR_KKG9AC1
|
||||
#ifndef D_STR_KKG29AC1
|
||||
#define D_STR_KKG29AC1 "KKG29AC1"
|
||||
#endif // D_STR_KKG9AC1
|
||||
#ifndef D_STR_LKE
|
||||
#define D_STR_LKE "LKE"
|
||||
#endif // D_STR_LKE
|
||||
|
||||
@@ -1354,30 +1354,55 @@ TEST(TestIRac, Mirage) {
|
||||
IRMirageAc ac(kGpioUnused);
|
||||
IRac irac(kGpioUnused);
|
||||
IRrecv capture(kGpioUnused);
|
||||
char expected[] =
|
||||
"Power: On, Mode: 3 (Dry), Temp: 27C, Fan: 2 (Medium), "
|
||||
"Swing(V): 9 (High), "
|
||||
"Turbo: Off, Light: Off, Sleep: On, Clock: 17:31";
|
||||
stdAc::state_t state, r, p;
|
||||
const char expected_KKG9AC1[] =
|
||||
"Model: 1 (KKG9AC1), Power: On, Mode: 3 (Dry), Temp: 27C, "
|
||||
"Fan: 2 (Medium), Turbo: Off, Sleep: On, Light: Off, "
|
||||
"Swing(V): 9 (High), Clock: 17:31";
|
||||
|
||||
ac.begin();
|
||||
irac.mirage(&ac,
|
||||
true, // Power
|
||||
stdAc::opmode_t::kDry, // Mode
|
||||
27, // Degrees (Celsius)
|
||||
stdAc::fanspeed_t::kMedium, // Fan speed
|
||||
stdAc::swingv_t::kHigh, // Veritical Swing
|
||||
false, // Turbo
|
||||
false, // Light
|
||||
8 * 60 + 0, // Sleep time
|
||||
17 * 60 + 31); // Clock
|
||||
|
||||
ASSERT_EQ(expected, ac.toString());
|
||||
state.model = mirage_ac_remote_model_t::KKG9AC1;
|
||||
state.power = true;
|
||||
state.mode = stdAc::opmode_t::kDry;
|
||||
state.celsius = true;
|
||||
state.degrees = 27;
|
||||
state.fanspeed = stdAc::fanspeed_t::kMedium;
|
||||
state.swingv = stdAc::swingv_t::kHigh;
|
||||
state.swingh = stdAc::swingh_t::kLeft;
|
||||
state.turbo = false;
|
||||
state.quiet = true;
|
||||
state.light = false;
|
||||
state.filter = true;
|
||||
state.clean = false;
|
||||
state.sleep = 8 * 60 + 0;
|
||||
state.clock = 17 * 60 + 31;
|
||||
state.beep = false;
|
||||
irac.mirage(&ac, state);
|
||||
|
||||
ASSERT_EQ(expected_KKG9AC1, ac.toString());
|
||||
ac._irsend.makeDecodeResult();
|
||||
EXPECT_TRUE(capture.decode(&ac._irsend.capture));
|
||||
ASSERT_EQ(MIRAGE, ac._irsend.capture.decode_type);
|
||||
ASSERT_EQ(kMirageBits, ac._irsend.capture.bits);
|
||||
ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture));
|
||||
stdAc::state_t r, p;
|
||||
ASSERT_EQ(expected_KKG9AC1, IRAcUtils::resultAcToString(&ac._irsend.capture));
|
||||
ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p));
|
||||
|
||||
const char expected_KKG29AC1[] =
|
||||
"Model: 2 (KKG29AC1), Power: On, Mode: 3 (Dry), Temp: 27C, "
|
||||
"Fan: 3 (Medium), Turbo: Off, Sleep: On, Quiet: On, Light: -, "
|
||||
"Swing(V): On, Swing(H): On, Filter: On, Clean: -, "
|
||||
"On Timer: Off, Off Timer: Off, IFeel: Off";
|
||||
ac._irsend.reset();
|
||||
state.model = mirage_ac_remote_model_t::KKG29AC1;
|
||||
irac.mirage(&ac, state);
|
||||
ASSERT_EQ(expected_KKG29AC1, ac.toString());
|
||||
ac._irsend.makeDecodeResult();
|
||||
EXPECT_TRUE(capture.decode(&ac._irsend.capture));
|
||||
ASSERT_EQ(MIRAGE, ac._irsend.capture.decode_type);
|
||||
ASSERT_EQ(kMirageBits, ac._irsend.capture.bits);
|
||||
ASSERT_EQ(expected_KKG29AC1,
|
||||
IRAcUtils::resultAcToString(&ac._irsend.capture));
|
||||
ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p));
|
||||
}
|
||||
|
||||
|
||||
@@ -56,9 +56,9 @@ TEST(TestDecodeMirage, RealExample) {
|
||||
ASSERT_EQ(kMirageBits, irsend.capture.bits);
|
||||
EXPECT_STATE_EQ(expected, irsend.capture.state, irsend.capture.bits);
|
||||
EXPECT_EQ(
|
||||
"Power: On, Mode: 2 (Cool), Temp: 25C, Fan: 0 (Auto), "
|
||||
"Swing(V): 0 (UNKNOWN), "
|
||||
"Turbo: Off, Light: Off, Sleep: Off, Clock: 14:16",
|
||||
"Model: 1 (KKG9AC1), Power: On, Mode: 2 (Cool), Temp: 25C, "
|
||||
"Fan: 0 (Auto), Turbo: Off, Sleep: Off, Light: Off, "
|
||||
"Swing(V): 0 (Off), Clock: 14:16",
|
||||
IRAcUtils::resultAcToString(&irsend.capture));
|
||||
}
|
||||
|
||||
@@ -78,9 +78,9 @@ TEST(TestDecodeMirage, SyntheticExample) {
|
||||
ASSERT_EQ(kMirageBits, irsend.capture.bits);
|
||||
EXPECT_STATE_EQ(expected, irsend.capture.state, irsend.capture.bits);
|
||||
EXPECT_EQ(
|
||||
"Power: On, Mode: 2 (Cool), Temp: 25C, Fan: 0 (Auto), "
|
||||
"Swing(V): 0 (UNKNOWN), "
|
||||
"Turbo: Off, Light: Off, Sleep: Off, Clock: 14:16",
|
||||
"Model: 1 (KKG9AC1), Power: On, Mode: 2 (Cool), Temp: 25C, "
|
||||
"Fan: 0 (Auto), Turbo: Off, Sleep: Off, Light: Off, "
|
||||
"Swing(V): 0 (Off), Clock: 14:16",
|
||||
IRAcUtils::resultAcToString(&irsend.capture));
|
||||
}
|
||||
|
||||
@@ -123,9 +123,9 @@ TEST(TestDecodeMirage, RealExampleWithDodgyHardwareCapture) {
|
||||
ASSERT_EQ(kMirageBits, irsend.capture.bits);
|
||||
EXPECT_STATE_EQ(expected, irsend.capture.state, irsend.capture.bits);
|
||||
EXPECT_EQ(
|
||||
"Power: On, Mode: 2 (Cool), Temp: 25C, Fan: 0 (Auto), "
|
||||
"Swing(V): 0 (UNKNOWN), "
|
||||
"Turbo: Off, Light: Off, Sleep: Off, Clock: 14:16",
|
||||
"Model: 1 (KKG9AC1), Power: On, Mode: 2 (Cool), Temp: 25C, "
|
||||
"Fan: 0 (Auto), Turbo: Off, Sleep: Off, Light: Off, "
|
||||
"Swing(V): 0 (Off), Clock: 14:16",
|
||||
IRAcUtils::resultAcToString(&irsend.capture));
|
||||
}
|
||||
|
||||
@@ -133,6 +133,7 @@ TEST(TestMirageAcClass, Power) {
|
||||
IRMirageAc ac(kGpioUnused);
|
||||
ac.begin();
|
||||
|
||||
ac.setModel(mirage_ac_remote_model_t::KKG9AC1);
|
||||
ac.on();
|
||||
EXPECT_TRUE(ac.getPower());
|
||||
ac.on();
|
||||
@@ -159,6 +160,16 @@ TEST(TestMirageAcClass, Power) {
|
||||
0x0C, 0x00, 0x0C, 0x2C, 0x23, 0x01, 0x61};
|
||||
ac.setRaw(off);
|
||||
EXPECT_FALSE(ac.getPower());
|
||||
|
||||
ac.setModel(mirage_ac_remote_model_t::KKG29AC1);
|
||||
ac.on();
|
||||
EXPECT_TRUE(ac.getPower());
|
||||
ac.off();
|
||||
EXPECT_FALSE(ac.getPower());
|
||||
ac.setPower(true);
|
||||
EXPECT_TRUE(ac.getPower());
|
||||
ac.setPower(false);
|
||||
EXPECT_FALSE(ac.getPower());
|
||||
}
|
||||
|
||||
TEST(TestMirageAcClass, OperatingMode) {
|
||||
@@ -182,10 +193,12 @@ TEST(TestMirageAcClass, OperatingMode) {
|
||||
TEST(TestMirageAcClass, HumanReadable) {
|
||||
IRMirageAc ac(kGpioUnused);
|
||||
ac.begin();
|
||||
|
||||
// Tests for the KKG9AC1 model.
|
||||
EXPECT_EQ(
|
||||
"Power: On, Mode: 2 (Cool), Temp: 16C, Fan: 0 (Auto), "
|
||||
"Swing(V): 13 (Auto), "
|
||||
"Turbo: Off, Light: Off, Sleep: Off, Clock: 00:00",
|
||||
"Model: 1 (KKG9AC1), Power: On, Mode: 2 (Cool), Temp: 16C, "
|
||||
"Fan: 0 (Auto), Turbo: Off, Sleep: Off, Light: Off, "
|
||||
"Swing(V): 13 (Auto), Clock: 00:00",
|
||||
ac.toString());
|
||||
// Ref: https://docs.google.com/spreadsheets/d/1Ucu9mOOIIJoWQjUJq_VCvwgV3EwKaRk8K2AuZgccYEk/edit#gid=0&range=C7
|
||||
// 0x56710000201A00000C000C26010041
|
||||
@@ -194,9 +207,9 @@ TEST(TestMirageAcClass, HumanReadable) {
|
||||
0x0C, 0x00, 0x0C, 0x26, 0x01, 0x00, 0x41};
|
||||
ac.setRaw(cool_21c_auto);
|
||||
EXPECT_EQ(
|
||||
"Power: On, Mode: 2 (Cool), Temp: 21C, Fan: 0 (Auto), "
|
||||
"Swing(V): 13 (Auto), "
|
||||
"Turbo: Off, Light: Off, Sleep: Off, Clock: 00:01",
|
||||
"Model: 1 (KKG9AC1), Power: On, Mode: 2 (Cool), Temp: 21C, "
|
||||
"Fan: 0 (Auto), Turbo: Off, Sleep: Off, Light: Off, "
|
||||
"Swing(V): 13 (Auto), Clock: 00:01",
|
||||
ac.toString());
|
||||
|
||||
const uint8_t SyntheticExample[kMirageStateLength] = {
|
||||
@@ -204,9 +217,19 @@ TEST(TestMirageAcClass, HumanReadable) {
|
||||
0x00, 0x00, 0x16, 0x14, 0x26};
|
||||
ac.setRaw(SyntheticExample);
|
||||
EXPECT_EQ(
|
||||
"Power: On, Mode: 2 (Cool), Temp: 25C, Fan: 0 (Auto), "
|
||||
"Swing(V): 0 (UNKNOWN), "
|
||||
"Turbo: Off, Light: Off, Sleep: Off, Clock: 14:16",
|
||||
"Model: 1 (KKG9AC1), Power: On, Mode: 2 (Cool), Temp: 25C, "
|
||||
"Fan: 0 (Auto), Turbo: Off, Sleep: Off, Light: Off, "
|
||||
"Swing(V): 0 (Off), Clock: 14:16",
|
||||
ac.toString());
|
||||
|
||||
// Tests for the KKG29AC1 model.
|
||||
ac.setModel(mirage_ac_remote_model_t::KKG29AC1);
|
||||
EXPECT_EQ(
|
||||
"Model: 2 (KKG29AC1), Power: On, Mode: 2 (Cool), Temp: 25C, "
|
||||
"Fan: 0 (Auto), Turbo: Off, Sleep: Off, Quiet: Off, Light: -, "
|
||||
"Swing(V): Off, Swing(H): Off, "
|
||||
"Filter: Off, Clean: -, On Timer: Off, Off Timer: Off, "
|
||||
"IFeel: Off",
|
||||
ac.toString());
|
||||
}
|
||||
|
||||
@@ -266,6 +289,15 @@ TEST(TestMirageAcClass, Turbo) {
|
||||
IRMirageAc ac(kGpioUnused);
|
||||
ac.begin();
|
||||
|
||||
ac.setModel(mirage_ac_remote_model_t::KKG9AC1);
|
||||
ac.setTurbo(true);
|
||||
EXPECT_TRUE(ac.getTurbo());
|
||||
ac.setTurbo(false);
|
||||
EXPECT_FALSE(ac.getTurbo());
|
||||
ac.setTurbo(true);
|
||||
EXPECT_TRUE(ac.getTurbo());
|
||||
|
||||
ac.setModel(mirage_ac_remote_model_t::KKG29AC1);
|
||||
ac.setTurbo(true);
|
||||
EXPECT_TRUE(ac.getTurbo());
|
||||
ac.setTurbo(false);
|
||||
@@ -278,6 +310,15 @@ TEST(TestMirageAcClass, Light) {
|
||||
IRMirageAc ac(kGpioUnused);
|
||||
ac.begin();
|
||||
|
||||
ac.setModel(mirage_ac_remote_model_t::KKG9AC1);
|
||||
ac.setLight(true);
|
||||
EXPECT_TRUE(ac.getLight());
|
||||
ac.setLight(false);
|
||||
EXPECT_FALSE(ac.getLight());
|
||||
ac.setLight(true);
|
||||
EXPECT_TRUE(ac.getLight());
|
||||
|
||||
ac.setModel(mirage_ac_remote_model_t::KKG29AC1);
|
||||
ac.setLight(true);
|
||||
EXPECT_TRUE(ac.getLight());
|
||||
ac.setLight(false);
|
||||
@@ -290,6 +331,15 @@ TEST(TestMirageAcClass, Sleep) {
|
||||
IRMirageAc ac(kGpioUnused);
|
||||
ac.begin();
|
||||
|
||||
ac.setModel(mirage_ac_remote_model_t::KKG9AC1);
|
||||
ac.setSleep(true);
|
||||
EXPECT_TRUE(ac.getSleep());
|
||||
ac.setSleep(false);
|
||||
EXPECT_FALSE(ac.getSleep());
|
||||
ac.setSleep(true);
|
||||
EXPECT_TRUE(ac.getSleep());
|
||||
|
||||
ac.setModel(mirage_ac_remote_model_t::KKG29AC1);
|
||||
ac.setSleep(true);
|
||||
EXPECT_TRUE(ac.getSleep());
|
||||
ac.setSleep(false);
|
||||
@@ -302,6 +352,7 @@ TEST(TestMirageAcClass, Clock) {
|
||||
IRMirageAc ac(kGpioUnused);
|
||||
ac.begin();
|
||||
|
||||
ac.setModel(mirage_ac_remote_model_t::KKG9AC1); // This model supports time.
|
||||
ac.setClock(0);
|
||||
EXPECT_EQ(0, ac.getClock());
|
||||
ac.setClock(12 * 60 * 60 + 30 * 60 + 59); // aka. 12:30:59
|
||||
@@ -310,6 +361,11 @@ TEST(TestMirageAcClass, Clock) {
|
||||
EXPECT_EQ(23 * 60 * 60 + 59 * 60 + 59, ac.getClock());
|
||||
ac.setClock(24 * 60 * 60); // aka. 24:00:00
|
||||
EXPECT_EQ(23 * 60 * 60 + 59 * 60 + 59, ac.getClock()); // aka. 23:59:59
|
||||
|
||||
ac.setModel(mirage_ac_remote_model_t::KKG29AC1); // This model has no clock.
|
||||
EXPECT_EQ(0, ac.getClock());
|
||||
ac.setClock(12 * 60 * 60 + 30 * 60 + 59); // aka. 12:30:59
|
||||
EXPECT_EQ(0, ac.getClock());
|
||||
}
|
||||
|
||||
TEST(TestMirageAcClass, Checksums) {
|
||||
@@ -327,6 +383,9 @@ TEST(TestMirageAcClass, SwingV) {
|
||||
IRMirageAc ac(kGpioUnused);
|
||||
ac.begin();
|
||||
|
||||
// Set the model to one with full swingv support.
|
||||
ac.setModel(mirage_ac_remote_model_t::KKG9AC1);
|
||||
|
||||
ac.setSwingV(kMirageAcSwingVAuto);
|
||||
EXPECT_EQ(kMirageAcSwingVAuto, ac.getSwingV());
|
||||
|
||||
@@ -341,4 +400,192 @@ TEST(TestMirageAcClass, SwingV) {
|
||||
|
||||
ac.setSwingV(kMirageAcSwingVLowest - 1);
|
||||
EXPECT_EQ(kMirageAcSwingVAuto, ac.getSwingV());
|
||||
|
||||
// Set the model to one with limited swingv support.
|
||||
ac.setModel(mirage_ac_remote_model_t::KKG29AC1);
|
||||
|
||||
ac.setSwingV(kMirageAcSwingVAuto);
|
||||
EXPECT_EQ(kMirageAcSwingVAuto, ac.getSwingV());
|
||||
ac.setSwingV(kMirageAcSwingVOff);
|
||||
EXPECT_EQ(kMirageAcSwingVOff, ac.getSwingV());
|
||||
ac.setSwingV(kMirageAcSwingVHigh);
|
||||
EXPECT_EQ(kMirageAcSwingVAuto, ac.getSwingV());
|
||||
ac.setSwingV(0xFF);
|
||||
EXPECT_EQ(kMirageAcSwingVAuto, ac.getSwingV());
|
||||
ac.setSwingV(kMirageAcSwingVOff);
|
||||
EXPECT_EQ(kMirageAcSwingVOff, ac.getSwingV());
|
||||
}
|
||||
|
||||
TEST(TestMirageAcClass, SwingH) {
|
||||
IRMirageAc ac(kGpioUnused);
|
||||
ac.begin();
|
||||
|
||||
ac.setModel(mirage_ac_remote_model_t::KKG9AC1);
|
||||
ac.setSwingH(true);
|
||||
EXPECT_FALSE(ac.getSwingH());
|
||||
ac.setSwingH(false);
|
||||
EXPECT_FALSE(ac.getSwingH());
|
||||
ac.setSwingH(true);
|
||||
EXPECT_FALSE(ac.getSwingH());
|
||||
|
||||
ac.setModel(mirage_ac_remote_model_t::KKG29AC1);
|
||||
ac.setSwingH(true);
|
||||
EXPECT_TRUE(ac.getSwingH());
|
||||
ac.setSwingH(false);
|
||||
EXPECT_FALSE(ac.getSwingH());
|
||||
ac.setSwingH(true);
|
||||
EXPECT_TRUE(ac.getSwingH());
|
||||
}
|
||||
|
||||
TEST(TestMirageAcClass, Filter) {
|
||||
IRMirageAc ac(kGpioUnused);
|
||||
ac.begin();
|
||||
|
||||
ac.setModel(mirage_ac_remote_model_t::KKG9AC1); // No Support
|
||||
ac.setFilter(true);
|
||||
EXPECT_FALSE(ac.getFilter());
|
||||
ac.setFilter(false);
|
||||
EXPECT_FALSE(ac.getFilter());
|
||||
ac.setFilter(true);
|
||||
EXPECT_FALSE(ac.getFilter());
|
||||
|
||||
ac.setModel(mirage_ac_remote_model_t::KKG29AC1); // Supported
|
||||
ac.setFilter(true);
|
||||
EXPECT_TRUE(ac.getFilter());
|
||||
ac.setFilter(false);
|
||||
EXPECT_FALSE(ac.getFilter());
|
||||
ac.setFilter(true);
|
||||
EXPECT_TRUE(ac.getFilter());
|
||||
}
|
||||
|
||||
TEST(TestMirageAcClass, Quiet) {
|
||||
IRMirageAc ac(kGpioUnused);
|
||||
ac.begin();
|
||||
|
||||
ac.setModel(mirage_ac_remote_model_t::KKG9AC1); // No Support
|
||||
ac.setQuiet(true);
|
||||
EXPECT_FALSE(ac.getQuiet());
|
||||
ac.setQuiet(false);
|
||||
EXPECT_FALSE(ac.getQuiet());
|
||||
ac.setQuiet(true);
|
||||
EXPECT_FALSE(ac.getQuiet());
|
||||
|
||||
ac.setModel(mirage_ac_remote_model_t::KKG29AC1); // Supported
|
||||
ac.setQuiet(true);
|
||||
EXPECT_TRUE(ac.getQuiet());
|
||||
ac.setQuiet(false);
|
||||
EXPECT_FALSE(ac.getQuiet());
|
||||
ac.setQuiet(true);
|
||||
EXPECT_TRUE(ac.getQuiet());
|
||||
}
|
||||
|
||||
TEST(TestMirageAcClass, CleanToggle) {
|
||||
IRMirageAc ac(kGpioUnused);
|
||||
ac.begin();
|
||||
|
||||
ac.setModel(mirage_ac_remote_model_t::KKG9AC1);
|
||||
ac.setCleanToggle(true);
|
||||
EXPECT_FALSE(ac.getCleanToggle());
|
||||
ac.setCleanToggle(false);
|
||||
EXPECT_FALSE(ac.getCleanToggle());
|
||||
ac.setCleanToggle(true);
|
||||
EXPECT_FALSE(ac.getCleanToggle());
|
||||
|
||||
ac.setModel(mirage_ac_remote_model_t::KKG29AC1);
|
||||
ac.setCleanToggle(true);
|
||||
EXPECT_TRUE(ac.getCleanToggle());
|
||||
ac.setCleanToggle(false);
|
||||
EXPECT_FALSE(ac.getCleanToggle());
|
||||
ac.setCleanToggle(true);
|
||||
EXPECT_TRUE(ac.getCleanToggle());
|
||||
ac.send(); // Should be reset when sent.
|
||||
EXPECT_FALSE(ac.getCleanToggle());
|
||||
}
|
||||
|
||||
TEST(TestMirageAcClass, Timers) {
|
||||
IRMirageAc ac(kGpioUnused);
|
||||
ac.begin();
|
||||
|
||||
ac.setModel(mirage_ac_remote_model_t::KKG9AC1); // No timer support
|
||||
EXPECT_EQ(0, ac.getOnTimer());
|
||||
EXPECT_EQ(0, ac.getOffTimer());
|
||||
ac.setOnTimer(12 * 60 + 37); // 12:37
|
||||
EXPECT_EQ(0, ac.getOnTimer());
|
||||
EXPECT_EQ(0, ac.getOffTimer());
|
||||
ac.setOffTimer(17 * 60 + 5); // 17:05
|
||||
EXPECT_EQ(0, ac.getOnTimer());
|
||||
EXPECT_EQ(0, ac.getOffTimer());
|
||||
|
||||
ac.setModel(mirage_ac_remote_model_t::KKG29AC1); // Timer supported
|
||||
EXPECT_EQ(0, ac.getOnTimer());
|
||||
EXPECT_EQ(0, ac.getOffTimer());
|
||||
|
||||
ac.setOnTimer(12 * 60 + 37); // 12:37
|
||||
EXPECT_EQ(12 * 60 + 37, ac.getOnTimer());
|
||||
EXPECT_EQ(0, ac.getOffTimer());
|
||||
|
||||
ac.setOffTimer(17 * 60 + 5); // 17:05
|
||||
EXPECT_EQ(17 * 60 + 5, ac.getOffTimer());
|
||||
EXPECT_EQ(12 * 60 + 37, ac.getOnTimer());
|
||||
ac.setOnTimer(0); // Off/Disabled
|
||||
EXPECT_EQ(0, ac.getOnTimer());
|
||||
EXPECT_EQ(17 * 60 + 5, ac.getOffTimer());
|
||||
ac.setOffTimer(0); // Off/Disabled
|
||||
EXPECT_EQ(0, ac.getOffTimer());
|
||||
EXPECT_EQ(0, ac.getOnTimer());
|
||||
|
||||
ac.setOnTimer(12 * 60 + 37); // 12:37
|
||||
ac.setOffTimer(17 * 60 + 5); // 17:05
|
||||
ac.setModel(mirage_ac_remote_model_t::KKG9AC1); // No timer support
|
||||
EXPECT_EQ(0, ac.getOffTimer());
|
||||
EXPECT_EQ(0, ac.getOnTimer());
|
||||
}
|
||||
|
||||
TEST(TestMirageAcClass, IFeelAndSensorTemp) {
|
||||
IRMirageAc ac(kGpioUnused);
|
||||
ac.begin();
|
||||
|
||||
ac.setModel(mirage_ac_remote_model_t::KKG9AC1); // No support
|
||||
EXPECT_FALSE(ac.getIFeel());
|
||||
EXPECT_EQ(0, ac.getSensorTemp());
|
||||
ac.setIFeel(true);
|
||||
EXPECT_FALSE(ac.getIFeel());
|
||||
EXPECT_EQ(0, ac.getSensorTemp());
|
||||
ac.setSensorTemp(20); // 20C
|
||||
EXPECT_FALSE(ac.getIFeel());
|
||||
EXPECT_EQ(0, ac.getSensorTemp());
|
||||
|
||||
ac.setModel(mirage_ac_remote_model_t::KKG29AC1); // Supported
|
||||
EXPECT_FALSE(ac.getIFeel());
|
||||
EXPECT_EQ(0, ac.getSensorTemp());
|
||||
ac.setIFeel(true);
|
||||
EXPECT_TRUE(ac.getIFeel());
|
||||
EXPECT_EQ(0, ac.getSensorTemp());
|
||||
ac.setSensorTemp(25); // 25C
|
||||
EXPECT_TRUE(ac.getIFeel());
|
||||
EXPECT_EQ(25, ac.getSensorTemp());
|
||||
ac.setIFeel(false);
|
||||
EXPECT_FALSE(ac.getIFeel());
|
||||
}
|
||||
|
||||
TEST(TestMirageAcClass, getModel) {
|
||||
IRMirageAc ac(kGpioUnused);
|
||||
ac.begin();
|
||||
const uint8_t KKG9AC1[kMirageStateLength] = {
|
||||
0x56, 0x6C, 0x00, 0x00, 0x20, 0xD8, 0x00, 0x00,
|
||||
0x0C, 0x32, 0x0B, 0x00, 0x32, 0x0F, 0x64};
|
||||
EXPECT_EQ(mirage_ac_remote_model_t::KKG9AC1, IRMirageAc::getModel(KKG9AC1));
|
||||
|
||||
// https://github.com/crankyoldgit/IRremoteESP8266/issues/1573#issuecomment-955722044
|
||||
const uint8_t KKG29AC1[kMirageStateLength] = {
|
||||
0x56, 0x74, 0x00, 0x00, 0x12, 0x00, 0x40, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1D};
|
||||
EXPECT_EQ(mirage_ac_remote_model_t::KKG29AC1, IRMirageAc::getModel(KKG29AC1));
|
||||
|
||||
// https://github.com/crankyoldgit/IRremoteESP8266/issues/1573#issuecomment-962362540
|
||||
const uint8_t KKG29AC1_2[kMirageStateLength] = {
|
||||
0x56, 0x72, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19};
|
||||
EXPECT_EQ(mirage_ac_remote_model_t::KKG29AC1,
|
||||
IRMirageAc::getModel(KKG29AC1_2));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user