Skip to content

Commit

Permalink
Mitsubishi ACs: Improve handling swing/vane settings. (#1401)
Browse files Browse the repository at this point in the history
* MitsubishiAC class
  - Tweak IRac/stdAc handing of swingv/vane:
    * Native Auto as stdAc's Off. 
    * Native Swing as stdAc's Auto.

Native "auto" is the A/C works out where the vanes point.
Native "swing" is the A/C just occilates the vanes up and down constant. Thus "swing" is closest to stdAc's "auto". Mapping the unused stdAc's "off" to the native "auto" so it is at least reachable by the user via `IRac` etc. 

* Various Mitsubish A/Cs
  - Improve `toString()` output for swingv & swingh positions.
  - Reduce code duplication.

Fixes #1399
  • Loading branch information
crankyoldgit authored Feb 3, 2021
1 parent 5a84897 commit ab466e4
Show file tree
Hide file tree
Showing 4 changed files with 153 additions and 96 deletions.
192 changes: 107 additions & 85 deletions src/ir_Mitsubishi.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// Copyright 2009 Ken Shirriff
// Copyright 2017-2019 David Conran
// Copyright 2017-2021 David Conran
// Copyright 2019 Mark Kuchel
// Copyright 2018 Denes Varga

Expand All @@ -14,6 +14,8 @@
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/619
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/888
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/947
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1398
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1399
/// @see https://github.com/kuchel77

#include "ir_Mitsubishi.h"
Expand Down Expand Up @@ -87,6 +89,8 @@ using irutils::addFanToString;
using irutils::addIntToString;
using irutils::addLabeledString;
using irutils::addModeToString;
using irutils::addSwingHToString;
using irutils::addSwingVToString;
using irutils::addTempToString;
using irutils::addTempFloatToString;
using irutils::minsToString;
Expand Down Expand Up @@ -630,14 +634,23 @@ uint8_t IRMitsubishiAC::convertFan(const stdAc::fanspeed_t speed) {
/// Convert a stdAc::swingv_t enum into it's native setting.
/// @param[in] position The enum to be converted.
/// @return The native equivalent of the enum.
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1399
/// @see https://github.com/crankyoldgit/IRremoteESP8266/pull/1401
uint8_t IRMitsubishiAC::convertSwingV(const stdAc::swingv_t position) {
switch (position) {
case stdAc::swingv_t::kHighest: return kMitsubishiAcVaneAutoMove - 6;
case stdAc::swingv_t::kHigh: return kMitsubishiAcVaneAutoMove - 5;
case stdAc::swingv_t::kMiddle: return kMitsubishiAcVaneAutoMove - 4;
case stdAc::swingv_t::kLow: return kMitsubishiAcVaneAutoMove - 3;
case stdAc::swingv_t::kLowest: return kMitsubishiAcVaneAutoMove - 2;
case stdAc::swingv_t::kAuto: return kMitsubishiAcVaneAutoMove;
case stdAc::swingv_t::kHighest: return kMitsubishiAcVaneHighest;
case stdAc::swingv_t::kHigh: return kMitsubishiAcVaneHigh;
case stdAc::swingv_t::kMiddle: return kMitsubishiAcVaneMiddle;
case stdAc::swingv_t::kLow: return kMitsubishiAcVaneLow;
case stdAc::swingv_t::kLowest: return kMitsubishiAcVaneLowest;
// These model Mitsubishi A/C have two automatic settings.
// 1. A typical up & down oscillation. (Native Swing)
// 2. The A/C determines where the best placement for the vanes, outside of
// user control. (Native Auto)
// Native "Swing" is what we consider "Auto" in stdAc. (Case 1)
case stdAc::swingv_t::kAuto: return kMitsubishiAcVaneSwing;
// Native "Auto" doesn't have a good match for this in stdAc. (Case 2)
// So we repurpose stdAc's "Off" (and anything else) to be Native Auto.
default: return kMitsubishiAcVaneAuto;
}
}
Expand All @@ -647,14 +660,14 @@ uint8_t IRMitsubishiAC::convertSwingV(const stdAc::swingv_t position) {
/// @return The native equivalent of the enum.
uint8_t IRMitsubishiAC::convertSwingH(const stdAc::swingh_t position) {
switch (position) {
case stdAc::swingh_t::kLeftMax: return kMitsubishiAcWideVaneAuto - 7;
case stdAc::swingh_t::kLeft: return kMitsubishiAcWideVaneAuto - 6;
case stdAc::swingh_t::kMiddle: return kMitsubishiAcWideVaneAuto - 5;
case stdAc::swingh_t::kRight: return kMitsubishiAcWideVaneAuto - 4;
case stdAc::swingh_t::kRightMax: return kMitsubishiAcWideVaneAuto - 3;
case stdAc::swingh_t::kWide: return kMitsubishiAcWideVaneAuto - 2;
case stdAc::swingh_t::kLeftMax: return kMitsubishiAcWideVaneLeftMax;
case stdAc::swingh_t::kLeft: return kMitsubishiAcWideVaneLeft;
case stdAc::swingh_t::kMiddle: return kMitsubishiAcWideVaneMiddle;
case stdAc::swingh_t::kRight: return kMitsubishiAcWideVaneRight;
case stdAc::swingh_t::kRightMax: return kMitsubishiAcWideVaneRightMax;
case stdAc::swingh_t::kWide: return kMitsubishiAcWideVaneWide;
case stdAc::swingh_t::kAuto: return kMitsubishiAcWideVaneAuto;
default: return kMitsubishiAcWideVaneAuto - 5;
default: return kMitsubishiAcWideVaneMiddle;
}
}

Expand Down Expand Up @@ -687,14 +700,24 @@ stdAc::fanspeed_t IRMitsubishiAC::toCommonFanSpeed(const uint8_t speed) {
/// Convert a native vertical swing postion to it's common equivalent.
/// @param[in] pos A native position to convert.
/// @return The common vertical swing position.
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1399
/// @see https://github.com/crankyoldgit/IRremoteESP8266/pull/1401
stdAc::swingv_t IRMitsubishiAC::toCommonSwingV(const uint8_t pos) {
switch (pos) {
case 1: return stdAc::swingv_t::kHighest;
case 2: return stdAc::swingv_t::kHigh;
case 3: return stdAc::swingv_t::kMiddle;
case 4: return stdAc::swingv_t::kLow;
case 5: return stdAc::swingv_t::kLowest;
default: return stdAc::swingv_t::kAuto;
case kMitsubishiAcVaneHighest: return stdAc::swingv_t::kHighest;
case kMitsubishiAcVaneHigh: return stdAc::swingv_t::kHigh;
case kMitsubishiAcVaneMiddle: return stdAc::swingv_t::kMiddle;
case kMitsubishiAcVaneLow: return stdAc::swingv_t::kLow;
case kMitsubishiAcVaneLowest: return stdAc::swingv_t::kLowest;
// These model Mitsubishi A/C have two automatic settings.
// 1. A typical up & down oscillation. (Native Swing)
// 2. The A/C determines where the best placement for the vanes, outside of
// user control. (Native Auto)
// Native "Auto" doesn't have a good match for this in stdAc. (Case 2)
// So we repurpose stdAc's "Off" to be Native Auto.
case kMitsubishiAcVaneAuto: return stdAc::swingv_t::kOff;
// Native "Swing" is what we consider "Auto" in stdAc. (Case 1)
default: return stdAc::swingv_t::kAuto;
}
}

Expand All @@ -703,13 +726,13 @@ stdAc::swingv_t IRMitsubishiAC::toCommonSwingV(const uint8_t pos) {
/// @return The common horizontal swing position.
stdAc::swingh_t IRMitsubishiAC::toCommonSwingH(const uint8_t pos) {
switch (pos) {
case 1: return stdAc::swingh_t::kLeftMax;
case 2: return stdAc::swingh_t::kLeft;
case 3: return stdAc::swingh_t::kMiddle;
case 4: return stdAc::swingh_t::kRight;
case 5: return stdAc::swingh_t::kRightMax;
case 6: return stdAc::swingh_t::kWide;
default: return stdAc::swingh_t::kAuto;
case kMitsubishiAcWideVaneLeftMax: return stdAc::swingh_t::kLeftMax;
case kMitsubishiAcWideVaneLeft: return stdAc::swingh_t::kLeft;
case kMitsubishiAcWideVaneMiddle: return stdAc::swingh_t::kMiddle;
case kMitsubishiAcWideVaneRight: return stdAc::swingh_t::kRight;
case kMitsubishiAcWideVaneRightMax: return stdAc::swingh_t::kRightMax;
case kMitsubishiAcWideVaneWide: return stdAc::swingh_t::kWide;
default: return stdAc::swingh_t::kAuto;
}
}

Expand Down Expand Up @@ -753,28 +776,26 @@ String IRMitsubishiAC::toString(void) const {
kMitsubishiAcFanRealMax - 3,
kMitsubishiAcFanAuto, kMitsubishiAcFanQuiet,
kMitsubishiAcFanRealMax - 2);
result += addIntToString(_.Vane, kSwingVStr);
result += kSpaceLBraceStr;
switch (_.Vane) {
case kMitsubishiAcVaneAuto:
result += kAutoStr;
break;
case kMitsubishiAcVaneAutoMove:
result += kAutoStr;
result += ' ';
result += kMoveStr;
break;
default:
result += kUnknownStr;
}
result += ')';
result += addIntToString(_.WideVane, kSwingHStr);
result += kSpaceLBraceStr;
switch (_.WideVane) {
case kMitsubishiAcWideVaneAuto: result += kAutoStr; break;
default: result += kUnknownStr;
}
result += ')';
result += addSwingVToString(_.Vane, kMitsubishiAcVaneAuto,
kMitsubishiAcVaneHighest, kMitsubishiAcVaneHigh,
kMitsubishiAcVaneAuto, // Upper Middle unused.
kMitsubishiAcVaneMiddle,
kMitsubishiAcVaneAuto, // Lower Middle unused.
kMitsubishiAcVaneLow, kMitsubishiAcVaneLowest,
kMitsubishiAcVaneAuto, kMitsubishiAcVaneSwing,
// Below are unused.
kMitsubishiAcVaneAuto, kMitsubishiAcVaneAuto);
result += addSwingHToString(_.WideVane, kMitsubishiAcWideVaneAuto,
kMitsubishiAcWideVaneLeftMax,
kMitsubishiAcWideVaneLeft,
kMitsubishiAcWideVaneMiddle,
kMitsubishiAcWideVaneRight,
kMitsubishiAcWideVaneRightMax,
kMitsubishiAcWideVaneAuto, // Unused
kMitsubishiAcWideVaneAuto, // Unused
kMitsubishiAcWideVaneAuto, // Unused
kMitsubishiAcWideVaneAuto, // Unused
kMitsubishiAcWideVaneWide);
result += addLabeledString(minsToString(_.Clock * 10), kClockStr);
result += addLabeledString(minsToString(_.StartClock * 10), kOnTimerStr);
result += addLabeledString(minsToString(_.StopClock * 10), kOffTimerStr);
Expand Down Expand Up @@ -1151,17 +1172,19 @@ String IRMitsubishi136::toString(void) const {
result += addFanToString(_.Fan, kMitsubishi136FanMax,
kMitsubishi136FanLow, kMitsubishi136FanMax,
kMitsubishi136FanQuiet, kMitsubishi136FanMed);
result += addIntToString(_.SwingV, kSwingVStr);
result += kSpaceLBraceStr;
switch (_.SwingV) {
case kMitsubishi136SwingVHighest: result += kHighestStr; break;
case kMitsubishi136SwingVHigh: result += kHighStr; break;
case kMitsubishi136SwingVLow: result += kLowStr; break;
case kMitsubishi136SwingVLowest: result += kLowestStr; break;
case kMitsubishi136SwingVAuto: result += kAutoStr; break;
default: result += kUnknownStr;
}
result += ')';
result += addSwingVToString(_.SwingV, kMitsubishi136SwingVAuto,
kMitsubishi136SwingVHighest,
kMitsubishi136SwingVHigh,
kMitsubishi136SwingVAuto, // Unused
kMitsubishi136SwingVAuto, // Unused
kMitsubishi136SwingVAuto, // Unused
kMitsubishi136SwingVLow,
kMitsubishi136SwingVLow,
// Below are unused.
kMitsubishi136SwingVAuto,
kMitsubishi136SwingVAuto,
kMitsubishi136SwingVAuto,
kMitsubishi136SwingVAuto);
result += addBoolToString(getQuiet(), kQuietStr);
return result;
}
Expand Down Expand Up @@ -1616,31 +1639,30 @@ String IRMitsubishi112::toString(void) const {
result += addFanToString(_.Fan, kMitsubishi112FanMax,
kMitsubishi112FanLow, kMitsubishi112FanMax,
kMitsubishi112FanQuiet, kMitsubishi112FanMed);
result += addIntToString(_.SwingV, kSwingVStr);
result += kSpaceLBraceStr;
switch (_.SwingV) {
case kMitsubishi112SwingVHighest: result += kHighestStr; break;
case kMitsubishi112SwingVHigh: result += kHighStr; break;
case kMitsubishi112SwingVMiddle: result += kMiddleStr; break;
case kMitsubishi112SwingVLow: result += kLowStr; break;
case kMitsubishi112SwingVLowest: result += kLowestStr; break;
case kMitsubishi112SwingVAuto: result += kAutoStr; break;
default: result += kUnknownStr;
}
result += ')';
result += addIntToString(_.SwingH, kSwingHStr);
result += kSpaceLBraceStr;
switch (_.SwingH) {
case kMitsubishi112SwingHLeftMax: result += kLeftMaxStr; break;
case kMitsubishi112SwingHLeft: result += kLeftStr; break;
case kMitsubishi112SwingHMiddle: result += kMiddleStr; break;
case kMitsubishi112SwingHRight: result += kRightStr; break;
case kMitsubishi112SwingHRightMax: result += kRightMaxStr; break;
case kMitsubishi112SwingHWide: result += kWideStr; break;
case kMitsubishi112SwingHAuto: result += kAutoStr; break;
default: result += kUnknownStr;
}
result += ')';
result += addSwingVToString(_.SwingV, kMitsubishi112SwingVAuto,
kMitsubishi112SwingVHighest,
kMitsubishi112SwingVHigh,
kMitsubishi112SwingVAuto, // Upper Middle unused.
kMitsubishi112SwingVMiddle,
kMitsubishi112SwingVAuto, // Lower Middle unused.
kMitsubishi112SwingVLow,
kMitsubishi112SwingVLowest,
// Below are unused.
kMitsubishi112SwingVAuto,
kMitsubishi112SwingVAuto,
kMitsubishi112SwingVAuto,
kMitsubishi112SwingVAuto);
result += addSwingHToString(_.SwingH, kMitsubishi112SwingHAuto,
kMitsubishi112SwingHLeftMax,
kMitsubishi112SwingHLeft,
kMitsubishi112SwingHMiddle,
kMitsubishi112SwingHRight,
kMitsubishi112SwingHRightMax,
kMitsubishi112SwingHAuto, // Unused
kMitsubishi112SwingHAuto, // Unused
kMitsubishi112SwingHAuto, // Unused
kMitsubishi112SwingHAuto, // Unused
kMitsubishi112SwingHWide);
result += addBoolToString(getQuiet(), kQuietStr);
return result;
}
22 changes: 18 additions & 4 deletions src/ir_Mitsubishi.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// Copyright 2009 Ken Shirriff
// Copyright 2017-2019 David Conran
// Copyright 2017-2021 David Conran
// Copyright 2019 Mark Kuchel

/// @file
Expand All @@ -13,6 +13,8 @@
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/619
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/888
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/947
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1398
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1399
/// @see https://github.com/kuchel77

// Supports:
Expand Down Expand Up @@ -98,13 +100,25 @@ const uint8_t kMitsubishiAcFanSilent = 6;
const uint8_t kMitsubishiAcFanQuiet = kMitsubishiAcFanSilent;
const float kMitsubishiAcMinTemp = 16.0; // 16C
const float kMitsubishiAcMaxTemp = 31.0; // 31C
const uint8_t kMitsubishiAcVaneAuto = 0;
const uint8_t kMitsubishiAcVaneAutoMove = 7;
const uint8_t kMitsubishiAcVaneAuto = 0b000; // Vanes move when AC wants to.
const uint8_t kMitsubishiAcVaneHighest = 0b001;
const uint8_t kMitsubishiAcVaneHigh = 0b010;
const uint8_t kMitsubishiAcVaneMiddle = 0b011;
const uint8_t kMitsubishiAcVaneLow = 0b100;
const uint8_t kMitsubishiAcVaneLowest = 0b101;
const uint8_t kMitsubishiAcVaneSwing = 0b111; // Vanes move all the time.
const uint8_t kMitsubishiAcVaneAutoMove = kMitsubishiAcVaneSwing; // Deprecated
const uint8_t kMitsubishiAcWideVaneLeftMax = 0b0001; // 1
const uint8_t kMitsubishiAcWideVaneLeft = 0b0010; // 2
const uint8_t kMitsubishiAcWideVaneMiddle = 0b0011; // 3
const uint8_t kMitsubishiAcWideVaneRight = 0b0100; // 4
const uint8_t kMitsubishiAcWideVaneRightMax = 0b0101; // 5
const uint8_t kMitsubishiAcWideVaneWide = 0b0110; // 6
const uint8_t kMitsubishiAcWideVaneAuto = 0b1000; // 8
const uint8_t kMitsubishiAcNoTimer = 0;
const uint8_t kMitsubishiAcStartTimer = 5;
const uint8_t kMitsubishiAcStopTimer = 3;
const uint8_t kMitsubishiAcStartStopTimer = 7;
const uint8_t kMitsubishiAcWideVaneAuto = 8;

/// Native representation of a Mitsubishi 136-bit A/C message.
union Mitsubishi136Protocol{
Expand Down
4 changes: 2 additions & 2 deletions test/IRac_test.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2019 David Conran
// Copyright 2019-2021 David Conran

#include <string>
#include "ir_Airwell.h"
Expand Down Expand Up @@ -990,7 +990,7 @@ TEST(TestIRac, Mitsubishi) {
IRrecv capture(kGpioUnused);
char expected[] =
"Power: On, Mode: 3 (Cool), Temp: 20C, Fan: 2 (Medium), "
"Swing(V): 0 (Auto), Swing(H): 3 (UNKNOWN), "
"Swing(V): 0 (Auto), Swing(H): 3 (Middle), "
"Clock: 14:30, On Timer: 00:00, Off Timer: 00:00, Timer: -";

ac.begin();
Expand Down
31 changes: 26 additions & 5 deletions test/ir_Mitsubishi_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1017,13 +1017,13 @@ TEST(TestMitsubishiACClass, HumanReadable) {
IRMitsubishiAC ac(kGpioUnused);
EXPECT_EQ(
"Power: On, Mode: 1 (Heat), Temp: 22C, Fan: 6 (Quiet), "
"Swing(V): 0 (Auto), Swing(H): 3 (UNKNOWN), "
"Swing(V): 0 (Auto), Swing(H): 3 (Middle), "
"Clock: 17:10, On Timer: 00:00, Off Timer: 00:00, Timer: -",
ac.toString());
ac.setTemp(21.5);
EXPECT_EQ(
"Power: On, Mode: 1 (Heat), Temp: 21.5C, Fan: 6 (Quiet), "
"Swing(V): 0 (Auto), Swing(H): 3 (UNKNOWN), "
"Swing(V): 0 (Auto), Swing(H): 3 (Middle), "
"Clock: 17:10, On Timer: 00:00, Off Timer: 00:00, Timer: -",
ac.toString());
}
Expand Down Expand Up @@ -1179,7 +1179,7 @@ TEST(TestMitsubishiACClass, toCommon) {
ac.setMode(kMitsubishiAcCool);
ac.setTemp(20);
ac.setFan(kMitsubishiAcFanSilent);
ac.setVane(kMitsubishiAcVaneAuto);
ac.setVane(kMitsubishiAcVaneAuto); // Aka "Off", not "Swing".
ac.setWideVane(kMitsubishiAcWideVaneAuto);
// Now test it.
ASSERT_EQ(decode_type_t::MITSUBISHI_AC, ac.toCommon().protocol);
Expand All @@ -1189,7 +1189,7 @@ TEST(TestMitsubishiACClass, toCommon) {
ASSERT_EQ(20, ac.toCommon().degrees);
ASSERT_EQ(stdAc::opmode_t::kCool, ac.toCommon().mode);
ASSERT_EQ(stdAc::fanspeed_t::kMin, ac.toCommon().fanspeed);
ASSERT_EQ(stdAc::swingv_t::kAuto, ac.toCommon().swingv);
ASSERT_EQ(stdAc::swingv_t::kOff, ac.toCommon().swingv);
ASSERT_EQ(stdAc::swingh_t::kAuto, ac.toCommon().swingh);
ASSERT_TRUE(ac.toCommon().quiet);
// Unsupported.
Expand Down Expand Up @@ -1535,7 +1535,7 @@ TEST(TestDecodeMitsubishiAC, Issue891) {
ac.setRaw(irsend.capture.state);
EXPECT_EQ(
"Power: Off, Mode: 3 (Cool), Temp: 24C, Fan: 0 (Auto), "
"Swing(V): 0 (Auto), Swing(H): 3 (UNKNOWN), "
"Swing(V): 0 (Auto), Swing(H): 3 (Middle), "
"Clock: 00:00, On Timer: 00:00, Off Timer: 00:00, Timer: -",
ac.toString());
}
Expand Down Expand Up @@ -1762,3 +1762,24 @@ TEST(TestDecodeMitsubishi112, SyntheticExample) {
EXPECT_EQ(kMitsubishi112Bits, irsend.capture.bits);
EXPECT_STATE_EQ(expected, irsend.capture.state, kMitsubishi112Bits);
}

TEST(TestMitsubishiACClass, Issue1399) {
IRMitsubishiAC ac(kGpioUnused);
const uint8_t swingv_auto[kMitsubishiACStateLength] = {
0x23, 0xCB, 0x26, 0x01, 0x00, 0x20, 0x08, 0x04, 0x00,
0x80, 0x8B, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x54};
ac.setRaw(swingv_auto);
EXPECT_EQ(kMitsubishiAcVaneAuto, ac.getVane());
// Yes, we expect off from the stdAc interface, when the native is Auto.
EXPECT_EQ(stdAc::swingv_t::kOff, ac.toCommonSwingV(ac.getVane()));
EXPECT_EQ(kMitsubishiAcVaneAuto, ac.convertSwingV(stdAc::swingv_t::kOff));

const uint8_t swingv_swing[kMitsubishiACStateLength] = {
0x23, 0xCB, 0x26, 0x01, 0x00, 0x20, 0x08, 0x04, 0x00,
0xF8, 0x8B, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0xCC};
ac.setRaw(swingv_swing);
EXPECT_EQ(kMitsubishiAcVaneSwing, ac.getVane());
// Yes, we expect auto from the stdAc interface, when the native is Swing.
EXPECT_EQ(stdAc::swingv_t::kAuto, ac.toCommonSwingV(ac.getVane()));
EXPECT_EQ(kMitsubishiAcVaneSwing, ac.convertSwingV(stdAc::swingv_t::kAuto));
}

0 comments on commit ab466e4

Please sign in to comment.