SharpAc: Allow position control of SwingV (#1594)

* Add the ability to set Vertical Swing positions via `setSwingV()`
  - e.g. Coanda setting.
    * Modify `setSwingV()` to take an optional parameter to override the heat mode check.
    * Enable forcing of Coanda mode in Cool.
  - Several other undocumented positions discovered via experimentation.
  - May not work on all models.
* Update & add unit tests accordingly.

Fixes #1590
This commit is contained in:
David Conran
2021-09-06 14:28:59 +10:00
committed by GitHub
parent 999dde2d71
commit a359a43367
6 changed files with 256 additions and 74 deletions

View File

@@ -1893,6 +1893,7 @@ void IRac::sanyo88(IRSanyoAc88 *ac,
/// @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] swingv_prev The previous vertical swing setting.
/// @param[in] turbo Run the device in turbo/powerful mode.
/// @param[in] light Turn on the LED/Display mode.
/// @param[in] filter Turn on the (ion/pollen/etc) filter mode.
@@ -1901,14 +1902,15 @@ void IRac::sharp(IRSharpAc *ac, const sharp_ac_remote_model_t model,
const bool on, const bool prev_power,
const stdAc::opmode_t mode,
const float degrees, const stdAc::fanspeed_t fan,
const stdAc::swingv_t swingv, const bool turbo,
const stdAc::swingv_t swingv,
const stdAc::swingv_t swingv_prev, const bool turbo,
const bool light, const bool filter, const bool clean) {
ac->begin();
ac->setModel(model);
ac->setMode(ac->convertMode(mode));
ac->setTemp(degrees);
ac->setFan(ac->convertFan(fan, model));
ac->setSwingToggle(swingv != stdAc::swingv_t::kOff);
if (swingv != swingv_prev) ac->setSwingV(ac->convertSwingV(swingv));
// Econo deliberately not used as it cycles through 3 modes uncontrollably.
// ac->setEconoToggle(econo);
ac->setIon(filter);
@@ -2495,10 +2497,10 @@ bool IRac::sendAc(const stdAc::state_t desired, const stdAc::state_t *prev) {
#if (SEND_HITACHI_AC1 || SEND_SAMSUNG_AC || SEND_SHARP_AC)
const bool prev_power = (prev != NULL) ? prev->power : !send.power;
#endif // (SEND_HITACHI_AC1 || SEND_SAMSUNG_AC || SEND_SHARP_AC)
#if SEND_LG
#if (SEND_LG || SEND_SHARP_AC)
const stdAc::swingv_t prev_swingv = (prev != NULL) ? prev->swingv
: stdAc::swingv_t::kOff;
#endif // SEND_LG
#endif // (SEND_LG || SEND_SHARP_AC)
// Per vendor settings & setup.
switch (send.protocol) {
#if SEND_AIRWELL
@@ -2899,8 +2901,8 @@ bool IRac::sendAc(const stdAc::state_t desired, const stdAc::state_t *prev) {
{
IRSharpAc ac(_pin, _inverted, _modulation);
sharp(&ac, (sharp_ac_remote_model_t)send.model, send.power, prev_power,
send.mode, degC, send.fanspeed, send.swingv, send.turbo, send.light,
send.filter, send.clean);
send.mode, degC, send.fanspeed, send.swingv, prev_swingv,
send.turbo, send.light, send.filter, send.clean);
break;
}
#endif // SEND_SHARP_AC
@@ -4136,7 +4138,7 @@ namespace IRAcUtils {
case decode_type_t::SHARP_AC: {
IRSharpAc ac(kGpioUnused);
ac.setRaw(decode->state);
*result = ac.toCommon();
*result = ac.toCommon(prev);
break;
}
#endif // DECODE_SHARP_AC

View File

@@ -413,7 +413,8 @@ void electra(IRElectraAc *ac,
void sharp(IRSharpAc *ac, const sharp_ac_remote_model_t model,
const bool on, const bool prev_power, 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 stdAc::swingv_t swingv, const stdAc::swingv_t swingv_prev,
const bool turbo, const bool light,
const bool filter, const bool clean);
#endif // SEND_SHARP_AC
#if SEND_TCL112AC

View File

@@ -45,6 +45,7 @@ using irutils::addIntToString;
using irutils::addLabeledString;
using irutils::addModeToString;
using irutils::addModelToString;
using irutils::addSwingVToString;
using irutils::addTempToString;
using irutils::minsToString;
@@ -544,24 +545,73 @@ void IRSharpAc::setTurbo(const bool on) {
_.Special = kSharpAcSpecialTurbo;
}
/// Get the Vertical Swing setting of the A/C.
/// @return The position of the Vertical Swing setting.
uint8_t IRSharpAc::getSwingV(void) const { return _.Swing; }
/// Set the Vertical Swing setting of the A/C.
/// @note Some positions may not work on all models.
/// @param[in] position The desired position/setting.
/// @note `setSwingV(kSharpAcSwingVLowest)` will only allow the Lowest setting
/// in Heat mode, it will default to `kSharpAcSwingVLow` otherwise.
/// If you want to set this value in other modes e.g. Cool, you must
/// use `setSwingV`s optional `force` parameter.
/// @param[in] force Do we override the safety checks and just do it?
void IRSharpAc::setSwingV(const uint8_t position, const bool force) {
switch (position) {
case kSharpAcSwingVCoanda:
// Only allowed in Heat mode.
if (!force && getMode() != kSharpAcHeat) {
setSwingV(kSharpAcSwingVLow); // Use the next lowest setting.
return;
}
// FALLTHRU
case kSharpAcSwingVHigh:
case kSharpAcSwingVMid:
case kSharpAcSwingVLow:
case kSharpAcSwingVToggle:
case kSharpAcSwingVOff:
case kSharpAcSwingVLast: // Technically valid, but we don't use it.
// All expected non-positions set the special bits.
_.Special = kSharpAcSpecialSwing;
// FALLTHRU
case kSharpAcSwingVIgnore:
_.Swing = position;
}
}
/// Convert a standard A/C vertical swing into its native setting.
/// @param[in] position A stdAc::swingv_t position to convert.
/// @return The equivalent native horizontal swing position.
uint8_t IRSharpAc::convertSwingV(const stdAc::swingv_t position) {
switch (position) {
case stdAc::swingv_t::kHighest:
case stdAc::swingv_t::kHigh: return kSharpAcSwingVHigh;
case stdAc::swingv_t::kMiddle: return kSharpAcSwingVMid;
case stdAc::swingv_t::kLow: return kSharpAcSwingVLow;
case stdAc::swingv_t::kLowest: return kSharpAcSwingVCoanda;
case stdAc::swingv_t::kAuto: return kSharpAcSwingVToggle;
case stdAc::swingv_t::kOff: return kSharpAcSwingVOff;
default: return kSharpAcSwingVIgnore;
}
}
/// Get the (vertical) Swing Toggle setting of the A/C.
/// @return true, the setting is on. false, the setting is off.
bool IRSharpAc::getSwingToggle(void) const {
return _.Swing == kSharpAcSwingToggle;
return getSwingV() == kSharpAcSwingVToggle;
}
/// Set the (vertical) Swing Toggle setting of the A/C.
/// @param[in] on true, the setting is on. false, the setting is off.
void IRSharpAc::setSwingToggle(const bool on) {
_.Swing = (on ? kSharpAcSwingToggle : kSharpAcSwingNoToggle);
setSwingV(on ? kSharpAcSwingVToggle : kSharpAcSwingVIgnore);
if (on) _.Special = kSharpAcSpecialSwing;
}
/// Get the Ion (Filter) setting of the A/C.
/// @return true, the setting is on. false, the setting is off.
bool IRSharpAc::getIon(void) const {
return _.Ion;
}
bool IRSharpAc::getIon(void) const { return _.Ion; }
/// Set the Ion (Filter) setting of the A/C.
/// @param[in] on true, the setting is on. false, the setting is off.
@@ -628,15 +678,11 @@ uint16_t IRSharpAc::getTimerTime(void) const {
/// Is the Timer enabled?
/// @return true, the setting is on. false, the setting is off.
bool IRSharpAc::getTimerEnabled(void) const {
return _.TimerEnabled;
}
bool IRSharpAc::getTimerEnabled(void) const { return _.TimerEnabled; }
/// Get the current timer type.
/// @return true, It's an "On" timer. false, It's an "Off" timer.
bool IRSharpAc::getTimerType(void) const {
return _.TimerType;
}
bool IRSharpAc::getTimerType(void) const { return _.TimerType; }
/// Set or cancel the timer function.
/// @param[in] enable Is the timer to be enabled (true) or canceled(false)?
@@ -765,10 +811,34 @@ stdAc::fanspeed_t IRSharpAc::toCommonFanSpeed(const uint8_t speed) const {
}
}
/// Convert a native vertical swing postion to it's common equivalent.
/// @param[in] pos A native position to convert.
/// @param[in] mode What operating mode are we in?
/// @return The common vertical swing position.
stdAc::swingv_t IRSharpAc::toCommonSwingV(const uint8_t pos,
const stdAc::opmode_t mode) const {
switch (pos) {
case kSharpAcSwingVHigh: return stdAc::swingv_t::kHighest;
case kSharpAcSwingVMid: return stdAc::swingv_t::kMiddle;
case kSharpAcSwingVLow: return stdAc::swingv_t::kLow;
case kSharpAcSwingVCoanda: // Coanda has mode dependent positionss
switch (mode) {
case stdAc::opmode_t::kCool: return stdAc::swingv_t::kHighest;
case stdAc::opmode_t::kHeat: return stdAc::swingv_t::kLowest;
default: return stdAc::swingv_t::kOff;
}
case kSharpAcSwingVToggle: return stdAc::swingv_t::kAuto;
default: return stdAc::swingv_t::kOff;
}
}
/// Convert the current internal state into its stdAc::state_t equivalent.
/// @param[in] prev Ptr to the previous state if required.
/// @return The stdAc equivalent of the native settings.
stdAc::state_t IRSharpAc::toCommon(void) const {
stdAc::state_t IRSharpAc::toCommon(const stdAc::state_t *prev) const {
stdAc::state_t result;
// Start with the previous state if given it.
if (prev != NULL) result = *prev;
result.protocol = decode_type_t::SHARP_AC;
result.model = getModel();
result.power = getPower();
@@ -777,8 +847,8 @@ stdAc::state_t IRSharpAc::toCommon(void) const {
result.degrees = getTemp();
result.fanspeed = toCommonFanSpeed(_.Fan);
result.turbo = getTurbo();
result.swingv = getSwingToggle() ? stdAc::swingv_t::kAuto
: stdAc::swingv_t::kOff;
if (getSwingV() != kSharpAcSwingVIgnore)
result.swingv = toCommonSwingV(getSwingV(), result.mode);
result.filter = _.Ion;
result.econo = getEconoToggle();
result.light = getLightToggle();
@@ -797,14 +867,15 @@ stdAc::state_t IRSharpAc::toCommon(void) const {
String IRSharpAc::toString(void) const {
String result = "";
const sharp_ac_remote_model_t model = getModel();
result.reserve(160); // Reserve some heap for the string to reduce fragging.
result.reserve(170); // Reserve some heap for the string to reduce fragging.
result += addModelToString(decode_type_t::SHARP_AC, getModel(), false);
result += addLabeledString(isPowerSpecial() ? "-"
: (getPower() ? kOnStr : kOffStr),
kPowerStr);
const uint8_t mode = _.Mode;
result += addModeToString(
_.Mode,
mode,
// Make the value invalid if the model doesn't support an Auto mode.
(model == sharp_ac_remote_model_t::A907) ? kSharpAcAuto : 255,
kSharpAcCool, kSharpAcHeat, kSharpAcDry, kSharpAcFan);
@@ -821,8 +892,29 @@ String IRSharpAc::toString(void) const {
kSharpAcFanAuto, kSharpAcFanAuto,
kSharpAcFanMed);
}
if (getSwingV() == kSharpAcSwingVIgnore) {
result += addIntToString(kSharpAcSwingVIgnore, kSwingVStr);
result += kSpaceLBraceStr;
result += kNAStr;
result += ')';
} else {
result += addSwingVToString(
getSwingV(), 0xFF,
// Coanda means Highest when in Cool mode.
(mode == kSharpAcCool) ? kSharpAcSwingVCoanda : kSharpAcSwingVToggle,
kSharpAcSwingVHigh,
0xFF, // Upper Middle is unused
kSharpAcSwingVMid,
0xFF, // Lower Middle is unused
kSharpAcSwingVLow,
kSharpAcSwingVCoanda,
kSharpAcSwingVOff,
// Below are unused.
kSharpAcSwingVToggle,
0xFF,
0xFF);
}
result += addBoolToString(getTurbo(), kTurboStr);
result += addBoolToString(getSwingToggle(), kSwingVToggleStr);
result += addBoolToString(_.Ion, kIonStr);
switch (model) {
case sharp_ac_remote_model_t::A705:

View File

@@ -122,8 +122,23 @@ const uint8_t kSharpAcTimerHoursMax = 0b1100; // 12
const uint8_t kSharpAcOffTimerType = 0b0;
const uint8_t kSharpAcOnTimerType = 0b1;
const uint8_t kSharpAcSwingToggle = 0b111;
const uint8_t kSharpAcSwingNoToggle = 0b000;
// Ref: https://github.com/crankyoldgit/IRremoteESP8266/discussions/1590#discussioncomment-1260213
const uint8_t kSharpAcSwingVIgnore = 0b000; // Don't change the swing setting.
const uint8_t kSharpAcSwingVHigh = 0b001; // 0° down. Similar to Cool Coanda.
const uint8_t kSharpAcSwingVOff = 0b010; // Stop & Go to last fixed pos.
const uint8_t kSharpAcSwingVMid = 0b011; // 30° down
const uint8_t kSharpAcSwingVLow = 0b100; // 45° down
const uint8_t kSharpAcSwingVLast = 0b101; // Same as kSharpAcSwingVOff.
// Toggles between last fixed pos & either 75° down (Heat) or 0° down (Cool)
// i.e. alternate between last pos <-> 75° down if in Heat mode, AND
// alternate between last pos <-> 0° down if in Cool mode.
// Note: `setSwingV(kSharpAcSwingVLowest)` will only allow the Lowest setting in
// Heat mode, it will default to `kSharpAcSwingVLow` otherwise.
// If you want to set this value in other modes e.g. Cool, you must
// use `setSwingV`s optional `force` parameter.
const uint8_t kSharpAcSwingVLowest = 0b110;
const uint8_t kSharpAcSwingVCoanda = kSharpAcSwingVLowest;
const uint8_t kSharpAcSwingVToggle = 0b111; // Toggle Constant swinging on/off.
const uint8_t kSharpAcSpecialPower = 0x00;
const uint8_t kSharpAcSpecialTurbo = 0x01;
@@ -167,6 +182,8 @@ class IRSharpAc {
void setTurbo(const bool on);
bool getSwingToggle(void) const;
void setSwingToggle(const bool on);
uint8_t getSwingV(void) const;
void setSwingV(const uint8_t position, const bool force = false);
bool getIon(void) const;
void setIon(const bool on);
bool getEconoToggle(void) const;
@@ -188,9 +205,13 @@ class IRSharpAc {
static uint8_t convertFan(const stdAc::fanspeed_t speed,
const sharp_ac_remote_model_t model =
sharp_ac_remote_model_t::A907);
static uint8_t convertSwingV(const stdAc::swingv_t position);
stdAc::opmode_t toCommonMode(const uint8_t mode) const;
stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed) const;
stdAc::state_t toCommon(void) const;
stdAc::swingv_t toCommonSwingV(
const uint8_t pos,
const stdAc::opmode_t mode = stdAc::opmode_t::kHeat) const;
stdAc::state_t toCommon(const stdAc::state_t *prev = NULL) const;
String toString(void) const;
#ifndef UNIT_TEST

View File

@@ -1610,7 +1610,7 @@ TEST(TestIRac, Sharp) {
IRrecv capture(kGpioUnused);
char expected[] =
"Model: 1 (A907), Power: On, Mode: 2 (Cool), Temp: 28C, Fan: 3 (Medium), "
"Turbo: Off, Swing(V) Toggle: On, Ion: On, Econo: -, Clean: Off";
"Swing(V): 7 (Swing), Turbo: Off, Ion: On, Econo: -, Clean: Off";
ac.begin();
irac.sharp(&ac,
@@ -1621,6 +1621,7 @@ TEST(TestIRac, Sharp) {
28, // Celsius
stdAc::fanspeed_t::kMedium, // Fan speed
stdAc::swingv_t::kAuto, // Vertical swing
stdAc::swingv_t::kOff, // Previous Vertical swing
false, // Turbo
false, // Light
true, // Filter (Ion)

View File

@@ -410,8 +410,8 @@ TEST(TestDecodeSharpAc, RealExample) {
EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits);
EXPECT_EQ(
"Model: 1 (A907), "
"Power: On, Mode: 2 (Cool), Temp: 27C, Fan: 2 (Auto), Turbo: Off, "
"Swing(V) Toggle: Off, Ion: Off, Econo: -, Clean: Off",
"Power: On, Mode: 2 (Cool), Temp: 27C, Fan: 2 (Auto), "
"Swing(V): 0 (N/A), Turbo: Off, Ion: Off, Econo: -, Clean: Off",
IRAcUtils::resultAcToString(&irsend.capture));
stdAc::state_t r, p;
ASSERT_TRUE(IRAcUtils::decodeToState(&irsend.capture, &r, &p));
@@ -561,7 +561,7 @@ TEST(TestSharpAcClass, OperatingMode) {
// Check toString() says Fan rather than Auto.
EXPECT_EQ(
"Model: 2 (A705), Power: Off, Mode: 0 (Fan), Temp: 15C, Fan: 2 (Auto), "
"Turbo: Off, Swing(V) Toggle: Off, Ion: Off, Light: -, Clean: Off",
"Swing(V): 0 (N/A), Turbo: Off, Ion: Off, Light: -, Clean: Off",
ac.toString());
}
@@ -615,8 +615,8 @@ TEST(TestSharpAcClass, ReconstructKnownState) {
EXPECT_STATE_EQ(on_auto_auto, ac.getRaw(), kSharpAcBits);
EXPECT_EQ(
"Model: 1 (A907), "
"Power: On, Mode: 0 (Auto), Temp: 15C, Fan: 2 (Auto), Turbo: Off, "
"Swing(V) Toggle: Off, Ion: Off, Econo: -, Clean: Off",
"Power: On, Mode: 0 (Auto), Temp: 15C, Fan: 2 (Auto), Swing(V): 0 (N/A), "
"Turbo: Off, Ion: Off, Econo: -, Clean: Off",
ac.toString());
uint8_t cool_auto_28[kSharpAcStateLength] = {
@@ -629,8 +629,8 @@ TEST(TestSharpAcClass, ReconstructKnownState) {
ac.setTemp(28);
EXPECT_EQ(
"Model: 1 (A907), "
"Power: On, Mode: 2 (Cool), Temp: 28C, Fan: 2 (Auto), Turbo: Off, "
"Swing(V) Toggle: Off, Ion: Off, Econo: -, Clean: Off",
"Power: On, Mode: 2 (Cool), Temp: 28C, Fan: 2 (Auto), Swing(V): 0 (N/A), "
"Turbo: Off, Ion: Off, Econo: -, Clean: Off",
ac.toString());
EXPECT_STATE_EQ(cool_auto_28, ac.getRaw(), kSharpAcBits);
}
@@ -647,8 +647,8 @@ TEST(TestSharpAcClass, KnownStates) {
ac.setRaw(off_auto_auto);
EXPECT_EQ(
"Model: 1 (A907), "
"Power: Off, Mode: 0 (Auto), Temp: 15C, Fan: 2 (Auto), Turbo: Off, "
"Swing(V) Toggle: Off, Ion: Off, Econo: -, Clean: Off",
"Power: Off, Mode: 0 (Auto), Temp: 15C, Fan: 2 (Auto), "
"Swing(V): 0 (N/A), Turbo: Off, Ion: Off, Econo: -, Clean: Off",
ac.toString());
uint8_t on_auto_auto[kSharpAcStateLength] = {
0xAA, 0x5A, 0xCF, 0x10, 0x00, 0x11, 0x20, 0x00, 0x08, 0x80, 0x00, 0xE0,
@@ -657,8 +657,8 @@ TEST(TestSharpAcClass, KnownStates) {
ac.setRaw(on_auto_auto);
EXPECT_EQ(
"Model: 1 (A907), "
"Power: On, Mode: 0 (Auto), Temp: 15C, Fan: 2 (Auto), Turbo: Off, "
"Swing(V) Toggle: Off, Ion: Off, Econo: -, Clean: Off",
"Power: On, Mode: 0 (Auto), Temp: 15C, Fan: 2 (Auto), Swing(V): 0 (N/A), "
"Turbo: Off, Ion: Off, Econo: -, Clean: Off",
ac.toString());
uint8_t cool_auto_28[kSharpAcStateLength] = {
0xAA, 0x5A, 0xCF, 0x10, 0xCD, 0x31, 0x22, 0x00, 0x08, 0x80, 0x04, 0xE0,
@@ -667,8 +667,8 @@ TEST(TestSharpAcClass, KnownStates) {
ac.setRaw(cool_auto_28);
EXPECT_EQ(
"Model: 1 (A907), "
"Power: On, Mode: 2 (Cool), Temp: 28C, Fan: 2 (Auto), Turbo: Off, "
"Swing(V) Toggle: Off, Ion: Off, Econo: -, Clean: Off",
"Power: On, Mode: 2 (Cool), Temp: 28C, Fan: 2 (Auto), Swing(V): 0 (N/A), "
"Turbo: Off, Ion: Off, Econo: -, Clean: Off",
ac.toString());
uint8_t cool_fan1_28[kSharpAcStateLength] = {
0xAA, 0x5A, 0xCF, 0x10, 0xCD, 0x31, 0x42, 0x00, 0x08, 0x80, 0x05, 0xE0,
@@ -677,8 +677,8 @@ TEST(TestSharpAcClass, KnownStates) {
ac.setRaw(cool_fan1_28);
EXPECT_EQ(
"Model: 1 (A907), "
"Power: On, Mode: 2 (Cool), Temp: 28C, Fan: 4 (Low), Turbo: Off, "
"Swing(V) Toggle: Off, Ion: Off, Econo: -, Clean: Off",
"Power: On, Mode: 2 (Cool), Temp: 28C, Fan: 4 (Low), Swing(V): 0 (N/A), "
"Turbo: Off, Ion: Off, Econo: -, Clean: Off",
ac.toString());
uint8_t cool_fan2_28[kSharpAcStateLength] = {
0xAA, 0x5A, 0xCF, 0x10, 0xCD, 0x31, 0x32, 0x00, 0x08, 0x80, 0x05, 0xE0,
@@ -688,7 +688,7 @@ TEST(TestSharpAcClass, KnownStates) {
EXPECT_EQ(
"Model: 1 (A907), "
"Power: On, Mode: 2 (Cool), Temp: 28C, Fan: 3 (Medium), "
"Turbo: Off, Swing(V) Toggle: Off, Ion: Off, Econo: -, Clean: Off",
"Swing(V): 0 (N/A), Turbo: Off, Ion: Off, Econo: -, Clean: Off",
ac.toString());
uint8_t cool_fan3_28[kSharpAcStateLength] = {
0xAA, 0x5A, 0xCF, 0x10, 0xCD, 0x31, 0x52, 0x00, 0x08, 0x80, 0x05, 0xE0,
@@ -698,7 +698,7 @@ TEST(TestSharpAcClass, KnownStates) {
EXPECT_EQ(
"Model: 1 (A907), "
"Power: On, Mode: 2 (Cool), Temp: 28C, Fan: 5 (UNKNOWN), "
"Turbo: Off, Swing(V) Toggle: Off, Ion: Off, Econo: -, Clean: Off",
"Swing(V): 0 (N/A), Turbo: Off, Ion: Off, Econo: -, Clean: Off",
ac.toString());
uint8_t cool_fan4_28[kSharpAcStateLength] = {
0xAA, 0x5A, 0xCF, 0x10, 0xCD, 0x31, 0x72, 0x00, 0x08, 0x80, 0x05, 0xE0,
@@ -707,8 +707,8 @@ TEST(TestSharpAcClass, KnownStates) {
ac.setRaw(cool_fan4_28);
EXPECT_EQ(
"Model: 1 (A907), "
"Power: On, Mode: 2 (Cool), Temp: 28C, Fan: 7 (High), Turbo: Off, "
"Swing(V) Toggle: Off, Ion: Off, Econo: -, Clean: Off",
"Power: On, Mode: 2 (Cool), Temp: 28C, Fan: 7 (High), Swing(V): 0 (N/A), "
"Turbo: Off, Ion: Off, Econo: -, Clean: Off",
ac.toString());
uint8_t cool_fan4_28_ion_on[kSharpAcStateLength] = {
0xAA, 0x5A, 0xCF, 0x10, 0xCD, 0x61, 0x72, 0x08, 0x08, 0x80, 0x00, 0xE4,
@@ -717,8 +717,8 @@ TEST(TestSharpAcClass, KnownStates) {
ac.setRaw(cool_fan4_28_ion_on);
EXPECT_EQ(
"Model: 1 (A907), "
"Power: -, Mode: 2 (Cool), Temp: 28C, Fan: 7 (High), Turbo: Off, "
"Swing(V) Toggle: Off, Ion: On, Econo: -, Clean: Off",
"Power: -, Mode: 2 (Cool), Temp: 28C, Fan: 7 (High), Swing(V): 0 (N/A), "
"Turbo: Off, Ion: On, Econo: -, Clean: Off",
ac.toString());
/* Unsupported / Not yet reverse engineered.
uint8_t cool_fan4_28_eco1[kSharpAcStateLength] = {
@@ -735,8 +735,8 @@ TEST(TestSharpAcClass, KnownStates) {
ac.setRaw(dry_auto);
EXPECT_EQ(
"Model: 1 (A907), "
"Power: On, Mode: 3 (Dry), Temp: 15C, Fan: 2 (Auto), Turbo: Off, "
"Swing(V) Toggle: Off, Ion: Off, Econo: -, Clean: Off",
"Power: On, Mode: 3 (Dry), Temp: 15C, Fan: 2 (Auto), Swing(V): 0 (N/A), "
"Turbo: Off, Ion: Off, Econo: -, Clean: Off",
ac.toString());
}
@@ -747,6 +747,7 @@ TEST(TestSharpAcClass, toCommon) {
ac.setMode(kSharpAcCool);
ac.setTemp(20);
ac.setFan(kSharpAcFanMax);
ac.setSwingV(kSharpAcSwingVOff);
// Now test it.
ASSERT_EQ(decode_type_t::SHARP_AC, ac.toCommon().protocol);
ASSERT_TRUE(ac.toCommon().power);
@@ -755,8 +756,8 @@ TEST(TestSharpAcClass, toCommon) {
ASSERT_EQ(stdAc::opmode_t::kCool, ac.toCommon().mode);
ASSERT_EQ(stdAc::fanspeed_t::kMax, ac.toCommon().fanspeed);
ASSERT_EQ(sharp_ac_remote_model_t::A705, ac.toCommon().model);
// Unsupported.
ASSERT_EQ(stdAc::swingv_t::kOff, ac.toCommon().swingv);
// Unsupported.
ASSERT_EQ(stdAc::swingh_t::kOff, ac.toCommon().swingh);
ASSERT_FALSE(ac.toCommon().turbo);
ASSERT_FALSE(ac.toCommon().quiet);
@@ -868,20 +869,20 @@ TEST(TestSharpAcClass, Turbo) {
EXPECT_EQ(kSharpAcFanMax, ac.getFan());
EXPECT_EQ(
"Model: 3 (A903), "
"Power: -, Mode: 2 (Cool), Temp: 21C, Fan: 7 (High), Turbo: On, "
"Swing(V) Toggle: Off, Ion: On, Light: -, Clean: Off",
"Power: -, Mode: 2 (Cool), Temp: 21C, Fan: 7 (High), "
"Swing(V): 0 (N/A), Turbo: On, Ion: On, Light: -, Clean: Off",
ac.toString());
ac.setRaw(off_state);
EXPECT_FALSE(ac.getTurbo());
EXPECT_EQ(
"Model: 3 (A903), "
"Power: -, Mode: 2 (Cool), Temp: 21C, Fan: 7 (High), Turbo: Off, "
"Swing(V) Toggle: Off, Ion: On, Light: -, Clean: Off",
"Power: -, Mode: 2 (Cool), Temp: 21C, Fan: 7 (High), "
"Swing(V): 0 (N/A), Turbo: Off, Ion: On, Light: -, Clean: Off",
ac.toString());
}
TEST(TestSharpAcClass, SwingToggle) {
TEST(TestSharpAcClass, Swings) {
IRSharpAc ac(kGpioUnused);
ac.begin();
@@ -906,6 +907,70 @@ TEST(TestSharpAcClass, SwingToggle) {
ac.setRaw(off_state);
EXPECT_FALSE(ac.getSwingToggle());
// Vertical
ac.setSwingV(kSharpAcSwingVToggle);
EXPECT_EQ(kSharpAcSwingVToggle, ac.getSwingV());
EXPECT_TRUE(ac.getSwingToggle());
ac.setSwingV(kSharpAcSwingVHigh);
EXPECT_EQ(kSharpAcSwingVHigh, ac.getSwingV());
EXPECT_FALSE(ac.getSwingToggle());
ac.setSwingV(0xFF); // Doesn't change if invalid position given.
EXPECT_EQ(kSharpAcSwingVHigh, ac.getSwingV());
ac.setSwingV(kSharpAcSwingVMid);
EXPECT_EQ(kSharpAcSwingVMid, ac.getSwingV());
EXPECT_FALSE(ac.getSwingToggle());
ac.setSwingV(kSharpAcSwingVLow);
EXPECT_EQ(kSharpAcSwingVLow, ac.getSwingV());
EXPECT_FALSE(ac.getSwingToggle());
ac.setSwingV(kSharpAcSwingVIgnore);
EXPECT_EQ(kSharpAcSwingVIgnore, ac.getSwingV());
EXPECT_FALSE(ac.getSwingToggle());
// Lowest/Coanda only works in Heat mode.
ac.setMode(kSharpAcCool);
ac.setSwingV(kSharpAcSwingVLowest);
EXPECT_EQ(kSharpAcSwingVLow, ac.getSwingV());
EXPECT_FALSE(ac.getSwingToggle());
ac.setModel(sharp_ac_remote_model_t::A907); // Model A907 has heat mode.
ac.setMode(kSharpAcHeat);
EXPECT_EQ(kSharpAcHeat, ac.getMode());
ac.setSwingV(kSharpAcSwingVLowest);
EXPECT_EQ(kSharpAcSwingVLowest, ac.getSwingV());
// Check we can force Coanda in Cool mode.
ac.setMode(kSharpAcCool);
ASSERT_EQ(kSharpAcSwingVCoanda, kSharpAcSwingVLowest);
ac.setSwingV(kSharpAcSwingVCoanda, true);
EXPECT_EQ(kSharpAcSwingVCoanda, ac.getSwingV());
EXPECT_FALSE(ac.getSwingToggle());
EXPECT_EQ(kSharpAcCool, ac.getMode());
// Real messages/states
// ref: https://github.com/crankyoldgit/IRremoteESP8266/discussions/1590#discussioncomment-1254748
ac.stateReset();
const uint8_t coanda_heat_on[13] = {
0xAA, 0x5A, 0xCF, 0x10, 0xC8, 0x31, 0x21,
0x0A, 0x0E, 0x80, 0x06, 0xF4, 0x81};
ac.setRaw(coanda_heat_on);
EXPECT_EQ(
"Model: 3 (A903), Power: On, Mode: 1 (Heat), Temp: 23C, Fan: 2 (Auto), "
"Swing(V): 6 (Lowest), Turbo: Off, Ion: On, Light: -, Clean: Off",
ac.toString());
const uint8_t coanda_cool_on[13] = {
0xAA, 0x5A, 0xCF, 0x10, 0xC7, 0x31, 0x22,
0x0A, 0x0E, 0x80, 0x06, 0xF4, 0x41};
ac.setRaw(coanda_cool_on);
EXPECT_EQ(
"Model: 3 (A903), Power: On, Mode: 2 (Cool), Temp: 22C, Fan: 2 (Auto), "
"Swing(V): 6 (Highest), Turbo: Off, Ion: On, Light: -, Clean: Off",
ac.toString());
}
TEST(TestSharpAcClass, Ion) {
@@ -1005,8 +1070,8 @@ TEST(TestSharpAcClass, Timers) {
EXPECT_TRUE(ac.isPowerSpecial());
EXPECT_EQ(
"Model: 3 (A903), "
"Power: -, Mode: 2 (Cool), Temp: 21C, Fan: 7 (High), Turbo: Off, "
"Swing(V) Toggle: Off, Ion: On, Light: -, Clean: Off, Off Timer: 08:30",
"Power: -, Mode: 2 (Cool), Temp: 21C, Fan: 7 (High), Swing(V): 0 (N/A), "
"Turbo: Off, Ion: On, Light: -, Clean: Off, Off Timer: 08:30",
ac.toString());
// ref: https://docs.google.com/spreadsheets/d/1otzVFM5_tegrZ4ROCLgQ_jvJaWCDlZs1vC-YuR1FFXM/edit#gid=0&range=E80
@@ -1020,7 +1085,7 @@ TEST(TestSharpAcClass, Timers) {
EXPECT_TRUE(ac.isPowerSpecial());
EXPECT_EQ(
"Model: 3 (A903), Power: -, Mode: 2 (Cool), Temp: 21C, Fan: 7 (High), "
"Turbo: Off, Swing(V) Toggle: Off, Ion: On, Light: -, Clean: Off, "
"Swing(V): 0 (N/A), Turbo: Off, Ion: On, Light: -, Clean: Off, "
"On Timer: 12:00",
ac.toString());
}
@@ -1058,13 +1123,13 @@ TEST(TestSharpAcClass, Clean) {
EXPECT_TRUE(ac.getClean());
EXPECT_EQ(
"Model: 3 (A903), Power: On, Mode: 3 (Dry), Temp: 15C, Fan: 2 (Auto), "
"Turbo: Off, Swing(V) Toggle: Off, Ion: Off, Light: -, Clean: On",
"Swing(V): 0 (N/A), Turbo: Off, Ion: Off, Light: -, Clean: On",
ac.toString());
ac.setRaw(clean_off_state);
EXPECT_FALSE(ac.getClean());
EXPECT_EQ(
"Model: 3 (A903), Power: On, Mode: 2 (Cool), Temp: 25C, Fan: 7 (High), "
"Turbo: Off, Swing(V) Toggle: Off, Ion: Off, Light: -, Clean: Off",
"Swing(V): 0 (N/A), Turbo: Off, Ion: Off, Light: -, Clean: Off",
ac.toString());
// Try constructing the clean on state.
@@ -1084,13 +1149,13 @@ TEST(TestSharpAcClass, Clean) {
ac.setPower(false);
EXPECT_EQ(
"Model: 1 (A907), Power: Off, Mode: 2 (Cool), Temp: 25C, Fan: 7 (High), "
"Turbo: Off, Swing(V) Toggle: Off, Ion: Off, Econo: -, Clean: Off",
"Swing(V): 0 (N/A), Turbo: Off, Ion: Off, Econo: -, Clean: Off",
ac.toString());
// Clean ON
ac.setClean(true);
EXPECT_EQ(
"Model: 1 (A907), Power: On, Mode: 3 (Dry), Temp: 15C, Fan: 2 (Auto), "
"Turbo: Off, Swing(V) Toggle: Off, Ion: Off, Econo: -, Clean: On",
"Swing(V): 0 (N/A), Turbo: Off, Ion: Off, Econo: -, Clean: On",
ac.toString());
// Clean OFF (state is identical to `off_msg`).
// i.e. It just clears the clean settings & turns off the device.
@@ -1098,25 +1163,25 @@ TEST(TestSharpAcClass, Clean) {
ac.setPower(false, true);
EXPECT_EQ(
"Model: 1 (A907), Power: Off, Mode: 2 (Cool), Temp: 25C, Fan: 7 (High), "
"Turbo: Off, Swing(V) Toggle: Off, Ion: Off, Econo: -, Clean: Off",
"Swing(V): 0 (N/A), Turbo: Off, Ion: Off, Econo: -, Clean: Off",
ac.toString());
// Clean ON
ac.setClean(true);
EXPECT_EQ(
"Model: 1 (A907), Power: On, Mode: 3 (Dry), Temp: 15C, Fan: 2 (Auto), "
"Turbo: Off, Swing(V) Toggle: Off, Ion: Off, Econo: -, Clean: On",
"Swing(V): 0 (N/A), Turbo: Off, Ion: Off, Econo: -, Clean: On",
ac.toString());
// AC OFF
ac.off();
EXPECT_EQ(
"Model: 1 (A907), Power: Off, Mode: 2 (Cool), Temp: 25C, Fan: 7 (High), "
"Turbo: Off, Swing(V) Toggle: Off, Ion: Off, Econo: -, Clean: Off",
"Swing(V): 0 (N/A), Turbo: Off, Ion: Off, Econo: -, Clean: Off",
ac.toString());
// AC ON (Mode Cool, Temp 25, Ion OFF, Fan 7)
ac.on();
EXPECT_EQ(
"Model: 1 (A907), Power: On, Mode: 2 (Cool), Temp: 25C, Fan: 7 (High), "
"Turbo: Off, Swing(V) Toggle: Off, Ion: Off, Econo: -, Clean: Off",
"Swing(V): 0 (N/A), Turbo: Off, Ion: Off, Econo: -, Clean: Off",
ac.toString());
}
@@ -1126,7 +1191,7 @@ TEST(TestSharpAcClass, Issue1309) {
ac.stateReset();
EXPECT_EQ(
"Model: 1 (A907), Power: Off, Mode: 0 (Auto), Temp: 15C, Fan: 0 (UNKNOWN), "
"Turbo: Off, Swing(V) Toggle: Off, Ion: Off, Econo: -, Clean: Off",
"Swing(V): 0 (N/A), Turbo: Off, Ion: Off, Econo: -, Clean: Off",
ac.toString());
const uint8_t issue1309_on[13] = {
@@ -1135,7 +1200,7 @@ TEST(TestSharpAcClass, Issue1309) {
ac.setRaw(issue1309_on);
EXPECT_EQ(
"Model: 2 (A705), Power: On, Mode: 2 (Cool), Temp: 16C, Fan: 2 (Auto), "
"Turbo: Off, Swing(V) Toggle: Off, Ion: Off, Light: -, Clean: Off",
"Swing(V): 0 (N/A), Turbo: Off, Ion: Off, Light: -, Clean: Off",
ac.toString());
EXPECT_STATE_EQ(issue1309_on, ac.getRaw(), kSharpAcBits);
@@ -1147,7 +1212,7 @@ TEST(TestSharpAcClass, Issue1309) {
ac.on();
EXPECT_EQ(
"Model: 2 (A705), Power: On, Mode: 2 (Cool), Temp: 16C, Fan: 2 (Auto), "
"Turbo: Off, Swing(V) Toggle: Off, Ion: Off, Light: -, Clean: Off",
"Swing(V): 0 (N/A), Turbo: Off, Ion: Off, Light: -, Clean: Off",
ac.toString());
}
@@ -1200,13 +1265,13 @@ TEST(TestSharpAcClass, Issue1387Power) {
EXPECT_STATE_EQ(real_off, ac.getRaw(), kSharpAcBits);
EXPECT_EQ(
"Model: 3 (A903), Power: Off, Mode: 2 (Cool), Temp: 27C, Fan: 3 (Low), "
"Turbo: Off, Swing(V) Toggle: Off, Ion: On, Light: -, Clean: Off",
"Swing(V): 0 (N/A), Turbo: Off, Ion: On, Light: -, Clean: Off",
ac.toString());
// Create the same off state.
ac.setPower(true, ac.getPower());
EXPECT_STATE_EQ(real_on, ac.getRaw(), kSharpAcBits);
EXPECT_EQ(
"Model: 3 (A903), Power: On, Mode: 2 (Cool), Temp: 27C, Fan: 3 (Low), "
"Turbo: Off, Swing(V) Toggle: Off, Ion: On, Light: -, Clean: Off",
"Swing(V): 0 (N/A), Turbo: Off, Ion: On, Light: -, Clean: Off",
ac.toString());
}