From 9a473c94cbe199844cd3ae032f56575c08d45f84 Mon Sep 17 00:00:00 2001 From: David Conran Date: Fri, 23 Jul 2021 06:12:09 +1000 Subject: [PATCH] Daikin2: Add support for Humidity setting/operation. (#1540) * Allow setting known humidity percentages via `setHumidity()` & `getHumidity()` * Report the setting correctly in `toString(). * Enforce it being available only in Heat & Dry Modes. * Change temp to Max when it's in operation. * Misc code style cleanups. * Update unit tests, and add coverage for new features. * Update supported models. Fixes #1535 --- src/ir_Daikin.cpp | 190 ++++++++++++++++++---------------------- src/ir_Daikin.h | 21 ++++- test/IRac_test.cpp | 3 +- test/ir_Daikin_test.cpp | 141 ++++++++++++++++++++++++++--- 4 files changed, 234 insertions(+), 121 deletions(-) diff --git a/src/ir_Daikin.cpp b/src/ir_Daikin.cpp index 062af2749..723b888eb 100644 --- a/src/ir_Daikin.cpp +++ b/src/ir_Daikin.cpp @@ -16,6 +16,7 @@ /// @see Daikin160 https://github.com/crankyoldgit/IRremoteESP8266/issues/731 /// @see Daikin2 https://docs.google.com/spreadsheets/d/1f8EGfIbBUo2B-CzUFdrgKQprWakoYNKM80IKZN4KXQE/edit#gid=236366525&range=B25:D32 /// @see Daikin2 https://github.com/crankyoldgit/IRremoteESP8266/issues/582 +/// @see Daikin2 https://github.com/crankyoldgit/IRremoteESP8266/issues/1535 /// @see Daikin2 https://www.daikin.co.nz/sites/default/files/daikin-split-system-US7-FTXZ25-50NV1B.pdf /// @see Daikin216 https://github.com/crankyoldgit/IRremoteESP8266/issues/689 /// @see Daikin216 https://github.com/danny-source/Arduino_DY_IRDaikin @@ -795,15 +796,11 @@ void IRDaikin2::setPower(const bool on) { /// Get the value of the current power setting. /// @return true, the setting is on. false, the setting is off. -bool IRDaikin2::getPower(void) const { - return _.Power && !_.Power2; -} +bool IRDaikin2::getPower(void) const { return _.Power && !_.Power2; } /// Get the operating mode setting of the A/C. /// @return The current operating mode setting. -uint8_t IRDaikin2::getMode(void) const { - return _.Mode; -} +uint8_t IRDaikin2::getMode(void) const { return _.Mode; } /// Set the operating mode of the A/C. /// @param[in] desired_mode The desired operating mode. @@ -819,6 +816,7 @@ void IRDaikin2::setMode(const uint8_t desired_mode) { _.Mode = mode; // Redo the temp setting as Cool mode has a different min temp. if (mode == kDaikinCool) setTemp(getTemp()); + setHumidity(getHumidity()); // Make sure the humidity is okay for this mode. } /// Set the temperature. @@ -829,6 +827,8 @@ void IRDaikin2::setTemp(const uint8_t desired) { (_.Mode == kDaikinCool) ? kDaikin2MinCoolTemp : kDaikinMinTemp, desired); _.Temp = std::min(kDaikinMaxTemp, temp); + // If the humidity setting is in use, the temp is a fixed value. + if (_.HumidOn) _.Temp = kDaikinMaxTemp; } /// Get the current temperature setting. @@ -880,9 +880,7 @@ void IRDaikin2::setSwingVertical(const uint8_t position) { /// Get the Vertical Swing mode of the A/C. /// @return The native position/mode setting. -uint8_t IRDaikin2::getSwingVertical(void) const { - return _.SwingV; -} +uint8_t IRDaikin2::getSwingVertical(void) const { return _.SwingV; } /// Convert a stdAc::swingv_t enum into it's native setting. /// @param[in] position The enum to be converted. @@ -938,9 +936,7 @@ void IRDaikin2::setCurrentTime(const uint16_t numMins) { /// Get the clock time to be sent to the A/C unit. /// @return The number of minutes past midnight. -uint16_t IRDaikin2::getCurrentTime(void) const { - return _.CurrentTime; -} +uint16_t IRDaikin2::getCurrentTime(void) const { return _.CurrentTime; } /// Set the enable status & time of the On Timer. /// @param[in] starttime The number of minutes past midnight. @@ -952,9 +948,7 @@ void IRDaikin2::enableOnTimer(const uint16_t starttime) { } /// Clear the On Timer flag. -void IRDaikin2::clearOnTimerFlag(void) { - _.OnTimer = false; -} +void IRDaikin2::clearOnTimerFlag(void) { _.OnTimer = false; } /// Disable the On timer. void IRDaikin2::disableOnTimer(void) { @@ -965,15 +959,11 @@ void IRDaikin2::disableOnTimer(void) { /// Get the On Timer time to be sent to the A/C unit. /// @return The number of minutes past midnight. -uint16_t IRDaikin2::getOnTime(void) const { - return _.OnTime; -} +uint16_t IRDaikin2::getOnTime(void) const { return _.OnTime; } /// Get the enable status of the On Timer. /// @return true, the setting is on. false, the setting is off. -bool IRDaikin2::getOnTimerEnabled(void) const { - return _.OnTimer; -} +bool IRDaikin2::getOnTimerEnabled(void) const { return _.OnTimer; } /// Set the enable status & time of the Off Timer. /// @param[in] endtime The number of minutes past midnight. @@ -992,123 +982,83 @@ void IRDaikin2::disableOffTimer(void) { /// Get the Off Timer time to be sent to the A/C unit. /// @return The number of minutes past midnight. -uint16_t IRDaikin2::getOffTime(void) const { - return _.OffTime; -} +uint16_t IRDaikin2::getOffTime(void) const { return _.OffTime; } /// Get the enable status of the Off Timer. /// @return true, the setting is on. false, the setting is off. -bool IRDaikin2::getOffTimerEnabled(void) const { - return _.OffTimer; -} +bool IRDaikin2::getOffTimerEnabled(void) const { return _.OffTimer; } /// Get the Beep status of the A/C. /// @return true, the setting is on. false, the setting is off. -uint8_t IRDaikin2::getBeep(void) const { - return _.Beep; -} +uint8_t IRDaikin2::getBeep(void) const { return _.Beep; } /// Set the Beep mode of the A/C. /// @param[in] beep true, the setting is on. false, the setting is off. -void IRDaikin2::setBeep(const uint8_t beep) { - _.Beep = beep; -} +void IRDaikin2::setBeep(const uint8_t beep) { _.Beep = beep; } /// Get the Light status of the A/C. /// @return true, the setting is on. false, the setting is off. -uint8_t IRDaikin2::getLight(void) const { - return _.Light; -} +uint8_t IRDaikin2::getLight(void) const { return _.Light; } /// Set the Light (LED) mode of the A/C. /// @param[in] light true, the setting is on. false, the setting is off. -void IRDaikin2::setLight(const uint8_t light) { - _.Light = light; -} +void IRDaikin2::setLight(const uint8_t light) { _.Light = light; } /// Set the Mould (filter) mode of the A/C. /// @param[in] on true, the setting is on. false, the setting is off. -void IRDaikin2::setMold(const bool on) { - _.Mold = on; -} +void IRDaikin2::setMold(const bool on) { _.Mold = on; } /// Get the Mould (filter) mode status of the A/C. /// @return true, the setting is on. false, the setting is off. -bool IRDaikin2::getMold(void) const { - return _.Mold; -} +bool IRDaikin2::getMold(void) const { return _.Mold; } /// Set the Auto clean mode of the A/C. /// @param[in] on true, the setting is on. false, the setting is off. -void IRDaikin2::setClean(const bool on) { - _.Clean = on; -} +void IRDaikin2::setClean(const bool on) { _.Clean = on; } /// Get the Auto Clean mode status of the A/C. /// @return true, the setting is on. false, the setting is off. -bool IRDaikin2::getClean(void) const { - return _.Clean; -} +bool IRDaikin2::getClean(void) const { return _.Clean; } /// Set the Fresh Air mode of the A/C. /// @param[in] on true, the setting is on. false, the setting is off. -void IRDaikin2::setFreshAir(const bool on) { - _.FreshAir = on; -} +void IRDaikin2::setFreshAir(const bool on) { _.FreshAir = on; } /// Get the Fresh Air mode status of the A/C. /// @return true, the setting is on. false, the setting is off. -bool IRDaikin2::getFreshAir(void) const { - return _.FreshAir; -} +bool IRDaikin2::getFreshAir(void) const { return _.FreshAir; } /// Set the (High) Fresh Air mode of the A/C. /// @param[in] on true, the setting is on. false, the setting is off. -void IRDaikin2::setFreshAirHigh(const bool on) { - _.FreshAirHigh = on; -} +void IRDaikin2::setFreshAirHigh(const bool on) { _.FreshAirHigh = on; } /// Get the (High) Fresh Air mode status of the A/C. /// @return true, the setting is on. false, the setting is off. -bool IRDaikin2::getFreshAirHigh(void) const { - return _.FreshAirHigh; -} +bool IRDaikin2::getFreshAirHigh(void) const { return _.FreshAirHigh; } /// Set the Automatic Eye (Sensor) mode of the A/C. /// @param[in] on true, the setting is on. false, the setting is off. -void IRDaikin2::setEyeAuto(bool on) { - _.EyeAuto = on; -} +void IRDaikin2::setEyeAuto(bool on) { _.EyeAuto = on; } /// Get the Automaitc Eye (Sensor) mode status of the A/C. /// @return true, the setting is on. false, the setting is off. -bool IRDaikin2::getEyeAuto(void) const { - return _.EyeAuto; -} +bool IRDaikin2::getEyeAuto(void) const { return _.EyeAuto; } /// Set the Eye (Sensor) mode of the A/C. /// @param[in] on true, the setting is on. false, the setting is off. -void IRDaikin2::setEye(bool on) { - _.Eye = on; -} +void IRDaikin2::setEye(bool on) { _.Eye = on; } /// Get the Eye (Sensor) mode status of the A/C. /// @return true, the setting is on. false, the setting is off. -bool IRDaikin2::getEye(void) const { - return _.Eye; -} +bool IRDaikin2::getEye(void) const { return _.Eye; } /// Set the Economy mode of the A/C. /// @param[in] on true, the setting is on. false, the setting is off. -void IRDaikin2::setEcono(bool on) { - _.Econo = on; -} +void IRDaikin2::setEcono(bool on) { _.Econo = on; } /// Get the Economical mode of the A/C. /// @return true, the setting is on. false, the setting is off. -bool IRDaikin2::getEcono(void) const { - return _.Econo; -} +bool IRDaikin2::getEcono(void) const { return _.Econo; } /// Set the enable status & time of the Sleep Timer. /// @param[in] sleeptime The number of minutes past midnight. @@ -1120,26 +1070,18 @@ void IRDaikin2::enableSleepTimer(const uint16_t sleeptime) { } /// Clear the sleep timer flag. -void IRDaikin2::clearSleepTimerFlag(void) { - _.SleepTimer = false; -} +void IRDaikin2::clearSleepTimerFlag(void) { _.SleepTimer = false; } /// Disable the sleep timer. -void IRDaikin2::disableSleepTimer(void) { - disableOnTimer(); -} +void IRDaikin2::disableSleepTimer(void) { disableOnTimer(); } /// Get the Sleep Timer time to be sent to the A/C unit. /// @return The number of minutes past midnight. -uint16_t IRDaikin2::getSleepTime(void) const { - return getOnTime(); -} +uint16_t IRDaikin2::getSleepTime(void) const { return getOnTime(); } /// Get the Sleep timer enabled status of the A/C. /// @return true, the setting is on. false, the setting is off. -bool IRDaikin2::getSleepTimerEnabled(void) const { - return _.SleepTimer; -} +bool IRDaikin2::getSleepTimerEnabled(void) const { return _.SleepTimer; } /// Set the Quiet mode of the A/C. /// @param[in] on true, the setting is on. false, the setting is off. @@ -1151,9 +1093,7 @@ void IRDaikin2::setQuiet(const bool on) { /// Get the Quiet mode status of the A/C. /// @return true, the setting is on. false, the setting is off. -bool IRDaikin2::getQuiet(void) const { - return _.Quiet; -} +bool IRDaikin2::getQuiet(void) const { return _.Quiet; } /// Set the Powerful (Turbo) mode of the A/C. /// @param[in] on true, the setting is on. false, the setting is off. @@ -1165,20 +1105,49 @@ void IRDaikin2::setPowerful(const bool on) { /// Get the Powerful (Turbo) mode of the A/C. /// @return true, the setting is on. false, the setting is off. -bool IRDaikin2::getPowerful(void) const { - return _.Powerful; -} +bool IRDaikin2::getPowerful(void) const { return _.Powerful; } /// Set the Purify (Filter) mode of the A/C. /// @param[in] on true, the setting is on. false, the setting is off. -void IRDaikin2::setPurify(const bool on) { - _.Purify = on; -} +void IRDaikin2::setPurify(const bool on) { _.Purify = on; } /// Get the Purify (Filter) mode status of the A/C. /// @return true, the setting is on. false, the setting is off. -bool IRDaikin2::getPurify(void) const { - return _.Purify; +bool IRDaikin2::getPurify(void) const { return _.Purify; } + +/// Get the Humidity percentage setting of the A/C. +/// @return The setting percentage. 255 is Automatic. 0 is Off. +uint8_t IRDaikin2::getHumidity(void) const { return _.Humidity; } + +/// Set the Humidity percentage setting of the A/C. +/// @param[in] percent Percentage humidty. 255 is Auto. 0 is Off. +/// @note Only available in Dry & Heat modes, otherwise it is Off. +void IRDaikin2::setHumidity(const uint8_t percent) { + _.Humidity = kDaikin2HumidityOff; // Default to off. + switch (getMode()) { + case kDaikinHeat: + switch (percent) { + case kDaikin2HumidityOff: + case kDaikin2HumidityHeatLow: + case kDaikin2HumidityHeatMedium: + case kDaikin2HumidityHeatHigh: + case kDaikin2HumidityAuto: + _.Humidity = percent; + } + break; + case kDaikinDry: + switch (percent) { + case kDaikin2HumidityOff: + case kDaikin2HumidityDryLow: + case kDaikin2HumidityDryMedium: + case kDaikin2HumidityDryHigh: + case kDaikin2HumidityAuto: + _.Humidity = percent; + } + break; + } + _.HumidOn = (_.Humidity != kDaikin2HumidityOff); // Enabled? + setTemp(getTemp()); // Adjust the temperature if we need to. } /// Convert a stdAc::opmode_t enum into its native mode. @@ -1257,7 +1226,7 @@ stdAc::state_t IRDaikin2::toCommon(void) const { /// @return A human readable string. String IRDaikin2::toString(void) const { String result = ""; - result.reserve(310); // Reserve some heap for the string to reduce fragging. + result.reserve(330); // Reserve some heap for the string to reduce fragging. result += addBoolToString(getPower(), kPowerStr, false); result += addModeToString(_.Mode, kDaikinAuto, kDaikinCool, kDaikinHeat, kDaikinDry, kDaikinFan); @@ -1336,6 +1305,17 @@ String IRDaikin2::toString(void) const { result += addBoolToString(_.Powerful, kPowerfulStr); result += addBoolToString(_.Purify, kPurifyStr); result += addBoolToString(_.Econo, kEconoStr); + result += addIntToString(_.Humidity, kHumidStr); + switch (_.Humidity) { + case kDaikin2HumidityOff: + case kDaikin2HumidityAuto: + result += kSpaceLBraceStr; + result += _.Humidity ? kAutoStr : kOffStr; + result += ')'; + break; + default: + result += '%'; + } return result; } diff --git a/src/ir_Daikin.h b/src/ir_Daikin.h index 297fb3995..ec00e4e2c 100644 --- a/src/ir_Daikin.h +++ b/src/ir_Daikin.h @@ -16,6 +16,7 @@ /// @see Daikin160 https://github.com/crankyoldgit/IRremoteESP8266/issues/731 /// @see Daikin2 https://docs.google.com/spreadsheets/d/1f8EGfIbBUo2B-CzUFdrgKQprWakoYNKM80IKZN4KXQE/edit#gid=236366525&range=B25:D32 /// @see Daikin2 https://github.com/crankyoldgit/IRremoteESP8266/issues/582 +/// @see Daikin2 https://github.com/crankyoldgit/IRremoteESP8266/issues/1535 /// @see Daikin2 https://www.daikin.co.nz/sites/default/files/daikin-split-system-US7-FTXZ25-50NV1B.pdf /// @see Daikin216 https://github.com/crankyoldgit/IRremoteESP8266/issues/689 /// @see Daikin216 https://github.com/danny-source/Arduino_DY_IRDaikin @@ -227,9 +228,10 @@ union Daikin2Protocol{ uint64_t :1; // Byte 26 uint64_t :1; - uint64_t Temp :7; + uint64_t Temp :6; + uint64_t HumidOn :1; // Byte 27 - uint64_t :8; + uint64_t Humidity :8; // Byte 28 uint64_t :4; uint64_t Fan :4; @@ -299,6 +301,19 @@ const uint8_t kDaikin2SwingHAuto = 0xBE; // A.k.a "Swing" const uint8_t kDaikin2SwingHOff = 0xBF; const uint8_t kDaikin2SwingHSwing = kDaikin2SwingHAuto; +// Ref: +// https://github.com/crankyoldgit/IRremoteESP8266/issues/1535#issuecomment-882092486 +// https://docs.google.com/spreadsheets/d/1kxHgFqiUB9ETXYEkszAIN5gE-t2ykvnPCnOV-sPUE0A/edit?usp=sharing +const uint8_t kDaikin2HumidityOff = 0x00; +const uint8_t kDaikin2HumidityHeatLow = 0x28; // Humidify (Heat) only (40%?) +const uint8_t kDaikin2HumidityHeatMedium = 0x2D; // Humidify (Heat) only (45%?) +const uint8_t kDaikin2HumidityHeatHigh = 0x32; // Humidify (Heat) only (50%?) +const uint8_t kDaikin2HumidityDryLow = 0x32; // Dry only (50%?) +const uint8_t kDaikin2HumidityDryMedium = 0x37; // Dry only (55%?) +const uint8_t kDaikin2HumidityDryHigh = 0x3C; // Dry only (60%?) +const uint8_t kDaikin2HumidityAuto = 0xFF; + + const uint8_t kDaikin2MinCoolTemp = 18; // Min temp (in C) when in Cool mode. /// Native representation of a Daikin216 A/C message. @@ -805,6 +820,8 @@ class IRDaikin2 { bool getFreshAir(void) const; void setFreshAirHigh(const bool on); bool getFreshAirHigh(void) const; + uint8_t getHumidity(void) const; + void setHumidity(const uint8_t percent); uint8_t* getRaw(void); void setRaw(const uint8_t new_code[]); static bool validChecksum(uint8_t state[], diff --git a/test/IRac_test.cpp b/test/IRac_test.cpp index b302b150c..b03cbcba8 100644 --- a/test/IRac_test.cpp +++ b/test/IRac_test.cpp @@ -399,7 +399,8 @@ TEST(TestIRac, Daikin2) { "Swing(V): 14 (Off), Swing(H): 170 (Middle), Clock: 00:00, " "On Timer: Off, Off Timer: Off, Sleep Timer: Off, Beep: 2 (Loud), " "Light: 1 (High), Mould: On, Clean: On, Fresh: Off, Eye: Off, " - "Eye Auto: Off, Quiet: Off, Powerful: Off, Purify: On, Econo: Off"; + "Eye Auto: Off, Quiet: Off, Powerful: Off, Purify: On, Econo: Off, " + "Humid: 0 (Off)"; ac.begin(); irac.daikin2(&ac, diff --git a/test/ir_Daikin_test.cpp b/test/ir_Daikin_test.cpp index 925f91ddb..851272fa0 100644 --- a/test/ir_Daikin_test.cpp +++ b/test/ir_Daikin_test.cpp @@ -1037,7 +1037,7 @@ TEST(TestDecodeDaikin2, SyntheticExample) { "Clock: 14:50, On Timer: Off, Off Timer: Off, Sleep Timer: Off, " "Beep: 1 (Quiet), Light: 3 (Off), Mould: On, Clean: On, Fresh: Off, " "Eye: Off, Eye Auto: Off, Quiet: Off, Powerful: Off, Purify: Off, " - "Econo: Off", + "Econo: Off, Humid: 0 (Off)", ac.toString()); } @@ -1448,7 +1448,8 @@ TEST(TestDaikin2Class, HumanReadable) { "Swing(V): 15 (Auto), Swing(H): 190 (Auto), Clock: 12:34, " "On Timer: Off, Off Timer: 20:00, Sleep Timer: 04:00, Beep: 2 (Loud), " "Light: 2 (Low), Mould: On, Clean: Off, Fresh: On, Eye: On, " - "Eye Auto: On, Quiet: Off, Powerful: On, Purify: On, Econo: Off", + "Eye Auto: On, Quiet: Off, Powerful: On, Purify: On, Econo: Off, " + "Humid: 0 (Off)", ac.toString()); ac.setQuiet(true); ac.setMode(kDaikinHeat); @@ -1463,7 +1464,8 @@ TEST(TestDaikin2Class, HumanReadable) { "Swing(V): 15 (Auto), Swing(H): 190 (Auto), Clock: 23:45, " "On Timer: 09:11, Off Timer: 20:00, Sleep Timer: Off, Beep: 1 (Quiet), " "Light: 1 (High), Mould: On, Clean: Off, Fresh: On, Eye: On, " - "Eye Auto: On, Quiet: On, Powerful: Off, Purify: On, Econo: Off", + "Eye Auto: On, Quiet: On, Powerful: Off, Purify: On, Econo: Off, " + "Humid: 0 (Off)", ac.toString()); } @@ -1505,7 +1507,7 @@ TEST(TestDaikin2Class, KnownConstruction) { "Clock: 14:50, On Timer: Off, Off Timer: Off, Sleep Timer: Off, " "Beep: 1 (Quiet), Light: 3 (Off), Mould: On, Clean: On, Fresh: Off, " "Eye: Off, Eye Auto: Off, Quiet: Off, Powerful: Off, Purify: Off, " - "Econo: Off", + "Econo: Off, Humid: 0 (Off)", ac.toString()); EXPECT_STATE_EQ(expectedState, ac.getRaw(), kDaikin2Bits); } @@ -1571,7 +1573,8 @@ TEST(TestDecodeDaikin2, Issue582DeepDecodeExample) { "Swing(V): 14 (Off), Swing(H): 190 (Auto), Clock: 09:20, " "On Timer: Off, Off Timer: Off, Sleep Timer: Off, Beep: 3 (Off), " "Light: 3 (Off), Mould: On, Clean: On, Fresh: Off, Eye: On, " - "Eye Auto: Off, Quiet: Off, Powerful: Off, Purify: On, Econo: Off", + "Eye Auto: Off, Quiet: Off, Powerful: Off, Purify: On, Econo: Off, " + "Humid: 0 (Off)", ac.toString()); } @@ -1596,7 +1599,8 @@ TEST(TestDecodeDaikin2, Issue582PowerfulEconoFix) { "Swing(V): 14 (Off), Swing(H): 190 (Auto), Clock: 13:46, " "On Timer: Off, Off Timer: Off, Sleep Timer: Off, Beep: 3 (Off), " "Light: 3 (Off), Mould: On, Clean: On, Fresh: Off, Eye: Off, " - "Eye Auto: Off, Quiet: Off, Powerful: On, Purify: On, Econo: Off", + "Eye Auto: Off, Quiet: Off, Powerful: On, Purify: On, Econo: Off, " + "Humid: 0 (Off)", ac.toString()); ac.setRaw(PowerfulOff); ASSERT_FALSE(ac.getPowerful()); @@ -1605,7 +1609,8 @@ TEST(TestDecodeDaikin2, Issue582PowerfulEconoFix) { "Swing(V): 14 (Off), Swing(H): 190 (Auto), Clock: 13:46, " "On Timer: Off, Off Timer: Off, Sleep Timer: Off, Beep: 3 (Off), " "Light: 3 (Off), Mould: On, Clean: On, Fresh: Off, Eye: Off, " - "Eye Auto: Off, Quiet: Off, Powerful: Off, Purify: On, Econo: Off", + "Eye Auto: Off, Quiet: Off, Powerful: Off, Purify: On, Econo: Off, " + "Humid: 0 (Off)", ac.toString()); const uint8_t EconoOn[39] = { @@ -1625,7 +1630,8 @@ TEST(TestDecodeDaikin2, Issue582PowerfulEconoFix) { "Swing(V): 14 (Off), Swing(H): 190 (Auto), Clock: 13:47, " "On Timer: Off, Off Timer: Off, Sleep Timer: Off, Beep: 3 (Off), " "Light: 3 (Off), Mould: On, Clean: On, Fresh: Off, Eye: Off, " - "Eye Auto: Off, Quiet: Off, Powerful: Off, Purify: On, Econo: On", + "Eye Auto: Off, Quiet: Off, Powerful: Off, Purify: On, Econo: On, " + "Humid: 0 (Off)", ac.toString()); ac.setRaw(EconoOff); ASSERT_FALSE(ac.getEcono()); @@ -1634,7 +1640,8 @@ TEST(TestDecodeDaikin2, Issue582PowerfulEconoFix) { "Swing(V): 14 (Off), Swing(H): 190 (Auto), Clock: 13:47, " "On Timer: Off, Off Timer: Off, Sleep Timer: Off, Beep: 3 (Off), " "Light: 3 (Off), Mould: On, Clean: On, Fresh: Off, Eye: Off, " - "Eye Auto: Off, Quiet: Off, Powerful: Off, Purify: On, Econo: Off", + "Eye Auto: Off, Quiet: Off, Powerful: Off, Purify: On, Econo: Off, " + "Humid: 0 (Off)", ac.toString()); } @@ -3033,7 +3040,8 @@ TEST(TestDaikin2ClassNew, Issue908) { "Swing(V): 3 (Upper Middle), Swing(H): 170 (Middle), Clock: 09:46, " "On Timer: Off, Off Timer: Off, Sleep Timer: Off, Beep: 2 (Loud), " "Light: 3 (Off), Mould: On, Clean: On, Fresh: Off, Eye: Off, " - "Eye Auto: Off, Quiet: Off, Powerful: Off, Purify: On, Econo: Off", + "Eye Auto: Off, Quiet: Off, Powerful: Off, Purify: On, Econo: Off, " + "Humid: 0 (Off)", ac.toString()); ASSERT_EQ(kDaikinFanMed, ac.getFan()); ASSERT_EQ(stdAc::fanspeed_t::kMedium, ac.toCommon().fanspeed); @@ -3051,7 +3059,8 @@ TEST(TestDaikin2ClassNew, Issue908) { "Swing(V): 3 (Upper Middle), Swing(H): 170 (Middle), Clock: 09:57, " "On Timer: Off, Off Timer: Off, Sleep Timer: Off, Beep: 2 (Loud), " "Light: 3 (Off), Mould: On, Clean: On, Fresh: Off, Eye: Off, " - "Eye Auto: Off, Quiet: Off, Powerful: Off, Purify: On, Econo: Off", + "Eye Auto: Off, Quiet: Off, Powerful: Off, Purify: On, Econo: Off, " + "Humid: 0 (Off)", ac.toString()); ASSERT_EQ(3, ac.getSwingVertical()); ASSERT_EQ(stdAc::swingv_t::kMiddle, ac.toCommon().swingv); @@ -3396,7 +3405,8 @@ TEST(TestDaikin2Class, Issue1035) { "Swing(V): 1 (Highest), Swing(H): 190 (Auto), Clock: 13:09, " "On Timer: Off, Off Timer: Off, Sleep Timer: Off, Beep: 2 (Loud), " "Light: 1 (High), Mould: On, Clean: On, Fresh: On, Eye: Off, " - "Eye Auto: Off, Quiet: Off, Powerful: Off, Purify: On, Econo: Off", + "Eye Auto: Off, Quiet: Off, Powerful: Off, Purify: On, Econo: Off, " + "Humid: 0 (Off)", ac.toString()); ASSERT_TRUE(ac.toCommon().power); ASSERT_NE(ac.toCommon().mode, stdAc::opmode_t::kOff); @@ -3407,7 +3417,8 @@ TEST(TestDaikin2Class, Issue1035) { "Swing(V): 1 (Highest), Swing(H): 190 (Auto), Clock: 13:09, " "On Timer: Off, Off Timer: Off, Sleep Timer: Off, Beep: 2 (Loud), " "Light: 1 (High), Mould: On, Clean: On, Fresh: On, Eye: Off, " - "Eye Auto: Off, Quiet: Off, Powerful: Off, Purify: On, Econo: Off", + "Eye Auto: Off, Quiet: Off, Powerful: Off, Purify: On, Econo: Off, " + "Humid: 0 (Off)", ac.toString()); ASSERT_FALSE(ac.toCommon().power); } @@ -3727,3 +3738,107 @@ TEST(TestDecodeDaikin64, Issue1092) { stdAc::state_t result, prev; ASSERT_TRUE(IRAcUtils::decodeToState(&irsend.capture, &result, &prev)); } + +// Test the humidity modes & settings. +TEST(TestDaikin2Class, Humidity) { + IRDaikin2 ac(kGpioUnused); + + EXPECT_FALSE(ac._.HumidOn); + EXPECT_EQ(kDaikin2HumidityOff, ac.getHumidity()); + EXPECT_NE(kDaikinMaxTemp, ac.getTemp()); + + // Test it can't be changed when NOT in Heat or Dry mode. + EXPECT_NE(kDaikinHeat, ac.getMode()); + EXPECT_NE(kDaikinDry, ac.getMode()); + ac.setHumidity(kDaikin2HumidityAuto); + EXPECT_FALSE(ac._.HumidOn); + EXPECT_EQ(kDaikin2HumidityOff, ac.getHumidity()); + + // Turn on the setting. + ac.setMode(kDaikinHeat); + ac.setHumidity(kDaikin2HumidityAuto); + EXPECT_EQ(kDaikinHeat, ac.getMode()); + EXPECT_TRUE(ac._.HumidOn); + EXPECT_EQ(kDaikin2HumidityAuto, ac._.Humidity); + EXPECT_EQ(kDaikinMaxTemp, ac.getTemp()); + + // Check it doesn't allow Dry only percentages. + ac.setHumidity(kDaikin2HumidityDryMedium); // Invalid + EXPECT_EQ(kDaikinHeat, ac.getMode()); + EXPECT_FALSE(ac._.HumidOn); + EXPECT_EQ(kDaikin2HumidityOff, ac.getHumidity()); + + // Now Dry modes/settings. + ac.setMode(kDaikinDry); + ac.setHumidity(kDaikin2HumidityAuto); + EXPECT_EQ(kDaikinDry, ac.getMode()); + EXPECT_TRUE(ac._.HumidOn); + EXPECT_EQ(kDaikin2HumidityAuto, ac.getHumidity()); + EXPECT_EQ(kDaikinMaxTemp, ac.getTemp()); + + ac.setHumidity(kDaikin2HumidityDryMedium); // Valid + EXPECT_EQ(kDaikinDry, ac.getMode()); + EXPECT_TRUE(ac._.HumidOn); + EXPECT_EQ(kDaikin2HumidityDryMedium, ac.getHumidity()); + EXPECT_EQ(kDaikinMaxTemp, ac.getTemp()); + + // Confirm we can't change the temp while in Humidity mode etc. + ac.setTemp(kDaikinMaxTemp - 1); + EXPECT_EQ(kDaikinMaxTemp, ac.getTemp()); + EXPECT_TRUE(ac._.HumidOn); + + // Confirm we can if it's off. + ac.setHumidity(kDaikin2HumidityOff); + ac.setTemp(kDaikinMaxTemp - 1); + EXPECT_EQ(kDaikinMaxTemp -1 , ac.getTemp()); + EXPECT_FALSE(ac._.HumidOn); + EXPECT_EQ(kDaikin2HumidityOff, ac.getHumidity()); + + // Change to a mode that's incompatible + ac.setHumidity(kDaikin2HumidityAuto); + ac.setMode(kDaikinCool); + EXPECT_FALSE(ac._.HumidOn); + EXPECT_EQ(kDaikin2HumidityOff, ac.getHumidity()); + + // Test some real codes. + + // Ref: https://docs.google.com/spreadsheets/d/1kxHgFqiUB9ETXYEkszAIN5gE-t2ykvnPCnOV-sPUE0A/edit#gid=1167536015&range=B7:AN7 + const uint8_t stateHumidAuto[kDaikin2StateLength] = { + 0x11, 0xDA, 0x27, 0x00, 0x01, 0x00, 0x40, 0x50, 0x60, 0x0C, 0x80, 0x04, + 0xB0, 0x16, 0x24, 0x00, 0x00, 0x8B, 0xCE, 0xD6, 0x11, 0xDA, 0x27, 0x00, + 0x00, 0x49, 0xC0, 0xFF, 0xA0, 0x00, 0x00, 0x06, 0x60, 0x00, 0x00, 0xC1, + 0x90, 0x60, 0xD1}; + ac.stateReset(); + ac.setRaw(stateHumidAuto); + EXPECT_TRUE(ac._.HumidOn); + EXPECT_EQ(kDaikinHeat, ac.getMode()); + EXPECT_EQ(kDaikinMaxTemp, ac.getTemp()); + EXPECT_EQ(kDaikin2HumidityAuto, ac.getHumidity()); + EXPECT_EQ( + "Power: On, Mode: 4 (Heat), Temp: 32C, Fan: 10 (Auto), " + "Swing(V): 14 (Off), Swing(H): 139 (UNKNOWN), Clock: 00:00, " + "On Timer: Off, Off Timer: Off, Sleep Timer: Off, Beep: 1 (Quiet), " + "Light: 1 (High), Mould: Off, Clean: On, Fresh: Off, Eye: Off, " + "Eye Auto: Off, Quiet: Off, Powerful: Off, Purify: On, Econo: Off, " + "Humid: 255 (Auto)", ac.toString()); + + // Ref: https://docs.google.com/spreadsheets/d/1kxHgFqiUB9ETXYEkszAIN5gE-t2ykvnPCnOV-sPUE0A/edit#gid=1966092848&range=B6:AN6 + const uint8_t stateDryStd[kDaikin2StateLength] = { + 0x11, 0xDA, 0x27, 0x00, 0x01, 0x00, 0x40, 0x50, 0x60, 0x0C, 0x80, 0x04, + 0xB0, 0x16, 0x24, 0x00, 0x00, 0x8B, 0xCE, 0xD6, 0x11, 0xDA, 0x27, 0x00, + 0x00, 0x29, 0xC0, 0x37, 0xA0, 0x00, 0x00, 0x06, 0x60, 0x00, 0x00, 0xC1, + 0x90, 0x60, 0xE9}; + ac.stateReset(); + ac.setRaw(stateDryStd); + EXPECT_TRUE(ac._.HumidOn); + EXPECT_EQ(kDaikinDry, ac.getMode()); + EXPECT_EQ(kDaikinMaxTemp, ac.getTemp()); + EXPECT_EQ(kDaikin2HumidityDryMedium, ac.getHumidity()); + EXPECT_EQ( + "Power: On, Mode: 2 (Dry), Temp: 32C, Fan: 10 (Auto), " + "Swing(V): 14 (Off), Swing(H): 139 (UNKNOWN), Clock: 00:00, " + "On Timer: Off, Off Timer: Off, Sleep Timer: Off, Beep: 1 (Quiet), " + "Light: 1 (High), Mould: Off, Clean: On, Fresh: Off, Eye: Off, " + "Eye Auto: Off, Quiet: Off, Powerful: Off, Purify: On, Econo: Off, " + "Humid: 55%", ac.toString()); +}