Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Gree: Support YBOFB models. #815

Merged
merged 6 commits into from
Jul 23, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 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,13 +19,15 @@
| [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>YBOFB | 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)** | YBOFB remote<BR>YBOFB2 remote | YAW1F<BR>YBOFB | 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>YBOFB | 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>YBOFB | 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 | | - |
| [JVC](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_JVC.cpp) | **Unknown** | | | - |
| [Kelvinator](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Kelvinator.cpp) | **[Green](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Kelvinator.h)** | YAPOF3 remote | | Yes |
| [Kelvinator](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Kelvinator.cpp) | **[Kelvinator](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Kelvinator.h)** | KSV26CRC A/C<BR>KSV26HRC A/C<BR>KSV35CRC A/C<BR>KSV35HRC A/C<BR>KSV53HRC A/C<BR>KSV62HRC A/C<BR>KSV70CRC A/C<BR>KSV70HRC A/C<BR>KSV80HRC A/C<BR>YALIF Remote | | Yes |
| [LG](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_LG.cpp) | **[LG](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_LG.h)** | 6711A20083V remote<BR>6711A20083V remote<BR>AKB74395308 remote<BR>AKB74395308 remote | | Yes |
| [Lasertag](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Lasertag.cpp) | **Unknown** | | | - |
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,
turbo, light, 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
73 changes: 51 additions & 22 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::YBOFB;
}
}

void IRGreeAC::checksum(const uint16_t length) {
Expand All @@ -165,33 +176,45 @@ 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;
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::YBOFB:
_model = model; break;
default:
setModel(gree_ac_remote_model_t::YAW1F);
}
}

void IRGreeAC::off(void) {
remote_state[0] &= ~kGreePower1Mask;
remote_state[2] &= ~kGreePower2Mask;
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::YBOFB: 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) {
return (remote_state[0] & kGreePower1Mask) &&
(remote_state[2] & kGreePower2Mask);
// See #814. Not checking/requiring: (remote_state[2] & kGreePower2Mask)
return remote_state[0] & kGreePower1Mask;
}

// Set the temp. in deg C
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::YBOFB: result += F(" (YBOFB)"); break;
default: result += F(" (UNKNOWN)");
}
result += addBoolToString(getPower(), F("Power"));
result += addModeToString(getMode(), kGreeAuto, kGreeCool, kGreeHeat,
kGreeDry, kGreeFan);
result += addTempToString(getTemp());
Expand Down
18 changes: 15 additions & 3 deletions src/ir_Gree.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
// Brand: EKOKAI, Model: A/C
// Brand: RusClimate, Model: EACS/I-09HAR_X/N3 A/C
// Brand: RusClimate, Model: YAW1F remote
// Brand: Green, Model: YBOFB remote
// Brand: Green, Model: YBOFB2 remote

#ifndef IR_GREE_H_
#define IR_GREE_H_
Expand All @@ -22,6 +24,11 @@
#endif

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

const uint8_t kGreeAuto = 0;
const uint8_t kGreeCool = 1;
const uint8_t kGreeDry = 2;
Expand All @@ -37,7 +44,7 @@ const uint8_t kGreeSleepMask = 0b10000000;
// Byte 2
const uint8_t kGreeTurboMask = 0b00010000;
const uint8_t kGreeLightMask = 0b00100000;
const uint8_t kGreePower2Mask = 0b01000000;
const uint8_t kGreePower2Mask = 0b01000000; // This might not be used. See #814
const uint8_t kGreeXfanMask = 0b10000000;
// Byte 4
const uint8_t kGreeSwingPosMask = 0b00001111;
Expand Down Expand Up @@ -87,8 +94,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 @@ -98,6 +107,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 @@ -142,6 +153,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
1 change: 1 addition & 0 deletions src/ir_Kelvinator.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
// Brand: Kelvinator, Model: KSV70CRC A/C
// Brand: Kelvinator, Model: KSV70HRC A/C
// Brand: Kelvinator, Model: KSV80HRC A/C
// Brand: Green, Model: YAPOF3 remote

#ifndef IR_KELVINATOR_H_
#define IR_KELVINATOR_H_
Expand Down
26 changes: 14 additions & 12 deletions test/IRac_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -334,21 +334,23 @@ 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, "
"Swing Vertical Mode: Manual, Swing Vertical Pos: 3";
"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
48 changes: 41 additions & 7 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 @@ -609,3 +609,37 @@ TEST(TestGreeClass, toCommon) {
ASSERT_FALSE(ac.toCommon().beep);
ASSERT_EQ(-1, ac.toCommon().clock);
}

TEST(TestGreeClass, Issue814Power) {
IRGreeAC ac(0);
ac.begin();

// https://github.com/crankyoldgit/IRremoteESP8266/issues/814#issuecomment-511263921
uint8_t YBOFB_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(YBOFB_on);
EXPECT_TRUE(ac.getPower());
EXPECT_EQ(gree_ac_remote_model_t::YBOFB, ac.getModel());
EXPECT_EQ(
"Model: 2 (YBOFB), Power: On, Mode: 1 (COOL), Temp: 23C, Fan: 1 (Low), "
"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(YBOFB_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::YBOFB);
ac.on();
EXPECT_STATE_EQ(YBOFB_on, ac.getRaw(), kGreeBits);
}