Skip to content

Commit

Permalink
Gree: Add differing model support.
Browse files Browse the repository at this point in the history
* Add set/getModel(). e.g. YAW1F (1) [default] & YBOFB2 (2)
* Automatic detection of models only works with messages with power on.
* set/getPower() now changes behaviour with different models.
* Report model type in the .toString() function.
* Update common a/c routines and unit tests.
* Unit test coverage for different power/model operation.

Fixes #814
  • Loading branch information
crankyoldgit committed Jul 22, 2019
1 parent 63d6059 commit ee0a288
Show file tree
Hide file tree
Showing 7 changed files with 112 additions and 50 deletions.
9 changes: 5 additions & 4 deletions SupportedProtocols.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<!--- WARNING: Do NOT edit this file directly.
It is generated by './tools/scrape_supported_devices.py'.
Last generated: Sat Jul 20 14:12:27 2019 --->
Last generated: Mon Jul 22 11:13:01 2019 --->
# IR Protocols supported by this library

| Protocol | Brand | Model | A/C Model | Detailed A/C Support |
Expand All @@ -19,9 +19,10 @@
| [GICable](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_GICable.cpp) | **Unknown** | | | - |
| [GlobalCache](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_GlobalCache.cpp) | **Unknown** | | | - |
| [Goodweather](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Goodweather.cpp) | **[Goodweather](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Goodweather.h)** | ZH/JT-03 remote | | Yes |
| [Gree](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Gree.cpp) | **[EKOKAI](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Gree.h)** | A/C | | Yes |
| [Gree](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Gree.cpp) | **[RusClimate](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Gree.h)** | EACS/I-09HAR_X/N3 A/C<BR>YAW1F remote | | Yes |
| [Gree](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Gree.cpp) | **[Ultimate](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Gree.h)** | Heat Pump | | Yes |
| [Gree](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Gree.cpp) | **[EKOKAI](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Gree.h)** | A/C | YAW1F<BR>YBOFB2 | Yes |
| [Gree](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Gree.cpp) | **[Green](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Gree.h)** | YBOFB2 remote | YAW1F<BR>YBOFB2 | Yes |
| [Gree](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Gree.cpp) | **[RusClimate](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Gree.h)** | EACS/I-09HAR_X/N3 A/C<BR>YAW1F remote | YAW1F<BR>YBOFB2 | Yes |
| [Gree](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Gree.cpp) | **[Ultimate](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Gree.h)** | Heat Pump | YAW1F<BR>YBOFB2 | Yes |
| [Haier](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Haier.cpp) | **[Haier](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Haier.h)** | HSU-09HMC203 A/C<BR>HSU07-HEA03 remote<BR>YR-W02 remote | | Yes |
| [Hitachi](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Hitachi.cpp) | **[Hitachi](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Hitachi.h)** | LT0541-HTA remote<BR>RAS-35THA6 remote<BR>Series VI A/C (Circa 2007) | | Yes |
| [Inax](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Inax.cpp) | **Lixil** | Inax DT-BA283 Toilet | | - |
Expand Down
8 changes: 5 additions & 3 deletions src/IRac.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -408,11 +408,12 @@ void IRac::goodweather(IRGoodweatherAc *ac,
#endif // SEND_GOODWEATHER

#if SEND_GREE
void IRac::gree(IRGreeAC *ac,
void IRac::gree(IRGreeAC *ac, const gree_ac_remote_model_t model,
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 bool clean,
const int16_t sleep) {
ac->setModel(model);
ac->setPower(on);
ac->setMode(ac->convertMode(mode));
ac->setTemp(degrees);
Expand Down Expand Up @@ -1061,9 +1062,10 @@ bool IRac::sendAc(const decode_type_t vendor, const int16_t model,
#if SEND_GREE
case GREE:
{
IRGreeAC ac(_pin, _inverted, _modulation);
IRGreeAC ac(_pin, (gree_ac_remote_model_t)model, _inverted, _modulation);
ac.begin();
gree(&ac, on, mode, degC, fan, swingv, light, turbo, clean, sleep);
gree(&ac, (gree_ac_remote_model_t)model, on, mode, degC, fan, swingv,
light, turbo, clean, sleep);
break;
}
#endif // SEND_GREE
Expand Down
2 changes: 1 addition & 1 deletion src/IRac.h
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ void electra(IRElectraAc *ac,
const int16_t sleep = -1);
#endif // SEND_GOODWEATHER
#if SEND_GREE
void gree(IRGreeAC *ac,
void gree(IRGreeAC *ac, const gree_ac_remote_model_t model,
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 bool clean,
Expand Down
69 changes: 49 additions & 20 deletions src/ir_Gree.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -113,9 +113,12 @@ void IRsend::sendGree(const uint64_t data, const uint16_t nbits,
}
#endif // SEND_GREE

IRGreeAC::IRGreeAC(const uint16_t pin, const bool inverted,
const bool use_modulation)
: _irsend(pin, inverted, use_modulation) { stateReset(); }
IRGreeAC::IRGreeAC(const uint16_t pin, const gree_ac_remote_model_t model,
const bool inverted, const bool use_modulation)
: _irsend(pin, inverted, use_modulation) {
stateReset();
setModel(model);
}

void IRGreeAC::stateReset(void) {
// This resets to a known-good state to Power Off, Fan Auto, Mode Auto, 25C.
Expand All @@ -128,6 +131,7 @@ void IRGreeAC::stateReset(void) {
}

void IRGreeAC::fixup(void) {
setPower(getPower()); // Redo the power bits as they differ between models.
checksum(); // Calculate the checksums
}

Expand All @@ -149,6 +153,13 @@ void IRGreeAC::setRaw(const uint8_t new_code[]) {
for (uint8_t i = 0; i < kGreeStateLength; i++) {
remote_state[i] = new_code[i];
}
// We can only detect the difference between models when the power is on.
if (getPower()) {
if (remote_state[2] & kGreePower2Mask)
_model = gree_ac_remote_model_t::YAW1F;
else
_model = gree_ac_remote_model_t::YBOFB2;
}
}

void IRGreeAC::checksum(const uint16_t length) {
Expand All @@ -165,28 +176,40 @@ void IRGreeAC::checksum(const uint16_t length) {
// A boolean.
bool IRGreeAC::validChecksum(const uint8_t state[], const uint16_t length) {
// Top 4 bits of the last byte in the state is the state's checksum.
if (state[length - 1] >> 4 ==
IRKelvinatorAC::calcBlockChecksum(state, length))
return true;
else
return false;
return (state[length - 1] >> 4 == IRKelvinatorAC::calcBlockChecksum(state,
length));
}

void IRGreeAC::on(void) {
remote_state[0] |= kGreePower1Mask;
remote_state[2] |= kGreePower2Mask; // May not be needed. See #814
void IRGreeAC::setModel(const gree_ac_remote_model_t model) {
switch (model) {
case gree_ac_remote_model_t::YAW1F:
case gree_ac_remote_model_t::YBOFB2:
_model = model; break;
default:
setModel(gree_ac_remote_model_t::YAW1F);
}
}

void IRGreeAC::off(void) {
remote_state[0] &= ~kGreePower1Mask;
remote_state[2] &= ~kGreePower2Mask; // May not be needed. See #814
gree_ac_remote_model_t IRGreeAC::getModel(void) {
return _model;
}

void IRGreeAC::on(void) { setPower(true); }

void IRGreeAC::off(void) { setPower(false); }

void IRGreeAC::setPower(const bool on) {
if (on)
this->on();
else
this->off();
if (on) {
remote_state[0] |= kGreePower1Mask;
switch (_model) {
case gree_ac_remote_model_t::YBOFB2: break;
default:
remote_state[2] |= kGreePower2Mask;
}
} else {
remote_state[0] &= ~kGreePower1Mask;
remote_state[2] &= ~kGreePower2Mask; // May not be needed. See #814
}
}

bool IRGreeAC::getPower(void) {
Expand Down Expand Up @@ -424,7 +447,7 @@ stdAc::swingv_t IRGreeAC::toCommonSwingV(const uint8_t pos) {
stdAc::state_t IRGreeAC::toCommon(void) {
stdAc::state_t result;
result.protocol = decode_type_t::GREE;
result.model = -1; // No models used.
result.model = this->getModel();
result.power = this->getPower();
result.mode = this->toCommonMode(this->getMode());
result.celsius = true;
Expand Down Expand Up @@ -452,7 +475,13 @@ stdAc::state_t IRGreeAC::toCommon(void) {
String IRGreeAC::toString(void) {
String result = "";
result.reserve(150); // Reserve some heap for the string to reduce fragging.
result += addBoolToString(getPower(), F("Power"), false);
result += addIntToString(getModel(), F("Model"), false);
switch (getModel()) {
case gree_ac_remote_model_t::YAW1F: result += F(" (YAW1F)"); break;
case gree_ac_remote_model_t::YBOFB2: result += F(" (YBOFB2)"); break;
default: result += F(" (UNKNOWN)");
}
result += addBoolToString(getPower(), F("Power"));
result += addModeToString(getMode(), kGreeAuto, kGreeCool, kGreeHeat,
kGreeDry, kGreeFan);
result += addTempToString(getTemp());
Expand Down
14 changes: 12 additions & 2 deletions src/ir_Gree.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@
#endif

// Constants
enum gree_ac_remote_model_t {
YAW1F = 1, // (1) Ultimate, EKOKAI, RusClimate (Default)
YBOFB2, // (2) Green
};

const uint8_t kGreeAuto = 0;
const uint8_t kGreeCool = 1;
const uint8_t kGreeDry = 2;
Expand Down Expand Up @@ -88,8 +93,10 @@ const uint8_t kGreeSwingUpAuto = 0b00001011;
// Classes
class IRGreeAC {
public:
explicit IRGreeAC(const uint16_t pin, const bool inverted = false,
const bool use_modulation = true);
explicit IRGreeAC(
const uint16_t pin,
const gree_ac_remote_model_t model = gree_ac_remote_model_t::YAW1F,
const bool inverted = false, const bool use_modulation = true);

void stateReset(void);
#if SEND_GREE
Expand All @@ -99,6 +106,8 @@ class IRGreeAC {
void begin(void);
void on(void);
void off(void);
void setModel(const gree_ac_remote_model_t model);
gree_ac_remote_model_t getModel(void);
void setPower(const bool on);
bool getPower(void);
void setTemp(const uint8_t temp);
Expand Down Expand Up @@ -143,6 +152,7 @@ class IRGreeAC {
#endif // UNIT_TEST
// The state of the IR remote in IR code form.
uint8_t remote_state[kGreeStateLength];
gree_ac_remote_model_t _model;
void checksum(const uint16_t length = kGreeStateLength);
void fixup(void);
};
Expand Down
23 changes: 12 additions & 11 deletions test/IRac_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -334,21 +334,22 @@ TEST(TestIRac, Gree) {
IRac irac(0);
IRrecv capture(0);
char expected[] =
"Power: On, Mode: 1 (COOL), Temp: 22C, Fan: 2 (Medium), Turbo: Off, "
"IFeel: Off, WiFi: Off, XFan: On, Light: On, Sleep: On, "
"Model: 1 (YAW1F),Power: On, Mode: 1 (COOL), Temp: 22C, Fan: 2 (Medium), "
"Turbo: Off, IFeel: Off, WiFi: Off, XFan: On, Light: On, Sleep: On, "
"Swing Vertical Mode: Manual, Swing Vertical Pos: 3";

ac.begin();
irac.gree(&ac,
true, // Power
stdAc::opmode_t::kCool, // Mode
22, // Celsius
stdAc::fanspeed_t::kMedium, // Fan speed
stdAc::swingv_t::kHigh, // Veritcal swing
false, // Turbo
true, // Light
true, // Clean (aka Mold/XFan)
8 * 60 + 0); // Sleep time
gree_ac_remote_model_t::YAW1F, // Model
true, // Power
stdAc::opmode_t::kCool, // Mode
22, // Celsius
stdAc::fanspeed_t::kMedium, // Fan speed
stdAc::swingv_t::kHigh, // Veritcal swing
false, // Turbo
true, // Light
true, // Clean (aka Mold/XFan)
8 * 60 + 0); // Sleep time
ASSERT_EQ(expected, ac.toString());
ac._irsend.makeDecodeResult();
EXPECT_TRUE(capture.decode(&ac._irsend.capture));
Expand Down
37 changes: 28 additions & 9 deletions test/ir_Gree_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -495,8 +495,8 @@ TEST(TestGreeClass, HumanReadable) {
IRGreeAC irgree(0);

EXPECT_EQ(
"Power: Off, Mode: 0 (AUTO), Temp: 25C, Fan: 0 (Auto), Turbo: Off, "
"IFeel: Off, WiFi: Off, XFan: Off, Light: On, Sleep: Off, "
"Model: 1 (YAW1F), Power: Off, Mode: 0 (AUTO), Temp: 25C, Fan: 0 (Auto), "
"Turbo: Off, IFeel: Off, WiFi: Off, XFan: Off, Light: On, Sleep: Off, "
"Swing Vertical Mode: Manual, Swing Vertical Pos: 0 (Last Pos)",
irgree.toString());
irgree.on();
Expand All @@ -511,8 +511,8 @@ TEST(TestGreeClass, HumanReadable) {
irgree.setWiFi(true);
irgree.setSwingVertical(true, kGreeSwingAuto);
EXPECT_EQ(
"Power: On, Mode: 1 (COOL), Temp: 16C, Fan: 3 (High), Turbo: On, "
"IFeel: On, WiFi: On, XFan: On, Light: Off, Sleep: On, "
"Model: 1 (YAW1F), Power: On, Mode: 1 (COOL), Temp: 16C, Fan: 3 (High), "
"Turbo: On, IFeel: On, WiFi: On, XFan: On, Light: Off, Sleep: On, "
"Swing Vertical Mode: Auto, Swing Vertical Pos: 1 (Auto)",
irgree.toString());
}
Expand Down Expand Up @@ -571,8 +571,8 @@ TEST(TestDecodeGree, NormalRealExample) {
EXPECT_STATE_EQ(gree_code, irsend.capture.state, kGreeBits);
irgree.setRaw(irsend.capture.state);
EXPECT_EQ(
"Power: On, Mode: 1 (COOL), Temp: 26C, Fan: 1 (Low), Turbo: Off, "
"IFeel: Off, WiFi: Off, XFan: Off, Light: On, Sleep: Off, "
"Model: 1 (YAW1F), Power: On, Mode: 1 (COOL), Temp: 26C, Fan: 1 (Low), "
"Turbo: Off, IFeel: Off, WiFi: Off, XFan: Off, Light: On, Sleep: Off, "
"Swing Vertical Mode: Manual, Swing Vertical Pos: 2",
irgree.toString());
}
Expand All @@ -590,7 +590,7 @@ TEST(TestGreeClass, toCommon) {
ac.setSleep(true);
// Now test it.
ASSERT_EQ(decode_type_t::GREE, ac.toCommon().protocol);
ASSERT_EQ(-1, ac.toCommon().model);
ASSERT_EQ(gree_ac_remote_model_t::YAW1F, ac.toCommon().model);
ASSERT_TRUE(ac.toCommon().power);
ASSERT_TRUE(ac.toCommon().celsius);
ASSERT_EQ(20, ac.toCommon().degrees);
Expand All @@ -615,12 +615,31 @@ TEST(TestGreeClass, Issue814Power) {
ac.begin();

// https://github.com/crankyoldgit/IRremoteESP8266/issues/814#issuecomment-511263921
uint8_t on[8] = {0x59, 0x07, 0x20, 0x50, 0x01, 0x20, 0x00, 0xC0};
uint8_t YBOFB2_on[8] = {0x59, 0x07, 0x20, 0x50, 0x01, 0x20, 0x00, 0xC0};
uint8_t off[8] = {0x51, 0x07, 0x20, 0x50, 0x01, 0x20, 0x00, 0x40};

ac.on();
EXPECT_EQ(gree_ac_remote_model_t::YAW1F, ac.getModel());
ac.setRaw(off);
EXPECT_FALSE(ac.getPower());
ac.setRaw(on);
ac.setRaw(YBOFB2_on);
EXPECT_TRUE(ac.getPower());
EXPECT_EQ(gree_ac_remote_model_t::YBOFB2, ac.getModel());
EXPECT_EQ(
"Model: 2 (YBOFB2), Power: On, Mode: 1 (COOL), Temp: 23C, Fan: 1, "
"Turbo: Off, IFeel: Off, WiFi: Off, XFan: Off, Light: On, Sleep: Off, "
"Swing Vertical Mode: Auto, Swing Vertical Pos: 1 (Auto)",
ac.toString());
ac.off();
EXPECT_STATE_EQ(off, ac.getRaw(), kGreeBits);
ac.on();
EXPECT_STATE_EQ(YBOFB2_on, ac.getRaw(), kGreeBits);
uint8_t YAW1F_on[8] = {0x59, 0x07, 0x60, 0x50, 0x01, 0x20, 0x00, 0xC0};
ac.setModel(gree_ac_remote_model_t::YAW1F);
EXPECT_STATE_EQ(YAW1F_on, ac.getRaw(), kGreeBits);
ac.off();
EXPECT_STATE_EQ(off, ac.getRaw(), kGreeBits);
ac.setModel(gree_ac_remote_model_t::YBOFB2);
ac.on();
EXPECT_STATE_EQ(YBOFB2_on, ac.getRaw(), kGreeBits);
}

0 comments on commit ee0a288

Please sign in to comment.