From 5a68487d7ce4bf25c705db0e8254551a9f3a6e40 Mon Sep 17 00:00:00 2001 From: crankyoldgit Date: Tue, 26 Oct 2021 19:04:50 +1000 Subject: [PATCH 1/2] [Gree] Add SwingH control. * Add `(set|get)SwingHorizontal()` methods. * Integrate into `IRac` class. * Update unit tests etc. * Fix typo in supported brand name. For #1587 --- src/IRac.cpp | 12 ++-- src/IRac.h | 3 +- src/ir_Gree.cpp | 164 ++++++++++++++++++++++-------------------- src/ir_Gree.h | 41 +++++++---- test/IRac_test.cpp | 4 +- test/ir_Gree_test.cpp | 34 ++++++--- 6 files changed, 151 insertions(+), 107 deletions(-) diff --git a/src/IRac.cpp b/src/IRac.cpp index 11f0005b4..b3cf246e8 100644 --- a/src/IRac.cpp +++ b/src/IRac.cpp @@ -1039,6 +1039,7 @@ void IRac::goodweather(IRGoodweatherAc *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] swingh The horizontal swing setting. /// @param[in] turbo Run the device in turbo/powerful mode. /// @param[in] light Turn on the LED/Display mode. /// @param[in] clean Turn on the self-cleaning mode. e.g. Mould, dry filters etc @@ -1046,8 +1047,9 @@ void IRac::goodweather(IRGoodweatherAc *ac, void IRac::gree(IRGreeAC *ac, const gree_ac_remote_model_t model, const bool on, const stdAc::opmode_t mode, const bool celsius, 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) { + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, + const bool turbo, const bool light, const bool clean, + const int16_t sleep) { ac->begin(); ac->setModel(model); ac->setPower(on); @@ -1056,11 +1058,11 @@ void IRac::gree(IRGreeAC *ac, const gree_ac_remote_model_t model, ac->setFan(ac->convertFan(fan)); ac->setSwingVertical(swingv == stdAc::swingv_t::kAuto, // Set auto flag. ac->convertSwingV(swingv)); + ac->setSwingHorizontal(ac->convertSwingH(swingh)); ac->setLight(light); ac->setTurbo(turbo); ac->setXFan(clean); ac->setSleep(sleep >= 0); // Sleep on this A/C is either on or off. - // No Horizontal Swing setting available. // No Econo setting available. // No Filter setting available. // No Beep setting available. @@ -2779,8 +2781,8 @@ bool IRac::sendAc(const stdAc::state_t desired, const stdAc::state_t *prev) { IRGreeAC ac(_pin, (gree_ac_remote_model_t)send.model, _inverted, _modulation); gree(&ac, (gree_ac_remote_model_t)send.model, send.power, send.mode, - send.celsius, send.degrees, send.fanspeed, send.swingv, send.turbo, - send.light, send.clean, send.sleep); + send.celsius, send.degrees, send.fanspeed, send.swingv, send.swingh, + send.turbo, send.light, send.clean, send.sleep); break; } #endif // SEND_GREE diff --git a/src/IRac.h b/src/IRac.h index c0ae4921f..868c26a7f 100644 --- a/src/IRac.h +++ b/src/IRac.h @@ -248,7 +248,8 @@ void electra(IRElectraAc *ac, void gree(IRGreeAC *ac, const gree_ac_remote_model_t model, const bool on, const stdAc::opmode_t mode, const bool celsius, 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::swingh_t swingh, + const bool turbo, const bool light, const bool clean, const int16_t sleep = -1); #endif // SEND_GREE #if SEND_HAIER_AC diff --git a/src/ir_Gree.cpp b/src/ir_Gree.cpp index 1d1371b2d..9170ef1dc 100644 --- a/src/ir_Gree.cpp +++ b/src/ir_Gree.cpp @@ -35,6 +35,7 @@ using irutils::addLabeledString; using irutils::addModeToString; using irutils::addModelToString; using irutils::addFanToString; +using irutils::addSwingHToString; using irutils::addTempToString; using irutils::minsToString; @@ -220,15 +221,11 @@ bool IRGreeAC::getPower(void) const { /// Set the default temperature units to use. /// @param[in] on Use Fahrenheit as the units. /// true is Fahrenheit, false is Celsius. -void IRGreeAC::setUseFahrenheit(const bool on) { - _.UseFahrenheit = on; -} +void IRGreeAC::setUseFahrenheit(const bool on) { _.UseFahrenheit = on; } /// Get the default temperature units in use. /// @return true is Fahrenheit, false is Celsius. -bool IRGreeAC::getUseFahrenheit(void) const { - return _.UseFahrenheit; -} +bool IRGreeAC::getUseFahrenheit(void) const { return _.UseFahrenheit; } /// Set the temp. in degrees /// @param[in] temp Desired temperature in Degrees. @@ -281,9 +278,7 @@ void IRGreeAC::setFan(const uint8_t speed) { /// Get the current fan speed setting. /// @return The current fan speed. -uint8_t IRGreeAC::getFan(void) const { - return _.Fan; -} +uint8_t IRGreeAC::getFan(void) const { return _.Fan; } /// Set the operating mode of the A/C. /// @param[in] new_mode The desired operating mode. @@ -305,81 +300,55 @@ void IRGreeAC::setMode(const uint8_t new_mode) { /// Get the operating mode setting of the A/C. /// @return The current operating mode setting. -uint8_t IRGreeAC::getMode(void) const { - return _.Mode; -} +uint8_t IRGreeAC::getMode(void) const { return _.Mode; } /// Set the Light (LED) setting of the A/C. /// @param[in] on true, the setting is on. false, the setting is off. -void IRGreeAC::setLight(const bool on) { - _.Light = on; -} +void IRGreeAC::setLight(const bool on) { _.Light = on; } /// Get the Light (LED) setting of the A/C. /// @return true, the setting is on. false, the setting is off. -bool IRGreeAC::getLight(void) const { - return _.Light; -} +bool IRGreeAC::getLight(void) const { return _.Light; } /// Set the IFeel setting of the A/C. /// @param[in] on true, the setting is on. false, the setting is off. -void IRGreeAC::setIFeel(const bool on) { - _.IFeel = on; -} +void IRGreeAC::setIFeel(const bool on) { _.IFeel = on; } /// Get the IFeel setting of the A/C. /// @return true, the setting is on. false, the setting is off. -bool IRGreeAC::getIFeel(void) const { - return _.IFeel; -} +bool IRGreeAC::getIFeel(void) const { return _.IFeel; } /// Set the Wifi (enabled) setting of the A/C. /// @param[in] on true, the setting is on. false, the setting is off. -void IRGreeAC::setWiFi(const bool on) { - _.WiFi = on; -} +void IRGreeAC::setWiFi(const bool on) { _.WiFi = on; } /// Get the Wifi (enabled) setting of the A/C. /// @return true, the setting is on. false, the setting is off. -bool IRGreeAC::getWiFi(void) const { - return _.WiFi; -} +bool IRGreeAC::getWiFi(void) const { return _.WiFi; } /// Set the XFan (Mould) setting of the A/C. /// @param[in] on true, the setting is on. false, the setting is off. -void IRGreeAC::setXFan(const bool on) { - _.Xfan = on; -} +void IRGreeAC::setXFan(const bool on) { _.Xfan = on; } /// Get the XFan (Mould) setting of the A/C. /// @return true, the setting is on. false, the setting is off. -bool IRGreeAC::getXFan(void) const { - return _.Xfan; -} +bool IRGreeAC::getXFan(void) const { return _.Xfan; } /// Set the Sleep setting of the A/C. /// @param[in] on true, the setting is on. false, the setting is off. -void IRGreeAC::setSleep(const bool on) { - _.Sleep = on; -} +void IRGreeAC::setSleep(const bool on) { _.Sleep = on; } /// Get the Sleep setting of the A/C. /// @return true, the setting is on. false, the setting is off. -bool IRGreeAC::getSleep(void) const { - return _.Sleep; -} +bool IRGreeAC::getSleep(void) const { return _.Sleep; } /// Set the Turbo setting of the A/C. /// @param[in] on true, the setting is on. false, the setting is off. -void IRGreeAC::setTurbo(const bool on) { - _.Turbo = on; -} +void IRGreeAC::setTurbo(const bool on) { _.Turbo = on; } /// Get the Turbo setting of the A/C. /// @return true, the setting is on. false, the setting is off. -bool IRGreeAC::getTurbo(void) const { - return _.Turbo; -} +bool IRGreeAC::getTurbo(void) const { return _.Turbo; } /// Set the Vertical Swing mode of the A/C. /// @param[in] automatic Do we use the automatic setting? @@ -409,32 +378,37 @@ void IRGreeAC::setSwingVertical(const bool automatic, const uint8_t position) { new_position = kGreeSwingAuto; } } - _.Swing = new_position; + _.SwingV = new_position; } /// Get the Vertical Swing Automatic mode setting of the A/C. /// @return true, the setting is on. false, the setting is off. -bool IRGreeAC::getSwingVerticalAuto(void) const { - return _.SwingAuto; -} +bool IRGreeAC::getSwingVerticalAuto(void) const { return _.SwingAuto; } /// Get the Vertical Swing position setting of the A/C. /// @return The native position/mode. -uint8_t IRGreeAC::getSwingVerticalPosition(void) const { - return _.Swing; +uint8_t IRGreeAC::getSwingVerticalPosition(void) const { return _.SwingV; } + +/// Get the Horizontal Swing position setting of the A/C. +/// @return The native position/mode. +uint8_t IRGreeAC::getSwingHorizontal(void) const { return _.SwingH; } + +/// Set the Horizontal Swing mode of the A/C. +/// @param[in] position The position/mode to set the vanes to. +void IRGreeAC::setSwingHorizontal(const uint8_t position) { + if (position <= kGreeSwingHMaxRight) + _.SwingH = position; + else + _.SwingH = kGreeSwingHOff; } /// Set the timer enable setting of the A/C. /// @param[in] on true, the setting is on. false, the setting is off. -void IRGreeAC::setTimerEnabled(const bool on) { - _.TimerEnabled = on; -} +void IRGreeAC::setTimerEnabled(const bool on) { _.TimerEnabled = on; } /// Get the timer enabled setting of the A/C. /// @return true, the setting is on. false, the setting is off. -bool IRGreeAC::getTimerEnabled(void) const { - return _.TimerEnabled; -} +bool IRGreeAC::getTimerEnabled(void) const { return _.TimerEnabled; } /// Get the timer time value from the A/C. /// @return The number of minutes the timer is set for. @@ -478,9 +452,7 @@ void IRGreeAC::setDisplayTempSource(const uint8_t mode) { /// Get the temperature display mode. /// i.e. Internal, External temperature sensing. /// @return The current temp source being displayed. -uint8_t IRGreeAC::getDisplayTempSource(void) const { - return _.DisplayTemp; -} +uint8_t IRGreeAC::getDisplayTempSource(void) const { return _.DisplayTemp; } /// Convert a stdAc::opmode_t enum into its native mode. /// @param[in] mode The enum to be converted. @@ -523,6 +495,21 @@ uint8_t IRGreeAC::convertSwingV(const stdAc::swingv_t swingv) { } } +/// Convert a stdAc::swingh_t enum into it's native setting. +/// @param[in] swingh The enum to be converted. +/// @return The native equivalent of the enum. +uint8_t IRGreeAC::convertSwingH(const stdAc::swingh_t swingh) { + switch (swingh) { + case stdAc::swingh_t::kAuto: return kGreeSwingHAuto; + case stdAc::swingh_t::kLeftMax: return kGreeSwingHMaxLeft; + case stdAc::swingh_t::kLeft: return kGreeSwingHLeft; + case stdAc::swingh_t::kMiddle: return kGreeSwingHMiddle; + case stdAc::swingh_t::kRight: return kGreeSwingHRight; + case stdAc::swingh_t::kRightMax: return kGreeSwingHMaxRight; + default: return kGreeSwingHOff; + } +} + /// Convert a native mode into its stdAc equivalent. /// @param[in] mode The native setting to be converted. /// @return The stdAc equivalent of the native setting. @@ -530,9 +517,9 @@ stdAc::opmode_t IRGreeAC::toCommonMode(const uint8_t mode) { switch (mode) { case kGreeCool: return stdAc::opmode_t::kCool; case kGreeHeat: return stdAc::opmode_t::kHeat; - case kGreeDry: return stdAc::opmode_t::kDry; - case kGreeFan: return stdAc::opmode_t::kFan; - default: return stdAc::opmode_t::kAuto; + case kGreeDry: return stdAc::opmode_t::kDry; + case kGreeFan: return stdAc::opmode_t::kFan; + default: return stdAc::opmode_t::kAuto; } } @@ -541,10 +528,10 @@ stdAc::opmode_t IRGreeAC::toCommonMode(const uint8_t mode) { /// @return The stdAc equivalent of the native setting. stdAc::fanspeed_t IRGreeAC::toCommonFanSpeed(const uint8_t speed) { switch (speed) { - case kGreeFanMax: return stdAc::fanspeed_t::kMax; + case kGreeFanMax: return stdAc::fanspeed_t::kMax; case kGreeFanMax - 1: return stdAc::fanspeed_t::kMedium; - case kGreeFanMin: return stdAc::fanspeed_t::kMin; - default: return stdAc::fanspeed_t::kAuto; + case kGreeFanMin: return stdAc::fanspeed_t::kMin; + default: return stdAc::fanspeed_t::kAuto; } } @@ -553,12 +540,27 @@ stdAc::fanspeed_t IRGreeAC::toCommonFanSpeed(const uint8_t speed) { /// @return The stdAc equivalent of the native setting. stdAc::swingv_t IRGreeAC::toCommonSwingV(const uint8_t pos) { switch (pos) { - case kGreeSwingUp: return stdAc::swingv_t::kHighest; - case kGreeSwingMiddleUp: return stdAc::swingv_t::kHigh; - case kGreeSwingMiddle: return stdAc::swingv_t::kMiddle; + case kGreeSwingUp: return stdAc::swingv_t::kHighest; + case kGreeSwingMiddleUp: return stdAc::swingv_t::kHigh; + case kGreeSwingMiddle: return stdAc::swingv_t::kMiddle; case kGreeSwingMiddleDown: return stdAc::swingv_t::kLow; - case kGreeSwingDown: return stdAc::swingv_t::kLowest; - default: return stdAc::swingv_t::kAuto; + case kGreeSwingDown: return stdAc::swingv_t::kLowest; + default: return stdAc::swingv_t::kAuto; + } +} + +/// Convert a native Horizontal Swing into its stdAc equivalent. +/// @param[in] pos The native setting to be converted. +/// @return The stdAc equivalent of the native setting. +stdAc::swingh_t IRGreeAC::toCommonSwingH(const uint8_t pos) { + switch (pos) { + case kGreeSwingHAuto: return stdAc::swingh_t::kAuto; + case kGreeSwingHMaxLeft: return stdAc::swingh_t::kLeftMax; + case kGreeSwingHLeft: return stdAc::swingh_t::kLeft; + case kGreeSwingHMiddle: return stdAc::swingh_t::kMiddle; + case kGreeSwingHRight: return stdAc::swingh_t::kRight; + case kGreeSwingHMaxRight: return stdAc::swingh_t::kRightMax; + default: return stdAc::swingh_t::kOff; } } @@ -576,13 +578,13 @@ stdAc::state_t IRGreeAC::toCommon(void) { if (_.SwingAuto) result.swingv = stdAc::swingv_t::kAuto; else - result.swingv = toCommonSwingV(_.Swing); + result.swingv = toCommonSwingV(_.SwingV); + result.swingh = toCommonSwingH(_.SwingH); result.turbo = _.Turbo; result.light = _.Light; result.clean = _.Xfan; result.sleep = _.Sleep ? 0 : -1; // Not supported. - result.swingh = stdAc::swingh_t::kOff; result.quiet = false; result.econo = false; result.filter = false; @@ -611,9 +613,9 @@ String IRGreeAC::toString(void) { result += addBoolToString(_.Sleep, kSleepStr); result += addLabeledString(_.SwingAuto ? kAutoStr : kManualStr, kSwingVModeStr); - result += addIntToString(_.Swing, kSwingVStr); + result += addIntToString(_.SwingV, kSwingVStr); result += kSpaceLBraceStr; - switch (_.Swing) { + switch (_.SwingV) { case kGreeSwingLastPos: result += kLastStr; break; @@ -623,6 +625,12 @@ String IRGreeAC::toString(void) { default: result += kUnknownStr; } result += ')'; + result += addSwingHToString(_.SwingH, kGreeSwingHAuto, kGreeSwingHMaxLeft, + kGreeSwingHLeft, kGreeSwingHMiddle, + kGreeSwingHRight, kGreeSwingHMaxRight, + kGreeSwingHOff, + // rest are unused. + 0xFF, 0xFF, 0xFF, 0xFF); result += addLabeledString( _.TimerEnabled ? minsToString(getTimer()) : kOffStr, kTimerStr); uint8_t src = _.DisplayTemp; diff --git a/src/ir_Gree.h b/src/ir_Gree.h index d93b42d8d..5117e9b8b 100644 --- a/src/ir_Gree.h +++ b/src/ir_Gree.h @@ -21,8 +21,8 @@ // Brand: Amana, Model: YX1FF remote // Brand: Cooper & Hunter, Model: YB1F2 remote // Brand: Cooper & Hunter, Model: CH-S09FTXG A/C -// Brand: Vaillant, Model: YACIFB remote -// Brand: Vaillant, Model: VAI5-035WNI A/C +// Brand: Vailland, Model: YACIFB remote +// Brand: Vailland, Model: VAI5-035WNI A/C #ifndef IR_GREE_H_ #define IR_GREE_H_ @@ -65,8 +65,9 @@ union GreeProtocol{ uint8_t UseFahrenheit :1; uint8_t unknown1 :4; // value=0b0101 // Byte 4 - uint8_t Swing:4; - uint8_t :0; + uint8_t SwingV :4; + uint8_t SwingH :3; + uint8_t :1; // Byte 5 uint8_t DisplayTemp :2; uint8_t IFeel :1; @@ -100,16 +101,24 @@ const uint8_t kGreeMinTempF = 61; // Fahrenheit const uint8_t kGreeMaxTempF = 86; // Fahrenheit const uint16_t kGreeTimerMax = 24 * 60; -const uint8_t kGreeSwingLastPos = 0b0000; -const uint8_t kGreeSwingAuto = 0b0001; -const uint8_t kGreeSwingUp = 0b0010; -const uint8_t kGreeSwingMiddleUp = 0b0011; -const uint8_t kGreeSwingMiddle = 0b0100; -const uint8_t kGreeSwingMiddleDown = 0b0101; -const uint8_t kGreeSwingDown = 0b0110; -const uint8_t kGreeSwingDownAuto = 0b0111; -const uint8_t kGreeSwingMiddleAuto = 0b1001; -const uint8_t kGreeSwingUpAuto = 0b1011; +const uint8_t kGreeSwingLastPos = 0b0000; // 0 +const uint8_t kGreeSwingAuto = 0b0001; // 1 +const uint8_t kGreeSwingUp = 0b0010; // 2 +const uint8_t kGreeSwingMiddleUp = 0b0011; // 3 +const uint8_t kGreeSwingMiddle = 0b0100; // 4 +const uint8_t kGreeSwingMiddleDown = 0b0101; // 5 +const uint8_t kGreeSwingDown = 0b0110; // 6 +const uint8_t kGreeSwingDownAuto = 0b0111; // 7 +const uint8_t kGreeSwingMiddleAuto = 0b1001; // 9 +const uint8_t kGreeSwingUpAuto = 0b1011; // 11 + +const uint8_t kGreeSwingHOff = 0b000; // 0 +const uint8_t kGreeSwingHAuto = 0b001; // 1 +const uint8_t kGreeSwingHMaxLeft = 0b010; // 2 +const uint8_t kGreeSwingHLeft = 0b011; // 3 +const uint8_t kGreeSwingHMiddle = 0b100; // 4 +const uint8_t kGreeSwingHRight = 0b101; // 5 +const uint8_t kGreeSwingHMaxRight = 0b110; // 6 const uint8_t kGreeDisplayTempOff = 0b00; // 0 const uint8_t kGreeDisplayTempSet = 0b01; // 1 @@ -183,6 +192,8 @@ class IRGreeAC { void setSwingVertical(const bool automatic, const uint8_t position); bool getSwingVerticalAuto(void) const; uint8_t getSwingVerticalPosition(void) const; + void setSwingHorizontal(const uint8_t position); + uint8_t getSwingHorizontal(void) const; uint16_t getTimer(void) const; void setTimer(const uint16_t minutes); void setDisplayTempSource(const uint8_t mode); @@ -190,9 +201,11 @@ class IRGreeAC { static uint8_t convertMode(const stdAc::opmode_t mode); static uint8_t convertFan(const stdAc::fanspeed_t speed); static uint8_t convertSwingV(const stdAc::swingv_t swingv); + static uint8_t convertSwingH(const stdAc::swingh_t swingh); static stdAc::opmode_t toCommonMode(const uint8_t mode); static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed); static stdAc::swingv_t toCommonSwingV(const uint8_t pos); + static stdAc::swingh_t toCommonSwingH(const uint8_t pos); stdAc::state_t toCommon(void); uint8_t* getRaw(void); void setRaw(const uint8_t new_code[]); diff --git a/test/IRac_test.cpp b/test/IRac_test.cpp index 354148e69..513bc6031 100644 --- a/test/IRac_test.cpp +++ b/test/IRac_test.cpp @@ -731,7 +731,8 @@ TEST(TestIRac, Gree) { "Model: 1 (YAW1F), Power: On, Mode: 1 (Cool), Temp: 71F, " "Fan: 2 (Medium), Turbo: Off, IFeel: Off, WiFi: Off, XFan: On, " "Light: On, Sleep: On, Swing(V) Mode: Manual, " - "Swing(V): 3 (UNKNOWN), Timer: Off, Display Temp: 0 (Off)"; + "Swing(V): 3 (UNKNOWN), Swing(H): 5 (Right), Timer: Off, " + "Display Temp: 0 (Off)"; ac.begin(); irac.gree(&ac, @@ -742,6 +743,7 @@ TEST(TestIRac, Gree) { 71, // Degrees (F) stdAc::fanspeed_t::kMedium, // Fan speed stdAc::swingv_t::kHigh, // Vertical swing + stdAc::swingh_t::kRight, // Horizontal swing false, // Turbo true, // Light true, // Clean (aka Mold/XFan) diff --git a/test/ir_Gree_test.cpp b/test/ir_Gree_test.cpp index 597fd3c09..f802a0d43 100644 --- a/test/ir_Gree_test.cpp +++ b/test/ir_Gree_test.cpp @@ -304,8 +304,8 @@ TEST(TestGreeClass, Temperature) { EXPECT_EQ( "Model: 2 (YBOFB), Power: On, Mode: 1 (Cool), Temp: 63F, Fan: 0 (Auto), " "Turbo: Off, IFeel: Off, WiFi: Off, XFan: Off, Light: On, Sleep: Off, " - "Swing(V) Mode: Manual, Swing(V): 0 (Last), Timer: Off, " - "Display Temp: 0 (Off)", ac.toString()); + "Swing(V) Mode: Manual, Swing(V): 0 (Last), Swing(H): 0 (Off), " + "Timer: Off, Display Temp: 0 (Off)", ac.toString()); } TEST(TestGreeClass, OperatingMode) { @@ -506,6 +506,24 @@ TEST(TestGreeClass, VerticalSwing) { EXPECT_EQ(kGreeSwingAuto, ac.getSwingVerticalPosition()); } +TEST(TestGreeClass, HorizontalSwing) { + IRGreeAC ac(kGpioUnused); + ac.begin(); + + ac.setSwingHorizontal(kGreeSwingHAuto); + EXPECT_EQ(kGreeSwingHAuto, ac.getSwingHorizontal()); + + ac.setSwingHorizontal(kGreeSwingHMiddle); + EXPECT_EQ(kGreeSwingHMiddle, ac.getSwingHorizontal()); + + ac.setSwingHorizontal(kGreeSwingHMaxRight); + EXPECT_EQ(kGreeSwingHMaxRight, ac.getSwingHorizontal()); + + // Out of bounds. + ac.setSwingHorizontal(kGreeSwingHMaxRight + 1); + EXPECT_EQ(kGreeSwingHOff, ac.getSwingHorizontal()); +} + TEST(TestGreeClass, SetAndGetRaw) { IRGreeAC ac(kGpioUnused); uint8_t initialState[kGreeStateLength] = {0x00, 0x09, 0x20, 0x50, @@ -544,7 +562,7 @@ TEST(TestGreeClass, HumanReadable) { EXPECT_EQ( "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(V) Mode: Manual, Swing(V): 0 (Last), " + "Swing(V) Mode: Manual, Swing(V): 0 (Last), Swing(H): 0 (Off), " "Timer: Off, Display Temp: 0 (Off)", ac.toString()); ac.on(); @@ -563,8 +581,8 @@ TEST(TestGreeClass, HumanReadable) { EXPECT_EQ( "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(V) Mode: Auto, Swing(V): 1 (Auto), Timer: 12:30, " - "Display Temp: 3 (Outside)", + "Swing(V) Mode: Auto, Swing(V): 1 (Auto), Swing(H): 0 (Off), " + "Timer: 12:30, Display Temp: 3 (Outside)", ac.toString()); } @@ -624,8 +642,8 @@ TEST(TestDecodeGree, NormalRealExample) { EXPECT_EQ( "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(V) Mode: Manual, Swing(V): 2 (UNKNOWN), Timer: Off, " - "Display Temp: 3 (Outside)", + "Swing(V) Mode: Manual, Swing(V): 2 (UNKNOWN), Swing(H): 0 (Off), " + "Timer: Off, Display Temp: 3 (Outside)", IRAcUtils::resultAcToString(&irsend.capture)); stdAc::state_t r, p; ASSERT_TRUE(IRAcUtils::decodeToState(&irsend.capture, &r, &p)); @@ -682,7 +700,7 @@ TEST(TestGreeClass, Issue814Power) { 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(V) Mode: Auto, Swing(V): 1 (Auto), Timer: Off, " + "Swing(V) Mode: Auto, Swing(V): 1 (Auto), Swing(H): 0 (Off), Timer: Off, " "Display Temp: 0 (Off)", ac.toString()); ac.off(); From a3d34423993a47f88c546dfb7b532ed8c49130fe Mon Sep 17 00:00:00 2001 From: crankyoldgit Date: Wed, 27 Oct 2021 16:27:43 +1000 Subject: [PATCH 2/2] [Gree] Add support for the Econo setting. * Add methods. * Update `IRac` class. * Update and add unit tests. For #1587 --- src/IRac.cpp | 8 +++++--- src/IRac.h | 2 +- src/ir_Gree.cpp | 11 ++++++++++- src/ir_Gree.h | 12 ++++++++---- test/IRac_test.cpp | 5 +++-- test/ir_Gree_test.cpp | 29 ++++++++++++++++++++++++----- 6 files changed, 51 insertions(+), 16 deletions(-) diff --git a/src/IRac.cpp b/src/IRac.cpp index b3cf246e8..554fed829 100644 --- a/src/IRac.cpp +++ b/src/IRac.cpp @@ -1041,6 +1041,7 @@ void IRac::goodweather(IRGoodweatherAc *ac, /// @param[in] swingv The vertical swing setting. /// @param[in] swingh The horizontal swing setting. /// @param[in] turbo Run the device in turbo/powerful mode. +/// @param[in] econo Toggle the device's economical mode. /// @param[in] light Turn on the LED/Display mode. /// @param[in] clean Turn on the self-cleaning mode. e.g. Mould, dry filters etc /// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. @@ -1048,8 +1049,8 @@ void IRac::gree(IRGreeAC *ac, const gree_ac_remote_model_t model, const bool on, const stdAc::opmode_t mode, 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 light, const bool clean, - const int16_t sleep) { + const bool turbo, const bool econo, const bool light, + const bool clean, const int16_t sleep) { ac->begin(); ac->setModel(model); ac->setPower(on); @@ -1061,6 +1062,7 @@ void IRac::gree(IRGreeAC *ac, const gree_ac_remote_model_t model, ac->setSwingHorizontal(ac->convertSwingH(swingh)); ac->setLight(light); ac->setTurbo(turbo); + ac->setEcono(econo); ac->setXFan(clean); ac->setSleep(sleep >= 0); // Sleep on this A/C is either on or off. // No Econo setting available. @@ -2782,7 +2784,7 @@ bool IRac::sendAc(const stdAc::state_t desired, const stdAc::state_t *prev) { _modulation); gree(&ac, (gree_ac_remote_model_t)send.model, send.power, send.mode, send.celsius, send.degrees, send.fanspeed, send.swingv, send.swingh, - send.turbo, send.light, send.clean, send.sleep); + send.turbo, send.econo, send.light, send.clean, send.sleep); break; } #endif // SEND_GREE diff --git a/src/IRac.h b/src/IRac.h index 868c26a7f..7ca52cc94 100644 --- a/src/IRac.h +++ b/src/IRac.h @@ -249,7 +249,7 @@ void electra(IRElectraAc *ac, const bool on, const stdAc::opmode_t mode, 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 light, + const bool turbo, const bool econo, const bool light, const bool clean, const int16_t sleep = -1); #endif // SEND_GREE #if SEND_HAIER_AC diff --git a/src/ir_Gree.cpp b/src/ir_Gree.cpp index 9170ef1dc..3c3a8583e 100644 --- a/src/ir_Gree.cpp +++ b/src/ir_Gree.cpp @@ -350,6 +350,14 @@ void IRGreeAC::setTurbo(const bool on) { _.Turbo = on; } /// @return true, the setting is on. false, the setting is off. bool IRGreeAC::getTurbo(void) const { return _.Turbo; } +/// Set the Econo setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRGreeAC::setEcono(const bool on) { _.Econo = on; } + +/// Get the Econo setting of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRGreeAC::getEcono(void) const { return _.Econo; } + /// Set the Vertical Swing mode of the A/C. /// @param[in] automatic Do we use the automatic setting? /// @param[in] position The position/mode to set the vanes to. @@ -581,12 +589,12 @@ stdAc::state_t IRGreeAC::toCommon(void) { result.swingv = toCommonSwingV(_.SwingV); result.swingh = toCommonSwingH(_.SwingH); result.turbo = _.Turbo; + result.econo = _.Econo; result.light = _.Light; result.clean = _.Xfan; result.sleep = _.Sleep ? 0 : -1; // Not supported. result.quiet = false; - result.econo = false; result.filter = false; result.beep = false; result.clock = -1; @@ -606,6 +614,7 @@ String IRGreeAC::toString(void) { result += addFanToString(_.Fan, kGreeFanMax, kGreeFanMin, kGreeFanAuto, kGreeFanAuto, kGreeFanMed); result += addBoolToString(_.Turbo, kTurboStr); + result += addBoolToString(_.Econo, kEconoStr); result += addBoolToString(_.IFeel, kIFeelStr); result += addBoolToString(_.WiFi, kWifiStr); result += addBoolToString(_.Xfan, kXFanStr); diff --git a/src/ir_Gree.h b/src/ir_Gree.h index 5117e9b8b..be5ac31ce 100644 --- a/src/ir_Gree.h +++ b/src/ir_Gree.h @@ -73,12 +73,14 @@ union GreeProtocol{ uint8_t IFeel :1; uint8_t unknown2 :3; // value = 0b100 uint8_t WiFi :1; - uint8_t :0; + uint8_t :1; // Byte 6 - uint8_t :8; + uint8_t :8; // Byte 7 - uint8_t :4; - uint8_t Sum:4; + uint8_t :2; + uint8_t Econo :1; + uint8_t :1; + uint8_t Sum :4; }; }; @@ -185,6 +187,8 @@ class IRGreeAC { bool getSleep(void) const; void setTurbo(const bool on); bool getTurbo(void) const; + void setEcono(const bool on); + bool getEcono(void) const; void setIFeel(const bool on); bool getIFeel(void) const; void setWiFi(const bool on); diff --git a/test/IRac_test.cpp b/test/IRac_test.cpp index 513bc6031..fa5f62b4a 100644 --- a/test/IRac_test.cpp +++ b/test/IRac_test.cpp @@ -729,8 +729,8 @@ TEST(TestIRac, Gree) { IRrecv capture(kGpioUnused); char expected[] = "Model: 1 (YAW1F), Power: On, Mode: 1 (Cool), Temp: 71F, " - "Fan: 2 (Medium), Turbo: Off, IFeel: Off, WiFi: Off, XFan: On, " - "Light: On, Sleep: On, Swing(V) Mode: Manual, " + "Fan: 2 (Medium), Turbo: Off, Econo: Off, IFeel: Off, WiFi: Off, " + "XFan: On, Light: On, Sleep: On, Swing(V) Mode: Manual, " "Swing(V): 3 (UNKNOWN), Swing(H): 5 (Right), Timer: Off, " "Display Temp: 0 (Off)"; @@ -745,6 +745,7 @@ TEST(TestIRac, Gree) { stdAc::swingv_t::kHigh, // Vertical swing stdAc::swingh_t::kRight, // Horizontal swing false, // Turbo + false, // Econo true, // Light true, // Clean (aka Mold/XFan) 8 * 60 + 0); // Sleep time diff --git a/test/ir_Gree_test.cpp b/test/ir_Gree_test.cpp index f802a0d43..cb1832f61 100644 --- a/test/ir_Gree_test.cpp +++ b/test/ir_Gree_test.cpp @@ -303,7 +303,8 @@ TEST(TestGreeClass, Temperature) { EXPECT_EQ(63, ac.getTemp()); EXPECT_EQ( "Model: 2 (YBOFB), Power: On, Mode: 1 (Cool), Temp: 63F, Fan: 0 (Auto), " - "Turbo: Off, IFeel: Off, WiFi: Off, XFan: Off, Light: On, Sleep: Off, " + "Turbo: Off, Econo: Off, IFeel: Off, WiFi: Off, XFan: Off, Light: On, " + "Sleep: Off, " "Swing(V) Mode: Manual, Swing(V): 0 (Last), Swing(H): 0 (Off), " "Timer: Off, Display Temp: 0 (Off)", ac.toString()); } @@ -385,6 +386,20 @@ TEST(TestGreeClass, Turbo) { EXPECT_TRUE(ac.getTurbo()); } +TEST(TestGreeClass, Econo) { + IRGreeAC ac(kGpioUnused); + ac.begin(); + + ac.setEcono(true); + EXPECT_TRUE(ac.getEcono()); + + ac.setEcono(false); + EXPECT_FALSE(ac.getEcono()); + + ac.setEcono(true); + EXPECT_TRUE(ac.getEcono()); +} + TEST(TestGreeClass, IFeel) { IRGreeAC ac(kGpioUnused); ac.begin(); @@ -561,7 +576,8 @@ TEST(TestGreeClass, HumanReadable) { EXPECT_EQ( "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, " + "Turbo: Off, Econo: Off, IFeel: Off, WiFi: Off, XFan: Off, Light: On, " + "Sleep: Off, " "Swing(V) Mode: Manual, Swing(V): 0 (Last), Swing(H): 0 (Off), " "Timer: Off, Display Temp: 0 (Off)", ac.toString()); @@ -580,7 +596,8 @@ TEST(TestGreeClass, HumanReadable) { ac.setDisplayTempSource(3); EXPECT_EQ( "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, " + "Turbo: On, Econo: Off, IFeel: On, WiFi: On, XFan: On, Light: Off, " + "Sleep: On, " "Swing(V) Mode: Auto, Swing(V): 1 (Auto), Swing(H): 0 (Off), " "Timer: 12:30, Display Temp: 3 (Outside)", ac.toString()); @@ -641,7 +658,8 @@ TEST(TestDecodeGree, NormalRealExample) { ac.setRaw(irsend.capture.state); EXPECT_EQ( "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, " + "Turbo: Off, Econo: Off, IFeel: Off, WiFi: Off, XFan: Off, Light: On, " + "Sleep: Off, " "Swing(V) Mode: Manual, Swing(V): 2 (UNKNOWN), Swing(H): 0 (Off), " "Timer: Off, Display Temp: 3 (Outside)", IRAcUtils::resultAcToString(&irsend.capture)); @@ -699,7 +717,8 @@ TEST(TestGreeClass, Issue814Power) { 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, " + "Turbo: Off, Econo: Off, IFeel: Off, WiFi: Off, XFan: Off, Light: On, " + "Sleep: Off, " "Swing(V) Mode: Auto, Swing(V): 1 (Auto), Swing(H): 0 (Off), Timer: Off, " "Display Temp: 0 (Off)", ac.toString());