[HAIER_AC176/HAIER_AC_YRW02] Add support degree Fahrenheit (#1659)

* Add support degree Fahrenheit
* Update IRac
* Add button `Celsius/Fahrenheit`
* Update related tests
This commit is contained in:
Ptilopsis Leucotis
2021-11-04 12:13:18 +03:00
committed by GitHub
parent 0f3fa4de0a
commit 21f26e0483
6 changed files with 185 additions and 47 deletions

View File

@@ -1112,6 +1112,7 @@ void IRac::haier(IRHaierAC *ac,
/// @param[in, out] ac A Ptr to an IRHaierAC176 object to use.
/// @param[in] on The power setting.
/// @param[in] mode The operation mode setting.
/// @param[in] celsius Temperature units. True is Celsius, False is Fahrenheit.
/// @param[in] degrees The temperature setting in degrees.
/// @param[in] fan The speed setting for the fan.
/// @param[in] swingv The vertical swing setting.
@@ -1122,12 +1123,15 @@ void IRac::haier(IRHaierAC *ac,
/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on.
void IRac::haier176(IRHaierAC176 *ac,
const bool on, const stdAc::opmode_t mode,
const float degrees, const stdAc::fanspeed_t fan,
const stdAc::swingv_t swingv, const stdAc::swingh_t swingh,
const bool celsius, const float degrees,
const stdAc::fanspeed_t fan,
const stdAc::swingv_t swingv,
const stdAc::swingh_t swingh,
const bool turbo, const bool quiet, const bool filter,
const int16_t sleep) {
ac->begin();
ac->setMode(ac->convertMode(mode));
ac->setUseFahrenheit(!celsius);
ac->setTemp(degrees);
ac->setFan(ac->convertFan(fan));
ac->setSwingV(ac->convertSwingV(swingv));
@@ -1149,6 +1153,7 @@ void IRac::haier176(IRHaierAC176 *ac,
/// @param[in, out] ac A Ptr to an IRHaierACYRW02 object to use.
/// @param[in] on The power setting.
/// @param[in] mode The operation mode setting.
/// @param[in] celsius Temperature units. True is Celsius, False is Fahrenheit.
/// @param[in] degrees The temperature setting in degrees.
/// @param[in] fan The speed setting for the fan.
/// @param[in] swingv The vertical swing setting.
@@ -1159,13 +1164,15 @@ void IRac::haier176(IRHaierAC176 *ac,
/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on.
void IRac::haierYrwo2(IRHaierACYRW02 *ac,
const bool on, const stdAc::opmode_t mode,
const float degrees, const stdAc::fanspeed_t fan,
const bool celsius, const float degrees,
const stdAc::fanspeed_t fan,
const stdAc::swingv_t swingv,
const stdAc::swingh_t swingh,
const bool turbo, const bool quiet, const bool filter,
const int16_t sleep) {
ac->begin();
ac->setMode(ac->convertMode(mode));
ac->setUseFahrenheit(!celsius);
ac->setTemp(degrees);
ac->setFan(ac->convertFan(fan));
ac->setSwingV(ac->convertSwingV(swingv));
@@ -2797,8 +2804,9 @@ bool IRac::sendAc(const stdAc::state_t desired, const stdAc::state_t *prev) {
case HAIER_AC176:
{
IRHaierAC176 ac(_pin, _inverted, _modulation);
haier176(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv,
send.swingh, send.turbo, send.filter, send.sleep);
haier176(&ac, send.power, send.mode, send.celsius, send.degrees,
send.fanspeed, send.swingv, send.swingh, send.turbo,
send.filter, send.sleep);
break;
}
#endif // SEND_HAIER_AC176
@@ -2806,8 +2814,9 @@ bool IRac::sendAc(const stdAc::state_t desired, const stdAc::state_t *prev) {
case HAIER_AC_YRW02:
{
IRHaierACYRW02 ac(_pin, _inverted, _modulation);
haierYrwo2(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv,
send.swingh, send.turbo, send.filter, send.sleep);
haierYrwo2(&ac, send.power, send.mode, send.celsius, send.degrees,
send.fanspeed, send.swingv, send.swingh, send.turbo,
send.filter, send.sleep);
break;
}
#endif // SEND_HAIER_AC_YRW02

View File

@@ -261,17 +261,19 @@ void electra(IRElectraAc *ac,
#if SEND_HAIER_AC176
void haier176(IRHaierAC176 *ac,
const bool on, const stdAc::opmode_t mode,
const float degrees, const stdAc::fanspeed_t fan,
const stdAc::swingv_t swingv, const stdAc::swingh_t swingh,
const bool turbo, const bool quiet, const bool filter,
const bool celsius, const float degrees,
const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv,
const stdAc::swingh_t swingh, const bool turbo,
const bool quiet, const bool filter,
const int16_t sleep = -1);
#endif // SEND_HAIER_AC176
#if SEND_HAIER_AC_YRW02
void haierYrwo2(IRHaierACYRW02 *ac,
const bool on, const stdAc::opmode_t mode,
const float degrees, const stdAc::fanspeed_t fan,
const stdAc::swingv_t swingv, const stdAc::swingh_t swingh,
const bool turbo, const bool quiet, const bool filter,
const bool celsius, const float degrees,
const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv,
const stdAc::swingh_t swingh, const bool turbo,
const bool quiet, const bool filter,
const int16_t sleep = -1);
#endif // SEND_HAIER_AC_YRW02
#if SEND_HITACHI_AC

View File

@@ -584,7 +584,7 @@ void IRHaierAC176::stateReset(void) {
std::memset(_.raw, 0, sizeof _.raw);
_.Prefix = kHaierAcYrw02Prefix;
_.Prefix2 = kHaierAc176Prefix;
_.Temp = kHaierAcDefTemp - kHaierAcMinTemp;
_.Temp = kHaierAcYrw02DefTempC - kHaierAcYrw02MinTempC;
_.Health = true;
setFan(kHaierAcYrw02FanAuto);
_.Power = true;
@@ -619,6 +619,7 @@ void IRHaierAC176::setButton(uint8_t button) {
case kHaierAcYrw02ButtonTurbo:
case kHaierAcYrw02ButtonSleep:
case kHaierAcYrw02ButtonLock:
case kHaierAcYrw02ButtonCF:
_.Button = button;
}
}
@@ -654,27 +655,96 @@ void IRHaierAC176::setMode(uint8_t mode) {
/// @return The current operating mode setting.
uint8_t IRHaierAC176::getMode(void) const { return _.Mode; }
/// Set the temperature.
/// @param[in] celsius The temperature in degrees celsius.
void IRHaierAC176::setTemp(const uint8_t celsius) {
uint8_t temp = celsius;
if (temp < kHaierAcMinTemp)
temp = kHaierAcMinTemp;
else if (temp > kHaierAcMaxTemp)
temp = kHaierAcMaxTemp;
/// Set the default temperature units to use.
/// @param[in] on Use Fahrenheit as the units.
/// true is Fahrenheit, false is Celsius.
void IRHaierAC176::setUseFahrenheit(const bool on) { _.UseFahrenheit = on; }
/// Get the default temperature units in use.
/// @return true is Fahrenheit, false is Celsius.
bool IRHaierAC176::getUseFahrenheit(void) const { return _.UseFahrenheit; }
/// Set the temperature.
/// @param[in] degree The temperature in degrees.
/// @param[in] fahrenheit Use units of Fahrenheit and set that as units used.
void IRHaierAC176::setTemp(const uint8_t degree, const bool fahrenheit) {
uint8_t old_temp = getTemp();
if (old_temp == temp) return;
if (old_temp > temp)
_.Button = kHaierAcYrw02ButtonTempDown;
else
_.Button = kHaierAcYrw02ButtonTempUp;
_.Temp = temp - kHaierAcMinTemp;
if (old_temp == degree) return;
if (_.UseFahrenheit == fahrenheit) {
if (old_temp > degree)
_.Button = kHaierAcYrw02ButtonTempDown;
else
_.Button = kHaierAcYrw02ButtonTempUp;
} else {
_.Button = kHaierAcYrw02ButtonCF;
}
_.UseFahrenheit = fahrenheit;
uint8_t temp = degree;
if (fahrenheit) {
if (temp < kHaierAcYrw02MinTempF)
temp = kHaierAcYrw02MinTempF;
else if (temp > kHaierAcYrw02MaxTempF)
temp = kHaierAcYrw02MaxTempF;
if (degree >= 77) { temp++; }
if (degree >= 79) { temp++; }
// See at IRHaierAC176::getTemp() comments for clarification
_.ExtraDegreeF = temp % 2;
_.Temp = (temp - kHaierAcYrw02MinTempF -_.ExtraDegreeF) >> 1;
} else {
if (temp < kHaierAcYrw02MinTempC)
temp = kHaierAcYrw02MinTempC;
else if (temp > kHaierAcYrw02MaxTempC)
temp = kHaierAcYrw02MaxTempC;
_.Temp = temp - kHaierAcYrw02MinTempC;
}
}
/// Get the current temperature setting.
/// @return The current setting for temp. in degrees celsius.
uint8_t IRHaierAC176::getTemp(void) const { return _.Temp + kHaierAcMinTemp; }
uint8_t IRHaierAC176::getTemp(void) const {
if (!_.UseFahrenheit) { return _.Temp + kHaierAcYrw02MinTempC; }
uint8_t degree = _.Temp*2 + kHaierAcYrw02MinTempF + _.ExtraDegreeF;
// The way of coding the temperature in degree Fahrenheit is
// kHaierAcYrw02MinTempF + Temp*2 + ExtraDegreeF, for example
// Temp = 0b0011, ExtraDegreeF = 0b1, temperature is 60 + 3*2 + 1 = 67F
// But around 78F there is unconsistency, see table below
//
// | Fahrenheit | Temp | ExtraDegreeF |
// | 60F | 0b0000 | 0b0 |
// | 61F | 0b0000 | 0b1 |
// | 62F | 0b0001 | 0b0 |
// | 63F | 0b0001 | 0b1 |
// | 64F | 0b0010 | 0b0 |
// | 65F | 0b0010 | 0b1 |
// | 66F | 0b0011 | 0b0 |
// | 67F | 0b0011 | 0b1 |
// | 68F | 0b0100 | 0b0 |
// | 69F | 0b0100 | 0b1 |
// | 70F | 0b0101 | 0b0 |
// | 71F | 0b0101 | 0b1 |
// | 72F | 0b0110 | 0b0 |
// | 73F | 0b0110 | 0b1 |
// | 74F | 0b0111 | 0b0 |
// | 75F | 0b0111 | 0b1 |
// | 76F | 0b1000 | 0b0 |
// | Not Used | 0b1000 | 0b1 |
// | 77F | 0b1001 | 0b0 |
// | Not Used | 0b1001 | 0b1 |
// | 78F | 0b1010 | 0b0 |
// | 79F | 0b1010 | 0b1 |
// | 80F | 0b1011 | 0b0 |
// | 81F | 0b1011 | 0b1 |
// | 82F | 0b1100 | 0b0 |
// | 83F | 0b1100 | 0b1 |
// | 84F | 0b1101 | 0b0 |
// | 86F | 0b1110 | 0b0 |
// | 85F | 0b1101 | 0b1 |
if (degree >= 77) { degree--; }
if (degree >= 79) { degree--; }
return degree;
}
/// Set the Health (filter) setting of the A/C.
/// @param[in] on true, the setting is on. false, the setting is off.
@@ -1038,7 +1108,7 @@ stdAc::state_t IRHaierAC176::toCommon(void) const {
result.model = -1; // No models used.
result.power = _.Power;
result.mode = toCommonMode(_.Mode);
result.celsius = true;
result.celsius = !_.UseFahrenheit;
result.degrees = getTemp();
result.fanspeed = toCommonFanSpeed(_.Fan);
result.swingv = toCommonSwingV(_.SwingV);
@@ -1102,6 +1172,9 @@ String IRHaierAC176::toString(void) const {
case kHaierAcYrw02ButtonLock:
result += kLockStr;
break;
case kHaierAcYrw02ButtonCF:
result += kCelsiusFahrenheitStr;
break;
default:
result += kUnknownStr;
}
@@ -1109,7 +1182,7 @@ String IRHaierAC176::toString(void) const {
result += addModeToString(_.Mode, kHaierAcYrw02Auto, kHaierAcYrw02Cool,
kHaierAcYrw02Heat, kHaierAcYrw02Dry,
kHaierAcYrw02Fan);
result += addTempToString(getTemp());
result += addTempToString(getTemp(), !_.UseFahrenheit);
result += addFanToString(_.Fan, kHaierAcYrw02FanHigh, kHaierAcYrw02FanLow,
kHaierAcYrw02FanAuto, kHaierAcYrw02FanAuto,
kHaierAcYrw02FanMed);

View File

@@ -133,6 +133,12 @@ const uint8_t kHaierAcSleepBit = 0b01000000;
#define HAIER_AC_FAN_MED kHaierAcFanMed
#define HAIER_AC_FAN_HIGH kHaierAcFanHigh
const uint8_t kHaierAcYrw02MinTempC = 16;
const uint8_t kHaierAcYrw02MaxTempC = 30;
const uint8_t kHaierAcYrw02MinTempF = 60;
const uint8_t kHaierAcYrw02MaxTempF = 86;
const uint8_t kHaierAcYrw02DefTempC = 25;
const uint8_t kHaierAcYrw02Prefix = 0xA6;
const uint8_t kHaierAc176Prefix = 0xB7;
@@ -173,6 +179,7 @@ const uint8_t kHaierAcYrw02ButtonTurbo = 0b01000;
const uint8_t kHaierAcYrw02ButtonSleep = 0b01011;
const uint8_t kHaierAcYrw02ButtonTimer = 0b10000;
const uint8_t kHaierAcYrw02ButtonLock = 0b10100;
const uint8_t kHaierAcYrw02ButtonCF = 0b11010;
const uint8_t kHaierAcYrw02NoTimers = 0b000;
const uint8_t kHaierAcYrw02OffTimer = 0b001;
@@ -218,7 +225,10 @@ union HaierAc176Protocol{
// Byte 9
uint8_t :8;
// Byte 10
uint8_t :8;
uint8_t ExtraDegreeF :1;
uint8_t :4;
uint8_t UseFahrenheit:1;
uint8_t :2;
// Byte 11
uint8_t :8;
// Byte 12
@@ -365,7 +375,9 @@ class IRHaierAC176 {
void setButton(const uint8_t button);
uint8_t getButton(void) const;
void setTemp(const uint8_t temp);
void setUseFahrenheit(const bool on);
bool getUseFahrenheit(void) const;
void setTemp(const uint8_t temp, const bool fahrenheit = false);
uint8_t getTemp(void) const;
void setFan(const uint8_t speed);

View File

@@ -798,7 +798,8 @@ TEST(TestIRac, Haier176) {
irac.haier176(&ac,
true, // Power
stdAc::opmode_t::kCool, // Mode
23, // Celsius
true, // Celsius
23, // Degrees
stdAc::fanspeed_t::kMedium, // Fan speed
stdAc::swingv_t::kHigh, // Vertical swing
stdAc::swingh_t::kOff, // Horizontal swing
@@ -830,7 +831,8 @@ TEST(TestIRac, HaierYrwo2) {
irac.haierYrwo2(&ac,
true, // Power
stdAc::opmode_t::kCool, // Mode
23, // Celsius
true, // Celsius
23, // Degrees
stdAc::fanspeed_t::kMedium, // Fan speed
stdAc::swingv_t::kHigh, // Vertical swing
stdAc::swingh_t::kAuto, // Vertical swing

View File

@@ -476,24 +476,24 @@ TEST(TestHaierACYRW02Class, Temperature) {
IRHaierACYRW02 ac(kGpioUnused);
ac.begin();
ac.setTemp(kHaierAcMinTemp);
EXPECT_EQ(kHaierAcMinTemp, ac.getTemp());
ac.setTemp(kHaierAcYrw02MinTempC);
EXPECT_EQ(kHaierAcYrw02MinTempC, ac.getTemp());
ac.setButton(kHaierAcYrw02ButtonPower);
ac.setTemp(kHaierAcMinTemp + 1);
EXPECT_EQ(kHaierAcMinTemp + 1, ac.getTemp());
ac.setTemp(kHaierAcYrw02MinTempC + 1);
EXPECT_EQ(kHaierAcYrw02MinTempC + 1, ac.getTemp());
EXPECT_EQ(kHaierAcYrw02ButtonTempUp, ac.getButton());
ac.setTemp(kHaierAcMaxTemp);
EXPECT_EQ(kHaierAcMaxTemp, ac.getTemp());
ac.setTemp(kHaierAcYrw02MaxTempC);
EXPECT_EQ(kHaierAcYrw02MaxTempC, ac.getTemp());
EXPECT_EQ(kHaierAcYrw02ButtonTempUp, ac.getButton());
ac.setTemp(kHaierAcMinTemp - 1);
EXPECT_EQ(kHaierAcMinTemp, ac.getTemp());
ac.setTemp(kHaierAcYrw02MinTempC - 1);
EXPECT_EQ(kHaierAcYrw02MinTempC, ac.getTemp());
EXPECT_EQ(kHaierAcYrw02ButtonTempDown, ac.getButton());
ac.setTemp(kHaierAcMaxTemp + 1);
EXPECT_EQ(kHaierAcMaxTemp, ac.getTemp());
ac.setTemp(kHaierAcYrw02MaxTempC + 1);
EXPECT_EQ(kHaierAcYrw02MaxTempC, ac.getTemp());
EXPECT_EQ(kHaierAcYrw02ButtonTempUp, ac.getButton());
ac.setTemp(23);
@@ -504,8 +504,47 @@ TEST(TestHaierACYRW02Class, Temperature) {
EXPECT_EQ(23, ac.getTemp());
EXPECT_EQ(kHaierAcYrw02ButtonPower, ac.getButton());
ac.setTemp(kHaierAcYrw02MinTempF, true);
EXPECT_EQ(kHaierAcYrw02MinTempF, ac.getTemp());
ac.setButton(kHaierAcYrw02ButtonPower);
ac.setTemp(kHaierAcYrw02MinTempF + 1, true);
EXPECT_EQ(kHaierAcYrw02MinTempF + 1, ac.getTemp());
EXPECT_EQ(kHaierAcYrw02ButtonTempUp, ac.getButton());
ac.setTemp(kHaierAcYrw02MaxTempF, true);
EXPECT_EQ(kHaierAcYrw02MaxTempF, ac.getTemp());
EXPECT_EQ(kHaierAcYrw02ButtonTempUp, ac.getButton());
ac.setTemp(kHaierAcYrw02MinTempF - 1, true);
EXPECT_EQ(kHaierAcYrw02MinTempF, ac.getTemp());
EXPECT_EQ(kHaierAcYrw02ButtonTempDown, ac.getButton());
ac.setTemp(kHaierAcYrw02MaxTempF + 1, true);
EXPECT_EQ(kHaierAcYrw02MaxTempF, ac.getTemp());
EXPECT_EQ(kHaierAcYrw02ButtonTempUp, ac.getButton());
ac.setTemp(66, true);
EXPECT_EQ(66, ac.getTemp());
EXPECT_EQ(kHaierAcYrw02ButtonTempDown, ac.getButton());
ac.setButton(kHaierAcYrw02ButtonPower);
ac.setTemp(66, true);
EXPECT_EQ(66, ac.getTemp());
EXPECT_EQ(kHaierAcYrw02ButtonPower, ac.getButton());
// Test specific cases for converting to Fahrenheit
ac.setTemp(76, true);
EXPECT_EQ(76, ac.getTemp());
ac.setTemp(77, true);
EXPECT_EQ(77, ac.getTemp());
ac.setTemp(78, true);
EXPECT_EQ(78, ac.getTemp());
ac.setTemp(24);
EXPECT_EQ(kHaierAcYrw02ButtonCF, ac.getButton());
ac.setTemp(0);
EXPECT_EQ(kHaierAcMinTemp, ac.getTemp());
EXPECT_EQ(kHaierAcYrw02MinTempC, ac.getTemp());
EXPECT_EQ(kHaierAcYrw02ButtonTempDown, ac.getButton());
ac.setTemp(255);
@@ -760,12 +799,13 @@ TEST(TestHaierACYRW02Class, MessageConstuction) {
"Timer Mode: 0 (N/A), On Timer: Off, Off Timer: Off, Lock: Off",
ac.toString());
ac.setTemp(75, true);
ac.setSwingV(kHaierAcYrw02SwingVMiddle);
ac.setHealth(false);
ac.setSleep(true);
ac.setTurbo(true);
EXPECT_EQ(
"Power: On, Button: 8 (Turbo), Mode: 1 (Cool), Temp: 21C, "
"Power: On, Button: 8 (Turbo), Mode: 1 (Cool), Temp: 75F, "
"Fan: 1 (High), Turbo: On, Quiet: Off, Swing(V): 2 (Middle), "
"Swing(H): 0 (Middle), Sleep: On, Health: Off, "
"Timer Mode: 0 (N/A), On Timer: Off, Off Timer: Off, Lock: Off",