diff --git a/examples/TurnOnKelvinatorAC/TurnOnKelvinatorAC.ino b/examples/TurnOnKelvinatorAC/TurnOnKelvinatorAC.ino index ebdb7536b..974bb94fc 100644 --- a/examples/TurnOnKelvinatorAC/TurnOnKelvinatorAC.ino +++ b/examples/TurnOnKelvinatorAC/TurnOnKelvinatorAC.ino @@ -57,7 +57,7 @@ void setup() { ac.setFan(1); ac.setMode(kKelvinatorCool); ac.setTemp(26); - ac.setSwingVertical(false); + ac.setSwingVertical(false, kKelvinatorSwingVOff); ac.setSwingHorizontal(true); ac.setXFan(true); ac.setIonFilter(false); diff --git a/src/IRac.cpp b/src/IRac.cpp index 4b700ff87..a9f35ff5c 100644 --- a/src/IRac.cpp +++ b/src/IRac.cpp @@ -1465,7 +1465,8 @@ void IRac::kelvinator(IRKelvinatorAC *ac, ac->setMode(ac->convertMode(mode)); ac->setTemp(degrees); ac->setFan((uint8_t)fan); // No conversion needed. - ac->setSwingVertical((int8_t)swingv >= 0); + ac->setSwingVertical(swingv == stdAc::swingv_t::kAuto, // Set auto flag. + ac->convertSwingV(swingv)); ac->setSwingHorizontal((int8_t)swingh >= 0); ac->setQuiet(quiet); ac->setTurbo(turbo); diff --git a/src/ir_Kelvinator.cpp b/src/ir_Kelvinator.cpp index b674e98b8..c44f7c683 100644 --- a/src/ir_Kelvinator.cpp +++ b/src/ir_Kelvinator.cpp @@ -53,6 +53,7 @@ using irutils::addLabeledString; using irutils::addModeToString; using irutils::addFanToString; using irutils::addTempToString; +using irutils::addSwingVToString; #if SEND_KELVINATOR /// Send a Kelvinator A/C message. @@ -274,16 +275,47 @@ void IRKelvinatorAC::setMode(const uint8_t mode) { } } -/// Control the current vertical swing setting. -/// @param[in] on The desired setting. -void IRKelvinatorAC::setSwingVertical(const bool on) { - _.SwingV = on; - _.VentSwing = (on || _.SwingH); +/// 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. +void IRKelvinatorAC::setSwingVertical(const bool automatic, + const uint8_t position) { + _.SwingAuto = (automatic || _.SwingH); + uint8_t new_position = position; + if (!automatic) { + switch (position) { + case kKelvinatorSwingVHighest: + case kKelvinatorSwingVUpperMiddle: + case kKelvinatorSwingVMiddle: + case kKelvinatorSwingVLowerMiddle: + case kKelvinatorSwingVLowest: + break; + default: + new_position = kKelvinatorSwingVOff; + } + } else { + switch (position) { + case kKelvinatorSwingVAuto: + case kKelvinatorSwingVLowAuto: + case kKelvinatorSwingVMiddleAuto: + case kKelvinatorSwingVHighAuto: + break; + default: + new_position = kKelvinatorSwingVAuto; + } + } + _.SwingV = new_position; } -/// Is the vertical swing setting on? -/// @return The current value. -bool IRKelvinatorAC::getSwingVertical(void) const { +/// Get the Vertical Swing Automatic mode setting of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRKelvinatorAC::getSwingVerticalAuto(void) const { + return _.SwingV & 0b0001; +} + +/// Get the Vertical Swing position setting of the A/C. +/// @return The native position/mode. +uint8_t IRKelvinatorAC::getSwingVerticalPosition(void) const { return _.SwingV; } @@ -291,7 +323,7 @@ bool IRKelvinatorAC::getSwingVertical(void) const { /// @param[in] on The desired setting. void IRKelvinatorAC::setSwingHorizontal(const bool on) { _.SwingH = on; - _.VentSwing = (on || _.SwingV); + _.SwingAuto = (on || (_.SwingV & 0b0001)); } /// Is the horizontal swing setting on? @@ -378,6 +410,20 @@ uint8_t IRKelvinatorAC::convertMode(const stdAc::opmode_t mode) { } } +/// Convert a stdAc::swingv_t enum into it's native setting. +/// @param[in] swingv The enum to be converted. +/// @return The native equivalent of the enum. +uint8_t IRKelvinatorAC::convertSwingV(const stdAc::swingv_t swingv) { + switch (swingv) { + case stdAc::swingv_t::kHighest: return kKelvinatorSwingVHighest; + case stdAc::swingv_t::kHigh: return kKelvinatorSwingVHighAuto; + case stdAc::swingv_t::kMiddle: return kKelvinatorSwingVMiddle; + case stdAc::swingv_t::kLow: return kKelvinatorSwingVLowAuto; + case stdAc::swingv_t::kLowest: return kKelvinatorSwingVLowest; + default: return kKelvinatorSwingVAuto; + } +} + /// Convert a native mode to it's stdAc::opmode_t equivalent. /// @param[in] mode A native operating mode value. /// @return The stdAc::opmode_t equivalent. @@ -442,7 +488,17 @@ String IRKelvinatorAC::toString(void) const { result += addBoolToString(_.IonFilter, kIonStr); result += addBoolToString(_.Light, kLightStr); result += addBoolToString(_.SwingH, kSwingHStr); - result += addBoolToString(_.SwingV, kSwingVStr); + result += addSwingVToString(_.SwingV, kKelvinatorSwingVAuto, + kKelvinatorSwingVHighest, + kKelvinatorSwingVHighAuto, + kKelvinatorSwingVUpperMiddle, + kKelvinatorSwingVMiddle, + kKelvinatorSwingVLowerMiddle, + kKelvinatorSwingVLowAuto, + kKelvinatorSwingVLowest, + kKelvinatorSwingVOff, + kKelvinatorSwingVAuto, kKelvinatorSwingVAuto, + kKelvinatorSwingVAuto); return result; } diff --git a/src/ir_Kelvinator.h b/src/ir_Kelvinator.h index 74371d8ef..32cb3e1fa 100644 --- a/src/ir_Kelvinator.h +++ b/src/ir_Kelvinator.h @@ -14,6 +14,7 @@ // Brand: Kelvinator, Model: KSV70HRC A/C // Brand: Kelvinator, Model: KSV80HRC A/C // Brand: Green, Model: YAPOF3 remote +// Brand: Gree, Model: YAP0F8 remote // Brand: Sharp, Model: YB1FA remote // Brand: Sharp, Model: A5VEY A/C @@ -39,7 +40,7 @@ union KelvinatorProtocol{ uint8_t Mode :3; uint8_t Power :1; uint8_t BasicFan :2; - uint8_t VentSwing :1; + uint8_t SwingAuto :1; uint8_t :1; // Sleep Modes 1 & 3 (1 = On, 0 = Off) // Byte 1 uint8_t Temp :4; // Degrees C. @@ -56,8 +57,7 @@ union KelvinatorProtocol{ uint8_t :2; // End of command block (B01) // (B010 marker and a gap of 20ms) // Byte 4 - uint8_t SwingV :1; - uint8_t :3; + uint8_t SwingV :4; uint8_t SwingH :1; uint8_t :3; // Byte 5~6 @@ -103,6 +103,17 @@ const uint8_t kKelvinatorMinTemp = 16; // 16C const uint8_t kKelvinatorMaxTemp = 30; // 30C const uint8_t kKelvinatorAutoTemp = 25; // 25C +const uint8_t kKelvinatorSwingVOff = 0b0000; // 0 +const uint8_t kKelvinatorSwingVAuto = 0b0001; // 1 +const uint8_t kKelvinatorSwingVHighest = 0b0010; // 2 +const uint8_t kKelvinatorSwingVUpperMiddle = 0b0011; // 3 +const uint8_t kKelvinatorSwingVMiddle = 0b0100; // 4 +const uint8_t kKelvinatorSwingVLowerMiddle = 0b0101; // 5 +const uint8_t kKelvinatorSwingVLowest = 0b0110; // 6 +const uint8_t kKelvinatorSwingVLowAuto = 0b0111; // 7 +const uint8_t kKelvinatorSwingVMiddleAuto = 0b1001; // 9 +const uint8_t kKelvinatorSwingVHighAuto = 0b1011; // 11 + // Legacy defines (Deprecated) #define KELVINATOR_MIN_TEMP kKelvinatorMinTemp #define KELVINATOR_MAX_TEMP kKelvinatorMaxTemp @@ -142,8 +153,11 @@ class IRKelvinatorAC { uint8_t getFan(void) const; void setMode(const uint8_t mode); uint8_t getMode(void) const; - void setSwingVertical(const bool on); - bool getSwingVertical(void) const; + void setSwingVertical(const bool automatic, const uint8_t position); + bool getSwingVerticalAuto(void) const; + uint8_t getSwingVerticalPosition(void) const; + static uint8_t convertSwingV(const stdAc::swingv_t swingv); + static stdAc::swingv_t toCommonSwingV(const uint8_t pos); void setSwingHorizontal(const bool on); bool getSwingHorizontal(void) const; void setQuiet(const bool on); diff --git a/test/IRac_test.cpp b/test/IRac_test.cpp index 431a2ebe9..59e1186a7 100644 --- a/test/IRac_test.cpp +++ b/test/IRac_test.cpp @@ -1076,7 +1076,7 @@ TEST(TestIRac, Kelvinator) { char expected[] = "Power: On, Mode: 1 (Cool), Temp: 19C, Fan: 3 (Medium), Turbo: Off, " "Quiet: Off, XFan: On, Ion: On, Light: On, " - "Swing(H): Off, Swing(V): Off"; + "Swing(H): Off, Swing(V): 0 (Off)"; ac.begin(); irac.kelvinator(&ac, diff --git a/test/ir_Kelvinator_test.cpp b/test/ir_Kelvinator_test.cpp index 73ad6581d..e4a335db0 100644 --- a/test/ir_Kelvinator_test.cpp +++ b/test/ir_Kelvinator_test.cpp @@ -251,23 +251,43 @@ TEST(TestKelvinatorClass, VaneSwing) { irkelv.begin(); irkelv.setSwingHorizontal(true); - irkelv.setSwingVertical(false); + EXPECT_TRUE(irkelv.getSwingHorizontal()); - irkelv.setSwingHorizontal(true); + EXPECT_FALSE(irkelv.getSwingVerticalAuto()); + EXPECT_EQ(kKelvinatorSwingVOff, irkelv.getSwingVerticalPosition()); + + irkelv.setSwingVertical(true, kKelvinatorSwingVAuto); + EXPECT_TRUE(irkelv.getSwingVerticalAuto()); + EXPECT_EQ(kKelvinatorSwingVAuto, irkelv.getSwingVerticalPosition()); EXPECT_TRUE(irkelv.getSwingHorizontal()); - EXPECT_FALSE(irkelv.getSwingVertical()); - irkelv.setSwingVertical(true); + irkelv.setSwingVertical(false, kKelvinatorSwingVMiddle); + EXPECT_FALSE(irkelv.getSwingVerticalAuto()); + EXPECT_EQ(kKelvinatorSwingVMiddle, irkelv.getSwingVerticalPosition()); EXPECT_TRUE(irkelv.getSwingHorizontal()); - EXPECT_TRUE(irkelv.getSwingVertical()); irkelv.setSwingHorizontal(false); EXPECT_FALSE(irkelv.getSwingHorizontal()); - EXPECT_TRUE(irkelv.getSwingVertical()); - irkelv.setSwingVertical(false); + irkelv.setSwingVertical(true, kKelvinatorSwingVLowAuto); + EXPECT_TRUE(irkelv.getSwingVerticalAuto()); + EXPECT_EQ(kKelvinatorSwingVLowAuto, irkelv.getSwingVerticalPosition()); EXPECT_FALSE(irkelv.getSwingHorizontal()); - EXPECT_FALSE(irkelv.getSwingVertical()); + + // Out of bounds. + irkelv.setSwingVertical(false, 255); + EXPECT_FALSE(irkelv.getSwingVerticalAuto()); + EXPECT_EQ(kKelvinatorSwingVOff, irkelv.getSwingVerticalPosition()); + irkelv.setSwingVertical(false, kKelvinatorSwingVAuto); + EXPECT_FALSE(irkelv.getSwingVerticalAuto()); + EXPECT_EQ(kKelvinatorSwingVOff, irkelv.getSwingVerticalPosition()); + + irkelv.setSwingVertical(true, 255); + EXPECT_TRUE(irkelv.getSwingVerticalAuto()); + EXPECT_EQ(kKelvinatorSwingVAuto, irkelv.getSwingVerticalPosition()); + irkelv.setSwingVertical(true, kKelvinatorSwingVLowest); + EXPECT_TRUE(irkelv.getSwingVerticalAuto()); + EXPECT_EQ(kKelvinatorSwingVAuto, irkelv.getSwingVerticalPosition()); } TEST(TestKelvinatorClass, QuietMode) { @@ -425,7 +445,7 @@ TEST(TestKelvinatorClass, HumanReadable) { EXPECT_EQ( "Power: Off, Mode: 0 (Auto), Temp: 16C, Fan: 0 (Auto), Turbo: Off, " "Quiet: Off, XFan: Off, Ion: Off, Light: Off, " - "Swing(H): Off, Swing(V): Off", + "Swing(H): Off, Swing(V): 0 (Off)", irkelv.toString()); irkelv.on(); irkelv.setMode(kKelvinatorCool); @@ -438,7 +458,7 @@ TEST(TestKelvinatorClass, HumanReadable) { EXPECT_EQ( "Power: On, Mode: 1 (Cool), Temp: 25C, Fan: 5 (High), Turbo: Off, " "Quiet: Off, XFan: On, Ion: On, Light: On, " - "Swing(H): On, Swing(V): Off", + "Swing(H): On, Swing(V): 0 (Off)", irkelv.toString()); } @@ -451,7 +471,7 @@ TEST(TestKelvinatorClass, MessageConstuction) { irkelv.setFan(1); irkelv.setMode(kKelvinatorCool); irkelv.setTemp(27); - irkelv.setSwingVertical(false); + irkelv.setSwingVertical(false, kKelvinatorSwingVOff); irkelv.setSwingHorizontal(true); irkelv.setIonFilter(true); irkelv.setQuiet(false); @@ -464,7 +484,7 @@ TEST(TestKelvinatorClass, MessageConstuction) { EXPECT_EQ(1, irkelv.getFan()); EXPECT_EQ(kKelvinatorCool, irkelv.getMode()); EXPECT_EQ(27, irkelv.getTemp()); - EXPECT_FALSE(irkelv.getSwingVertical()); + EXPECT_FALSE(irkelv.getSwingVerticalAuto()); EXPECT_TRUE(irkelv.getSwingHorizontal()); EXPECT_TRUE(irkelv.getIonFilter()); EXPECT_FALSE(irkelv.getQuiet()); @@ -523,7 +543,7 @@ TEST(TestDecodeKelvinator, NormalSynthetic) { EXPECT_EQ( "Power: On, Mode: 1 (Cool), Temp: 27C, Fan: 1 (Low), Turbo: Off, " "Quiet: Off, XFan: On, Ion: Off, Light: Off, " - "Swing(H): Off, Swing(V): Off", + "Swing(H): Off, Swing(V): 0 (Off)", IRAcUtils::resultAcToString(&irsend.capture)); stdAc::state_t r, p; ASSERT_TRUE(IRAcUtils::decodeToState(&irsend.capture, &r, &p)); @@ -541,7 +561,7 @@ TEST(TestKelvinatorClass, toCommon) { ac.setTurbo(true); ac.setLight(true); ac.setSwingHorizontal(false); - ac.setSwingVertical(true); + ac.setSwingVertical(true, kKelvinatorSwingVAuto); // Now test it. ASSERT_EQ(decode_type_t::KELVINATOR, ac.toCommon().protocol); diff --git a/tools/code_to_raw_test.sh b/tools/code_to_raw_test.sh index fd587f2ad..c95b3da8b 100755 --- a/tools/code_to_raw_test.sh +++ b/tools/code_to_raw_test.sh @@ -56,7 +56,7 @@ unittest_success "${CODE_TO_RAW} --protocol SAMSUNG --code 0xE0E09966" "${OUT}" read -r -d '' OUT << xEOMx Code type: 18 (KELVINATOR) Code bits: 128 -Description: Power: On, Mode: 1 (Cool), Temp: 27C, Fan: 1 (Low), Turbo: Off, Quiet: Off, XFan: On, Ion: Off, Light: Off, Swing(H): Off, Swing(V): Off +Description: Power: On, Mode: 1 (Cool), Temp: 27C, Fan: 1 (Low), Turbo: Off, Quiet: Off, XFan: On, Ion: Off, Light: Off, Swing(H): Off, Swing(V): 0 (Off) uint16_t rawData[280] = {9010, 4504, 680, 1530, 680, 510, 680, 510, 680, 1530, 680, 1530, 680, 510, 680, 510, 680, 510, 680, 1530, 680, 1530, 680, 510, 680, 1530, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 1530, 680, 510, 680, 510, 680, 510, 680, 510, 680, 1530, 680, 510, 680, 1530, 680, 510, 680, 510, 680, 1530, 680, 510, 680, 19974, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 1530, 680, 1530, 680, 1530, 680, 39950, 9010, 4504, 680, 1530, 680, 510, 680, 510, 680, 1530, 680, 1530, 680, 510, 680, 510, 680, 510, 680, 1530, 680, 1530, 680, 510, 680, 1530, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 1530, 680, 510, 680, 510, 680, 510, 680, 510, 680, 1530, 680, 1530, 680, 1530, 680, 510, 680, 510, 680, 1530, 680, 510, 680, 19974, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 1530, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 1530, 680, 1530, 680, 1530, 680, 1530, 680, 39950 }; // KELVINATOR uint8_t state[16] = {0x19, 0x0B, 0x80, 0x50, 0x00, 0x00, 0x00, 0xE0, 0x19, 0x0B, 0x80, 0x70, 0x00, 0x00, 0x10, 0xF0};