#include <MitsubishiHeavyHeatpumpIR.h>

// These are protected methods, i.e. generic MitsubishiHeavy instances cannot be created directly
MitsubishiHeavyHeatpumpIR::MitsubishiHeavyHeatpumpIR() : HeatpumpIR()
{
}


// The different models just set the model accordingly

MitsubishiHeavyZJHeatpumpIR::MitsubishiHeavyZJHeatpumpIR() : MitsubishiHeavyHeatpumpIR()
{
  static const char model[] PROGMEM = "mitsubishi_heavy_zj";
  static const char info[]  PROGMEM = "{\"mdl\":\"mitsubishi_heavy_zj\",\"dn\":\"Mitsubishi Heavy ZJ\",\"mT\":18,\"xT\":30,\"fs\":3}";

  _model = model;
  _info = info;

  _mitsubishiModel = MITSUBISHIHEAVY_ZJ;
}

MitsubishiHeavyZMHeatpumpIR::MitsubishiHeavyZMHeatpumpIR() : MitsubishiHeavyHeatpumpIR()
{
  static const char model[] PROGMEM = "mitsubishi_heavy_zm";
  static const char info[]  PROGMEM = "{\"mdl\":\"mitsubishi_heavy_zm\",\"dn\":\"Mitsubishi Heavy ZM\",\"mT\":18,\"xT\":30,\"fs\":4}";

  _model = model;
  _info = info;

  _mitsubishiModel = MITSUBISHIHEAVY_ZM;
}

MitsubishiHeavyZMPHeatpumpIR::MitsubishiHeavyZMPHeatpumpIR() : MitsubishiHeavyHeatpumpIR()
{
  static const char model[] PROGMEM = "mitsubishi_heavy_zmp";
  static const char info[]  PROGMEM = "{\"mdl\":\"mitsubishi_heavy_zmp\",\"dn\":\"Mitsubishi Heavy ZMP\",\"mT\":18,\"xT\":30,\"fs\":3}";

  _model = model;
  _info = info;

  _mitsubishiModel = MITSUBISHIHEAVY_ZMP;
}

void MitsubishiHeavyHeatpumpIR::send(IRSender& IR, uint8_t powerModeCmd, uint8_t operatingModeCmd, uint8_t fanSpeedCmd, uint8_t temperatureCmd, uint8_t swingVCmd, uint8_t swingHCmd)
{
  send(IR, powerModeCmd, operatingModeCmd, fanSpeedCmd, temperatureCmd, swingVCmd, swingHCmd, true, false, false);
}

// This is a virtual dummy method, i.e. MitsubishiHeavyZJHeatpumpIR::send or MitsubishiHeavyZMHeatpumpIR::send is called instead
void MitsubishiHeavyHeatpumpIR::send(IRSender&, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, bool, bool, bool)
{
}


void MitsubishiHeavyZJHeatpumpIR::send(IRSender& IR, uint8_t powerModeCmd, uint8_t operatingModeCmd, uint8_t fanSpeedCmd, uint8_t temperatureCmd, uint8_t swingVCmd, uint8_t swingHCmd, bool cleanModeCmd, bool silentModeCmd, bool _3DAutoCmd)
{
  // Sensible defaults for the heat pump mode

  uint8_t powerMode     = MITSUBISHI_HEAVY_MODE_ON;
  uint8_t operatingMode = MITSUBISHI_HEAVY_MODE_HEAT;
  uint8_t fanSpeed      = MITSUBISHI_HEAVY_ZJ_FAN_AUTO;
  uint8_t temperature   = 23;
  uint8_t swingV        = MITSUBISHI_HEAVY_ZJ_VS_STOP;
  uint8_t swingH        = MITSUBISHI_HEAVY_ZJ_HS_STOP;
  uint8_t cleanMode     = MITSUBISHI_HEAVY_ZJ_CLEAN_OFF;

  if (powerModeCmd == POWER_OFF)
  {
    powerMode = MITSUBISHI_HEAVY_MODE_OFF;
  }

  if (cleanModeCmd && powerModeCmd == POWER_OFF && (operatingModeCmd == MODE_AUTO || operatingModeCmd == MODE_COOL || operatingModeCmd == MODE_DRY))
  {
    powerMode = MITSUBISHI_HEAVY_MODE_ON;
    cleanMode = MITSUBISHI_HEAVY_CLEAN_ON;
  }

  switch (operatingModeCmd)
  {
    case MODE_AUTO:
      operatingMode = MITSUBISHI_HEAVY_MODE_AUTO;
      break;
    case MODE_HEAT:
      operatingMode = MITSUBISHI_HEAVY_MODE_HEAT;
      break;
    case MODE_COOL:
      operatingMode = MITSUBISHI_HEAVY_MODE_COOL;
      break;
    case MODE_DRY:
      operatingMode = MITSUBISHI_HEAVY_MODE_DRY;
      break;
    case MODE_FAN:
      operatingMode = MITSUBISHI_HEAVY_MODE_FAN;
      break;
  }

  switch (fanSpeedCmd)
  {
    case FAN_AUTO:
      fanSpeed = MITSUBISHI_HEAVY_ZJ_FAN_AUTO;
      break;
    case FAN_1:
      fanSpeed = MITSUBISHI_HEAVY_ZJ_FAN1;
      break;
    case FAN_2:
      fanSpeed = MITSUBISHI_HEAVY_ZJ_FAN2;
      break;
    case FAN_3:
      fanSpeed = MITSUBISHI_HEAVY_ZJ_FAN3;
      break;
  }

  if (silentModeCmd)
  {
  	// Silent mode doesn't exist on ZJ model, use ECONO mode instead
    fanSpeed = MITSUBISHI_HEAVY_ZJ_SILENT_ON;
  }

  if ( temperatureCmd > 17 && temperatureCmd < 31)
  {
    temperature = (~((temperatureCmd - 17) << 4)) & 0xF0;
  }

  switch (swingVCmd)
  {
    case VDIR_MANUAL:
      swingV = MITSUBISHI_HEAVY_ZJ_VS_STOP;
      break;
    case VDIR_SWING:
      swingV = MITSUBISHI_HEAVY_ZJ_VS_SWING;
      break;
    case VDIR_UP:
      swingV = MITSUBISHI_HEAVY_ZJ_VS_UP;
      break;
    case VDIR_MUP:
      swingV = MITSUBISHI_HEAVY_ZJ_VS_MUP;
      break;
    case VDIR_MIDDLE:
      swingV = MITSUBISHI_HEAVY_ZJ_VS_MIDDLE;
      break;
    case VDIR_MDOWN:
      swingV = MITSUBISHI_HEAVY_ZJ_VS_MDOWN;
      break;
    case VDIR_DOWN:
      swingV = MITSUBISHI_HEAVY_ZJ_VS_DOWN;
      break;
  }

  switch (swingHCmd)
  {
    case HDIR_MANUAL:
      swingH = MITSUBISHI_HEAVY_ZJ_HS_STOP;
      break;
    case HDIR_SWING:
      swingH = MITSUBISHI_HEAVY_ZJ_HS_SWING;
      break;
    case HDIR_MIDDLE:
      swingH = MITSUBISHI_HEAVY_ZJ_HS_MIDDLE;
      break;
    case HDIR_LEFT:
      swingH = MITSUBISHI_HEAVY_ZJ_HS_LEFT;
      break;
    case HDIR_MLEFT:
      swingH = MITSUBISHI_HEAVY_ZJ_HS_MLEFT;
      break;
    case HDIR_RIGHT:
      swingH = MITSUBISHI_HEAVY_ZJ_HS_RIGHT;
      break;
    case HDIR_MRIGHT:
      swingH = MITSUBISHI_HEAVY_ZJ_HS_MRIGHT;
      break;
  }

  if (_3DAutoCmd == true && (operatingModeCmd == MODE_AUTO || operatingModeCmd == MODE_COOL || operatingModeCmd == MODE_HEAT))
  {
      swingH = MITSUBISHI_HEAVY_ZJ_HS_3DAUTO;
  }
  sendMitsubishiHeavy(IR, powerMode, operatingMode, fanSpeed, temperature, swingV, swingH, cleanMode);
}


void MitsubishiHeavyZMHeatpumpIR::send(IRSender& IR, uint8_t powerModeCmd, uint8_t operatingModeCmd, uint8_t fanSpeedCmd, uint8_t temperatureCmd, uint8_t swingVCmd, uint8_t swingHCmd, bool cleanModeCmd, bool silentModeCmd, bool _3DAutoCmd)
{
  // Sensible defaults for the heat pump mode

  uint8_t powerMode     = MITSUBISHI_HEAVY_MODE_ON;
  uint8_t operatingMode = MITSUBISHI_HEAVY_MODE_HEAT;
  uint8_t fanSpeed      = MITSUBISHI_HEAVY_ZM_FAN_AUTO;
  uint8_t temperature   = 23;
  uint8_t swingV        = MITSUBISHI_HEAVY_ZM_VS_STOP;
  uint8_t swingH        = MITSUBISHI_HEAVY_ZM_HS_STOP;
  uint8_t cleanMode     = MITSUBISHI_HEAVY_ZM_CLEAN_OFF;
  uint8_t _3DAuto       = MITSUBISHI_HEAVY_ZM_3DAUTO_OFF;
  uint8_t silentMode    = MITSUBISHI_HEAVY_ZM_SILENT_OFF;

  if (powerModeCmd == POWER_OFF)
  {
    powerMode = MITSUBISHI_HEAVY_MODE_OFF;
  }

  if (cleanModeCmd && powerModeCmd == POWER_OFF && (operatingModeCmd == MODE_AUTO || operatingModeCmd == MODE_COOL || operatingModeCmd == MODE_DRY))
  {
    powerMode = MITSUBISHI_HEAVY_MODE_ON;
    cleanMode = MITSUBISHI_HEAVY_CLEAN_ON;
  }

  if (silentModeCmd && !(operatingModeCmd == MODE_DRY || operatingModeCmd == MODE_FAN))
  {
    silentMode = MITSUBISHI_HEAVY_ZM_SILENT_ON;
  }

  if (_3DAutoCmd && !(operatingModeCmd == MODE_DRY || operatingModeCmd == MODE_FAN))
  {
    _3DAuto = MITSUBISHI_HEAVY_ZM_3DAUTO_ON;
  }

  switch (operatingModeCmd)
  {
    case MODE_AUTO:
      operatingMode = MITSUBISHI_HEAVY_MODE_AUTO;
      break;
    case MODE_HEAT:
      operatingMode = MITSUBISHI_HEAVY_MODE_HEAT;
      break;
    case MODE_COOL:
      operatingMode = MITSUBISHI_HEAVY_MODE_COOL;
      break;
    case MODE_DRY:
      operatingMode = MITSUBISHI_HEAVY_MODE_DRY;
      break;
    case MODE_FAN:
      operatingMode = MITSUBISHI_HEAVY_MODE_FAN;
      break;
  }

  switch (fanSpeedCmd)
  {
    case FAN_AUTO:
      fanSpeed = MITSUBISHI_HEAVY_ZM_FAN_AUTO;
      break;
    case FAN_1:
      fanSpeed = MITSUBISHI_HEAVY_ZM_FAN1;
      break;
    case FAN_2:
      fanSpeed = MITSUBISHI_HEAVY_ZM_FAN2;
      break;
    case FAN_3:
      fanSpeed = MITSUBISHI_HEAVY_ZM_FAN3;
      break;
    case FAN_4:
      fanSpeed = MITSUBISHI_HEAVY_ZM_FAN4;
      break;
    case FAN_5: //Eco mode
      fanSpeed = MITSUBISHI_HEAVY_ZM_ECONO;
      break;
  }

  if ( temperatureCmd > 17 && temperatureCmd < 31)
  {
    temperature = (~(temperatureCmd - 17) & 0x0F);
  }

  switch (swingVCmd)
  {
    case VDIR_MANUAL:
      swingV = MITSUBISHI_HEAVY_ZM_VS_STOP;
      break;
    case VDIR_SWING:
      swingV = MITSUBISHI_HEAVY_ZM_VS_SWING;
      break;
    case VDIR_UP:
      swingV = MITSUBISHI_HEAVY_ZM_VS_UP;
      break;
    case VDIR_MUP:
      swingV = MITSUBISHI_HEAVY_ZM_VS_MUP;
      break;
    case VDIR_MIDDLE:
      swingV = MITSUBISHI_HEAVY_ZM_VS_MIDDLE;
      break;
    case VDIR_MDOWN:
      swingV = MITSUBISHI_HEAVY_ZM_VS_MDOWN;
      break;
    case VDIR_DOWN:
      swingV = MITSUBISHI_HEAVY_ZM_VS_DOWN;
      break;
  }

  switch (swingHCmd)
  {
    case HDIR_MANUAL:
      swingH = MITSUBISHI_HEAVY_ZM_HS_STOP;
      break;
    case HDIR_SWING:
      swingH = MITSUBISHI_HEAVY_ZM_HS_SWING;
      break;
    case HDIR_MIDDLE:
      swingH = MITSUBISHI_HEAVY_ZM_HS_MIDDLE;
      break;
    case HDIR_LEFT:
      swingH = MITSUBISHI_HEAVY_ZM_HS_LEFT;
      break;
    case HDIR_MLEFT:
      swingH = MITSUBISHI_HEAVY_ZM_HS_MLEFT;
      break;
    case HDIR_RIGHT:
      swingH = MITSUBISHI_HEAVY_ZM_HS_RIGHT;
      break;
    case HDIR_MRIGHT:
      swingH = MITSUBISHI_HEAVY_ZM_HS_MRIGHT;
      break;
  }

  sendMitsubishiHeavy(IR, powerMode, operatingMode, fanSpeed, temperature, swingV, swingH, cleanMode, silentMode, _3DAuto);
}

void MitsubishiHeavyZMPHeatpumpIR::send(IRSender& IR, uint8_t powerModeCmd, uint8_t operatingModeCmd, uint8_t fanSpeedCmd, uint8_t temperatureCmd, uint8_t swingVCmd, uint8_t swingHCmd, bool cleanModeCmd, bool silentModeCmd, bool _3DAutoCmd)
{
  // Sensible defaults for the heat pump mode

  uint8_t powerMode     = MITSUBISHI_HEAVY_MODE_ON;
  uint8_t operatingMode = MITSUBISHI_HEAVY_MODE_HEAT;
  uint8_t fanSpeed      = MITSUBISHI_HEAVY_ZMP_FAN_AUTO;
  uint8_t temperature   = 23;
  uint8_t swingV        = MITSUBISHI_HEAVY_ZMP_VS_STOP;
  uint8_t swingH        = MITSUBISHI_HEAVY_ZMP_HS_STOP;
  uint8_t cleanMode     = MITSUBISHI_HEAVY_ZMP_CLEAN_OFF;

  if (powerModeCmd == POWER_OFF)
  {
    powerMode = MITSUBISHI_HEAVY_MODE_OFF;
  }

  if (operatingModeCmd == MODE_MAINT && powerModeCmd == POWER_OFF)
  {
    powerMode = MITSUBISHI_HEAVY_MODE_ON;
    cleanMode = MITSUBISHI_HEAVY_ZMP_CLEAN_ON;
  }

  switch (operatingModeCmd)
  {
    case MODE_AUTO:
      operatingMode = MITSUBISHI_HEAVY_MODE_AUTO;
      //In MODE_AUTO we need to handle temperature differently. It can range from -6 to +6
      temperature = 0x80 - (0x10*temperatureCmd);
      break;
    case MODE_HEAT:
      operatingMode = MITSUBISHI_HEAVY_MODE_HEAT;
      break;
    case MODE_COOL:
      operatingMode = MITSUBISHI_HEAVY_MODE_COOL;
      break;
    case MODE_DRY:
      operatingMode = MITSUBISHI_HEAVY_MODE_DRY;
      break;
    case MODE_FAN:
      //Fan mode has no temperature setting
      //ZMP model uses different code for fan mode.
      operatingMode = MITSUBISHI_HEAVY_ZMP_MODE_FAN;
      temperature = 0;
      break;
    case MODE_MAINT:
      //Specify maintenance mode to activate clean mode
      operatingMode = MITSUBISHI_HEAVY_ZMP_MODE_MAINT;
      break;
  }

  switch (fanSpeedCmd)
  {
    case FAN_AUTO:
      fanSpeed = MITSUBISHI_HEAVY_ZMP_FAN_AUTO;
      break;
    case FAN_1:
      fanSpeed = MITSUBISHI_HEAVY_ZMP_FAN1;
      break;
    case FAN_2:
      fanSpeed = MITSUBISHI_HEAVY_ZMP_FAN2;
      break;
    case FAN_3:
      fanSpeed = MITSUBISHI_HEAVY_ZMP_FAN3;
      break;
    case FAN_4: //Map FAN_4 to HiPower
      fanSpeed = MITSUBISHI_HEAVY_ZMP_HIPOWER;
      break;
    case FAN_5: //Map FAN_5 to Econo
      fanSpeed = MITSUBISHI_HEAVY_ZMP_ECONO;
      break;
  }

  if (silentModeCmd)
  {
  	// Silent mode doesn't exist on ZMP model, use ECONO mode instead
    fanSpeed = MITSUBISHI_HEAVY_ZMP_SILENT_ON;
  }
  
  if ( temperatureCmd > 17 && temperatureCmd < 31)
  {
    temperature = (~((temperatureCmd - 17) << 4)) & 0xF0;
  }

  switch (swingVCmd)
  {
    case VDIR_MANUAL:
      swingV = MITSUBISHI_HEAVY_ZMP_VS_STOP;
      break;
    case VDIR_SWING:
      swingV = MITSUBISHI_HEAVY_ZMP_VS_SWING;
      break;
    case VDIR_UP:
      swingV = MITSUBISHI_HEAVY_ZMP_VS_UP;
      break;
    case VDIR_MUP:
      swingV = MITSUBISHI_HEAVY_ZMP_VS_MUP;
      break;
    case VDIR_MIDDLE:
      swingV = MITSUBISHI_HEAVY_ZMP_VS_MIDDLE;
      break;
    case VDIR_MDOWN:
      swingV = MITSUBISHI_HEAVY_ZMP_VS_MDOWN;
      break;
    case VDIR_DOWN:
      swingV = MITSUBISHI_HEAVY_ZMP_VS_DOWN;
      break;
  }

  /* ZMP model has no horizontal swing
  */

  LOGLN("Calling sendHeavy from ZMP with");
  LOG(F("PowerMode: "));
  LOGLN(powerMode);
  LOG(F("OperatingMode: "));
  LOGLN(operatingMode);
  LOG(F("FanSpeed: "));
  LOGLN(fanSpeed);
  LOG(F("Temperature: "));
  LOGLN(temperature);
  LOG(F("swingV: "));
  LOGLN(swingV);
  LOG(F("swingH: "));
  LOGLN(swingH);
  LOG(F("cleanMode: "));
  LOGLN(cleanMode);
  sendMitsubishiHeavy(IR, powerMode, operatingMode, fanSpeed, temperature, swingV, swingH, cleanMode);
}

void MitsubishiHeavyZJHeatpumpIR::sendMitsubishiHeavy(IRSender& IR, uint8_t powerMode, uint8_t operatingMode, uint8_t fanSpeed, uint8_t temperature, uint8_t swingV, uint8_t swingH, uint8_t cleanMode)
{
  uint8_t MitsubishiHeavyZJTemplate[] = { 0x52, 0xAE, 0xC3, 0x26, 0xD9, 0x11, 0x00, 0x07, 0x00, 0x00, 0x00 };
  //                                         0     1     2     3     4     5     6     7     8     9    10

  // Horizontal & vertical air flow + allergen + clean + 3D
  MitsubishiHeavyZJTemplate[5] |= swingH | (swingV & 0b00000010) | cleanMode;

  // Vertical air flow + fan speed
  MitsubishiHeavyZJTemplate[7] |= fanSpeed | (swingV & 0b00011000);

  // Power state + operating mode + temperature
  MitsubishiHeavyZJTemplate[9] |= operatingMode | powerMode | temperature;

  // There is no checksum, but some bytes are inverted
  MitsubishiHeavyZJTemplate[6] = ~MitsubishiHeavyZJTemplate[5];
  MitsubishiHeavyZJTemplate[8] = ~MitsubishiHeavyZJTemplate[7];
  MitsubishiHeavyZJTemplate[10] = ~MitsubishiHeavyZJTemplate[9];

  // 38 kHz PWM frequency
  IR.setFrequency(38);

  // Header
  IR.mark(MITSUBISHI_HEAVY_HDR_MARK);
  IR.space(MITSUBISHI_HEAVY_HDR_SPACE);

  // Data
  for (uint8_t i=0; i<sizeof(MitsubishiHeavyZJTemplate); i++) {
    IR.sendIRbyte(MitsubishiHeavyZJTemplate[i], MITSUBISHI_HEAVY_BIT_MARK, MITSUBISHI_HEAVY_ZERO_SPACE, MITSUBISHI_HEAVY_ONE_SPACE);
  }

  // End mark
  IR.mark(MITSUBISHI_HEAVY_BIT_MARK);
  IR.space(0);
}

void MitsubishiHeavyZMHeatpumpIR::sendMitsubishiHeavy(IRSender& IR, uint8_t powerMode, uint8_t operatingMode, uint8_t fanSpeed, uint8_t temperature, uint8_t swingV, uint8_t swingH, uint8_t cleanMode, uint8_t silentMode, uint8_t _3DAuto)
{
  uint8_t MitsubishiHeavyZMTemplate[] = { 0x52, 0xAE, 0xC3, 0x1A, 0xE5, 0x90, 0x00, 0xF0, 0x00, 0xF0, 0x00, 0x0D, 0x00, 0x10, 0x00, 0xFF, 0x00, 0x7B, 0x00 };
  //                                         0     1     2     3     4     5     6     7     8     9    10    11    12    13    14    15    16    17    18

  // Power state + operating mode
  MitsubishiHeavyZMTemplate[5] |= operatingMode | powerMode | cleanMode;

  // Temperature
  MitsubishiHeavyZMTemplate[7] |= temperature;

  // Fan speed
  MitsubishiHeavyZMTemplate[9] |= fanSpeed;

  // Vertical air flow + 3D auto
  MitsubishiHeavyZMTemplate[11] |= swingV | _3DAuto;

  // Horizontal air flow
  MitsubishiHeavyZMTemplate[13] |= swingH | swingV;

  // Silent
  MitsubishiHeavyZMTemplate[15] |= silentMode;

  // There is no checksum, but some bytes are inverted
  MitsubishiHeavyZMTemplate[6] = ~MitsubishiHeavyZMTemplate[5];
  MitsubishiHeavyZMTemplate[8] = ~MitsubishiHeavyZMTemplate[7];
  MitsubishiHeavyZMTemplate[10] = ~MitsubishiHeavyZMTemplate[9];
  MitsubishiHeavyZMTemplate[12] = ~MitsubishiHeavyZMTemplate[11];
  MitsubishiHeavyZMTemplate[14] = ~MitsubishiHeavyZMTemplate[13];
  MitsubishiHeavyZMTemplate[16] = ~MitsubishiHeavyZMTemplate[15];
  MitsubishiHeavyZMTemplate[18] = ~MitsubishiHeavyZMTemplate[17];

  // 38 kHz PWM frequency
  IR.setFrequency(38);

  // Header
  IR.mark(MITSUBISHI_HEAVY_HDR_MARK);
  IR.space(MITSUBISHI_HEAVY_HDR_SPACE);

  // Data
  for (uint8_t i=0; i<sizeof(MitsubishiHeavyZMTemplate); i++) {
    IR.sendIRbyte(MitsubishiHeavyZMTemplate[i], MITSUBISHI_HEAVY_BIT_MARK, MITSUBISHI_HEAVY_ZERO_SPACE, MITSUBISHI_HEAVY_ONE_SPACE);
  }

  // End mark
  IR.mark(MITSUBISHI_HEAVY_BIT_MARK);
  IR.space(0);
}

void MitsubishiHeavyZMPHeatpumpIR::sendMitsubishiHeavy(IRSender& IR, uint8_t powerMode, uint8_t operatingMode, uint8_t fanSpeed, uint8_t temperature, uint8_t swingV, uint8_t swingH, uint8_t cleanMode)
{
  uint8_t MitsubishiHeavyZMPTemplate[] = { 0x52, 0xAE, 0xC3, 0x26, 0xD9, 0x11, 0x00, 0x07, 0x00, 0x00, 0x00 };
  //                                         0     1     2     3     4     5     6     7     8     9    10

  // Horizontal & vertical air flow + allergen + clean + 3D
  MitsubishiHeavyZMPTemplate[5] |= swingH | (swingV & 0b00000010) | cleanMode;

  // Vertical air flow + fan speed
  MitsubishiHeavyZMPTemplate[7] |= fanSpeed | (swingV & 0b00011000);

  // Power state + operating mode + temperature
  MitsubishiHeavyZMPTemplate[9] |= operatingMode | powerMode | temperature;

  // There is no checksum, but some bytes are inverted
  MitsubishiHeavyZMPTemplate[6] = ~MitsubishiHeavyZMPTemplate[5];
  MitsubishiHeavyZMPTemplate[8] = ~MitsubishiHeavyZMPTemplate[7];
  MitsubishiHeavyZMPTemplate[10] = ~MitsubishiHeavyZMPTemplate[9];

  // 38 kHz PWM frequency
  IR.setFrequency(38);

  // Header
  IR.mark(MITSUBISHI_HEAVY_HDR_MARK);
  IR.space(MITSUBISHI_HEAVY_HDR_SPACE);

  // Data
  for (uint8_t i=0; i<sizeof(MitsubishiHeavyZMPTemplate); i++) {
    LOG(F("Byte "));
    LOG(i);
    LOG(F(": "));
    LOGLN(MitsubishiHeavyZMPTemplate[i]);
    IR.sendIRbyte(MitsubishiHeavyZMPTemplate[i], MITSUBISHI_HEAVY_BIT_MARK, MITSUBISHI_HEAVY_ZERO_SPACE, MITSUBISHI_HEAVY_ONE_SPACE);
  }

  // End mark
  IR.mark(MITSUBISHI_HEAVY_BIT_MARK);
  IR.space(0);
}
