mirror of https://github.com/arendst/Tasmota.git
1581 lines
59 KiB
C++
1581 lines
59 KiB
C++
// Copyright 2018-2019 David Conran
|
|
/// @file
|
|
/// @brief Support for Hitachi A/C protocols.
|
|
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/417
|
|
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/453
|
|
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/973
|
|
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1056
|
|
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1060
|
|
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1134
|
|
|
|
#include "ir_Hitachi.h"
|
|
#include <algorithm>
|
|
#include <cstring>
|
|
#ifndef ARDUINO
|
|
#include <string>
|
|
#endif
|
|
#include "IRrecv.h"
|
|
#include "IRremoteESP8266.h"
|
|
#include "IRsend.h"
|
|
#include "IRtext.h"
|
|
#include "IRutils.h"
|
|
|
|
// Constants
|
|
const uint16_t kHitachiAcHdrMark = 3300;
|
|
const uint16_t kHitachiAcHdrSpace = 1700;
|
|
const uint16_t kHitachiAc1HdrMark = 3400;
|
|
const uint16_t kHitachiAc1HdrSpace = 3400;
|
|
const uint16_t kHitachiAcBitMark = 400;
|
|
const uint16_t kHitachiAcOneSpace = 1250;
|
|
const uint16_t kHitachiAcZeroSpace = 500;
|
|
const uint32_t kHitachiAcMinGap = kDefaultMessageGap; // Just a guess.
|
|
// Support for HitachiAc424 protocol
|
|
const uint16_t kHitachiAc424LdrMark = 29784; // Leader
|
|
const uint16_t kHitachiAc424LdrSpace = 49290; // Leader
|
|
const uint16_t kHitachiAc424HdrMark = 3416; // Header
|
|
const uint16_t kHitachiAc424HdrSpace = 1604; // Header
|
|
const uint16_t kHitachiAc424BitMark = 463;
|
|
const uint16_t kHitachiAc424OneSpace = 1208;
|
|
const uint16_t kHitachiAc424ZeroSpace = 372;
|
|
|
|
// Support for HitachiAc3 protocol
|
|
const uint16_t kHitachiAc3HdrMark = 3400; // Header
|
|
const uint16_t kHitachiAc3HdrSpace = 1660; // Header
|
|
const uint16_t kHitachiAc3BitMark = 460;
|
|
const uint16_t kHitachiAc3OneSpace = 1250;
|
|
const uint16_t kHitachiAc3ZeroSpace = 410;
|
|
|
|
using irutils::addBoolToString;
|
|
using irutils::addIntToString;
|
|
using irutils::addLabeledString;
|
|
using irutils::addModeToString;
|
|
using irutils::addModelToString;
|
|
using irutils::addFanToString;
|
|
using irutils::addTempToString;
|
|
using irutils::checkInvertedBytePairs;
|
|
using irutils::invertBytePairs;
|
|
using irutils::minsToString;
|
|
|
|
#if (SEND_HITACHI_AC || SEND_HITACHI_AC2 || SEND_HITACHI_AC344)
|
|
/// Send a Hitachi 28-byte/224-bit A/C formatted message. (HITACHI_AC)
|
|
/// Status: STABLE / Working.
|
|
/// @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.
|
|
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/417
|
|
void IRsend::sendHitachiAC(const unsigned char data[], const uint16_t nbytes,
|
|
const uint16_t repeat) {
|
|
if (nbytes < kHitachiAcStateLength)
|
|
return; // Not enough bytes to send a proper message.
|
|
|
|
const bool MSBfirst = (nbytes == kHitachiAc344StateLength) ? false : true;
|
|
sendGeneric(kHitachiAcHdrMark, kHitachiAcHdrSpace, kHitachiAcBitMark,
|
|
kHitachiAcOneSpace, kHitachiAcBitMark, kHitachiAcZeroSpace,
|
|
kHitachiAcBitMark, kHitachiAcMinGap, data, nbytes, 38, MSBfirst,
|
|
repeat, 50);
|
|
}
|
|
#endif // (SEND_HITACHI_AC || SEND_HITACHI_AC2 || SEND_HITACHI_AC344)
|
|
|
|
#if SEND_HITACHI_AC1
|
|
/// Send a Hitachi 13 byte/224-bit A/C formatted message. (HITACHI_AC1)
|
|
/// Status: STABLE / Confirmed Working.
|
|
/// @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.
|
|
/// @note Basically the same as sendHitatchiAC() except different size & header.
|
|
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/453
|
|
void IRsend::sendHitachiAC1(const unsigned char data[], const uint16_t nbytes,
|
|
const uint16_t repeat) {
|
|
if (nbytes < kHitachiAc1StateLength)
|
|
return; // Not enough bytes to send a proper message.
|
|
sendGeneric(kHitachiAc1HdrMark, kHitachiAc1HdrSpace, kHitachiAcBitMark,
|
|
kHitachiAcOneSpace, kHitachiAcBitMark, kHitachiAcZeroSpace,
|
|
kHitachiAcBitMark, kHitachiAcMinGap, data, nbytes, kHitachiAcFreq,
|
|
true, repeat, kDutyDefault);
|
|
}
|
|
#endif // SEND_HITACHI_AC1
|
|
|
|
#if SEND_HITACHI_AC2
|
|
/// Send a Hitachi 53 byte/424-bit A/C formatted message. (HITACHI_AC2)
|
|
/// Basically the same as sendHitatchiAC() except different size.
|
|
/// Status: STABLE / Expected to work.
|
|
/// @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::sendHitachiAC2(const unsigned char data[], const uint16_t nbytes,
|
|
const uint16_t repeat) {
|
|
if (nbytes < kHitachiAc2StateLength)
|
|
return; // Not enough bytes to send a proper message.
|
|
sendHitachiAC(data, nbytes, repeat);
|
|
}
|
|
#endif // SEND_HITACHI_AC2
|
|
|
|
#if SEND_HITACHI_AC344
|
|
/// Send a Hitachi A/C 43-byte/344-bit message. (HITACHI_AC344)
|
|
/// Basically the same as sendHitatchiAC() except different size.
|
|
/// Status: Beta / Probably works.
|
|
/// @param[in] data An array of bytes containing the IR command.
|
|
/// @param[in] nbytes Nr. of bytes of data in the array.
|
|
/// @param[in] repeat Nr. of times the message is to be repeated. (Default = 0).
|
|
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1134
|
|
void IRsend::sendHitachiAc344(const unsigned char data[], const uint16_t nbytes,
|
|
const uint16_t repeat) {
|
|
if (nbytes < kHitachiAc344StateLength)
|
|
return; // Not enough bytes to send a proper message.
|
|
sendHitachiAC(data, nbytes, repeat);
|
|
}
|
|
#endif // SEND_HITACHI_AC344
|
|
|
|
/// 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?
|
|
IRHitachiAc::IRHitachiAc(const uint16_t pin, const bool inverted,
|
|
const bool use_modulation)
|
|
: _irsend(pin, inverted, use_modulation) { stateReset(); }
|
|
|
|
/// Reset the internal state to a fixed known good state.
|
|
void IRHitachiAc::stateReset(void) {
|
|
_.raw[0] = 0x80;
|
|
_.raw[1] = 0x08;
|
|
_.raw[2] = 0x0C;
|
|
_.raw[3] = 0x02;
|
|
_.raw[4] = 0xFD;
|
|
_.raw[5] = 0x80;
|
|
_.raw[6] = 0x7F;
|
|
_.raw[7] = 0x88;
|
|
_.raw[8] = 0x48;
|
|
_.raw[9] = 0x10;
|
|
for (uint8_t i = 10; i < kHitachiAcStateLength; i++) _.raw[i] = 0x00;
|
|
_.raw[14] = 0x60;
|
|
_.raw[15] = 0x60;
|
|
_.raw[24] = 0x80;
|
|
setTemp(23);
|
|
}
|
|
|
|
/// Set up hardware to be able to send a message.
|
|
void IRHitachiAc::begin(void) { _irsend.begin(); }
|
|
|
|
/// Calculate the checksum for a given state.
|
|
/// @param[in] state The value to calc the checksum of.
|
|
/// @param[in] length The size/length of the state.
|
|
/// @return The calculated checksum value.
|
|
uint8_t IRHitachiAc::calcChecksum(const uint8_t state[],
|
|
const uint16_t length) {
|
|
uint8_t sum = 62;
|
|
for (uint16_t i = 0; i < length - 1; i++) sum -= reverseBits(state[i], 8);
|
|
return reverseBits(sum, 8);
|
|
}
|
|
|
|
/// Calculate and set the checksum values for the internal state.
|
|
/// @param[in] length The size/length of the state.
|
|
void IRHitachiAc::checksum(const uint16_t length) {
|
|
_.Sum = calcChecksum(_.raw, length);
|
|
}
|
|
|
|
/// Verify the checksum is valid for a given state.
|
|
/// @param[in] state The array to verify the checksum of.
|
|
/// @param[in] length The length of the state array.
|
|
/// @return true, if the state has a valid checksum. Otherwise, false.
|
|
bool IRHitachiAc::validChecksum(const uint8_t state[], const uint16_t length) {
|
|
if (length < 2) return true; // Assume true for lengths that are too short.
|
|
return (state[length - 1] == calcChecksum(state, length));
|
|
}
|
|
|
|
/// 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 *IRHitachiAc::getRaw(void) {
|
|
checksum();
|
|
return _.raw;
|
|
}
|
|
|
|
/// Set the internal state from a valid code for this protocol.
|
|
/// @param[in] new_code A valid code for this protocol.
|
|
/// @param[in] length The length of the new_code array.
|
|
void IRHitachiAc::setRaw(const uint8_t new_code[], const uint16_t length) {
|
|
std::memcpy(_.raw, new_code, std::min(length, kHitachiAcStateLength));
|
|
}
|
|
|
|
#if SEND_HITACHI_AC
|
|
/// Send the current internal state as an IR message.
|
|
/// @param[in] repeat Nr. of times the message will be repeated.
|
|
void IRHitachiAc::send(const uint16_t repeat) {
|
|
_irsend.sendHitachiAC(getRaw(), kHitachiAcStateLength, repeat);
|
|
}
|
|
#endif // SEND_HITACHI_AC
|
|
|
|
/// Get the value of the current power setting.
|
|
/// @return true, the setting is on. false, the setting is off.
|
|
bool IRHitachiAc::getPower(void) const {
|
|
return _.Power;
|
|
}
|
|
|
|
/// Change the power setting.
|
|
/// @param[in] on true, the setting is on. false, the setting is off.
|
|
void IRHitachiAc::setPower(const bool on) {
|
|
_.Power = on;
|
|
}
|
|
|
|
/// Change the power setting to On.
|
|
void IRHitachiAc::on(void) { setPower(true); }
|
|
|
|
/// Change the power setting to Off.
|
|
void IRHitachiAc::off(void) { setPower(false); }
|
|
|
|
/// Get the operating mode setting of the A/C.
|
|
/// @return The current operating mode setting.
|
|
uint8_t IRHitachiAc::getMode(void) const { return reverseBits(_.Mode, 8); }
|
|
|
|
/// Set the operating mode of the A/C.
|
|
/// @param[in] mode The desired operating mode.
|
|
void IRHitachiAc::setMode(const uint8_t mode) {
|
|
uint8_t newmode = mode;
|
|
switch (mode) {
|
|
// Fan mode sets a special temp.
|
|
case kHitachiAcFan: setTemp(64); break;
|
|
case kHitachiAcAuto:
|
|
case kHitachiAcHeat:
|
|
case kHitachiAcCool:
|
|
case kHitachiAcDry: break;
|
|
default: newmode = kHitachiAcAuto;
|
|
}
|
|
_.Mode = reverseBits(newmode, 8);
|
|
if (mode != kHitachiAcFan) setTemp(_previoustemp);
|
|
setFan(getFan()); // Reset the fan speed after the mode change.
|
|
}
|
|
|
|
/// Get the current temperature setting.
|
|
/// @return The current setting for temp. in degrees celsius.
|
|
uint8_t IRHitachiAc::getTemp(void) const {
|
|
return reverseBits(_.Temp, 8) >> 1;
|
|
}
|
|
|
|
/// Set the temperature.
|
|
/// @param[in] celsius The temperature in degrees celsius.
|
|
void IRHitachiAc::setTemp(const uint8_t celsius) {
|
|
uint8_t temp;
|
|
if (celsius != 64) _previoustemp = celsius;
|
|
switch (celsius) {
|
|
case 64:
|
|
temp = celsius;
|
|
break;
|
|
default:
|
|
temp = std::min(celsius, kHitachiAcMaxTemp);
|
|
temp = std::max(temp, kHitachiAcMinTemp);
|
|
}
|
|
_.Temp = reverseBits(temp << 1, 8);
|
|
if (temp == kHitachiAcMinTemp)
|
|
_.raw[9] = 0x90;
|
|
else
|
|
_.raw[9] = 0x10;
|
|
}
|
|
|
|
/// Get the current fan speed setting.
|
|
/// @return The current fan speed.
|
|
uint8_t IRHitachiAc::getFan(void) const { return reverseBits(_.Fan, 8); }
|
|
|
|
/// Set the speed of the fan.
|
|
/// @param[in] speed The desired setting.
|
|
void IRHitachiAc::setFan(const uint8_t speed) {
|
|
uint8_t fanmin = kHitachiAcFanAuto;
|
|
uint8_t fanmax = kHitachiAcFanHigh;
|
|
switch (getMode()) {
|
|
case kHitachiAcDry: // Only 2 x low speeds in Dry mode.
|
|
fanmin = kHitachiAcFanLow;
|
|
fanmax = kHitachiAcFanLow + 1;
|
|
break;
|
|
case kHitachiAcFan:
|
|
fanmin = kHitachiAcFanLow; // No Auto in Fan mode.
|
|
break;
|
|
}
|
|
uint8_t newspeed = std::max(speed, fanmin);
|
|
newspeed = std::min(newspeed, fanmax);
|
|
_.Fan = reverseBits(newspeed, 8);
|
|
}
|
|
|
|
/// Get the Vertical Swing setting of the A/C.
|
|
/// @return true, the setting is on. false, the setting is off.
|
|
bool IRHitachiAc::getSwingVertical(void) const {
|
|
return _.SwingV;
|
|
}
|
|
|
|
/// Set the Vertical Swing setting of the A/C.
|
|
/// @param[in] on true, the setting is on. false, the setting is off.
|
|
void IRHitachiAc::setSwingVertical(const bool on) {
|
|
_.SwingV = on;
|
|
}
|
|
|
|
/// Get the Horizontal Swing setting of the A/C.
|
|
/// @return true, the setting is on. false, the setting is off.
|
|
bool IRHitachiAc::getSwingHorizontal(void) const {
|
|
return _.SwingH;
|
|
}
|
|
|
|
/// Set the Horizontal Swing setting of the A/C.
|
|
/// @param[in] on true, the setting is on. false, the setting is off.
|
|
void IRHitachiAc::setSwingHorizontal(const bool on) {
|
|
_.SwingH = on;
|
|
}
|
|
|
|
/// Convert a stdAc::opmode_t enum into its native mode.
|
|
/// @param[in] mode The enum to be converted.
|
|
/// @return The native equivalent of the enum.
|
|
uint8_t IRHitachiAc::convertMode(const stdAc::opmode_t mode) {
|
|
switch (mode) {
|
|
case stdAc::opmode_t::kCool: return kHitachiAcCool;
|
|
case stdAc::opmode_t::kHeat: return kHitachiAcHeat;
|
|
case stdAc::opmode_t::kDry: return kHitachiAcDry;
|
|
case stdAc::opmode_t::kFan: return kHitachiAcFan;
|
|
default: return kHitachiAcAuto;
|
|
}
|
|
}
|
|
|
|
/// Convert a stdAc::fanspeed_t enum into it's native speed.
|
|
/// @param[in] speed The enum to be converted.
|
|
/// @return The native equivalent of the enum.
|
|
uint8_t IRHitachiAc::convertFan(const stdAc::fanspeed_t speed) {
|
|
switch (speed) {
|
|
case stdAc::fanspeed_t::kMin:
|
|
case stdAc::fanspeed_t::kLow: return kHitachiAcFanLow;
|
|
case stdAc::fanspeed_t::kMedium: return kHitachiAcFanLow + 1;
|
|
case stdAc::fanspeed_t::kHigh: return kHitachiAcFanHigh - 1;
|
|
case stdAc::fanspeed_t::kMax: return kHitachiAcFanHigh;
|
|
default: return kHitachiAcFanAuto;
|
|
}
|
|
}
|
|
|
|
/// Convert a native mode into its stdAc equivalent.
|
|
/// @param[in] mode The native setting to be converted.
|
|
/// @return The stdAc equivalent of the native setting.
|
|
stdAc::opmode_t IRHitachiAc::toCommonMode(const uint8_t mode) {
|
|
switch (mode) {
|
|
case kHitachiAcCool: return stdAc::opmode_t::kCool;
|
|
case kHitachiAcHeat: return stdAc::opmode_t::kHeat;
|
|
case kHitachiAcDry: return stdAc::opmode_t::kDry;
|
|
case kHitachiAcFan: return stdAc::opmode_t::kFan;
|
|
default: return stdAc::opmode_t::kAuto;
|
|
}
|
|
}
|
|
|
|
/// Convert a native fan speed into its stdAc equivalent.
|
|
/// @param[in] speed The native setting to be converted.
|
|
/// @return The stdAc equivalent of the native setting.
|
|
stdAc::fanspeed_t IRHitachiAc::toCommonFanSpeed(const uint8_t speed) {
|
|
switch (speed) {
|
|
case kHitachiAcFanHigh: return stdAc::fanspeed_t::kMax;
|
|
case kHitachiAcFanHigh - 1: return stdAc::fanspeed_t::kHigh;
|
|
case kHitachiAcFanLow + 1: return stdAc::fanspeed_t::kMedium;
|
|
case kHitachiAcFanLow: return stdAc::fanspeed_t::kLow;
|
|
default: return stdAc::fanspeed_t::kAuto;
|
|
}
|
|
}
|
|
|
|
/// Convert the current internal state into its stdAc::state_t equivalent.
|
|
/// @return The stdAc equivalent of the native settings.
|
|
stdAc::state_t IRHitachiAc::toCommon(void) const {
|
|
stdAc::state_t result;
|
|
result.protocol = decode_type_t::HITACHI_AC;
|
|
result.model = -1; // No models used.
|
|
result.power = _.Power;
|
|
result.mode = toCommonMode(getMode());
|
|
result.celsius = true;
|
|
result.degrees = getTemp();
|
|
result.fanspeed = toCommonFanSpeed(getFan());
|
|
result.swingv = (_.SwingV ? stdAc::swingv_t::kAuto : stdAc::swingv_t::kOff);
|
|
result.swingh = (_.SwingH ? stdAc::swingh_t::kAuto : stdAc::swingh_t::kOff);
|
|
// Not supported.
|
|
result.quiet = false;
|
|
result.turbo = false;
|
|
result.clean = false;
|
|
result.econo = false;
|
|
result.filter = false;
|
|
result.light = false;
|
|
result.beep = false;
|
|
result.sleep = -1;
|
|
result.clock = -1;
|
|
return result;
|
|
}
|
|
|
|
/// Convert the current internal state into a human readable string.
|
|
/// @return A human readable string.
|
|
String IRHitachiAc::toString(void) const {
|
|
String result = "";
|
|
result.reserve(110); // Reserve some heap for the string to reduce fragging.
|
|
result += addBoolToString(_.Power, kPowerStr, false);
|
|
result += addModeToString(getMode(), kHitachiAcAuto, kHitachiAcCool,
|
|
kHitachiAcHeat, kHitachiAcDry, kHitachiAcFan);
|
|
result += addTempToString(getTemp());
|
|
result += addFanToString(getFan(), kHitachiAcFanHigh, kHitachiAcFanLow,
|
|
kHitachiAcFanAuto, kHitachiAcFanAuto,
|
|
kHitachiAcFanMed);
|
|
result += addBoolToString(_.SwingV, kSwingVStr);
|
|
result += addBoolToString(_.SwingH, kSwingHStr);
|
|
return result;
|
|
}
|
|
|
|
/// 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?
|
|
IRHitachiAc1::IRHitachiAc1(const uint16_t pin, const bool inverted,
|
|
const bool use_modulation)
|
|
: _irsend(pin, inverted, use_modulation) { stateReset(); }
|
|
|
|
/// Reset the internal state to a fixed known good state.
|
|
void IRHitachiAc1::stateReset(void) {
|
|
for (uint8_t i = 0; i < kHitachiAc1StateLength; i++) _.raw[i] = 0x00;
|
|
// Copy in a known good state.
|
|
_.raw[0] = 0xB2;
|
|
_.raw[1] = 0xAE;
|
|
_.raw[2] = 0x4D;
|
|
_.raw[3] = 0x91;
|
|
_.raw[4] = 0xF0;
|
|
_.raw[5] = 0xE1;
|
|
_.raw[6] = 0xA4;
|
|
_.raw[11] = 0x61;
|
|
_.raw[12] = 0x24;
|
|
}
|
|
|
|
/// Set up hardware to be able to send a message.
|
|
void IRHitachiAc1::begin(void) { _irsend.begin(); }
|
|
|
|
/// Calculate the checksum for a given state.
|
|
/// @param[in] state The value to calc the checksum of.
|
|
/// @param[in] length The size/length of the state.
|
|
/// @return The calculated checksum value.
|
|
uint8_t IRHitachiAc1::calcChecksum(const uint8_t state[],
|
|
const uint16_t length) {
|
|
uint8_t sum = 0;
|
|
for (uint16_t i = kHitachiAc1ChecksumStartByte; i < length - 1; i++) {
|
|
sum += reverseBits(GETBITS8(state[i], kLowNibble, kNibbleSize),
|
|
kNibbleSize);
|
|
sum += reverseBits(GETBITS8(state[i], kHighNibble, kNibbleSize),
|
|
kNibbleSize);
|
|
}
|
|
return reverseBits(sum, 8);
|
|
}
|
|
|
|
/// Calculate and set the checksum values for the internal state.
|
|
/// @param[in] length The size/length of the state.
|
|
void IRHitachiAc1::checksum(const uint16_t length) {
|
|
_.Sum = calcChecksum(_.raw, length);
|
|
}
|
|
|
|
/// Verify the checksum is valid for a given state.
|
|
/// @param[in] state The array to verify the checksum of.
|
|
/// @param[in] length The length of the state array.
|
|
/// @return true, if the state has a valid checksum. Otherwise, false.
|
|
bool IRHitachiAc1::validChecksum(const uint8_t state[], const uint16_t length) {
|
|
if (length < 2) return true; // Assume true for lengths that are too short.
|
|
return (state[length - 1] == calcChecksum(state, length));
|
|
}
|
|
|
|
/// 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 *IRHitachiAc1::getRaw(void) {
|
|
checksum();
|
|
return _.raw;
|
|
}
|
|
|
|
/// Set the internal state from a valid code for this protocol.
|
|
/// @param[in] new_code A valid code for this protocol.
|
|
/// @param[in] length The length of the new_code array.
|
|
void IRHitachiAc1::setRaw(const uint8_t new_code[], const uint16_t length) {
|
|
std::memcpy(_.raw, new_code, std::min(length, kHitachiAc1StateLength));
|
|
}
|
|
|
|
#if SEND_HITACHI_AC
|
|
/// Send the current internal state as an IR message.
|
|
/// @param[in] repeat Nr. of times the message will be repeated.
|
|
void IRHitachiAc1::send(const uint16_t repeat) {
|
|
_irsend.sendHitachiAC1(getRaw(), kHitachiAc1StateLength, repeat);
|
|
// Clear the toggle bits as we have actioned them by sending them.
|
|
setPowerToggle(false);
|
|
setSwingToggle(false);
|
|
}
|
|
#endif // SEND_HITACHI_AC
|
|
|
|
/// Get/Detect the model of the A/C.
|
|
/// @return The enum of the compatible model.
|
|
hitachi_ac1_remote_model_t IRHitachiAc1::getModel(void) const {
|
|
switch (_.Model) {
|
|
case kHitachiAc1Model_B: return hitachi_ac1_remote_model_t::R_LT0541_HTA_B;
|
|
default: return hitachi_ac1_remote_model_t::R_LT0541_HTA_A;
|
|
}
|
|
}
|
|
|
|
/// Set the model of the A/C to emulate.
|
|
/// @param[in] model The enum of the appropriate model.
|
|
void IRHitachiAc1::setModel(const hitachi_ac1_remote_model_t model) {
|
|
uint8_t value = 0;
|
|
switch (model) {
|
|
case hitachi_ac1_remote_model_t::R_LT0541_HTA_B:
|
|
value = kHitachiAc1Model_B;
|
|
break;
|
|
default:
|
|
value = kHitachiAc1Model_A; // i.e. 'A' mode.
|
|
}
|
|
_.Model = value;
|
|
}
|
|
|
|
/// Get the value of the current power setting.
|
|
/// @return true, the setting is on. false, the setting is off.
|
|
bool IRHitachiAc1::getPower(void) const {
|
|
return _.Power;
|
|
}
|
|
|
|
/// Change the power setting.
|
|
/// @param[in] on true, the setting is on. false, the setting is off.
|
|
void IRHitachiAc1::setPower(const bool on) {
|
|
// If the power changes, set the power toggle bit.
|
|
if (on != _.Power) setPowerToggle(true);
|
|
_.Power = on;
|
|
}
|
|
|
|
/// Get the value of the current power toggle setting.
|
|
/// @return true, the setting is on. false, the setting is off.
|
|
bool IRHitachiAc1::getPowerToggle(void) const {
|
|
return _.PowerToggle;
|
|
}
|
|
|
|
/// Change the power toggle setting.
|
|
/// @param[in] on true, the setting is on. false, the setting is off.
|
|
void IRHitachiAc1::setPowerToggle(const bool on) {
|
|
_.PowerToggle = on;
|
|
}
|
|
|
|
/// Change the power setting to On.
|
|
void IRHitachiAc1::on(void) { setPower(true); }
|
|
|
|
/// Change the power setting to Off.
|
|
void IRHitachiAc1::off(void) { setPower(false); }
|
|
|
|
/// Get the operating mode setting of the A/C.
|
|
/// @return The current operating mode setting.
|
|
uint8_t IRHitachiAc1::getMode(void) const {
|
|
return _.Mode;
|
|
}
|
|
|
|
/// Set the operating mode of the A/C.
|
|
/// @param[in] mode The desired operating mode.
|
|
void IRHitachiAc1::setMode(const uint8_t mode) {
|
|
switch (mode) {
|
|
case kHitachiAc1Auto:
|
|
setTemp(kHitachiAc1TempAuto);
|
|
// FALL THRU
|
|
case kHitachiAc1Fan:
|
|
case kHitachiAc1Heat:
|
|
case kHitachiAc1Cool:
|
|
case kHitachiAc1Dry:
|
|
_.Mode = mode;
|
|
break;
|
|
default:
|
|
setTemp(kHitachiAc1TempAuto);
|
|
_.Mode = kHitachiAc1Auto;
|
|
break;
|
|
}
|
|
setSleep(_.Sleep); // Correct the sleep mode if required.
|
|
setFan(_.Fan); // Correct the fan speed if required.
|
|
}
|
|
|
|
/// Get the current temperature setting.
|
|
/// @return The current setting for temp. in degrees celsius.
|
|
uint8_t IRHitachiAc1::getTemp(void) const {
|
|
return reverseBits(_.Temp, kHitachiAc1TempSize) + kHitachiAc1TempDelta;
|
|
}
|
|
|
|
/// Set the temperature.
|
|
/// @param[in] celsius The temperature in degrees celsius.
|
|
void IRHitachiAc1::setTemp(const uint8_t celsius) {
|
|
if (_.Mode == kHitachiAc1Auto) return; // Can't change temp in Auto mode.
|
|
uint8_t temp = std::min(celsius, kHitachiAcMaxTemp);
|
|
temp = std::max(temp, kHitachiAcMinTemp);
|
|
temp -= kHitachiAc1TempDelta;
|
|
temp = reverseBits(temp, kHitachiAc1TempSize);
|
|
_.Temp = temp;
|
|
}
|
|
|
|
/// Get the current fan speed setting.
|
|
/// @return The current fan speed.
|
|
uint8_t IRHitachiAc1::getFan(void) const {
|
|
return _.Fan;
|
|
}
|
|
|
|
/// Set the speed of the fan.
|
|
/// @param[in] speed The desired setting.
|
|
/// @param[in] force Deprecated
|
|
void IRHitachiAc1::setFan(const uint8_t speed, const bool /*force*/) {
|
|
// restrictions
|
|
switch (_.Mode) {
|
|
case kHitachiAc1Dry:
|
|
_.Fan = kHitachiAc1FanLow; // Dry is locked to Low speed.
|
|
return;
|
|
case kHitachiAc1Auto:
|
|
_.Fan = kHitachiAc1FanAuto; // Auto is locked to Auto speed.
|
|
return;
|
|
case kHitachiAc1Heat:
|
|
case kHitachiAc1Fan: // Auto speed not allowed in these modes.
|
|
if (speed == kHitachiAc1FanAuto || _.Fan == kHitachiAc1FanAuto)
|
|
_.Fan = kHitachiAc1FanLow;
|
|
return;
|
|
}
|
|
|
|
switch (speed) {
|
|
case kHitachiAc1FanAuto:
|
|
case kHitachiAc1FanHigh:
|
|
case kHitachiAc1FanMed:
|
|
case kHitachiAc1FanLow:
|
|
_.Fan = speed;
|
|
break;
|
|
default: _.Fan = kHitachiAc1FanAuto;
|
|
}
|
|
}
|
|
|
|
/// Get the Swing Toggle setting of the A/C.
|
|
/// @return true, the setting is on. false, the setting is off.
|
|
bool IRHitachiAc1::getSwingToggle(void) const {
|
|
return _.SwingToggle;
|
|
}
|
|
|
|
/// Set the Swing toggle setting of the A/C.
|
|
/// @param[in] toggle true, the setting is on. false, the setting is off.
|
|
void IRHitachiAc1::setSwingToggle(const bool toggle) {
|
|
_.SwingToggle = toggle;
|
|
}
|
|
|
|
/// Get the Vertical Swing setting of the A/C.
|
|
/// @return true, the setting is on. false, the setting is off.
|
|
bool IRHitachiAc1::getSwingV(void) const {
|
|
return _.SwingV;
|
|
}
|
|
|
|
/// Set the Vertical Swing setting of the A/C.
|
|
/// @param[in] on true, the setting is on. false, the setting is off.
|
|
void IRHitachiAc1::setSwingV(const bool on) {
|
|
_.SwingV = on;
|
|
}
|
|
|
|
/// Get the Horizontal Swing setting of the A/C.
|
|
/// @return true, the setting is on. false, the setting is off.
|
|
bool IRHitachiAc1::getSwingH(void) const {
|
|
return _.SwingH;
|
|
}
|
|
|
|
/// Set the Horizontal Swing setting of the A/C.
|
|
/// @param[in] on true, the setting is on. false, the setting is off.
|
|
void IRHitachiAc1::setSwingH(const bool on) {
|
|
_.SwingH = on;
|
|
}
|
|
|
|
/// Get the Sleep setting of the A/C.
|
|
/// @return The currently configured sleep mode.
|
|
/// @note Sleep modes only available in Auto & Cool modes, otherwise it's off.
|
|
uint8_t IRHitachiAc1::getSleep(void) const {
|
|
return _.Sleep;
|
|
}
|
|
|
|
/// Set the Sleep setting of the A/C.
|
|
/// @param[in] mode The mode of sleep to set the A/C to.
|
|
/// @note Sleep modes only available in Auto & Cool modes, otherwise it's off.
|
|
void IRHitachiAc1::setSleep(const uint8_t mode) {
|
|
switch (_.Mode) {
|
|
case kHitachiAc1Auto:
|
|
case kHitachiAc1Cool:
|
|
_.Sleep = std::min(mode, kHitachiAc1Sleep4);
|
|
break;
|
|
default:
|
|
_.Sleep = kHitachiAc1SleepOff;
|
|
}
|
|
}
|
|
|
|
/// Set the On Timer time.
|
|
/// @param[in] mins The time expressed in total number of minutes.
|
|
void IRHitachiAc1::setOnTimer(const uint16_t mins) {
|
|
const uint16_t mins_lsb = reverseBits(mins, kHitachiAc1TimerSize);
|
|
_.OnTimerLow = GETBITS16(mins_lsb, 8, 8);
|
|
_.OnTimerHigh = GETBITS16(mins_lsb, 0, 8);
|
|
}
|
|
|
|
/// Get the On Timer vtime of the A/C.
|
|
/// @return Nr of minutes the timer is set to.
|
|
uint16_t IRHitachiAc1::getOnTimer(void) const {
|
|
return reverseBits(
|
|
(_.OnTimerLow << 8) | _.OnTimerHigh, kHitachiAc1TimerSize);
|
|
}
|
|
|
|
/// Set the Off Timer time.
|
|
/// @param[in] mins The time expressed in total number of minutes.
|
|
void IRHitachiAc1::setOffTimer(const uint16_t mins) {
|
|
const uint16_t mins_lsb = reverseBits(mins, kHitachiAc1TimerSize);
|
|
_.OffTimerLow = GETBITS16(mins_lsb, 8, 8);
|
|
_.OffTimerHigh = GETBITS16(mins_lsb, 0, 8);
|
|
}
|
|
|
|
/// Get the Off Timer vtime of the A/C.
|
|
/// @return Nr of minutes the timer is set to.
|
|
uint16_t IRHitachiAc1::getOffTimer(void) const {
|
|
return reverseBits(
|
|
(_.OffTimerLow << 8) | _.OffTimerHigh, kHitachiAc1TimerSize);
|
|
}
|
|
|
|
/// Convert a stdAc::opmode_t enum into its native mode.
|
|
/// @param[in] mode The enum to be converted.
|
|
/// @return The native equivalent of the enum.
|
|
uint8_t IRHitachiAc1::convertMode(const stdAc::opmode_t mode) {
|
|
switch (mode) {
|
|
case stdAc::opmode_t::kCool: return kHitachiAc1Cool;
|
|
case stdAc::opmode_t::kHeat: return kHitachiAc1Heat;
|
|
case stdAc::opmode_t::kDry: return kHitachiAc1Dry;
|
|
case stdAc::opmode_t::kFan: return kHitachiAc1Fan;
|
|
default: return kHitachiAc1Auto;
|
|
}
|
|
}
|
|
|
|
/// Convert a stdAc::fanspeed_t enum into it's native speed.
|
|
/// @param[in] speed The enum to be converted.
|
|
/// @return The native equivalent of the enum.
|
|
uint8_t IRHitachiAc1::convertFan(const stdAc::fanspeed_t speed) {
|
|
switch (speed) {
|
|
case stdAc::fanspeed_t::kMin:
|
|
case stdAc::fanspeed_t::kLow: return kHitachiAc1FanLow;
|
|
case stdAc::fanspeed_t::kMedium: return kHitachiAc1FanMed;
|
|
case stdAc::fanspeed_t::kHigh:
|
|
case stdAc::fanspeed_t::kMax: return kHitachiAc1FanHigh;
|
|
default: return kHitachiAc1FanAuto;
|
|
}
|
|
}
|
|
|
|
/// Convert a native mode into its stdAc equivalent.
|
|
/// @param[in] mode The native setting to be converted.
|
|
/// @return The stdAc equivalent of the native setting.
|
|
stdAc::opmode_t IRHitachiAc1::toCommonMode(const uint8_t mode) {
|
|
switch (mode) {
|
|
case kHitachiAc1Cool: return stdAc::opmode_t::kCool;
|
|
case kHitachiAc1Heat: return stdAc::opmode_t::kHeat;
|
|
case kHitachiAc1Dry: return stdAc::opmode_t::kDry;
|
|
case kHitachiAc1Fan: return stdAc::opmode_t::kFan;
|
|
default: return stdAc::opmode_t::kAuto;
|
|
}
|
|
}
|
|
|
|
/// Convert a native fan speed into its stdAc equivalent.
|
|
/// @param[in] speed The native setting to be converted.
|
|
/// @return The stdAc equivalent of the native setting.
|
|
stdAc::fanspeed_t IRHitachiAc1::toCommonFanSpeed(const uint8_t speed) {
|
|
switch (speed) {
|
|
case kHitachiAc1FanHigh: return stdAc::fanspeed_t::kMax;
|
|
case kHitachiAc1FanMed: return stdAc::fanspeed_t::kMedium;
|
|
case kHitachiAc1FanLow: return stdAc::fanspeed_t::kLow;
|
|
default: return stdAc::fanspeed_t::kAuto;
|
|
}
|
|
}
|
|
|
|
/// Convert the current internal state into its stdAc::state_t equivalent.
|
|
/// @return The stdAc equivalent of the native settings.
|
|
stdAc::state_t IRHitachiAc1::toCommon(void) const {
|
|
stdAc::state_t result;
|
|
result.protocol = decode_type_t::HITACHI_AC1;
|
|
result.model = getModel();
|
|
result.power = _.Power;
|
|
result.mode = toCommonMode(_.Mode);
|
|
result.celsius = true;
|
|
result.degrees = getTemp();
|
|
result.fanspeed = toCommonFanSpeed(_.Fan);
|
|
result.swingv = _.SwingV ? stdAc::swingv_t::kAuto :
|
|
stdAc::swingv_t::kOff;
|
|
result.swingh = _.SwingH ? stdAc::swingh_t::kAuto :
|
|
stdAc::swingh_t::kOff;
|
|
result.sleep = _.Sleep ? 0 : -1;
|
|
// Not supported.
|
|
result.quiet = false;
|
|
result.turbo = false;
|
|
result.clean = false;
|
|
result.econo = false;
|
|
result.filter = false;
|
|
result.light = false;
|
|
result.beep = false;
|
|
result.clock = -1;
|
|
return result;
|
|
}
|
|
|
|
/// Convert the current internal state into a human readable string.
|
|
/// @return A human readable string.
|
|
String IRHitachiAc1::toString(void) const {
|
|
String result = "";
|
|
result.reserve(170); // Reserve some heap for the string to reduce fragging.
|
|
result += addModelToString(decode_type_t::HITACHI_AC1, getModel(), false);
|
|
result += addBoolToString(_.Power, kPowerStr);
|
|
result += addBoolToString(_.PowerToggle, kPowerToggleStr);
|
|
result += addModeToString(_.Mode, kHitachiAc1Auto, kHitachiAc1Cool,
|
|
kHitachiAc1Heat, kHitachiAc1Dry, kHitachiAc1Fan);
|
|
result += addTempToString(getTemp());
|
|
result += addFanToString(_.Fan, kHitachiAc1FanHigh, kHitachiAc1FanLow,
|
|
kHitachiAc1FanAuto, kHitachiAc1FanAuto,
|
|
kHitachiAc1FanMed);
|
|
result += addBoolToString(_.SwingToggle, kSwingVToggleStr);
|
|
result += addBoolToString(_.SwingV, kSwingVStr);
|
|
result += addBoolToString(_.SwingH, kSwingHStr);
|
|
result += addLabeledString(_.Sleep ? uint64ToString(_.Sleep) : kOffStr,
|
|
kSleepStr);
|
|
result += addLabeledString(getOnTimer() ? minsToString(getOnTimer())
|
|
: kOffStr,
|
|
kOnTimerStr);
|
|
result += addLabeledString(getOffTimer() ? minsToString(getOffTimer())
|
|
: kOffStr,
|
|
kOffTimerStr);
|
|
return result;
|
|
}
|
|
|
|
#if (DECODE_HITACHI_AC || DECODE_HITACHI_AC1 || DECODE_HITACHI_AC2 || \
|
|
DECODE_HITACHI_AC344)
|
|
/// Decode the supplied Hitachi A/C message.
|
|
/// Status: STABLE / Expected to work.
|
|
/// @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 kHitachiAcBits, kHitachiAc1Bits, kHitachiAc2Bits,
|
|
/// kHitachiAc344Bits
|
|
/// @param[in] strict Flag indicating if we should perform strict matching.
|
|
/// @param[in] MSBfirst Is the data per byte stored in MSB First (true) or
|
|
/// LSB First order(false)?
|
|
/// @return True if it can decode it, false if it can't.
|
|
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/417
|
|
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/453
|
|
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1134
|
|
bool IRrecv::decodeHitachiAC(decode_results *results, uint16_t offset,
|
|
const uint16_t nbits, const bool strict,
|
|
const bool MSBfirst) {
|
|
const uint8_t k_tolerance = _tolerance + 5;
|
|
|
|
if (strict) {
|
|
switch (nbits) {
|
|
case kHitachiAcBits:
|
|
case kHitachiAc1Bits:
|
|
case kHitachiAc2Bits:
|
|
case kHitachiAc344Bits:
|
|
break; // Okay to continue.
|
|
default:
|
|
return false; // Not strictly a Hitachi message.
|
|
}
|
|
}
|
|
uint16_t hmark;
|
|
uint32_t hspace;
|
|
if (nbits == kHitachiAc1Bits) {
|
|
hmark = kHitachiAc1HdrMark;
|
|
hspace = kHitachiAc1HdrSpace;
|
|
} else {
|
|
hmark = kHitachiAcHdrMark;
|
|
hspace = kHitachiAcHdrSpace;
|
|
}
|
|
// Match Header + Data + Footer
|
|
if (!matchGeneric(results->rawbuf + offset, results->state,
|
|
results->rawlen - offset, nbits,
|
|
hmark, hspace,
|
|
kHitachiAcBitMark, kHitachiAcOneSpace,
|
|
kHitachiAcBitMark, kHitachiAcZeroSpace,
|
|
kHitachiAcBitMark, kHitachiAcMinGap, true,
|
|
k_tolerance, kMarkExcess, MSBfirst)) return false;
|
|
|
|
// Compliance
|
|
if (strict) {
|
|
if (nbits / 8 == kHitachiAcStateLength &&
|
|
!IRHitachiAc::validChecksum(results->state, kHitachiAcStateLength))
|
|
return false;
|
|
if (nbits / 8 == kHitachiAc1StateLength &&
|
|
!IRHitachiAc1::validChecksum(results->state, kHitachiAc1StateLength))
|
|
return false;
|
|
if (nbits / 8 == kHitachiAc344StateLength &&
|
|
!IRHitachiAc3::hasInvertedStates(results->state,
|
|
kHitachiAc344StateLength))
|
|
return false;
|
|
}
|
|
|
|
// Success
|
|
switch (nbits) {
|
|
case kHitachiAc1Bits:
|
|
results->decode_type = decode_type_t::HITACHI_AC1;
|
|
break;
|
|
case kHitachiAc2Bits:
|
|
results->decode_type = decode_type_t::HITACHI_AC2;
|
|
break;
|
|
case kHitachiAc344Bits:
|
|
results->decode_type = decode_type_t::HITACHI_AC344;
|
|
break;
|
|
case kHitachiAcBits:
|
|
default:
|
|
results->decode_type = decode_type_t::HITACHI_AC;
|
|
}
|
|
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_HITACHI_AC || DECODE_HITACHI_AC1 || DECODE_HITACHI_AC2 ||
|
|
// DECODE_HITACHI_AC344)
|
|
|
|
#if SEND_HITACHI_AC424
|
|
/// Send a Hitachi 53-byte/424-bit A/C formatted message. (HITACHI_AC424)
|
|
/// Status: STABLE / Reported as working.
|
|
/// @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.
|
|
/// @note This protocol is almost exactly the same as HitachiAC2 except this
|
|
/// variant has a leader section as well, and subtle timing differences.
|
|
/// It is also in LSBF order (per byte), rather than MSBF order.
|
|
void IRsend::sendHitachiAc424(const uint8_t data[], const uint16_t nbytes,
|
|
const uint16_t repeat) {
|
|
enableIROut(kHitachiAcFreq);
|
|
for (uint16_t r = 0; r <= repeat; r++) {
|
|
// Leader
|
|
mark(kHitachiAc424LdrMark);
|
|
space(kHitachiAc424LdrSpace);
|
|
// Header + Data + Footer
|
|
sendGeneric(kHitachiAc424HdrMark, kHitachiAc424HdrSpace,
|
|
kHitachiAc424BitMark, kHitachiAc424OneSpace,
|
|
kHitachiAc424BitMark, kHitachiAc424ZeroSpace,
|
|
kHitachiAc424BitMark, kHitachiAcMinGap,
|
|
data, nbytes, // Bytes
|
|
kHitachiAcFreq, false, kNoRepeat, kDutyDefault);
|
|
}
|
|
}
|
|
#endif // SEND_HITACHI_AC424
|
|
|
|
#if DECODE_HITACHI_AC424
|
|
/// Decode the supplied Hitachi 53-byte/424-bit A/C message.
|
|
/// Status: STABLE / Reported as working.
|
|
/// @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.
|
|
/// @param[in] strict Flag indicating if we should perform strict matching.
|
|
/// @return True if it can decode it, false if it can't.
|
|
/// @note This protocol is almost exactly the same as HitachiAC2 except this
|
|
/// variant has a leader section as well, and subtle timing differences.
|
|
/// It is also in LSBF order (per byte), rather than MSBF order.
|
|
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/973
|
|
/// @see (Japanese Manual) https://kadenfan.hitachi.co.jp/support/raj/item/docs/ras_aj22h_a_tori.pdf
|
|
bool IRrecv::decodeHitachiAc424(decode_results *results, uint16_t offset,
|
|
const uint16_t nbits,
|
|
const bool strict) {
|
|
if (results->rawlen < 2 * nbits + kHeader + kHeader + kFooter - 1 + offset)
|
|
return false; // Too short a message to match.
|
|
if (strict && nbits != kHitachiAc424Bits)
|
|
return false;
|
|
|
|
uint16_t used;
|
|
|
|
// Leader
|
|
if (!matchMark(results->rawbuf[offset++], kHitachiAc424LdrMark))
|
|
return false;
|
|
if (!matchSpace(results->rawbuf[offset++], kHitachiAc424LdrSpace))
|
|
return false;
|
|
|
|
// Header + Data + Footer
|
|
used = matchGeneric(results->rawbuf + offset, results->state,
|
|
results->rawlen - offset, nbits,
|
|
kHitachiAc424HdrMark, kHitachiAc424HdrSpace,
|
|
kHitachiAc424BitMark, kHitachiAc424OneSpace,
|
|
kHitachiAc424BitMark, kHitachiAc424ZeroSpace,
|
|
kHitachiAc424BitMark, kHitachiAcMinGap, true,
|
|
kUseDefTol, 0, false);
|
|
if (used == 0) return false; // We failed to find any data.
|
|
|
|
// Success
|
|
results->decode_type = decode_type_t::HITACHI_AC424;
|
|
results->bits = nbits;
|
|
return true;
|
|
}
|
|
#endif // DECODE_HITACHI_AC424
|
|
|
|
/// 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?
|
|
IRHitachiAc424::IRHitachiAc424(const uint16_t pin, const bool inverted,
|
|
const bool use_modulation)
|
|
: _irsend(pin, inverted, use_modulation) { stateReset(); }
|
|
|
|
/// Reset the internal state to a fixed known good state.
|
|
/// @note Reset to auto fan, cooling, 23° Celsius
|
|
void IRHitachiAc424::stateReset(void) {
|
|
for (uint8_t i = 0; i < kHitachiAc424StateLength; i++)
|
|
_.raw[i] = 0x00;
|
|
|
|
_.raw[0] = 0x01;
|
|
_.raw[1] = 0x10;
|
|
_.raw[3] = 0x40;
|
|
_.raw[5] = 0xFF;
|
|
_.raw[7] = 0xCC;
|
|
_.raw[33] = 0x80;
|
|
_.raw[35] = 0x03;
|
|
_.raw[37] = 0x01;
|
|
_.raw[39] = 0x88;
|
|
_.raw[45] = 0xFF;
|
|
_.raw[47] = 0xFF;
|
|
_.raw[49] = 0xFF;
|
|
_.raw[51] = 0xFF;
|
|
|
|
setTemp(23);
|
|
setPower(true);
|
|
setMode(kHitachiAc424Cool);
|
|
setFan(kHitachiAc424FanAuto);
|
|
}
|
|
|
|
/// Update the internal consistency check for the protocol.
|
|
void IRHitachiAc424::setInvertedStates(void) {
|
|
invertBytePairs(_.raw + 3, kHitachiAc424StateLength - 3);
|
|
}
|
|
|
|
/// Set up hardware to be able to send a message.
|
|
void IRHitachiAc424::begin(void) { _irsend.begin(); }
|
|
|
|
/// 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 *IRHitachiAc424::getRaw(void) {
|
|
setInvertedStates();
|
|
return _.raw;
|
|
}
|
|
|
|
/// Set the internal state from a valid code for this protocol.
|
|
/// @param[in] new_code A valid code for this protocol.
|
|
/// @param[in] length The length of the new_code array.
|
|
void IRHitachiAc424::setRaw(const uint8_t new_code[], const uint16_t length) {
|
|
std::memcpy(_.raw, new_code, std::min(length, kHitachiAc424StateLength));
|
|
}
|
|
|
|
#if SEND_HITACHI_AC424
|
|
/// Send the current internal state as an IR message.
|
|
/// @param[in] repeat Nr. of times the message will be repeated.
|
|
void IRHitachiAc424::send(const uint16_t repeat) {
|
|
_irsend.sendHitachiAc424(getRaw(), kHitachiAc424StateLength, repeat);
|
|
}
|
|
#endif // SEND_HITACHI_AC424
|
|
|
|
/// Get the value of the current power setting.
|
|
/// @return true, the setting is on. false, the setting is off.
|
|
bool IRHitachiAc424::getPower(void) const {
|
|
return _.Power == kHitachiAc424PowerOn;
|
|
}
|
|
|
|
/// Change the power setting.
|
|
/// @param[in] on true, the setting is on. false, the setting is off.
|
|
void IRHitachiAc424::setPower(const bool on) {
|
|
setButton(kHitachiAc424ButtonPowerMode);
|
|
_.Power = (on ? kHitachiAc424PowerOn : kHitachiAc424PowerOff);
|
|
}
|
|
|
|
/// Change the power setting to On.
|
|
void IRHitachiAc424::on(void) { setPower(true); }
|
|
|
|
/// Change the power setting to Off.
|
|
void IRHitachiAc424::off(void) { setPower(false); }
|
|
|
|
/// Get the operating mode setting of the A/C.
|
|
/// @return The current operating mode setting.
|
|
uint8_t IRHitachiAc424::getMode(void) const {
|
|
return _.Mode;
|
|
}
|
|
|
|
/// Set the operating mode of the A/C.
|
|
/// @param[in] mode The desired operating mode.
|
|
void IRHitachiAc424::setMode(const uint8_t mode) {
|
|
uint8_t newMode = mode;
|
|
switch (mode) {
|
|
// Fan mode sets a special temp.
|
|
case kHitachiAc424Fan: setTemp(kHitachiAc424FanTemp, false); break;
|
|
case kHitachiAc424Heat:
|
|
case kHitachiAc424Cool:
|
|
case kHitachiAc424Dry: break;
|
|
default: newMode = kHitachiAc424Cool;
|
|
}
|
|
_.Mode = newMode;
|
|
if (newMode != kHitachiAc424Fan) setTemp(_previoustemp);
|
|
setFan(_.Fan); // Reset the fan speed after the mode change.
|
|
setButton(kHitachiAc424ButtonPowerMode);
|
|
}
|
|
|
|
/// Get the current temperature setting.
|
|
/// @return The current setting for temp. in degrees celsius.
|
|
uint8_t IRHitachiAc424::getTemp(void) const {
|
|
return _.Temp;
|
|
}
|
|
|
|
/// Set the temperature.
|
|
/// @param[in] celsius The temperature in degrees celsius.
|
|
/// @param[in] setPrevious true, remember this if we change mode. false, don't.
|
|
void IRHitachiAc424::setTemp(const uint8_t celsius, bool setPrevious) {
|
|
uint8_t temp;
|
|
temp = std::min(celsius, kHitachiAc424MaxTemp);
|
|
temp = std::max(temp, kHitachiAc424MinTemp);
|
|
_.Temp = temp;
|
|
if (_previoustemp > temp)
|
|
setButton(kHitachiAc424ButtonTempDown);
|
|
else if (_previoustemp < temp)
|
|
setButton(kHitachiAc424ButtonTempUp);
|
|
if (setPrevious) _previoustemp = temp;
|
|
}
|
|
|
|
/// Get the current fan speed setting.
|
|
/// @return The current fan speed.
|
|
uint8_t IRHitachiAc424::getFan(void) const {
|
|
return _.Fan;
|
|
}
|
|
|
|
/// Set the speed of the fan.
|
|
/// @param[in] speed The desired setting.
|
|
void IRHitachiAc424::setFan(const uint8_t speed) {
|
|
uint8_t newSpeed = std::max(speed, kHitachiAc424FanMin);
|
|
uint8_t fanMax = kHitachiAc424FanMax;
|
|
|
|
// Only 2 x low speeds in Dry mode or Auto
|
|
if (_.Mode == kHitachiAc424Dry && speed == kHitachiAc424FanAuto) {
|
|
fanMax = kHitachiAc424FanAuto;
|
|
} else if (_.Mode == kHitachiAc424Dry) {
|
|
fanMax = kHitachiAc424FanMaxDry;
|
|
} else if (_.Mode == kHitachiAc424Fan && speed == kHitachiAc424FanAuto) {
|
|
// Fan Mode does not have auto. Set to safe low
|
|
newSpeed = kHitachiAc424FanMin;
|
|
}
|
|
|
|
newSpeed = std::min(newSpeed, fanMax);
|
|
// Handle the setting the button value if we are going to change the value.
|
|
if (newSpeed != _.Fan) setButton(kHitachiAc424ButtonFan);
|
|
// Set the values
|
|
_.Fan = newSpeed;
|
|
_.raw[9] = 0x92;
|
|
_.raw[29] = 0x00;
|
|
|
|
// When fan is at min/max, additional bytes seem to be set
|
|
if (newSpeed == kHitachiAc424FanMin) _.raw[9] = 0x98;
|
|
if (newSpeed == kHitachiAc424FanMax) {
|
|
_.raw[9] = 0xA9;
|
|
_.raw[29] = 0x30;
|
|
}
|
|
}
|
|
|
|
/// Get the Button/Command setting of the A/C.
|
|
/// @return The value of the button/command that was pressed.
|
|
uint8_t IRHitachiAc424::getButton(void) const {
|
|
return _.Button;
|
|
}
|
|
|
|
/// Set the Button/Command pressed setting of the A/C.
|
|
/// @param[in] button The value of the button/command that was pressed.
|
|
void IRHitachiAc424::setButton(const uint8_t button) {
|
|
_.Button = button;
|
|
}
|
|
|
|
/// Set the Vertical Swing toggle setting of the A/C.
|
|
/// @param[in] on true, the setting is on. false, the setting is off.
|
|
/// @note The remote does not keep state of the vertical swing.
|
|
/// A byte is sent indicating the swing button is pressed on the remote
|
|
void IRHitachiAc424::setSwingVToggle(const bool on) {
|
|
uint8_t button = _.Button; // Get the current button value.
|
|
if (on)
|
|
button = kHitachiAc424ButtonSwingV; // Set the button to SwingV.
|
|
else if (button == kHitachiAc424ButtonSwingV) // Asked to unset it
|
|
// It was set previous, so use Power as a default
|
|
button = kHitachiAc424ButtonPowerMode;
|
|
setButton(button);
|
|
}
|
|
|
|
/// Get the Vertical Swing toggle setting of the A/C.
|
|
/// @return true, the setting is on. false, the setting is off.
|
|
bool IRHitachiAc424::getSwingVToggle(void) const {
|
|
return _.Button == kHitachiAc424ButtonSwingV;
|
|
}
|
|
|
|
/// Convert a stdAc::opmode_t enum into its native mode.
|
|
/// @param[in] mode The enum to be converted.
|
|
/// @return The native equivalent of the enum.
|
|
uint8_t IRHitachiAc424::convertMode(const stdAc::opmode_t mode) {
|
|
switch (mode) {
|
|
case stdAc::opmode_t::kCool: return kHitachiAc424Cool;
|
|
case stdAc::opmode_t::kHeat: return kHitachiAc424Heat;
|
|
case stdAc::opmode_t::kDry: return kHitachiAc424Dry;
|
|
case stdAc::opmode_t::kFan: return kHitachiAc424Fan;
|
|
default: return kHitachiAc424Cool;
|
|
}
|
|
}
|
|
|
|
/// Convert a stdAc::fanspeed_t enum into it's native speed.
|
|
/// @param[in] speed The enum to be converted.
|
|
/// @return The native equivalent of the enum.
|
|
uint8_t IRHitachiAc424::convertFan(const stdAc::fanspeed_t speed) {
|
|
switch (speed) {
|
|
case stdAc::fanspeed_t::kMin: return kHitachiAc424FanMin;
|
|
case stdAc::fanspeed_t::kLow: return kHitachiAc424FanLow;
|
|
case stdAc::fanspeed_t::kMedium: return kHitachiAc424FanMedium;
|
|
case stdAc::fanspeed_t::kHigh: return kHitachiAc424FanHigh;
|
|
case stdAc::fanspeed_t::kMax: return kHitachiAc424FanMax;
|
|
default: return kHitachiAc424FanAuto;
|
|
}
|
|
}
|
|
|
|
/// Convert a native mode into its stdAc equivalent.
|
|
/// @param[in] mode The native setting to be converted.
|
|
/// @return The stdAc equivalent of the native setting.
|
|
stdAc::opmode_t IRHitachiAc424::toCommonMode(const uint8_t mode) {
|
|
switch (mode) {
|
|
case kHitachiAc424Cool: return stdAc::opmode_t::kCool;
|
|
case kHitachiAc424Heat: return stdAc::opmode_t::kHeat;
|
|
case kHitachiAc424Dry: return stdAc::opmode_t::kDry;
|
|
case kHitachiAc424Fan: return stdAc::opmode_t::kFan;
|
|
default: return stdAc::opmode_t::kCool;
|
|
}
|
|
}
|
|
|
|
/// Convert a native fan speed into its stdAc equivalent.
|
|
/// @param[in] speed The native setting to be converted.
|
|
/// @return The stdAc equivalent of the native setting.
|
|
stdAc::fanspeed_t IRHitachiAc424::toCommonFanSpeed(const uint8_t speed) {
|
|
switch (speed) {
|
|
case kHitachiAc424FanMax: return stdAc::fanspeed_t::kMax;
|
|
case kHitachiAc424FanHigh: return stdAc::fanspeed_t::kHigh;
|
|
case kHitachiAc424FanMedium: return stdAc::fanspeed_t::kMedium;
|
|
case kHitachiAc424FanLow: return stdAc::fanspeed_t::kLow;
|
|
case kHitachiAc424FanMin: return stdAc::fanspeed_t::kMin;
|
|
default: return stdAc::fanspeed_t::kAuto;
|
|
}
|
|
}
|
|
|
|
/// Convert the current internal state into its stdAc::state_t equivalent.
|
|
/// @return The stdAc equivalent of the native settings.
|
|
stdAc::state_t IRHitachiAc424::toCommon(void) const {
|
|
stdAc::state_t result;
|
|
result.protocol = decode_type_t::HITACHI_AC424;
|
|
result.model = -1; // No models used.
|
|
result.power = getPower();
|
|
result.mode = toCommonMode(_.Mode);
|
|
result.celsius = true;
|
|
result.degrees = _.Temp;
|
|
result.fanspeed = toCommonFanSpeed(_.Fan);
|
|
result.swingv = getSwingVToggle() ? stdAc::swingv_t::kAuto
|
|
: stdAc::swingv_t::kOff;
|
|
// Not supported.
|
|
result.swingh = stdAc::swingh_t::kOff;
|
|
result.quiet = false;
|
|
result.turbo = false;
|
|
result.clean = false;
|
|
result.econo = 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 for the settings
|
|
/// that are common to protocols of this nature.
|
|
/// @return A string containing the common settings in human-readable form.
|
|
String IRHitachiAc424::_toString(void) const {
|
|
String result = "";
|
|
result.reserve(100); // Reserve some heap for the string to reduce fragging.
|
|
result += addBoolToString(getPower(), kPowerStr, false);
|
|
result += addModeToString(_.Mode, 0, kHitachiAc424Cool,
|
|
kHitachiAc424Heat, kHitachiAc424Dry,
|
|
kHitachiAc424Fan);
|
|
result += addTempToString(_.Temp);
|
|
result += addIntToString(_.Fan, kFanStr);
|
|
result += kSpaceLBraceStr;
|
|
switch (_.Fan) {
|
|
case kHitachiAc424FanAuto: result += kAutoStr; break;
|
|
case kHitachiAc424FanMax: result += kMaxStr; break;
|
|
case kHitachiAc424FanHigh: result += kHighStr; break;
|
|
case kHitachiAc424FanMedium: result += kMedStr; break;
|
|
case kHitachiAc424FanLow: result += kLowStr; break;
|
|
case kHitachiAc424FanMin: result += kMinStr; break;
|
|
default: result += kUnknownStr;
|
|
}
|
|
result += ')';
|
|
result += addIntToString(_.Button, kButtonStr);
|
|
result += kSpaceLBraceStr;
|
|
switch (_.Button) {
|
|
case kHitachiAc424ButtonPowerMode:
|
|
result += kPowerStr;
|
|
result += '/';
|
|
result += kModeStr;
|
|
break;
|
|
case kHitachiAc424ButtonFan: result += kFanStr; break;
|
|
case kHitachiAc424ButtonSwingV: result += kSwingVStr; break;
|
|
case kHitachiAc344ButtonSwingH: result += kSwingHStr; break;
|
|
case kHitachiAc424ButtonTempDown: result += kTempDownStr; break;
|
|
case kHitachiAc424ButtonTempUp: result += kTempUpStr; break;
|
|
default: result += kUnknownStr;
|
|
}
|
|
result += ')';
|
|
return result;
|
|
}
|
|
|
|
/// Convert the internal state into a human readable string.
|
|
/// @return A string containing the settings in human-readable form.
|
|
String IRHitachiAc424::toString(void) const {
|
|
return _toString() + addBoolToString(getSwingVToggle(), kSwingVToggleStr);
|
|
}
|
|
|
|
|
|
#if SEND_HITACHI_AC3
|
|
/// Send a Hitachi(3) A/C formatted message. (HITACHI_AC3)
|
|
/// Status: STABLE / Working fine.
|
|
/// @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.
|
|
/// @note This protocol is almost exactly the same as HitachiAC424 except this
|
|
/// variant has subtle timing differences. There are five(5) typical sizes:
|
|
/// kHitachiAc3MinStateLength (Cancel Timer),
|
|
/// kHitachiAc3MinStateLength + 2 (Change Temp),
|
|
/// kHitachiAc3StateLength - 6 (Change Mode),
|
|
/// kHitachiAc3StateLength - 4 (Normal), &
|
|
/// kHitachiAc3StateLength (Set Timer)
|
|
void IRsend::sendHitachiAc3(const uint8_t data[], const uint16_t nbytes,
|
|
const uint16_t repeat) {
|
|
// Header + Data + Footer
|
|
sendGeneric(kHitachiAc3HdrMark, kHitachiAc3HdrSpace,
|
|
kHitachiAc3BitMark, kHitachiAc3OneSpace,
|
|
kHitachiAc3BitMark, kHitachiAc3ZeroSpace,
|
|
kHitachiAc3BitMark, kHitachiAcMinGap,
|
|
data, nbytes, // Bytes
|
|
kHitachiAcFreq, false, repeat, kDutyDefault);
|
|
}
|
|
#endif // SEND_HITACHI_AC3
|
|
|
|
|
|
/// 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?
|
|
IRHitachiAc3::IRHitachiAc3(const uint16_t pin, const bool inverted,
|
|
const bool use_modulation)
|
|
: _irsend(pin, inverted, use_modulation) { stateReset(); }
|
|
|
|
/// Reset the internal state to a fixed known good state.
|
|
/// @note Reset to auto fan, cooling, 23° Celsius
|
|
void IRHitachiAc3::stateReset(void) {
|
|
for (uint8_t i = 0; i < kHitachiAc3StateLength; i++)
|
|
remote_state[i] = 0x00;
|
|
remote_state[0] = 0x01;
|
|
remote_state[1] = 0x10;
|
|
remote_state[3] = 0x40;
|
|
remote_state[5] = 0xFF;
|
|
remote_state[7] = 0xE8;
|
|
remote_state[9] = 0x89;
|
|
remote_state[11] = 0x0B;
|
|
remote_state[13] = 0x3F;
|
|
remote_state[15] = 0x15;
|
|
remote_state[21] = 0x4B;
|
|
remote_state[23] = 0x18;
|
|
setInvertedStates();
|
|
}
|
|
|
|
/// Invert every second byte of the internal state, after the fixed header.
|
|
/// @param[in] length The size of the state array.
|
|
/// @note This is this protocols integrity check.
|
|
void IRHitachiAc3::setInvertedStates(const uint16_t length) {
|
|
if (length > 3) invertBytePairs(remote_state + 3, length - 3);
|
|
}
|
|
|
|
/// Check if every second byte of the state, after the fixed header
|
|
/// is inverted to the previous byte.
|
|
/// @param[in] state The state array to be checked.
|
|
/// @param[in] length The size of the state array.
|
|
/// @note This is this protocols integrity check.
|
|
bool IRHitachiAc3::hasInvertedStates(const uint8_t state[],
|
|
const uint16_t length) {
|
|
return (length <= 3 || checkInvertedBytePairs(state + 3, length - 3));
|
|
}
|
|
|
|
/// Set up hardware to be able to send a message.
|
|
void IRHitachiAc3::begin(void) { _irsend.begin(); }
|
|
|
|
/// 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 *IRHitachiAc3::getRaw(void) {
|
|
setInvertedStates();
|
|
return remote_state;
|
|
}
|
|
|
|
/// Set the internal state from a valid code for this protocol.
|
|
/// @param[in] new_code A valid code for this protocol.
|
|
/// @param[in] length The length of the new_code array.
|
|
void IRHitachiAc3::setRaw(const uint8_t new_code[], const uint16_t length) {
|
|
memcpy(remote_state, new_code, std::min(length, kHitachiAc3StateLength));
|
|
}
|
|
|
|
#if DECODE_HITACHI_AC3
|
|
/// Decode the supplied Hitachi 15to27-byte/120to216-bit A/C message.
|
|
/// Status: STABLE / Works fine.
|
|
/// @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.
|
|
/// @param[in] strict Flag indicating if we should perform strict matching.
|
|
/// @return True if it can decode it, false if it can't.
|
|
/// @note This protocol is almost exactly the same as HitachiAC424 except this
|
|
/// variant has subtle timing differences and multiple lengths.
|
|
/// There are five(5) typical lengths:
|
|
/// kHitachiAc3MinStateLength (Cancel Timer),
|
|
/// kHitachiAc3MinStateLength + 2 (Change Temp),
|
|
/// kHitachiAc3StateLength - 6 (Change Mode),
|
|
/// kHitachiAc3StateLength - 4 (Normal), &
|
|
/// kHitachiAc3StateLength (Set Timer)
|
|
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1060
|
|
bool IRrecv::decodeHitachiAc3(decode_results *results, uint16_t offset,
|
|
const uint16_t nbits,
|
|
const bool strict) {
|
|
if (results->rawlen < 2 * nbits + kHeader + kFooter - 1 + offset)
|
|
return false; // Too short a message to match.
|
|
if (strict) {
|
|
// Check the requested bit length.
|
|
switch (nbits) {
|
|
case kHitachiAc3MinBits: // Cancel Timer (Min Size)
|
|
case kHitachiAc3MinBits + 2 * 8: // Change Temp
|
|
case kHitachiAc3Bits - 6 * 8: // Change Mode
|
|
case kHitachiAc3Bits - 4 * 8: // Normal
|
|
case kHitachiAc3Bits: // Set Temp (Max Size)
|
|
break;
|
|
default: return false;
|
|
}
|
|
}
|
|
|
|
// Header + Data + Footer
|
|
if (!matchGeneric(results->rawbuf + offset, results->state,
|
|
results->rawlen - offset, nbits,
|
|
kHitachiAc3HdrMark, kHitachiAc3HdrSpace,
|
|
kHitachiAc3BitMark, kHitachiAc3OneSpace,
|
|
kHitachiAc3BitMark, kHitachiAc3ZeroSpace,
|
|
kHitachiAc3BitMark, kHitachiAcMinGap, true,
|
|
kUseDefTol, 0, false))
|
|
return false; // We failed to find any data.
|
|
|
|
// Compliance
|
|
if (strict && !IRHitachiAc3::hasInvertedStates(results->state, nbits / 8))
|
|
return false;
|
|
// Success
|
|
results->decode_type = decode_type_t::HITACHI_AC3;
|
|
results->bits = nbits;
|
|
return true;
|
|
}
|
|
#endif // DECODE_HITACHI_AC3
|
|
|
|
/// Class constructor for handling detailed Hitachi_AC344 43 byte A/C messages.
|
|
/// @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?
|
|
IRHitachiAc344::IRHitachiAc344(const uint16_t pin, const bool inverted,
|
|
const bool use_modulation)
|
|
: IRHitachiAc424(pin, inverted, use_modulation) { stateReset(); }
|
|
|
|
/// Reset the internal state to auto fan, cooling, 23° Celsius
|
|
void IRHitachiAc344::stateReset(void) {
|
|
IRHitachiAc424::stateReset();
|
|
_.raw[37] = 0x00;
|
|
_.raw[39] = 0x00;
|
|
}
|
|
|
|
#if SEND_HITACHI_AC344
|
|
/// Create and send the IR message to the A/C.
|
|
/// @param[in] repeat Nr. of times to repeat the message.
|
|
void IRHitachiAc344::send(const uint16_t repeat) {
|
|
_irsend.sendHitachiAc344(getRaw(), kHitachiAc344StateLength, repeat);
|
|
}
|
|
#endif // SEND_HITACHI_AC344
|
|
|
|
/// Set the internal state from a valid code for this protocol.
|
|
/// @param[in] new_code A valid code for this protocol.
|
|
/// @param[in] length Size (in bytes) of the code for this protocol.
|
|
void IRHitachiAc344::setRaw(const uint8_t new_code[], const uint16_t length) {
|
|
memcpy(_.raw, new_code, std::min(length, kHitachiAc344StateLength));
|
|
}
|
|
|
|
/// Control the vertical swing setting.
|
|
/// @param[in] on True, turns on the feature. False, turns off the feature.
|
|
void IRHitachiAc344::setSwingV(const bool on) {
|
|
setSwingVToggle(on); // Set the button value.
|
|
_.SwingV = on;
|
|
}
|
|
|
|
/// Get the current vertical swing setting.
|
|
/// @return True, if the setting is on. False, it is off.
|
|
bool IRHitachiAc344::getSwingV(void) const {
|
|
return _.SwingV;
|
|
}
|
|
|
|
/// Control the horizontal swing setting.
|
|
/// @param[in] position The position to set the horizontal swing to.
|
|
void IRHitachiAc344::setSwingH(const uint8_t position) {
|
|
if (position > kHitachiAc344SwingHLeftMax)
|
|
_.SwingH = kHitachiAc344SwingHMiddle;
|
|
else
|
|
_.SwingH = position;
|
|
setButton(kHitachiAc344ButtonSwingH);
|
|
}
|
|
|
|
/// Get the current horizontal swing setting.
|
|
/// @return The current position horizontal swing is set to.
|
|
uint8_t IRHitachiAc344::getSwingH(void) const {
|
|
return _.SwingH;
|
|
}
|
|
|
|
/// Convert a standard A/C horizontal swing into its native setting.
|
|
/// @param[in] position A stdAc::swingh_t position to convert.
|
|
/// @return The equivilent native horizontal swing position.
|
|
uint8_t IRHitachiAc344::convertSwingH(const stdAc::swingh_t position) {
|
|
switch (position) {
|
|
case stdAc::swingh_t::kAuto: return kHitachiAc344SwingHAuto;
|
|
case stdAc::swingh_t::kLeftMax: return kHitachiAc344SwingHLeftMax;
|
|
case stdAc::swingh_t::kLeft: return kHitachiAc344SwingHLeft;
|
|
case stdAc::swingh_t::kRight: return kHitachiAc344SwingHRight;
|
|
case stdAc::swingh_t::kRightMax: return kHitachiAc344SwingHRightMax;
|
|
default: return kHitachiAc344SwingHMiddle;
|
|
}
|
|
}
|
|
|
|
/// 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 IRHitachiAc344::toCommonSwingH(const uint8_t pos) {
|
|
switch (pos) {
|
|
case kHitachiAc344SwingHLeftMax: return stdAc::swingh_t::kLeftMax;
|
|
case kHitachiAc344SwingHLeft: return stdAc::swingh_t::kLeft;
|
|
case kHitachiAc344SwingHRight: return stdAc::swingh_t::kRight;
|
|
case kHitachiAc344SwingHRightMax: return stdAc::swingh_t::kRightMax;
|
|
case kHitachiAc344SwingHAuto: return stdAc::swingh_t::kAuto;
|
|
default: return stdAc::swingh_t::kOff;
|
|
}
|
|
}
|
|
|
|
/// Convert the current internal state into its stdAc::state_t equivalent.
|
|
/// @return The stdAc equivalent of the native settings.
|
|
stdAc::state_t IRHitachiAc344::toCommon(void) const {
|
|
stdAc::state_t result = IRHitachiAc424::toCommon();
|
|
result.protocol = decode_type_t::HITACHI_AC344;
|
|
result.swingv = _.SwingV ? stdAc::swingv_t::kAuto : stdAc::swingv_t::kOff;
|
|
result.swingh = toCommonSwingH(_.SwingH);
|
|
return result;
|
|
}
|
|
|
|
/// Convert the internal state into a human readable string.
|
|
/// @return A string containing the settings in human-readable form.
|
|
String IRHitachiAc344::toString(void) const {
|
|
String result;
|
|
result.reserve(120); // Reserve some heap for the string to reduce fragging.
|
|
result += _toString();
|
|
result += addBoolToString(_.SwingV, kSwingVStr);
|
|
result += addIntToString(_.SwingH, kSwingHStr);
|
|
result += kSpaceLBraceStr;
|
|
switch (_.SwingH) {
|
|
case kHitachiAc344SwingHLeftMax: result += kLeftMaxStr; break;
|
|
case kHitachiAc344SwingHLeft: result += kLeftStr; break;
|
|
case kHitachiAc344SwingHMiddle: result += kMiddleStr; break;
|
|
case kHitachiAc344SwingHRight: result += kRightStr; break;
|
|
case kHitachiAc344SwingHRightMax: result += kRightMaxStr; break;
|
|
case kHitachiAc344SwingHAuto: result += kAutoStr; break;
|
|
default: result += kUnknownStr;
|
|
}
|
|
result += ')';
|
|
return result;
|
|
}
|