From 3909982a21ae5972df6a7f12a711afb231e7db40 Mon Sep 17 00:00:00 2001 From: crankyoldgit Date: Wed, 20 Oct 2021 23:11:25 +1000 Subject: [PATCH] [ELECTRA_AC] Add support for "IFeel" setting. * Add `(set|get)IFeel()` & `(set|get)IFeelTemp()` methods. * Add and update Unit test coverage. For #1644 Co-Authored-By: @Aculeasis --- src/ir_Electra.cpp | 42 ++++++++++++++++++++++-- src/ir_Electra.h | 21 ++++++++++-- test/IRac_test.cpp | 3 +- test/ir_Electra_test.cpp | 69 +++++++++++++++++++++++++++++++++++----- 4 files changed, 121 insertions(+), 14 deletions(-) diff --git a/src/ir_Electra.cpp b/src/ir_Electra.cpp index 7945cb0ad..5b547da1d 100644 --- a/src/ir_Electra.cpp +++ b/src/ir_Electra.cpp @@ -1,4 +1,4 @@ -// Copyright 2018, 2019 David Conran +// Copyright 2018-2021 David Conran /// @file /// @brief Support for Electra A/C protocols. /// @see https://github.com/ToniA/arduino-heatpumpir/blob/master/AUXHeatpumpIR.cpp @@ -310,6 +310,37 @@ bool IRElectraAc::getTurbo(void) const { return _.Turbo; } +/// Get the IFeel mode of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRElectraAc::getIFeel(void) const { return _.IFeel; } + +/// Set the IFeel mode of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRElectraAc::setIFeel(const bool on) { + _.IFeel = on; + if (_.IFeel) + // Make sure there is a reasonable value in _.IFeelTemp + setIFeelTemp(getIFeelTemp()); + else + // Clear any previous stored temp.. + _.IFeelTemp = kElectraAcIFeelMinTemp; +} + +/// Set the temperature for the IFeel mode. +/// @param[in] temp The temperature in degrees celsius. +void IRElectraAc::setIFeelTemp(const uint8_t temp) { + _.IFeelTemp = std::min(kElectraAcIFeelMaxTemp, + std::max(kElectraAcIFeelMinTemp, temp)) + + kElectraAcIFeelTempDelta; +} + +/// Get the current temperature setting for the IFeel mode. +/// @return The current setting for temp. in degrees celsius. +uint8_t IRElectraAc::getIFeelTemp(void) const { + return std::max(kElectraAcIFeelTempDelta, _.IFeelTemp) - + kElectraAcIFeelTempDelta; +} + /// Convert the current internal state into its stdAc::state_t equivalent. /// @return The stdAc equivalent of the native settings. stdAc::state_t IRElectraAc::toCommon(void) const { @@ -342,7 +373,7 @@ stdAc::state_t IRElectraAc::toCommon(void) const { /// @return A human readable string. String IRElectraAc::toString(void) const { String result = ""; - result.reserve(130); // Reserve some heap for the string to reduce fragging. + result.reserve(160); // Reserve some heap for the string to reduce fragging. result += addBoolToString(_.Power, kPowerStr, false); result += addModeToString(_.Mode, kElectraAcAuto, kElectraAcCool, kElectraAcHeat, kElectraAcDry, kElectraAcFan); @@ -355,6 +386,13 @@ String IRElectraAc::toString(void) const { result += addToggleToString(getLightToggle(), kLightStr); result += addBoolToString(_.Clean, kCleanStr); result += addBoolToString(_.Turbo, kTurboStr); + result += addBoolToString(_.IFeel, kIFeelStr); + if (_.IFeel) { + result += kCommaSpaceStr; + result += kIFeelStr; + result += ' '; + result += addTempToString(getIFeelTemp(), true, false); + } return result; } diff --git a/src/ir_Electra.h b/src/ir_Electra.h index 886a92c56..85df532ca 100644 --- a/src/ir_Electra.h +++ b/src/ir_Electra.h @@ -1,4 +1,4 @@ -// Copyright 2019 David Conran +// Copyright 2019-2021 David Conran /// @file /// @brief Support for Electra A/C protocols. /// @see https://github.com/ToniA/arduino-heatpumpir/blob/master/AUXHeatpumpIR.cpp @@ -9,6 +9,10 @@ // Brand: Electra, Model: Classic INV 17 / AXW12DCS A/C // Brand: Electra, Model: YKR-M/003E remote // Brand: Frigidaire, Model: FGPC102AB1 A/C +// Brand: Subtropic, Model: SUB-07HN1_18Y A/C +// Brand: Subtropic, Model: YKR-H/102E remote +// Brand: Centek, Model: SCT-65Q09 A/C +// Brand: Centek, Model: YKR-P/002E remote #ifndef IR_ELECTRA_H_ #define IR_ELECTRA_H_ @@ -46,10 +50,12 @@ union ElectraProtocol { uint8_t Turbo :1; uint8_t :1; // Byte 6 - uint8_t :5; + uint8_t :3; + uint8_t IFeel :1; + uint8_t :1; uint8_t Mode :3; // Byte 7 - uint8_t :8; + uint8_t IFeelTemp :8; // Byte 8 uint8_t :8; // Byte 9 @@ -93,6 +99,11 @@ const uint8_t kElectraAcLightToggleMask = 0x11; // and known OFF values of 0x08 (0b00001000) & 0x05 (0x00000101) const uint8_t kElectraAcLightToggleOff = 0x08; +// Re: Byte[7]. Or Delta == 0xA and Temperature are stored in last 6 bits, +// and bit 7 stores Unknown flag +const uint8_t kElectraAcIFeelTempDelta = 0x4A; +const uint8_t kElectraAcIFeelMinTemp = 0; // 0C +const uint8_t kElectraAcIFeelMaxTemp = 50; // 50C // Classes /// Class for handling detailed Electra A/C messages. @@ -130,6 +141,10 @@ class IRElectraAc { bool getLightToggle(void) const; void setTurbo(const bool on); bool getTurbo(void) const; + void setIFeel(const bool on); + bool getIFeel(void) const; + void setIFeelTemp(const uint8_t temp); + uint8_t getIFeelTemp(void) const; uint8_t* getRaw(void); void setRaw(const uint8_t new_code[], const uint16_t length = kElectraAcStateLength); diff --git a/test/IRac_test.cpp b/test/IRac_test.cpp index 7ff512e28..a6036dd1c 100644 --- a/test/IRac_test.cpp +++ b/test/IRac_test.cpp @@ -560,7 +560,8 @@ TEST(TestIRac, Electra) { IRrecv capture(kGpioUnused); char expected[] = "Power: On, Mode: 6 (Fan), Temp: 26C, Fan: 1 (High), " - "Swing(V): On, Swing(H): On, Light: Toggle, Clean: On, Turbo: On"; + "Swing(V): On, Swing(H): On, Light: Toggle, Clean: On, Turbo: On, " + "IFeel: Off"; ac.begin(); irac.electra(&ac, diff --git a/test/ir_Electra_test.cpp b/test/ir_Electra_test.cpp index 1fddf537b..4ba58b2fa 100644 --- a/test/ir_Electra_test.cpp +++ b/test/ir_Electra_test.cpp @@ -101,7 +101,8 @@ TEST(TestDecodeElectraAC, RealExampleDecode) { EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); EXPECT_EQ( "Power: On, Mode: 1 (Cool), Temp: 24C, Fan: 3 (Low), " - "Swing(V): Off, Swing(H): Off, Light: -, Clean: Off, Turbo: Off", + "Swing(V): Off, Swing(H): Off, Light: -, Clean: Off, Turbo: Off, " + "IFeel: Off", IRAcUtils::resultAcToString(&irsend.capture)); stdAc::state_t r, p; ASSERT_TRUE(IRAcUtils::decodeToState(&irsend.capture, &r, &p)); @@ -235,7 +236,8 @@ TEST(TestIRElectraAcClass, HumanReadable) { ac.setRaw(on_cool_32C_auto_voff); EXPECT_EQ( "Power: On, Mode: 1 (Cool), Temp: 32C, Fan: 5 (Auto), " - "Swing(V): Off, Swing(H): Off, Light: -, Clean: Off, Turbo: Off", + "Swing(V): Off, Swing(H): Off, Light: -, Clean: Off, Turbo: Off, " + "IFeel: Off", ac.toString()); uint8_t on_cool_16C_auto_voff[13] = { 0xC3, 0x47, 0xE0, 0x00, 0xA0, 0x00, 0x20, @@ -243,7 +245,8 @@ TEST(TestIRElectraAcClass, HumanReadable) { ac.setRaw(on_cool_16C_auto_voff); EXPECT_EQ( "Power: On, Mode: 1 (Cool), Temp: 16C, Fan: 5 (Auto), " - "Swing(V): Off, Swing(H): Off, Light: -, Clean: Off, Turbo: Off", + "Swing(V): Off, Swing(H): Off, Light: -, Clean: Off, Turbo: Off, " + "IFeel: Off", ac.toString()); uint8_t on_cool_16C_low_voff[13] = { 0xC3, 0x47, 0xE0, 0x00, 0x60, 0x00, 0x20, @@ -251,7 +254,8 @@ TEST(TestIRElectraAcClass, HumanReadable) { ac.setRaw(on_cool_16C_low_voff); EXPECT_EQ( "Power: On, Mode: 1 (Cool), Temp: 16C, Fan: 3 (Low), " - "Swing(V): Off, Swing(H): Off, Light: -, Clean: Off, Turbo: Off", + "Swing(V): Off, Swing(H): Off, Light: -, Clean: Off, Turbo: Off, " + "IFeel: Off", ac.toString()); } @@ -275,7 +279,8 @@ TEST(TestIRElectraAcClass, Clean) { ac.setRaw(on); EXPECT_EQ( "Power: On, Mode: 1 (Cool), Temp: 24C, Fan: 3 (Low), " - "Swing(V): Off, Swing(H): Off, Light: Toggle, Clean: On, Turbo: Off", + "Swing(V): Off, Swing(H): Off, Light: Toggle, Clean: On, Turbo: Off, " + "IFeel: Off", ac.toString()); } @@ -301,7 +306,8 @@ TEST(TestIRElectraAcClass, Turbo) { ac.setRaw(on); EXPECT_EQ( "Power: On, Mode: 1 (Cool), Temp: 24C, Fan: 3 (Low), " - "Swing(V): Off, Swing(H): Off, Light: -, Clean: Off, Turbo: On", + "Swing(V): Off, Swing(H): Off, Light: -, Clean: Off, Turbo: On, " + "IFeel: Off", ac.toString()); } @@ -325,7 +331,8 @@ TEST(TestIRElectraAcClass, LightToggle) { ac.setRaw(on); EXPECT_EQ( "Power: On, Mode: 1 (Cool), Temp: 24C, Fan: 3 (Low), " - "Swing(V): Off, Swing(H): Off, Light: Toggle, Clean: On, Turbo: Off", + "Swing(V): Off, Swing(H): Off, Light: Toggle, Clean: On, Turbo: Off, " + "IFeel: Off", ac.toString()); } @@ -352,8 +359,54 @@ TEST(TestIRElectraAcClass, ConstructKnownState) { EXPECT_EQ( "Power: On, Mode: 1 (Cool), Temp: 24C, Fan: 3 (Low), " - "Swing(V): Off, Swing(H): Off, Light: Toggle, Clean: On, Turbo: Off", + "Swing(V): Off, Swing(H): Off, Light: Toggle, Clean: On, Turbo: Off, " + "IFeel: Off", ac.toString()); EXPECT_STATE_EQ(on_cool_24C_fan1_swing_off_turbo_off_clean_on, ac.getRaw(), kElectraAcBits); } + +TEST(TestIRElectraAcClass, IFeel) { + IRElectraAc ac(kGpioUnused); + ac.stateReset(); + // Test a real example. + const uint8_t ifeel_on[kElectraAcStateLength] = { + 0xC3, 0x6F, 0xE0, 0x00, 0xA0, 0x00, 0x28, + 0x64, 0x00, 0x20, 0x00, 0x1E, 0x7C}; + ac.setRaw(ifeel_on); + EXPECT_TRUE(ac.getIFeel()); + EXPECT_EQ(26, ac.getIFeelTemp()); + EXPECT_EQ( + "Power: On, Mode: 1 (Cool), Temp: 21C, Fan: 5 (Auto), " + "Swing(V): Off, Swing(H): Off, Light: -, Clean: Off, Turbo: Off, " + "IFeel: On, IFeel Temp: 26C", + ac.toString()); + + ac.stateReset(); + EXPECT_FALSE(ac.getIFeel()); + EXPECT_EQ(kElectraAcIFeelMinTemp, ac.getIFeelTemp()); + + ac.setIFeel(true); + EXPECT_TRUE(ac.getIFeel()); + EXPECT_EQ(kElectraAcIFeelMinTemp, ac.getIFeelTemp()); + + ac.setIFeelTemp(kElectraAcIFeelMaxTemp); + EXPECT_EQ(kElectraAcIFeelMaxTemp, ac.getIFeelTemp()); + + ac.setIFeelTemp(kElectraAcIFeelMaxTemp + 1); + EXPECT_EQ(kElectraAcIFeelMaxTemp, ac.getIFeelTemp()); + + ac.setIFeel(false); + EXPECT_FALSE(ac.getIFeel()); + EXPECT_EQ(kElectraAcIFeelMinTemp, ac.getIFeelTemp()); + EXPECT_EQ(0, ac._.IFeelTemp); + + ac.setIFeel(true); + ac.setIFeelTemp(kElectraAcIFeelMinTemp); + EXPECT_TRUE(ac.getIFeel()); + EXPECT_EQ(kElectraAcIFeelMinTemp, ac.getIFeelTemp()); + + ac.setIFeelTemp(26); // Celsius + EXPECT_TRUE(ac.getIFeel()); + EXPECT_EQ(26, ac.getIFeelTemp()); +}