Tasmota/lib/IRremoteESP8266-2.7.8/src/ir_MitsubishiHeavy.cpp

1161 lines
44 KiB
C++

// Copyright 2019 David Conran
/// @file
/// @brief Support for Mitsubishi Heavy Industry protocols.
/// Code to emulate Mitsubishi Heavy Industries A/C IR remote control units.
/// @note This code was *heavily* influenced by ToniA's great work & code,
/// but it has been written from scratch.
/// Nothing was copied other than constants and message analysis.
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/660
/// @see https://github.com/ToniA/Raw-IR-decoder-for-Arduino/blob/master/MitsubishiHeavy.cpp
/// @see https://github.com/ToniA/arduino-heatpumpir/blob/master/MitsubishiHeavyHeatpumpIR.cpp
#include "ir_MitsubishiHeavy.h"
#include <algorithm>
#include <cstring>
#include "IRremoteESP8266.h"
#include "IRtext.h"
#include "IRutils.h"
#ifndef ARDUINO
#include <string>
#endif
// Constants
const uint16_t kMitsubishiHeavyHdrMark = 3140;
const uint16_t kMitsubishiHeavyHdrSpace = 1630;
const uint16_t kMitsubishiHeavyBitMark = 370;
const uint16_t kMitsubishiHeavyOneSpace = 420;
const uint16_t kMitsubishiHeavyZeroSpace = 1220;
const uint32_t kMitsubishiHeavyGap = kDefaultMessageGap; // Just a guess.
using irutils::addBoolToString;
using irutils::addIntToString;
using irutils::addLabeledString;
using irutils::addModeToString;
using irutils::addTempToString;
using irutils::checkInvertedBytePairs;
using irutils::invertBytePairs;
using irutils::setBit;
using irutils::setBits;
#if SEND_MITSUBISHIHEAVY
/// Send a MitsubishiHeavy 88-bit A/C message.
/// Status: BETA / Appears to be working. Needs testing against a real device.
/// @param[in] data The message to be sent.
/// @param[in] nbytes The number of bytes of message to be sent.
/// @param[in] repeat The number of times the command is to be repeated.
void IRsend::sendMitsubishiHeavy88(const unsigned char data[],
const uint16_t nbytes,
const uint16_t repeat) {
if (nbytes < kMitsubishiHeavy88StateLength)
return; // Not enough bytes to send a proper message.
sendGeneric(kMitsubishiHeavyHdrMark, kMitsubishiHeavyHdrSpace,
kMitsubishiHeavyBitMark, kMitsubishiHeavyOneSpace,
kMitsubishiHeavyBitMark, kMitsubishiHeavyZeroSpace,
kMitsubishiHeavyBitMark, kMitsubishiHeavyGap,
data, nbytes, 38000, false, repeat, kDutyDefault);
}
/// Send a MitsubishiHeavy 152-bit A/C message.
/// Status: BETA / Appears to be working. Needs testing against a real device.
/// @param[in] data The message to be sent.
/// @param[in] nbytes The number of bytes of message to be sent.
/// @param[in] repeat The number of times the command is to be repeated.
void IRsend::sendMitsubishiHeavy152(const unsigned char data[],
const uint16_t nbytes,
const uint16_t repeat) {
if (nbytes < kMitsubishiHeavy152StateLength)
return; // Not enough bytes to send a proper message.
sendMitsubishiHeavy88(data, nbytes, repeat);
}
#endif // SEND_MITSUBISHIHEAVY
// Class for decoding and constructing MitsubishiHeavy152 AC messages.
/// Class constructor
/// @param[in] pin GPIO to be used when sending.
/// @param[in] inverted Is the output signal to be inverted?
/// @param[in] use_modulation Is frequency modulation to be used?
IRMitsubishiHeavy152Ac::IRMitsubishiHeavy152Ac(const uint16_t pin,
const bool inverted,
const bool use_modulation)
: _irsend(pin, inverted, use_modulation) { stateReset(); }
/// Set up hardware to be able to send a message.
void IRMitsubishiHeavy152Ac::begin(void) { _irsend.begin(); }
#if SEND_MITSUBISHIHEAVY
/// Send the current internal state as an IR message.
/// @param[in] repeat Nr. of times the message will be repeated.
void IRMitsubishiHeavy152Ac::send(const uint16_t repeat) {
_irsend.sendMitsubishiHeavy152(this->getRaw(), kMitsubishiHeavy152StateLength,
repeat);
}
#endif // SEND_MITSUBISHIHEAVY
/// Reset the state of the remote to a known good state/sequence.
void IRMitsubishiHeavy152Ac::stateReset(void) {
memcpy(remote_state, kMitsubishiHeavyZmsSig, kMitsubishiHeavySigLength);
for (uint8_t i = kMitsubishiHeavySigLength;
i < kMitsubishiHeavy152StateLength - 3; i += 2) remote_state[i] = 0;
remote_state[17] = 0x80;
}
/// Get a PTR to the internal state/code for this protocol.
/// @return PTR to a code for this protocol based on the current internal state.
uint8_t *IRMitsubishiHeavy152Ac::getRaw(void) {
checksum();
return remote_state;
}
/// Set the internal state from a valid code for this protocol.
/// @param[in] data A valid code for this protocol.
void IRMitsubishiHeavy152Ac::setRaw(const uint8_t *data) {
memcpy(remote_state, data, kMitsubishiHeavy152StateLength);
}
/// Set the requested power state of the A/C to on.
void IRMitsubishiHeavy152Ac::on(void) { setPower(true); }
/// Set the requested power state of the A/C to off.
void IRMitsubishiHeavy152Ac::off(void) { setPower(false); }
/// Change the power setting.
/// @param[in] on true, the setting is on. false, the setting is off.
void IRMitsubishiHeavy152Ac::setPower(const bool on) {
setBit(&remote_state[5], kMitsubishiHeavyPowerOffset, on);
}
/// Get the value of the current power setting.
/// @return true, the setting is on. false, the setting is off.
bool IRMitsubishiHeavy152Ac::getPower(void) {
return GETBIT8(remote_state[5], kMitsubishiHeavyPowerOffset);
}
/// Set the temperature.
/// @param[in] temp The temperature in degrees celsius.
void IRMitsubishiHeavy152Ac::setTemp(const uint8_t temp) {
uint8_t newtemp = temp;
newtemp = std::min(newtemp, kMitsubishiHeavyMaxTemp);
newtemp = std::max(newtemp, kMitsubishiHeavyMinTemp);
setBits(&remote_state[7], kLowNibble, kNibbleSize,
newtemp - kMitsubishiHeavyMinTemp);
}
/// Get the current temperature setting.
/// @return The current setting for temp. in degrees celsius.
uint8_t IRMitsubishiHeavy152Ac::getTemp(void) {
return GETBITS8(remote_state[7], kLowNibble, kNibbleSize) +
kMitsubishiHeavyMinTemp;
}
/// Set the speed of the fan.
/// @param[in] speed The desired setting.
void IRMitsubishiHeavy152Ac::setFan(const uint8_t speed) {
uint8_t newspeed = speed;
switch (speed) {
case kMitsubishiHeavy152FanLow:
case kMitsubishiHeavy152FanMed:
case kMitsubishiHeavy152FanHigh:
case kMitsubishiHeavy152FanMax:
case kMitsubishiHeavy152FanEcono:
case kMitsubishiHeavy152FanTurbo: break;
default: newspeed = kMitsubishiHeavy152FanAuto;
}
setBits(&remote_state[9], kLowNibble, kNibbleSize, newspeed);
}
/// Get the current fan speed setting.
/// @return The current fan speed/mode.
uint8_t IRMitsubishiHeavy152Ac::getFan(void) {
return GETBITS8(remote_state[9], kLowNibble, kNibbleSize);
}
/// Set the operating mode of the A/C.
/// @param[in] mode The desired operating mode.
void IRMitsubishiHeavy152Ac::setMode(const uint8_t mode) {
uint8_t newmode = mode;
switch (mode) {
case kMitsubishiHeavyCool:
case kMitsubishiHeavyDry:
case kMitsubishiHeavyFan:
case kMitsubishiHeavyHeat:
break;
default:
newmode = kMitsubishiHeavyAuto;
}
setBits(&remote_state[5], kMitsubishiHeavyModeOffset, kModeBitsSize, newmode);
}
/// Get the operating mode setting of the A/C.
/// @return The current operating mode setting.
uint8_t IRMitsubishiHeavy152Ac::getMode(void) {
return GETBITS8(remote_state[5], kMitsubishiHeavyModeOffset, kModeBitsSize);
}
/// Set the Vertical Swing mode of the A/C.
/// @param[in] pos The position/mode to set the swing to.
void IRMitsubishiHeavy152Ac::setSwingVertical(const uint8_t pos) {
setBits(&remote_state[11], kMitsubishiHeavy152SwingVOffset,
kMitsubishiHeavy152SwingVSize,
std::min(pos, kMitsubishiHeavy152SwingVOff));
}
/// Get the Vertical Swing mode of the A/C.
/// @return The native position/mode setting.
uint8_t IRMitsubishiHeavy152Ac::getSwingVertical(void) {
return GETBITS8(remote_state[11], kMitsubishiHeavy152SwingVOffset,
kMitsubishiHeavy152SwingVSize);
}
/// Set the Horizontal Swing mode of the A/C.
/// @param[in] pos The position/mode to set the swing to.
void IRMitsubishiHeavy152Ac::setSwingHorizontal(const uint8_t pos) {
setBits(&remote_state[13], kLowNibble, kNibbleSize,
std::min(pos, kMitsubishiHeavy152SwingHOff));
}
/// Get the Horizontal Swing mode of the A/C.
/// @return The native position/mode setting.
uint8_t IRMitsubishiHeavy152Ac::getSwingHorizontal(void) {
return GETBITS8(remote_state[13], kLowNibble, kNibbleSize);
}
/// Set the Night (Sleep) mode of the A/C.
/// @param[in] on true, the setting is on. false, the setting is off.
void IRMitsubishiHeavy152Ac::setNight(const bool on) {
setBit(&remote_state[15], kMitsubishiHeavyNightOffset, on);
}
/// Get the Night (Sleep) mode of the A/C.
/// @return true, the setting is on. false, the setting is off.
bool IRMitsubishiHeavy152Ac::getNight(void) {
return GETBIT8(remote_state[15], kMitsubishiHeavyNightOffset);
}
/// Set the 3D mode of the A/C.
/// @param[in] on true, the setting is on. false, the setting is off.
void IRMitsubishiHeavy152Ac::set3D(const bool on) {
if (on)
remote_state[11] |= kMitsubishiHeavy3DMask;
else
remote_state[11] &= ~kMitsubishiHeavy3DMask;
}
/// Get the 3D mode of the A/C.
/// @return true, the setting is on. false, the setting is off.
bool IRMitsubishiHeavy152Ac::get3D(void) {
return (remote_state[11] & kMitsubishiHeavy3DMask) == kMitsubishiHeavy3DMask;
}
/// Set the Silent (Quiet) mode of the A/C.
/// @param[in] on true, the setting is on. false, the setting is off.
void IRMitsubishiHeavy152Ac::setSilent(const bool on) {
setBit(&remote_state[15], kMitsubishiHeavySilentOffset, on);
}
/// Get the Silent (Quiet) mode of the A/C.
/// @return true, the setting is on. false, the setting is off.
bool IRMitsubishiHeavy152Ac::getSilent(void) {
return GETBIT8(remote_state[15], kMitsubishiHeavySilentOffset);
}
/// Set the Filter mode of the A/C.
/// @param[in] on true, the setting is on. false, the setting is off.
void IRMitsubishiHeavy152Ac::setFilter(const bool on) {
setBit(&remote_state[5], kMitsubishiHeavyFilterOffset, on);
}
/// Get the Filter mode of the A/C.
/// @return true, the setting is on. false, the setting is off.
bool IRMitsubishiHeavy152Ac::getFilter(void) {
return GETBIT8(remote_state[5], kMitsubishiHeavyFilterOffset);
}
/// Set the Clean mode of the A/C.
/// @param[in] on true, the setting is on. false, the setting is off.
void IRMitsubishiHeavy152Ac::setClean(const bool on) {
this->setFilter(on);
setBit(&remote_state[5], kMitsubishiHeavyCleanOffset, on);
}
/// Get the Clean mode of the A/C.
/// @return true, the setting is on. false, the setting is off.
bool IRMitsubishiHeavy152Ac::getClean(void) {
return GETBIT8(remote_state[5], kMitsubishiHeavyCleanOffset) && getFilter();
}
/// Set the Turbo mode of the A/C.
/// @param[in] on true, the setting is on. false, the setting is off.
void IRMitsubishiHeavy152Ac::setTurbo(const bool on) {
if (on)
this->setFan(kMitsubishiHeavy152FanTurbo);
else if (this->getTurbo()) this->setFan(kMitsubishiHeavy152FanAuto);
}
/// Get the Turbo mode of the A/C.
/// @return true, the setting is on. false, the setting is off.
bool IRMitsubishiHeavy152Ac::getTurbo(void) {
return this->getFan() == kMitsubishiHeavy152FanTurbo;
}
/// Set the Economical mode of the A/C.
/// @param[in] on true, the setting is on. false, the setting is off.
void IRMitsubishiHeavy152Ac::setEcono(const bool on) {
if (on)
this->setFan(kMitsubishiHeavy152FanEcono);
else if (this->getEcono()) this->setFan(kMitsubishiHeavy152FanAuto);
}
/// Get the Economical mode of the A/C.
/// @return true, the setting is on. false, the setting is off.
bool IRMitsubishiHeavy152Ac::getEcono(void) {
return this->getFan() == kMitsubishiHeavy152FanEcono;
}
/// Verify the given state has a ZM-S signature.
/// @param[in] state A ptr to a state to be checked.
/// @return true, the check passed. Otherwise, false.
bool IRMitsubishiHeavy152Ac::checkZmsSig(const uint8_t *state) {
for (uint8_t i = 0; i < kMitsubishiHeavySigLength; i++)
if (state[i] != kMitsubishiHeavyZmsSig[i]) return false;
return true;
}
/// Calculate the checksum for the current internal state of the remote.
/// Note: Technically it has no checksum, but does have inverted byte pairs.
void IRMitsubishiHeavy152Ac::checksum(void) {
const uint8_t kOffset = kMitsubishiHeavySigLength - 2;
invertBytePairs(remote_state + kOffset,
kMitsubishiHeavy152StateLength - kOffset);
}
/// Verify the checksum is valid for a given state.
/// @param[in] state The array to verify the checksum of.
/// @param[in] length The length/size of the state array.
/// @return true, if the state has a valid checksum. Otherwise, false.
/// Note: Technically it has no checksum, but does have inverted byte pairs.
bool IRMitsubishiHeavy152Ac::validChecksum(const uint8_t *state,
const uint16_t length) {
// Assume anything too short is fine.
if (length < kMitsubishiHeavySigLength) return true;
const uint8_t kOffset = kMitsubishiHeavySigLength - 2;
return checkInvertedBytePairs(state + kOffset, length - kOffset);
}
/// Convert a stdAc::opmode_t enum into its native mode.
/// @param[in] mode The enum to be converted.
/// @return The native equivilant of the enum.
uint8_t IRMitsubishiHeavy152Ac::convertMode(const stdAc::opmode_t mode) {
switch (mode) {
case stdAc::opmode_t::kCool: return kMitsubishiHeavyCool;
case stdAc::opmode_t::kHeat: return kMitsubishiHeavyHeat;
case stdAc::opmode_t::kDry: return kMitsubishiHeavyDry;
case stdAc::opmode_t::kFan: return kMitsubishiHeavyFan;
default: return kMitsubishiHeavyAuto;
}
}
/// Convert a stdAc::fanspeed_t enum into it's native speed.
/// @param[in] speed The enum to be converted.
/// @return The native equivilant of the enum.
uint8_t IRMitsubishiHeavy152Ac::convertFan(const stdAc::fanspeed_t speed) {
switch (speed) {
// Assumes Econo is slower than Low.
case stdAc::fanspeed_t::kMin: return kMitsubishiHeavy152FanEcono;
case stdAc::fanspeed_t::kLow: return kMitsubishiHeavy152FanLow;
case stdAc::fanspeed_t::kMedium: return kMitsubishiHeavy152FanMed;
case stdAc::fanspeed_t::kHigh: return kMitsubishiHeavy152FanHigh;
case stdAc::fanspeed_t::kMax: return kMitsubishiHeavy152FanMax;
default: return kMitsubishiHeavy152FanAuto;
}
}
/// Convert a stdAc::swingv_t enum into it's native setting.
/// @param[in] position The enum to be converted.
/// @return The native equivilant of the enum.
uint8_t IRMitsubishiHeavy152Ac::convertSwingV(const stdAc::swingv_t position) {
switch (position) {
case stdAc::swingv_t::kAuto: return kMitsubishiHeavy152SwingVAuto;
case stdAc::swingv_t::kHighest: return kMitsubishiHeavy152SwingVHighest;
case stdAc::swingv_t::kHigh: return kMitsubishiHeavy152SwingVHigh;
case stdAc::swingv_t::kMiddle: return kMitsubishiHeavy152SwingVMiddle;
case stdAc::swingv_t::kLow: return kMitsubishiHeavy152SwingVLow;
case stdAc::swingv_t::kLowest: return kMitsubishiHeavy152SwingVLowest;
default: return kMitsubishiHeavy152SwingVOff;
}
}
/// Convert a stdAc::swingh_t enum into it's native setting.
/// @param[in] position The enum to be converted.
/// @return The native equivilant of the enum.
uint8_t IRMitsubishiHeavy152Ac::convertSwingH(const stdAc::swingh_t position) {
switch (position) {
case stdAc::swingh_t::kAuto: return kMitsubishiHeavy152SwingHAuto;
case stdAc::swingh_t::kLeftMax: return kMitsubishiHeavy152SwingHLeftMax;
case stdAc::swingh_t::kLeft: return kMitsubishiHeavy152SwingHLeft;
case stdAc::swingh_t::kMiddle: return kMitsubishiHeavy152SwingHMiddle;
case stdAc::swingh_t::kRight: return kMitsubishiHeavy152SwingHRight;
case stdAc::swingh_t::kRightMax: return kMitsubishiHeavy152SwingHRightMax;
default: return kMitsubishiHeavy152SwingHOff;
}
}
/// Convert a native mode into its stdAc equivilant.
/// @param[in] mode The native setting to be converted.
/// @return The stdAc equivilant of the native setting.
stdAc::opmode_t IRMitsubishiHeavy152Ac::toCommonMode(const uint8_t mode) {
switch (mode) {
case kMitsubishiHeavyCool: return stdAc::opmode_t::kCool;
case kMitsubishiHeavyHeat: return stdAc::opmode_t::kHeat;
case kMitsubishiHeavyDry: return stdAc::opmode_t::kDry;
case kMitsubishiHeavyFan: return stdAc::opmode_t::kFan;
default: return stdAc::opmode_t::kAuto;
}
}
/// Convert a native fan speed into its stdAc equivilant.
/// @param[in] spd The native setting to be converted.
/// @return The stdAc equivilant of the native setting.
stdAc::fanspeed_t IRMitsubishiHeavy152Ac::toCommonFanSpeed(const uint8_t spd) {
switch (spd) {
case kMitsubishiHeavy152FanMax: return stdAc::fanspeed_t::kMax;
case kMitsubishiHeavy152FanHigh: return stdAc::fanspeed_t::kHigh;
case kMitsubishiHeavy152FanMed: return stdAc::fanspeed_t::kMedium;
case kMitsubishiHeavy152FanLow: return stdAc::fanspeed_t::kLow;
case kMitsubishiHeavy152FanEcono: return stdAc::fanspeed_t::kMin;
default: return stdAc::fanspeed_t::kAuto;
}
}
/// Convert a native horizontal swing postion to it's common equivalent.
/// @param[in] pos A native position to convert.
/// @return The common horizontal swing position.
stdAc::swingh_t IRMitsubishiHeavy152Ac::toCommonSwingH(const uint8_t pos) {
switch (pos) {
case kMitsubishiHeavy152SwingHLeftMax: return stdAc::swingh_t::kLeftMax;
case kMitsubishiHeavy152SwingHLeft: return stdAc::swingh_t::kLeft;
case kMitsubishiHeavy152SwingHMiddle: return stdAc::swingh_t::kMiddle;
case kMitsubishiHeavy152SwingHRight: return stdAc::swingh_t::kRight;
case kMitsubishiHeavy152SwingHRightMax: return stdAc::swingh_t::kRightMax;
case kMitsubishiHeavy152SwingHOff: return stdAc::swingh_t::kOff;
default: return stdAc::swingh_t::kAuto;
}
}
/// 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.
stdAc::swingv_t IRMitsubishiHeavy152Ac::toCommonSwingV(const uint8_t pos) {
switch (pos) {
case kMitsubishiHeavy152SwingVHighest: return stdAc::swingv_t::kHighest;
case kMitsubishiHeavy152SwingVHigh: return stdAc::swingv_t::kHigh;
case kMitsubishiHeavy152SwingVMiddle: return stdAc::swingv_t::kMiddle;
case kMitsubishiHeavy152SwingVLow: return stdAc::swingv_t::kLow;
case kMitsubishiHeavy152SwingVLowest: return stdAc::swingv_t::kLowest;
case kMitsubishiHeavy152SwingVOff: return stdAc::swingv_t::kOff;
default: return stdAc::swingv_t::kAuto;
}
}
/// Convert the current internal state into its stdAc::state_t equivilant.
/// @return The stdAc equivilant of the native settings.
stdAc::state_t IRMitsubishiHeavy152Ac::toCommon(void) {
stdAc::state_t result;
result.protocol = decode_type_t::MITSUBISHI_HEAVY_152;
result.model = -1; // No models used.
result.power = this->getPower();
result.mode = this->toCommonMode(this->getMode());
result.celsius = true;
result.degrees = this->getTemp();
result.fanspeed = this->toCommonFanSpeed(this->getFan());
result.swingv = this->toCommonSwingV(this->getSwingVertical());
result.swingh = this->toCommonSwingH(this->getSwingHorizontal());
result.turbo = this->getTurbo();
result.econo = this->getEcono();
result.clean = this->getClean();
result.quiet = this->getSilent();
result.filter = this->getFilter();
result.sleep = this->getNight() ? 0 : -1;
// Not supported.
result.light = false;
result.beep = false;
result.clock = -1;
return result;
}
/// Convert the internal state into a human readable string.
/// @return A string containing the settings in human-readable form.
String IRMitsubishiHeavy152Ac::toString(void) {
String result = "";
result.reserve(180); // Reserve some heap for the string to reduce fragging.
result += addBoolToString(getPower(), kPowerStr, false);
result += addModeToString(getMode(), kMitsubishiHeavyAuto,
kMitsubishiHeavyCool, kMitsubishiHeavyHeat,
kMitsubishiHeavyDry, kMitsubishiHeavyFan);
result += addTempToString(getTemp());
result += addIntToString(getFan(), kFanStr);
result += kSpaceLBraceStr;
switch (this->getFan()) {
case kMitsubishiHeavy152FanAuto:
result += kAutoStr;
break;
case kMitsubishiHeavy152FanHigh:
result += kHighStr;
break;
case kMitsubishiHeavy152FanLow:
result += kLowStr;
break;
case kMitsubishiHeavy152FanMed:
result += kMedStr;
break;
case kMitsubishiHeavy152FanMax:
result += kMaxStr;
break;
case kMitsubishiHeavy152FanEcono:
result += kEconoStr;
break;
case kMitsubishiHeavy152FanTurbo:
result += kTurboStr;
break;
default:
result += kUnknownStr;
}
result += ')';
result += addIntToString(getSwingVertical(), kSwingVStr);
result += kSpaceLBraceStr;
switch (this->getSwingVertical()) {
case kMitsubishiHeavy152SwingVAuto:
result += kAutoStr;
break;
case kMitsubishiHeavy152SwingVHighest:
result += kHighestStr;
break;
case kMitsubishiHeavy152SwingVHigh:
result += kHighStr;
break;
case kMitsubishiHeavy152SwingVMiddle:
result += kMiddleStr;
break;
case kMitsubishiHeavy152SwingVLow:
result += kLowStr;
break;
case kMitsubishiHeavy152SwingVLowest:
result += kLowestStr;
break;
case kMitsubishiHeavy152SwingVOff:
result += kOffStr;
break;
default:
result += kUnknownStr;
}
result += ')';
result += addIntToString(getSwingHorizontal(), kSwingHStr);
result += kSpaceLBraceStr;
switch (this->getSwingHorizontal()) {
case kMitsubishiHeavy152SwingHAuto:
result += kAutoStr;
break;
case kMitsubishiHeavy152SwingHLeftMax:
result += kMaxLeftStr;
break;
case kMitsubishiHeavy152SwingHLeft:
result += kLeftStr;
break;
case kMitsubishiHeavy152SwingHMiddle:
result += kMiddleStr;
break;
case kMitsubishiHeavy152SwingHRight:
result += kRightStr;
break;
case kMitsubishiHeavy152SwingHRightMax:
result += kMaxRightStr;
break;
case kMitsubishiHeavy152SwingHLeftRight:
result += kLeftStr;
result += ' ';
result += kRightStr;
break;
case kMitsubishiHeavy152SwingHRightLeft:
result += kRightStr;
result += ' ';
result += kLeftStr;
break;
case kMitsubishiHeavy152SwingHOff:
result += kOffStr;
break;
default:
result += kUnknownStr;
}
result += ')';
result += addBoolToString(getSilent(), kSilentStr);
result += addBoolToString(getTurbo(), kTurboStr);
result += addBoolToString(getEcono(), kEconoStr);
result += addBoolToString(getNight(), kNightStr);
result += addBoolToString(getFilter(), kFilterStr);
result += addBoolToString(get3D(), k3DStr);
result += addBoolToString(getClean(), kCleanStr);
return result;
}
// Class for decoding and constructing MitsubishiHeavy88 AC messages.
/// Class constructor
/// @param[in] pin GPIO to be used when sending.
/// @param[in] inverted Is the output signal to be inverted?
/// @param[in] use_modulation Is frequency modulation to be used?
IRMitsubishiHeavy88Ac::IRMitsubishiHeavy88Ac(const uint16_t pin,
const bool inverted,
const bool use_modulation)
: _irsend(pin, inverted, use_modulation) { stateReset(); }
/// Set up hardware to be able to send a message.
void IRMitsubishiHeavy88Ac::begin(void) { _irsend.begin(); }
#if SEND_MITSUBISHIHEAVY
/// Send the current internal state as an IR message.
/// @param[in] repeat Nr. of times the message will be repeated.
void IRMitsubishiHeavy88Ac::send(const uint16_t repeat) {
_irsend.sendMitsubishiHeavy88(this->getRaw(), kMitsubishiHeavy88StateLength,
repeat);
}
#endif // SEND_MITSUBISHIHEAVY
/// Reset the state of the remote to a known good state/sequence.
void IRMitsubishiHeavy88Ac::stateReset(void) {
memcpy(remote_state, kMitsubishiHeavyZjsSig, kMitsubishiHeavySigLength);
for (uint8_t i = kMitsubishiHeavySigLength; i < kMitsubishiHeavy88StateLength;
i++) remote_state[i] = 0;
}
/// Get a PTR to the internal state/code for this protocol.
/// @return PTR to a code for this protocol based on the current internal state.
uint8_t *IRMitsubishiHeavy88Ac::getRaw(void) {
checksum();
return remote_state;
}
/// Set the internal state from a valid code for this protocol.
/// @param[in] data A valid code for this protocol.
void IRMitsubishiHeavy88Ac::setRaw(const uint8_t *data) {
memcpy(remote_state, data, kMitsubishiHeavy88StateLength);
}
/// Set the requested power state of the A/C to on.
void IRMitsubishiHeavy88Ac::on(void) { setPower(true); }
/// Set the requested power state of the A/C to off.
void IRMitsubishiHeavy88Ac::off(void) { setPower(false); }
/// Change the power setting.
/// @param[in] on true, the setting is on. false, the setting is off.
void IRMitsubishiHeavy88Ac::setPower(const bool on) {
setBit(&remote_state[9], kMitsubishiHeavyPowerOffset, on);
}
/// Get the value of the current power setting.
/// @return true, the setting is on. false, the setting is off.
bool IRMitsubishiHeavy88Ac::getPower(void) {
return GETBIT8(remote_state[9], kMitsubishiHeavyPowerOffset);
}
/// Set the temperature.
/// @param[in] temp The temperature in degrees celsius.
void IRMitsubishiHeavy88Ac::setTemp(const uint8_t temp) {
uint8_t newtemp = temp;
newtemp = std::min(newtemp, kMitsubishiHeavyMaxTemp);
newtemp = std::max(newtemp, kMitsubishiHeavyMinTemp);
setBits(&remote_state[9], kHighNibble, kNibbleSize,
newtemp - kMitsubishiHeavyMinTemp);
}
/// Get the current temperature setting.
/// @return The current setting for temp. in degrees celsius.
uint8_t IRMitsubishiHeavy88Ac::getTemp(void) {
return GETBITS8(remote_state[9], kHighNibble, kNibbleSize) +
kMitsubishiHeavyMinTemp;
}
/// Set the speed of the fan.
/// @param[in] speed The desired setting.
void IRMitsubishiHeavy88Ac::setFan(const uint8_t speed) {
uint8_t newspeed = speed;
switch (speed) {
case kMitsubishiHeavy88FanLow:
case kMitsubishiHeavy88FanMed:
case kMitsubishiHeavy88FanHigh:
case kMitsubishiHeavy88FanTurbo:
case kMitsubishiHeavy88FanEcono: break;
default: newspeed = kMitsubishiHeavy88FanAuto;
}
setBits(&remote_state[7], kMitsubishiHeavy88FanOffset,
kMitsubishiHeavy88FanSize, newspeed);
}
/// Get the current fan speed setting.
/// @return The current fan speed/mode.
uint8_t IRMitsubishiHeavy88Ac::getFan(void) {
return GETBITS8(remote_state[7], kMitsubishiHeavy88FanOffset,
kMitsubishiHeavy88FanSize);
}
/// Set the operating mode of the A/C.
/// @param[in] mode The desired operating mode.
void IRMitsubishiHeavy88Ac::setMode(const uint8_t mode) {
uint8_t newmode = mode;
switch (mode) {
case kMitsubishiHeavyCool:
case kMitsubishiHeavyDry:
case kMitsubishiHeavyFan:
case kMitsubishiHeavyHeat:
break;
default:
newmode = kMitsubishiHeavyAuto;
}
setBits(&remote_state[9], kMitsubishiHeavyModeOffset, kModeBitsSize, newmode);
}
/// Get the operating mode setting of the A/C.
/// @return The current operating mode setting.
uint8_t IRMitsubishiHeavy88Ac::getMode(void) {
return GETBITS8(remote_state[9], kMitsubishiHeavyModeOffset, kModeBitsSize);
}
/// Set the Vertical Swing mode of the A/C.
/// @param[in] pos The position/mode to set the swing to.
void IRMitsubishiHeavy88Ac::setSwingVertical(const uint8_t pos) {
uint8_t newpos;
switch (pos) {
case kMitsubishiHeavy88SwingVAuto:
case kMitsubishiHeavy88SwingVHighest:
case kMitsubishiHeavy88SwingVHigh:
case kMitsubishiHeavy88SwingVMiddle:
case kMitsubishiHeavy88SwingVLow:
case kMitsubishiHeavy88SwingVLowest: newpos = pos; break;
default: newpos = kMitsubishiHeavy88SwingVOff;
}
setBit(&remote_state[5], kMitsubishiHeavy88SwingVByte5Offset,
newpos & 1);
setBits(&remote_state[7], kMitsubishiHeavy88SwingVByte7Offset,
kMitsubishiHeavy88SwingVByte7Size,
newpos >> kMitsubishiHeavy88SwingVByte5Size);
}
/// Get the Vertical Swing mode of the A/C.
/// @return The native position/mode setting.
uint8_t IRMitsubishiHeavy88Ac::getSwingVertical(void) {
return GETBITS8(remote_state[5], kMitsubishiHeavy88SwingVByte5Offset,
kMitsubishiHeavy88SwingVByte5Size) |
(GETBITS8(remote_state[7], kMitsubishiHeavy88SwingVByte7Offset,
kMitsubishiHeavy88SwingVByte7Size) <<
kMitsubishiHeavy88SwingVByte5Size);
}
/// Set the Horizontal Swing mode of the A/C.
/// @param[in] pos The position/mode to set the swing to.
void IRMitsubishiHeavy88Ac::setSwingHorizontal(const uint8_t pos) {
uint8_t newpos;
switch (pos) {
case kMitsubishiHeavy88SwingHAuto:
case kMitsubishiHeavy88SwingHLeftMax:
case kMitsubishiHeavy88SwingHLeft:
case kMitsubishiHeavy88SwingHMiddle:
case kMitsubishiHeavy88SwingHRight:
case kMitsubishiHeavy88SwingHRightMax:
case kMitsubishiHeavy88SwingHLeftRight:
case kMitsubishiHeavy88SwingHRightLeft:
case kMitsubishiHeavy88SwingH3D: newpos = pos; break;
default: newpos = kMitsubishiHeavy88SwingHOff;
}
setBits(&remote_state[5], kMitsubishiHeavy88SwingHOffset1,
kMitsubishiHeavy88SwingHSize, newpos);
setBits(&remote_state[5], kMitsubishiHeavy88SwingHOffset2,
kMitsubishiHeavy88SwingHSize,
newpos >> kMitsubishiHeavy88SwingHSize);
}
/// Get the Horizontal Swing mode of the A/C.
/// @return The native position/mode setting.
uint8_t IRMitsubishiHeavy88Ac::getSwingHorizontal(void) {
return GETBITS8(remote_state[5], kMitsubishiHeavy88SwingHOffset1,
kMitsubishiHeavy88SwingHSize) |
(GETBITS8(remote_state[5], kMitsubishiHeavy88SwingHOffset2,
kMitsubishiHeavy88SwingHSize) <<
kMitsubishiHeavy88SwingHSize);
}
/// Set the Turbo mode of the A/C.
/// @param[in] on true, the setting is on. false, the setting is off.
void IRMitsubishiHeavy88Ac::setTurbo(const bool on) {
if (on)
this->setFan(kMitsubishiHeavy88FanTurbo);
else if (this->getTurbo()) this->setFan(kMitsubishiHeavy88FanAuto);
}
/// Get the Turbo mode of the A/C.
/// @return true, the setting is on. false, the setting is off.
bool IRMitsubishiHeavy88Ac::getTurbo(void) {
return this->getFan() == kMitsubishiHeavy88FanTurbo;
}
/// Set the Economical mode of the A/C.
/// @param[in] on true, the setting is on. false, the setting is off.
void IRMitsubishiHeavy88Ac::setEcono(const bool on) {
if (on)
this->setFan(kMitsubishiHeavy88FanEcono);
else if (this->getEcono()) this->setFan(kMitsubishiHeavy88FanAuto);
}
/// Get the Economical mode of the A/C.
/// @return true, the setting is on. false, the setting is off.
bool IRMitsubishiHeavy88Ac::getEcono(void) {
return this->getFan() == kMitsubishiHeavy88FanEcono;
}
/// Set the 3D mode of the A/C.
/// @param[in] on true, the setting is on. false, the setting is off.
void IRMitsubishiHeavy88Ac::set3D(const bool on) {
if (on)
this->setSwingHorizontal(kMitsubishiHeavy88SwingH3D);
else if (this->get3D())
this->setSwingHorizontal(kMitsubishiHeavy88SwingHOff);
}
/// Get the 3D mode of the A/C.
/// @return true, the setting is on. false, the setting is off.
bool IRMitsubishiHeavy88Ac::get3D(void) {
return this->getSwingHorizontal() == kMitsubishiHeavy88SwingH3D;
}
/// Set the Clean mode of the A/C.
/// @param[in] on true, the setting is on. false, the setting is off.
void IRMitsubishiHeavy88Ac::setClean(const bool on) {
setBit(&remote_state[5], kMitsubishiHeavy88CleanOffset, on);
}
/// Get the Clean mode of the A/C.
/// @return true, the setting is on. false, the setting is off.
bool IRMitsubishiHeavy88Ac::getClean(void) {
return GETBIT8(remote_state[5], kMitsubishiHeavy88CleanOffset);
}
/// Verify the given state has a ZJ-S signature.
/// @param[in] state A ptr to a state to be checked.
/// @return true, the check passed. Otherwise, false.
bool IRMitsubishiHeavy88Ac::checkZjsSig(const uint8_t *state) {
for (uint8_t i = 0; i < kMitsubishiHeavySigLength; i++)
if (state[i] != kMitsubishiHeavyZjsSig[i]) return false;
return true;
}
/// Calculate the checksum for the current internal state of the remote.
/// Note: Technically it has no checksum, but does have inverted byte pairs.
void IRMitsubishiHeavy88Ac::checksum(void) {
const uint8_t kOffset = kMitsubishiHeavySigLength - 2;
invertBytePairs(remote_state + kOffset,
kMitsubishiHeavy88StateLength - kOffset);
}
/// Verify the checksum is valid for a given state.
/// @param[in] state The array to verify the checksum of.
/// @param[in] length The length/size of the state array.
/// @return true, if the state has a valid checksum. Otherwise, false.
/// Note: Technically it has no checksum, but does have inverted byte pairs.
bool IRMitsubishiHeavy88Ac::validChecksum(const uint8_t *state,
const uint16_t length) {
return IRMitsubishiHeavy152Ac::validChecksum(state, length);
}
/// Convert a stdAc::opmode_t enum into its native mode.
/// @param[in] mode The enum to be converted.
/// @return The native equivilant of the enum.
uint8_t IRMitsubishiHeavy88Ac::convertMode(const stdAc::opmode_t mode) {
return IRMitsubishiHeavy152Ac::convertMode(mode);
}
/// Convert a stdAc::fanspeed_t enum into it's native speed.
/// @param[in] speed The enum to be converted.
/// @return The native equivilant of the enum.
uint8_t IRMitsubishiHeavy88Ac::convertFan(const stdAc::fanspeed_t speed) {
switch (speed) {
// Assumes Econo is slower than Low.
case stdAc::fanspeed_t::kMin: return kMitsubishiHeavy88FanEcono;
case stdAc::fanspeed_t::kLow: return kMitsubishiHeavy88FanLow;
case stdAc::fanspeed_t::kMedium: return kMitsubishiHeavy88FanMed;
case stdAc::fanspeed_t::kHigh: return kMitsubishiHeavy88FanHigh;
case stdAc::fanspeed_t::kMax: return kMitsubishiHeavy88FanTurbo;
default: return kMitsubishiHeavy88FanAuto;
}
}
/// Convert a stdAc::swingv_t enum into it's native setting.
/// @param[in] position The enum to be converted.
/// @return The native equivilant of the enum.
uint8_t IRMitsubishiHeavy88Ac::convertSwingV(const stdAc::swingv_t position) {
switch (position) {
case stdAc::swingv_t::kAuto: return kMitsubishiHeavy88SwingVAuto;
case stdAc::swingv_t::kHighest: return kMitsubishiHeavy88SwingVHighest;
case stdAc::swingv_t::kHigh: return kMitsubishiHeavy88SwingVHigh;
case stdAc::swingv_t::kMiddle: return kMitsubishiHeavy88SwingVMiddle;
case stdAc::swingv_t::kLow: return kMitsubishiHeavy88SwingVLow;
case stdAc::swingv_t::kLowest: return kMitsubishiHeavy88SwingVLowest;
default: return kMitsubishiHeavy88SwingVOff;
}
}
/// Convert a stdAc::swingh_t enum into it's native setting.
/// @param[in] position The enum to be converted.
/// @return The native equivilant of the enum.
uint8_t IRMitsubishiHeavy88Ac::convertSwingH(const stdAc::swingh_t position) {
switch (position) {
case stdAc::swingh_t::kAuto: return kMitsubishiHeavy88SwingHAuto;
case stdAc::swingh_t::kLeftMax: return kMitsubishiHeavy88SwingHLeftMax;
case stdAc::swingh_t::kLeft: return kMitsubishiHeavy88SwingHLeft;
case stdAc::swingh_t::kMiddle: return kMitsubishiHeavy88SwingHMiddle;
case stdAc::swingh_t::kRight: return kMitsubishiHeavy88SwingHRight;
case stdAc::swingh_t::kRightMax: return kMitsubishiHeavy88SwingHRightMax;
default: return kMitsubishiHeavy88SwingHOff;
}
}
/// Convert a native fan speed into its stdAc equivilant.
/// @param[in] speed The native setting to be converted.
/// @return The stdAc equivilant of the native setting.
stdAc::fanspeed_t IRMitsubishiHeavy88Ac::toCommonFanSpeed(const uint8_t speed) {
switch (speed) {
case kMitsubishiHeavy88FanTurbo: return stdAc::fanspeed_t::kMax;
case kMitsubishiHeavy88FanHigh: return stdAc::fanspeed_t::kHigh;
case kMitsubishiHeavy88FanMed: return stdAc::fanspeed_t::kMedium;
case kMitsubishiHeavy88FanLow: return stdAc::fanspeed_t::kLow;
case kMitsubishiHeavy88FanEcono: return stdAc::fanspeed_t::kMin;
default: return stdAc::fanspeed_t::kAuto;
}
}
/// Convert a native horizontal swing postion to it's common equivalent.
/// @param[in] pos A native position to convert.
/// @return The common horizontal swing position.
stdAc::swingh_t IRMitsubishiHeavy88Ac::toCommonSwingH(const uint8_t pos) {
switch (pos) {
case kMitsubishiHeavy88SwingHLeftMax: return stdAc::swingh_t::kLeftMax;
case kMitsubishiHeavy88SwingHLeft: return stdAc::swingh_t::kLeft;
case kMitsubishiHeavy88SwingHMiddle: return stdAc::swingh_t::kMiddle;
case kMitsubishiHeavy88SwingHRight: return stdAc::swingh_t::kRight;
case kMitsubishiHeavy88SwingHRightMax: return stdAc::swingh_t::kRightMax;
case kMitsubishiHeavy88SwingHOff: return stdAc::swingh_t::kOff;
default: return stdAc::swingh_t::kAuto;
}
}
/// 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.
stdAc::swingv_t IRMitsubishiHeavy88Ac::toCommonSwingV(const uint8_t pos) {
switch (pos) {
case kMitsubishiHeavy88SwingVHighest: return stdAc::swingv_t::kHighest;
case kMitsubishiHeavy88SwingVHigh: return stdAc::swingv_t::kHigh;
case kMitsubishiHeavy88SwingVMiddle: return stdAc::swingv_t::kMiddle;
case kMitsubishiHeavy88SwingVLow: return stdAc::swingv_t::kLow;
case kMitsubishiHeavy88SwingVLowest: return stdAc::swingv_t::kLowest;
case kMitsubishiHeavy88SwingVOff: return stdAc::swingv_t::kOff;
default: return stdAc::swingv_t::kAuto;
}
}
/// Convert the current internal state into its stdAc::state_t equivilant.
/// @return The stdAc equivilant of the native settings.
stdAc::state_t IRMitsubishiHeavy88Ac::toCommon(void) {
stdAc::state_t result;
result.protocol = decode_type_t::MITSUBISHI_HEAVY_88;
result.model = -1; // No models used.
result.power = this->getPower();
result.mode = IRMitsubishiHeavy152Ac::toCommonMode(this->getMode());
result.celsius = true;
result.degrees = this->getTemp();
result.fanspeed = this->toCommonFanSpeed(this->getFan());
result.swingv = this->toCommonSwingV(this->getSwingVertical());
result.swingh = this->toCommonSwingH(this->getSwingHorizontal());
result.turbo = this->getTurbo();
result.econo = this->getEcono();
result.clean = this->getClean();
// Not supported.
result.quiet = false;
result.filter = false;
result.light = false;
result.beep = false;
result.sleep = -1;
result.clock = -1;
return result;
}
/// Convert the internal state into a human readable string.
/// @return A string containing the settings in human-readable form.
String IRMitsubishiHeavy88Ac::toString(void) {
String result = "";
result.reserve(140); // Reserve some heap for the string to reduce fragging.
result += addBoolToString(getPower(), kPowerStr, false);
result += addModeToString(getMode(), kMitsubishiHeavyAuto,
kMitsubishiHeavyCool, kMitsubishiHeavyHeat,
kMitsubishiHeavyDry, kMitsubishiHeavyFan);
result += addTempToString(getTemp());
result += addIntToString(getFan(), kFanStr);
result += kSpaceLBraceStr;
switch (this->getFan()) {
case kMitsubishiHeavy88FanAuto:
result += kAutoStr;
break;
case kMitsubishiHeavy88FanHigh:
result += kHighStr;
break;
case kMitsubishiHeavy88FanLow:
result += kLowStr;
break;
case kMitsubishiHeavy88FanMed:
result += kMedStr;
break;
case kMitsubishiHeavy88FanEcono:
result += kEconoStr;
break;
case kMitsubishiHeavy88FanTurbo:
result += kTurboStr;
break;
default:
result += kUnknownStr;
}
result += ')';
result += addIntToString(getSwingVertical(), kSwingVStr);
result += kSpaceLBraceStr;
switch (this->getSwingVertical()) {
case kMitsubishiHeavy88SwingVAuto:
result += kAutoStr;
break;
case kMitsubishiHeavy88SwingVHighest:
result += kHighestStr;
break;
case kMitsubishiHeavy88SwingVHigh:
result += kHighStr;
break;
case kMitsubishiHeavy88SwingVMiddle:
result += kMiddleStr;
break;
case kMitsubishiHeavy88SwingVLow:
result += kLowStr;
break;
case kMitsubishiHeavy88SwingVLowest:
result += kLowestStr;
break;
case kMitsubishiHeavy88SwingVOff:
result += kOffStr;
break;
default:
result += kUnknownStr;
}
result += ')';
result += addIntToString(getSwingHorizontal(), kSwingHStr);
result += kSpaceLBraceStr;
switch (this->getSwingHorizontal()) {
case kMitsubishiHeavy88SwingHAuto:
result += kAutoStr;
break;
case kMitsubishiHeavy88SwingHLeftMax:
result += kMaxLeftStr;
break;
case kMitsubishiHeavy88SwingHLeft:
result += kLeftStr;
break;
case kMitsubishiHeavy88SwingHMiddle:
result += kMiddleStr;
break;
case kMitsubishiHeavy88SwingHRight:
result += kRightStr;
break;
case kMitsubishiHeavy88SwingHRightMax:
result += kMaxRightStr;
break;
case kMitsubishiHeavy88SwingHLeftRight:
result += kLeftStr;
result += ' ';
result += kRightStr;
break;
case kMitsubishiHeavy88SwingHRightLeft:
result += kRightStr;
result += ' ';
result += kLeftStr;
break;
case kMitsubishiHeavy88SwingH3D:
result += k3DStr;
break;
case kMitsubishiHeavy88SwingHOff:
result += kOffStr;
break;
default:
result += kUnknownStr;
}
result += ')';
result += addBoolToString(getTurbo(), kTurboStr);
result += addBoolToString(getEcono(), kEconoStr);
result += addBoolToString(get3D(), k3DStr);
result += addBoolToString(getClean(), kCleanStr);
return result;
}
#if DECODE_MITSUBISHIHEAVY
/// Decode the supplied Mitsubishi Heavy Industries A/C message.
/// Status: BETA / Appears to be working. Needs testing against a real device.
/// @param[in,out] results Ptr to the data to decode & where to store the result
/// @param[in] offset The starting index to use when attempting to decode the
/// raw data. Typically/Defaults to kStartOffset.
/// @param[in] nbits The number of data bits to expect.
/// Typically kMitsubishiHeavy88Bits or kMitsubishiHeavy152Bits (def).
/// @param[in] strict Flag indicating if we should perform strict matching.
/// @return True if it can decode it, false if it can't.
bool IRrecv::decodeMitsubishiHeavy(decode_results* results, uint16_t offset,
const uint16_t nbits, const bool strict) {
if (strict) {
switch (nbits) {
case kMitsubishiHeavy88Bits:
case kMitsubishiHeavy152Bits:
break;
default:
return false; // Not what is expected
}
}
uint16_t used;
used = matchGeneric(results->rawbuf + offset, results->state,
results->rawlen - offset, nbits,
kMitsubishiHeavyHdrMark, kMitsubishiHeavyHdrSpace,
kMitsubishiHeavyBitMark, kMitsubishiHeavyOneSpace,
kMitsubishiHeavyBitMark, kMitsubishiHeavyZeroSpace,
kMitsubishiHeavyBitMark, kMitsubishiHeavyGap, true,
_tolerance, 0, false);
if (used == 0) return false;
offset += used;
// Compliance
switch (nbits) {
case kMitsubishiHeavy88Bits:
if (strict && !(IRMitsubishiHeavy88Ac::checkZjsSig(results->state) &&
IRMitsubishiHeavy88Ac::validChecksum(results->state)))
return false;
results->decode_type = MITSUBISHI_HEAVY_88;
break;
case kMitsubishiHeavy152Bits:
if (strict && !(IRMitsubishiHeavy152Ac::checkZmsSig(results->state) &&
IRMitsubishiHeavy152Ac::validChecksum(results->state)))
return false;
results->decode_type = MITSUBISHI_HEAVY_152;
break;
default:
return false;
}
// Success
results->bits = nbits;
// No need to record the state as we stored it as we decoded it.
// As we use result->state, we don't record value, address, or command as it
// is a union data type.
return true;
}
#endif // DECODE_MITSUBISHIHEAVY